From 488093ea44ef953b00a9d6091cb0a97cd5971945 Mon Sep 17 00:00:00 2001 From: pp482 Date: Fri, 26 Aug 2022 10:42:48 -0400 Subject: [PATCH 001/101] Rename basic_rtl to primitive --- examples/ex03_proc/MiscRTL.py | 2 +- examples/ex03_proc/NullXcel.py | 2 +- examples/ex03_proc/ProcDpathRTL.py | 2 +- examples/ex04_xcel/ChecksumXcelRTL.py | 2 +- .../verilog/test/TranslationImport_stdlib_test.py | 8 ++++---- pymtl3/stdlib/ifcs/test/xcel_ifcs_test.py | 2 +- pymtl3/stdlib/{basic_rtl => primitive}/__init__.py | 0 pymtl3/stdlib/{basic_rtl => primitive}/arbiters.py | 0 pymtl3/stdlib/{basic_rtl => primitive}/arithmetics.py | 0 pymtl3/stdlib/{basic_rtl => primitive}/crossbars.py | 0 pymtl3/stdlib/{basic_rtl => primitive}/encoders.py | 0 pymtl3/stdlib/{basic_rtl => primitive}/register_files.py | 0 pymtl3/stdlib/{basic_rtl => primitive}/registers.py | 0 pymtl3/stdlib/{basic_rtl => primitive}/test/__init__.py | 0 .../stdlib/{basic_rtl => primitive}/test/arbiters_test.py | 0 .../{basic_rtl => primitive}/test/crossbars_test.py | 0 .../stdlib/{basic_rtl => primitive}/test/encoders_test.py | 0 pymtl3/stdlib/queues/enrdy_queues.py | 2 +- pymtl3/stdlib/queues/queues.py | 2 +- pymtl3/stdlib/queues/valrdy_queues.py | 2 +- pymtl3/stdlib/stream/queues.py | 2 +- 21 files changed, 13 insertions(+), 13 deletions(-) rename pymtl3/stdlib/{basic_rtl => primitive}/__init__.py (100%) rename pymtl3/stdlib/{basic_rtl => primitive}/arbiters.py (100%) rename pymtl3/stdlib/{basic_rtl => primitive}/arithmetics.py (100%) rename pymtl3/stdlib/{basic_rtl => primitive}/crossbars.py (100%) rename pymtl3/stdlib/{basic_rtl => primitive}/encoders.py (100%) rename pymtl3/stdlib/{basic_rtl => primitive}/register_files.py (100%) rename pymtl3/stdlib/{basic_rtl => primitive}/registers.py (100%) rename pymtl3/stdlib/{basic_rtl => primitive}/test/__init__.py (100%) rename pymtl3/stdlib/{basic_rtl => primitive}/test/arbiters_test.py (100%) rename pymtl3/stdlib/{basic_rtl => primitive}/test/crossbars_test.py (100%) rename pymtl3/stdlib/{basic_rtl => primitive}/test/encoders_test.py (100%) diff --git a/examples/ex03_proc/MiscRTL.py b/examples/ex03_proc/MiscRTL.py index caa6b486c..cc99f5a5e 100644 --- a/examples/ex03_proc/MiscRTL.py +++ b/examples/ex03_proc/MiscRTL.py @@ -9,7 +9,7 @@ """ from pymtl3 import * from pymtl3.stdlib.ifcs import GetIfcRTL, GiveIfcRTL -from pymtl3.stdlib.basic_rtl import RegRst +from pymtl3.stdlib.primitive import RegRst from .TinyRV0InstRTL import * diff --git a/examples/ex03_proc/NullXcel.py b/examples/ex03_proc/NullXcel.py index 0713788e8..fa95a576f 100644 --- a/examples/ex03_proc/NullXcel.py +++ b/examples/ex03_proc/NullXcel.py @@ -9,7 +9,7 @@ from pymtl3 import * from pymtl3.stdlib.ifcs.xcel_ifcs import XcelMinionIfcRTL from pymtl3.stdlib.ifcs.XcelMsg import XcelMsgType, mk_xcel_msg -from pymtl3.stdlib.basic_rtl import RegEn +from pymtl3.stdlib.primitive import RegEn from pymtl3.stdlib.queues import NormalQueueRTL diff --git a/examples/ex03_proc/ProcDpathRTL.py b/examples/ex03_proc/ProcDpathRTL.py index 8f4118dd9..5f5ca5ac4 100644 --- a/examples/ex03_proc/ProcDpathRTL.py +++ b/examples/ex03_proc/ProcDpathRTL.py @@ -10,7 +10,7 @@ from pymtl3 import * from pymtl3.stdlib.mem import mk_mem_msg -from pymtl3.stdlib.basic_rtl import Adder, Incrementer, Mux, RegEn, RegEnRst, RegisterFile +from pymtl3.stdlib.primitive import Adder, Incrementer, Mux, RegEn, RegEnRst, RegisterFile from .MiscRTL import AluRTL, ImmGenRTL from .TinyRV0InstRTL import OPCODE, RD, RS1, RS2, SHAMT diff --git a/examples/ex04_xcel/ChecksumXcelRTL.py b/examples/ex04_xcel/ChecksumXcelRTL.py index d9df596ba..1235623a8 100644 --- a/examples/ex04_xcel/ChecksumXcelRTL.py +++ b/examples/ex04_xcel/ChecksumXcelRTL.py @@ -11,7 +11,7 @@ from pymtl3 import * from pymtl3.stdlib.ifcs import XcelMsgType, mk_xcel_msg, XcelMinionIfcRTL from pymtl3.stdlib.queues import NormalQueueRTL -from pymtl3.stdlib.basic_rtl import Reg +from pymtl3.stdlib.primitive import Reg # TODO: add more comments. diff --git a/pymtl3/passes/backends/verilog/test/TranslationImport_stdlib_test.py b/pymtl3/passes/backends/verilog/test/TranslationImport_stdlib_test.py index 0ca18e513..91f5c4bc8 100644 --- a/pymtl3/passes/backends/verilog/test/TranslationImport_stdlib_test.py +++ b/pymtl3/passes/backends/verilog/test/TranslationImport_stdlib_test.py @@ -15,10 +15,10 @@ from pymtl3.passes.PassGroups import DefaultPassGroup from pymtl3.passes.rtlir.util.test_utility import do_test -from pymtl3.stdlib.basic_rtl.test.arbiters_test import test_rr_arb_4 as _rr_arb_4 -from pymtl3.stdlib.basic_rtl.test.arbiters_test import test_rr_arb_en_4 as _rr_arb_en_4 -from pymtl3.stdlib.basic_rtl.test.crossbars_test import test_crossbar3 as _crossbar3 -from pymtl3.stdlib.basic_rtl.test.encoders_test import ( +from pymtl3.stdlib.primitive.test.arbiters_test import test_rr_arb_4 as _rr_arb_4 +from pymtl3.stdlib.primitive.test.arbiters_test import test_rr_arb_en_4 as _rr_arb_en_4 +from pymtl3.stdlib.primitive.test.crossbars_test import test_crossbar3 as _crossbar3 +from pymtl3.stdlib.primitive.test.encoders_test import ( test_encoder_5_directed as _encoder5, ) from pymtl3.stdlib.stream.test.queues_test import test_normal1_simple as _n1 diff --git a/pymtl3/stdlib/ifcs/test/xcel_ifcs_test.py b/pymtl3/stdlib/ifcs/test/xcel_ifcs_test.py index 4ea16ab0d..dbfaf32df 100644 --- a/pymtl3/stdlib/ifcs/test/xcel_ifcs_test.py +++ b/pymtl3/stdlib/ifcs/test/xcel_ifcs_test.py @@ -8,7 +8,7 @@ """ from pymtl3 import * -from pymtl3.stdlib.basic_rtl import RegisterFile +from pymtl3.stdlib.primitive import RegisterFile from pymtl3.stdlib.ifcs import XcelMsgType, mk_xcel_msg from pymtl3.stdlib.queues import NormalQueueRTL from pymtl3.stdlib.test_utils import run_sim diff --git a/pymtl3/stdlib/basic_rtl/__init__.py b/pymtl3/stdlib/primitive/__init__.py similarity index 100% rename from pymtl3/stdlib/basic_rtl/__init__.py rename to pymtl3/stdlib/primitive/__init__.py diff --git a/pymtl3/stdlib/basic_rtl/arbiters.py b/pymtl3/stdlib/primitive/arbiters.py similarity index 100% rename from pymtl3/stdlib/basic_rtl/arbiters.py rename to pymtl3/stdlib/primitive/arbiters.py diff --git a/pymtl3/stdlib/basic_rtl/arithmetics.py b/pymtl3/stdlib/primitive/arithmetics.py similarity index 100% rename from pymtl3/stdlib/basic_rtl/arithmetics.py rename to pymtl3/stdlib/primitive/arithmetics.py diff --git a/pymtl3/stdlib/basic_rtl/crossbars.py b/pymtl3/stdlib/primitive/crossbars.py similarity index 100% rename from pymtl3/stdlib/basic_rtl/crossbars.py rename to pymtl3/stdlib/primitive/crossbars.py diff --git a/pymtl3/stdlib/basic_rtl/encoders.py b/pymtl3/stdlib/primitive/encoders.py similarity index 100% rename from pymtl3/stdlib/basic_rtl/encoders.py rename to pymtl3/stdlib/primitive/encoders.py diff --git a/pymtl3/stdlib/basic_rtl/register_files.py b/pymtl3/stdlib/primitive/register_files.py similarity index 100% rename from pymtl3/stdlib/basic_rtl/register_files.py rename to pymtl3/stdlib/primitive/register_files.py diff --git a/pymtl3/stdlib/basic_rtl/registers.py b/pymtl3/stdlib/primitive/registers.py similarity index 100% rename from pymtl3/stdlib/basic_rtl/registers.py rename to pymtl3/stdlib/primitive/registers.py diff --git a/pymtl3/stdlib/basic_rtl/test/__init__.py b/pymtl3/stdlib/primitive/test/__init__.py similarity index 100% rename from pymtl3/stdlib/basic_rtl/test/__init__.py rename to pymtl3/stdlib/primitive/test/__init__.py diff --git a/pymtl3/stdlib/basic_rtl/test/arbiters_test.py b/pymtl3/stdlib/primitive/test/arbiters_test.py similarity index 100% rename from pymtl3/stdlib/basic_rtl/test/arbiters_test.py rename to pymtl3/stdlib/primitive/test/arbiters_test.py diff --git a/pymtl3/stdlib/basic_rtl/test/crossbars_test.py b/pymtl3/stdlib/primitive/test/crossbars_test.py similarity index 100% rename from pymtl3/stdlib/basic_rtl/test/crossbars_test.py rename to pymtl3/stdlib/primitive/test/crossbars_test.py diff --git a/pymtl3/stdlib/basic_rtl/test/encoders_test.py b/pymtl3/stdlib/primitive/test/encoders_test.py similarity index 100% rename from pymtl3/stdlib/basic_rtl/test/encoders_test.py rename to pymtl3/stdlib/primitive/test/encoders_test.py diff --git a/pymtl3/stdlib/queues/enrdy_queues.py b/pymtl3/stdlib/queues/enrdy_queues.py index 086296021..807722de0 100644 --- a/pymtl3/stdlib/queues/enrdy_queues.py +++ b/pymtl3/stdlib/queues/enrdy_queues.py @@ -9,7 +9,7 @@ """ from pymtl3 import * -from pymtl3.stdlib.basic_rtl import Mux, Reg, RegEn, RegRst +from pymtl3.stdlib.primitive import Mux, Reg, RegEn, RegRst from pymtl3.stdlib.ifcs import RecvIfcRTL, SendIfcRTL diff --git a/pymtl3/stdlib/queues/queues.py b/pymtl3/stdlib/queues/queues.py index fd4ccc63c..c29b2d06b 100644 --- a/pymtl3/stdlib/queues/queues.py +++ b/pymtl3/stdlib/queues/queues.py @@ -9,7 +9,7 @@ from pymtl3 import * -from pymtl3.stdlib.basic_rtl import Mux, RegisterFile +from pymtl3.stdlib.primitive import Mux, RegisterFile from .enq_deq_ifcs import DeqIfcRTL, EnqIfcRTL diff --git a/pymtl3/stdlib/queues/valrdy_queues.py b/pymtl3/stdlib/queues/valrdy_queues.py index 2eb97f0a5..9e24f067c 100644 --- a/pymtl3/stdlib/queues/valrdy_queues.py +++ b/pymtl3/stdlib/queues/valrdy_queues.py @@ -1,5 +1,5 @@ from pymtl3 import * -from pymtl3.stdlib.basic_rtl import Mux, Reg, RegEn, RegisterFile +from pymtl3.stdlib.primitive import Mux, Reg, RegEn, RegisterFile from pymtl3.stdlib.ifcs import InValRdyIfc, OutValRdyIfc diff --git a/pymtl3/stdlib/stream/queues.py b/pymtl3/stdlib/stream/queues.py index 77004f9f0..f7ac1e3db 100644 --- a/pymtl3/stdlib/stream/queues.py +++ b/pymtl3/stdlib/stream/queues.py @@ -9,7 +9,7 @@ from pymtl3 import * -from pymtl3.stdlib.basic_rtl import Mux, RegisterFile +from pymtl3.stdlib.primitive import Mux, RegisterFile from .ifcs import RecvIfcRTL, SendIfcRTL From 658470991600e131af0ebfe3200e34ea3df10489 Mon Sep 17 00:00:00 2001 From: pp482 Date: Sun, 28 Aug 2022 11:51:06 -0400 Subject: [PATCH 002/101] [WIP] Stream and dstruct --- pymtl3/stdlib/dstruct/__init__.py | 1 + pymtl3/stdlib/dstruct/ifcs/__init__.py | 1 + pymtl3/stdlib/dstruct/ifcs/ifcs.py | 262 ++++++++ pymtl3/stdlib/dstruct/queues.py | 595 ++++++++++++++++++ pymtl3/stdlib/dstruct/test/__init__.py | 0 pymtl3/stdlib/dstruct/test/queues_test.py | 182 ++++++ .../{MagicMemoryFL.py => BehavioralMemory.py} | 4 +- .../magic_memory.py => mem/MemoryFL.py} | 59 +- pymtl3/stdlib/reqresp/__init__.py | 0 pymtl3/stdlib/reqresp/ifcs/__init__.py | 1 + pymtl3/stdlib/reqresp/ifcs/ifcs.py | 34 + .../stream/{SinkRTL.py => StreamSinkFL.py} | 28 +- .../{SourceRTL.py => StreamSourceFL.py} | 28 +- pymtl3/stdlib/stream/__init__.py | 9 +- pymtl3/stdlib/stream/fl.py | 248 -------- pymtl3/stdlib/stream/ifcs/__init__.py | 1 + pymtl3/stdlib/stream/{ => ifcs}/ifcs.py | 30 +- pymtl3/stdlib/stream/queue_adapters.py | 65 -- pymtl3/stdlib/stream/queues.py | 299 ++++----- ...magic_memory_test.py => memory_fl_test.py} | 0 pymtl3/stdlib/stream/test/queues_test.py | 68 +- pymtl3/stdlib/test_utils/__init__.py | 4 +- .../stdlib/test_utils/test/src_sink_test.py | 26 +- pymtl3/stdlib/test_utils/test_masters.py | 8 +- pymtl3/stdlib/test_utils/test_sinks.py | 17 +- pymtl3/stdlib/test_utils/test_srcs.py | 16 +- 26 files changed, 1364 insertions(+), 622 deletions(-) create mode 100644 pymtl3/stdlib/dstruct/__init__.py create mode 100644 pymtl3/stdlib/dstruct/ifcs/__init__.py create mode 100644 pymtl3/stdlib/dstruct/ifcs/ifcs.py create mode 100644 pymtl3/stdlib/dstruct/queues.py create mode 100644 pymtl3/stdlib/dstruct/test/__init__.py create mode 100644 pymtl3/stdlib/dstruct/test/queues_test.py rename pymtl3/stdlib/mem/{MagicMemoryFL.py => BehavioralMemory.py} (95%) rename pymtl3/stdlib/{stream/magic_memory.py => mem/MemoryFL.py} (81%) create mode 100644 pymtl3/stdlib/reqresp/__init__.py create mode 100644 pymtl3/stdlib/reqresp/ifcs/__init__.py create mode 100644 pymtl3/stdlib/reqresp/ifcs/ifcs.py rename pymtl3/stdlib/stream/{SinkRTL.py => StreamSinkFL.py} (85%) rename pymtl3/stdlib/stream/{SourceRTL.py => StreamSourceFL.py} (65%) delete mode 100644 pymtl3/stdlib/stream/fl.py create mode 100644 pymtl3/stdlib/stream/ifcs/__init__.py rename pymtl3/stdlib/stream/{ => ifcs}/ifcs.py (57%) delete mode 100644 pymtl3/stdlib/stream/queue_adapters.py rename pymtl3/stdlib/stream/test/{magic_memory_test.py => memory_fl_test.py} (100%) diff --git a/pymtl3/stdlib/dstruct/__init__.py b/pymtl3/stdlib/dstruct/__init__.py new file mode 100644 index 000000000..0f297d724 --- /dev/null +++ b/pymtl3/stdlib/dstruct/__init__.py @@ -0,0 +1 @@ +from .queues import NormalQueue, PipeQueue, BypassQueue diff --git a/pymtl3/stdlib/dstruct/ifcs/__init__.py b/pymtl3/stdlib/dstruct/ifcs/__init__.py new file mode 100644 index 000000000..813e96128 --- /dev/null +++ b/pymtl3/stdlib/dstruct/ifcs/__init__.py @@ -0,0 +1 @@ +from .ifcs import EnqIfc, DeqIfc, RecvIfc, SendIfc diff --git a/pymtl3/stdlib/dstruct/ifcs/ifcs.py b/pymtl3/stdlib/dstruct/ifcs/ifcs.py new file mode 100644 index 000000000..04ebee9bc --- /dev/null +++ b/pymtl3/stdlib/dstruct/ifcs/ifcs.py @@ -0,0 +1,262 @@ +from pymtl3 import * +from pymtl3.extra import clone_deepcopy +from pymtl3.stdlib.connects import connect_pairs + +def enrdy_to_str( msg, en, rdy, trace_len=15 ): + if en and not rdy: return "X".ljust( trace_len ) # Not allowed! + if not en and rdy: return " ".ljust( trace_len ) # Idle + if not en and not rdy: return "#".ljust( trace_len ) # Stalled + return f"{msg}".ljust( trace_len ) # en and rdy + +class RecvIfc( Interface ): + + def construct( s, Type ): + s.en = InPort() + s.rdy = OutPort() + s.msg = InPort( Type ) + + s.MsgType = Type + + s.trace_len = len(str(Type())) + + def connect( s, other, parent ): + # We are doing SendCL (other) -> [ RecvCL -> SendRTL ] -> RecvRTL (s) + # SendCL is a caller interface + if isinstance( other, CallerIfcCL ): + m = RecvCL2SendRTL( s.MsgType ) + + if hasattr( parent, "RecvCL2SendRTL_count" ): + count = parent.RecvCL2SendRTL_count + setattr( parent, "RecvCL2SendRTL_" + str( count ), m ) + else: + parent.RecvCL2SendRTL_count = 0 + parent.RecvCL2SendRTL_0 = m + + connect_pairs( + other, m.recv, + m.send.msg, s.msg, + m.send.en, s.en, + m.send.rdy, s.rdy + ) + parent.RecvCL2SendRTL_count += 1 + return True + + elif isinstance( other, CalleeIfcCL ): + if s._dsl.level <= other._dsl.level: + raise InvalidConnectionError( + "CL2RTL connection is not supported between RecvIfcRTL" + " and CalleeIfcCL.\n" + " - level {}: {} (class {})\n" + " - level {}: {} (class {})".format( + s._dsl.level, repr( s ), type( s ), other._dsl.level, + repr( other ), type( other ) ) ) + + m = RecvCL2SendRTL( s.MsgType ) + + if hasattr( parent, "RecvCL2SendRTL_count" ): + count = parent.RecvCL2SendRTL_count + setattr( parent, "RecvCL2SendRTL_" + str( count ), m ) + else: + parent.RecvCL2SendRTL_count = 0 + parent.RecvCL2SendRTL_0 = m + + connect_pairs( + other, m.recv, + m.send.msg, s.msg, + m.send.en, s.en, + m.send.rdy, s.rdy + ) + parent.RecvCL2SendRTL_count += 1 + return True + + return False + + def __str__( s ): + return enrdy_to_str( s.msg, s.en, s.rdy, s.trace_len ) + +class SendIfc( Interface ): + + def construct( s, Type ): + s.en = OutPort() + s.rdy = InPort() + s.msg = OutPort( Type ) + + s.MsgType = Type + + s.trace_len = len(str(Type())) + + def connect( s, other, parent ): + # We are doing SendRTL (s) -> [ RecvRTL -> SendCL ] -> RecvCL (other) + # RecvCL is a callee interface + if isinstance( other, CalleeIfcCL ): + m = RecvRTL2SendCL( s.MsgType ) + + if hasattr( parent, "RecvRTL2SendCL_count" ): + count = parent.RecvRTL2SendCL_count + setattr( parent, "RecvRTL2SendCL_" + str( count ), m ) + else: + parent.RecvRTL2SendCL_count = 0 + parent.RecvRTL2SendCL_0 = m + + connect_pairs( + m.send, other, + s.msg, m.recv.msg, + s.en, m.recv.en, + s.rdy, m.recv.rdy, + ) + parent.RecvRTL2SendCL_count += 1 + return True + + return False + + def __str__( s ): + return enrdy_to_str( s.msg, s.en, s.rdy, s.trace_len ) + +class GetIfc( Interface ): + + def construct( s, Type ): + s.en = OutPort() + s.rdy = InPort() + s.msg = InPort( Type ) + + s.trace_len = len(str(Type())) + + def __str__( s ): + return enrdy_to_str( s.msg, s.en, s.rdy, s.trace_len ) + +# Helper component to connect a GiveIfc to a RecvIfc +class And( Component ): + + def construct( s, Type ): + s.in0 = InPort( Type ) + s.in1 = InPort( Type ) + s.out = OutPort( Type ) + + @update + def up_and(): + s.out @= s.in0 & s.in1 + +class GiveIfc( Interface ): + + def construct( s, Type ): + s.en = InPort() + s.rdy = OutPort() + s.msg = OutPort( Type ) + + s.trace_len = len(str(Type())) + + def connect( s, other, parent ): + # We are doing GiveIfc (s) -> [ AND ] -> RecvIfc (other) + # Basically we AND the rdy of both sides for enable + if isinstance( other, RecvIfc ): + connect( s.msg, other.msg ) + + m = And( Bits1 ) + + if hasattr( parent, "give_recv_ander_cnt" ): + cnt = parent.give_recv_ander_cnt + setattr( parent, "give_recv_ander_" + str( cnt ), m ) + else: + parent.give_recv_ander_cnt = 0 + parent.give_recv_ander_0 = m + + connect_pairs( + m.in0, s.rdy, + m.in1, other.rdy, + m.out, s.en, + m.out, other.en, + ) + parent.give_recv_ander_cnt += 1 + return True + return False + + def __str__( s ): + return enrdy_to_str( s.msg, s.en, s.rdy, s.trace_len ) + +class EnqIfc( RecvIfc ): + + def construct( s, Type ): + super().construct( Type ) + +class DeqIfc( GiveIfc ): + + def construct( s, Type ): + super().construct( Type ) + +#------------------------------------------------------------------------- +# RecvCL2SendRTL +#------------------------------------------------------------------------- + +class RecvCL2SendRTL( Component ): + + def construct( s, MsgType ): + + # Interface + + s.send = SendIfc( MsgType ) + + s.entry = None + + @update_once + def up_clear(): + if s.send.en: # constraints reverse this + s.entry = None + + @update + def up_send_rtl(): + if s.entry is None: + s.send.en @= b1( 0 ) + else: + s.send.en @= b1( s.send.rdy ) + s.send.msg @= s.entry + + s.add_constraints( + U( up_clear ) < WR( s.send.en ), + U( up_clear ) < M( s.recv ), + U( up_clear ) < M( s.recv.rdy ), + M( s.recv ) < U( up_send_rtl ), + M( s.recv.rdy ) < U( up_send_rtl ) + ) + + @non_blocking( lambda s : s.entry is None ) + def recv( s, msg ): + s.entry = clone_deepcopy( msg ) + + def line_trace( s ): + return "{}(){}".format( s.recv, s.send ) + +#------------------------------------------------------------------------- +# RecvRTL2SendCL +#------------------------------------------------------------------------- + +class RecvRTL2SendCL( Component ): + + def construct( s, MsgType ): + + # Interface + + s.recv = RecvIfc( MsgType ) + s.send = CallerIfcCL() + + s.sent_msg = None + s.send_rdy = False + + @update_once + def up_recv_rtl_rdy(): + s.send_rdy = s.send.rdy() & ~s.reset + s.recv.rdy @= s.send_rdy + + @update_once + def up_send_cl(): + s.sent_msg = None + if s.recv.en: + s.send( s.recv.msg ) + s.sent_msg = s.recv.msg + + s.add_constraints( U( up_recv_rtl_rdy ) < U( up_send_cl ) ) + + def line_trace( s ): + return "{}(){}".format( + s.recv.line_trace(), + enrdy_to_str( s.sent_msg, s.sent_msg is not None, s.send_rdy ) + ) diff --git a/pymtl3/stdlib/dstruct/queues.py b/pymtl3/stdlib/dstruct/queues.py new file mode 100644 index 000000000..08f98b356 --- /dev/null +++ b/pymtl3/stdlib/dstruct/queues.py @@ -0,0 +1,595 @@ +""" +------------------------------------------------------------------------- +Queues +------------------------------------------------------------------------- +Common queue data structures. + +Author : Yanghui Ou, Peitian Pan + Date : Aug 26, 2022 +""" + + +from pymtl3 import * +from pymtl3.stdlib.primitive import Mux, RegisterFile + +def enrdy_to_str( msg, en, rdy, trace_len=15 ): + if en and not rdy: return "X".ljust( trace_len ) # Not allowed! + if not en and rdy: return " ".ljust( trace_len ) # Idle + if not en and not rdy: return "#".ljust( trace_len ) # Stalled + return f"{msg}".ljust( trace_len ) # en and rdy + +#------------------------------------------------------------------------- +# Dpath and Ctrl for NormalQueue +#------------------------------------------------------------------------- + +class NormalQueueDpath( Component ): + + def construct( s, EntryType, num_entries=2 ): + + # Interface + + s.enq_msg = InPort( EntryType ) + s.deq_msg = OutPort( EntryType ) + + s.wen = InPort() + s.waddr = InPort( clog2( num_entries ) ) + s.raddr = InPort( clog2( num_entries ) ) + + # Component + + s.queue = m = RegisterFile( EntryType, num_entries ) + m.raddr[0] //= s.raddr + m.rdata[0] //= s.deq_msg + m.wen[0] //= s.wen + m.waddr[0] //= s.waddr + m.wdata[0] //= s.enq_msg + +class NormalQueueCtrl( Component ): + + def construct( s, num_entries=2 ): + + # Constants + + addr_nbits = clog2 ( num_entries ) + count_nbits = clog2 ( num_entries+1 ) + PtrType = mk_bits ( addr_nbits ) + CountType = mk_bits ( count_nbits ) + s.last_idx = PtrType ( num_entries-1 ) + s.num_entries = CountType( num_entries ) + + # Interface + + s.enq_en = InPort () + s.enq_rdy = OutPort() + s.deq_en = InPort () + s.deq_rdy = OutPort() + s.count = OutPort( CountType ) + + s.wen = OutPort() + s.waddr = OutPort( PtrType ) + s.raddr = OutPort( PtrType ) + + # Registers + + s.head = Wire( PtrType ) + s.tail = Wire( PtrType ) + + # Wires + + s.enq_xfer = Wire( Bits1 ) + s.deq_xfer = Wire( Bits1 ) + + # Connections + + connect( s.wen, s.enq_xfer ) + connect( s.waddr, s.tail ) + connect( s.raddr, s.head ) + + s.enq_rdy //= lambda: ~s.reset & ( s.count < s.num_entries ) + s.deq_rdy //= lambda: ~s.reset & ( s.count > CountType(0) ) + + s.enq_xfer //= lambda: s.enq_en & s.enq_rdy + s.deq_xfer //= lambda: s.deq_en & s.deq_rdy + + @update_ff + def up_reg(): + + if s.reset: + s.head <<= PtrType(0) + s.tail <<= PtrType(0) + s.count <<= CountType(0) + + else: + if s.deq_xfer: + s.head <<= s.head + PtrType(1) if s.head < s.last_idx else PtrType(0) + + if s.enq_xfer: + s.tail <<= s.tail + PtrType(1) if s.tail < s.last_idx else PtrType(0) + + if s.enq_xfer & ~s.deq_xfer: + s.count <<= s.count + CountType(1) + if ~s.enq_xfer & s.deq_xfer: + s.count <<= s.count - CountType(1) + +#------------------------------------------------------------------------- +# NormalQueue +#------------------------------------------------------------------------- + +class NormalQueue( Component ): + + def construct( s, EntryType, num_entries=2 ): + + # Interface + + s.enq_en = InPort() + s.enq_rdy = OutPort() + s.enq_msg = InPort( EntryType ) + s.deq_en = InPort() + s.deq_rdy = OutPort() + s.deq_msg = OutPort( EntryType ) + s.count = OutPort( mk_bits( clog2( num_entries+1 ) ) ) + + # Components + + assert num_entries > 0 + if num_entries == 1: + s.q = NormalQueue1Entry( EntryType ) + connect( s.enq_en, s.q.enq_en ) + connect( s.enq_rdy, s.q.enq_rdy ) + connect( s.enq_msg, s.q.enq_msg ) + connect( s.deq_en, s.q.deq_en ) + connect( s.deq_rdy, s.q.deq_rdy ) + connect( s.deq_msg, s.q.deq_msg ) + connect( s.count, s.q.count ) + + else: + s.ctrl = NormalQueueCtrl ( num_entries ) + s.dpath = NormalQueueDpath( EntryType, num_entries ) + + # Connect ctrl to data path + + connect( s.ctrl.wen, s.dpath.wen ) + connect( s.ctrl.waddr, s.dpath.waddr ) + connect( s.ctrl.raddr, s.dpath.raddr ) + + # Connect to interface + + connect( s.enq_en, s.ctrl.enq_en ) + connect( s.enq_rdy, s.ctrl.enq_rdy ) + connect( s.deq_en, s.ctrl.deq_en ) + connect( s.deq_rdy, s.ctrl.deq_rdy ) + connect( s.count, s.ctrl.count ) + connect( s.enq_msg, s.dpath.enq_msg ) + connect( s.deq_msg, s.dpath.deq_msg ) + + # Line trace + + def line_trace( s ): + enq_str = enrdy_to_str( s.enq_msg, s.enq_en, s.enq_rdy ) + deq_str = enrdy_to_str( s.deq_msg, s.deq_en, s.deq_rdy ) + return f"{enq_str}({s.count}){deq_str}" + +#------------------------------------------------------------------------- +# Ctrl for PipeQueue +#------------------------------------------------------------------------- + +class PipeQueueCtrl( Component ): + + def construct( s, num_entries=2 ): + + # Constants + + addr_nbits = clog2 ( num_entries ) + count_nbits = clog2 ( num_entries+1 ) + PtrType = mk_bits ( addr_nbits ) + CountType = mk_bits ( count_nbits ) + s.last_idx = PtrType ( num_entries-1 ) + s.num_entries = CountType( num_entries ) + + # Interface + + s.enq_en = InPort ( Bits1 ) + s.enq_rdy = OutPort( Bits1 ) + s.deq_en = InPort ( Bits1 ) + s.deq_rdy = OutPort( Bits1 ) + s.count = OutPort( CountType ) + + s.wen = OutPort( Bits1 ) + s.waddr = OutPort( PtrType ) + s.raddr = OutPort( PtrType ) + + # Registers + + s.head = Wire( PtrType ) + s.tail = Wire( PtrType ) + + # Wires + + s.enq_xfer = Wire( Bits1 ) + s.deq_xfer = Wire( Bits1 ) + + # Connections + + connect( s.wen, s.enq_xfer ) + connect( s.waddr, s.tail ) + connect( s.raddr, s.head ) + + s.deq_rdy //= lambda: ~s.reset & ( s.count > CountType(0) ) + s.enq_rdy //= lambda: ~s.reset & ( ( s.count < s.num_entries ) | s.deq_en ) + + s.enq_xfer //= lambda: s.enq_en & s.enq_rdy + s.deq_xfer //= lambda: s.deq_en & s.deq_rdy + + @update_ff + def up_reg(): + + if s.reset: + s.head <<= PtrType(0) + s.tail <<= PtrType(0) + s.count <<= CountType(0) + + else: + if s.deq_xfer: + s.head <<= s.head + PtrType(1) if s.head < s.last_idx else PtrType(0) + + if s.enq_xfer: + s.tail <<= s.tail + PtrType(1) if s.tail < s.last_idx else PtrType(0) + + if s.enq_xfer & ~s.deq_xfer: + s.count <<= s.count + CountType(1) + if ~s.enq_xfer & s.deq_xfer: + s.count <<= s.count - CountType(1) + +#------------------------------------------------------------------------- +# PipeQueue +#------------------------------------------------------------------------- + +class PipeQueue( Component ): + + def construct( s, EntryType, num_entries=2 ): + + # Interface + + s.enq_en = InPort() + s.enq_rdy = OutPort() + s.enq_msg = InPort( EntryType ) + s.deq_en = InPort() + s.deq_rdy = OutPort() + s.deq_msg = OutPort( EntryType ) + s.count = OutPort( mk_bits( clog2( num_entries+1 ) ) ) + + # Components + + assert num_entries > 0 + if num_entries == 1: + s.q = PipeQueue1Entry( EntryType ) + connect( s.enq_en, s.q.enq_en ) + connect( s.enq_rdy, s.q.enq_rdy ) + connect( s.enq_msg, s.q.enq_msg ) + connect( s.deq_en, s.q.deq_en ) + connect( s.deq_rdy, s.q.deq_rdy ) + connect( s.deq_msg, s.q.deq_msg ) + connect( s.count, s.q.count ) + + else: + s.ctrl = PipeQueueCtrl ( num_entries ) + s.dpath = NormalQueueDpath( EntryType, num_entries ) + + # Connect ctrl to data path + + connect( s.ctrl.wen, s.dpath.wen ) + connect( s.ctrl.waddr, s.dpath.waddr ) + connect( s.ctrl.raddr, s.dpath.raddr ) + + # Connect to interface + + connect( s.enq_en, s.ctrl.enq_en ) + connect( s.enq_rdy, s.ctrl.enq_rdy ) + connect( s.deq_en, s.ctrl.deq_en ) + connect( s.deq_rdy, s.ctrl.deq_rdy ) + connect( s.count, s.ctrl.count ) + connect( s.enq_msg, s.dpath.enq_msg ) + connect( s.deq_msg, s.dpath.deq_msg ) + + # Line trace + + def line_trace( s ): + enq_str = enrdy_to_str( s.enq_msg, s.enq_en, s.enq_rdy ) + deq_str = enrdy_to_str( s.deq_msg, s.deq_en, s.deq_rdy ) + return f"{enq_str}({s.count}){deq_str}" + +#------------------------------------------------------------------------- +# Ctrl and Dpath for BypassQueue +#------------------------------------------------------------------------- + +class BypassQueueDpath( Component ): + + def construct( s, EntryType, num_entries=2 ): + + # Interface + + s.enq_msg = InPort( EntryType ) + s.deq_msg = OutPort( EntryType ) + + s.wen = InPort( Bits1 ) + s.waddr = InPort( mk_bits( clog2( num_entries ) ) ) + s.raddr = InPort( mk_bits( clog2( num_entries ) ) ) + s.mux_sel = InPort( Bits1 ) + + # Component + + s.queue = m = RegisterFile( EntryType, num_entries ) + m.raddr[0] //= s.raddr + m.wen[0] //= s.wen + m.waddr[0] //= s.waddr + m.wdata[0] //= s.enq_msg + + s.mux = m = Mux( EntryType, 2 ) + m.sel //= s.mux_sel + m.in_[0] //= s.queue.rdata[0] + m.in_[1] //= s.enq_msg + m.out //= s.deq_msg + +class BypassQueueCtrl( Component ): + + def construct( s, num_entries=2 ): + + # Constants + + addr_nbits = clog2 ( num_entries ) + count_nbits = clog2 ( num_entries+1 ) + PtrType = mk_bits ( addr_nbits ) + CountType = mk_bits ( count_nbits ) + s.last_idx = PtrType ( num_entries-1 ) + s.num_entries = CountType( num_entries ) + + # Interface + + s.enq_en = InPort ( Bits1 ) + s.enq_rdy = OutPort( Bits1 ) + s.deq_en = InPort ( Bits1 ) + s.deq_rdy = OutPort( Bits1 ) + s.count = OutPort( CountType ) + + s.wen = OutPort( Bits1 ) + s.waddr = OutPort( PtrType ) + s.raddr = OutPort( PtrType ) + s.mux_sel = OutPort( Bits1 ) + + # Registers + + s.head = Wire( PtrType ) + s.tail = Wire( PtrType ) + + # Wires + + s.enq_xfer = Wire( Bits1 ) + s.deq_xfer = Wire( Bits1 ) + + # Connections + + connect( s.wen, s.enq_xfer ) + connect( s.waddr, s.tail ) + connect( s.raddr, s.head ) + + s.enq_rdy //= lambda: ~s.reset & ( s.count < s.num_entries ) + s.deq_rdy //= lambda: ~s.reset & ( (s.count > CountType(0) ) | s.enq_en ) + + s.mux_sel //= lambda: s.count == CountType(0) + + s.enq_xfer //= lambda: s.enq_en & s.enq_rdy + s.deq_xfer //= lambda: s.deq_en & s.deq_rdy + + @update_ff + def up_reg(): + + if s.reset: + s.head <<= PtrType(0) + s.tail <<= PtrType(0) + s.count <<= CountType(0) + + else: + if s.deq_xfer: + s.head <<= s.head + PtrType(1) if s.head < s.last_idx else PtrType(0) + + if s.enq_xfer: + s.tail <<= s.tail + PtrType(1) if s.tail < s.last_idx else PtrType(0) + + if s.enq_xfer & ~s.deq_xfer: + s.count <<= s.count + CountType(1) + if ~s.enq_xfer & s.deq_xfer: + s.count <<= s.count - CountType(1) + +#------------------------------------------------------------------------- +# BypassQueue +#------------------------------------------------------------------------- + +class BypassQueue( Component ): + + def construct( s, EntryType, num_entries=2 ): + + # Interface + + s.enq_en = InPort() + s.enq_rdy = OutPort() + s.enq_msg = InPort( EntryType ) + s.deq_en = InPort() + s.deq_rdy = OutPort() + s.deq_msg = OutPort( EntryType ) + s.count = OutPort( mk_bits( clog2( num_entries+1 ) ) ) + + # Components + + assert num_entries > 0 + if num_entries == 1: + s.q = BypassQueue1Entry( EntryType ) + connect( s.enq_en, s.q.enq_en ) + connect( s.enq_rdy, s.q.enq_rdy ) + connect( s.enq_msg, s.q.enq_msg ) + connect( s.deq_en, s.q.deq_en ) + connect( s.deq_rdy, s.q.deq_rdy ) + connect( s.deq_msg, s.q.deq_msg ) + connect( s.count, s.q.count ) + + else: + s.ctrl = BypassQueueCtrl ( num_entries ) + s.dpath = BypassQueueDpath( EntryType, num_entries ) + + # Connect ctrl to data path + + connect( s.ctrl.wen, s.dpath.wen ) + connect( s.ctrl.waddr, s.dpath.waddr ) + connect( s.ctrl.raddr, s.dpath.raddr ) + connect( s.ctrl.mux_sel, s.dpath.mux_sel ) + + # Connect to interface + + connect( s.enq_en, s.ctrl.enq_en ) + connect( s.enq_rdy, s.ctrl.enq_rdy ) + connect( s.deq_en, s.ctrl.deq_en ) + connect( s.deq_rdy, s.ctrl.deq_rdy ) + connect( s.count, s.ctrl.count ) + connect( s.enq_msg, s.dpath.enq_msg ) + connect( s.deq_msg, s.dpath.deq_msg ) + + # Line trace + + def line_trace( s ): + enq_str = enrdy_to_str( s.enq_msg, s.enq_en, s.enq_rdy ) + deq_str = enrdy_to_str( s.deq_msg, s.deq_en, s.deq_rdy ) + return f"{enq_str}({s.count}){deq_str}" + +#------------------------------------------------------------------------- +# NormalQueue1Entry +#------------------------------------------------------------------------- + +class NormalQueue1Entry( Component ): + + def construct( s, EntryType ): + + # Interface + + s.enq_en = InPort() + s.enq_rdy = OutPort() + s.enq_msg = InPort( EntryType ) + s.deq_en = InPort() + s.deq_rdy = OutPort() + s.deq_msg = OutPort( EntryType ) + s.count = OutPort ( Bits1 ) + + # Components + + s.entry = Wire( EntryType ) + s.full = Wire( Bits1 ) + + # Logic + + s.count //= s.full + + s.deq_msg //= s.entry + + s.enq_rdy //= lambda: ~s.reset & ~s.full + s.deq_rdy //= lambda: ~s.reset & s.full + + @update_ff + def ff_normal1(): + s.full <<= ~s.reset & ( ~s.deq_en & (s.enq_en | s.full) ) + if s.enq_en: + s.entry <<= s.enq_msg + + def line_trace( s ): + enq_str = enrdy_to_str( s.enq_msg, s.enq_en, s.enq_rdy ) + deq_str = enrdy_to_str( s.deq_msg, s.deq_en, s.deq_rdy ) + return f"{enq_str}({s.full}){deq_str}" + +#------------------------------------------------------------------------- +# PipeQueue1Entry +#------------------------------------------------------------------------- + +class PipeQueue1Entry( Component ): + + def construct( s, EntryType ): + + # Interface + + s.enq_en = InPort() + s.enq_rdy = OutPort() + s.enq_msg = InPort( EntryType ) + s.deq_en = InPort() + s.deq_rdy = OutPort() + s.deq_msg = OutPort( EntryType ) + s.count = OutPort ( Bits1 ) + + # Components + + s.entry = Wire( EntryType ) + s.full = Wire( Bits1 ) + + # Logic + + s.count //= s.full + + s.deq_msg //= s.entry + + s.enq_rdy //= lambda: ~s.reset & ( ~s.full | s.deq_en ) + s.deq_rdy //= lambda: s.full & ~s.reset + + @update_ff + def ff_pipe1(): + s.full <<= ~s.reset & ( s.enq_en | s.full & ~s.deq_en ) + + if s.enq_en: + s.entry <<= s.enq_msg + + def line_trace( s ): + enq_str = enrdy_to_str( s.enq_msg, s.enq_en, s.enq_rdy ) + deq_str = enrdy_to_str( s.deq_msg, s.deq_en, s.deq_rdy ) + return f"{enq_str}({s.full}){deq_str}" + +#------------------------------------------------------------------------- +# BypassQueue1Entry +#------------------------------------------------------------------------- + +class BypassQueue1Entry( Component ): + + def construct( s, EntryType ): + + # Interface + + s.enq_en = InPort() + s.enq_rdy = OutPort() + s.enq_msg = InPort( EntryType ) + s.deq_en = InPort() + s.deq_rdy = OutPort() + s.deq_msg = OutPort( EntryType ) + s.count = OutPort ( Bits1 ) + + # Components + + s.entry = Wire( EntryType ) + s.full = Wire( Bits1 ) + + s.bypass_mux = m = Mux( EntryType, 2 ) + m.in_[0] //= s.enq_msg + m.in_[1] //= s.entry + m.out //= s.deq_msg + m.sel //= s.full + + # Logic + + s.count //= s.full + + s.enq_rdy //= lambda: ~s.reset & ~s.full + s.deq_rdy //= lambda: ~s.reset & ( s.full | s.enq_en ) + + @update_ff + def ff_bypass1(): + s.full <<= ~s.reset & ( ~s.deq_en & (s.enq_en | s.full) ) + + if s.enq_en & ~s.deq_en: + s.entry <<= s.enq_msg + + def line_trace( s ): + enq_str = enrdy_to_str( s.enq_msg, s.enq_en, s.enq_rdy ) + deq_str = enrdy_to_str( s.deq_msg, s.deq_en, s.deq_rdy ) + return f"{enq_str}({s.full}){deq_str}" diff --git a/pymtl3/stdlib/dstruct/test/__init__.py b/pymtl3/stdlib/dstruct/test/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pymtl3/stdlib/dstruct/test/queues_test.py b/pymtl3/stdlib/dstruct/test/queues_test.py new file mode 100644 index 000000000..3d2915697 --- /dev/null +++ b/pymtl3/stdlib/dstruct/test/queues_test.py @@ -0,0 +1,182 @@ +""" +======================================================================== +Tests for CL queues +======================================================================== + +Author: Yanghui Ou + Date: Mar 24, 2019 +""" +from itertools import product + +import pytest + +from pymtl3 import * +from pymtl3.stdlib.stream import StreamSinkFL, StreamSourceFL +from pymtl3.stdlib.test_utils import TestVectorSimulator, run_sim + +from ..queues import ( + BypassQueue1Entry, + BypassQueue, + NormalQueue1Entry, + NormalQueue, + PipeQueue1Entry, + PipeQueue, +) + +#------------------------------------------------------------------------- +# TestVectorSimulator test +#------------------------------------------------------------------------- + +def run_tv_test( dut, test_vectors ): + + # Define input/output functions + + def tv_in( dut, tv ): + dut.enq_en @= tv[0] + dut.enq_msg @= tv[2] + dut.deq_en @= tv[3] + + def tv_out( dut, tv ): + if tv[1] != '?': assert dut.enq_rdy == tv[1] + if tv[4] != '?': assert dut.deq_rdy == tv[4] + if tv[5] != '?': assert dut.deq_msg == tv[5] + + # Run the test + + sim = TestVectorSimulator( dut, test_vectors, tv_in, tv_out ) + sim.run_test() + +def test_pipe_Bits(): + + B1 = mk_bits(1) + B32 = mk_bits(32) + run_tv_test( NormalQueue( Bits32, 2 ), [ + # enq.en enq.rdy enq.msg deq.en deq.rdy deq.msg + [ B1(1), B1(1), B32(123), B1(0), B1(0), '?' ], + [ B1(1), B1(1), B32(345), B1(0), B1(1), B32(123) ], + [ B1(0), B1(0), B32(567), B1(0), B1(1), B32(123) ], + [ B1(0), B1(0), B32(567), B1(1), B1(1), B32(123) ], + [ B1(0), B1(1), B32(567), B1(1), B1(1), B32(345) ], + [ B1(1), B1(1), B32(567), B1(0), B1(0), '?' ], + [ B1(1), B1(1), B32(0 ), B1(1), B1(1), B32(567) ], + [ B1(1), B1(1), B32(1 ), B1(1), B1(1), B32(0 ) ], + [ B1(1), B1(1), B32(2 ), B1(1), B1(1), B32(1 ) ], + [ B1(0), B1(1), B32(2 ), B1(1), B1(1), B32(2 ) ], +] ) + +#------------------------------------------------------------------------- +# TestHarness +#------------------------------------------------------------------------- + +class TestHarness( Component ): + + def construct( s, MsgType, QType, src_msgs, sink_msgs ): + + s.src = StreamSourceFL ( MsgType, src_msgs ) + s.dut = QType( MsgType ) + s.sink = StreamSinkFL( MsgType, sink_msgs ) + + connect( s.src.ostream.msg, s.dut.enq_msg ) + connect( s.dut.deq_msg, s.sink.istream.msg ) + connect( s.src.ostream.rdy, s.dut.enq_rdy ) + + @update + def upblk_enq(): + s.dut.enq_en @= s.dut.enq_rdy & s.src.ostream.val + + @update + def upblk_deq(): + s.dut.deq_en @= s.dut.deq_rdy & s.sink.istream.rdy + s.sink.istream.val @= s.dut.deq_en + + def done( s ): + return s.src.done() and s.sink.done() + + def line_trace( s ): + return "{} ({}) {}".format( + s.src.line_trace(), s.dut.line_trace(), s.sink.line_trace() ) + +#------------------------------------------------------------------------- +# Test cases +#------------------------------------------------------------------------- + +test_msgs = [ Bits16( 4 ), Bits16( 1 ), Bits16( 2 ), Bits16( 3 ) ] + +arrival_normal = [ 2, 4, 6, 8 ] +arrival_pipe = [ 2, 3, 4, 5 ] +arrival_bypass = [ 1, 2, 3, 4 ] + + +def test_normal1_simple(): + th = TestHarness( Bits16, NormalQueue, test_msgs, test_msgs ) + th.set_param( "top.sink.construct", arrival_time = arrival_normal ) + th.set_param( "top.dut.construct", num_entries = 1 ) + run_sim( th ) + +def test_normal2_simple(): + th = TestHarness( Bits16, NormalQueue, test_msgs, test_msgs ) + th.set_param( "top.sink.construct", arrival_time = arrival_pipe ) + th.set_param( "top.dut.construct", num_entries = 2 ) + run_sim( th ) + +def test_pipe1_simple(): + th = TestHarness( Bits16, PipeQueue, test_msgs, test_msgs ) + th.set_param( "top.sink.construct", arrival_time = arrival_pipe ) + th.set_param( "top.dut.construct", num_entries = 1 ) + run_sim( th ) + +def test_pipe1_backpressure(): + th = TestHarness( Bits16, PipeQueue, test_msgs, test_msgs ) + th.set_param( "top.sink.construct", initial_delay = 20 ) + th.set_param( "top.dut.construct", num_entries = 1 ) + run_sim( th ) + +def test_pipe2_backpressure(): + th = TestHarness( Bits16, PipeQueue, test_msgs, test_msgs ) + th.set_param( "top.sink.construct", initial_delay = 20 ) + th.set_param( "top.dut.construct", num_entries = 2 ) + run_sim( th ) + +def test_bypass1_simple(): + th = TestHarness( Bits16, BypassQueue, test_msgs, test_msgs ) + th.set_param( "top.sink.construct", arrival_time = arrival_bypass ) + th.set_param( "top.dut.construct", num_entries = 1 ) + run_sim( th ) + +def test_bypass1_backpressure(): + th = TestHarness( Bits16, BypassQueue, test_msgs, test_msgs ) + th.set_param( "top.sink.construct", initial_delay = 20 ) + th.set_param( "top.dut.construct", num_entries = 1 ) + run_sim( th ) + +def test_bypass2_sparse(): + th = TestHarness( Bits16, BypassQueue, test_msgs, test_msgs ) + th.set_param( "top.src.construct", interval_delay = 3 ) + th.set_param( "top.dut.construct", num_entries = 2 ) + run_sim( th ) + +@pytest.mark.parametrize( + 'QType, num_entries', + product( [ NormalQueue, PipeQueue, BypassQueue ], [ 8, 10, 12, 16 ] ) +) +def test_large_backpressure( QType, num_entries ): + msgs = test_msgs * 8 + th = TestHarness( Bits16, QType, msgs, msgs ) + th.set_param( "top.sink.construct", initial_delay = 20 ) + th.set_param( "top.dut.construct", num_entries = 16 ) + run_sim( th ) + +@pytest.mark.parametrize( + 'QType', [ NormalQueue1Entry, PipeQueue1Entry, BypassQueue1Entry ] +) +def test_single_simple( QType ): + th = TestHarness( Bits16, QType, test_msgs, test_msgs ) + run_sim( th ) + +@pytest.mark.parametrize( + 'QType', [ NormalQueue1Entry, PipeQueue1Entry, BypassQueue1Entry ] +) +def test_single_backpressure( QType ): + th = TestHarness( Bits16, QType, test_msgs, test_msgs ) + th.set_param( "top.sink.construct", initial_delay = 10, interval_delay=2 ) + run_sim( th ) diff --git a/pymtl3/stdlib/mem/MagicMemoryFL.py b/pymtl3/stdlib/mem/BehavioralMemory.py similarity index 95% rename from pymtl3/stdlib/mem/MagicMemoryFL.py rename to pymtl3/stdlib/mem/BehavioralMemory.py index 34e1b1142..68ac42eef 100644 --- a/pymtl3/stdlib/mem/MagicMemoryFL.py +++ b/pymtl3/stdlib/mem/BehavioralMemory.py @@ -18,13 +18,11 @@ MemMsgType.AMO_XOR : lambda m,a : m^a, } -class MagicMemoryFL( Component ): +class BehavioralMemory( Component ): def construct( s, mem_nbytes=1<<20 ): s.mem = bytearray( mem_nbytes ) - s.ifc = MemMinionIfcFL( s.read, s.write, s.amo ) - s.trace = " " @update_once def up_clear_trace(): diff --git a/pymtl3/stdlib/stream/magic_memory.py b/pymtl3/stdlib/mem/MemoryFL.py similarity index 81% rename from pymtl3/stdlib/stream/magic_memory.py rename to pymtl3/stdlib/mem/MemoryFL.py index 9fc9ef7f8..1ee09b388 100644 --- a/pymtl3/stdlib/stream/magic_memory.py +++ b/pymtl3/stdlib/mem/MemoryFL.py @@ -1,6 +1,6 @@ """ ======================================================================== -MagicMemoryCL +MemoryFL ======================================================================== A behavioral magic memory which is parameterized based on the number of memory request/response ports. This version is a little different from @@ -15,12 +15,11 @@ from pymtl3 import * from pymtl3.extra import clone_deepcopy -from pymtl3.stdlib.delays import DelayPipeDeqCL, DelayPipeSendCL, StallCL -from pymtl3.stdlib.mem.MagicMemoryFL import MagicMemoryFL +from pymtl3.stdlib.mem.BehavioralMemory import BehavioralMemory from pymtl3.stdlib.mem.MemMsg import MemMsgType, mk_mem_msg -from .ifcs import MinionIfcRTL, RecvIfcRTL, SendIfcRTL -from .queues import NormalQueueRTL, PipeQueueRTL +from pymtl3.stdlib.reqresp.ifcs import ResponderIfc +from pymtl3.stdlib.stream.ifcs import IStreamIfc, OStreamIfc # BRGTC2 custom MemMsg modified for RISC-V 32 @@ -51,10 +50,10 @@ class RandomStall( Component ): def construct( s, Type, stall_prob=0, stall_seed=0xdeadbeef ): - s.recv = RecvIfcRTL( Type ) - s.send = SendIfcRTL( Type ) + s.istream = IStreamIfc( Type ) + s.ostream = OStreamIfc( Type ) - s.recv.msg //= s.send.msg + s.istream.msg //= s.ostream.msg stall_rgen = Random( stall_seed ) @@ -67,10 +66,10 @@ def up_rand(): @update def up_stall_rdy(): - s.recv.rdy @= s.send.rdy & (s.rand_value > stall_prob) + s.istream.rdy @= s.ostream.rdy & (s.rand_value > stall_prob) @update def up_stall_val(): - s.send.val @= s.recv.val & (s.rand_value > stall_prob) + s.ostream.val @= s.istream.val & (s.rand_value > stall_prob) def line_trace( s ): return "[ ]" if s.rand_value > s.stall_prob else "[#]" @@ -80,8 +79,8 @@ class InelasticDelayPipe( Component ): def construct( s, Type, delay=1 ): - s.recv = RecvIfcRTL( Type ) - s.send = SendIfcRTL( Type ) + s.istream = IStreamIfc( Type ) + s.ostream = OStreamIfc( Type ) assert delay >= 1 s.delay = delay @@ -92,32 +91,32 @@ def construct( s, Type, delay=1 ): @update_ff def up_delay(): - if s.recv.rdy & s.recv.val: - s.delay_pipe[0] = clone_deepcopy( s.recv.msg ) + if s.istream.rdy & s.istream.val: + s.delay_pipe[0] = clone_deepcopy( s.istream.msg ) # We remove the sent message from the pipe first and then # clone the recv message. This allows us to use one entry for one # delay - if s.send.val: - if s.send.rdy: + if s.ostream.val: + if s.ostream.rdy: s.delay_pipe[-1] = None s.delay_pipe.rotate() else: # this cycle invalid send means pipe[-1] is None s.delay_pipe.rotate() if s.delay_pipe[-1] is None: - s.send.val <<= 0 + s.ostream.val <<= 0 else: - s.send.val <<= 1 - s.send.msg <<= s.delay_pipe[-1] + s.ostream.val <<= 1 + s.ostream.msg <<= s.delay_pipe[-1] - s.recv.rdy <<= s.delay_pipe[0] is None + s.istream.rdy <<= s.delay_pipe[0] is None def line_trace( s ): return f"[{''.join([ ' ' if x is None else '*' for x in s.delay_pipe])}]" -class MagicMemoryRTL( Component ): +class MemoryFL( Component ): # Magical methods @@ -141,7 +140,7 @@ def construct( s, nports=1, mem_ifc_dtypes=[mk_mem_msg(8,32,32)], # Interface - s.ifc = [ MinionIfcRTL( req_classes[i], resp_classes[i] ) for i in range(nports) ] + s.ifc = [ ResponderIfc( req_classes[i], resp_classes[i] ) for i in range(nports) ] # stall and delays @@ -150,23 +149,23 @@ def construct( s, nports=1, mem_ifc_dtypes=[mk_mem_msg(8,32,32)], s.resp_qs = [ InelasticDelayPipe( resp_classes[i], extra_latency+1 ) for i in range(nports) ] for i in range(nports): - s.req_stalls[i].recv //= s.ifc[i].req - # s.req_stalls[i].send //= s.req_qs[i].recv - s.resp_qs[i].send //= s.ifc[i].resp + s.req_stalls[i].istream //= s.ifc[i].req + # s.req_stalls[i].ostream //= s.req_qs[i].istream + s.resp_qs[i].ostream //= s.ifc[i].resp - s.req_stalls[i].send.rdy //= s.resp_qs[i].recv.rdy - s.req_stalls[i].send.val //= s.resp_qs[i].recv.val + s.req_stalls[i].ostream.rdy //= s.resp_qs[i].istream.rdy + s.req_stalls[i].ostream.val //= s.resp_qs[i].istream.val @update_once def up_mem(): for i in range(nports): - if s.req_stalls[i].send.val: + if s.req_stalls[i].ostream.val: # Dequeue memory request message - req = s.req_stalls[i].send.msg + req = s.req_stalls[i].ostream.msg len_ = int(req.len) if len_ == 0: len_ = req_classes[i].data_nbits >> 3 @@ -196,7 +195,7 @@ def up_mem(): else: assert False - s.resp_qs[i].recv.msg @= resp + s.resp_qs[i].istream.msg @= resp #----------------------------------------------------------------------- # line_trace diff --git a/pymtl3/stdlib/reqresp/__init__.py b/pymtl3/stdlib/reqresp/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pymtl3/stdlib/reqresp/ifcs/__init__.py b/pymtl3/stdlib/reqresp/ifcs/__init__.py new file mode 100644 index 000000000..b58c005e0 --- /dev/null +++ b/pymtl3/stdlib/reqresp/ifcs/__init__.py @@ -0,0 +1 @@ +from .ifcs import RequesterIfc, ResponderIfc diff --git a/pymtl3/stdlib/reqresp/ifcs/ifcs.py b/pymtl3/stdlib/reqresp/ifcs/ifcs.py new file mode 100644 index 000000000..805b8f6bf --- /dev/null +++ b/pymtl3/stdlib/reqresp/ifcs/ifcs.py @@ -0,0 +1,34 @@ +""" +======================================================================== +Requester and Responder interfaces +======================================================================== +RTL Requester and Responder interfaces. + +Author : Shunning Jiang, Peitian Pan + Date : Aug 26, 2022 +""" + +from pymtl3 import * +from pymtl3.stdlib.stream.ifcs import IStreamIfc, OStreamIfc + +class RequesterIfc( Interface ): + + def construct( s, ReqType, RespType ): + s.ReqType = ReqType + s.RespType = RespType + s.reqstream = OStreamIfc( Type=ReqType ) + s.respstream = IStreamIfc( Type=RespType ) + + def __str__( s ): + return f"{s.reqstream}|{s.respstream}" + +class ResponderIfc( Interface ): + + def construct( s, ReqType, RespType ): + s.ReqType = ReqType + s.RespType = RespType + s.reqstream = IStreamIfc( Type=ReqType ) + s.respstream = OStreamIfc( Type=RespType ) + + def __str__( s ): + return f"{s.reqstream}|{s.respstream}" diff --git a/pymtl3/stdlib/stream/SinkRTL.py b/pymtl3/stdlib/stream/StreamSinkFL.py similarity index 85% rename from pymtl3/stdlib/stream/SinkRTL.py rename to pymtl3/stdlib/stream/StreamSinkFL.py index 8cbfbc4d9..d7ba308a4 100644 --- a/pymtl3/stdlib/stream/SinkRTL.py +++ b/pymtl3/stdlib/stream/StreamSinkFL.py @@ -1,31 +1,31 @@ """ ======================================================================== -SinkRTL +StremSinkFL ======================================================================== -Test sinks with RTL interfaces. +Test sinks with port interfaces. -Author : Shunning Jiang - Date : Feb 12, 2021 +Author : Shunning Jiang, Peitian Pan + Date : Aug 26, 2022 """ from pymtl3 import * -from .ifcs import RecvIfcRTL +from .ifcs import IStreamIfc class PyMTLTestSinkError( Exception ): pass #------------------------------------------------------------------------- -# TestSinkRTL +# StreamSinkFL #------------------------------------------------------------------------- -class SinkRTL( Component ): +class StreamSinkFL( Component ): def construct( s, Type, msgs, initial_delay=0, interval_delay=0, arrival_time=None, cmp_fn=lambda a, b : a == b ): # Interface - s.recv = RecvIfcRTL( Type ) + s.istream = IStreamIfc( Type ) # Data @@ -69,14 +69,14 @@ def up_sink(): s.idx = 0 s.count = initial_delay - s.recv.rdy <<= (s.idx < len(s.msgs)) & (s.count == 0) + s.istream.rdy <<= (s.idx < len(s.msgs)) & (s.count == 0) else: s.cycle_count += 1 # This means at least previous cycle count = 0 - if s.recv.val & s.recv.rdy: - msg = s.recv.msg + if s.istream.val & s.istream.rdy: + msg = s.istream.msg # Sanity check if s.idx >= len(s.msgs): @@ -107,9 +107,9 @@ def up_sink(): if s.count > 0: s.count -= 1 - s.recv.rdy <<= 0 + s.istream.rdy <<= 0 else: # s.count == 0 - s.recv.rdy <<= (s.idx < len(s.msgs)) + s.istream.rdy <<= (s.idx < len(s.msgs)) def done( s ): return s.done_flag @@ -117,4 +117,4 @@ def done( s ): # Line trace def line_trace( s ): - return f"{s.recv}" + return f"{s.istream}" diff --git a/pymtl3/stdlib/stream/SourceRTL.py b/pymtl3/stdlib/stream/StreamSourceFL.py similarity index 65% rename from pymtl3/stdlib/stream/SourceRTL.py rename to pymtl3/stdlib/stream/StreamSourceFL.py index 920d84dc4..3b934c396 100644 --- a/pymtl3/stdlib/stream/SourceRTL.py +++ b/pymtl3/stdlib/stream/StreamSourceFL.py @@ -1,26 +1,26 @@ """ ======================================================================== -SourceRTL +StreamSourceFL ======================================================================== -Test sources with RTL interfaces. +Test sources with port interfaces. -Author : Shunning Jiang - Date : Feb 12, 2021 +Author : Shunning Jiang, Peitian Pan + Date : Aug 26, 2022 """ from collections import deque from copy import deepcopy from pymtl3 import * -from .ifcs import SendIfcRTL +from .ifcs import OStreamIfc -class SourceRTL( Component ): +class StreamSourceFL( Component ): def construct( s, Type, msgs, initial_delay=0, interval_delay=0 ): # Interface - s.send = SendIfcRTL( Type ) + s.ostream = OStreamIfc( Type ) # Data @@ -35,23 +35,23 @@ def up_src(): if s.reset: s.idx = 0 s.count = initial_delay - s.send.val <<= 0 + s.ostream.val <<= 0 else: - if s.send.val & s.send.rdy: + if s.ostream.val & s.ostream.rdy: s.idx += 1 s.count = interval_delay if s.count > 0: s.count -= 1 - s.send.val <<= 0 + s.ostream.val <<= 0 else: # s.count == 0 if s.idx < len(s.msgs): - s.send.val <<= 1 - s.send.msg <<= s.msgs[s.idx] + s.ostream.val <<= 1 + s.ostream.msg <<= s.msgs[s.idx] else: - s.send.val <<= 0 + s.ostream.val <<= 0 def done( s ): @@ -60,4 +60,4 @@ def done( s ): # Line trace def line_trace( s ): - return f"{s.send}" + return f"{s.ostream}" diff --git a/pymtl3/stdlib/stream/__init__.py b/pymtl3/stdlib/stream/__init__.py index 64fb7b68d..101ff27b8 100644 --- a/pymtl3/stdlib/stream/__init__.py +++ b/pymtl3/stdlib/stream/__init__.py @@ -1,7 +1,4 @@ -from .SinkRTL import SinkRTL -from .SourceRTL import SourceRTL +from .StreamSinkFL import StreamSinkFL +from .StreamSourceFL import StreamSourceFL from . import ifcs -from .queue_adapters import RecvQueueAdapter, SendQueueAdapter -from .queues import NormalQueueRTL, PipeQueueRTL, BypassQueueRTL -from .magic_memory import MagicMemoryRTL -from . import fl +from .queues import StreamNormalQueue, StreamPipeQueue, StreamBypassQueue diff --git a/pymtl3/stdlib/stream/fl.py b/pymtl3/stdlib/stream/fl.py deleted file mode 100644 index 6895c2e2d..000000000 --- a/pymtl3/stdlib/stream/fl.py +++ /dev/null @@ -1,248 +0,0 @@ -import greenlet -from pymtl3 import * -from pymtl3.extra import clone_deepcopy -from pymtl3.stdlib.mem.MemMsg import MemMsgType -from .ifcs import RecvIfcRTL, SendIfcRTL, MasterIfcRTL - -class RecvQueueAdapter( Component ): - - @blocking - def deq( s ): - while s.entry is None: - greenlet.getcurrent().parent.switch(0) - ret = s.entry - s.entry = None - return ret - - def construct( s, Type ): - s.recv = RecvIfcRTL( Type ) - s.entry = None - - @update_once - def up_recv_rdy(): - s.recv.rdy @= (s.entry is None) - - @update_once - def up_recv_msg(): - if (s.entry is None) & s.recv.val: - s.entry = clone_deepcopy( s.recv.msg ) - - s.add_constraints( M( s.deq ) < U( up_recv_rdy ), # deq before recv in a cycle -- pipe behavior - U( up_recv_rdy ) < U( up_recv_msg ) ) - - def line_trace( s ): - return "{}(){}".format( s.recv, s.send ) - -class SendQueueAdapter( Component ): - - @blocking - def enq( s, msg ): - while s.entry is not None: - greenlet.getcurrent().parent.switch(0) - - s.entry = clone_deepcopy(msg) - - def construct( s, Type ): - s.send = SendIfcRTL( Type ) - s.entry = None - - s.sent = Wire() - - @update - def up_send(): - if s.entry is None: - s.send.val @= 0 - else: - s.send.val @= 1 - s.send.msg @= s.entry - - @update_ff - def up_sent(): - s.sent <<= s.send.val & s.send.rdy - - @update_once - def up_clear(): - if s.sent: # constraints reverse this - s.entry = None - - s.add_constraints( U( up_clear ) < M( s.enq ), - M( s.enq ) < U( up_send ) ) - - def line_trace( s ): - return "{}(){}".format( s.recv, s.send ) - -class MemMasterAdapter( Component ): - - @blocking - def read( s, addr, nbytes ): - while s.req_entry is not None: - greenlet.getcurrent().parent.switch(0) - - s.req_entry = s.create_req( MemMsgType.READ, 0, addr, nbytes ) - - while s.resp_entry is None: - greenlet.getcurrent().parent.switch(0) - - ret = s.resp_entry.data[0:nbytes<<3] - s.resp_entry = None - return ret - - @blocking - def write( s, addr, nbytes, data ): - while s.req_entry is not None: - greenlet.getcurrent().parent.switch(0) - - s.req_entry = s.create_req( MemMsgType.WRITE, 0, addr, nbytes, data ) - - while s.resp_entry is None: - greenlet.getcurrent().parent.switch(0) - - s.resp_entry = None - - @blocking - def amo( s, amo_type, addr, nbytes, data ): - while s.req_entry is not None: - greenlet.getcurrent().parent.switch(0) - - s.req_entry = s.create_req( amo_type, 0, addr, nbytes, data ) - - while s.resp_entry is None: - greenlet.getcurrent().parent.switch(0) - - ret = s.resp_entry.data[0:nbytes<<3] - s.resp_entry = None - return ret - - def construct( s, ReqType, RespType ): - s.master = MasterIfcRTL( ReqType, RespType ) - - # Use create_req to handle type mismatch - Tlen = ReqType.get_field_type('len') - Tdata = ReqType.get_field_type('data') - s.create_req = lambda a,b,c,d,e=0: ReqType( a, b, c, Tlen(d, trunc_int=True), Tdata(int(e)) ) - - s.req_entry = None - s.resp_entry = None - - # req path - - s.req_sent = Wire() - - @update_ff - def up_req_sent(): - s.req_sent <<= s.master.req.val & s.master.req.rdy - - @update - def up_clear_req(): - if s.req_sent: - s.req_entry = None - - @update_once - def up_send_req(): - if s.req_entry is None: - s.master.req.val @= 0 - else: - s.master.req.val @= 1 - s.master.req.msg @= s.req_entry - - # resp path - @update_once - def up_resp_rdy(): - s.master.resp.rdy @= (s.resp_entry is None) - - @update_once - def up_resp_msg(): - if (s.resp_entry is None) & s.master.resp.val: - s.resp_entry = clone_deepcopy( s.master.resp.msg ) - - s.add_constraints( U( up_clear_req ) < M(s.read), - U( up_clear_req ) < M(s.write), - U( up_clear_req ) < M(s.amo), - - M( s.read ) < U( up_send_req ), - M( s.write ) < U( up_send_req ), - M( s.amo ) < U( up_send_req ), - - M( s.read ) < U( up_resp_rdy ), - M( s.write ) < U( up_resp_rdy ), - M( s.amo ) < U( up_resp_rdy ), - U( up_resp_rdy ) < U( up_resp_msg ) ) - -class XcelMasterAdapter( Component ): - - @blocking - def read( s, addr ): - while s.req_entry is not None: - greenlet.getcurrent().parent.switch(0) - - s.req_entry = s.create_req( 0, addr ) - - while s.resp_entry is None: - greenlet.getcurrent().parent.switch(0) - - ret = s.resp_entry.data - s.resp_entry = None - return ret - - @blocking - def write( s, addr, data ): - while s.req_entry is not None: - greenlet.getcurrent().parent.switch(0) - - s.req_entry = s.create_req( 1, addr, data ) - - while s.resp_entry is None: - greenlet.getcurrent().parent.switch(0) - - s.resp_entry = None - - def construct( s, ReqType, RespType ): - s.master = MasterIfcRTL( ReqType, RespType ) - - # Use create_req to handle type mismatch - Tdata = ReqType.get_field_type('data') - s.create_req = lambda a,b,c=0: ReqType( a, b, Tdata(int(c)) ) - - s.req_entry = None - s.resp_entry = None - - # req path - - s.req_sent = Wire() - - @update_ff - def up_req_sent(): - s.req_sent <<= s.master.req.val & s.master.req.rdy - - @update - def up_clear_req(): - if s.req_sent: - s.req_entry = None - - @update_once - def up_send_req(): - if s.req_entry is None: - s.master.req.val @= 0 - else: - s.master.req.val @= 1 - s.master.req.msg @= s.req_entry - - # resp path - @update_once - def up_resp_rdy(): - s.master.resp.rdy @= (s.resp_entry is None) - - @update_once - def up_resp_msg(): - if (s.resp_entry is None) & s.master.resp.val: - s.resp_entry = clone_deepcopy( s.master.resp.msg ) - - s.add_constraints( U( up_clear_req ) < M(s.read), - U( up_clear_req ) < M(s.write), - - M( s.read ) < U( up_send_req ), - M( s.write ) < U( up_send_req ), - - M( s.read ) < U( up_resp_rdy ), - M( s.write ) < U( up_resp_rdy ), - U( up_resp_rdy ) < U( up_resp_msg ) ) diff --git a/pymtl3/stdlib/stream/ifcs/__init__.py b/pymtl3/stdlib/stream/ifcs/__init__.py new file mode 100644 index 000000000..227dc3dcf --- /dev/null +++ b/pymtl3/stdlib/stream/ifcs/__init__.py @@ -0,0 +1 @@ +from .ifcs import IStreamIfc, OStreamIfc diff --git a/pymtl3/stdlib/stream/ifcs.py b/pymtl3/stdlib/stream/ifcs/ifcs.py similarity index 57% rename from pymtl3/stdlib/stream/ifcs.py rename to pymtl3/stdlib/stream/ifcs/ifcs.py index 0912b517b..b1a6dfdc3 100644 --- a/pymtl3/stdlib/stream/ifcs.py +++ b/pymtl3/stdlib/stream/ifcs/ifcs.py @@ -1,11 +1,11 @@ """ ======================================================================== -ValRdyIfc +IStream and OStream interfaces ======================================================================== -RTL val/rdy interface. +RTL IStream and OStream interface with val/rdy. -Author : Shunning Jiang - Date : Apr 5, 2019 +Author : Shunning Jiang, Peitian Pan + Date : Aug 26, 2022 """ from pymtl3 import * @@ -16,7 +16,7 @@ def valrdy_to_str( msg, val, rdy, trace_len=15 ): if not val and not rdy: return ".".ljust( trace_len ) return f"{msg}".ljust( trace_len ) # val and rdy -class RecvIfcRTL( Interface ): +class IStreamIfc( Interface ): def construct( s, Type ): @@ -29,7 +29,7 @@ def construct( s, Type ): def __str__( s ): return valrdy_to_str( s.msg, s.val, s.rdy, s.trace_len ) -class SendIfcRTL( Interface ): +class OStreamIfc( Interface ): def construct( s, Type ): @@ -41,21 +41,3 @@ def construct( s, Type ): def __str__( s ): return valrdy_to_str( s.msg, s.val, s.rdy, s.trace_len ) - -class MasterIfcRTL( Interface ): - def construct( s, ReqType, RespType ): - s.ReqType = ReqType - s.RespType = RespType - s.req = SendIfcRTL( Type=ReqType ) - s.resp = RecvIfcRTL( Type=RespType ) - def __str__( s ): - return f"{s.req}|{s.resp}" - -class MinionIfcRTL( Interface ): - def construct( s, ReqType, RespType ): - s.ReqType = ReqType - s.RespType = RespType - s.req = RecvIfcRTL( Type=ReqType ) - s.resp = SendIfcRTL( Type=RespType ) - def __str__( s ): - return f"{s.req}|{s.resp}" diff --git a/pymtl3/stdlib/stream/queue_adapters.py b/pymtl3/stdlib/stream/queue_adapters.py deleted file mode 100644 index 991857357..000000000 --- a/pymtl3/stdlib/stream/queue_adapters.py +++ /dev/null @@ -1,65 +0,0 @@ -from pymtl3 import * -from pymtl3.extra import clone_deepcopy -from .ifcs import RecvIfcRTL, SendIfcRTL - -class RecvQueueAdapter( Component ): - - @non_blocking( lambda s: s.entry is not None ) - def deq( s ): - ret = s.entry - s.entry = None - return ret - - def construct( s, Type ): - s.recv = RecvIfcRTL( Type ) - s.entry = None - - @update_once - def up_recv_rdy(): - s.recv.rdy @= (s.entry is None) - - @update_once - def up_recv_msg(): - if (s.entry is None) & s.recv.val: - s.entry = clone_deepcopy( s.recv.msg ) - - s.add_constraints( M( s.deq ) < U( up_recv_rdy ), # deq before recv in a cycle -- pipe behavior - M( s.deq.rdy ) < U( up_recv_rdy ), - U( up_recv_rdy ) < U( up_recv_msg ) ) - - -class SendQueueAdapter( Component ): - - @non_blocking( lambda s: s.entry is None ) - def enq( s, msg ): - s.entry = clone_deepcopy( msg ) - - def construct( s, Type ): - s.send = SendIfcRTL( Type ) - - s.entry = None - s.sent = Wire() - - @update_once - def up_send(): - if s.entry is None: - s.send.val @= 0 - else: - s.send.val @= 1 - s.send.msg @= s.entry - - @update_ff - def up_sent(): - s.sent <<= s.send.val & s.send.rdy - - @update_once - def up_clear(): - if s.sent: # constraints reverse this - s.entry = None - - s.add_constraints( - U( up_clear ) < M( s.enq ), - U( up_clear ) < M( s.enq.rdy ), - M( s.enq ) < U( up_send ), - M( s.enq.rdy ) < U( up_send ) - ) diff --git a/pymtl3/stdlib/stream/queues.py b/pymtl3/stdlib/stream/queues.py index f7ac1e3db..8570d27ca 100644 --- a/pymtl3/stdlib/stream/queues.py +++ b/pymtl3/stdlib/stream/queues.py @@ -1,30 +1,31 @@ """ ------------------------------------------------------------------------- -Library of RTL queues +Stream queue, stream pipeline queue, and stream bypass queue ------------------------------------------------------------------------- +Queues with stream interface. -Author : Yanghui Ou - Date : Feb 22, 2021 +Author : Yanghui Ou, Peitian Pan + Date : Aug 26, 2022 """ from pymtl3 import * from pymtl3.stdlib.primitive import Mux, RegisterFile -from .ifcs import RecvIfcRTL, SendIfcRTL +from .ifcs import IStreamIfc, OStreamIfc #------------------------------------------------------------------------- -# NormalQueue1EntryRTL +# StreamNormalQueue1Entry #------------------------------------------------------------------------- -class NormalQueue1EntryRTL( Component ): +class StreamNormalQueue1Entry( Component ): def construct( s, EntryType ): # Interface - s.recv = RecvIfcRTL( EntryType ) - s.send = SendIfcRTL( EntryType ) + s.istream = IStreamIfc( EntryType ) + s.ostream = OStreamIfc( EntryType ) s.count = OutPort() # Components @@ -36,36 +37,36 @@ def construct( s, EntryType ): s.count //= s.full - s.send.msg //= s.entry - s.send.val //= s.full - s.recv.rdy //= lambda: ~s.full + s.ostream.msg //= s.entry + s.ostream.val //= s.full + s.istream.rdy //= lambda: ~s.full @update_ff def ff_normal1(): if s.reset: s.full <<= 0 else: - s.full <<= (s.recv.val & ~s.full) | (s.full & ~s.send.rdy) + s.full <<= (s.istream.val & ~s.full) | (s.full & ~s.ostream.rdy) - if s.recv.val & ~s.full: - s.entry <<= s.recv.msg + if s.istream.val & ~s.full: + s.entry <<= s.istream.msg def line_trace( s ): - return f"{s.recv}({s.full}){s.send}" + return f"{s.istream}({s.full}){s.ostream}" #------------------------------------------------------------------------- -# Dpath and Ctrl for NormalQueueRTL +# Dpath and Ctrl for StreamNormalQueue #------------------------------------------------------------------------- -class NormalQueueDpathRTL( Component ): +class StreamNormalQueueDpath( Component ): def construct( s, EntryType, num_entries=2 ): assert num_entries >= 2 # Interface - s.recv_msg = InPort( EntryType ) - s.send_msg = OutPort( EntryType ) + s.istream_msg = InPort( EntryType ) + s.ostream_msg = OutPort( EntryType ) s.wen = InPort() s.waddr = InPort( clog2( num_entries ) ) @@ -75,12 +76,12 @@ def construct( s, EntryType, num_entries=2 ): s.rf = m = RegisterFile( EntryType, num_entries ) m.raddr[0] //= s.raddr - m.rdata[0] //= s.send_msg + m.rdata[0] //= s.ostream_msg m.wen[0] //= s.wen m.waddr[0] //= s.waddr - m.wdata[0] //= s.recv_msg + m.wdata[0] //= s.istream_msg -class NormalQueueCtrlRTL( Component ): +class StreamNormalQueueCtrl( Component ): def construct( s, num_entries=2 ): assert num_entries >= 2 @@ -92,10 +93,10 @@ def construct( s, num_entries=2 ): # Interface - s.recv_val = InPort() - s.recv_rdy = OutPort() - s.send_val = OutPort() - s.send_rdy = InPort() + s.istream_val = InPort() + s.istream_rdy = OutPort() + s.ostream_val = OutPort() + s.ostream_rdy = InPort() s.count = OutPort( count_nbits ) s.wen = OutPort() @@ -109,20 +110,20 @@ def construct( s, num_entries=2 ): # Wires - s.recv_xfer = Wire() - s.send_xfer = Wire() + s.istream_xfer = Wire() + s.ostream_xfer = Wire() # Connections - s.wen //= s.recv_xfer + s.wen //= s.istream_xfer s.waddr //= s.tail s.raddr //= s.head - s.recv_rdy //= lambda: s.count < num_entries - s.send_val //= lambda: s.count > 0 + s.istream_rdy //= lambda: s.count < num_entries + s.ostream_val //= lambda: s.count > 0 - s.recv_xfer //= lambda: s.recv_val & s.recv_rdy - s.send_xfer //= lambda: s.send_val & s.send_rdy + s.istream_xfer //= lambda: s.istream_val & s.istream_rdy + s.ostream_xfer //= lambda: s.ostream_val & s.ostream_rdy @update_ff def up_reg(): @@ -133,29 +134,29 @@ def up_reg(): s.count <<= 0 else: - if s.recv_xfer: + if s.istream_xfer: s.tail <<= s.tail + 1 if ( s.tail < num_entries - 1 ) else 0 - if s.send_xfer: + if s.ostream_xfer: s.head <<= s.head + 1 if ( s.head < num_entries -1 ) else 0 - if s.recv_xfer & ~s.send_xfer: + if s.istream_xfer & ~s.ostream_xfer: s.count <<= s.count + 1 - elif ~s.recv_xfer & s.send_xfer: + elif ~s.istream_xfer & s.ostream_xfer: s.count <<= s.count - 1 #------------------------------------------------------------------------- -# NormalQueueRTL +# StreamNormalQueue #------------------------------------------------------------------------- -class NormalQueueRTL( Component ): +class StreamNormalQueue( Component ): def construct( s, EntryType, num_entries=2 ): # Interface - s.recv = RecvIfcRTL( EntryType ) - s.send = SendIfcRTL( EntryType ) + s.istream = IStreamIfc( EntryType ) + s.ostream = OStreamIfc( EntryType ) s.count = OutPort( clog2( num_entries+1 ) ) # Components @@ -163,14 +164,14 @@ def construct( s, EntryType, num_entries=2 ): assert num_entries > 0 if num_entries == 1: - s.q = NormalQueue1EntryRTL( EntryType ) - s.recv //= s.q.recv - s.send //= s.q.send + s.q = StreamNormalQueue1Entry( EntryType ) + s.istream //= s.q.istream + s.ostream //= s.q.ostream s.count //= s.q.count else: - s.ctrl = NormalQueueCtrlRTL ( num_entries ) - s.dpath = NormalQueueDpathRTL( EntryType, num_entries ) + s.ctrl = StreamNormalQueueCtrl ( num_entries ) + s.dpath = StreamNormalQueueDpath( EntryType, num_entries ) # Connect ctrl to data path @@ -180,32 +181,32 @@ def construct( s, EntryType, num_entries=2 ): # Connect to interface - s.recv.val //= s.ctrl.recv_val - s.recv.rdy //= s.ctrl.recv_rdy - s.recv.msg //= s.dpath.recv_msg + s.istream.val //= s.ctrl.istream_val + s.istream.rdy //= s.ctrl.istream_rdy + s.istream.msg //= s.dpath.istream_msg - s.send.val //= s.ctrl.send_val - s.send.rdy //= s.ctrl.send_rdy - s.send.msg //= s.dpath.send_msg + s.ostream.val //= s.ctrl.ostream_val + s.ostream.rdy //= s.ctrl.ostream_rdy + s.ostream.msg //= s.dpath.ostream_msg s.count //= s.ctrl.count # Line trace def line_trace( s ): - return f"{s.recv}({s.count}){s.send}" + return f"{s.istream}({s.count}){s.ostream}" #------------------------------------------------------------------------- -# PipeQueue1EntryRTL +# StreamPipeQueue1Entry #------------------------------------------------------------------------- -class PipeQueue1EntryRTL( Component ): +class StreamPipeQueue1Entry( Component ): def construct( s, EntryType ): # Interface - s.recv = RecvIfcRTL( EntryType ) - s.send = SendIfcRTL( EntryType ) + s.istream = IStreamIfc( EntryType ) + s.ostream = OStreamIfc( EntryType ) s.count = OutPort() # Components @@ -217,13 +218,13 @@ def construct( s, EntryType ): s.count //= s.full - s.send.msg //= s.entry - s.send.val //= s.full + s.ostream.msg //= s.entry + s.ostream.val //= s.full # If send is rdy, either the entry will be sent out to free up space # for recv, or entry is already available for send. Then if not full # entry can always buffer up a message. rdy path is elongated - s.recv.rdy //= lambda: s.send.rdy | ~s.full + s.istream.rdy //= lambda: s.ostream.rdy | ~s.full @update_ff def ff_pipe1(): @@ -234,20 +235,20 @@ def ff_pipe1(): # message due to back pressure and the entry is already full. # Otherwise it is not full and it becomes full only if there is # a valid incoming message. - s.full <<= ~s.recv.rdy | s.recv.val + s.full <<= ~s.istream.rdy | s.istream.val # AND rdy and val to buffer the incoming message - if s.recv.rdy & s.recv.val: - s.entry <<= s.recv.msg + if s.istream.rdy & s.istream.val: + s.entry <<= s.istream.msg def line_trace( s ): - return f"{s.recv}({s.full}){s.send}" + return f"{s.istream}({s.full}){s.ostream}" #------------------------------------------------------------------------- -# Ctrl for PipeQueue +# Ctrl for StreamPipeQueue #------------------------------------------------------------------------- -class PipeQueueCtrlRTL( Component ): +class StreamPipeQueueCtrl( Component ): def construct( s, num_entries=2 ): assert num_entries >= 2 @@ -259,10 +260,10 @@ def construct( s, num_entries=2 ): # Interface - s.recv_val = InPort () - s.recv_rdy = OutPort() - s.send_val = OutPort() - s.send_rdy = InPort () + s.istream_val = InPort () + s.istream_rdy = OutPort() + s.ostream_val = OutPort() + s.ostream_rdy = InPort () s.count = OutPort( count_nbits ) s.wen = OutPort() @@ -276,20 +277,20 @@ def construct( s, num_entries=2 ): # Wires - s.recv_xfer = Wire() - s.send_xfer = Wire() + s.istream_xfer = Wire() + s.ostream_xfer = Wire() # Connections - s.wen //= s.recv_xfer + s.wen //= s.istream_xfer s.waddr //= s.tail s.raddr //= s.head - s.send_val //= lambda: s.count > 0 - s.recv_rdy //= lambda: ( s.count < num_entries ) | s.send_rdy + s.ostream_val //= lambda: s.count > 0 + s.istream_rdy //= lambda: ( s.count < num_entries ) | s.ostream_rdy - s.recv_xfer //= lambda: s.recv_val & s.recv_rdy - s.send_xfer //= lambda: s.send_val & s.send_rdy + s.istream_xfer //= lambda: s.istream_val & s.istream_rdy + s.ostream_xfer //= lambda: s.ostream_val & s.ostream_rdy @update_ff def up_reg(): @@ -300,43 +301,43 @@ def up_reg(): s.count <<= 0 else: - if s.recv_xfer: + if s.istream_xfer: s.tail <<= s.tail + 1 if ( s.tail < num_entries - 1 ) else 0 - if s.send_xfer: + if s.ostream_xfer: s.head <<= s.head + 1 if ( s.head < num_entries -1 ) else 0 - if s.recv_xfer & ~s.send_xfer: + if s.istream_xfer & ~s.ostream_xfer: s.count <<= s.count + 1 - elif ~s.recv_xfer & s.send_xfer: + elif ~s.istream_xfer & s.ostream_xfer: s.count <<= s.count - 1 #------------------------------------------------------------------------- -# PipeQueueRTL +# StreamPipeQueue #------------------------------------------------------------------------- -class PipeQueueRTL( Component ): +class StreamPipeQueue( Component ): def construct( s, EntryType, num_entries=2 ): # Interface - s.recv = RecvIfcRTL( EntryType ) - s.send = SendIfcRTL( EntryType ) + s.istream = IStreamIfc( EntryType ) + s.ostream = OStreamIfc( EntryType ) s.count = OutPort( clog2( num_entries+1 ) ) # Components assert num_entries > 0 if num_entries == 1: - s.q = PipeQueue1EntryRTL( EntryType ) - s.recv //= s.q.recv - s.send //= s.q.send + s.q = StreamPipeQueue1Entry( EntryType ) + s.istream //= s.q.istream + s.ostream //= s.q.ostream s.count //= s.q.count else: - s.ctrl = PipeQueueCtrlRTL ( num_entries ) - s.dpath = NormalQueueDpathRTL( EntryType, num_entries ) + s.ctrl = StreamPipeQueueCtrl ( num_entries ) + s.dpath = StreamNormalQueueDpath( EntryType, num_entries ) # Connect ctrl to data path @@ -346,31 +347,31 @@ def construct( s, EntryType, num_entries=2 ): # Connect to interface - s.recv.val //= s.ctrl.recv_val - s.recv.rdy //= s.ctrl.recv_rdy - s.send.val //= s.ctrl.send_val - s.send.rdy //= s.ctrl.send_rdy + s.istream.val //= s.ctrl.istream_val + s.istream.rdy //= s.ctrl.istream_rdy + s.ostream.val //= s.ctrl.ostream_val + s.ostream.rdy //= s.ctrl.ostream_rdy s.count //= s.ctrl.count - s.recv.msg //= s.dpath.recv_msg - s.send.msg //= s.dpath.send_msg + s.istream.msg //= s.dpath.istream_msg + s.ostream.msg //= s.dpath.ostream_msg # Line trace def line_trace( s ): - return f"{s.recv}({s.count}){s.send}" + return f"{s.istream}({s.count}){s.ostream}" #------------------------------------------------------------------------- -# BypassQueue1EntryRTL +# StreamBypassQueue1Entry #------------------------------------------------------------------------- -class BypassQueue1EntryRTL( Component ): +class StreamBypassQueue1Entry( Component ): def construct( s, EntryType ): # Interface - s.recv = RecvIfcRTL( EntryType ) - s.send = SendIfcRTL( EntryType ) + s.istream = IStreamIfc( EntryType ) + s.ostream = OStreamIfc( EntryType ) s.count = OutPort() # Components @@ -379,44 +380,45 @@ def construct( s, EntryType ): s.entry = Wire( EntryType ) s.bypass_mux = m = Mux( EntryType, 2 ) - m.in_[0] //= s.recv.msg + m.in_[0] //= s.istream.msg m.in_[1] //= s.entry - m.out //= s.send.msg + m.out //= s.ostream.msg m.sel //= s.full # Logic s.count //= s.full - s.send.val //= lambda: s.full | s.recv.val - s.recv.rdy //= lambda: ~s.full + s.ostream.val //= lambda: s.full | s.istream.val + s.istream.rdy //= lambda: ~s.full @update_ff def ff_bypass1(): if s.reset: s.full <<= 0 else: - s.full <<= ~s.send.rdy & (s.full | s.recv.val) + s.full <<= ~s.ostream.rdy & (s.full | s.istream.val) # buffer the incoming message if we cannot directly send it out - if ~s.send.rdy & ~s.full & s.recv.val: - s.entry <<= s.recv.msg + if ~s.ostream.rdy & ~s.full & s.istream.val: + s.entry <<= s.istream.msg def line_trace( s ): - return f"{s.recv}({s.full}){s.send}" + return f"{s.istream}({s.full}){s.ostream}" + #------------------------------------------------------------------------- -# Ctrl and Dpath for BypassQueue +# Ctrl and Dpath for StreamBypassQueue #------------------------------------------------------------------------- -class BypassQueueDpathRTL( Component ): +class StreamBypassQueueDpath( Component ): def construct( s, EntryType, num_entries=2 ): assert num_entries >= 2 # Interface - s.recv_msg = InPort( EntryType ) - s.send_msg = OutPort( EntryType ) + s.istream_msg = InPort( EntryType ) + s.ostream_msg = OutPort( EntryType ) s.wen = InPort() s.waddr = InPort( clog2( num_entries ) ) @@ -429,15 +431,15 @@ def construct( s, EntryType, num_entries=2 ): m.raddr[0] //= s.raddr m.wen[0] //= s.wen m.waddr[0] //= s.waddr - m.wdata[0] //= s.recv_msg + m.wdata[0] //= s.istream_msg s.mux = m = Mux( EntryType, 2 ) m.sel //= s.mux_sel m.in_[0] //= s.rf.rdata[0] - m.in_[1] //= s.recv_msg - m.out //= s.send_msg + m.in_[1] //= s.istream_msg + m.out //= s.ostream_msg -class BypassQueueCtrlRTL( Component ): +class StreamBypassQueueCtrl( Component ): def construct( s, num_entries=2 ): assert num_entries >= 2 @@ -451,10 +453,10 @@ def construct( s, num_entries=2 ): # Interface - s.recv_val = InPort () - s.recv_rdy = OutPort() - s.send_val = OutPort() - s.send_rdy = InPort() + s.istream_val = InPort () + s.istream_rdy = OutPort() + s.ostream_val = OutPort() + s.ostream_rdy = InPort() s.count = OutPort( count_nbits ) s.wen = OutPort() @@ -469,22 +471,22 @@ def construct( s, num_entries=2 ): # Wires - s.recv_xfer = Wire() - s.send_xfer = Wire() + s.istream_xfer = Wire() + s.ostream_xfer = Wire() # Connections - s.wen //= s.recv_xfer + s.wen //= s.istream_xfer s.waddr //= s.tail s.raddr //= s.head - s.recv_rdy //= lambda: s.count < num_entries - s.send_val //= lambda: (s.count > 0) | s.recv_val + s.istream_rdy //= lambda: s.count < num_entries + s.ostream_val //= lambda: (s.count > 0) | s.istream_val s.mux_sel //= lambda: s.count == 0 - s.recv_xfer //= lambda: s.recv_val & s.recv_rdy - s.send_xfer //= lambda: s.send_val & s.send_rdy + s.istream_xfer //= lambda: s.istream_val & s.istream_rdy + s.ostream_xfer //= lambda: s.ostream_val & s.ostream_rdy @update_ff def up_reg(): @@ -495,43 +497,43 @@ def up_reg(): s.count <<= 0 else: - if s.recv_xfer: + if s.istream_xfer: s.tail <<= s.tail + 1 if ( s.tail < num_entries - 1 ) else 0 - if s.send_xfer: + if s.ostream_xfer: s.head <<= s.head + 1 if ( s.head < num_entries -1 ) else 0 - if s.recv_xfer & ~s.send_xfer: + if s.istream_xfer & ~s.ostream_xfer: s.count <<= s.count + 1 - if ~s.recv_xfer & s.send_xfer: + if ~s.istream_xfer & s.ostream_xfer: s.count <<= s.count - 1 #------------------------------------------------------------------------- -# BypassQueueRTL +# StreamBypassQueue #------------------------------------------------------------------------- -class BypassQueueRTL( Component ): +class StreamBypassQueue( Component ): def construct( s, EntryType, num_entries=2 ): # Interface - s.recv = RecvIfcRTL( EntryType ) - s.send = SendIfcRTL( EntryType ) + s.istream = IStreamIfc( EntryType ) + s.ostream = OStreamIfc( EntryType ) s.count = OutPort( clog2( num_entries+1 ) ) # Components assert num_entries > 0 if num_entries == 1: - s.q = BypassQueue1EntryRTL( EntryType ) - s.recv //= s.q.recv - s.send //= s.q.send + s.q = StreamBypassQueue1Entry( EntryType ) + s.istream //= s.q.istream + s.ostream //= s.q.ostream s.count //= s.q.count else: - s.ctrl = BypassQueueCtrlRTL ( num_entries ) - s.dpath = BypassQueueDpathRTL( EntryType, num_entries ) + s.ctrl = StreamBypassQueueCtrl ( num_entries ) + s.dpath = StreamBypassQueueDpath( EntryType, num_entries ) # Connect ctrl to data path @@ -542,16 +544,15 @@ def construct( s, EntryType, num_entries=2 ): # Connect to interface - s.recv.val //= s.ctrl.recv_val - s.recv.rdy //= s.ctrl.recv_rdy - s.send.val //= s.ctrl.send_val - s.send.rdy //= s.ctrl.send_rdy + s.istream.val //= s.ctrl.istream_val + s.istream.rdy //= s.ctrl.istream_rdy + s.ostream.val //= s.ctrl.ostream_val + s.ostream.rdy //= s.ctrl.ostream_rdy s.count //= s.ctrl.count - s.recv.msg //= s.dpath.recv_msg - s.send.msg //= s.dpath.send_msg + s.istream.msg //= s.dpath.istream_msg + s.ostream.msg //= s.dpath.ostream_msg # Line trace def line_trace( s ): - return f"{s.recv}({s.count}){s.send}" - + return f"{s.istream}({s.count}){s.ostream}" diff --git a/pymtl3/stdlib/stream/test/magic_memory_test.py b/pymtl3/stdlib/stream/test/memory_fl_test.py similarity index 100% rename from pymtl3/stdlib/stream/test/magic_memory_test.py rename to pymtl3/stdlib/stream/test/memory_fl_test.py diff --git a/pymtl3/stdlib/stream/test/queues_test.py b/pymtl3/stdlib/stream/test/queues_test.py index 59cc71f08..0b1b68a8d 100644 --- a/pymtl3/stdlib/stream/test/queues_test.py +++ b/pymtl3/stdlib/stream/test/queues_test.py @@ -12,16 +12,16 @@ from pymtl3 import * from pymtl3.stdlib.test_utils import TestVectorSimulator, run_sim -from ..SourceRTL import SourceRTL -from ..SinkRTL import SinkRTL +from ..StreamSourceFL import StreamSourceFL +from ..StreamSinkFL import StreamSinkFL from ..queues import ( - BypassQueue1EntryRTL, - BypassQueueRTL, - NormalQueue1EntryRTL, - NormalQueueRTL, - PipeQueue1EntryRTL, - PipeQueueRTL, + StreamBypassQueue1Entry, + StreamBypassQueue, + StreamNormalQueue1Entry, + StreamNormalQueue, + StreamPipeQueue1Entry, + StreamPipeQueue, ) #------------------------------------------------------------------------- @@ -33,14 +33,14 @@ def run_tv_test( dut, test_vectors, cmdline_opts ): # Define input/output functions def tv_in( dut, tv ): - dut.recv.val @= tv[0] - dut.recv.msg @= tv[2] - dut.send.rdy @= tv[4] + dut.istream.val @= tv[0] + dut.istream.msg @= tv[2] + dut.ostream.rdy @= tv[4] def tv_out( dut, tv ): - if tv[1] != '?': assert dut.recv.rdy == tv[1] - if tv[3] != '?': assert dut.send.val == tv[3] - if tv[5] != '?': assert dut.send.msg == tv[5] + if tv[1] != '?': assert dut.istream.rdy == tv[1] + if tv[3] != '?': assert dut.ostream.val == tv[3] + if tv[5] != '?': assert dut.ostream.msg == tv[5] # Run the test @@ -51,7 +51,7 @@ def test_normal_behavior( cmdline_opts ): B1 = mk_bits(1) B32 = mk_bits(32) - run_tv_test( NormalQueueRTL( Bits32, 2 ), [ + run_tv_test( StreamNormalQueue( Bits32, 2 ), [ # recv.val recv.rdy recv.msg send.val send.rdy send.msg [ 1, 1, 0x123, 0, 0, '?' ], [ 1, 1, 0x345, 1, 0, 0x123 ], @@ -74,12 +74,12 @@ class TestHarness( Component ): def construct( s, MsgType, QueueType, src_msgs, sink_msgs ): - s.src = SourceRTL( MsgType, src_msgs ) + s.src = StreamSourceFL( MsgType, src_msgs ) s.dut = QueueType( MsgType ) - s.sink = SinkRTL( MsgType, sink_msgs ) + s.sink = StreamSinkFL( MsgType, sink_msgs ) - s.src.send //= s.dut.recv - s.dut.send //= s.sink.recv + s.src.ostream //= s.dut.istream + s.dut.ostream //= s.sink.istream def done( s ): return s.src.done() and s.sink.done() @@ -100,74 +100,74 @@ def line_trace( s ): def test_normal1_simple( cmdline_opts ): - th = TestHarness( Bits16, NormalQueueRTL, test_msgs, test_msgs ) + th = TestHarness( Bits16, StreamNormalQueue, test_msgs, test_msgs ) th.set_param( "top.sink.construct", arrival_time = arrival_normal ) th.set_param( "top.dut.construct", num_entries = 1 ) run_sim( th, cmdline_opts, duts=['dut'] ) def test_normal2_simple( cmdline_opts ): - th = TestHarness( Bits16, NormalQueueRTL, test_msgs, test_msgs ) + th = TestHarness( Bits16, StreamNormalQueue, test_msgs, test_msgs ) th.set_param( "top.sink.construct", arrival_time = arrival_pipe ) th.set_param( "top.dut.construct", num_entries = 2 ) run_sim( th, cmdline_opts, duts=['dut'] ) def test_normal3_simple( cmdline_opts ): - th = TestHarness( Bits16, NormalQueueRTL, test_msgs, test_msgs ) + th = TestHarness( Bits16, StreamNormalQueue, test_msgs, test_msgs ) th.set_param( "top.sink.construct", arrival_time = arrival_pipe ) th.set_param( "top.dut.construct", num_entries = 3 ) run_sim( th, cmdline_opts, duts=['dut'] ) def test_pipe1_simple( cmdline_opts ): - th = TestHarness( Bits16, PipeQueueRTL, test_msgs, test_msgs ) + th = TestHarness( Bits16, StreamPipeQueue, test_msgs, test_msgs ) th.set_param( "top.sink.construct", arrival_time = arrival_pipe ) th.set_param( "top.dut.construct", num_entries = 1 ) run_sim( th, cmdline_opts, duts=['dut'] ) def test_pipe1_backpressure( cmdline_opts ): - th = TestHarness( Bits16, PipeQueueRTL, test_msgs, test_msgs ) + th = TestHarness( Bits16, StreamPipeQueue, test_msgs, test_msgs ) th.set_param( "top.sink.construct", initial_delay = 20 ) th.set_param( "top.dut.construct", num_entries = 1 ) run_sim( th, cmdline_opts, duts=['dut'] ) def test_pipe2_backpressure( cmdline_opts ): - th = TestHarness( Bits16, PipeQueueRTL, test_msgs, test_msgs ) + th = TestHarness( Bits16, StreamPipeQueue, test_msgs, test_msgs ) th.set_param( "top.sink.construct", initial_delay = 20 ) th.set_param( "top.dut.construct", num_entries = 2 ) run_sim( th, cmdline_opts, duts=['dut'] ) def test_pipe3_backpressure( cmdline_opts ): - th = TestHarness( Bits16, PipeQueueRTL, test_msgs, test_msgs ) + th = TestHarness( Bits16, StreamPipeQueue, test_msgs, test_msgs ) th.set_param( "top.sink.construct", initial_delay = 20 ) th.set_param( "top.dut.construct", num_entries = 3 ) run_sim( th, cmdline_opts, duts=['dut'] ) def test_bypass1_simple( cmdline_opts ): - th = TestHarness( Bits16, BypassQueueRTL, test_msgs, test_msgs ) + th = TestHarness( Bits16, StreamBypassQueue, test_msgs, test_msgs ) th.set_param( "top.sink.construct", arrival_time = arrival_bypass ) th.set_param( "top.dut.construct", num_entries = 1 ) run_sim( th, cmdline_opts, duts=['dut'] ) def test_bypass1_backpressure( cmdline_opts ): - th = TestHarness( Bits16, BypassQueueRTL, test_msgs, test_msgs ) + th = TestHarness( Bits16, StreamBypassQueue, test_msgs, test_msgs ) th.set_param( "top.sink.construct", initial_delay = 20 ) th.set_param( "top.dut.construct", num_entries = 1 ) run_sim( th, cmdline_opts, duts=['dut'] ) def test_bypass2_sparse( cmdline_opts ): - th = TestHarness( Bits16, BypassQueueRTL, test_msgs, test_msgs ) + th = TestHarness( Bits16, StreamBypassQueue, test_msgs, test_msgs ) th.set_param( "top.src.construct", interval_delay = 3 ) th.set_param( "top.dut.construct", num_entries = 2 ) run_sim( th, cmdline_opts, duts=['dut'] ) def test_bypass3_sparse( cmdline_opts ): - th = TestHarness( Bits16, BypassQueueRTL, test_msgs, test_msgs ) + th = TestHarness( Bits16, StreamBypassQueue, test_msgs, test_msgs ) th.set_param( "top.src.construct", interval_delay = 3 ) th.set_param( "top.dut.construct", num_entries = 3 ) run_sim( th, cmdline_opts, duts=['dut'] ) @pytest.mark.parametrize( 'QType, num_entries', - product( [ NormalQueueRTL, PipeQueueRTL, BypassQueueRTL ], [ 8, 10, 12, 16 ] ) + product( [ StreamNormalQueue, StreamPipeQueue, StreamBypassQueue ], [ 8, 10, 12, 16 ] ) ) def test_large_backpressure( QType, num_entries, cmdline_opts ): msgs = test_msgs * 8 @@ -177,14 +177,14 @@ def test_large_backpressure( QType, num_entries, cmdline_opts ): run_sim( th, cmdline_opts, duts=['dut'] ) @pytest.mark.parametrize( - 'QType', [ NormalQueue1EntryRTL, PipeQueue1EntryRTL, BypassQueue1EntryRTL ] + 'QType', [ StreamNormalQueue1Entry, StreamPipeQueue1Entry, StreamBypassQueue1Entry ] ) def test_single_simple( QType, cmdline_opts ): th = TestHarness( Bits16, QType, test_msgs, test_msgs ) run_sim( th, cmdline_opts, duts=['dut'] ) @pytest.mark.parametrize( - 'QType', [ NormalQueue1EntryRTL, PipeQueue1EntryRTL, BypassQueue1EntryRTL ] + 'QType', [ StreamNormalQueue1Entry, StreamPipeQueue1Entry, StreamBypassQueue1Entry ] ) def test_single_backpressure( QType, cmdline_opts ): th = TestHarness( Bits16, QType, test_msgs, test_msgs ) @@ -194,7 +194,7 @@ def test_single_backpressure( QType, cmdline_opts ): @pytest.mark.parametrize( 'QType, num_entries', - product( [ NormalQueueRTL, PipeQueueRTL, BypassQueueRTL ], [ 8, 10, 12, 16 ] ) + product( [ StreamNormalQueue, StreamPipeQueue, StreamBypassQueue ], [ 8, 10, 12, 16 ] ) ) def test_large_delay( QType, num_entries, cmdline_opts ): msgs = test_msgs * 8 diff --git a/pymtl3/stdlib/test_utils/__init__.py b/pymtl3/stdlib/test_utils/__init__.py index d626c7e41..544e93183 100644 --- a/pymtl3/stdlib/test_utils/__init__.py +++ b/pymtl3/stdlib/test_utils/__init__.py @@ -7,5 +7,5 @@ run_test_vector_sim, ) from .test_masters import TestMasterCL -from .test_sinks import TestSinkCL -from .test_srcs import TestSrcCL +from .test_sinks import TestSinkFL +from .test_srcs import TestSourceFL diff --git a/pymtl3/stdlib/test_utils/test/src_sink_test.py b/pymtl3/stdlib/test_utils/test/src_sink_test.py index 3512b6ff4..988a90fc5 100644 --- a/pymtl3/stdlib/test_utils/test/src_sink_test.py +++ b/pymtl3/stdlib/test_utils/test/src_sink_test.py @@ -12,8 +12,8 @@ from pymtl3 import * from ..test_helpers import run_sim -from ..test_sinks import PyMTLTestSinkError, TestSinkCL, TestSinkRTL -from ..test_srcs import TestSrcCL, TestSrcRTL +from ..test_sinks import PyMTLTestSinkError, BehavioralTestSink, TestSinkFL +from ..test_srcs import BehavioralTestSource, TestSourceFL #------------------------------------------------------------------------- # TestHarnessSimple @@ -41,7 +41,7 @@ def line_trace( s ): def test_cl_no_delay(): msgs = [ Bits16( 0 ), Bits16( 1 ), Bits16( 2 ), Bits16( 3 ) ] - th = TestHarnessSimple( Bits16, TestSrcCL, TestSinkCL, msgs, msgs ) + th = TestHarnessSimple( Bits16, BehavioralTestSource, BehavioralTestSink, msgs, msgs ) run_sim( th ) # int_msgs = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] @@ -68,7 +68,7 @@ def test_cl_no_delay(): ) def test_src_sink_cl( Type, msgs, src_init, src_intv, sink_init, sink_intv, arrival_time ): - th = TestHarnessSimple( Type, TestSrcCL, TestSinkCL, msgs, msgs ) + th = TestHarnessSimple( Type, BehavioralTestSource, BehavioralTestSink, msgs, msgs ) th.set_param( "top.src.construct", initial_delay = src_init, interval_delay = src_intv, @@ -93,7 +93,7 @@ def test_src_sink_cl( Type, msgs, src_init, src_intv, ) def test_src_sink_rtl( Type, msgs, src_init, src_intv, sink_init, sink_intv, arrival_time ): - th = TestHarnessSimple( Type, TestSrcRTL, TestSinkRTL, msgs, msgs ) + th = TestHarnessSimple( Type, TestSourceFL, TestSinkFL, msgs, msgs ) th.set_param( "top.src.construct", initial_delay = src_init, interval_delay = src_intv, @@ -121,19 +121,19 @@ def construct( s, src_level, sink_level, MsgType, src_msgs, sink_msgs, s.num_pairs = 2 if src_level == 'cl': - s.srcs = [ TestSrcCL ( MsgType, src_msgs, src_init, src_intv ) + s.srcs = [ BehavioralTestSource ( MsgType, src_msgs, src_init, src_intv ) for i in range(s.num_pairs) ] elif src_level == 'rtl': - s.srcs = [ TestSrcRTL( MsgType, src_msgs, src_init, src_intv ) + s.srcs = [ TestSourceFL( MsgType, src_msgs, src_init, src_intv ) for i in range(s.num_pairs) ] else: raise if sink_level == 'cl': - s.sinks = [ TestSinkCL( MsgType, sink_msgs, sink_init, sink_interval, arrival_time ) + s.sinks = [ BehavioralTestSink( MsgType, sink_msgs, sink_init, sink_interval, arrival_time ) for i in range(s.num_pairs) ] elif sink_level == 'rtl': - s.sinks = [ TestSinkRTL( MsgType, sink_msgs, sink_init, sink_interval, arrival_time ) + s.sinks = [ TestSinkFL( MsgType, sink_msgs, sink_init, sink_interval, arrival_time ) for i in range(s.num_pairs) ] else: raise @@ -182,7 +182,7 @@ def test_adaptive( src_level, sink_level, msgs, src_init, src_intv, def test_error_more_msg(): try: th = TestHarnessSimple( - Bits16, TestSrcCL, TestSinkCL, + Bits16, BehavioralTestSource, BehavioralTestSink, src_msgs = [ b16(0xface), b16(0xface) ], sink_msgs = [ b16(0xface) ], ) @@ -194,7 +194,7 @@ def test_error_more_msg(): def test_error_wrong_msg(): try: th = TestHarnessSimple( - Bits16, TestSrcCL, TestSinkCL, + Bits16, BehavioralTestSource, BehavioralTestSink, src_msgs = [ b16(0xface), b16(0xface) ], sink_msgs = [ b16(0xface), b16(0xdead) ], ) @@ -206,7 +206,7 @@ def test_error_wrong_msg(): def test_error_late_msg(): try: th = TestHarnessSimple( - Bits16, TestSrcCL, TestSinkCL, + Bits16, BehavioralTestSource, BehavioralTestSink, src_msgs = [ b16(0xface), b16(0xface) ], sink_msgs = [ b16(0xface), b16(0xdead) ], ) @@ -223,7 +223,7 @@ def test_error_late_msg(): def test_customized_cmp(): th = TestHarnessSimple( - Bits4, TestSrcCL, TestSinkCL, + Bits4, BehavioralTestSource, BehavioralTestSink, src_msgs = [ b4(0b1110), b4(0b1111) ], sink_msgs = [ b4(0b0010), b4(0b0011) ], ) diff --git a/pymtl3/stdlib/test_utils/test_masters.py b/pymtl3/stdlib/test_utils/test_masters.py index 9ec9e4eb2..16d188574 100644 --- a/pymtl3/stdlib/test_utils/test_masters.py +++ b/pymtl3/stdlib/test_utils/test_masters.py @@ -9,16 +9,16 @@ from pymtl3 import * from pymtl3.stdlib.ifcs import MasterIfcCL -from .test_sinks import TestSinkCL -from .test_srcs import TestSrcCL +from .test_sinks import TestSinkFL +from .test_srcs import TestSourceFL class TestMasterCL( Component ): def construct( s, ReqType, RespType, MasterIfc=MasterIfcCL ): s.master = MasterIfc( ReqType, RespType ) - s.src = TestSrcCL( ReqType ) - s.sink = TestSinkCL( RespType ) + s.src = TestSourceFL( ReqType ) + s.sink = TestSinkFL( RespType ) s.src.send //= s.master.req s.sink.recv //= s.master.resp diff --git a/pymtl3/stdlib/test_utils/test_sinks.py b/pymtl3/stdlib/test_utils/test_sinks.py index f9b8cacc3..65d3890a7 100644 --- a/pymtl3/stdlib/test_utils/test_sinks.py +++ b/pymtl3/stdlib/test_utils/test_sinks.py @@ -9,16 +9,17 @@ """ from pymtl3 import * -from pymtl3.stdlib.ifcs import RecvIfcRTL, RecvRTL2SendCL +from pymtl3.stdlib.dstruct.ifcs import RecvIfc +from pymtl3.stdlib.dstruct.ifcs.ifcs import RecvRTL2SendCL class PyMTLTestSinkError( Exception ): pass #------------------------------------------------------------------------- -# TestSinkCL +# BehavioralTestSink #------------------------------------------------------------------------- -class TestSinkCL( Component ): +class BehavioralTestSink( Component ): def construct( s, Type, msgs, initial_delay=0, interval_delay=0, arrival_time=None, cmp_fn=lambda a, b : a == b ): @@ -118,22 +119,22 @@ def line_trace( s ): return "{}".format( s.recv ) #------------------------------------------------------------------------- -# TestSinkRTL +# TestSinkFL #------------------------------------------------------------------------- -class TestSinkRTL( Component ): +class TestSinkFL( Component ): def construct( s, Type, msgs, initial_delay=0, interval_delay=0, arrival_time=None, cmp_fn=lambda a, b : a == b ): # Interface - s.recv = RecvIfcRTL( Type ) + s.recv = RecvIfc( Type ) # Components - s.sink = TestSinkCL( Type, msgs, initial_delay, interval_delay, - arrival_time, cmp_fn ) + s.sink = BehavioralTestSink( Type, msgs, initial_delay, interval_delay, + arrival_time, cmp_fn ) s.adapter = RecvRTL2SendCL( Type ) connect( s.recv, s.adapter.recv ) diff --git a/pymtl3/stdlib/test_utils/test_srcs.py b/pymtl3/stdlib/test_utils/test_srcs.py index 68df842bf..a8d930b71 100644 --- a/pymtl3/stdlib/test_utils/test_srcs.py +++ b/pymtl3/stdlib/test_utils/test_srcs.py @@ -10,13 +10,14 @@ from collections import deque from pymtl3 import * -from pymtl3.stdlib.ifcs import RecvCL2SendRTL, SendIfcRTL +from pymtl3.stdlib.dstruct.ifcs import SendIfc +from pymtl3.stdlib.dstruct.ifcs.ifcs import RecvCL2SendRTL #------------------------------------------------------------------------- -# TestSrcCL +# BehavioralTestSource #------------------------------------------------------------------------- -class TestSrcCL( Component ): +class BehavioralTestSource( Component ): def construct( s, Type, msgs, initial_delay=0, interval_delay=0 ): @@ -44,21 +45,20 @@ def line_trace( s ): return "{}".format( s.send ) #------------------------------------------------------------------------- -# TestSrcRTL +# TestSourceFL #------------------------------------------------------------------------- -# TODO: deprecating TestSrcRTL. -class TestSrcRTL( Component ): +class TestSourceFL( Component ): def construct( s, Type, msgs, initial_delay=0, interval_delay=0 ): # Interface - s.send = SendIfcRTL( Type ) + s.send = SendIfc( Type ) # Components - s.src = TestSrcCL( Type, msgs, initial_delay, interval_delay ) + s.src = BehavioralTestSource( Type, msgs, initial_delay, interval_delay ) s.adapter = RecvCL2SendRTL( Type ) connect( s.src.send, s.adapter.recv ) From 60d73b555f038a0f7440f118eb2a045291e09eff Mon Sep 17 00:00:00 2001 From: pp482 Date: Sun, 28 Aug 2022 16:38:01 -0400 Subject: [PATCH 003/101] [WIP] StreamSrc/Sink test passing --- pymtl3/stdlib/dstruct/ifcs/__init__.py | 1 - pymtl3/stdlib/dstruct/ifcs/ifcs.py | 262 ------------------ pymtl3/stdlib/dstruct/test/queues_test.py | 6 +- pymtl3/stdlib/stream/StreamSinkFL.py | 3 +- pymtl3/stdlib/stream/test/src_sink_test.py | 132 +++++++++ pymtl3/stdlib/test_utils/__init__.py | 3 - .../stdlib/test_utils/test/src_sink_test.py | 231 --------------- 7 files changed, 137 insertions(+), 501 deletions(-) delete mode 100644 pymtl3/stdlib/dstruct/ifcs/__init__.py delete mode 100644 pymtl3/stdlib/dstruct/ifcs/ifcs.py create mode 100644 pymtl3/stdlib/stream/test/src_sink_test.py delete mode 100644 pymtl3/stdlib/test_utils/test/src_sink_test.py diff --git a/pymtl3/stdlib/dstruct/ifcs/__init__.py b/pymtl3/stdlib/dstruct/ifcs/__init__.py deleted file mode 100644 index 813e96128..000000000 --- a/pymtl3/stdlib/dstruct/ifcs/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .ifcs import EnqIfc, DeqIfc, RecvIfc, SendIfc diff --git a/pymtl3/stdlib/dstruct/ifcs/ifcs.py b/pymtl3/stdlib/dstruct/ifcs/ifcs.py deleted file mode 100644 index 04ebee9bc..000000000 --- a/pymtl3/stdlib/dstruct/ifcs/ifcs.py +++ /dev/null @@ -1,262 +0,0 @@ -from pymtl3 import * -from pymtl3.extra import clone_deepcopy -from pymtl3.stdlib.connects import connect_pairs - -def enrdy_to_str( msg, en, rdy, trace_len=15 ): - if en and not rdy: return "X".ljust( trace_len ) # Not allowed! - if not en and rdy: return " ".ljust( trace_len ) # Idle - if not en and not rdy: return "#".ljust( trace_len ) # Stalled - return f"{msg}".ljust( trace_len ) # en and rdy - -class RecvIfc( Interface ): - - def construct( s, Type ): - s.en = InPort() - s.rdy = OutPort() - s.msg = InPort( Type ) - - s.MsgType = Type - - s.trace_len = len(str(Type())) - - def connect( s, other, parent ): - # We are doing SendCL (other) -> [ RecvCL -> SendRTL ] -> RecvRTL (s) - # SendCL is a caller interface - if isinstance( other, CallerIfcCL ): - m = RecvCL2SendRTL( s.MsgType ) - - if hasattr( parent, "RecvCL2SendRTL_count" ): - count = parent.RecvCL2SendRTL_count - setattr( parent, "RecvCL2SendRTL_" + str( count ), m ) - else: - parent.RecvCL2SendRTL_count = 0 - parent.RecvCL2SendRTL_0 = m - - connect_pairs( - other, m.recv, - m.send.msg, s.msg, - m.send.en, s.en, - m.send.rdy, s.rdy - ) - parent.RecvCL2SendRTL_count += 1 - return True - - elif isinstance( other, CalleeIfcCL ): - if s._dsl.level <= other._dsl.level: - raise InvalidConnectionError( - "CL2RTL connection is not supported between RecvIfcRTL" - " and CalleeIfcCL.\n" - " - level {}: {} (class {})\n" - " - level {}: {} (class {})".format( - s._dsl.level, repr( s ), type( s ), other._dsl.level, - repr( other ), type( other ) ) ) - - m = RecvCL2SendRTL( s.MsgType ) - - if hasattr( parent, "RecvCL2SendRTL_count" ): - count = parent.RecvCL2SendRTL_count - setattr( parent, "RecvCL2SendRTL_" + str( count ), m ) - else: - parent.RecvCL2SendRTL_count = 0 - parent.RecvCL2SendRTL_0 = m - - connect_pairs( - other, m.recv, - m.send.msg, s.msg, - m.send.en, s.en, - m.send.rdy, s.rdy - ) - parent.RecvCL2SendRTL_count += 1 - return True - - return False - - def __str__( s ): - return enrdy_to_str( s.msg, s.en, s.rdy, s.trace_len ) - -class SendIfc( Interface ): - - def construct( s, Type ): - s.en = OutPort() - s.rdy = InPort() - s.msg = OutPort( Type ) - - s.MsgType = Type - - s.trace_len = len(str(Type())) - - def connect( s, other, parent ): - # We are doing SendRTL (s) -> [ RecvRTL -> SendCL ] -> RecvCL (other) - # RecvCL is a callee interface - if isinstance( other, CalleeIfcCL ): - m = RecvRTL2SendCL( s.MsgType ) - - if hasattr( parent, "RecvRTL2SendCL_count" ): - count = parent.RecvRTL2SendCL_count - setattr( parent, "RecvRTL2SendCL_" + str( count ), m ) - else: - parent.RecvRTL2SendCL_count = 0 - parent.RecvRTL2SendCL_0 = m - - connect_pairs( - m.send, other, - s.msg, m.recv.msg, - s.en, m.recv.en, - s.rdy, m.recv.rdy, - ) - parent.RecvRTL2SendCL_count += 1 - return True - - return False - - def __str__( s ): - return enrdy_to_str( s.msg, s.en, s.rdy, s.trace_len ) - -class GetIfc( Interface ): - - def construct( s, Type ): - s.en = OutPort() - s.rdy = InPort() - s.msg = InPort( Type ) - - s.trace_len = len(str(Type())) - - def __str__( s ): - return enrdy_to_str( s.msg, s.en, s.rdy, s.trace_len ) - -# Helper component to connect a GiveIfc to a RecvIfc -class And( Component ): - - def construct( s, Type ): - s.in0 = InPort( Type ) - s.in1 = InPort( Type ) - s.out = OutPort( Type ) - - @update - def up_and(): - s.out @= s.in0 & s.in1 - -class GiveIfc( Interface ): - - def construct( s, Type ): - s.en = InPort() - s.rdy = OutPort() - s.msg = OutPort( Type ) - - s.trace_len = len(str(Type())) - - def connect( s, other, parent ): - # We are doing GiveIfc (s) -> [ AND ] -> RecvIfc (other) - # Basically we AND the rdy of both sides for enable - if isinstance( other, RecvIfc ): - connect( s.msg, other.msg ) - - m = And( Bits1 ) - - if hasattr( parent, "give_recv_ander_cnt" ): - cnt = parent.give_recv_ander_cnt - setattr( parent, "give_recv_ander_" + str( cnt ), m ) - else: - parent.give_recv_ander_cnt = 0 - parent.give_recv_ander_0 = m - - connect_pairs( - m.in0, s.rdy, - m.in1, other.rdy, - m.out, s.en, - m.out, other.en, - ) - parent.give_recv_ander_cnt += 1 - return True - return False - - def __str__( s ): - return enrdy_to_str( s.msg, s.en, s.rdy, s.trace_len ) - -class EnqIfc( RecvIfc ): - - def construct( s, Type ): - super().construct( Type ) - -class DeqIfc( GiveIfc ): - - def construct( s, Type ): - super().construct( Type ) - -#------------------------------------------------------------------------- -# RecvCL2SendRTL -#------------------------------------------------------------------------- - -class RecvCL2SendRTL( Component ): - - def construct( s, MsgType ): - - # Interface - - s.send = SendIfc( MsgType ) - - s.entry = None - - @update_once - def up_clear(): - if s.send.en: # constraints reverse this - s.entry = None - - @update - def up_send_rtl(): - if s.entry is None: - s.send.en @= b1( 0 ) - else: - s.send.en @= b1( s.send.rdy ) - s.send.msg @= s.entry - - s.add_constraints( - U( up_clear ) < WR( s.send.en ), - U( up_clear ) < M( s.recv ), - U( up_clear ) < M( s.recv.rdy ), - M( s.recv ) < U( up_send_rtl ), - M( s.recv.rdy ) < U( up_send_rtl ) - ) - - @non_blocking( lambda s : s.entry is None ) - def recv( s, msg ): - s.entry = clone_deepcopy( msg ) - - def line_trace( s ): - return "{}(){}".format( s.recv, s.send ) - -#------------------------------------------------------------------------- -# RecvRTL2SendCL -#------------------------------------------------------------------------- - -class RecvRTL2SendCL( Component ): - - def construct( s, MsgType ): - - # Interface - - s.recv = RecvIfc( MsgType ) - s.send = CallerIfcCL() - - s.sent_msg = None - s.send_rdy = False - - @update_once - def up_recv_rtl_rdy(): - s.send_rdy = s.send.rdy() & ~s.reset - s.recv.rdy @= s.send_rdy - - @update_once - def up_send_cl(): - s.sent_msg = None - if s.recv.en: - s.send( s.recv.msg ) - s.sent_msg = s.recv.msg - - s.add_constraints( U( up_recv_rtl_rdy ) < U( up_send_cl ) ) - - def line_trace( s ): - return "{}(){}".format( - s.recv.line_trace(), - enrdy_to_str( s.sent_msg, s.sent_msg is not None, s.send_rdy ) - ) diff --git a/pymtl3/stdlib/dstruct/test/queues_test.py b/pymtl3/stdlib/dstruct/test/queues_test.py index 3d2915697..6785b5de9 100644 --- a/pymtl3/stdlib/dstruct/test/queues_test.py +++ b/pymtl3/stdlib/dstruct/test/queues_test.py @@ -102,9 +102,9 @@ def line_trace( s ): test_msgs = [ Bits16( 4 ), Bits16( 1 ), Bits16( 2 ), Bits16( 3 ) ] -arrival_normal = [ 2, 4, 6, 8 ] -arrival_pipe = [ 2, 3, 4, 5 ] -arrival_bypass = [ 1, 2, 3, 4 ] +arrival_normal = [ 3, 5, 7, 9 ] +arrival_pipe = [ 3, 4, 5, 6 ] +arrival_bypass = [ 2, 3, 4, 5 ] def test_normal1_simple(): diff --git a/pymtl3/stdlib/stream/StreamSinkFL.py b/pymtl3/stdlib/stream/StreamSinkFL.py index d7ba308a4..65bd21a4c 100644 --- a/pymtl3/stdlib/stream/StreamSinkFL.py +++ b/pymtl3/stdlib/stream/StreamSinkFL.py @@ -109,7 +109,8 @@ def up_sink(): s.count -= 1 s.istream.rdy <<= 0 else: # s.count == 0 - s.istream.rdy <<= (s.idx < len(s.msgs)) + # s.istream.rdy <<= (s.idx < len(s.msgs)) + s.istream.rdy <<= 1 def done( s ): return s.done_flag diff --git a/pymtl3/stdlib/stream/test/src_sink_test.py b/pymtl3/stdlib/stream/test/src_sink_test.py new file mode 100644 index 000000000..eb54a84f6 --- /dev/null +++ b/pymtl3/stdlib/stream/test/src_sink_test.py @@ -0,0 +1,132 @@ +""" +======================================================================== +src_sink_test +======================================================================== +Tests for test sources and test sinks. + +Author : Yanghui Ou + Date : Mar 11, 2019 +""" +import pytest + +from pymtl3 import * + +from pymtl3.stdlib.test_utils import run_sim +from pymtl3.stdlib.stream.StreamSinkFL import PyMTLTestSinkError, StreamSinkFL +from pymtl3.stdlib.stream.StreamSourceFL import StreamSourceFL + +#------------------------------------------------------------------------- +# TestHarnessSimple +#------------------------------------------------------------------------- +# Test a single pair of test src/sink. + +class TestHarnessSimple( Component ): + + def construct( s, MsgType, SrcType, SinkType, src_msgs, sink_msgs ): + + s.src = SrcType ( MsgType, src_msgs ) + s.sink = SinkType( MsgType, sink_msgs ) + + connect( s.src.ostream, s.sink.istream ) + + def done( s ): + return s.src.done() and s.sink.done() + + def line_trace( s ): + return "{} > {}".format( s.src.line_trace(), s.sink.line_trace() ) + +#------------------------------------------------------------------------- +# Test cases +#------------------------------------------------------------------------- + +# int_msgs = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] +bit_msgs = [ Bits16( 0 ), Bits16( 1 ), Bits16( 2 ), Bits16( 3 ), + Bits16( 0 ), Bits16( 1 ), Bits16( 2 ), Bits16( 3 ), + Bits16( 0 ), Bits16( 1 ), Bits16( 2 ), Bits16( 3 ) ] + +arrival0 = [ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ] +arrival1 = [ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 ] +arrival2 = [ 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35 ] +arrival3 = [ 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35 ] +arrival4 = [ 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65 ] + + +@pytest.mark.parametrize( + ('Type', 'msgs', 'src_init', 'src_intv', + 'sink_init', 'sink_intv', 'arrival_time' ), + [ + ( Bits16, bit_msgs, 0, 0, 0, 0, arrival0 ), + # ( int, int_msgs, 10, 0, 0, 0, arrival1 ), + ( Bits16, bit_msgs, 10, 1, 0, 0, arrival2 ), + ( Bits16, bit_msgs, 10, 0, 0, 1, arrival3 ), + ( Bits16, bit_msgs, 3, 4, 5, 3, arrival4 ) + ] +) +def test_src_sink_rtl( Type, msgs, src_init, src_intv, + sink_init, sink_intv, arrival_time ): + th = TestHarnessSimple( Type, StreamSourceFL, StreamSinkFL, msgs, msgs ) + th.set_param( "top.src.construct", + initial_delay = src_init, + interval_delay = src_intv, + ) + th.set_param( "top.sink.construct", + initial_delay = sink_init, + interval_delay = sink_intv, + arrival_time = arrival_time, + ) + run_sim( th ) + +#------------------------------------------------------------------------- +# Error message test +#------------------------------------------------------------------------- + +def test_error_more_msg(): + try: + th = TestHarnessSimple( + Bits16, StreamSourceFL, StreamSinkFL, + src_msgs = [ b16(0xface), b16(0xface) ], + sink_msgs = [ b16(0xface) ], + ) + run_sim( th ) + except PyMTLTestSinkError as e: + return + raise Exception( 'Failed to detect error!' ) + +def test_error_wrong_msg(): + try: + th = TestHarnessSimple( + Bits16, StreamSourceFL, StreamSinkFL, + src_msgs = [ b16(0xface), b16(0xface) ], + sink_msgs = [ b16(0xface), b16(0xdead) ], + ) + run_sim( th ) + except PyMTLTestSinkError as e: + return + raise Exception( 'Fail to detect error!' ) + +def test_error_late_msg(): + try: + th = TestHarnessSimple( + Bits16, StreamSourceFL, StreamSinkFL, + src_msgs = [ b16(0xface), b16(0xface) ], + sink_msgs = [ b16(0xface), b16(0xdead) ], + ) + th.set_param( 'top.src.construct', initial_delay=5 ) + th.set_param( 'top.sink.construct', arrival_time=[1,2] ) + run_sim( th ) + except PyMTLTestSinkError as e: + return + raise Exception( 'Fail to detect error!') + +#------------------------------------------------------------------------- +# Customized compare function test +#------------------------------------------------------------------------- + +def test_customized_cmp(): + th = TestHarnessSimple( + Bits4, StreamSourceFL, StreamSinkFL, + src_msgs = [ b4(0b1110), b4(0b1111) ], + sink_msgs = [ b4(0b0010), b4(0b0011) ], + ) + th.set_param( 'top.sink.construct', cmp_fn=lambda a, b: a[0:2] == b[0:2] ) + run_sim( th ) diff --git a/pymtl3/stdlib/test_utils/__init__.py b/pymtl3/stdlib/test_utils/__init__.py index 544e93183..87da1842a 100644 --- a/pymtl3/stdlib/test_utils/__init__.py +++ b/pymtl3/stdlib/test_utils/__init__.py @@ -6,6 +6,3 @@ run_sim, run_test_vector_sim, ) -from .test_masters import TestMasterCL -from .test_sinks import TestSinkFL -from .test_srcs import TestSourceFL diff --git a/pymtl3/stdlib/test_utils/test/src_sink_test.py b/pymtl3/stdlib/test_utils/test/src_sink_test.py deleted file mode 100644 index 988a90fc5..000000000 --- a/pymtl3/stdlib/test_utils/test/src_sink_test.py +++ /dev/null @@ -1,231 +0,0 @@ -""" -======================================================================== -src_sink_test -======================================================================== -Tests for test sources and test sinks. - -Author : Yanghui Ou - Date : Mar 11, 2019 -""" -import pytest - -from pymtl3 import * - -from ..test_helpers import run_sim -from ..test_sinks import PyMTLTestSinkError, BehavioralTestSink, TestSinkFL -from ..test_srcs import BehavioralTestSource, TestSourceFL - -#------------------------------------------------------------------------- -# TestHarnessSimple -#------------------------------------------------------------------------- -# Test a single pair of test src/sink. - -class TestHarnessSimple( Component ): - - def construct( s, MsgType, SrcType, SinkType, src_msgs, sink_msgs ): - - s.src = SrcType ( MsgType, src_msgs ) - s.sink = SinkType( MsgType, sink_msgs ) - - connect( s.src.send, s.sink.recv ) - - def done( s ): - return s.src.done() and s.sink.done() - - def line_trace( s ): - return "{} > {}".format( s.src.line_trace(), s.sink.line_trace() ) - -#------------------------------------------------------------------------- -# Test cases -#------------------------------------------------------------------------- - -def test_cl_no_delay(): - msgs = [ Bits16( 0 ), Bits16( 1 ), Bits16( 2 ), Bits16( 3 ) ] - th = TestHarnessSimple( Bits16, BehavioralTestSource, BehavioralTestSink, msgs, msgs ) - run_sim( th ) - -# int_msgs = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] -bit_msgs = [ Bits16( 0 ), Bits16( 1 ), Bits16( 2 ), Bits16( 3 ), - Bits16( 0 ), Bits16( 1 ), Bits16( 2 ), Bits16( 3 ), - Bits16( 0 ), Bits16( 1 ), Bits16( 2 ), Bits16( 3 ) ] - -arrival0 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ] -arrival1 = [ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 ] -arrival2 = [ 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33 ] -arrival3 = [ 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33 ] -arrival4 = [ 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60 ] - -@pytest.mark.parametrize( - ('Type', 'msgs', 'src_init', 'src_intv', - 'sink_init', 'sink_intv', 'arrival_time' ), - [ - ( Bits16, bit_msgs, 0, 0, 0, 0, arrival0 ), - # ( int, int_msgs, 10, 0, 0, 0, arrival1 ), - ( Bits16, bit_msgs, 10, 1, 0, 0, arrival2 ), - ( Bits16, bit_msgs, 10, 0, 0, 1, arrival3 ), - ( Bits16, bit_msgs, 3, 4, 5, 3, arrival4 ) - ] -) -def test_src_sink_cl( Type, msgs, src_init, src_intv, - sink_init, sink_intv, arrival_time ): - th = TestHarnessSimple( Type, BehavioralTestSource, BehavioralTestSink, msgs, msgs ) - th.set_param( "top.src.construct", - initial_delay = src_init, - interval_delay = src_intv, - ) - th.set_param( "top.sink.construct", - initial_delay = sink_init, - interval_delay = sink_intv, - arrival_time = arrival_time, - ) - run_sim( th ) - -@pytest.mark.parametrize( - ('Type', 'msgs', 'src_init', 'src_intv', - 'sink_init', 'sink_intv', 'arrival_time' ), - [ - ( Bits16, bit_msgs, 0, 0, 0, 0, arrival0 ), - # ( int, int_msgs, 10, 0, 0, 0, arrival1 ), - ( Bits16, bit_msgs, 10, 1, 0, 0, arrival2 ), - ( Bits16, bit_msgs, 10, 0, 0, 1, arrival3 ), - ( Bits16, bit_msgs, 3, 4, 5, 3, arrival4 ) - ] -) -def test_src_sink_rtl( Type, msgs, src_init, src_intv, - sink_init, sink_intv, arrival_time ): - th = TestHarnessSimple( Type, TestSourceFL, TestSinkFL, msgs, msgs ) - th.set_param( "top.src.construct", - initial_delay = src_init, - interval_delay = src_intv, - ) - th.set_param( "top.sink.construct", - initial_delay = sink_init, - interval_delay = sink_intv, - arrival_time = arrival_time, - ) - run_sim( th ) - -#------------------------------------------------------------------------- -# Adaptive composition test -#------------------------------------------------------------------------- -# This test attempts to mix-and-match different levels of test srcs and -# sinks for all possible combinations -- cl/cl, rtl/cl, cl/rtl, rtl/rtl. -# It also creates multiple src/sink pairs to stress the management of -# multiple instances of the same adapter class - -class TestHarness( Component ): - - def construct( s, src_level, sink_level, MsgType, src_msgs, sink_msgs, - src_init, src_intv, - sink_init, sink_interval, arrival_time=None ): - s.num_pairs = 2 - - if src_level == 'cl': - s.srcs = [ BehavioralTestSource ( MsgType, src_msgs, src_init, src_intv ) - for i in range(s.num_pairs) ] - elif src_level == 'rtl': - s.srcs = [ TestSourceFL( MsgType, src_msgs, src_init, src_intv ) - for i in range(s.num_pairs) ] - else: - raise - - if sink_level == 'cl': - s.sinks = [ BehavioralTestSink( MsgType, sink_msgs, sink_init, sink_interval, arrival_time ) - for i in range(s.num_pairs) ] - elif sink_level == 'rtl': - s.sinks = [ TestSinkFL( MsgType, sink_msgs, sink_init, sink_interval, arrival_time ) - for i in range(s.num_pairs) ] - else: - raise - - # Connections - for i in range(s.num_pairs): - connect( s.srcs[i].send, s.sinks[i].recv ) - - def done( s ): - for i in range(s.num_pairs): - if not s.srcs[i].done() or not s.sinks[i].done(): - return False - return True - - def line_trace( s ): - return "{} >>> {}".format( "|".join( [ x.line_trace() for x in s.srcs ] ), - "|".join( [ x.line_trace() for x in s.sinks ] ) ) - -test_case_table = [] -for src in ['cl', 'rtl']: - for sink in ['cl', 'rtl']: - test_case_table += [ - ( src, sink, bit_msgs, 0, 0, 0, 0, arrival0 ), - # ( src, sink, int_msgs, 10, 0, 0, 0, arrival1 ), - ( src, sink, bit_msgs, 10, 1, 0, 0, arrival2 ), - ( src, sink, bit_msgs, 10, 0, 0, 1, arrival3 ), - ( src, sink, bit_msgs, 3, 4, 5, 3, arrival4 ), - ] - -@pytest.mark.parametrize( - ('src_level', 'sink_level', 'msgs', - 'src_init', 'src_intv', 'sink_init', 'sink_intv', 'arrival_time' ), - test_case_table, -) -def test_adaptive( src_level, sink_level, msgs, src_init, src_intv, - sink_init, sink_intv, arrival_time ): - th = TestHarness( src_level, sink_level, Bits16, msgs, msgs, - src_init, src_intv, sink_init, - sink_intv, arrival_time ) - run_sim( th ) - -#------------------------------------------------------------------------- -# Error message test -#------------------------------------------------------------------------- - -def test_error_more_msg(): - try: - th = TestHarnessSimple( - Bits16, BehavioralTestSource, BehavioralTestSink, - src_msgs = [ b16(0xface), b16(0xface) ], - sink_msgs = [ b16(0xface) ], - ) - run_sim( th ) - except PyMTLTestSinkError as e: - return - raise Exception( 'Failed to detect error!' ) - -def test_error_wrong_msg(): - try: - th = TestHarnessSimple( - Bits16, BehavioralTestSource, BehavioralTestSink, - src_msgs = [ b16(0xface), b16(0xface) ], - sink_msgs = [ b16(0xface), b16(0xdead) ], - ) - run_sim( th ) - except PyMTLTestSinkError as e: - return - raise Exception( 'Fail to detect error!' ) - -def test_error_late_msg(): - try: - th = TestHarnessSimple( - Bits16, BehavioralTestSource, BehavioralTestSink, - src_msgs = [ b16(0xface), b16(0xface) ], - sink_msgs = [ b16(0xface), b16(0xdead) ], - ) - th.set_param( 'top.src.construct', initial_delay=5 ) - th.set_param( 'top.sink.construct', arrival_time=[1,2] ) - run_sim( th ) - except PyMTLTestSinkError as e: - return - raise Exception( 'Fail to detect error!') - -#------------------------------------------------------------------------- -# Customized compare function test -#------------------------------------------------------------------------- - -def test_customized_cmp(): - th = TestHarnessSimple( - Bits4, BehavioralTestSource, BehavioralTestSink, - src_msgs = [ b4(0b1110), b4(0b1111) ], - sink_msgs = [ b4(0b0010), b4(0b0011) ], - ) - th.set_param( 'top.sink.construct', cmp_fn=lambda a, b: a[0:2] == b[0:2] ) - run_sim( th ) From 5778a40e1f342a0a46133d114f3d45010f94da21 Mon Sep 17 00:00:00 2001 From: pp482 Date: Mon, 29 Aug 2022 18:47:17 -0400 Subject: [PATCH 004/101] [WIP] example and stdlib unit tests passing --- examples/ex02_cksum/ChecksumCL.py | 70 --- examples/ex02_cksum/ChecksumRTL.py | 26 +- examples/ex02_cksum/test/ChecksumCL_test.py | 276 --------- examples/ex02_cksum/test/ChecksumRTL_test.py | 220 +++++-- examples/ex02_cksum/test/ChecksumVRTL_test.py | 13 +- examples/ex03_proc/MiscRTL.py | 24 +- examples/ex03_proc/NullXcel.py | 42 +- examples/ex03_proc/ProcCL.py | 276 --------- examples/ex03_proc/ProcCtrlRTL.py | 50 +- examples/ex03_proc/ProcFL.py | 132 ----- examples/ex03_proc/ProcRTL.py | 96 ++-- examples/ex03_proc/proc-sim | 12 +- examples/ex03_proc/test/ProcCL_test.py | 32 -- examples/ex03_proc/test/ProcFL_test.py | 256 --------- examples/ex03_proc/test/ProcRTL_test.py | 235 +++++++- examples/ex03_proc/test/harness.py | 19 +- examples/ex04_xcel/ChecksumXcelCL.py | 104 ---- examples/ex04_xcel/ChecksumXcelFL.py | 55 -- examples/ex04_xcel/ChecksumXcelRTL.py | 75 +-- examples/ex04_xcel/ProcXcel.py | 28 +- examples/ex04_xcel/proc-xcel-sim | 25 +- .../ex04_xcel/test/ChecksumXcelCL_test.py | 216 ------- .../ex04_xcel/test/ChecksumXcelFL_test.py | 59 -- .../ex04_xcel/test/ChecksumXcelRTL_test.py | 138 ++++- .../ex04_xcel/test/ChecksumXcelVRTL_test.py | 18 +- pymtl3/stdlib/delays/DelayPipeCL.py | 114 ---- pymtl3/stdlib/delays/StallCL.py | 39 -- pymtl3/stdlib/delays/__init__.py | 2 - pymtl3/stdlib/delays/test/DelayPipeCL_test.py | 108 ---- pymtl3/stdlib/ifcs/__init__.py | 20 - pymtl3/stdlib/ifcs/get_give_ifcs.py | 248 -------- pymtl3/stdlib/ifcs/master_minion_ifcs.py | 64 --- pymtl3/stdlib/ifcs/send_recv_ifcs.py | 311 ---------- pymtl3/stdlib/ifcs/test/__init__.py | 0 pymtl3/stdlib/ifcs/test/xcel_ifcs_test.py | 463 --------------- pymtl3/stdlib/ifcs/xcel_ifcs.py | 278 --------- pymtl3/stdlib/mem/BehavioralMemory.py | 1 - pymtl3/stdlib/mem/MagicMemoryCL.py | 152 ----- pymtl3/stdlib/mem/MemoryFL.py | 13 +- pymtl3/stdlib/mem/{ROMRTL.py => ROM.py} | 6 +- pymtl3/stdlib/mem/__init__.py | 13 +- pymtl3/stdlib/mem/ifcs/__init__.py | 1 + pymtl3/stdlib/mem/ifcs/ifcs.py | 8 + pymtl3/stdlib/mem/mem_ifcs.py | 290 ---------- ...MagicMemoryCL_test.py => MemoryFL_test.py} | 25 +- .../mem/test/{ROMRTL_test.py => ROM_test.py} | 12 +- pymtl3/stdlib/mem/test/mem_ifcs_test.py | 131 ----- pymtl3/stdlib/queues/__init__.py | 11 - pymtl3/stdlib/queues/cl_queues.py | 109 ---- pymtl3/stdlib/queues/enq_deq_ifcs.py | 31 - pymtl3/stdlib/queues/enrdy_queues.py | 117 ---- pymtl3/stdlib/queues/queues.py | 542 ------------------ pymtl3/stdlib/queues/test/__init__.py | 0 pymtl3/stdlib/queues/test/cl_queues_test.py | 98 ---- .../stdlib/queues/test/enrdy_queues_test.py | 68 --- pymtl3/stdlib/queues/test/queues_test.py | 179 ------ pymtl3/stdlib/queues/valrdy_queues.py | 294 ---------- pymtl3/stdlib/stream/test/memory_fl_test.py | 333 ----------- .../stream/valrdy_master_minion_ifcs.py | 152 ----- pymtl3/stdlib/stream/valrdy_test_masters.py | 30 - pymtl3/stdlib/test_utils/test_sinks.py | 149 ----- pymtl3/stdlib/test_utils/test_srcs.py | 73 --- pymtl3/stdlib/test_utils/valrdy_test_srcs.py | 64 --- pymtl3/stdlib/{ifcs => xcel}/XcelMsg.py | 0 pymtl3/stdlib/xcel/__init__.py | 1 + pymtl3/stdlib/xcel/ifcs/__init__.py | 1 + pymtl3/stdlib/xcel/ifcs/ifcs.py | 8 + .../{ifcs => xcel}/test/XcelMsg_test.py | 0 .../stdlib/{delays => xcel}/test/__init__.py | 0 pymtl3/stdlib/xcel/test/xcel_ifcs_test.py | 157 +++++ 70 files changed, 929 insertions(+), 6284 deletions(-) delete mode 100644 examples/ex02_cksum/ChecksumCL.py delete mode 100644 examples/ex02_cksum/test/ChecksumCL_test.py delete mode 100644 examples/ex03_proc/ProcCL.py delete mode 100644 examples/ex03_proc/ProcFL.py mode change 100644 => 100755 examples/ex03_proc/proc-sim delete mode 100644 examples/ex03_proc/test/ProcCL_test.py delete mode 100644 examples/ex03_proc/test/ProcFL_test.py delete mode 100644 examples/ex04_xcel/ChecksumXcelCL.py delete mode 100644 examples/ex04_xcel/ChecksumXcelFL.py mode change 100644 => 100755 examples/ex04_xcel/proc-xcel-sim delete mode 100644 examples/ex04_xcel/test/ChecksumXcelCL_test.py delete mode 100644 examples/ex04_xcel/test/ChecksumXcelFL_test.py delete mode 100644 pymtl3/stdlib/delays/DelayPipeCL.py delete mode 100644 pymtl3/stdlib/delays/StallCL.py delete mode 100644 pymtl3/stdlib/delays/__init__.py delete mode 100644 pymtl3/stdlib/delays/test/DelayPipeCL_test.py delete mode 100644 pymtl3/stdlib/ifcs/__init__.py delete mode 100644 pymtl3/stdlib/ifcs/get_give_ifcs.py delete mode 100644 pymtl3/stdlib/ifcs/master_minion_ifcs.py delete mode 100644 pymtl3/stdlib/ifcs/send_recv_ifcs.py delete mode 100644 pymtl3/stdlib/ifcs/test/__init__.py delete mode 100644 pymtl3/stdlib/ifcs/test/xcel_ifcs_test.py delete mode 100644 pymtl3/stdlib/ifcs/xcel_ifcs.py delete mode 100644 pymtl3/stdlib/mem/MagicMemoryCL.py rename pymtl3/stdlib/mem/{ROMRTL.py => ROM.py} (94%) create mode 100644 pymtl3/stdlib/mem/ifcs/__init__.py create mode 100644 pymtl3/stdlib/mem/ifcs/ifcs.py delete mode 100644 pymtl3/stdlib/mem/mem_ifcs.py rename pymtl3/stdlib/mem/test/{MagicMemoryCL_test.py => MemoryFL_test.py} (93%) rename pymtl3/stdlib/mem/test/{ROMRTL_test.py => ROM_test.py} (78%) delete mode 100644 pymtl3/stdlib/mem/test/mem_ifcs_test.py delete mode 100644 pymtl3/stdlib/queues/__init__.py delete mode 100644 pymtl3/stdlib/queues/cl_queues.py delete mode 100644 pymtl3/stdlib/queues/enq_deq_ifcs.py delete mode 100644 pymtl3/stdlib/queues/enrdy_queues.py delete mode 100644 pymtl3/stdlib/queues/queues.py delete mode 100644 pymtl3/stdlib/queues/test/__init__.py delete mode 100644 pymtl3/stdlib/queues/test/cl_queues_test.py delete mode 100644 pymtl3/stdlib/queues/test/enrdy_queues_test.py delete mode 100644 pymtl3/stdlib/queues/test/queues_test.py delete mode 100644 pymtl3/stdlib/queues/valrdy_queues.py delete mode 100644 pymtl3/stdlib/stream/test/memory_fl_test.py delete mode 100644 pymtl3/stdlib/stream/valrdy_master_minion_ifcs.py delete mode 100644 pymtl3/stdlib/stream/valrdy_test_masters.py delete mode 100644 pymtl3/stdlib/test_utils/test_sinks.py delete mode 100644 pymtl3/stdlib/test_utils/test_srcs.py delete mode 100644 pymtl3/stdlib/test_utils/valrdy_test_srcs.py rename pymtl3/stdlib/{ifcs => xcel}/XcelMsg.py (100%) create mode 100644 pymtl3/stdlib/xcel/__init__.py create mode 100644 pymtl3/stdlib/xcel/ifcs/__init__.py create mode 100644 pymtl3/stdlib/xcel/ifcs/ifcs.py rename pymtl3/stdlib/{ifcs => xcel}/test/XcelMsg_test.py (100%) rename pymtl3/stdlib/{delays => xcel}/test/__init__.py (100%) create mode 100644 pymtl3/stdlib/xcel/test/xcel_ifcs_test.py diff --git a/examples/ex02_cksum/ChecksumCL.py b/examples/ex02_cksum/ChecksumCL.py deleted file mode 100644 index c3a515f55..000000000 --- a/examples/ex02_cksum/ChecksumCL.py +++ /dev/null @@ -1,70 +0,0 @@ -""" -========================================================================== -ChecksumCL.py -========================================================================== -Cycle-level implementation of a checksum unit which implements a -simplified version of Fletcher's algorithm. A cycle-level model often -involves an input queue connected to the recv interface to buffer up the -input message and an update block to process each message and send it out -the send interface. In this case, we will simply reuse the checksum -function we developed in ChecksumFL to implement the desired -functionality. To model a latency greater than one, we can add a -DelayPipeDeqCL at the send interface. So instead of sending the result -directly out the send interface we enq the result into the DelayPipeDeqCL -and then wait for the result to appear on the other end of the -DelayPipeDeqCL before sending it out the send interface. - -Author : Yanghui Ou - Date : June 6, 2019 -""" -from pymtl3 import * -from pymtl3.stdlib.delays import DelayPipeDeqCL -from pymtl3.stdlib.queues import PipeQueueCL - -from .ChecksumFL import checksum -from .utils import b128_to_words - -#------------------------------------------------------------------------- -# ChecksumCL -#------------------------------------------------------------------------- - -class ChecksumCL( Component ): - - def construct( s ): - - s.recv = CalleeIfcCL( Type=Bits128 ) - s.send = CallerIfcCL( Type=Bits32 ) - - # ''' TUTORIAL TASK '''''''''''''''''''''''''''''''''''''''''''''''''' - # Implement the checksum CL component - # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''\/ - #; Instantiate a PipeQueueCL with one entry and connect it to the - #; recv interface. Then create an update block which will check if - #; the deq interface is ready and the send interface is ready. If - #; both of these conditions are try, then deq the message, calculate - #; the checksum using the checksum function from ChecksumFL, and - #; send the result through the send interface. - - s.in_q = PipeQueueCL( num_entries=2 ) - s.in_q.enq //= s.recv - - @update_once - def up_checksum_cl(): - if s.in_q.deq.rdy() and s.send.rdy(): - bits = s.in_q.deq() - words = b128_to_words( bits ) - - # Try injecting a bug and let hypothesis catch it. For example, - # you can add the following to zero out the sixth word before - # calculating the checksum. - # - # words[5] = b16(0) - # - - result = checksum( words ) - s.send( result ) - - # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''/\ - - def line_trace( s ): - return "{}(){}".format( s.recv, s.send ) diff --git a/examples/ex02_cksum/ChecksumRTL.py b/examples/ex02_cksum/ChecksumRTL.py index 889278acc..8ad084c78 100644 --- a/examples/ex02_cksum/ChecksumRTL.py +++ b/examples/ex02_cksum/ChecksumRTL.py @@ -11,8 +11,8 @@ Date : June 6, 2019 """ from pymtl3 import * -from pymtl3.stdlib.ifcs import RecvIfcRTL, SendIfcRTL -from pymtl3.stdlib.queues import PipeQueueRTL +from pymtl3.stdlib.stream.ifcs import IStreamIfc, OStreamIfc +from pymtl3.stdlib.stream import StreamPipeQueue #------------------------------------------------------------------------- # Step unit @@ -55,8 +55,8 @@ def construct( s ): # Interface - s.recv = RecvIfcRTL( Bits128 ) - s.send = SendIfcRTL( Bits32 ) + s.istream = IStreamIfc( Bits128 ) + s.ostream = OStreamIfc( Bits32 ) # Component @@ -64,17 +64,17 @@ def construct( s ): s.sum1 = Wire( Bits32 ) s.sum2 = Wire( Bits32 ) - s.in_q = PipeQueueRTL( Bits128, num_entries=1 ) + s.in_q = StreamPipeQueue( Bits128, num_entries=1 ) s.steps = [ StepUnit() for _ in range( 8 ) ] # Register input - connect( s.recv, s.in_q.enq ) + connect( s.istream, s.in_q.istream ) # Decompose input message into 8 words for i in range( 8 ): - s.words[i] //= s.in_q.deq.ret[i*16:(i+1)*16] + s.words[i] //= s.in_q.ostream.msg[i*16:(i+1)*16] # Connect step units @@ -91,14 +91,14 @@ def construct( s ): s.sum2 //= s.steps[-1].sum2_out @update - def up_rtl_send(): - go = s.in_q.deq.rdy & s.send.rdy - s.send.en @= go - s.in_q.deq.en @= go + def up_rtl_ostream(): + go = s.in_q.ostream.val & s.ostream.rdy + s.ostream.val @= go + s.in_q.ostream.rdy @= go @update def up_rtl_sum(): - s.send.msg @= ( s.sum2 << 16 ) | s.sum1 + s.ostream.msg @= ( s.sum2 << 16 ) | s.sum1 def line_trace( s ): - return "{}(){}".format( s.recv, s.send ) + return "{}(){}".format( s.istream, s.ostream ) diff --git a/examples/ex02_cksum/test/ChecksumCL_test.py b/examples/ex02_cksum/test/ChecksumCL_test.py deleted file mode 100644 index 6b1cbdfa2..000000000 --- a/examples/ex02_cksum/test/ChecksumCL_test.py +++ /dev/null @@ -1,276 +0,0 @@ -""" -========================================================================== -ChecksumCL_test.py -========================================================================== -Test cases for CL checksum unit. - -Author : Yanghui Ou - Date : June 6, 2019 -""" - -import pytest -import hypothesis -from hypothesis import strategies as st - -from pymtl3 import * -from pymtl3.datatypes import strategies as pm_st -from pymtl3.stdlib.queues import BypassQueueCL -from pymtl3.stdlib.connects import connect_pairs -from pymtl3.stdlib.test_utils import TestSinkCL, TestSrcCL, run_sim - -from ..ChecksumCL import ChecksumCL -from ..ChecksumFL import checksum -from ..utils import b128_to_words, words_to_b128 - -#------------------------------------------------------------------------- -# WrappedChecksumCL -#------------------------------------------------------------------------- -# WrappedChecksumCL is a simple wrapper around the CL checksum unit. It -# simply appends an output queue to the send side of the checksum unit. -# In this way it only exposes callee interfaces which can be directly -# called by the outside world. - -class WrappedChecksumCL( Component ): - - def construct( s, DutType=ChecksumCL ): - s.recv = CalleeIfcCL( Type=Bits128 ) - s.give = CalleeIfcCL( Type=Bits32 ) - - s.checksum_unit = DutType() - s.out_q = BypassQueueCL( num_entries=1 ) - - connect_pairs( - s.recv, s.checksum_unit.recv, - s.checksum_unit.send, s.out_q.enq, - s.out_q.deq, s.give, - ) - -#------------------------------------------------------------------------- -# Wrap CL component into a function -#------------------------------------------------------------------------- -# [checksum_cl] takes a list of 16-bit words, converts it to bits, creates -# a checksum unit instance and feed in the input. It then ticks the -# checksum unit until the output is ready to be taken. - -def checksum_cl( words ): - - # Create a simulator - dut = WrappedChecksumCL() - dut.elaborate() - dut.apply( DefaultPassGroup() ) - dut.sim_reset() - - # Wait until recv ready - while not dut.recv.rdy(): - dut.sim_tick() - - # Call recv on dut - dut.recv( words_to_b128( words ) ) - dut.sim_tick() - - # Wait until dut is ready to give result - while not dut.give.rdy(): - dut.sim_tick() - - return dut.give() - -#------------------------------------------------------------------------- -# Reuse FL tests -#------------------------------------------------------------------------- -# By directly inhering from the FL test class, we can easily reuse all the -# FL tests. We only need to overwrite the cksum_func that is used in all -# test cases. Here we also extend the test case by adding a hypothesis -# test that compares the CL implementation against the FL as reference. - -from .ChecksumFL_test import ChecksumFL_Tests as BaseTests - -class ChecksumCL_Tests( BaseTests ): - - def cksum_func( s, words ): - return checksum_cl( words ) - - # ''' TUTORIAL TASK '''''''''''''''''''''''''''''''''''''''''''''''''''' - # Use Hypothesis to test Checksum CL - # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''\/ - #; Use Hypothesis to verify that ChecksumCL has the same behavior as - #; ChecksumFL. Simply uncomment the following test_hypothesis method - #; and rerun pytest. Make sure that you fix the indentation so that - #; this new test_hypothesis method is correctly indented with respect - #; to the class ChecksumCL_Tests - #; - #; @hypothesis.settings( deadline=None ) - #; @hypothesis.given( - #; words=st.lists( pm_st.bits(16), min_size=8, max_size=8 ) - #; ) - #; def test_hypothesis( s, words ): - #; print( [ int(x) for x in words ] ) - #; assert s.cksum_func( words ) == checksum( words ) - #; - #; This new test uses Hypothesis to generate random inputs, then uses - #; the checksum_cl to run a little simulation and compares the output to - #; the checksum function from ChecksumFL. - #; - #; To really see Hypothesis in action, go back to ChecksumCL and - #; corrupt one word of the input by forcing it to always be zero. For - #; example, change the update block in the CL implementation to be - #; something like this: - #; - #; @update - #; def up_checksum_cl(): - #; if s.pipe.enq.rdy() and s.in_q.deq.rdy(): - #; bits = s.in_q.deq() - #; words = b128_to_words( bits ) - #; words[5] = b16(0) # <--- INJECT A BUG! - #; result = checksum( words ) - #; s.pipe.enq( result ) !\vspace{0.07in}! - #; if s.send.rdy() and s.pipe.deq.rdy(): - #; s.send( s.pipe.deq() ) - - @hypothesis.settings( deadline=None ) - @hypothesis.given( - words = st.lists( pm_st.bits(16), min_size=8, max_size=8 ) - ) - def test_hypothesis( s, words ): - print( [ int(x) for x in words ] ) - assert s.cksum_func( words ) == checksum( words ) - - # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''/\ - -#------------------------------------------------------------------------- -# TestHarness -#------------------------------------------------------------------------- -# TestHarness is used for more advanced source/sink based testing. It -# hooks a test source to the input of the design under test and a test -# sink to the output of the DUT. Test source feeds data into the DUT -# while test sink drains data from the DUT and verifies it. - -class TestHarness( Component ): - def construct( s, DutType, src_msgs, sink_msgs ): - - s.src = TestSrcCL( Bits128, src_msgs ) - s.dut = DutType() - s.sink = TestSinkCL( Bits32, sink_msgs ) - - connect_pairs( - s.src.send, s.dut.recv, - s.dut.send, s.sink.recv, - ) - - def done( s ): - return s.src.done() and s.sink.done() - - def line_trace( s ): - return "{}>{}>{}".format( - s.src.line_trace(), s.dut.line_trace(), s.sink.line_trace() - ) - -#========================================================================= -# Src/sink based tests -#========================================================================= -# We use source/sink based tests to stress test the checksum unit. - -@pytest.mark.usefixtures("cmdline_opts") -class ChecksumCLSrcSink_Tests: - - # [setup_class] will be called by pytest before running all the tests in - # the test class. Here we specify the type of the design under test - # that is used in all test cases. We can easily reuse all the tests in - # this class simply by creating a new test class that inherits from - # this class and overwrite the setup_class to provide a different DUT - # type. - @classmethod - def setup_class( cls ): - cls.DutType = ChecksumCL - - #----------------------------------------------------------------------- - # run_sim - #----------------------------------------------------------------------- - # A helper function in the test suite that creates a simulator and - # runs test. We can overwrite this function when inheriting from the - # test class to apply different passes to the DUT. - - def run_sim( s, th ): - run_sim( th, s.__class__.cmdline_opts ) - - #----------------------------------------------------------------------- - # test_srcsink_simple - #----------------------------------------------------------------------- - # is a simple test case with only 1 input. - - def test_srcsink_simple( s ): - words = [ b16(x) for x in [ 1, 2, 3, 4, 5, 6, 7, 8 ] ] - bits = words_to_b128( words ) - - result = b32( 0x00780024 ) - - src_msgs = [ bits ] - sink_msgs = [ result ] - - th = TestHarness( s.DutType, src_msgs, sink_msgs ) - s.run_sim( th ) - - #----------------------------------------------------------------------- - # test_srcsink_pipeline - #----------------------------------------------------------------------- - # test the checksum unit with a sequence of inputs. - - def test_srcsink_pipeline( s ): - words0 = [ b16(x) for x in [ 1, 2, 3, 4, 5, 6, 7, 8 ] ] - words1 = [ b16(x) for x in [ 8, 7, 6, 5, 4, 3, 2, 1 ] ] - bits0 = words_to_b128( words0 ) - bits1 = words_to_b128( words1 ) - - result0 = b32( 0x00780024 ) - result1 = b32( 0x00cc0024 ) - - src_msgs = [ bits0, bits1, bits0, bits1 ] - sink_msgs = [ result0, result1, result0, result1 ] - - th = TestHarness( s.DutType, src_msgs, sink_msgs ) - s.run_sim( th ) - - #----------------------------------------------------------------------- - # test_srcsink_backpressure - #----------------------------------------------------------------------- - # test the checksum unit with a large sink delay. - - def test_srcsink_backpressure( s ): - words0 = [ b16(x) for x in [ 1, 2, 3, 4, 5, 6, 7, 8 ] ] - words1 = [ b16(x) for x in [ 8, 7, 6, 5, 4, 3, 2, 1 ] ] - result0 = b32( 0x00780024 ) - result1 = b32( 0x00cc0024 ) - - bits0 = words_to_b128( words0 ) - bits1 = words_to_b128( words1 ) - - src_msgs = [ bits0, bits1, bits0, bits1 ] - sink_msgs = [ result0, result1, result0, result1 ] - - th = TestHarness( s.DutType, src_msgs, sink_msgs ) - th.set_param( "top.sink.construct", initial_delay=10 ) - s.run_sim( th ) - - # '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''\// - # Cutting out this hypothesis test since in the new flow we only want - # the attendees to write a single hypothesis test above ... and we - # don't want them to get confused by this additional hypothesis test. - - # This hypothesis test not only generates a sequence of input to the - # the checksum unit but it also configure the test source and sink with - # different initial and interval delays. - @hypothesis.given( - input_msgs = st.lists( st.lists( pm_st.bits(16), min_size=8, max_size=8 ) ), - src_init = st.integers( 0, 10 ), - src_intv = st.integers( 0, 3 ), - sink_init = st.integers( 0, 10 ), - sink_intv = st.integers( 0, 3 ), - ) - @hypothesis.settings( deadline=None, max_examples=50 ) - def test_srcsink_hypothesis( s, input_msgs, src_init, src_intv, sink_init, sink_intv ): - src_msgs = [ words_to_b128( words ) for words in input_msgs ] - sink_msgs = [ checksum( words ) for words in input_msgs ] - - th = TestHarness( s.DutType, src_msgs, sink_msgs ) - th.set_param( "top.src.construct", initial_delay = src_init, interval_delay = src_intv ) - th.set_param( "top.sink.construct", initial_delay = sink_init, interval_delay = sink_intv ) - s.run_sim( th ) diff --git a/examples/ex02_cksum/test/ChecksumRTL_test.py b/examples/ex02_cksum/test/ChecksumRTL_test.py index 1e5e1814f..0d5a0190f 100644 --- a/examples/ex02_cksum/test/ChecksumRTL_test.py +++ b/examples/ex02_cksum/test/ChecksumRTL_test.py @@ -8,11 +8,15 @@ Date : June 6, 2019 """ import pytest +import hypothesis +from hypothesis import strategies as st from pymtl3 import * +from pymtl3.datatypes import strategies as pm_st from pymtl3.passes.tracing import VcdGenerationPass, PrintTextWavePass -from pymtl3.stdlib.test_utils import TestSinkCL, TestSrcCL -from pymtl3.stdlib.test_utils.test_helpers import finalize_verilator +from pymtl3.stdlib.connects import connect_pairs +from pymtl3.stdlib.stream import StreamSinkFL, StreamSourceFL +from pymtl3.stdlib.test_utils.test_helpers import finalize_verilator, run_sim from ..ChecksumFL import checksum from ..ChecksumRTL import ChecksumRTL, StepUnit @@ -54,32 +58,32 @@ def checksum_rtl( words ): dut.sim_reset() # Wait until the checksum unit is ready to receive input - dut.send.rdy @= 1 - while not dut.recv.rdy: - dut.recv.en @= 0 + dut.ostream.rdy @= 1 + while not dut.istream.rdy: + dut.istream.val @= 0 dut.sim_tick() # Feed in the input - dut.recv.en @= 1 - dut.recv.msg @= bits_in + dut.istream.val @= 1 + dut.istream.msg @= bits_in dut.sim_tick() # Wait until the checksum unit is about to send the message - while not dut.send.en: - dut.recv.en @= 0 + while not dut.ostream.val: + dut.istream.val @= 0 dut.sim_tick() # Return the result - return dut.send.msg + return dut.ostream.msg #------------------------------------------------------------------------- -# Reuse functionality from CL test suite +# Reuse functionality from FL test suite #------------------------------------------------------------------------- -# Similar to what we did for CL tests, we can reuse CL test cases by -# inherit from the CL test class and overwrite cksum_func to use the rtl +# Similar to what we did for FL tests, we can reuse FL test cases by +# inherit from the FL test class and overwrite cksum_func to use the rtl # version instead. -from .ChecksumCL_test import ChecksumCL_Tests as BaseTests +from .ChecksumFL_test import ChecksumFL_Tests as BaseTests @pytest.mark.usefixtures("cmdline_opts") class ChecksumRTL_Tests( BaseTests ): @@ -87,16 +91,72 @@ class ChecksumRTL_Tests( BaseTests ): def cksum_func( s, words ): return checksum_rtl( words ) + # ''' TUTORIAL TASK '''''''''''''''''''''''''''''''''''''''''''''''''''' + # Use Hypothesis to test Checksum RTL + # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''\/ + #; Use Hypothesis to verify that ChecksumRTL has the same behavior as + #; ChecksumFL. Simply uncomment the following test_hypothesis method + #; and rerun pytest. Make sure that you fix the indentation so that + #; this new test_hypothesis method is correctly indented with respect + #; to the class ChecksumRTL_Tests + #; + #; @hypothesis.settings( deadline=None ) + #; @hypothesis.given( + #; words=st.lists( pm_st.bits(16), min_size=8, max_size=8 ) + #; ) + #; def test_hypothesis( s, words ): + #; print( [ int(x) for x in words ] ) + #; assert s.cksum_func( words ) == checksum( words ) + #; + #; This new test uses Hypothesis to generate random inputs, then uses + #; the checksum_rtl to run a little simulation and compares the output to + #; the checksum function from ChecksumFL. + + @hypothesis.settings( deadline=None ) + @hypothesis.given( + words = st.lists( pm_st.bits(16), min_size=8, max_size=8 ) + ) + def test_hypothesis( s, words ): + print( [ int(x) for x in words ] ) + assert s.cksum_func( words ) == checksum( words ) + + # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''/\ + #------------------------------------------------------------------------- -# Reuse src/sink based tests from CL test suite to test simulation +# TestHarness #------------------------------------------------------------------------- -# Again, we reuse all source/sink based tests for CL by simply inheriting -# from the test class and providing a different DUT type in [setup_class]. +# TestHarness is used for more advanced source/sink based testing. It +# hooks a test source to the input of the design under test and a test +# sink to the output of the DUT. Test source feeds data into the DUT +# while test sink drains data from the DUT and verifies it. + +class TestHarness( Component ): + def construct( s, DutType, src_msgs, sink_msgs ): + + s.src = StreamSourceFL( Bits128, src_msgs ) + s.dut = DutType() + s.sink = StreamSinkFL( Bits32, sink_msgs ) + + connect_pairs( + s.src.ostream, s.dut.istream, + s.dut.ostream, s.sink.istream, + ) -from .ChecksumCL_test import ChecksumCLSrcSink_Tests as BaseSrcSinkTests + def done( s ): + return s.src.done() and s.sink.done() + + def line_trace( s ): + return "{}>{}>{}".format( + s.src.line_trace(), s.dut.line_trace(), s.sink.line_trace() + ) + +#========================================================================= +# Src/sink based tests +#========================================================================= +# We use source/sink based tests to stress test the checksum unit. @pytest.mark.usefixtures("cmdline_opts") -class ChecksumRTLSrcSink_Tests( BaseSrcSinkTests ): +class ChecksumRTLSrcSink_Tests: # [setup_class] will be called by pytest before running all the tests in # the test class. Here we specify the type of the design under test @@ -108,37 +168,95 @@ class ChecksumRTLSrcSink_Tests( BaseSrcSinkTests ): def setup_class( cls ): cls.DutType = ChecksumRTL - # [setup_method] will be called by pytest before executing each class method. - # See pytest documetnation for more details. - def setup_method( s, method ): - s.current_test_method_name = method.__name__ - - # [teardown_method] will be called by pytest after executing each class method. - # See pytest documetnation for more details. - def teardown_method( s, method ): - s.current_test_method_name = "" + #----------------------------------------------------------------------- + # run_sim + #----------------------------------------------------------------------- + # A helper function in the test suite that creates a simulator and + # runs test. We can overwrite this function when inheriting from the + # test class to apply different passes to the DUT. def run_sim( s, th ): - - vcd_file_name = s.__class__.cmdline_opts["dump_vcd"] - max_cycles = s.__class__.cmdline_opts["max_cycles"] or 10000 - - # Check for vcd dumping - if vcd_file_name: - th.set_metadata( VcdGenerationPass.vcd_file_name, vcd_file_name ) - th.set_metadata( PrintTextWavePass.enable, True ) - - # Create a simulator - th.apply( DefaultPassGroup() ) - th.sim_reset() - - while not th.done() and th.sim_cycle_count() < max_cycles: - th.sim_tick() - - if vcd_file_name: - th.print_textwave() - - # Check timeout - assert th.sim_cycle_count() < max_cycles - - finalize_verilator( th ) + run_sim( th, s.__class__.cmdline_opts ) + + #----------------------------------------------------------------------- + # test_srcsink_simple + #----------------------------------------------------------------------- + # is a simple test case with only 1 input. + + def test_srcsink_simple( s ): + words = [ b16(x) for x in [ 1, 2, 3, 4, 5, 6, 7, 8 ] ] + bits = words_to_b128( words ) + + result = b32( 0x00780024 ) + + src_msgs = [ bits ] + sink_msgs = [ result ] + + th = TestHarness( s.DutType, src_msgs, sink_msgs ) + s.run_sim( th ) + + #----------------------------------------------------------------------- + # test_srcsink_pipeline + #----------------------------------------------------------------------- + # test the checksum unit with a sequence of inputs. + + def test_srcsink_pipeline( s ): + words0 = [ b16(x) for x in [ 1, 2, 3, 4, 5, 6, 7, 8 ] ] + words1 = [ b16(x) for x in [ 8, 7, 6, 5, 4, 3, 2, 1 ] ] + bits0 = words_to_b128( words0 ) + bits1 = words_to_b128( words1 ) + + result0 = b32( 0x00780024 ) + result1 = b32( 0x00cc0024 ) + + src_msgs = [ bits0, bits1, bits0, bits1 ] + sink_msgs = [ result0, result1, result0, result1 ] + + th = TestHarness( s.DutType, src_msgs, sink_msgs ) + s.run_sim( th ) + + #----------------------------------------------------------------------- + # test_srcsink_backpressure + #----------------------------------------------------------------------- + # test the checksum unit with a large sink delay. + + def test_srcsink_backpressure( s ): + words0 = [ b16(x) for x in [ 1, 2, 3, 4, 5, 6, 7, 8 ] ] + words1 = [ b16(x) for x in [ 8, 7, 6, 5, 4, 3, 2, 1 ] ] + result0 = b32( 0x00780024 ) + result1 = b32( 0x00cc0024 ) + + bits0 = words_to_b128( words0 ) + bits1 = words_to_b128( words1 ) + + src_msgs = [ bits0, bits1, bits0, bits1 ] + sink_msgs = [ result0, result1, result0, result1 ] + + th = TestHarness( s.DutType, src_msgs, sink_msgs ) + th.set_param( "top.sink.construct", initial_delay=10 ) + s.run_sim( th ) + + # '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''\// + # Cutting out this hypothesis test since in the new flow we only want + # the attendees to write a single hypothesis test above ... and we + # don't want them to get confused by this additional hypothesis test. + + # This hypothesis test not only generates a sequence of input to the + # the checksum unit but it also configure the test source and sink with + # different initial and interval delays. + @hypothesis.given( + input_msgs = st.lists( st.lists( pm_st.bits(16), min_size=8, max_size=8 ) ), + src_init = st.integers( 0, 10 ), + src_intv = st.integers( 0, 3 ), + sink_init = st.integers( 0, 10 ), + sink_intv = st.integers( 0, 3 ), + ) + @hypothesis.settings( deadline=None, max_examples=50 ) + def test_srcsink_hypothesis( s, input_msgs, src_init, src_intv, sink_init, sink_intv ): + src_msgs = [ words_to_b128( words ) for words in input_msgs ] + sink_msgs = [ checksum( words ) for words in input_msgs ] + + th = TestHarness( s.DutType, src_msgs, sink_msgs ) + th.set_param( "top.src.construct", initial_delay = src_init, interval_delay = src_intv ) + th.set_param( "top.sink.construct", initial_delay = sink_init, interval_delay = sink_intv ) + s.run_sim( th ) diff --git a/examples/ex02_cksum/test/ChecksumVRTL_test.py b/examples/ex02_cksum/test/ChecksumVRTL_test.py index ad78c8b36..afbba1df8 100644 --- a/examples/ex02_cksum/test/ChecksumVRTL_test.py +++ b/examples/ex02_cksum/test/ChecksumVRTL_test.py @@ -10,7 +10,6 @@ from pymtl3 import * from pymtl3.passes.backends.yosys import * from pymtl3.passes.tracing import * -from pymtl3.stdlib.test_utils import TestSinkCL, TestSrcCL from pymtl3.stdlib.test_utils.test_helpers import finalize_verilator from ..ChecksumFL import checksum @@ -44,21 +43,21 @@ def checksum_vrtl( words ): dut.sim_reset() # Wait until the checksum unit is ready to receive input - dut.send.rdy @= 1 - while not dut.recv.rdy: + dut.ostream.rdy @= 1 + while not dut.istream.rdy: dut.sim_tick() # Feed in the input - dut.recv.en @= 1 - dut.recv.msg @= bits_in + dut.istream.val @= 1 + dut.istream.msg @= bits_in dut.sim_tick() # Wait until the checksum unit is about to send the message - while not dut.send.en: + while not dut.ostream.val: dut.sim_tick() # Return the result - return dut.send.msg + return dut.ostream.msg #------------------------------------------------------------------------- # Reuse functionality from CL test suite diff --git a/examples/ex03_proc/MiscRTL.py b/examples/ex03_proc/MiscRTL.py index cc99f5a5e..dda930c4e 100644 --- a/examples/ex03_proc/MiscRTL.py +++ b/examples/ex03_proc/MiscRTL.py @@ -8,7 +8,7 @@ Date : June 13, 2019 """ from pymtl3 import * -from pymtl3.stdlib.ifcs import GetIfcRTL, GiveIfcRTL +from pymtl3.stdlib.stream.ifcs import IStreamIfc, OStreamIfc from pymtl3.stdlib.primitive import RegRst from .TinyRV0InstRTL import * @@ -31,10 +31,10 @@ class DropUnitRTL( Component ): def construct( s, dtype ): s.drop = InPort() - s.in_ = GetIfcRTL( dtype ) - s.out = GiveIfcRTL( dtype ) + s.in_ = IStreamIfc( dtype ) + s.out = OStreamIfc( dtype ) - s.out.ret //= s.in_.ret + s.out.msg //= s.in_.msg s.snoop_state = Wire() @@ -49,11 +49,11 @@ def state_transitions(): s.snoop_state <<= SNOOP elif s.snoop_state == SNOOP: - if s.drop & ~s.in_.rdy: + if s.drop & ~s.in_.val: s.snoop_state <<= WAIT elif s.snoop_state == WAIT: - if s.in_.rdy: + if s.in_.val: s.snoop_state <<= SNOOP #------------------------------------------------------------------ @@ -62,16 +62,16 @@ def state_transitions(): @update def set_outputs(): - s.out.rdy @= 0 - s.in_.en @= 0 + s.out.val @= 0 + s.in_.rdy @= 0 if s.snoop_state == SNOOP: - s.out.rdy @= s.in_.rdy & ~s.drop - s.in_.en @= s.out.en + s.out.val @= s.in_.val & ~s.drop + s.in_.rdy @= s.out.rdy elif s.snoop_state == WAIT: - s.out.rdy @= 0 - s.in_.en @= s.in_.rdy + s.out.val @= 0 + s.in_.rdy @= s.in_.val #------------------------------------------------------------------------- # Generate intermediate (imm) based on type diff --git a/examples/ex03_proc/NullXcel.py b/examples/ex03_proc/NullXcel.py index fa95a576f..badd1b04e 100644 --- a/examples/ex03_proc/NullXcel.py +++ b/examples/ex03_proc/NullXcel.py @@ -7,10 +7,10 @@ Date : June 14, 2019 """ from pymtl3 import * -from pymtl3.stdlib.ifcs.xcel_ifcs import XcelMinionIfcRTL -from pymtl3.stdlib.ifcs.XcelMsg import XcelMsgType, mk_xcel_msg +from pymtl3.stdlib.xcel import XcelMsgType, mk_xcel_msg +from pymtl3.stdlib.xcel.ifcs import XcelResponderIfc from pymtl3.stdlib.primitive import RegEn -from pymtl3.stdlib.queues import NormalQueueRTL +from pymtl3.stdlib.stream import StreamNormalQueue class NullXcelRTL(Component): @@ -20,34 +20,34 @@ def construct( s, nbits=32 ): dtype = mk_bits(32) xreq_class, xresp_class = mk_xcel_msg( 5,nbits ) - s.xcel = XcelMinionIfcRTL( xreq_class, xresp_class ) + s.xcel = XcelResponderIfc( xreq_class, xresp_class ) - s.xcelreq_q = NormalQueueRTL( xreq_class, 2 ) - s.xcelreq_q.enq //= s.xcel.req + s.xcelreq_q = StreamNormalQueue( xreq_class, 2 ) + s.xcelreq_q.istream //= s.xcel.reqstream s.xr0 = RegEn( dtype ) - s.xr0.in_ //= s.xcelreq_q.deq.ret.data + s.xr0.in_ //= s.xcelreq_q.ostream.msg.data @update def up_null_xcel(): - if s.xcelreq_q.deq.rdy & s.xcel.resp.rdy: - s.xcelreq_q.deq.en @= 1 - s.xcel.resp.en @= 1 - s.xcel.resp.msg.type_ @= s.xcelreq_q.deq.ret.type_ + if s.xcelreq_q.ostream.val & s.xcel.respstream.rdy: + s.xcelreq_q.ostream.rdy @= 1 + s.xcel.respstream.val @= 1 + s.xcel.respstream.msg.type_ @= s.xcelreq_q.ostream.msg.type_ - if s.xcelreq_q.deq.ret.type_ == XcelMsgType.WRITE: - s.xr0.en @= 1 - s.xcel.resp.msg.data @= 0 + if s.xcelreq_q.ostream.msg.type_ == XcelMsgType.WRITE: + s.xr0.en @= 1 + s.xcel.respstream.msg.data @= 0 else: - s.xr0.en @= 0 - s.xcel.resp.msg.data @= s.xr0.out + s.xr0.en @= 0 + s.xcel.respstream.msg.data @= s.xr0.out else: - s.xcelreq_q.deq.en @= 0 - s.xcel.resp.en @= 0 - s.xr0.en @= 0 - s.xcel.resp.msg.data @= 0 - s.xcel.resp.msg.type_ @= 0 + s.xcelreq_q.ostream.rdy @= 0 + s.xcel.respstream.val @= 0 + s.xr0.en @= 0 + s.xcel.respstream.msg.data @= 0 + s.xcel.respstream.msg.type_ @= 0 def line_trace( s ): return str(s.xcel) diff --git a/examples/ex03_proc/ProcCL.py b/examples/ex03_proc/ProcCL.py deleted file mode 100644 index 57ab0d53b..000000000 --- a/examples/ex03_proc/ProcCL.py +++ /dev/null @@ -1,276 +0,0 @@ -""" -========================================================================== -ProcCL.py -========================================================================== -TinyRV0 CL proc. - -Author : Shunning Jiang - Date : June 14, 2019 -""" -from enum import Enum - -from pymtl3 import * -from pymtl3.stdlib.delays import DelayPipeDeqCL -from pymtl3.stdlib.queues import PipeQueueCL -from pymtl3.stdlib.mem import MemMsgType, mk_mem_msg, MemMasterIfcCL -from pymtl3.stdlib.ifcs import XcelMsgType, mk_xcel_msg, XcelMasterIfcCL - -from .tinyrv0_encoding import RegisterFile, TinyRV0Inst, disassemble_inst - - -class DXM_W(Enum): - mem = 1 - xcel = 2 - arith = 3 - mngr = 4 - -class PipelineStatus(Enum): - idle = 0 - stall = 1 - work = 2 - -class ProcCL( Component ): - - def construct( s ): - - memreq_cls, memresp_cls = mk_mem_msg( 8,32,32 ) - xreq_class, xresp_class = mk_xcel_msg(5,32) - - # Interface - - s.commit_inst = OutPort( Bits1 ) - - s.imem = MemMasterIfcCL( memreq_cls, memresp_cls ) - s.dmem = MemMasterIfcCL( memreq_cls, memresp_cls ) - - s.xcel = XcelMasterIfcCL( xreq_class, xresp_class ) - - s.proc2mngr = CallerIfcCL() - s.mngr2proc = CalleeIfcCL() - - # Buffers to hold input messages - - s.imemresp_q = DelayPipeDeqCL(0) - s.imemresp_q.enq //= s.imem.resp - - s.dmemresp_q = DelayPipeDeqCL(1) - s.dmemresp_q.enq //= s.dmem.resp - - s.mngr2proc_q = DelayPipeDeqCL(1) - s.mngr2proc_q.enq //= s.mngr2proc - - s.xcelresp_q = DelayPipeDeqCL(0) - s.xcelresp_q.enq //= s.xcel.resp - - s.pc = b32( 0x200 ) - s.R = RegisterFile( 32 ) - - s.F_DXM_queue = PipeQueueCL(1) - s.DXM_W_queue = PipeQueueCL(1) - - s.F_status = PipelineStatus.idle - s.DXM_status = PipelineStatus.idle - s.W_status = PipelineStatus.idle - - @update_once - def F(): - s.F_status = PipelineStatus.idle - - if s.reset: - s.pc = b32( 0x200 ) - return - - if s.imem.req.rdy() and s.F_DXM_queue.enq.rdy(): - if s.redirected_pc_DXM >= 0: - s.imem.req( memreq_cls( MemMsgType.READ, 0, s.redirected_pc_DXM ) ) - s.pc = s.redirected_pc_DXM - s.redirected_pc_DXM = -1 - else: - s.imem.req( memreq_cls( MemMsgType.READ, 0, s.pc ) ) - - s.F_DXM_queue.enq( s.pc ) - s.F_status = PipelineStatus.work - s.pc += 4 - else: - s.F_status = PipelineStatus.stall - - s.redirected_pc_DXM = -1 - - s.raw_inst = b32(0) - - @update_once - def DXM(): - s.DXM_status = PipelineStatus.idle - - if s.redirected_pc_DXM >= 0: - s.DXM_status = PipelineStatus.stall - - elif s.F_DXM_queue.deq.rdy() and s.imemresp_q.deq.rdy(): - - if not s.DXM_W_queue.enq.rdy(): - s.DXM_status = PipelineStatus.stall - else: - pc = s.F_DXM_queue.peek() - - s.raw_inst = s.imemresp_q.peek().data - inst = TinyRV0Inst( s.raw_inst ) - inst_name = inst.name - - s.DXM_status = PipelineStatus.work - - if inst_name == "nop": - pass - elif inst_name == "add": - s.DXM_W_queue.enq( (inst.rd, s.R[ inst.rs1 ] + s.R[ inst.rs2 ], DXM_W.arith) ) - elif inst_name == "sll": - s.DXM_W_queue.enq( (inst.rd, s.R[inst.rs1] << (s.R[inst.rs2] & 0x1F), DXM_W.arith) ) - elif inst_name == "srl": - s.DXM_W_queue.enq( (inst.rd, s.R[inst.rs1] >> (s.R[inst.rs2].uint() & 0x1F), DXM_W.arith) ) - - # ''' TUTORIAL TASK '''''''''''''''''''''''''''''''''''''''''''' - # Implement instruction AND in CL processor - # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''\/ - #; Make an "elif" statement here to implement instruction AND - #; that applies bit-wise "and" operator to rs1 and rs2 and - #; pass the result to the pipeline. - - elif inst_name == "and": - s.DXM_W_queue.enq( (inst.rd, s.R[inst.rs1] & s.R[inst.rs2], DXM_W.arith) ) - - # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''/\ - - elif inst_name == "addi": - s.DXM_W_queue.enq( (inst.rd, s.R[ inst.rs1 ] + sext(inst.i_imm, 32), DXM_W.arith) ) - elif inst_name == "sw": - if s.dmem.req.rdy(): - s.dmem.req( memreq_cls( MemMsgType.WRITE, 0, - s.R[ inst.rs1 ] + sext(inst.s_imm, 32), - 0, - s.R[ inst.rs2 ] ) ) - s.DXM_W_queue.enq( (0, 0, DXM_W.mem) ) - else: - s.DXM_status = PipelineStatus.stall - - elif inst_name == "lw": - if s.dmem.req.rdy(): - s.dmem.req( memreq_cls( MemMsgType.READ, 0, - s.R[ inst.rs1 ] + sext(inst.i_imm, 32), - 0 ) ) - s.DXM_W_queue.enq( (inst.rd, 0, DXM_W.mem) ) - else: - s.DXM_status = PipelineStatus.stall - - elif inst_name == "bne": - if s.R[ inst.rs1 ] != s.R[ inst.rs2 ]: - s.redirected_pc_DXM = pc + sext(inst.b_imm, 32) - s.DXM_W_queue.enq( None ) - - elif inst_name == "csrw": - if inst.csrnum == 0x7C0: # CSR: proc2mngr - # We execute csrw in W stage - s.DXM_W_queue.enq( (0, s.R[ inst.rs1 ], DXM_W.mngr) ) - - elif 0x7E0 <= inst.csrnum <= 0x7FF: - if s.xcel.req.rdy(): - s.xcel.req( xreq_class( XcelMsgType.WRITE, inst.csrnum[0:5], s.R[inst.rs1]) ) - s.DXM_W_queue.enq( (0, 0, DXM_W.xcel) ) - else: - s.DXM_status = PipelineStatus.stall - elif inst_name == "csrr": - if inst.csrnum == 0xFC0: # CSR: mngr2proc - if s.mngr2proc_q.deq.rdy(): - s.DXM_W_queue.enq( (inst.rd, s.mngr2proc_q.deq(), DXM_W.arith) ) - else: - s.DXM_status = PipelineStatus.stall - elif 0x7E0 <= inst.csrnum <= 0x7FF: - if s.xcel.req.rdy(): - s.xcel.req( xreq_class( XcelMsgType.READ, inst.csrnum[0:5], s.R[inst.rs1]) ) - s.DXM_W_queue.enq( (inst.rd, 0, DXM_W.xcel) ) - else: - s.DXM_status = PipelineStatus.stall - - # If we execute any instruction, we pop from queues - if s.DXM_status == PipelineStatus.work: - s.F_DXM_queue.deq() - s.imemresp_q.deq() - - s.rd = b5(0) - - @update_once - def W(): - s.rd = b5(0) - s.commit_inst @= 0 - s.W_status = PipelineStatus.idle - - if s.DXM_W_queue.deq.rdy(): - entry = s.DXM_W_queue.peek() - if entry is not None: - rd, data, entry_type = entry - s.rd = rd - - if entry_type == DXM_W.mem: - if s.dmemresp_q.deq.rdy(): - if rd > 0: # load - s.R[ rd ] = Bits32( s.dmemresp_q.deq().data ) - else: # store - s.dmemresp_q.deq() - - s.W_status = PipelineStatus.work - - else: - s.W_status = PipelineStatus.stall - - elif entry_type == DXM_W.xcel: - if s.xcelresp_q.deq.rdy(): - if rd > 0: # csrr - s.R[ rd ] = Bits32( s.xcelresp_q.deq().data ) - else: # csrw - s.xcelresp_q.deq() - - s.W_status = PipelineStatus.work - else: - s.W_status = PipelineStatus.stall - - elif entry_type == DXM_W.mngr: - if s.proc2mngr.rdy(): - s.proc2mngr( data ) - s.W_status = PipelineStatus.work - else: - s.W_status = PipelineStatus.stall - - else: # other WB insts - assert entry_type == DXM_W.arith - if rd > 0: s.R[ rd ] = Bits32( data ) - s.W_status = PipelineStatus.work - - else: # non-WB insts - s.W_status = PipelineStatus.work - - if s.W_status == PipelineStatus.work: - s.DXM_W_queue.deq() - s.commit_inst @= 1 - - #----------------------------------------------------------------------- - # line_trace - #----------------------------------------------------------------------- - - def line_trace( s ): - F_line_trace = " " - if s.F_status == PipelineStatus.work: - F_line_trace = str(s.pc) - elif s.F_status == PipelineStatus.stall: - F_line_trace = "#" - - DXM_line_trace = " " - if s.DXM_status == PipelineStatus.work: - DXM_line_trace = disassemble_inst(s.raw_inst) - elif s.DXM_status == PipelineStatus.stall: - DXM_line_trace = "#" - - W_line_trace = " " - if s.W_status == PipelineStatus.work: - W_line_trace = "x{:2}".format(str(s.rd) if s.rd > 0 else "--") - elif s.F_status == PipelineStatus.stall: - W_line_trace = "#" - - return "[{:<8s}|{:<23s}|{:<3s}]".format( F_line_trace, DXM_line_trace, W_line_trace ) diff --git a/examples/ex03_proc/ProcCtrlRTL.py b/examples/ex03_proc/ProcCtrlRTL.py index 99c9188ba..dcd0574b5 100644 --- a/examples/ex03_proc/ProcCtrlRTL.py +++ b/examples/ex03_proc/ProcCtrlRTL.py @@ -8,7 +8,7 @@ Date : June 13, 2019 """ from pymtl3 import * -from pymtl3.stdlib.ifcs.XcelMsg import XcelMsgType +from pymtl3.stdlib.xcel import XcelMsgType from .TinyRV0InstRTL import * @@ -23,37 +23,37 @@ def construct( s ): # imem ports - s.imemreq_en = OutPort() + s.imemreq_val = OutPort() s.imemreq_rdy = InPort () - s.imemresp_en = OutPort() - s.imemresp_rdy = InPort() + s.imemresp_val = InPort() + s.imemresp_rdy = OutPort() s.imemresp_drop = OutPort() # dmem ports - s.dmemreq_en = OutPort() + s.dmemreq_val = OutPort() s.dmemreq_rdy = InPort () s.dmemreq_type = OutPort( Bits4 ) - s.dmemresp_en = OutPort() - s.dmemresp_rdy = InPort () + s.dmemresp_val = InPort() + s.dmemresp_rdy = OutPort() # mngr ports # Get interface - s.mngr2proc_en = OutPort() - s.mngr2proc_rdy = InPort () + s.mngr2proc_val = InPort() + s.mngr2proc_rdy = OutPort() # Send interface - s.proc2mngr_en = OutPort() + s.proc2mngr_val = OutPort() s.proc2mngr_rdy = InPort () # Send interface s.xcelreq_rdy = InPort () - s.xcelreq_en = OutPort() + s.xcelreq_val = OutPort() s.xcelreq_type = OutPort() # Get interface - s.xcelresp_rdy = InPort () - s.xcelresp_en = OutPort() + s.xcelresp_rdy = OutPort () + s.xcelresp_val = InPort() # Control signals (ctrl->dpath) @@ -194,7 +194,7 @@ def comb_F(): # ostall due to imemresp - s.ostall_F @= s.val_F & ~s.imemresp_rdy + s.ostall_F @= s.val_F & ~s.imemresp_val # stall and squash in F stage @@ -206,8 +206,8 @@ def comb_F(): # because we need to redirect the PC. We also need to factor in # reset. When we are resetting we shouldn't send out imem req. - s.imemreq_en @= ~s.reset & (~s.stall_F | s.squash_F) & s.imemreq_rdy - s.imemresp_en @= ~s.stall_F | s.squash_F + s.imemreq_val @= ~s.reset & (~s.stall_F | s.squash_F) & s.imemreq_rdy + s.imemresp_rdy @= ~s.stall_F | s.squash_F # We drop the mem response when we are getting squashed @@ -457,7 +457,7 @@ def comb_hazard_D(): def comb_D(): # ostall due to mngr2proc not ready - s.ostall_mngr_D @= s.mngr2proc_D & ~s.mngr2proc_rdy # This is get, rdy means we can get the message + s.ostall_mngr_D @= s.mngr2proc_D & ~s.mngr2proc_val # This is get, rdy means we can get the message # put together all ostall conditions s.ostall_D @= s.val_D & ( s.ostall_mngr_D | s.ostall_hazard_D ) @@ -473,7 +473,7 @@ def comb_D(): s.next_val_D @= s.val_D & ~s.stall_D & ~s.squash_D # enable signal for send/get interface - s.mngr2proc_en @= s.val_D & ~s.stall_D & ~s.squash_D & s.mngr2proc_D + s.mngr2proc_rdy @= s.val_D & ~s.stall_D & ~s.squash_D & s.mngr2proc_D #--------------------------------------------------------------------- # X stage @@ -545,13 +545,13 @@ def comb_X(): # send dmemreq enable if not stalling - s.dmemreq_en @= s.val_X & ~s.stall_X & ( s.dmemreq_type_X != nr ) + s.dmemreq_val @= s.val_X & ~s.stall_X & ( s.dmemreq_type_X != nr ) s.dmemreq_type @= zext( s.dmemreq_type_X == st, 4 ) # 0-load/DC, 1-store # send xcelreq enable if not stalling - s.xcelreq_en @= s.val_X & ~s.stall_X & s.xcelreq_X + s.xcelreq_val @= s.val_X & ~s.stall_X & s.xcelreq_X s.xcelreq_type @= s.xcelreq_type_X # next valid bit @@ -595,8 +595,8 @@ def comb_M(): # ostall due to xcel resp or dmem resp - s.ostall_xcel_M @= (s.xcelreq_M & ~s.xcelresp_rdy) - s.ostall_dmem_M @= (s.dmemreq_type_M != nr ) & ~s.dmemresp_rdy + s.ostall_xcel_M @= (s.xcelreq_M & ~s.xcelresp_val) + s.ostall_dmem_M @= (s.dmemreq_type_M != nr ) & ~s.dmemresp_val s.ostall_M @= s.val_M & ( s.ostall_dmem_M | s.ostall_xcel_M ) @@ -606,8 +606,8 @@ def comb_M(): # dmemresp/xcelresp get enable if not stalling - s.dmemresp_en @= s.val_M & ~s.stall_M & ( s.dmemreq_type_M != nr ) - s.xcelresp_en @= s.val_M & ~s.stall_M & s.xcelreq_M + s.dmemresp_rdy @= s.val_M & ~s.stall_M & ( s.dmemreq_type_M != nr ) + s.xcelresp_rdy @= s.val_M & ~s.stall_M & s.xcelreq_M # next valid bit @@ -655,6 +655,6 @@ def comb_W(): # proc2mngr send is enabled if not stalling - s.proc2mngr_en @= s.val_W & ~s.stall_W & s.proc2mngr_en_W + s.proc2mngr_val @= s.val_W & ~s.stall_W & s.proc2mngr_en_W s.commit_inst @= s.val_W & ~s.stall_W diff --git a/examples/ex03_proc/ProcFL.py b/examples/ex03_proc/ProcFL.py deleted file mode 100644 index 647bca3b0..000000000 --- a/examples/ex03_proc/ProcFL.py +++ /dev/null @@ -1,132 +0,0 @@ -""" -========================================================================== -ProcFL -========================================================================== -TinyRV0 FL proc. - -Author : Shunning Jiang - Date : June 14, 2019 -""" - -from pymtl3 import * -from pymtl3.stdlib.ifcs import GetIfcFL, SendIfcFL, XcelMasterIfcFL, mk_xcel_msg -from pymtl3.stdlib.mem import MemMasterIfcFL - -from .tinyrv0_encoding import RegisterFile, TinyRV0Inst, disassemble_inst - - -class ProcFL( Component ): - - def construct( s ): - - # Interface, Buffers to hold request/response messages - - s.commit_inst = OutPort( Bits1 ) - - s.imem = MemMasterIfcFL() - s.dmem = MemMasterIfcFL() - s.xcel = XcelMasterIfcFL() - - s.proc2mngr = SendIfcFL() - s.mngr2proc = GetIfcFL() - - # Internal data structures - - s.PC = b32( 0x200 ) - - s.R = RegisterFile(32) - s.raw_inst = None - - @update_once - def up_ProcFL(): - if s.reset: - s.PC = b32( 0x200 ) - return - - s.commit_inst @= 0 - - try: - s.raw_inst = s.imem.read( s.PC, 4 ) # line trace - - inst = TinyRV0Inst( s.raw_inst ) - inst_name = inst.name - - if inst_name == "nop": - s.PC += 4 - elif inst_name == "add": - s.R[inst.rd] = s.R[inst.rs1] + s.R[inst.rs2] - s.PC += 4 - elif inst_name == "sll": - s.R[inst.rd] = s.R[inst.rs1] << (s.R[inst.rs2] & 0x1F) - s.PC += 4 - elif inst_name == "srl": - s.R[inst.rd] = s.R[inst.rs1] >> (s.R[inst.rs2].uint() & 0x1F) - s.PC += 4 - - # ''' TUTORIAL TASK '''''''''''''''''''''''''''''''''''''''''''''' - # Implement instruction AND in FL processor - # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''\/ - #; Make an "elif" statement here to implement instruction AND - #; that applies bit-wise "and" operator to rs1 and rs2 and stores - #; the result to rd - - elif inst_name == "and": - s.R[inst.rd] = s.R[inst.rs1] & s.R[inst.rs2] - s.PC += 4 - - # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''/\ - - elif inst_name == "addi": - s.R[inst.rd] = s.R[inst.rs1] + sext( inst.i_imm, 32 ) - s.PC += 4 - elif inst_name == "sw": - addr = s.R[inst.rs1] + sext( inst.s_imm, 32 ) - s.dmem.write( addr, 4, s.R[inst.rs2] ) - s.PC += 4 - elif inst_name == "lw": - addr = s.R[inst.rs1] + sext( inst.i_imm, 32 ) - s.R[inst.rd] = s.dmem.read( addr, 4 ) - s.PC += 4 - elif inst_name == "bne": - if s.R[inst.rs1] != s.R[inst.rs2]: - s.PC = s.PC + sext( inst.b_imm, 32 ) - else: - s.PC += 4 - - elif inst_name == "csrw": - if inst.csrnum == 0x7C0: - s.proc2mngr( s.R[inst.rs1] ) - elif 0x7E0 <= inst.csrnum <= 0x7FF: - s.xcel.write( inst.csrnum[0:5], s.R[inst.rs1] ) - else: - raise TinyRV2Semantics.IllegalInstruction( - "Unrecognized CSR register ({}) for csrw at PC={}" \ - .format(inst.csrnum.uint(),s.PC) ) - s.PC += 4 - - elif inst_name == "csrr": - if inst.csrnum == 0xFC0: - s.R[inst.rd] = s.mngr2proc() - elif 0x7E0 <= inst.csrnum <= 0x7FF: - s.R[inst.rd] = s.xcel.read( inst.csrnum[0:5] ) - else: - raise TinyRV2Semantics.IllegalInstruction( - "Unrecognized CSR register ({}) for csrr at PC={}" \ - .format(inst.csrnum.uint(),s.PC) ) - - s.PC += 4 - - except: - print( "Unexpected error at PC={:0>8s}!".format( str(s.PC) ) ) - raise - - s.commit_inst @= 1 - - #----------------------------------------------------------------------- - # line_trace - #----------------------------------------------------------------------- - - def line_trace( s ): - if s.commit_inst: - return "[{:0>8s} {: <24}]".format( str(s.PC), disassemble_inst( s.raw_inst ) ) - return "[{}]".format( "#".ljust(33) ) diff --git a/examples/ex03_proc/ProcRTL.py b/examples/ex03_proc/ProcRTL.py index 99a69f71f..329113e87 100644 --- a/examples/ex03_proc/ProcRTL.py +++ b/examples/ex03_proc/ProcRTL.py @@ -9,10 +9,12 @@ """ from pymtl3 import * from pymtl3.stdlib.connects import connect_pairs -from pymtl3.stdlib.ifcs import RecvIfcRTL, SendIfcRTL, mk_xcel_msg, XcelMasterIfcRTL -from pymtl3.stdlib.mem import mk_mem_msg, MemMasterIfcRTL -from pymtl3.stdlib.queues.enrdy_queues import BypassQueue2RTL -from pymtl3.stdlib.queues import BypassQueueRTL +from pymtl3.stdlib.xcel import mk_xcel_msg +from pymtl3.stdlib.xcel.ifcs import XcelRequesterIfc +from pymtl3.stdlib.mem import mk_mem_msg +from pymtl3.stdlib.mem.ifcs import MemRequesterIfc +from pymtl3.stdlib.stream import StreamBypassQueue +from pymtl3.stdlib.stream.ifcs import IStreamIfc, OStreamIfc from .MiscRTL import DropUnitRTL from .ProcCtrlRTL import ProcCtrl @@ -29,22 +31,22 @@ def construct( s ): # Proc/Mngr Interface - s.mngr2proc = RecvIfcRTL( Bits32 ) - s.proc2mngr = SendIfcRTL( Bits32 ) + s.mngr2proc = IStreamIfc( Bits32 ) + s.proc2mngr = OStreamIfc( Bits32 ) # Instruction Memory Request/Response Interface - s.imem = MemMasterIfcRTL( req_class, resp_class ) + s.imem = MemRequesterIfc( req_class, resp_class ) # Data Memory Request/Response Interface - s.dmem = MemMasterIfcRTL( req_class, resp_class ) + s.dmem = MemRequesterIfc( req_class, resp_class ) # Xcel Request/Response Interface xreq_class, xresp_class = mk_xcel_msg( 5, 32 ) - s.xcel = XcelMasterIfcRTL( xreq_class, xresp_class ) + s.xcel = XcelRequesterIfc( xreq_class, xresp_class ) # val_W port used for counting commited insts. @@ -52,29 +54,29 @@ def construct( s ): # Bypass queues - s.imemreq_q = BypassQueue2RTL( req_class, 2 ) + s.imemreq_q = StreamBypassQueue( req_class, 2 ) # We have to turn input receive interface into get interface - s.imemresp_q = BypassQueueRTL( resp_class, 1 ) - s.dmemresp_q = BypassQueueRTL( resp_class, 1 ) - s.mngr2proc_q = BypassQueueRTL( Bits32, 1 ) - s.xcelresp_q = BypassQueueRTL( xresp_class, 1 ) + s.imemresp_q = StreamBypassQueue( resp_class, 1 ) + s.dmemresp_q = StreamBypassQueue( resp_class, 1 ) + s.mngr2proc_q = StreamBypassQueue( Bits32, 1 ) + s.xcelresp_q = StreamBypassQueue( xresp_class, 1 ) # imem drop unit s.imemresp_drop = m = DropUnitRTL( Bits32 ) - m.in_.en //= s.imemresp_q.deq.en - m.in_.rdy //= s.imemresp_q.deq.rdy - m.in_.ret //= s.imemresp_q.deq.ret.data + m.in_.val //= s.imemresp_q.ostream.val + m.in_.rdy //= s.imemresp_q.ostream.rdy + m.in_.msg //= s.imemresp_q.ostream.msg.data # connect all the queues - s.imemreq_q.deq //= s.imem.req - s.imemresp_q.enq //= s.imem.resp - s.dmemresp_q.enq //= s.dmem.resp - s.mngr2proc_q.enq //= s.mngr2proc - s.xcelresp_q.enq //= s.xcel.resp + s.imemreq_q.ostream //= s.imem.reqstream + s.imemresp_q.istream //= s.imem.respstream + s.dmemresp_q.istream //= s.dmem.respstream + s.mngr2proc_q.istream //= s.mngr2proc + s.xcelresp_q.istream //= s.xcel.respstream # Control @@ -82,31 +84,31 @@ def construct( s ): # imem port m.imemresp_drop //= s.imemresp_drop.drop - m.imemreq_en //= s.imemreq_q.enq.en - m.imemreq_rdy //= s.imemreq_q.enq.rdy - m.imemresp_en //= s.imemresp_drop.out.en + m.imemreq_val //= s.imemreq_q.istream.val + m.imemreq_rdy //= s.imemreq_q.istream.rdy + m.imemresp_val //= s.imemresp_drop.out.val m.imemresp_rdy //= s.imemresp_drop.out.rdy # dmem port - m.dmemreq_en //= s.dmem.req.en - m.dmemreq_rdy //= s.dmem.req.rdy - m.dmemreq_type //= s.dmem.req.msg.type_ - m.dmemresp_en //= s.dmemresp_q.deq.en - m.dmemresp_rdy //= s.dmemresp_q.deq.rdy + m.dmemreq_val //= s.dmem.reqstream.val + m.dmemreq_rdy //= s.dmem.reqstream.rdy + m.dmemreq_type //= s.dmem.reqstream.msg.type_ + m.dmemresp_val //= s.dmemresp_q.ostream.val + m.dmemresp_rdy //= s.dmemresp_q.ostream.rdy # xcel port - m.xcelreq_type //= s.xcel.req.msg.type_ + m.xcelreq_type //= s.xcel.reqstream.msg.type_ - m.xcelreq_en //= s.xcel.req.en - m.xcelreq_rdy //= s.xcel.req.rdy - m.xcelresp_en //= s.xcelresp_q.deq.en - m.xcelresp_rdy //= s.xcelresp_q.deq.rdy + m.xcelreq_val //= s.xcel.reqstream.val + m.xcelreq_rdy //= s.xcel.reqstream.rdy + m.xcelresp_val //= s.xcelresp_q.ostream.val + m.xcelresp_rdy //= s.xcelresp_q.ostream.rdy # proc2mngr and mngr2proc - m.proc2mngr_en //= s.proc2mngr.en + m.proc2mngr_val //= s.proc2mngr.val m.proc2mngr_rdy //= s.proc2mngr.rdy - m.mngr2proc_en //= s.mngr2proc_q.deq.en - m.mngr2proc_rdy //= s.mngr2proc_q.deq.rdy + m.mngr2proc_val //= s.mngr2proc_q.ostream.val + m.mngr2proc_rdy //= s.mngr2proc_q.ostream.rdy # commit inst for counting m.commit_inst //= s.commit_inst @@ -116,21 +118,21 @@ def construct( s ): s.dpath = m = ProcDpath() # imem ports - m.imemreq_addr //= s.imemreq_q.enq.msg.addr - m.imemresp_data //= s.imemresp_drop.out.ret + m.imemreq_addr //= s.imemreq_q.istream.msg.addr + m.imemresp_data //= s.imemresp_drop.out.msg # dmem ports - m.dmemreq_addr //= s.dmem.req.msg.addr - m.dmemreq_data //= s.dmem.req.msg.data - m.dmemresp_data //= s.dmemresp_q.deq.ret.data + m.dmemreq_addr //= s.dmem.reqstream.msg.addr + m.dmemreq_data //= s.dmem.reqstream.msg.data + m.dmemresp_data //= s.dmemresp_q.ostream.msg.data # xcel ports - m.xcelreq_addr //= s.xcel.req.msg.addr - m.xcelreq_data //= s.xcel.req.msg.data - m.xcelresp_data //= s.xcelresp_q.deq.ret.data + m.xcelreq_addr //= s.xcel.reqstream.msg.addr + m.xcelreq_data //= s.xcel.reqstream.msg.data + m.xcelresp_data //= s.xcelresp_q.ostream.msg.data # mngr - m.mngr2proc_data //= s.mngr2proc_q.deq.ret + m.mngr2proc_data //= s.mngr2proc_q.ostream.msg m.proc2mngr_data //= s.proc2mngr.msg # Ctrl <-> Dpath diff --git a/examples/ex03_proc/proc-sim b/examples/ex03_proc/proc-sim old mode 100644 new mode 100755 index 346ae2bff..bb5ec93a9 --- a/examples/ex03_proc/proc-sim +++ b/examples/ex03_proc/proc-sim @@ -22,8 +22,6 @@ import os import sys from pymtl3 import * -from pymtl3.stdlib.mem import MagicMemoryCL -from pymtl3.stdlib.test_utils import TestSinkCL, TestSrcCL # Hack to add project root to python path sim_dir = os.path.dirname( os.path.abspath( __file__ ) ) @@ -34,8 +32,6 @@ while sim_dir: sim_dir = os.path.dirname(sim_dir) from examples.ex03_proc.NullXcel import NullXcelRTL -from examples.ex03_proc.ProcCL import ProcCL -from examples.ex03_proc.ProcFL import ProcFL from examples.ex03_proc.ProcRTL import ProcRTL from examples.ex03_proc.SparseMemoryImage import SparseMemoryImage from examples.ex03_proc.tinyrv0_encoding import assemble @@ -80,8 +76,6 @@ def parse_cmdline(): return opts impl_dict = { - "fl" : ProcFL, - "cl" : ProcCL, "rtl": ProcRTL, } @@ -116,11 +110,11 @@ def main(): # Create test harness and elaborate if opts.delay: - model = TestHarness( impl_dict[ opts.impl ], NullXcelRTL, 0, + model = TestHarness( impl_dict[ opts.impl ], NullXcelRTL, # src sink memstall memlat 3, 4, 0.5, 4 ) else: - model = TestHarness( impl_dict[ opts.impl ], NullXcelRTL, 0, + model = TestHarness( impl_dict[ opts.impl ], NullXcelRTL, # src sink memstall memlat 0, 0, 0, 1 ) @@ -132,7 +126,7 @@ def main(): model.proc.set_metadata( YosysTranslationImportPass.enable, True ) model = YosysTranslationImportPass()( model ) - model.apply( DefaultPassGroup(print_line_trace=opts.trace) ) + model.apply( DefaultPassGroup(linetrace=opts.trace) ) # Load the program into the model diff --git a/examples/ex03_proc/test/ProcCL_test.py b/examples/ex03_proc/test/ProcCL_test.py deleted file mode 100644 index 0bdc85943..000000000 --- a/examples/ex03_proc/test/ProcCL_test.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -========================================================================= -ProcCL_test.py -========================================================================= -Includes test cases for the cycle level TinyRV0 processor. - -Author : Shunning Jiang, Yanghui Ou - Date : June 12, 2019 -""" -import random - -import pytest - -from examples.ex03_proc.ProcCL import ProcCL -from pymtl3 import * - -random.seed(0xdeadbeef) - - -#------------------------------------------------------------------------- -# ProcCL_Tests -#------------------------------------------------------------------------- -# It is as simple as inheriting from FL tests and change the ProcType to -# ProcCL. - -from .ProcFL_test import ProcFL_Tests as BaseTests - -class ProcCL_Tests( BaseTests ): - - @classmethod - def setup_class( cls ): - cls.ProcType = ProcCL diff --git a/examples/ex03_proc/test/ProcFL_test.py b/examples/ex03_proc/test/ProcFL_test.py deleted file mode 100644 index 4b7bd84e7..000000000 --- a/examples/ex03_proc/test/ProcFL_test.py +++ /dev/null @@ -1,256 +0,0 @@ -""" -========================================================================= -ProcFL_test.py -========================================================================= -Includes test cases for the functional level TinyRV0 processor. - -Author : Shunning Jiang, Yanghui Ou - Date : June 12, 2019 -""" -import random - -import pytest - -from examples.ex03_proc.ProcFL import ProcFL -from pymtl3 import * -from pymtl3.stdlib.test_utils import run_sim - -from . import ( - inst_add, - inst_addi, - inst_and, - inst_bne, - inst_csr, - inst_lw, - inst_sll, - inst_srl, - inst_sw, - inst_xcel, -) -from .harness import TestHarness, asm_test, assemble - -random.seed(0xdeadbeef) - - -#------------------------------------------------------------------------- -# ProcFL_Tests -#------------------------------------------------------------------------- -# We group all our test cases into a class so that we can easily reuse -# these test cases in our CL and RTL tests. We can simply inherit from -# this test class and overwrite the ProcType of the test class. - -@pytest.mark.usefixtures("cmdline_opts") -class ProcFL_Tests: - - # [setup_class] will be called by pytest before running all the tests in - # the test class. Here we specify the type of the processor that is used - # in all test cases. We can easily reuse all these test cases in simply - # by creating a new test class that inherits from this class and - # overwrite the setup_class to provide a different processor type. - @classmethod - def setup_class( cls ): - cls.ProcType = ProcFL - - # [run_sim] is a helper function in the test suite that creates a - # simulator and runs test. We can overwrite this function when - # inheriting from the test class to apply different passes to the DUT. - def run_sim( s, th, gen_test ): - - th.elaborate() - - # Assemble the program - mem_image = assemble( gen_test() ) - - # Load the program into memory - th.load( mem_image ) - - run_sim( th, s.__class__.cmdline_opts ) - - #----------------------------------------------------------------------- - # add - #----------------------------------------------------------------------- - - @pytest.mark.parametrize( "name,test", [ - asm_test( inst_add.gen_add_basic_test ) , - asm_test( inst_add.gen_dest_dep_test ) , - asm_test( inst_add.gen_src0_dep_test ) , - asm_test( inst_add.gen_src1_dep_test ) , - asm_test( inst_add.gen_srcs_dep_test ) , - asm_test( inst_add.gen_srcs_dest_test ) , - asm_test( inst_add.gen_value_test ) , - asm_test( inst_add.gen_random_test ) , - ]) - def test_add( s, name, test ): - th = TestHarness( s.ProcType ) - s.run_sim( th, test ) - - def test_add_rand_delays( s ): - th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, - mem_stall_prob=0.5, mem_latency=3 ) - s.run_sim( th, inst_add.gen_random_test ) - - #----------------------------------------------------------------------- - # and - #----------------------------------------------------------------------- - - @pytest.mark.parametrize( "name,test", [ - asm_test( inst_and.gen_and_basic_test ) , - ]) - def test_and( s, name, test ): - th = TestHarness( s.ProcType ) - s.run_sim( th, test ) - - #----------------------------------------------------------------------- - # sll - #----------------------------------------------------------------------- - - @pytest.mark.parametrize( "name,test", [ - asm_test( inst_sll.gen_basic_test ) , - asm_test( inst_sll.gen_random_test ) , - ]) - def test_sll( s, name, test ): - th = TestHarness( s.ProcType ) - s.run_sim( th, test ) - - def test_sll_rand_delays( s ): - th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, - mem_stall_prob=0.5, mem_latency=3 ) - s.run_sim( th, inst_sll.gen_random_test ) - - #----------------------------------------------------------------------- - # srl - #----------------------------------------------------------------------- - - @pytest.mark.parametrize( "name,test", [ - asm_test( inst_srl.gen_basic_test ) , - asm_test( inst_srl.gen_random_test ) , - ]) - def test_srl( s, name, test ): - th = TestHarness( s.ProcType ) - s.run_sim( th, test ) - - def test_srl_rand_delays( s ): - th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, - mem_stall_prob=0.5, mem_latency=3 ) - s.run_sim( th, inst_srl.gen_random_test ) - - #----------------------------------------------------------------------- - # bne - #----------------------------------------------------------------------- - - @pytest.mark.parametrize( "name,test", [ - asm_test( inst_bne.gen_basic_test ), - asm_test( inst_bne.gen_src0_dep_taken_test ), - asm_test( inst_bne.gen_src0_dep_nottaken_test ), - asm_test( inst_bne.gen_src1_dep_taken_test ), - asm_test( inst_bne.gen_src1_dep_nottaken_test ), - asm_test( inst_bne.gen_srcs_dep_taken_test ), - asm_test( inst_bne.gen_srcs_dep_nottaken_test ), - asm_test( inst_bne.gen_src0_eq_src1_test ), - asm_test( inst_bne.gen_value_test ), - asm_test( inst_bne.gen_random_test ), - ]) - def test_bne( s, name, test ): - th = TestHarness( s.ProcType ) - s.run_sim( th, test ) - - def test_bne_rand_delays( s ): - th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, - mem_stall_prob=0.5, mem_latency=3 ) - s.run_sim( th, inst_bne.gen_random_test ) - - #------------------------------------------------------------------------- - # addi - #------------------------------------------------------------------------- - - @pytest.mark.parametrize( "name,test", [ - asm_test( inst_addi.gen_basic_test ), - asm_test( inst_addi.gen_dest_dep_test ), - asm_test( inst_addi.gen_src_dep_test ), - asm_test( inst_addi.gen_srcs_dest_test ), - asm_test( inst_addi.gen_value_test ), - asm_test( inst_addi.gen_random_test ), - ]) - def test_addi( s, name, test ): - th = TestHarness( s.ProcType ) - s.run_sim( th, test ) - - def test_addi_rand_delays( s ): - th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, - mem_stall_prob=0.5, mem_latency=3 ) - s.run_sim( th, inst_addi.gen_random_test ) - - #----------------------------------------------------------------------- - # lw - #----------------------------------------------------------------------- - - @pytest.mark.parametrize( "name,test", [ - asm_test( inst_lw.gen_basic_test ), - asm_test( inst_lw.gen_dest_dep_test ), - asm_test( inst_lw.gen_base_dep_test ), - asm_test( inst_lw.gen_srcs_dest_test ), - asm_test( inst_lw.gen_value_test ), - asm_test( inst_lw.gen_random_test ), - ]) - def test_lw( s, name, test ): - th = TestHarness( s.ProcType ) - s.run_sim( th, test ) - - def test_lw_rand_delays( s ): - th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, - mem_stall_prob=0.5, mem_latency=3 ) - s.run_sim( th, inst_lw.gen_random_test ) - - #----------------------------------------------------------------------- - # sw - #----------------------------------------------------------------------- - - - @pytest.mark.parametrize( "name,test", [ - asm_test( inst_sw.gen_basic_test ), - asm_test( inst_sw.gen_random_test ), - ]) - def test_sw( s, name, test ): - th = TestHarness( s.ProcType ) - s.run_sim( th, test ) - - def test_sw_rand_delays( s ): - th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, - mem_stall_prob=0.5, mem_latency=3 ) - s.run_sim( th, inst_sw.gen_random_test ) - - #----------------------------------------------------------------------- - # csr - #----------------------------------------------------------------------- - - @pytest.mark.parametrize( "name,test", [ - asm_test( inst_csr.gen_basic_test ), - asm_test( inst_csr.gen_bypass_test ), - asm_test( inst_csr.gen_value_test ), - asm_test( inst_csr.gen_random_test ), - ]) - def test_csr( s, name, test ): - th = TestHarness( s.ProcType ) - s.run_sim( th, test ) - - def test_csr_rand_delays( s ): - th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, - mem_stall_prob=0.5, mem_latency=3 ) - s.run_sim( th, inst_csr.gen_random_test ) - - #----------------------------------------------------------------------- - # xcel - #----------------------------------------------------------------------- - - @pytest.mark.parametrize( "name,test", [ - asm_test( inst_xcel.gen_basic_test ), - asm_test( inst_xcel.gen_multiple_test ), - ]) - def test_xcel( s, name, test ): - th = TestHarness( s.ProcType ) - s.run_sim( th, test ) - - def test_xcel_rand_delays( s ): - th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, - mem_stall_prob=0.5, mem_latency=3 ) - s.run_sim( th, inst_xcel.gen_multiple_test ) diff --git a/examples/ex03_proc/test/ProcRTL_test.py b/examples/ex03_proc/test/ProcRTL_test.py index 5c7afd5f9..1f7b13b6c 100644 --- a/examples/ex03_proc/test/ProcRTL_test.py +++ b/examples/ex03_proc/test/ProcRTL_test.py @@ -2,10 +2,10 @@ ========================================================================= ProcRTL_test.py ========================================================================= -Includes test cases for the register transfer level TinyRV0 processor. +Includes test cases for the RTL TinyRV0 processor. Author : Shunning Jiang, Yanghui Ou - Date : June 15, 2019 + Date : June 12, 2019 """ import random @@ -13,6 +13,21 @@ from examples.ex03_proc.ProcRTL import ProcRTL from pymtl3 import * +from pymtl3.stdlib.test_utils import run_sim + +from . import ( + inst_add, + inst_addi, + inst_and, + inst_bne, + inst_csr, + inst_lw, + inst_sll, + inst_srl, + inst_sw, + inst_xcel, +) +from .harness import TestHarness, asm_test, assemble random.seed(0xdeadbeef) @@ -20,13 +35,219 @@ #------------------------------------------------------------------------- # ProcRTL_Tests #------------------------------------------------------------------------- -# It is as simple as inheriting from CL tests and change the ProcType to -# ProcRTL. - -from .ProcCL_test import ProcCL_Tests as BaseTests -class ProcRTL_Tests( BaseTests ): +@pytest.mark.usefixtures("cmdline_opts") +class ProcRTL_Tests: + # [setup_class] will be called by pytest before running all the tests in + # the test class. Here we specify the type of the processor that is used + # in all test cases. We can easily reuse all these test cases in simply + # by creating a new test class that inherits from this class and + # overwrite the setup_class to provide a different processor type. @classmethod def setup_class( cls ): cls.ProcType = ProcRTL + + # [run_sim] is a helper function in the test suite that creates a + # simulator and runs test. We can overwrite this function when + # inheriting from the test class to apply different passes to the DUT. + def run_sim( s, th, gen_test ): + + th.elaborate() + + # Assemble the program + mem_image = assemble( gen_test() ) + + # Load the program into memory + th.load( mem_image ) + + run_sim( th, s.__class__.cmdline_opts ) + + #----------------------------------------------------------------------- + # add + #----------------------------------------------------------------------- + + @pytest.mark.parametrize( "name,test", [ + asm_test( inst_add.gen_add_basic_test ) , + asm_test( inst_add.gen_dest_dep_test ) , + asm_test( inst_add.gen_src0_dep_test ) , + asm_test( inst_add.gen_src1_dep_test ) , + asm_test( inst_add.gen_srcs_dep_test ) , + asm_test( inst_add.gen_srcs_dest_test ) , + asm_test( inst_add.gen_value_test ) , + asm_test( inst_add.gen_random_test ) , + ]) + def test_add( s, name, test ): + th = TestHarness( s.ProcType ) + s.run_sim( th, test ) + + def test_add_rand_delays( s ): + th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, + mem_stall_prob=0.5, mem_latency=3 ) + s.run_sim( th, inst_add.gen_random_test ) + + #----------------------------------------------------------------------- + # and + #----------------------------------------------------------------------- + + @pytest.mark.parametrize( "name,test", [ + asm_test( inst_and.gen_and_basic_test ) , + ]) + def test_and( s, name, test ): + th = TestHarness( s.ProcType ) + s.run_sim( th, test ) + + #----------------------------------------------------------------------- + # sll + #----------------------------------------------------------------------- + + @pytest.mark.parametrize( "name,test", [ + asm_test( inst_sll.gen_basic_test ) , + asm_test( inst_sll.gen_random_test ) , + ]) + def test_sll( s, name, test ): + th = TestHarness( s.ProcType ) + s.run_sim( th, test ) + + def test_sll_rand_delays( s ): + th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, + mem_stall_prob=0.5, mem_latency=3 ) + s.run_sim( th, inst_sll.gen_random_test ) + + #----------------------------------------------------------------------- + # srl + #----------------------------------------------------------------------- + + @pytest.mark.parametrize( "name,test", [ + asm_test( inst_srl.gen_basic_test ) , + asm_test( inst_srl.gen_random_test ) , + ]) + def test_srl( s, name, test ): + th = TestHarness( s.ProcType ) + s.run_sim( th, test ) + + def test_srl_rand_delays( s ): + th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, + mem_stall_prob=0.5, mem_latency=3 ) + s.run_sim( th, inst_srl.gen_random_test ) + + #----------------------------------------------------------------------- + # bne + #----------------------------------------------------------------------- + + @pytest.mark.parametrize( "name,test", [ + asm_test( inst_bne.gen_basic_test ), + asm_test( inst_bne.gen_src0_dep_taken_test ), + asm_test( inst_bne.gen_src0_dep_nottaken_test ), + asm_test( inst_bne.gen_src1_dep_taken_test ), + asm_test( inst_bne.gen_src1_dep_nottaken_test ), + asm_test( inst_bne.gen_srcs_dep_taken_test ), + asm_test( inst_bne.gen_srcs_dep_nottaken_test ), + asm_test( inst_bne.gen_src0_eq_src1_test ), + asm_test( inst_bne.gen_value_test ), + asm_test( inst_bne.gen_random_test ), + ]) + def test_bne( s, name, test ): + th = TestHarness( s.ProcType ) + s.run_sim( th, test ) + + def test_bne_rand_delays( s ): + th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, + mem_stall_prob=0.5, mem_latency=3 ) + s.run_sim( th, inst_bne.gen_random_test ) + + #------------------------------------------------------------------------- + # addi + #------------------------------------------------------------------------- + + @pytest.mark.parametrize( "name,test", [ + asm_test( inst_addi.gen_basic_test ), + asm_test( inst_addi.gen_dest_dep_test ), + asm_test( inst_addi.gen_src_dep_test ), + asm_test( inst_addi.gen_srcs_dest_test ), + asm_test( inst_addi.gen_value_test ), + asm_test( inst_addi.gen_random_test ), + ]) + def test_addi( s, name, test ): + th = TestHarness( s.ProcType ) + s.run_sim( th, test ) + + def test_addi_rand_delays( s ): + th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, + mem_stall_prob=0.5, mem_latency=3 ) + s.run_sim( th, inst_addi.gen_random_test ) + + #----------------------------------------------------------------------- + # lw + #----------------------------------------------------------------------- + + @pytest.mark.parametrize( "name,test", [ + asm_test( inst_lw.gen_basic_test ), + asm_test( inst_lw.gen_dest_dep_test ), + asm_test( inst_lw.gen_base_dep_test ), + asm_test( inst_lw.gen_srcs_dest_test ), + asm_test( inst_lw.gen_value_test ), + asm_test( inst_lw.gen_random_test ), + ]) + def test_lw( s, name, test ): + th = TestHarness( s.ProcType ) + s.run_sim( th, test ) + + def test_lw_rand_delays( s ): + th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, + mem_stall_prob=0.5, mem_latency=3 ) + s.run_sim( th, inst_lw.gen_random_test ) + + #----------------------------------------------------------------------- + # sw + #----------------------------------------------------------------------- + + + @pytest.mark.parametrize( "name,test", [ + asm_test( inst_sw.gen_basic_test ), + asm_test( inst_sw.gen_random_test ), + ]) + def test_sw( s, name, test ): + th = TestHarness( s.ProcType ) + s.run_sim( th, test ) + + def test_sw_rand_delays( s ): + th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, + mem_stall_prob=0.5, mem_latency=3 ) + s.run_sim( th, inst_sw.gen_random_test ) + + #----------------------------------------------------------------------- + # csr + #----------------------------------------------------------------------- + + @pytest.mark.parametrize( "name,test", [ + asm_test( inst_csr.gen_basic_test ), + asm_test( inst_csr.gen_bypass_test ), + asm_test( inst_csr.gen_value_test ), + asm_test( inst_csr.gen_random_test ), + ]) + def test_csr( s, name, test ): + th = TestHarness( s.ProcType ) + s.run_sim( th, test ) + + def test_csr_rand_delays( s ): + th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, + mem_stall_prob=0.5, mem_latency=3 ) + s.run_sim( th, inst_csr.gen_random_test ) + + #----------------------------------------------------------------------- + # xcel + #----------------------------------------------------------------------- + + @pytest.mark.parametrize( "name,test", [ + asm_test( inst_xcel.gen_basic_test ), + asm_test( inst_xcel.gen_multiple_test ), + ]) + def test_xcel( s, name, test ): + th = TestHarness( s.ProcType ) + s.run_sim( th, test ) + + def test_xcel_rand_delays( s ): + th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, + mem_stall_prob=0.5, mem_latency=3 ) + s.run_sim( th, inst_xcel.gen_multiple_test ) diff --git a/examples/ex03_proc/test/harness.py b/examples/ex03_proc/test/harness.py index e83cadf30..abc32b783 100644 --- a/examples/ex03_proc/test/harness.py +++ b/examples/ex03_proc/test/harness.py @@ -14,9 +14,9 @@ from examples.ex03_proc.NullXcel import NullXcelRTL from examples.ex03_proc.tinyrv0_encoding import assemble from pymtl3 import * -from pymtl3.stdlib.mem.MagicMemoryCL import MagicMemoryCL, mk_mem_msg +from pymtl3.stdlib.mem import MemoryFL, mk_mem_msg from pymtl3.stdlib.connects import connect_pairs -from pymtl3.stdlib.test_utils import TestSinkCL, TestSrcCL +from pymtl3.stdlib.stream import StreamSinkFL, StreamSourceFL #========================================================================= # TestHarness @@ -60,23 +60,24 @@ def construct( s, proc_cls, xcel_cls=NullXcelRTL, s.commit_inst = OutPort() req, resp = mk_mem_msg( 8, 32, 32 ) - s.src = TestSrcCL ( Bits32, [], src_delay, src_delay ) - s.sink = TestSinkCL( Bits32, [], sink_delay, sink_delay ) + s.src = StreamSourceFL( Bits32, [], src_delay, src_delay ) + s.sink = StreamSinkFL( Bits32, [], sink_delay, sink_delay ) s.proc = proc_cls() s.xcel = xcel_cls() - s.mem = MagicMemoryCL(2, stall_prob=mem_stall_prob, latency = mem_latency) + s.mem = MemoryFL(2, mem_ifc_dtypes = [mk_mem_msg(8,32,32), mk_mem_msg(8,32,32)], + stall_prob=mem_stall_prob, extra_latency = mem_latency) connect_pairs( s.proc.commit_inst, s.commit_inst, # Processor <-> Proc/Mngr - s.src.send, s.proc.mngr2proc, - s.proc.proc2mngr, s.sink.recv, + s.src.ostream, s.proc.mngr2proc, + s.proc.proc2mngr, s.sink.istream, # Processor <-> Memory - s.proc.imem, s.mem.ifc[0], - s.proc.dmem, s.mem.ifc[1], + s.proc.imem, s.mem.ifc[0], + s.proc.dmem, s.mem.ifc[1], ) connect( s.proc.xcel, s.xcel.xcel ) diff --git a/examples/ex04_xcel/ChecksumXcelCL.py b/examples/ex04_xcel/ChecksumXcelCL.py deleted file mode 100644 index 9afe317ae..000000000 --- a/examples/ex04_xcel/ChecksumXcelCL.py +++ /dev/null @@ -1,104 +0,0 @@ -""" -========================================================================== -ChecksumXcelCL.py -========================================================================== -Cycle level implementation of a checksum accelerator. - -Author : Yanghui Ou - Date : June 14, 2019 -""" -from examples.ex02_cksum.ChecksumCL import ChecksumCL -from examples.ex02_cksum.ChecksumRTL import ChecksumRTL -from examples.ex02_cksum.utils import words_to_b128 -from pymtl3 import * -from pymtl3.stdlib.queues import BypassQueueCL, NormalQueueCL -from pymtl3.stdlib.ifcs import XcelMsgType, mk_xcel_msg, XcelMinionIfcCL - -#------------------------------------------------------------------------- -# ChecksumXcelCL -#------------------------------------------------------------------------- - -class ChecksumXcelCL( Component ): - - def construct( s ): - - # Interface - - ReqType, RespType = mk_xcel_msg( 5, 32 ) - s.RespType = RespType - - s.xcel = XcelMinionIfcCL( ReqType, RespType ) - - # State encoding - - s.XCFG = 0 - s.WAIT = 1 - s.BUSY = 2 - - # Local paramters - - RD = XcelMsgType.READ - WR = XcelMsgType.WRITE - - # Components - - s.in_q = NormalQueueCL( num_entries=2 ) - s.reg_file = [ b32(0) for _ in range(6) ] - s.checksum_unit = ChecksumCL() - - s.state = s.XCFG - - s.out_q = BypassQueueCL( num_entries=1 ) - - connect( s.checksum_unit.send, s.out_q.enq ) - connect( s.xcel.req, s.in_q.enq ) - - @update_once - def up_tick(): - if s.state == s.XCFG: - # Dequeue a request message from input queue and send response. - if s.in_q.deq.rdy() and s.xcel.resp.rdy(): - req = s.in_q.deq() - if req.type_ == RD: - s.xcel.resp( s.RespType( RD, s.reg_file[ int(req.addr) ] ) ) - elif req.type_ == WR: - s.reg_file[ int(req.addr) ] = req.data - s.xcel.resp( s.RespType( WR, 0 ) ) - - # If the go bit is written - if req.addr == 4: - if s.checksum_unit.recv.rdy(): - s.state = s.BUSY - words = s.get_words() - s.checksum_unit.recv( words_to_b128( words ) ) - else: - s.state = s.WAIT - - elif s.state == s.WAIT: - if s.checksum_unit.recv.rdy(): - words = s.get_words() - s.checksum_unit.recv( words_to_b128( words ) ) - s.state = s.BUSY - - else: # s.state == s.BUSY - if s.out_q.deq.rdy(): - s.reg_file[5] = s.out_q.deq() - s.state = s.XCFG - #----------------------------------------------------------------------- - # [get_words] is a helper function that extracts the 128-bit input from - # the register file. - def get_words( s ): - words = [] - for i in range( 4 ): - words.append( s.reg_file[i][0 :16] ) - words.append( s.reg_file[i][16:32] ) - return words - - def line_trace( s ): - state_str = ( - "XCFG" if s.state == s.XCFG else - "WAIT" if s.state == s.WAIT else - "BUSY" if s.state == s.BUSY else - "XXXX" - ) - return "{}(CL :{}){}".format( s.xcel.req, state_str, s.xcel.resp ) diff --git a/examples/ex04_xcel/ChecksumXcelFL.py b/examples/ex04_xcel/ChecksumXcelFL.py deleted file mode 100644 index bb246a0af..000000000 --- a/examples/ex04_xcel/ChecksumXcelFL.py +++ /dev/null @@ -1,55 +0,0 @@ -""" -========================================================================== -ChecksumXcelFL.py -========================================================================== -Functional level implementation of a checksum accelerator. - -Author : Yanghui Ou - Date : June 14, 2019 -""" -from examples.ex02_cksum.ChecksumFL import checksum -from pymtl3 import * -from pymtl3.stdlib.ifcs import mk_xcel_msg, XcelMinionIfcFL - - -# Address space: 0~3: checksum input, 4: go bit, 5: result -class ChecksumXcelFL( Component ): - - def construct( s ): - - # Interface - - ReqType, RespType = mk_xcel_msg( 5, 32 ) - - s.xcel = XcelMinionIfcFL( read=s.read, write=s.write ) - - # Components - - s.reg_file = [ b32(0) for _ in range(6) ] - - s.trace = " " - @update - def up_clear_trace(): - s.trace = " " - - s.add_constraints( U(up_clear_trace) < M(s.read) ) - s.add_constraints( U(up_clear_trace) < M(s.write) ) - - def read( s, addr ): - s.trace = "fl:".format(int(addr)) - return s.reg_file[ int(addr) ] - - def write( s, addr, data ): - s.trace = "fl:".format(int(addr)) - s.reg_file[ int(addr) ] = b32(data) - - # If go bit is written - if s.reg_file[4]: - words = [] - for i in range( 4 ): - words.append( s.reg_file[i][0 :16] ) - words.append( s.reg_file[i][16:32] ) - s.reg_file[5] = checksum( words ) - - def line_trace( s ): - return s.trace diff --git a/examples/ex04_xcel/ChecksumXcelRTL.py b/examples/ex04_xcel/ChecksumXcelRTL.py index 1235623a8..50cf0e478 100644 --- a/examples/ex04_xcel/ChecksumXcelRTL.py +++ b/examples/ex04_xcel/ChecksumXcelRTL.py @@ -9,8 +9,9 @@ """ from examples.ex02_cksum.ChecksumRTL import ChecksumRTL from pymtl3 import * -from pymtl3.stdlib.ifcs import XcelMsgType, mk_xcel_msg, XcelMinionIfcRTL -from pymtl3.stdlib.queues import NormalQueueRTL +from pymtl3.stdlib.xcel import XcelMsgType, mk_xcel_msg +from pymtl3.stdlib.xcel.ifcs import XcelResponderIfc +from pymtl3.stdlib.stream import StreamNormalQueue from pymtl3.stdlib.primitive import Reg @@ -20,7 +21,7 @@ def construct( s ): # Interface ReqType, RespType = mk_xcel_msg( 5, 32 ) - s.xcel = XcelMinionIfcRTL( ReqType, RespType ) + s.xcel = XcelResponderIfc( ReqType, RespType ) # State encoding @@ -30,7 +31,7 @@ def construct( s ): # Components - s.in_q = NormalQueueRTL( ReqType, num_entries=2 ) + s.in_q = StreamNormalQueue( ReqType, num_entries=2 ) s.reg_file = [ Reg( Bits32 ) for _ in range(6) ] s.checksum_unit = ChecksumRTL() @@ -41,34 +42,34 @@ def construct( s ): # Connections - s.xcel.req //= s.in_q.enq - s.checksum_unit.recv.msg[0 :32 ] //= s.reg_file[0].out - s.checksum_unit.recv.msg[32:64 ] //= s.reg_file[1].out - s.checksum_unit.recv.msg[64:96 ] //= s.reg_file[2].out - s.checksum_unit.recv.msg[96:128] //= s.reg_file[3].out + s.xcel.reqstream //= s.in_q.istream + s.checksum_unit.istream.msg[0 :32 ] //= s.reg_file[0].out + s.checksum_unit.istream.msg[32:64 ] //= s.reg_file[1].out + s.checksum_unit.istream.msg[64:96 ] //= s.reg_file[2].out + s.checksum_unit.istream.msg[96:128] //= s.reg_file[3].out # Logic @update def up_start_pulse(): - s.start_pulse @= s.xcel.resp.en & \ - ( s.in_q.deq.ret.type_ == XcelMsgType.WRITE ) & \ - ( s.in_q.deq.ret.addr == 4 ) + s.start_pulse @= (s.xcel.respstream.val & s.xcel.respstream.rdy) & \ + ( s.in_q.ostream.msg.type_ == XcelMsgType.WRITE ) & \ + ( s.in_q.ostream.msg.addr == 4 ) @update def up_state_next(): if s.state == s.XCFG: s.state_next @= ( - s.WAIT if s.start_pulse & ~s.checksum_unit.recv.rdy else - s.BUSY if s.start_pulse & s.checksum_unit.recv.rdy else + s.WAIT if s.start_pulse & ~s.checksum_unit.istream.rdy else + s.BUSY if s.start_pulse & s.checksum_unit.istream.rdy else s.XCFG ) elif s.state == s.WAIT: - s.state_next @= s.BUSY if s.checksum_unit.recv.rdy else s.WAIT + s.state_next @= s.BUSY if s.checksum_unit.istream.rdy else s.WAIT else: # s.state == s.BUSY - s.state_next @= s.XCFG if s.checksum_unit.send.en else s.BUSY + s.state_next @= s.XCFG if s.checksum_unit.ostream.val else s.BUSY @update_ff def up_state(): @@ -80,44 +81,44 @@ def up_state(): @update def up_fsm_output(): if s.state == s.XCFG: - s.in_q.deq.en @= s.in_q.deq.rdy - s.xcel.resp.en @= s.in_q.deq.rdy - s.checksum_unit.recv.en @= s.start_pulse & s.checksum_unit.recv.rdy - s.checksum_unit.send.rdy @= 1 + s.in_q.ostream.rdy @= s.in_q.ostream.val + s.xcel.respstream.val @= s.in_q.ostream.val + s.checksum_unit.istream.val @= s.start_pulse & s.checksum_unit.istream.rdy + s.checksum_unit.ostream.rdy @= 1 elif s.state == s.WAIT: - s.in_q.deq.en @= 0 - s.xcel.resp.en @= 0 - s.checksum_unit.recv.en @= s.checksum_unit.recv.rdy - s.checksum_unit.send.rdy @= 1 + s.in_q.ostream.rdy @= 0 + s.xcel.respstream.val @= 0 + s.checksum_unit.istream.val @= s.checksum_unit.istream.rdy + s.checksum_unit.ostream.rdy @= 1 else: # s.state == s.BUSY: - s.in_q.deq.en @= 0 - s.xcel.resp.en @= 0 - s.checksum_unit.recv.en @= 0 - s.checksum_unit.send.rdy @= 1 + s.in_q.ostream.rdy @= 0 + s.xcel.respstream.val @= 0 + s.checksum_unit.istream.val @= 0 + s.checksum_unit.ostream.rdy @= 1 @update def up_resp_msg(): - s.xcel.resp.msg.type_ @= s.in_q.deq.ret.type_ - s.xcel.resp.msg.data @= 0 - if s.in_q.deq.ret.type_ == XcelMsgType.READ: - s.xcel.resp.msg.data @= s.reg_file[ s.in_q.deq.ret.addr[0:3] ].out + s.xcel.respstream.msg.type_ @= s.in_q.ostream.msg.type_ + s.xcel.respstream.msg.data @= 0 + if s.in_q.ostream.msg.type_ == XcelMsgType.READ: + s.xcel.respstream.msg.data @= s.reg_file[ s.in_q.ostream.msg.addr[0:3] ].out @update def up_wr_regfile(): for i in range(6): s.reg_file[i].in_ @= s.reg_file[i].out - if s.in_q.deq.en & (s.in_q.deq.ret.type_ == XcelMsgType.WRITE): + if s.in_q.ostream.val & (s.in_q.ostream.msg.type_ == XcelMsgType.WRITE): for i in range(6): s.reg_file[i].in_ @= ( - s.in_q.deq.ret.data if b5(i) == s.in_q.deq.ret.addr else + s.in_q.ostream.msg.data if b5(i) == s.in_q.ostream.msg.addr else s.reg_file[i].out ) - if s.checksum_unit.send.en: - s.reg_file[5].in_ @= s.checksum_unit.send.msg + if s.checksum_unit.ostream.val: + s.reg_file[5].in_ @= s.checksum_unit.ostream.msg def line_trace( s ): state_str = ( @@ -126,4 +127,4 @@ def line_trace( s ): "BUSY" if s.state == s.BUSY else "XXXX" ) - return "{}(RTL:{}){}".format( s.xcel.req, state_str, s.xcel.resp ) + return "{}(RTL:{}){}".format( s.xcel.reqstream, state_str, s.xcel.respstream ) diff --git a/examples/ex04_xcel/ProcXcel.py b/examples/ex04_xcel/ProcXcel.py index c03f67dd3..1ff2f27b7 100644 --- a/examples/ex04_xcel/ProcXcel.py +++ b/examples/ex04_xcel/ProcXcel.py @@ -10,8 +10,9 @@ from pymtl3 import * from pymtl3.stdlib.connects import connect_pairs -from pymtl3.stdlib.ifcs import RecvIfcRTL, SendIfcRTL, SendIfcFL, GetIfcFL -from pymtl3.stdlib.mem import MemMasterIfcCL, MemMasterIfcFL, MemMasterIfcRTL, mk_mem_msg +from pymtl3.stdlib.mem import mk_mem_msg +from pymtl3.stdlib.mem.ifcs import MemRequesterIfc +from pymtl3.stdlib.stream.ifcs import IStreamIfc, OStreamIfc class ProcXcel( Component ): @@ -30,24 +31,11 @@ def construct( s, ProcClass, XcelClass ): s.xcel = XcelClass() s.xcel.xcel //= s.proc.xcel - if isinstance( s.proc.imem, MemMasterIfcRTL ): # RTL proc - s.mngr2proc = RecvIfcRTL( Bits32 ) - s.proc2mngr = SendIfcRTL( Bits32 ) - s.imem = MemMasterIfcRTL( req_class, resp_class ) - s.dmem = MemMasterIfcRTL( req_class, resp_class ) - - elif isinstance( s.proc.imem, MemMasterIfcCL ): # CL proc - s.mngr2proc = CalleeIfcCL( Type=Bits32 ) - s.proc2mngr = CallerIfcCL( Type=Bits32 ) - s.imem = MemMasterIfcCL( req_class, resp_class ) - s.dmem = MemMasterIfcCL( req_class, resp_class ) - - elif isinstance( s.proc.imem, MemMasterIfcFL ): # FL proc - s.mngr2proc = GetIfcFL( Type=Bits32 ) - s.proc2mngr = SendIfcFL( Type=Bits32 ) - s.imem = MemMasterIfcFL() - s.dmem = MemMasterIfcFL() - + assert isinstance( s.proc.imem, MemRequesterIfc ) + s.mngr2proc = IStreamIfc( Bits32 ) + s.proc2mngr = OStreamIfc( Bits32 ) + s.imem = MemRequesterIfc( req_class, resp_class ) + s.dmem = MemRequesterIfc( req_class, resp_class ) s.mngr2proc //= s.proc.mngr2proc s.proc2mngr //= s.proc.proc2mngr diff --git a/examples/ex04_xcel/proc-xcel-sim b/examples/ex04_xcel/proc-xcel-sim old mode 100644 new mode 100755 index 582255006..81b56d58e --- a/examples/ex04_xcel/proc-xcel-sim +++ b/examples/ex04_xcel/proc-xcel-sim @@ -23,8 +23,8 @@ import struct import sys from pymtl3 import * -from pymtl3.stdlib.mem import MagicMemoryCL, mk_mem_msg -from pymtl3.stdlib.test_utils import TestSinkCL, TestSrcCL +from pymtl3.stdlib.mem import MemoryFL, mk_mem_msg +from pymtl3.stdlib.stream import StreamSourceFL, StreamSinkFL # Hack to add project root to python path sim_dir = os.path.dirname( os.path.abspath( __file__ ) ) @@ -35,15 +35,11 @@ while sim_dir: sim_dir = os.path.dirname(sim_dir) from examples.ex03_proc.NullXcel import NullXcelRTL -from examples.ex03_proc.ProcCL import ProcCL -from examples.ex03_proc.ProcFL import ProcFL from examples.ex03_proc.ProcRTL import ProcRTL from examples.ex03_proc.SparseMemoryImage import SparseMemoryImage from examples.ex03_proc.test.harness import TestHarness from examples.ex03_proc.tinyrv0_encoding import assemble from examples.ex03_proc.ubmark.proc_ubmark_cksum_roll import ubmark_cksum_roll -from examples.ex04_xcel.ChecksumXcelCL import ChecksumXcelCL -from examples.ex04_xcel.ChecksumXcelFL import ChecksumXcelFL from examples.ex04_xcel.ChecksumXcelRTL import ChecksumXcelRTL from examples.ex04_xcel.ProcXcel import ProcXcel from examples.ex04_xcel.ubmark.proc_ubmark_cksum_xcel_roll import ubmark_cksum_xcel_roll @@ -83,13 +79,9 @@ def parse_cmdline(): return opts proc_impl_dict = { - "fl" : ProcFL, - "cl" : ProcCL, "rtl": ProcRTL, } xcel_impl_dict = { - "fl" : ChecksumXcelFL, - "cl" : ChecksumXcelCL, "rtl" : ChecksumXcelRTL, "null" : NullXcelRTL, } @@ -110,13 +102,14 @@ class TestHarness(Component): mem_stall_prob, mem_latency ): s.commit_inst = OutPort( Bits1 ) - s.src = TestSrcCL ( Bits32, [], src_delay, src_delay ) - s.sink = TestSinkCL( Bits32, [], sink_delay, sink_delay ) - s.mem = MagicMemoryCL ( 2, latency = mem_latency ) + s.src = StreamSourceFL( Bits32, [], src_delay, src_delay ) + s.sink = StreamSinkFL( Bits32, [], sink_delay, sink_delay ) + s.mem = MemoryFL(2, mem_ifc_dtypes = [mk_mem_msg(8,32,32), mk_mem_msg(8,32,32)], + extra_latency = mem_latency ) s.dut = m = ProcXcel( ProcClass, XcelClass ) - m.mngr2proc //= s.src.send - m.proc2mngr //= s.sink.recv + m.mngr2proc //= s.src.ostream + m.proc2mngr //= s.sink.istream m.imem //= s.mem.ifc[0] m.dmem //= s.mem.ifc[1] @@ -219,7 +212,7 @@ def main(): model.dut.set_metadata( YosysTranslationImportPass.enable, True ) model = YosysTranslationImportPass()( model ) - model.apply( DefaultPassGroup(print_line_trace=opts.trace) ) + model.apply( DefaultPassGroup(linetrace=opts.trace) ) # Load the program into the model diff --git a/examples/ex04_xcel/test/ChecksumXcelCL_test.py b/examples/ex04_xcel/test/ChecksumXcelCL_test.py deleted file mode 100644 index 0e8f50b7a..000000000 --- a/examples/ex04_xcel/test/ChecksumXcelCL_test.py +++ /dev/null @@ -1,216 +0,0 @@ -""" -========================================================================== -ChecksumXcelCL_test.py -========================================================================== -Tests for cycle level checksum accelerator. - -Author : Yanghui Ou - Date : June 14, 2019 -""" -import pytest - -from examples.ex02_cksum.ChecksumFL import checksum -from examples.ex02_cksum.utils import words_to_b128 -from pymtl3 import * -from pymtl3.stdlib.queues import BypassQueueCL -from pymtl3.stdlib.connects import connect_pairs -from pymtl3.stdlib.ifcs import XcelMsgType, mk_xcel_msg -from pymtl3.stdlib.test_utils import TestSinkCL, TestSrcCL, run_sim - -from ..ChecksumXcelCL import ChecksumXcelCL - -#------------------------------------------------------------------------- -# Helper functions to create a sequence of req/resp msg -#------------------------------------------------------------------------- - -Req, Resp = mk_xcel_msg( 5, 32 ) -rd = XcelMsgType.READ -wr = XcelMsgType.WRITE - -def mk_xcel_transaction( words ): - words = [ b16(x) for x in words ] - bits = words_to_b128( words ) - reqs = [] - reqs.append( Req( wr, 0, bits[0 :32 ] ) ) - reqs.append( Req( wr, 1, bits[32:64 ] ) ) - reqs.append( Req( wr, 2, bits[64:96 ] ) ) - reqs.append( Req( wr, 3, bits[96:128] ) ) - reqs.append( Req( wr, 4, 1 ) ) - reqs.append( Req( rd, 5, 0 ) ) - - resps = [] - resps.append( Resp( wr, 0 ) ) - resps.append( Resp( wr, 0 ) ) - resps.append( Resp( wr, 0 ) ) - resps.append( Resp( wr, 0 ) ) - resps.append( Resp( wr, 0 ) ) - resps.append( Resp( rd, checksum(words) ) ) - - return reqs, resps - -#------------------------------------------------------------------------- -# WrappedChecksumXcelCL -#------------------------------------------------------------------------- -# WrappedChecksumXcelCL is a simple wrapper around the CL accelerator. It -# simply appends an output buffer at its response side so that its -# response can be obtained by calling dequeue. - -class WrappedChecksumXcelCL( Component ): - - def construct( s ): - - s.recv = CalleeIfcCL( Type=Req ) - s.give = CalleeIfcCL( Type=Resp ) - - s.checksum_xcel = ChecksumXcelCL() - s.out_q = BypassQueueCL( num_entries=1 ) - connect_pairs( - s.recv, s.checksum_xcel.xcel.req, - s.checksum_xcel.xcel.resp, s.out_q.enq, - s.out_q.deq, s.give, - ) - - def line_trace( s ): - return s.checksum_xcel.line_trace() - -#------------------------------------------------------------------------- -# Wrap CL Xcel into a function -#------------------------------------------------------------------------- -# [checksum_xcel_cl] creates a checksum accelerator, feeds in the input, -# ticks it, gets the response, and returns the result. - -def checksum_xcel_cl( words ): - assert len( words ) == 8 - - # Create a simulator using CL accelerator - dut = WrappedChecksumXcelCL() - dut.elaborate() - dut.apply( DefaultPassGroup() ) - dut.sim_reset() - - reqs, _ = mk_xcel_transaction( words ) - - for req in reqs: - - # Wait until xcel is ready to accept a request - while not dut.recv.rdy(): - dut.sim_tick() - - # Send the request message to xcel - dut.recv( req ) - dut.sim_tick() - - # Wait until xcel is ready to give a response - while not dut.give.rdy(): - dut.sim_tick() - - resp_msg = dut.give() - - return resp_msg.data - -#------------------------------------------------------------------------- -# Reuse ChecksumXcelFL_test -#------------------------------------------------------------------------- -# We reuse the function tests in ChecksumXcelFL_test. - -from .ChecksumXcelFL_test import ChecksumXcelFL_Tests as BaseTests - -# ''' TUTORIAL TASK '''''''''''''''''''''''''''''''''''''''''''''''''''''' -# Implement the tests for ChecksumXcelCL -# ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''\/ -#; Create a class called ChecksumXcelCL_Tests that inherits from BaseTests -#; and override the cksum_func by calling checksum_xcel_cl. This way helps -#; you reuse all test cases in the ChecksumXcelFL_Tests to test this -#; ChecksumXcelCL model - -class ChecksumXcelCL_Tests( BaseTests ): - - def cksum_func( s, words ): - return checksum_xcel_cl( words ) - -# ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''/\ - -#------------------------------------------------------------------------- -# Test Harness for src/sink based tests -#------------------------------------------------------------------------- -# The test harness has a test source that sends requests to the xcel and a -# test sink that checks the xcel responses. - -class TestHarness( Component ): - - def construct( s, DutType=ChecksumXcelCL, src_msgs=[], sink_msgs=[] ): - - ReqType, RespType = mk_xcel_msg( 5, 32 ) - - s.src = TestSrcCL( ReqType, src_msgs ) - s.dut = DutType() - s.sink = TestSinkCL( ReqType, sink_msgs ) - - connect( s.src.send, s.dut.xcel.req ) - connect( s.dut.xcel.resp, s.sink.recv ) - - def done( s ): - return s.src.done() and s.sink.done() - - def line_trace( s ): - return "{}>{}>{}".format( - s.src.line_trace(), s.dut.line_trace(), s.sink.line_trace() - ) - -#------------------------------------------------------------------------- -# Src/sink based tests -#------------------------------------------------------------------------- -# More adavanced testsing that uses test source and test sink. - -@pytest.mark.usefixtures("cmdline_opts") -class ChecksumXcelCLSrcSink_Tests: - - # [setup_class] will be called by pytest before running all the tests in - # the test class. Here we specify the type of the design under test - # that is used in all test cases. We can easily reuse all the tests in - # this class simply by creating a new test class that inherits from - # this class and overwrite the setup_class to provide a different DUT - # type. - @classmethod - def setup_class( cls ): - cls.DutType = ChecksumXcelCL - - # [run_sim] is a helper function in the test suite that creates a - # simulator and runs test. We can overwrite this function when - # inheriting from the test class to apply different passes to the DUT. - def run_sim( s, th ): - run_sim( th, s.__class__.cmdline_opts ) - - #----------------------------------------------------------------------- - # test_xcel_srcsink_simple - #----------------------------------------------------------------------- - # A simple test case with only 1 xcel transaction. - - def test_xcel_srcsink_simple( s ): - words = [ 1, 2, 3, 4, 5, 6, 7, 8 ] - src_msgs, sink_msgs = mk_xcel_transaction( words ) - - th = TestHarness( s.DutType, src_msgs, sink_msgs ) - s.run_sim( th ) - - #----------------------------------------------------------------------- - # test_xcel_srcsink_multi_msg - #----------------------------------------------------------------------- - # Test the xcel with multiple transactions. - - def test_xcel_srcsink_multi_msg( s ): - seq = [ - [ 1, 2, 3, 4, 5, 6, 7, 8 ], - [ 8, 7, 6, 5, 4, 3, 2, 1 ], - [ 0xf000, 0xff00, 0x1000, 0x2000, 0x5000, 0x6000, 0x7000, 0x8000 ], - ] - - src_msgs = [] - sink_msgs = [] - for words in seq: - reqs, resps = mk_xcel_transaction( words ) - src_msgs.extend( reqs ) - sink_msgs.extend( resps ) - - th = TestHarness( s.DutType, src_msgs, sink_msgs ) - s.run_sim( th ) diff --git a/examples/ex04_xcel/test/ChecksumXcelFL_test.py b/examples/ex04_xcel/test/ChecksumXcelFL_test.py deleted file mode 100644 index 453457781..000000000 --- a/examples/ex04_xcel/test/ChecksumXcelFL_test.py +++ /dev/null @@ -1,59 +0,0 @@ -""" -========================================================================== -ChecksumXcelFL_test.py -========================================================================== -Tests for the functional level checksum accelerator. - -Author : Yanghui Ou - Date : June 14, 2019 -""" -from examples.ex02_cksum.test.ChecksumCL_test import ChecksumCL_Tests as BaseTests -from pymtl3 import * -from pymtl3.stdlib.ifcs import mk_xcel_msg - -from ..ChecksumXcelFL import ChecksumXcelFL - -#------------------------------------------------------------------------- -# Wrap Xcel into a function -#------------------------------------------------------------------------- - -Req, Resp = mk_xcel_msg( 3, 32 ) - -def checksum_xcel_fl( words ): - assert len( words ) == 8 - - # Create a simulator using ChecksumXcelFL - dut = ChecksumXcelFL() - dut.elaborate() - dut.apply( DefaultPassGroup() ) - - # Transfer checksum input - for i in range( 4 ): - data = concat( words[i*2+1], words[i*2] ) - dut.xcel.write( i, data ) - - # Set the go bit - dut.xcel.write( 4, b32(1) ) - - # get result - return dut.xcel.read( 5 ) - -#------------------------------------------------------------------------- -# Test checksum as a function -#------------------------------------------------------------------------- -# We reuse the extened function tests in ex02_cksum.test.ChecksumCL_test. - - -# ''' TUTORIAL TASK '''''''''''''''''''''''''''''''''''''''''''''''''''''' -# Implement the tests for ChecksumXcelFL -# ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''\/ -#; Write a ChecksumXcelFL_Tests class that inherits from BaseTests which -#; is basically ChecksumCL_Tests. ChecksumCL_Tests is developed in Task 2 -#; for the checksum unit. - -class ChecksumXcelFL_Tests( BaseTests ): - - def cksum_func( s, words ): - return checksum_xcel_fl( words ) - -# ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''/\ diff --git a/examples/ex04_xcel/test/ChecksumXcelRTL_test.py b/examples/ex04_xcel/test/ChecksumXcelRTL_test.py index 60ecf138c..f47193443 100644 --- a/examples/ex04_xcel/test/ChecksumXcelRTL_test.py +++ b/examples/ex04_xcel/test/ChecksumXcelRTL_test.py @@ -7,10 +7,45 @@ Author : Yanghui Ou Date : June 14, 2019 """ +import pytest + from pymtl3 import * +from pymtl3.stdlib.xcel import XcelMsgType, mk_xcel_msg +from pymtl3.stdlib.stream import StreamSinkFL, StreamSourceFL +from pymtl3.stdlib.test_utils import run_sim +from ...ex02_cksum.ChecksumFL import checksum +from ...ex02_cksum.utils import words_to_b128 from ..ChecksumXcelRTL import ChecksumXcelRTL -from .ChecksumXcelCL_test import mk_xcel_transaction + +#------------------------------------------------------------------------- +# Helper functions to create a sequence of req/resp msg +#------------------------------------------------------------------------- + +Req, Resp = mk_xcel_msg( 5, 32 ) +rd = XcelMsgType.READ +wr = XcelMsgType.WRITE + +def mk_xcel_transaction( words ): + words = [ b16(x) for x in words ] + bits = words_to_b128( words ) + reqs = [] + reqs.append( Req( wr, 0, bits[0 :32 ] ) ) + reqs.append( Req( wr, 1, bits[32:64 ] ) ) + reqs.append( Req( wr, 2, bits[64:96 ] ) ) + reqs.append( Req( wr, 3, bits[96:128] ) ) + reqs.append( Req( wr, 4, 1 ) ) + reqs.append( Req( rd, 5, 0 ) ) + + resps = [] + resps.append( Resp( wr, 0 ) ) + resps.append( Resp( wr, 0 ) ) + resps.append( Resp( wr, 0 ) ) + resps.append( Resp( wr, 0 ) ) + resps.append( Resp( wr, 0 ) ) + resps.append( Resp( rd, checksum(words) ) ) + + return reqs, resps #------------------------------------------------------------------------- # Wrap Xcel into a function @@ -32,32 +67,32 @@ def checksum_xcel_rtl( words ): for req in reqs: # Wait until xcel is ready to accept a request - dut.xcel.resp.rdy @= 1 - while not dut.xcel.req.rdy: - dut.xcel.req.en @= 0 + dut.xcel.respstream.rdy @= 1 + while not dut.xcel.reqstream.rdy: + dut.xcel.reqstream.val @= 0 dut.sim_tick() # Send a request - dut.xcel.req.en @= 1 - dut.xcel.req.msg @= req + dut.xcel.reqstream.val @= 1 + dut.xcel.reqstream.msg @= req dut.sim_tick() # Wait for response - while not dut.xcel.resp.en: - dut.xcel.req.en @= 0 + while not dut.xcel.respstream.val: + dut.xcel.reqstream.val @= 0 dut.sim_tick() # Get the response message - resp_data = dut.xcel.resp.msg.data + resp_data = dut.xcel.respstream.msg.data return resp_data #------------------------------------------------------------------------- # Reuse ChecksumXcelCL_test #------------------------------------------------------------------------- -# We reuse the function tests in ChecksumXcelFL_test. +# We reuse the function tests in ChecksumRTL_test. -from .ChecksumXcelCL_test import ChecksumXcelCL_Tests as BaseTests +from examples.ex02_cksum.test.ChecksumRTL_test import ChecksumRTL_Tests as BaseTests # ''' TUTORIAL TASK '''''''''''''''''''''''''''''''''''''''''''''''''''''' # Implement the tests for ChecksumXcelRTL @@ -75,15 +110,86 @@ def cksum_func( s, words ): # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''/\ #------------------------------------------------------------------------- -# Src/sink based tests +# Test Harness for src/sink based tests #------------------------------------------------------------------------- -# Here we directly reuse all test cases in ChecksumXcelCL_test. We only -# need to provide a different DutType in the setup_class. +# The test harness has a test source that sends requests to the xcel and a +# test sink that checks the xcel responses. + +class TestHarness( Component ): + + def construct( s, DutType=ChecksumXcelRTL, src_msgs=[], sink_msgs=[] ): + + ReqType, RespType = mk_xcel_msg( 5, 32 ) -from .ChecksumXcelCL_test import ChecksumXcelCLSrcSink_Tests as SrcSinkBaseTests + s.src = StreamSourceFL( ReqType, src_msgs ) + s.dut = DutType() + s.sink = StreamSinkFL( RespType, sink_msgs ) -class ChecksumXcelRTLSrcSink_Tests( SrcSinkBaseTests ): + connect( s.src.ostream, s.dut.xcel.reqstream ) + connect( s.dut.xcel.respstream, s.sink.istream ) + def done( s ): + return s.src.done() and s.sink.done() + + def line_trace( s ): + return "{}>{}>{}".format( + s.src.line_trace(), s.dut.line_trace(), s.sink.line_trace() + ) + +#------------------------------------------------------------------------- +# Src/sink based tests +#------------------------------------------------------------------------- +# More adavanced testsing that uses test source and test sink. + +@pytest.mark.usefixtures("cmdline_opts") +class ChecksumXcelRTLSrcSink_Tests: + + # [setup_class] will be called by pytest before running all the tests in + # the test class. Here we specify the type of the design under test + # that is used in all test cases. We can easily reuse all the tests in + # this class simply by creating a new test class that inherits from + # this class and overwrite the setup_class to provide a different DUT + # type. @classmethod def setup_class( cls ): cls.DutType = ChecksumXcelRTL + + # [run_sim] is a helper function in the test suite that creates a + # simulator and runs test. We can overwrite this function when + # inheriting from the test class to apply different passes to the DUT. + def run_sim( s, th ): + run_sim( th, s.__class__.cmdline_opts ) + + #----------------------------------------------------------------------- + # test_xcel_srcsink_simple + #----------------------------------------------------------------------- + # A simple test case with only 1 xcel transaction. + + def test_xcel_srcsink_simple( s ): + words = [ 1, 2, 3, 4, 5, 6, 7, 8 ] + src_msgs, sink_msgs = mk_xcel_transaction( words ) + + th = TestHarness( s.DutType, src_msgs, sink_msgs ) + s.run_sim( th ) + + #----------------------------------------------------------------------- + # test_xcel_srcsink_multi_msg + #----------------------------------------------------------------------- + # Test the xcel with multiple transactions. + + def test_xcel_srcsink_multi_msg( s ): + seq = [ + [ 1, 2, 3, 4, 5, 6, 7, 8 ], + [ 8, 7, 6, 5, 4, 3, 2, 1 ], + [ 0xf000, 0xff00, 0x1000, 0x2000, 0x5000, 0x6000, 0x7000, 0x8000 ], + ] + + src_msgs = [] + sink_msgs = [] + for words in seq: + reqs, resps = mk_xcel_transaction( words ) + src_msgs.extend( reqs ) + sink_msgs.extend( resps ) + + th = TestHarness( s.DutType, src_msgs, sink_msgs ) + s.run_sim( th ) diff --git a/examples/ex04_xcel/test/ChecksumXcelVRTL_test.py b/examples/ex04_xcel/test/ChecksumXcelVRTL_test.py index 2479c7d90..62989ef8c 100644 --- a/examples/ex04_xcel/test/ChecksumXcelVRTL_test.py +++ b/examples/ex04_xcel/test/ChecksumXcelVRTL_test.py @@ -14,7 +14,7 @@ from pymtl3.stdlib.test_utils.test_helpers import finalize_verilator from ..ChecksumXcelRTL import ChecksumXcelRTL -from .ChecksumXcelCL_test import mk_xcel_transaction +from .ChecksumXcelRTL_test import mk_xcel_transaction #------------------------------------------------------------------------- # Wrap Xcel into a function @@ -45,23 +45,23 @@ def checksum_xcel_vrtl( words ): for req in reqs: # Wait until xcel is ready to accept a request - dut.xcel.resp.rdy @= 1 - while not dut.xcel.req.rdy: - dut.xcel.req.en @= 0 + dut.xcel.respstream.rdy @= 1 + while not dut.xcel.reqstream.rdy: + dut.xcel.reqstream.val @= 0 dut.sim_tick() # Send a request - dut.xcel.req.en @= 1 - dut.xcel.req.msg @= req + dut.xcel.reqstream.val @= 1 + dut.xcel.reqstream.msg @= req dut.sim_tick() # Wait for response - while not dut.xcel.resp.en: - dut.xcel.req.en @= 0 + while not dut.xcel.respstream.val: + dut.xcel.reqstream.val @= 0 dut.sim_tick() # Get the response message - resp_data = dut.xcel.resp.msg.data + resp_data = dut.xcel.respstream.msg.data dut.sim_tick() return resp_data diff --git a/pymtl3/stdlib/delays/DelayPipeCL.py b/pymtl3/stdlib/delays/DelayPipeCL.py deleted file mode 100644 index 601e35bdd..000000000 --- a/pymtl3/stdlib/delays/DelayPipeCL.py +++ /dev/null @@ -1,114 +0,0 @@ -#========================================================================= -# DelayPipeCL.py -#========================================================================= -# This delay pipe models a inelasic pipeline queue with enq/deq interfaces -# -# Author : Shunning Jiang -# Date : May 7, 2018 - -from collections import deque - -from pymtl3 import * -from pymtl3.extra import clone_deepcopy - -# This delay pipe is for cycle-level performance modeling purpose - -class DelayPipeDeqCL( Component ): - - @non_blocking( lambda s: s.pipeline[0] is None ) - def enq( s, msg ): - assert s.pipeline[0] is None - s.pipeline[0] = clone_deepcopy(msg) - - @non_blocking( lambda s: s.pipeline[-1] is not None ) - def deq( s ): - ret = s.pipeline[-1] - s.pipeline[-1] = None - return ret - - @non_blocking( lambda s: True ) - def peek( s ): - assert s.pipeline[-1] is not None - return s.pipeline[-1] - - def construct( s, delay=5, trace_len=0 ): - - s.delay = delay - - s.trace_len = trace_len - - if delay == 0: # This is essentially a bypass queue - s.pipeline = [ None ] - - s.add_constraints( - M(s.enq) < M(s.deq), # bypass behavior - ) - - else: # delay >= 1, pipe behavior - s.pipeline = deque( [None]*(delay+1), maxlen=(delay+1) ) - - @update_once - def up_delay(): - if s.pipeline[-1] is None: - s.pipeline.rotate() - - # Model decoupled pipe behavior to cut cyclic dependencies. - # Basically no matter in what order s.deq and s.enq are called, - # the outcomes are the same as long as up_delay is called before - # both of them. - - # NOTE THAT this up_delay affects ready signal, we need to mark it - # before enq.rdy - s.add_constraints( - U(up_delay) < M(s.peek), - U(up_delay) < M(s.deq), - U(up_delay) < M(s.deq.rdy), - U(up_delay) < M(s.enq), - U(up_delay) < M(s.enq.rdy), - ) - - def line_trace( s ): - return "[{}]".format( "".join( [ " " if x is None else "*" for x in list(s.pipeline)[:-1] ] ) ) - -class DelayPipeSendCL( Component ): - - def enq_pipe( s, msg ): - assert s.pipeline[0] is None - s.pipeline[0] = clone_deepcopy(msg) - - def enq_rdy_pipe( s ): - return s.pipeline[0] is None - - def construct( s, delay=5 ): - - s.send = CallerIfcCL() - - s.delay = delay - - if delay == 0: # combinational behavior - s.enq = CalleeIfcCL() - connect( s.enq, s.send ) - - else: # delay >= 1, pipe behavior - s.enq = CalleeIfcCL( Type=None, method=s.enq_pipe, rdy=s.enq_rdy_pipe ) - s.pipeline = deque( [None]*delay, maxlen=delay ) - - @update_once - def up_delay(): - if s.pipeline[-1] is not None: - if s.send.rdy(): - s.send( s.pipeline[-1] ) - s.pipeline[-1] = None - s.pipeline.rotate() - else: - s.pipeline.rotate() - - s.add_constraints( - M(s.enq) > U(up_delay), # pipe behavior - M(s.enq.rdy) > U(up_delay), # pipe behavior - ) - - def line_trace( s ): - if s.delay > 0: - return "[{}]".format( "".join( [ " " if x is None else "*" for x in s.pipeline ] ) ) - return "" diff --git a/pymtl3/stdlib/delays/StallCL.py b/pymtl3/stdlib/delays/StallCL.py deleted file mode 100644 index 112bf5117..000000000 --- a/pymtl3/stdlib/delays/StallCL.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -========================================================================= - StallCL.py -========================================================================= - Models random stall - - Author : Shunning Jiang - Date : Feb 6, 2020 -""" - -from random import Random - -from pymtl3 import * - -# This stall is for testing purpose -# Recv side has a random stall - -class StallCL( Component ): - - # ready <==> stall_rand > stall_prob - @non_blocking( lambda s: s.stall_rgen.random() > s.stall_prob and s.send.rdy() ) - def recv( s, msg ): - s.send( msg ) - - def construct( s, stall_prob=0.5, stall_seed=0x1 ): - - s.send = CallerIfcCL() - - s.stall_prob = stall_prob - s.stall_rgen = Random( stall_seed ) # Separate randgen for each injector - - s.add_constraints( - M(s.recv) == M(s.send), # pass_through - M(s.recv.rdy) == M(s.send.rdy), # pass_through - ) - - - def line_trace( s ): - return f"{s.recv}" diff --git a/pymtl3/stdlib/delays/__init__.py b/pymtl3/stdlib/delays/__init__.py deleted file mode 100644 index 090746e73..000000000 --- a/pymtl3/stdlib/delays/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .DelayPipeCL import DelayPipeDeqCL, DelayPipeSendCL -from .StallCL import StallCL diff --git a/pymtl3/stdlib/delays/test/DelayPipeCL_test.py b/pymtl3/stdlib/delays/test/DelayPipeCL_test.py deleted file mode 100644 index 5f50cf376..000000000 --- a/pymtl3/stdlib/delays/test/DelayPipeCL_test.py +++ /dev/null @@ -1,108 +0,0 @@ -#========================================================================= -# DelayPipeCL_test.py -#========================================================================= -# -# Author : Shunning Jiang -# Date : May 7, 2018 - -import pytest - -from pymtl3 import * -from pymtl3.stdlib.test_utils import TestSinkCL, TestSrcCL, mk_test_case_table, run_sim - -from ..DelayPipeCL import DelayPipeDeqCL, DelayPipeSendCL - -#------------------------------------------------------------------------- -# TestHarness -#------------------------------------------------------------------------- - -class TestHarness( Component ): - - def construct( s, dut_class, src_msgs, sink_msgs, latency, src_lat, sink_lat ): - - # Messge type - - # Instantiate models - - s.src = TestSrcCL( None, src_msgs, 0, src_lat ) - s.dut = dut_class( latency ) - s.sink = TestSinkCL( None, sink_msgs, 0, sink_lat ) - - # Connect - - connect( s.src.send, s.dut.enq ) - - if dut_class is DelayPipeDeqCL: - @update_once - def up_adapt(): - if s.dut.deq.rdy() and s.sink.recv.rdy(): - s.sink.recv( s.dut.deq() ) - - elif dut_class is DelayPipeSendCL: - connect( s.dut.send, s.sink.recv ) - - def done( s ): - return s.src.done() and s.sink.done() - - def line_trace(s ): - return s.src.line_trace() + " >>> " + s.dut.line_trace() + " >>> " + s.sink.line_trace() - -#------------------------------------------------------------------------- -# Test Case Table -#------------------------------------------------------------------------- - -def basic_msgs(): - return [ - 1, 1, - 2, 2, - 3, 3, - 4, 4, - 5, 5, - 6, 6, - 7, 7, - 8, 8, - 9, 9, - 10, 10, - ] - -#------------------------------------------------------------------------- -# Test cases -#------------------------------------------------------------------------- - -@pytest.mark.parametrize( **mk_test_case_table([ - ( "msg_func lat src_lat sink_lat "), - [ "basic", basic_msgs, 0, 0, 0 ], - [ "basic_lat1", basic_msgs, 1, 0, 0 ], - [ "basic_lat2", basic_msgs, 2, 0, 0 ], - [ "basic_lat3", basic_msgs, 3, 0, 0 ], - [ "basic_lat4", basic_msgs, 4, 0, 0 ], - [ "basic_lat10", basic_msgs, 10, 0, 0 ], - [ "basic_3_14", basic_msgs, 0, 3, 14 ], - [ "basic_lat1_3_14", basic_msgs, 1, 3, 14 ], - [ "basic_lat4_3_14", basic_msgs, 4, 3, 14 ], - [ "basic_lat10_3_14", basic_msgs, 10, 3, 14 ], -]) ) -def test_delay_pipe_deq( test_params, cmdline_opts ): - msgs = test_params.msg_func() - run_sim( TestHarness( DelayPipeDeqCL, msgs[::2], msgs[1::2], - test_params.lat, - test_params.src_lat, test_params.sink_lat ) ) - -@pytest.mark.parametrize( **mk_test_case_table([ - ( "msg_func lat src_lat sink_lat "), - [ "basic", basic_msgs, 0, 0, 0 ], - [ "basic_lat1", basic_msgs, 1, 0, 0 ], - [ "basic_lat2", basic_msgs, 2, 0, 0 ], - [ "basic_lat3", basic_msgs, 3, 0, 0 ], - [ "basic_lat4", basic_msgs, 4, 0, 0 ], - [ "basic_lat10", basic_msgs, 10, 0, 0 ], - [ "basic_3_14", basic_msgs, 0, 3, 14 ], - [ "basic_lat1_3_14", basic_msgs, 1, 3, 14 ], - [ "basic_lat4_3_14", basic_msgs, 4, 3, 14 ], - [ "basic_lat10_3_14", basic_msgs, 10, 3, 14 ], -]) ) -def test_delay_pipe_send( test_params, cmdline_opts ): - msgs = test_params.msg_func() - run_sim( TestHarness( DelayPipeSendCL, msgs[::2], msgs[1::2], - test_params.lat, - test_params.src_lat, test_params.sink_lat ) ) diff --git a/pymtl3/stdlib/ifcs/__init__.py b/pymtl3/stdlib/ifcs/__init__.py deleted file mode 100644 index f68cc595f..000000000 --- a/pymtl3/stdlib/ifcs/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -# from .EnqDeqIfc import DeqIfcFL, DeqIfcRTL, EnqIfcFL, EnqIfcRTL -from .get_give_ifcs import GetIfcFL, GetIfcRTL, GiveIfcFL, GiveIfcRTL -from .master_minion_ifcs import MasterIfcCL, MasterIfcRTL, MinionIfcCL, MinionIfcRTL -from .send_recv_ifcs import ( - RecvCL2SendRTL, - RecvIfcFL, - RecvIfcRTL, - RecvRTL2SendCL, - SendIfcFL, - SendIfcRTL, -) -from .xcel_ifcs import ( - XcelMasterIfcCL, - XcelMasterIfcFL, - XcelMasterIfcRTL, - XcelMinionIfcCL, - XcelMinionIfcFL, - XcelMinionIfcRTL, -) -from .XcelMsg import XcelMsgType, mk_xcel_msg, mk_xcel_req_msg, mk_xcel_resp_msg diff --git a/pymtl3/stdlib/ifcs/get_give_ifcs.py b/pymtl3/stdlib/ifcs/get_give_ifcs.py deleted file mode 100644 index 02dcb3978..000000000 --- a/pymtl3/stdlib/ifcs/get_give_ifcs.py +++ /dev/null @@ -1,248 +0,0 @@ -""" -======================================================================== -GetGiveIfc.py -======================================================================== -RTL implementation of en/rdy micro-protocol. - -Author: Yanghui Ou - Date: Mar 19, 2019 -""" -import greenlet - -from pymtl3 import * -from pymtl3.dsl.errors import InvalidConnectionError -from pymtl3.extra import clone_deepcopy -from pymtl3.stdlib.connects import connect_pairs - -from .send_recv_ifcs import RecvIfcRTL - -#------------------------------------------------------------------------- -# GetIfcRTL -#------------------------------------------------------------------------- - -class GetIfcRTL( CallerIfcRTL ): - def construct( s, Type ): - super().construct( en=True, rdy=True, MsgType=None, RetType=Type ) - -#------------------------------------------------------------------------- -# GiveIfcRTL -#------------------------------------------------------------------------- - -class And( Component ): - - def construct( s, Type ): - s.in0 = InPort( Type ) - s.in1 = InPort( Type ) - s.out = OutPort( Type ) - - @update - def up_and(): - s.out @= s.in0 & s.in1 - -class GiveIfcRTL( CalleeIfcRTL ): - def construct( s, Type ): - super().construct( en=True, rdy=True, MsgType=None, RetType=Type ) - - def connect( s, other, parent ): - # We are doing GiveIfcRTL (s) -> [ AND ] -> RecvIfcRTL (other) - # Basically we AND the rdy of both sides for enable - if isinstance( other, RecvIfcRTL ): - connect( s.ret, other.msg ) - - m = And( Bits1 ) - - if hasattr( parent, "give_recv_ander_cnt" ): - cnt = parent.give_recv_ander_cnt - setattr( parent, "give_recv_ander_" + str( cnt ), m ) - else: - parent.give_recv_ander_cnt = 0 - parent.give_recv_ander_0 = m - - connect_pairs( - m.in0, s.rdy, - m.in1, other.rdy, - m.out, s.en, - m.out, other.en, - ) - parent.give_recv_ander_cnt += 1 - return True - - elif isinstance( other, CalleeIfcCL ): - if s._dsl.level <= other._dsl.level: - raise InvalidConnectionError( - "CL2RTL connection is not supported between GiveIfcRTL" - " and CalleeIfcCL.\n" - " - level {}: {} (class {})\n" - " - level {}: {} (class {})".format( - s._dsl.level, repr( s ), type( s ), other._dsl.level, - repr( other ), type( other ) ) ) - - m = GetRTL2GiveCL( s.MsgType ) - - if hasattr( parent, "GetRTL2GiveCL_count" ): - count = parent.GetRTL2GiveCL_count - setattr( parent, "GetRTL2GiveCL_" + str( count ), m ) - else: - parent.GetRTL2GiveCL_count = 0 - parent.GetRTL2GiveCL_0 = m - - connect_pairs( - s, m.get, - m.give, other, - ) - parent.GetRTL2GiveCL_count += 1 - return True - - return False - -class GetIfcFL( CallerIfcFL ): - - def connect( s, other, parent ): - - # We are doing SendCL (other) -> [ RecvCL -> GiveIfcFL ] -> GetIfcFL (s) - # SendCL is a caller interface - if isinstance( other, CallerIfcCL ): - m = RecvCL2GiveFL() - - if hasattr( parent, "RecvCL2GiveFL_count" ): - count = parent.RecvCL2GiveFL_count - setattr( parent, "RecvCL2GiveFL_" + str( count ), m ) - else: - parent.RecvCL2GiveFL_count = 0 - parent.RecvCL2GiveFL_0 = m - - connect_pairs( - other, m.recv, - m.give, s - ) - parent.RecvCL2GiveFL_count += 1 - return True - - elif isinstance( other, RecvIfcRTL ): - m = RecvRTL2GiveFL(other.MsgType) - - if hasattr( parent, "RecvRTL2GiveFL_count" ): - count = parent.RecvRTL2GiveFL_count - setattr( parent, "RecvRTL2GiveFL_" + str( count ), m ) - else: - parent.RecvRTL2GiveFL_count = 0 - parent.RecvRTL2GiveFL_0 = m - - connect_pairs( - other, m.recv, - m.give, s - ) - parent.RecvRTL2GiveFL_count += 1 - return True - - return False - -class GiveIfcFL( CalleeIfcFL ): - pass - -#------------------------------------------------------------------------- -# GetRTL2GiveCL -#------------------------------------------------------------------------- - -class GetRTL2GiveCL( Component ): - - def construct( s, MsgType ): - # Interface - s.get = GetIfcRTL( MsgType ) - - s.entry = None - - @update - def up_get_rtl(): - if s.entry is None and s.get.rdy: - s.get.en @= 1 - else: - s.get.en @= 0 - - @update - def up_entry(): - if s.get.en: - s.entry = clone_deepcopy( s.get.msg ) - - s.add_constraints( - U( up_get_rtl ) < M( s.give ), - U( up_get_rtl ) < M( s.give.rdy ), - U( up_entry ) < M( s.give ), - U( up_entry ) < M( s.give.rdy ), - ) - - @non_blocking( lambda s : s.entry is not None ) - def give( s ): - tmp = s.entry - s.entry = None - return tmp - - def line_trace( s ): - return "{}(){}".format( s.get, s.give ) - -#------------------------------------------------------------------------- -# RecvCL2SendRTL -#------------------------------------------------------------------------- - -class RecvCL2GiveFL( Component ): - - @blocking - def give( s ): - while s.entry is None: - greenlet.getcurrent().parent.switch(0) - ret = s.entry - s.entry = None - return ret - - @non_blocking( lambda s : s.entry is None ) - def recv( s, msg ): - s.entry = msg - - def construct( s ): - - # Interface - - s.entry = None - - s.add_constraints( M( s.recv ) > M( s.give ) ) # pipe behavior - - def line_trace( s ): - return "{}(){}".format( s.recv, s.give ) - -#------------------------------------------------------------------------- -# RecvRTL2GiveFL -#------------------------------------------------------------------------- - -class RecvRTL2GiveFL( Component ): - - @blocking - def give( s ): - while s.entry is None: - greenlet.getcurrent().parent.switch(0) - ret = s.entry - s.entry = None - return ret - - def construct( s, MsgType ): - - # Interface - - s.recv = RecvIfcRTL( MsgType ) - - s.entry = None - - @update_once - def up_recv_rtl_rdy(): - s.recv.rdy @= s.entry is not None - - @update_once - def up_recv_cl(): - s.entry = None - if s.recv.en: - assert s.entry is None - s.entry = deepcopy( s.recv.msg ) - - s.add_constraints( U( up_recv_cl ) < M(s.give) ) # bypass - - def line_trace( s ): - return "{}(){}".format( s.recv, s.send ) diff --git a/pymtl3/stdlib/ifcs/master_minion_ifcs.py b/pymtl3/stdlib/ifcs/master_minion_ifcs.py deleted file mode 100644 index a22fc9f38..000000000 --- a/pymtl3/stdlib/ifcs/master_minion_ifcs.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -========================================================================== - master_minion_ifc.py -========================================================================== -Master/minion send/recv interface implementations at CL and RTL. - Author: Shunning Jiang - Date: Jan 28, 2020 -""" -from pymtl3 import * - -from .send_recv_ifcs import RecvIfcRTL, SendIfcRTL - -#------------------------------------------------------------------------- -# CL interfaces -#------------------------------------------------------------------------- - -# There is no custom connect method in CL ifcs. -# For MasterCL-MinionRTL and MasterRTL-MinionCL connections, we just need -# to connect by name and leverage the custom connect method of the nested -# Send/RecvIfc. - -class MasterIfcCL( Interface ): - def construct( s, ReqType, RespType, resp=None, resp_rdy=None ): - s.ReqType = ReqType - s.RespType = RespType - s.req = CallerIfcCL( Type=ReqType ) - s.resp = CalleeIfcCL( Type=RespType, method=resp, rdy=resp_rdy ) - def __str__( s ): - return f"{s.req}|{s.resp}" - -class MinionIfcCL( Interface ): - def construct( s, ReqType, RespType, req=None, req_rdy=None ): - s.ReqType = ReqType - s.RespType = RespType - s.req = CalleeIfcCL( Type=ReqType, method=req, rdy=req_rdy ) - s.resp = CallerIfcCL( Type=RespType ) - def __str__( s ): - return f"{s.req}|{s.resp}" - -#------------------------------------------------------------------------- -# RTL interfaces -#------------------------------------------------------------------------- -# There is no custom connect method in CL ifcs. -# For MasterCL-MinionRTL and MasterRTL-MinionCL connections, we just need -# to connect by name and leverage the custom connect method of the nested -# Send/RecvIfc. - -class MasterIfcRTL( Interface ): - def construct( s, ReqType, RespType ): - s.ReqType = ReqType - s.RespType = RespType - s.req = SendIfcRTL( Type=ReqType ) - s.resp = RecvIfcRTL( Type=RespType ) - def __str__( s ): - return f"{s.req}|{s.resp}" - -class MinionIfcRTL( Interface ): - def construct( s, ReqType, RespType ): - s.ReqType = ReqType - s.RespType = RespType - s.req = RecvIfcRTL( Type=ReqType ) - s.resp = SendIfcRTL( Type=RespType ) - def __str__( s ): - return f"{s.req}|{s.resp}" diff --git a/pymtl3/stdlib/ifcs/send_recv_ifcs.py b/pymtl3/stdlib/ifcs/send_recv_ifcs.py deleted file mode 100644 index fb60e1b20..000000000 --- a/pymtl3/stdlib/ifcs/send_recv_ifcs.py +++ /dev/null @@ -1,311 +0,0 @@ -""" -======================================================================== -SendRecvIfc.py -======================================================================== -RTL implementation of en/rdy micro-protocol. - -Author: Yanghui Ou, Shunning Jiang - Date: May 5, 2019 -""" -import greenlet - -from pymtl3 import * -from pymtl3.dsl.errors import InvalidConnectionError -from pymtl3.extra import clone_deepcopy -from pymtl3.stdlib.connects import connect_pairs - -#------------------------------------------------------------------------- -# RecvIfcRTL -#------------------------------------------------------------------------- - -class RecvIfcRTL( CalleeIfcRTL ): - - def construct( s, Type ): - super().construct( en=True, rdy=True, MsgType=Type, RetType=None ) - - def connect( s, other, parent ): - - # We are doing SendCL (other) -> [ RecvCL -> SendRTL ] -> RecvRTL (s) - # SendCL is a caller interface - if isinstance( other, CallerIfcCL ): - m = RecvCL2SendRTL( s.MsgType ) - - if hasattr( parent, "RecvCL2SendRTL_count" ): - count = parent.RecvCL2SendRTL_count - setattr( parent, "RecvCL2SendRTL_" + str( count ), m ) - else: - parent.RecvCL2SendRTL_count = 0 - parent.RecvCL2SendRTL_0 = m - - connect_pairs( - other, m.recv, - m.send.msg, s.msg, - m.send.en, s.en, - m.send.rdy, s.rdy - ) - parent.RecvCL2SendRTL_count += 1 - return True - - elif isinstance( other, CalleeIfcCL ): - if s._dsl.level <= other._dsl.level: - raise InvalidConnectionError( - "CL2RTL connection is not supported between RecvIfcRTL" - " and CalleeIfcCL.\n" - " - level {}: {} (class {})\n" - " - level {}: {} (class {})".format( - s._dsl.level, repr( s ), type( s ), other._dsl.level, - repr( other ), type( other ) ) ) - - m = RecvCL2SendRTL( s.MsgType ) - - if hasattr( parent, "RecvCL2SendRTL_count" ): - count = parent.RecvCL2SendRTL_count - setattr( parent, "RecvCL2SendRTL_" + str( count ), m ) - else: - parent.RecvCL2SendRTL_count = 0 - parent.RecvCL2SendRTL_0 = m - - connect_pairs( - other, m.recv, - m.send.msg, s.msg, - m.send.en, s.en, - m.send.rdy, s.rdy - ) - parent.RecvCL2SendRTL_count += 1 - return True - - return False - -#------------------------------------------------------------------------- -# SendIfcRTL -#------------------------------------------------------------------------- - -class SendIfcRTL( CallerIfcRTL ): - - def construct( s, Type ): - super().construct( en=True, rdy=True, MsgType=Type, RetType=None ) - - def connect( s, other, parent ): - - # We are doing SendRTL (s) -> [ RecvRTL -> SendCL ] -> RecvCL (other) - # RecvCL is a callee interface - if isinstance( other, CalleeIfcCL ): - m = RecvRTL2SendCL( s.MsgType ) - - if hasattr( parent, "RecvRTL2SendCL_count" ): - count = parent.RecvRTL2SendCL_count - setattr( parent, "RecvRTL2SendCL_" + str( count ), m ) - else: - parent.RecvRTL2SendCL_count = 0 - parent.RecvRTL2SendCL_0 = m - - connect_pairs( - m.send, other, - s.msg, m.recv.msg, - s.en, m.recv.en, - s.rdy, m.recv.rdy, - ) - parent.RecvRTL2SendCL_count += 1 - return True - - return False - - -class SendIfcFL( CallerIfcFL ): - pass - - def connect( s, other, parent ): - - # We are doing SendFL (s) -> [ RecvFL -> SendCL ] -> RecvCL (s) - # SendCL is a caller interface - # FIXME direction - if isinstance( other, CallerIfcCL ) or \ - isinstance( other, CalleeIfcCL ): - m = RecvFL2SendCL() - - if hasattr( parent, "RecvFL2SendCL_count" ): - count = parent.RecvFL2SendCL_count - setattr( parent, "RecvFL2SendCL_" + str( count ), m ) - else: - parent.RecvFL2SendCL_count = 0 - parent.RecvFL2SendCL_0 = m - - connect_pairs( - s, m.recv, - m.send, other, - ) - parent.RecvFL2SendCL_count += 1 - return True - - elif isinstance( other, SendIfcRTL ): - m = RecvFL2SendRTL( other.MsgType ) - - if hasattr( parent, "RecvFL2SendRTL_count" ): - count = parent.RecvFL2SendRTL_count - setattr( parent, "RecvFL2SendRTL_" + str( count ), m ) - else: - parent.RecvFL2SendRTL_count = 0 - parent.RecvFL2SendRTL_0 = m - - connect_pairs( - s, m.recv, - m.send, other, - ) - parent.RecvFL2SendRTL_count += 1 - return True - - return False - -class RecvIfcFL( CalleeIfcFL ): - pass - -""" -======================================================================== -Send/RecvIfc adapters -======================================================================== -CL/RTL adapters for send/recv interface. - -Author : Yanghui Ou - Date : Mar 07, 2019 -""" - -#------------------------------------------------------------------------- -# RecvCL2SendRTL -#------------------------------------------------------------------------- - -class RecvCL2SendRTL( Component ): - - def construct( s, MsgType ): - - # Interface - - s.send = SendIfcRTL( MsgType ) - - s.entry = None - - @update_once - def up_clear(): - if s.send.en: # constraints reverse this - s.entry = None - - @update - def up_send_rtl(): - if s.entry is None: - s.send.en @= b1( 0 ) - else: - s.send.en @= b1( s.send.rdy ) - s.send.msg @= s.entry - - s.add_constraints( - U( up_clear ) < WR( s.send.en ), - U( up_clear ) < M( s.recv ), - U( up_clear ) < M( s.recv.rdy ), - M( s.recv ) < U( up_send_rtl ), - M( s.recv.rdy ) < U( up_send_rtl ) - ) - - @non_blocking( lambda s : s.entry is None ) - def recv( s, msg ): - s.entry = clone_deepcopy( msg ) - - def line_trace( s ): - return "{}(){}".format( s.recv, s.send ) - -#------------------------------------------------------------------------- -# RecvRTL2SendCL -#------------------------------------------------------------------------- - -class RecvRTL2SendCL( Component ): - - def construct( s, MsgType ): - - # Interface - - s.recv = RecvIfcRTL( MsgType ) - s.send = CallerIfcCL() - - s.sent_msg = None - s.send_rdy = False - - @update_once - def up_recv_rtl_rdy(): - s.send_rdy = s.send.rdy() & ~s.reset - s.recv.rdy @= s.send_rdy - - @update_once - def up_send_cl(): - s.sent_msg = None - if s.recv.en: - s.send( s.recv.msg ) - s.sent_msg = s.recv.msg - - s.add_constraints( U( up_recv_rtl_rdy ) < U( up_send_cl ) ) - - def line_trace( s ): - return "{}(){}".format( - s.recv.line_trace(), - enrdy_to_str( s.sent_msg, s.sent_msg is not None, s.send_rdy ) - ) - -#------------------------------------------------------------------------- -# RecvFL2SendCL -#------------------------------------------------------------------------- - -class RecvFL2SendCL( Component ): - - @blocking - def recv( s, msg ): - while not s.send.rdy(): - greenlet.getcurrent().parent.switch(0) - assert s.send.rdy() - s.send( msg ) - - def construct( s ): - - # Interface - - s.send = CallerIfcCL() - - s.add_constraints( M( s.recv ) == M( s.send ) ) - - def line_trace( s ): - return "{}(){}".format( s.recv, s.send ) - -#------------------------------------------------------------------------- -# RecvFL2SendRTL -#------------------------------------------------------------------------- - -class RecvFL2SendRTL( Component ): - - def recv( s, msg ): - while s.entry is not None: - greenlet.getcurrent().parent.switch(0) - s.entry = msg - - def construct( s, MsgType ): - - # Interface - - s.recv = RecvIfcFL( method=s.recv ) - s.send = SendIfcRTL( MsgType ) - - s.entry = None - - @update - def up_clear(): - if s.send.en & (s.entry is not None): - s.entry = None - - @update - def up_fl_send_rtl(): - if s.send.rdy & (s.entry is not None): - s.send.en @= 1 - s.send.msg @= s.entry - else: - s.send.en @= 0 - - s.add_constraints( M( s.recv ) < U(up_fl_send_rtl), - U( up_clear ) < WR( s.send.en ) ) - - def line_trace( s ): - return "{}(){}".format( s.recv, s.send ) diff --git a/pymtl3/stdlib/ifcs/test/__init__.py b/pymtl3/stdlib/ifcs/test/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pymtl3/stdlib/ifcs/test/xcel_ifcs_test.py b/pymtl3/stdlib/ifcs/test/xcel_ifcs_test.py deleted file mode 100644 index dbfaf32df..000000000 --- a/pymtl3/stdlib/ifcs/test/xcel_ifcs_test.py +++ /dev/null @@ -1,463 +0,0 @@ -""" -========================================================================== - xcel_ifcs_test.py -========================================================================== - -Author : Yanghui Ou - Date : June 4, 2019 -""" - -from pymtl3 import * -from pymtl3.stdlib.primitive import RegisterFile -from pymtl3.stdlib.ifcs import XcelMsgType, mk_xcel_msg -from pymtl3.stdlib.queues import NormalQueueRTL -from pymtl3.stdlib.test_utils import run_sim - -from ..xcel_ifcs import ( - XcelMasterIfcCL, - XcelMasterIfcFL, - XcelMasterIfcRTL, - XcelMinionIfcCL, - XcelMinionIfcFL, - XcelMinionIfcRTL, -) - -#------------------------------------------------------------------------- -# FL master/minion -#------------------------------------------------------------------------- - -class SomeMasterNonBlockingFL( Component ): - - # Actually we don't need ReqType and RespType for FL models. It's just - # for polymorphic instantiation in the test harness, since CL/RTL models - # have these two parameters. - def construct( s, ReqType, RespType, nregs=16 ): - s.xcel = XcelMasterIfcFL() - - s.addr = 0 - s.nregs = nregs - s.trace = "" - - @update_once - def up_master_while(): - while s.addr < s.nregs: - s.trace = "# " - wr_data = 0xbabe0000 | s.addr - s.xcel.write( s.addr, wr_data ) - s.trace = "wr:{:x}:{:x}".format( int(s.addr), int(wr_data) ) - rd_data = s.xcel.read( s.addr ) - assert rd_data == wr_data, "{} {}".format( hex(int(rd_data)), hex(int(wr_data)) ) - s.trace = "rd:{:x}:{:x}".format( int(s.addr), int(rd_data) ) - s.addr += 1 - - def done( s ): - return s.addr >= s.nregs - - def line_trace( s ): - ret = s.trace - s.trace = "# " - return ret - -class SomeMasterBlockingFL( Component ): - - def construct( s, ReqType, RespType, nregs=16 ): - s.xcel = XcelMasterIfcFL() - - s.addr = 0 - s.nregs = nregs - s.trace = "" - - @update_once - def up_master_noloop(): - if s.addr < s.nregs: - s.trace = "# " - wr_data = 0xbabe0000 | s.addr - s.xcel.write( s.addr, wr_data ) - s.trace = "wr:{:x}:{:x}".format( s.addr, wr_data ) - rd_data = s.xcel.read( s.addr ) - assert rd_data == wr_data, "{} {}".format( hex(int(rd_data)), hex(int(wr_data)) ) - s.trace = "rd:{:x}:{:x}".format( int(s.addr), int(rd_data) ) - s.addr += 1 - - def done( s ): - return s.addr >= s.nregs - - def line_trace( s ): - ret = s.trace - s.trace = "# " - return ret - -class SomeMinionFL( Component ): - - def read_method( s, addr ): - return s.reg_file[ int(addr) ] - - def write_method( s, addr, data ): - s.reg_file[ int(addr) ] = data - - def construct( s, ReqType, RespType, nregs=16 ): - s.xcel = XcelMinionIfcFL( read=s.read_method, write=s.write_method ) - s.reg_file = [ 0 for _ in range( nregs ) ] - - def line_trace( s ): - return s.xcel.line_trace() - -#------------------------------------------------------------------------- -# CL master/minion -#------------------------------------------------------------------------- - -class SomeMasterCL( Component ): - - def recv( s, msg ): - if msg.type_ == XcelMsgType.READ: - assert msg.data == 0xface0000 | s.addr-1 - s.count += 1 - - def recv_rdy( s ): - return True - - def construct( s, ReqType, RespType, nregs=16 ): - s.xcel = XcelMasterIfcCL( ReqType, RespType, resp=s.recv, resp_rdy=s.recv_rdy ) - s.addr = 0 - s.count = 0 - s.nregs = nregs - s.flag = True - - DataType = ReqType.get_field_type( 'data' ) - assert DataType is RespType.get_field_type( 'data' ) - AddrType = ReqType.get_field_type( 'addr' ) - - @update_once - def up_master_req(): - if s.xcel.req.rdy(): - if s.flag: - s.xcel.req( ReqType( XcelMsgType.WRITE, AddrType(s.addr, trunc_int=True), - DataType(0xface0000 | s.addr) ) ) - s.flag = not s.flag - else: - s.xcel.req( ReqType( XcelMsgType.READ, AddrType(s.addr, trunc_int=True), - DataType(0) ) ) - s.addr += 1 - s.flag = not s.flag - - def done( s ): - return s.count == s.nregs - - def line_trace( s ): - return str( s.xcel ) - -class SomeMinionCL( Component ): - - def recv( s, msg ): - assert s.entry is None - s.entry = msg - - def recv_rdy( s ): - return s.entry is None - - def read( s, addr ): - addr = int(addr) - return s.reg_file[ addr ] - - def write( s, addr, data ): - addr = int(addr) - s.reg_file[ addr ] = data - - def construct( s, ReqType, RespType, nregs=16 ): - s.xcel = XcelMinionIfcCL( ReqType, RespType, req=s.recv, req_rdy=s.recv_rdy ) - s.entry = None - s.reg_file = [ 0 for _ in range( nregs ) ] - - @update_once - def up_process(): - - if s.entry is not None and s.xcel.resp.rdy(): - # Dequeue xcel request message - req = s.entry - s.entry = None - - if req.type_ == XcelMsgType.READ: - resp = RespType( req.type_, s.read( req.addr ) ) - - elif req.type_ == XcelMsgType.WRITE: - s.write( req.addr, req.data ) - resp = RespType( req.type_, 0 ) - - s.xcel.resp( resp ) - - s.add_constraints( U(up_process) < M(s.xcel.req) ) # pipeline behavior - - def line_trace( s ): - return str(s.xcel) - -#------------------------------------------------------------------------- -# RTL master/minion -#------------------------------------------------------------------------- - -class SomeMasterRTL( Component ): - - def construct( s, ReqType, RespType, nregs=16 ): - - # Interface - - s.xcel = XcelMasterIfcRTL( ReqType, RespType ) - - # Local parameters - - DataType = ReqType.get_field_type( 'data' ) - assert DataType is RespType.get_field_type( 'data' ) - AddrType = ReqType.get_field_type( 'addr' ) - - s.nregs = nregs - - # Components - - s.addr = Wire( AddrType ) - s.count = Wire( Bits16 ) - s.flag = Wire( Bits1 ) - - @update_ff - def up_rtl_addr(): - if s.reset: - s.addr <<= AddrType(0) - elif s.xcel.req.en and not s.flag: - s.addr <<= s.addr + AddrType(1) - - @update_ff - def up_rtl_flag(): - if s.reset: - s.flag <<= Bits1(1) - elif s.xcel.req.en: - s.flag <<= ~s.flag - - @update_ff - def up_rtl_count(): - if s.reset: - s.count <<= Bits16(0) - elif s.xcel.resp.en and s.xcel.resp.msg.type_ == XcelMsgType.READ: - s.count <<= s.count + Bits16(1) - - @update - def up_req(): - s.xcel.req.en @= ~s.reset & s.xcel.req.rdy - s.xcel.req.msg.type_ @= XcelMsgType.WRITE if s.flag else XcelMsgType.READ - s.xcel.req.msg.addr @= s.addr - s.xcel.req.msg.data @= 0xface0000 | int(s.addr) - - @update - def up_resp(): - s.xcel.resp.rdy @= 1 - - def done( s ): - return s.count == s.nregs - - def line_trace( s ): - return "{}({}){}".format( s.xcel.req, s.flag, s.xcel.resp ) - -class SomeMinionRTL( Component ): - - def construct( s, ReqType, RespType, nregs=16 ): - - # Interface - - s.xcel = XcelMinionIfcRTL( ReqType, RespType ) - - # Local parameters - - DataType = ReqType.get_field_type( 'data' ) - assert DataType is RespType.get_field_type( 'data' ) - - s.nregs = nregs - - # Components - - s.req_q = NormalQueueRTL( ReqType, num_entries=1 ) - s.wen = Wire( Bits1 ) - - s.reg_file = m = RegisterFile( DataType, nregs ) - m.raddr[0] //= s.req_q.deq.ret.addr - m.rdata[0] //= s.xcel.resp.msg.data - m.wen[0] //= s.wen - m.waddr[0] //= s.req_q.deq.ret.addr - m.wdata[0] //= s.req_q.deq.ret.data - - connect( s.xcel.req, s.req_q.enq ) - connect( s.xcel.resp.msg.type_, s.req_q.deq.ret.type_ ) - - @update - def up_wen(): - s.wen @= s.req_q.deq.rdy & (s.req_q.deq.ret.type_ == XcelMsgType.WRITE) - - @update - def up_resp(): - s.xcel.resp.en @= s.req_q.deq.rdy & s.xcel.resp.rdy - s.req_q.deq.en @= s.req_q.deq.rdy & s.xcel.resp.rdy - - def line_trace( s ): - return str(s.xcel) - -#------------------------------------------------------------------------- -# TestHarness -#------------------------------------------------------------------------- - -class TestHarness( Component ): - - def construct( s, - MasterType = SomeMasterBlockingFL, - MinionType=SomeMinionFL, - nregs = 16 ): - ReqType, RespType = mk_xcel_msg( addr=clog2(nregs), data=32 ) - s.master = MasterType( ReqType, RespType, nregs=nregs ) - s.minion = MinionType( ReqType, RespType, nregs=nregs ) - - connect( s.master.xcel, s.minion.xcel ) - - def line_trace( s ): - return "{} > {}".format( s.master.line_trace(), s.minion.line_trace() ) - - def done( s ): - return s.master.done() - -#------------------------------------------------------------------------- -# FL-FL composition -#------------------------------------------------------------------------- - -def test_xcel_fl_fl_blocking(): - th = TestHarness() - th.set_param( "top.construct", - MasterType = SomeMasterBlockingFL, - MinionType = SomeMinionFL, - nregs = 16, - ) - run_sim( th ) - -def test_xcel_fl_fl_nonblocking(): - th = TestHarness() - th.set_param( "top.construct", - MasterType = SomeMasterNonBlockingFL, - MinionType = SomeMinionFL, - nregs = 16, - ) - run_sim( th ) - -#------------------------------------------------------------------------- -# FL-CL composition -#------------------------------------------------------------------------- - -def test_xcel_fl_cl_blocking(): - th = TestHarness() - th.set_param( "top.construct", - MasterType = SomeMasterBlockingFL, - MinionType = SomeMinionCL, - nregs = 16, - ) - run_sim( th ) - -def test_xcel_fl_cl_nonblocking(): - th = TestHarness() - th.set_param( "top.construct", - MasterType = SomeMasterNonBlockingFL, - MinionType = SomeMinionCL, - nregs = 16, - ) - run_sim( th ) - -#------------------------------------------------------------------------- -# FL-RTL composition -#------------------------------------------------------------------------- - -def test_xcel_fl_rtl_blocking(): - th = TestHarness() - th.set_param( "top.construct", - MasterType = SomeMasterBlockingFL, - MinionType = SomeMinionRTL, - nregs = 16, - ) - run_sim( th ) - -def test_xcel_fl_rtl_nonblocking(): - th = TestHarness() - th.set_param( "top.construct", - MasterType = SomeMasterNonBlockingFL, - MinionType = SomeMinionRTL, - nregs = 16, - ) - run_sim( th ) - -#------------------------------------------------------------------------- -# CL-CL composition -#------------------------------------------------------------------------- - -def test_xcel_cl_cl(): - th = TestHarness() - th.set_param( "top.construct", - MasterType = SomeMasterCL, - MinionType = SomeMinionCL, - nregs = 8, - ) - run_sim( th ) - -#------------------------------------------------------------------------- -# CL-RTL composition -#------------------------------------------------------------------------- - -def test_xcel_cl_rtl(): - th = TestHarness() - th.set_param( "top.construct", - MasterType = SomeMasterCL, - MinionType = SomeMinionRTL, - nregs = 8, - ) - run_sim( th ) - -#------------------------------------------------------------------------- -# CL-FL composition -#------------------------------------------------------------------------- - -def test_xcel_cl_fl(): - th = TestHarness() - th.set_param( "top.construct", - MasterType = SomeMasterCL, - MinionType = SomeMinionFL, - nregs = 8, - ) - run_sim( th ) - -#------------------------------------------------------------------------- -# RTL-RTL composition -#------------------------------------------------------------------------- - -def test_xcel_rtl_rtl(): - th = TestHarness() - th.set_param( "top.construct", - MasterType = SomeMasterRTL, - MinionType = SomeMinionRTL, - nregs = 8, - ) - run_sim( th ) - -#------------------------------------------------------------------------- -# RTL-CL composition -#------------------------------------------------------------------------- - -def test_xcel_rtl_cl(): - th = TestHarness() - th.set_param( "top.construct", - MasterType = SomeMasterRTL, - MinionType = SomeMinionCL, - nregs = 8, - ) - run_sim( th ) - -#------------------------------------------------------------------------- -# RTL-FL composition -#------------------------------------------------------------------------- - -def test_xcel_rtl_fl(): - th = TestHarness() - th.set_param( "top.construct", - MasterType = SomeMasterRTL, - MinionType = SomeMinionFL, - nregs = 8, - ) - run_sim( th ) diff --git a/pymtl3/stdlib/ifcs/xcel_ifcs.py b/pymtl3/stdlib/ifcs/xcel_ifcs.py deleted file mode 100644 index 97ff7a0fe..000000000 --- a/pymtl3/stdlib/ifcs/xcel_ifcs.py +++ /dev/null @@ -1,278 +0,0 @@ -""" -========================================================================== - xcel_ifcs.py -========================================================================== -Accelerator interface implementations at FL, CL, and RTL. - - Author: Yanghui Ou - Date: June 3, 2019 -""" -from greenlet import greenlet - -from pymtl3 import * -from pymtl3.stdlib.connects import connect_pairs - -from .master_minion_ifcs import MasterIfcCL, MasterIfcRTL, MinionIfcCL, MinionIfcRTL -from .send_recv_ifcs import RecvCL2SendRTL, RecvIfcRTL, RecvRTL2SendCL, SendIfcRTL -from .XcelMsg import XcelMsgType, mk_xcel_msg - -#------------------------------------------------------------------------- -# FL interfaces -#------------------------------------------------------------------------- -# We assume FL interfaces shouldn't take types by default and the CL one should ... - -class XcelMasterIfcFL( Interface ): - - def construct( s ): - s.read = CallerIfcFL() - s.write = CallerIfcFL() - - def __str__( s ): - return f"r{s.read}|w{s.write}" - - def connect( s, other, parent ): - if isinstance( other, XcelMinionIfcRTL ): - m = XcelIfcFL2RTLAdapter( other.ReqType, other.RespType ) - - if hasattr( parent, "XcelIfcFL2RTL_count" ): - count = parent.XcelIfcFL2RTL_count - setattr( parent, "XcelIfcFL2RTL_" + str( count ), m ) - else: - parent.XcelIfcFL2RTL_count = 0 - parent.XcelIfcFL2RTL_0 = m - - connect_pairs( - s, m.left, - m.right, other, - ) - parent.XcelIfcFL2RTL_count += 1 - return True - - elif isinstance( other, XcelMinionIfcCL ): - m = XcelIfcFL2CLAdapter( other.ReqType, other.RespType ) - - if hasattr( parent, "XcelIfcFL2CL_count" ): - count = parent.XcelIfcFL2CL_count - setattr( parent, "XcelIfcFL2CL_" + str( count ), m ) - else: - parent.XcelIfcFL2CL_count = 0 - parent.XcelIfcFL2CL_0 = m - - connect_pairs( - s, m.left, - m.right, other, - ) - parent.XcelIfcFL2CL_count += 1 - return True - - return False - -class XcelMinionIfcFL( Interface ): - - def construct( s, *, read=None, write=None ): - s.read = CalleeIfcFL( method=read ) - s.write = CalleeIfcFL( method=write ) - - def __str__( s ): - return f"r{s.read}|w{s.write}" - - def connect( s, other, parent ): - if isinstance( other, XcelMasterIfcRTL ): - m = XcelIfcRTL2FLAdapter( other.ReqType, other.RespType ) - - if hasattr( parent, "XcelIfcRTL2FL_count" ): - count = parent.XcelIfcRTL2FL_count - setattr( parent, "XcelIfcRTL2FL_" + str( count ), m ) - else: - parent.XcelIfcRTL2FL_count = 0 - parent.XcelIfcRTL2FL_0 = m - - connect_pairs( - other, m.left, - m.right, s, - ) - parent.XcelIfcRTL2FL_count += 1 - return True - - elif isinstance( other, XcelMasterIfcCL ): - m = XcelIfcCL2FLAdapter( other.ReqType, other.RespType ) - - if hasattr( parent, "XcelIfcCL2FL_count" ): - count = parent.XcelIfcCL2FL_count - setattr( parent, "XcelIfcCL2FL_" + str( count ), m ) - else: - parent.XcelIfcCL2FL_count = 0 - parent.XcelIfcCL2FL_0 = m - - connect_pairs( - other, m.left, - m.right, s, - ) - parent.XcelIfcCL2FL_count += 1 - return True - - return False - - def line_trace( s ): - return f"[r]{s.read}[w]{s.write}" - -#------------------------------------------------------------------------- -# CL interfaces -#------------------------------------------------------------------------- - -# There is no custom connect method in CL ifcs. -# For MasterCL-MinionRTL and MasterRTL-MinionCL connections, we just need -# to connect by name and leverage the custom connect method of the nested -# Send/RecvIfc. The CL-FL and FL-CL has been implemented in the FL ifc. - -class XcelMasterIfcCL( MasterIfcCL ): pass - -class XcelMinionIfcCL( MinionIfcCL ): pass - -#------------------------------------------------------------------------- -# RTL interfaces -#------------------------------------------------------------------------- -# There is no custom connect method in CL ifcs. -# For MasterCL-MinionRTL and MasterRTL-MinionCL connections, we just need -# to connect by name and leverage the custom connect method of the nested -# Send/RecvIfc. The RTL-FL and FL-RTL has been implemented in the FL ifc. - -class XcelMasterIfcRTL( MasterIfcRTL ): pass - -class XcelMinionIfcRTL( MinionIfcRTL ): pass - -#------------------------------------------------------------------------- -# CL/FL adapters -#------------------------------------------------------------------------- - -class XcelIfcCL2FLAdapter( Component ): - - def recv_rdy( s ): - return s.entry is None - - def recv( s, msg ): - assert s.entry is None - s.entry = msg - - def construct( s, ReqType, RespType ): - s.left = XcelMinionIfcCL( ReqType, RespType, req=s.recv, req_rdy=s.recv_rdy ) - s.right = XcelMasterIfcFL() - s.entry = None - - @update_once - def up_xcelifc_cl_fl_blk(): - - if s.entry is not None and s.left.resp.rdy(): - - # Dequeue xcel request message - req = s.entry - s.entry = None - - if req.type_ == XcelMsgType.READ: - resp = RespType( req.type_, s.right.read(req.addr) ) - - elif req.type_ == XcelMsgType.WRITE: - s.right.write( req.addr, req.data ) - resp = RespType( req.type_, 0 ) - - # Make line trace look better since s.right might get blocked - assert s.left.resp.rdy() - s.left.resp( resp ) - - s.add_constraints( - M( s.left.req ) > U( up_xcelifc_cl_fl_blk ), # add an edge - ) - -class XcelIfcFL2CLAdapter( Component ): - - def read( s, addr ): - - # TODO: refactor this greenlet stuff into some utility API - while not s.right.req.rdy(): - greenlet.getcurrent().parent.switch(0) - - s.right.req( s.ReqType( XcelMsgType.READ, addr ) ) - - while s.entry is None: - greenlet.getcurrent().parent.switch(0) - - ret = s.entry.data - s.entry = None - return ret - - def write( s, addr, data ): - - while not s.right.req.rdy(): - greenlet.getcurrent().parent.switch(0) - - s.right.req( s.ReqType( XcelMsgType.WRITE, addr, data ) ) - - while s.entry is None: - greenlet.getcurrent().parent.switch(0) - - s.entry = None - - def recv_rdy( s ): - return s.entry is None - - def recv( s, msg ): - assert s.entry is None - s.entry = msg - - def construct( s, ReqType, RespType ): - s.entry = None # store response - - s.ReqType = ReqType - - s.left = XcelMinionIfcFL( read=s.read, write=s.write ) - s.right = XcelMasterIfcCL( ReqType, RespType, resp=s.recv, resp_rdy=s.recv_rdy ) - - s.add_constraints( - M( s.left.read ) == M( s.right.req ), - M( s.left.write ) == M( s.right.req ), - M( s.left.read ) > M( s.right.resp ), - M( s.left.write ) > M( s.right.resp ), - ) - -#------------------------------------------------------------------------- -# RTL/FL adapters -#------------------------------------------------------------------------- -# Yanghui: directly adapting FL/RTL is tricky. I first convert FL/CL -# then CL/RTL using the adapters we already have. - -class XcelIfcRTL2FLAdapter( Component ): - - def construct( s, ReqType, RespType ): - s.left = XcelMinionIfcRTL( ReqType, RespType ) - s.right = XcelMasterIfcFL() - - s.req_rtl2cl = RecvRTL2SendCL( ReqType ) - s.resp_cl2rtl = RecvCL2SendRTL( RespType ) - s.cl2fl = XcelIfcCL2FLAdapter( ReqType, RespType ) - - s.left.req //= s.req_rtl2cl.recv - s.req_rtl2cl.send //= s.cl2fl.left.req - - s.cl2fl.right //= s.right - - s.cl2fl.left.resp //= s.resp_cl2rtl.recv - s.left.resp //= s.resp_cl2rtl.send - -class XcelIfcFL2RTLAdapter( Component ): - - def construct( s, ReqType, RespType ): - s.left = XcelMinionIfcFL () - s.right = XcelMasterIfcRTL( ReqType, RespType ) - - s.fl2cl = XcelIfcFL2CLAdapter( ReqType, RespType ) - s.req_cl2rtl = RecvCL2SendRTL( ReqType ) - s.resp_rtl2cl = RecvRTL2SendCL( RespType) - connect( s.left, s.fl2cl.left ) - connect_pairs( - s.fl2cl.right.req, s.req_cl2rtl.recv, - s.req_cl2rtl.send, s.right.req, - ) - connect_pairs( - s.fl2cl.right.resp, s.resp_rtl2cl.send, - s.resp_rtl2cl.recv, s.right.resp, - ) diff --git a/pymtl3/stdlib/mem/BehavioralMemory.py b/pymtl3/stdlib/mem/BehavioralMemory.py index 68ac42eef..c143609e1 100644 --- a/pymtl3/stdlib/mem/BehavioralMemory.py +++ b/pymtl3/stdlib/mem/BehavioralMemory.py @@ -4,7 +4,6 @@ write_bytearray_bits, ) -from .mem_ifcs import MemMinionIfcFL from .MemMsg import MemMsgType AMO_FUNS = { MemMsgType.AMO_ADD : lambda m,a : m+a, diff --git a/pymtl3/stdlib/mem/MagicMemoryCL.py b/pymtl3/stdlib/mem/MagicMemoryCL.py deleted file mode 100644 index 389210ec1..000000000 --- a/pymtl3/stdlib/mem/MagicMemoryCL.py +++ /dev/null @@ -1,152 +0,0 @@ -""" -======================================================================== -MagicMemoryCL -======================================================================== -A behavioral magic memory which is parameterized based on the number of -memory request/response ports. This version is a little different from -the one in pclib because we actually use the memory messages correctly -in the interface. - -Author : Shunning Jiang -Date : Feb 6, 2020 -""" - -from pymtl3 import * -from pymtl3.stdlib.delays import DelayPipeDeqCL, DelayPipeSendCL, StallCL - -from .MagicMemoryFL import MagicMemoryFL -from .mem_ifcs import MemMinionIfcCL -from .MemMsg import MemMsgType, mk_mem_msg - -# BRGTC2 custom MemMsg modified for RISC-V 32 - -#- - NOTE - - - NOTE - - - NOTE - - - NOTE - - - NOTE - - - NOTE - - -#------------------------------------------------------------------------- -# BRGTC2 -#------------------------------------------------------------------------- -# The AMO implementations (and MemMsg) has been updated to match RISC-V. -# -# There is also a small fix to the AMO ops to handle signed ops. The AMO -# operations act on the bitwidth of the processor architecture, so the -# read_data from the TestMemory used with AMOs cannot just be the memory -# request message size (e.g., 128b): -# -# read_data = Bits( s.data_nbits ) -# -# It must instead be the number of bytes matching the bitwidth in the -# processor (e.g., 32b): -# -# read_data = Bits( nbytes*8 ) -# -# Otherwise for example we would be reading 128b from the memory and -# comparing that to the 32b value from the request message. -# -#------------------------------------------------------------------------- -#- - NOTE - - - NOTE - - - NOTE - - - NOTE - - - NOTE - - - NOTE - - - -class MagicMemoryCL( Component ): - - # Magical methods - - def read_mem( s, addr, size ): - return s.mem.read_mem( addr, size ) - - def write_mem( s, addr, data ): - return s.mem.write_mem( addr, data ) - - # Actual stuff - def construct( s, nports, mem_ifc_dtypes=[mk_mem_msg(8,32,32), mk_mem_msg(8,32,32)], stall_prob=0, latency=1, mem_nbytes=2**20 ): - - # Local constants - - s.nports = nports - req_classes = [ x for (x,y) in mem_ifc_dtypes ] - resp_classes = [ y for (x,y) in mem_ifc_dtypes ] - - s.mem = MagicMemoryFL( mem_nbytes ) - - # Interface - - s.ifc = [ MemMinionIfcCL( req_classes[i], resp_classes[i] ) for i in range(nports) ] - - # Queues - req_latency = min(1, latency) - resp_latency = latency - req_latency - - s.req_stalls = [ StallCL( stall_prob, i ) for i in range(nports) ] - s.req_qs = [ DelayPipeDeqCL( req_latency ) for i in range(nports) ] - s.resp_qs = [ DelayPipeSendCL( resp_latency ) for i in range(nports) ] - - for i in range(nports): - s.req_stalls[i].recv //= s.ifc[i].req - s.resp_qs[i].send //= s.ifc[i].resp - - s.req_qs[i].enq //= s.req_stalls[i].send - - @update_once - def up_mem(): - - for i in range(s.nports): - - if s.req_qs[i].deq.rdy() and s.resp_qs[i].enq.rdy(): - - # Dequeue memory request message - - req = s.req_qs[i].deq() - len_ = int(req.len) - if len_ == 0: len_ = req_classes[i].data_nbits >> 3 - - # - # READ - # - if req.type_ == MemMsgType.READ: - resp = resp_classes[i]( req.type_, req.opaque, 0, req.len, - zext( s.mem.read( req.addr, len_ ), req_classes[i].data_nbits ) ) - - # - # WRITE - # - elif req.type_ == MemMsgType.WRITE: - s.mem.write( req.addr, len_, req.data[0:len_<<3] ) - # FIXME do we really set len=0 in response when doing subword wr? - # resp = resp_classes[i]( req.type_, req.opaque, 0, req.len, 0 ) - resp = resp_classes[i]( req.type_, req.opaque, 0, 0, 0 ) - - # - # AMOs - # - elif req.type_ == MemMsgType.AMO_ADD or \ - req.type_ == MemMsgType.AMO_AND or \ - req.type_ == MemMsgType.AMO_MAX or \ - req.type_ == MemMsgType.AMO_MAXU or \ - req.type_ == MemMsgType.AMO_MIN or \ - req.type_ == MemMsgType.AMO_MINU or \ - req.type_ == MemMsgType.AMO_OR or \ - req.type_ == MemMsgType.AMO_SWAP or \ - req.type_ == MemMsgType.AMO_XOR: - resp = resp_classes[i]( req.type_, req.opaque, 0, req.len, - s.mem.amo( req.type_, req.addr, len_, req.data ) ) - - # INV - elif req.type_ == MemMsgType.INV: - resp = resp_classes[i]( req.type_, req.opaque, 0, 0, 0 ) - - # FLUSH - elif req.type_ == MemMsgType.FLUSH: - resp = resp_classes[i]( req.type_, req.opaque, 0, 0, 0 ) - - # Invalid type - else: - assert( False ) - - s.resp_qs[i].enq( resp ) - - #----------------------------------------------------------------------- - # line_trace - #----------------------------------------------------------------------- - - def line_trace( s ): - msg = "" - for i in range( s.nports ): - msg += f"[{i}] {str(s.ifc[i].req)} {str(s.ifc[i].resp)} " - return msg diff --git a/pymtl3/stdlib/mem/MemoryFL.py b/pymtl3/stdlib/mem/MemoryFL.py index 1ee09b388..3d4ab0699 100644 --- a/pymtl3/stdlib/mem/MemoryFL.py +++ b/pymtl3/stdlib/mem/MemoryFL.py @@ -18,9 +18,10 @@ from pymtl3.stdlib.mem.BehavioralMemory import BehavioralMemory from pymtl3.stdlib.mem.MemMsg import MemMsgType, mk_mem_msg -from pymtl3.stdlib.reqresp.ifcs import ResponderIfc from pymtl3.stdlib.stream.ifcs import IStreamIfc, OStreamIfc +from .ifcs.ifcs import MemResponderIfc + # BRGTC2 custom MemMsg modified for RISC-V 32 #- - NOTE - - - NOTE - - - NOTE - - - NOTE - - - NOTE - - - NOTE - - @@ -136,11 +137,11 @@ def construct( s, nports=1, mem_ifc_dtypes=[mk_mem_msg(8,32,32)], req_classes = [ x for (x,y) in mem_ifc_dtypes ] resp_classes = [ y for (x,y) in mem_ifc_dtypes ] - s.mem = MagicMemoryFL( mem_nbytes ) + s.mem = BehavioralMemory( mem_nbytes ) # Interface - s.ifc = [ ResponderIfc( req_classes[i], resp_classes[i] ) for i in range(nports) ] + s.ifc = [ MemResponderIfc( req_classes[i], resp_classes[i] ) for i in range(nports) ] # stall and delays @@ -149,9 +150,9 @@ def construct( s, nports=1, mem_ifc_dtypes=[mk_mem_msg(8,32,32)], s.resp_qs = [ InelasticDelayPipe( resp_classes[i], extra_latency+1 ) for i in range(nports) ] for i in range(nports): - s.req_stalls[i].istream //= s.ifc[i].req + s.req_stalls[i].istream //= s.ifc[i].reqstream # s.req_stalls[i].ostream //= s.req_qs[i].istream - s.resp_qs[i].ostream //= s.ifc[i].resp + s.resp_qs[i].ostream //= s.ifc[i].respstream s.req_stalls[i].ostream.rdy //= s.resp_qs[i].istream.rdy s.req_stalls[i].ostream.val //= s.resp_qs[i].istream.val @@ -203,5 +204,5 @@ def up_mem(): def line_trace( s ): # print() - return "|".join( f"{s.req_stalls[i].line_trace()}{s.ifc[i].req}>{s.ifc[i].resp}{s.resp_qs[i].line_trace()}" + return "|".join( f"{s.req_stalls[i].line_trace()}{s.ifc[i].reqstream}>{s.ifc[i].respstream}{s.resp_qs[i].line_trace()}" for i in range(len(s.ifc)) ) diff --git a/pymtl3/stdlib/mem/ROMRTL.py b/pymtl3/stdlib/mem/ROM.py similarity index 94% rename from pymtl3/stdlib/mem/ROMRTL.py rename to pymtl3/stdlib/mem/ROM.py index 377ef60fb..b5f4d7e80 100644 --- a/pymtl3/stdlib/mem/ROMRTL.py +++ b/pymtl3/stdlib/mem/ROM.py @@ -1,6 +1,6 @@ """ ======================================================================== -ROMRTL.py +ROM.py ======================================================================== Multiported ROM @@ -11,7 +11,7 @@ from pymtl3 import * -class CombinationalROMRTL( Component ): +class CombinationalROM( Component ): def construct( s, Type, num_entries, data, num_ports=1 ): assert len(data) == num_entries @@ -28,7 +28,7 @@ def up_read_rom(): for i in range(num_ports): s.rdata[i] @= s.mem[ s.raddr[i] ] -class SequentialROMRTL( Component ): +class SequentialROM( Component ): def construct( s, Type, num_entries, data, num_ports=1 ): assert len(data) == num_entries diff --git a/pymtl3/stdlib/mem/__init__.py b/pymtl3/stdlib/mem/__init__.py index c00c420ae..f2845ef7c 100644 --- a/pymtl3/stdlib/mem/__init__.py +++ b/pymtl3/stdlib/mem/__init__.py @@ -1,12 +1,3 @@ -from .MagicMemoryCL import MagicMemoryCL -from .MagicMemoryFL import MagicMemoryFL -from .mem_ifcs import ( - MemMasterIfcCL, - MemMasterIfcFL, - MemMasterIfcRTL, - MemMinionIfcCL, - MemMinionIfcFL, - MemMinionIfcRTL, -) +from .MemoryFL import MemoryFL from .MemMsg import MemMsgType, mk_mem_msg, mk_mem_req_msg, mk_mem_resp_msg -from .ROMRTL import CombinationalROMRTL, SequentialROMRTL +from .ROM import CombinationalROM, SequentialROM diff --git a/pymtl3/stdlib/mem/ifcs/__init__.py b/pymtl3/stdlib/mem/ifcs/__init__.py new file mode 100644 index 000000000..76c75943e --- /dev/null +++ b/pymtl3/stdlib/mem/ifcs/__init__.py @@ -0,0 +1 @@ +from .ifcs import MemRequesterIfc, MemResponderIfc diff --git a/pymtl3/stdlib/mem/ifcs/ifcs.py b/pymtl3/stdlib/mem/ifcs/ifcs.py new file mode 100644 index 000000000..d5c27893a --- /dev/null +++ b/pymtl3/stdlib/mem/ifcs/ifcs.py @@ -0,0 +1,8 @@ +from pymtl3 import * +from pymtl3.stdlib.reqresp.ifcs import RequesterIfc, ResponderIfc + +class MemRequesterIfc( RequesterIfc ): + pass + +class MemResponderIfc( ResponderIfc ): + pass diff --git a/pymtl3/stdlib/mem/mem_ifcs.py b/pymtl3/stdlib/mem/mem_ifcs.py deleted file mode 100644 index bd1ce9d26..000000000 --- a/pymtl3/stdlib/mem/mem_ifcs.py +++ /dev/null @@ -1,290 +0,0 @@ -""" -#========================================================================= -# MemIfcs -#========================================================================= -# -# Author: Shunning Jiang -# Date : May 18, 2019 -""" -from greenlet import greenlet - -from pymtl3 import * -from pymtl3.stdlib.connects import connect_pairs -from pymtl3.stdlib.ifcs import MasterIfcCL, MasterIfcRTL, MinionIfcCL, MinionIfcRTL - -from .MemMsg import MemMsgType, mk_mem_msg - - -class MemMasterIfcFL( Interface ): - def construct( s, ReqType=None, RespType=None ): - s.read = CallerIfcFL() - s.write = CallerIfcFL() - s.amo = CallerIfcFL() - - def __str__( s ): - return f"r{s.read}|w{s.write}|a{s.amo}" - - def connect( s, other, parent ): - if isinstance( other, MemMinionIfcCL ): - m = MemIfcFL2CLAdapter( other.ReqType, other.RespType ) - - if hasattr( parent, "MemIfcFL2CL_count" ): - count = parent.MemIfcFL2CL_count - setattr( parent, "MemIfcFL2CL_" + str( count ), m ) - else: - parent.MemIfcFL2CL_count = 0 - parent.MemIfcFL2CL_0 = m - - connect_pairs( - s, m.left, - m.right, other, - ) - parent.MemIfcFL2CL_count += 1 - return True - - elif isinstance( other, MemMinionIfcRTL ): - m = MemIfcFL2RTLAdapter( other.ReqType, other.RespType ) - - if hasattr( parent, "MemIfcFL2RTL_count" ): - count = parent.MemIfcFL2RTL_count - setattr( parent, "MemIfcFL2RTL_" + str( count ), m ) - else: - parent.MemIfcFL2RTL_count = 0 - parent.MemIfcFL2RTL_0 = m - - connect_pairs( - s, m.left, - m.right, other, - ) - parent.MemIfcFL2RTL_count += 1 - return True - - return False - -class MemMinionIfcFL( Interface ): - def construct( s, read=None, write=None, amo=None ): - s.read = CalleeIfcFL( method=read ) - s.write = CalleeIfcFL( method=write ) - s.amo = CalleeIfcFL( method=amo ) - - def __str__( s ): - return f"r{s.read}|w{s.write}|a{s.amo}" - - def connect( s, other, parent ): - if isinstance( other, MemMasterIfcCL ): - m = MemIfcCL2FLAdapter( other.ReqType, other.RespType ) - - if hasattr( parent, "MemIfcCL2FL_count" ): - count = parent.MemIfcCL2FL_count - setattr( parent, "MemIfcCL2FL_" + str( count ), m ) - else: - parent.MemIfcCL2FL_count = 0 - parent.MemIfcCL2FL_0 = m - - connect_pairs( - other, m.left, - m.right, other, - ) - parent.MemIfcCL2FL_count += 1 - return True - - elif isinstance( other, MemMasterIfcRTL ): - m = MemIfcRTL2FLAdapter( other.ReqType, other.RespType ) - - if hasattr( parent, "MemIfcRTL2FL_count" ): - count = parent.MemIfcRTL2FL_count - setattr( parent, "MemIfcRTL2FL_" + str( count ), m ) - else: - parent.MemIfcRTL2FL_count = 0 - parent.MemIfcRTL2FL_0 = m - - connect_pairs( - other, m.left, - m.right, s, - ) - parent.MemIfcRTL2FL_count += 1 - return True - - return False - -class MemMasterIfcCL( MasterIfcCL ): pass - -class MemMinionIfcCL( MinionIfcCL ): pass - -class MemMasterIfcRTL( MasterIfcRTL ): pass - -class MemMinionIfcRTL( MinionIfcRTL ): pass - -class MemIfcCL2FLAdapter( Component ): - - def recv_rdy( s ): - return s.entry is None - - def recv( s, msg ): - assert s.entry is None - s.entry = msg - - def construct( s, ReqType, RespType ): - s.left = MemMinionIfcCL( ReqType, RespType, s.recv, s.recv_rdy ) - s.right = MemMasterIfcFL() - s.entry = None - - @update_once - def up_memifc_cl_fl_blk(): - - if s.entry is not None and s.left.resp.rdy(): - - # Dequeue memory request message - - req = s.entry - s.entry = None - - len_ = int(req.len) - if not len_: len_ = ReqType.data_nbits >> 3 - - if req.type_ == MemMsgType.READ: - resp = RespType( req.type_, req.opaque, 0, req.len, - s.right.read( req.addr, len_ ) ) - - elif req.type_ == MemMsgType.WRITE: - s.right.write( req.addr, len_, req.data ) - # FIXME do we really set len=0 in response when doing subword wr? - # resp = RespTypees( req.type_, req.opaque, 0, req.len, 0 ) - resp = RespType( req.type_, req.opaque, 0, 0, 0 ) - - else: # AMOS - resp = RespType( req.type_, req.opaque, 0, req.len, - s.right.amo( req.type_, req.addr, len_, req.data ) ) - - # Make line trace look better since s.right might get blocked - assert s.left.resp.rdy() - s.left.resp( resp ) - - s.add_constraints( - M(s.left.req) < U(up_memifc_cl_fl_blk), # bypass behavior - ) - -class MemIfcFL2CLAdapter( Component ): - - def read( s, addr, nbytes ): - - # TODO refactor this greenlet stuff into some utility API - while not s.right.req.rdy(): - greenlet.getcurrent().parent.switch(0) - - s.right.req( s.create_req( MemMsgType.READ, 0, addr, nbytes ) ) - - while s.entry is None: - greenlet.getcurrent().parent.switch(0) - - ret = s.entry.data[0:nbytes<<3] - s.entry = None - return ret - - def write( s, addr, nbytes, data ): - - while not s.right.req.rdy(): - greenlet.getcurrent().parent.switch(0) - - s.right.req( s.create_req( MemMsgType.WRITE, 0, addr, nbytes, data ) ) - - while s.entry is None: - greenlet.getcurrent().parent.switch(0) - - s.entry = None - - def amo( s, amo, addr, nbytes, data ): - - while not s.right.req.rdy(): - greenlet.getcurrent().parent.switch(0) - - s.right.req( s.create_req( amo, 0, addr, nbytes, data ) ) - - while s.entry is None: - greenlet.getcurrent().parent.switch(0) - - ret = s.entry.data - s.entry = None - return ret - - def recv_rdy( s ): - return s.entry is None - - def recv( s, msg ): - assert s.entry is None - s.entry = msg - - def construct( s, ReqType, RespType ): - s.entry = None # store response - - s.ReqType = ReqType - s.RespType = RespType - - Tlen = ReqType.get_field_type('len') - Tdata = ReqType.get_field_type('data') - # Use create_req to handle type mismatch - s.create_req = lambda a,b,c,d,e=0: ReqType( a, b, c, Tlen(d, trunc_int=True), Tdata(int(e)) ) - - s.left = MemMinionIfcFL( read=s.read, write=s.write, amo=s.amo ) - s.right = MemMasterIfcCL( ReqType, RespType, s.recv, s.recv_rdy ) - - s.add_constraints( - M(s.left.read) == M(s.right.req), - M(s.left.write) == M(s.right.req), - M(s.left.amo) == M(s.right.req), - M(s.left.read) > M(s.right.resp), # Comb behavior - M(s.left.write) > M(s.right.resp), # Comb behavior - M(s.left.amo) > M(s.right.resp), # Comb behavior - ) - -#------------------------------------------------------------------------- -# RTL/FL adapters -#------------------------------------------------------------------------- - -class MemIfcRTL2FLAdapter( Component ): - - def construct( s, ReqType, RespType ): - s.left = MemMinionIfcRTL( ReqType, RespType ) - s.right = MemMasterIfcFL() - - @update_once - def up_memifc_rtl_fl_blk(): - - if s.left.req.en and s.left.resp.rdy: - - if s.left.req.msg.type_ == MemMsgType.READ: - resp = RespType( s.left.req.msg.type_, s.right.read( s.left.req.msg.addr ) ) - - elif s.left.req.msg.type_ == MemMsgType.WRITE: - s.right.write( s.left.req.msg.addr, s.left.req.msg.data ) - resp = RespType( s.left.req.msg.type_, 0 ) - - else: # AMOs - resp = RespType( req.type_, req.opaque, 0, req.len, - s.right.amo( req.type_, req.addr, len_, req.data ) ) - - s.left.resp.en = Bits1(1) - s.left.resp.msg = resp - - @update - def up_memifc_rtl_fl_rdy(): - s.left.req.rdy = s.left.resp.rdy - -class MemIfcFL2RTLAdapter( Component ): - - def construct( s, ReqType, RespType ): - s.left = MemMinionIfcFL () - s.right = MemMasterIfcRTL( ReqType, RespType ) - - s.fl2cl = MemIfcFL2CLAdapter( ReqType, RespType ) - s.req_cl2rtl = RecvCL2SendRTL( ReqType ) - s.resp_rtl2cl = RecvRTL2SendCL( RespType) - connect( s.left, s.fl2cl.left ) - connect_pairs( - s.fl2cl.right.req, s.req_cl2rtl.recv, - s.req_cl2rtl.send, s.right.req, - ) - connect_pairs( - s.fl2cl.right.resp, s.resp_rtl2cl.send, - s.resp_rtl2cl.recv, s.right.resp, - ) diff --git a/pymtl3/stdlib/mem/test/MagicMemoryCL_test.py b/pymtl3/stdlib/mem/test/MemoryFL_test.py similarity index 93% rename from pymtl3/stdlib/mem/test/MagicMemoryCL_test.py rename to pymtl3/stdlib/mem/test/MemoryFL_test.py index 286dd82e6..e6a6afd81 100644 --- a/pymtl3/stdlib/mem/test/MagicMemoryCL_test.py +++ b/pymtl3/stdlib/mem/test/MemoryFL_test.py @@ -8,9 +8,10 @@ import pytest from pymtl3 import * -from pymtl3.stdlib.test_utils import TestSinkCL, TestSrcCL, mk_test_case_table, run_sim +from pymtl3.stdlib.stream import StreamSourceFL, StreamSinkFL +from pymtl3.stdlib.test_utils import mk_test_case_table, run_sim -from ..MagicMemoryCL import MagicMemoryCL +from ..MemoryFL import MemoryFL from ..MemMsg import MemMsgType, mk_mem_msg #------------------------------------------------------------------------- @@ -23,16 +24,16 @@ def construct( s, cls, nports, PortTypes, src_msgs, sink_msgs, stall_prob, mem_latency, src_initial, src_interval, sink_initial, sink_interval, arrival_time=None ): assert len(PortTypes) == nports - s.srcs = [ TestSrcCL( PortTypes[i][0], src_msgs[i], src_initial, src_interval ) - for i in range(nports) ] + s.srcs = [ StreamSourceFL( PortTypes[i][0], src_msgs[i], src_initial, src_interval ) + for i in range(nports) ] s.mem = cls( nports, PortTypes, stall_prob, mem_latency ) - s.sinks = [ TestSinkCL( PortTypes[i][1], sink_msgs[i], sink_initial, sink_interval, - arrival_time ) for i in range(nports) ] + s.sinks = [ StreamSinkFL( PortTypes[i][1], sink_msgs[i], sink_initial, sink_interval, + arrival_time ) for i in range(nports) ] # Connections for i in range(nports): - connect( s.srcs[i].send, s.mem.ifc[i].req ) - connect( s.mem.ifc[i].resp, s.sinks[i].recv ) + connect( s.srcs[i].ostream, s.mem.ifc[i].reqstream ) + connect( s.mem.ifc[i].respstream, s.sinks[i].istream ) def done( s ): return all([x.done() for x in s.srcs] + [x.done() for x in s.sinks]) @@ -246,7 +247,7 @@ def random_msgs( base_addr ): @pytest.mark.parametrize( **test_case_table ) def test_1port( test_params, cmdline_opts ): msgs = test_params.msg_func(0x1000) - run_sim( TestHarness( MagicMemoryCL, 1, [(req_cls, resp_cls)], + run_sim( TestHarness( MemoryFL, 1, [(req_cls, resp_cls)], [ msgs[::2] ], [ msgs[1::2] ], test_params.stall, test_params.lat, @@ -261,7 +262,7 @@ def test_1port( test_params, cmdline_opts ): def test_2port( test_params, cmdline_opts ): msgs0 = test_params.msg_func(0x1000) msgs1 = test_params.msg_func(0x2000) - run_sim( TestHarness( MagicMemoryCL, 2, [(req_cls, resp_cls)]*2, + run_sim( TestHarness( MemoryFL, 2, [(req_cls, resp_cls)]*2, [ msgs0[::2], msgs1[::2] ], [ msgs0[1::2], msgs1[1::2] ], test_params.stall, test_params.lat, @@ -271,7 +272,7 @@ def test_2port( test_params, cmdline_opts ): @pytest.mark.parametrize( **test_case_table ) def test_20port( test_params, cmdline_opts ): msgs = [ test_params.msg_func(0x1000*i) for i in range(20) ] - run_sim( TestHarness( MagicMemoryCL, 20, [(req_cls, resp_cls)]*20, + run_sim( TestHarness( MemoryFL, 20, [(req_cls, resp_cls)]*20, [ x[::2] for x in msgs ], [ x[1::2] for x in msgs ], test_params.stall, test_params.lat, @@ -305,7 +306,7 @@ def test_read_write_mem( cmdline_opts ): # Create test harness with above memory messages - th = TestHarness( MagicMemoryCL, 2, [(req_cls, resp_cls)]*2, [msgs[::2], []], [msgs[1::2], []], + th = TestHarness( MemoryFL, 2, [(req_cls, resp_cls)]*2, [msgs[::2], []], [msgs[1::2], []], 0, 0, 0, 0, 0, 0 ) th.elaborate() diff --git a/pymtl3/stdlib/mem/test/ROMRTL_test.py b/pymtl3/stdlib/mem/test/ROM_test.py similarity index 78% rename from pymtl3/stdlib/mem/test/ROMRTL_test.py rename to pymtl3/stdlib/mem/test/ROM_test.py index 5d4afd0f2..356947884 100644 --- a/pymtl3/stdlib/mem/test/ROMRTL_test.py +++ b/pymtl3/stdlib/mem/test/ROM_test.py @@ -1,21 +1,21 @@ #========================================================================= -# ROMRTL_test.py +# ROM_test.py #========================================================================= from pymtl3 import * from pymtl3.stdlib.test_utils import run_test_vector_sim -from ..ROMRTL import CombinationalROMRTL, SequentialROMRTL +from ..ROM import CombinationalROM, SequentialROM def test_combinational_rom_rtl(): - run_test_vector_sim( CombinationalROMRTL(Bits32, 8, [8,7,6,5,4,3,2,1], num_ports=2), [ + run_test_vector_sim( CombinationalROM(Bits32, 8, [8,7,6,5,4,3,2,1], num_ports=2), [ ('raddr[0]', 'rdata[0]*', 'raddr[1]', 'rdata[1]*'), [ 1, 7, 5, 3 ], [ 2, 6, 7, 1 ], ]) - run_test_vector_sim( CombinationalROMRTL(Bits32, 8, [8,7,6,5,4,3,2,1], num_ports=2), [ + run_test_vector_sim( CombinationalROM(Bits32, 8, [8,7,6,5,4,3,2,1], num_ports=2), [ ('raddr[0]', 'rdata[0]*', 'raddr[1]', 'rdata[1]*'), [ 1, 7, 5, 3 ], [ 2, 6, 7, 1 ], @@ -24,14 +24,14 @@ def test_combinational_rom_rtl(): def test_sequential_rom_rtl(): - run_test_vector_sim( SequentialROMRTL(Bits32, 8, [8,7,6,5,4,3,2,1], num_ports=2), [ + run_test_vector_sim( SequentialROM(Bits32, 8, [8,7,6,5,4,3,2,1], num_ports=2), [ ('raddr[0]', 'rdata[0]*', 'raddr[1]', 'rdata[1]*'), [ 1, '?', 5, '?' ], [ 2, 7, 7, 3 ], [ 0, 6, 0, 1 ], ]) - run_test_vector_sim( SequentialROMRTL(Bits32, 8, [8,7,6,5,4,3,2,1], num_ports=2), [ + run_test_vector_sim( SequentialROM(Bits32, 8, [8,7,6,5,4,3,2,1], num_ports=2), [ ('raddr[0]', 'rdata[0]*', 'raddr[1]', 'rdata[1]*'), [ 1, '?', 5, '?' ], [ 2, 7, 7, 3 ], diff --git a/pymtl3/stdlib/mem/test/mem_ifcs_test.py b/pymtl3/stdlib/mem/test/mem_ifcs_test.py deleted file mode 100644 index 4dae5c692..000000000 --- a/pymtl3/stdlib/mem/test/mem_ifcs_test.py +++ /dev/null @@ -1,131 +0,0 @@ -""" -#========================================================================= -# MemIfcs -#========================================================================= -# -# Author: Shunning Jiang -# Date : May 19, 2019 -""" -from pymtl3 import * -from pymtl3.stdlib.test_utils import run_sim - -from ..mem_ifcs import MemMasterIfcFL, MemMinionIfcCL -from ..MemMsg import MemMsgType, mk_mem_msg - - -def test_mem_fl_cl_adapter(): - - class SomeMasterFL( Component ): - def construct( s, base, has_loop=0 ): - s.mem = MemMasterIfcFL() - - s.addr = 0x1000 + base - s.end = 0x1000 + base + 0x10 - - s.trace = " " - - if has_loop == 1: - @update_once - def up_master_while(): - s.trace = " " - while s.addr < s.end: - s.mem.write( s.addr, 4, 0xdead0000 | s.addr ) - s.trace = "wr 0x{:x} ".format( s.addr ) - x = s.mem.read( s.addr, 4 ) - s.trace = "rd 0x{:x} {}".format( s.addr, x ) - s.addr += 4 - else: - @update_once - def up_master_noloop(): - s.trace = "# " - s.mem.write( s.addr, 4, 0xdead0000 | s.addr ) - s.trace = "wr 0x{:x} ".format( s.addr ) - x = s.mem.read( s.addr, 4 ) - s.trace = "rd 0x{:x} {}".format( s.addr, x ) - s.addr += 4 - - def done( s ): - return s.addr >= s.end - - def line_trace( s ): - return s.trace - - class SomeMinionCL( Component ): - - def recv( s, msg ): - assert s.entry is None - s.entry = msg - - def recv_rdy( s ): - return s.entry is None - - def read( s, addr, nbytes ): - nbytes = int(nbytes) - nbits = nbytes << 3 - ret, shamt = Bits( nbits, 0 ), Bits( nbits, 0 ) - addr = int(addr) - end = addr + nbytes - for j in range( addr, end ): - ret += Bits( nbits, s.memory[j] ) << shamt - shamt += 8 - return ret - - def write( s, addr, nbytes, data ): - tmp = int(data) - addr = int(addr) - end = addr + int(nbytes) - for j in range( addr, end ): - s.memory[j] = tmp & 255 - tmp >>= 8 - - def construct( s, req_class, resp_class ): - s.mem = MemMinionIfcCL( req_class, resp_class, s.recv, s.recv_rdy ) - s.entry = None - - s.memory = bytearray( 2**20 ) - - @update_once - def up_process(): - - if s.entry is not None and s.mem.resp.rdy(): - - # Dequeue memory request message - - req = s.entry - s.entry = None - - len_ = int(req.len) - if not len_: len_ = req_class.data_nbits >> 3 - - if req.type_ == MemMsgType.READ: - resp = resp_class( req.type_, req.opaque, 0, req.len, - s.read( req.addr, len_ ) ) - - elif req.type_ == MemMsgType.WRITE: - s.write( req.addr, len_, req.data ) - resp = resp_class( req.type_, req.opaque, 0, 0, 0 ) - - s.mem.resp( resp ) - - s.add_constraints( U(up_process) < M(s.mem.req) ) # definite pipe behavior - - def line_trace( s ): - return str(s.mem) - - class TestHarness( Component ): - def construct( s ): - s.master = [ SomeMasterFL( i*0x222, i ) for i in range(2) ] - s.minion = [ SomeMinionCL( *mk_mem_msg(8,32,32) ) for _ in range(2) ] - for i in range(2): - s.minion[i].mem //= s.master[i].mem - - def line_trace( s ): - return "|".join( [ x.line_trace() for x in s.master ] ) + " >>> " + \ - "|".join( [ x.line_trace() for x in s.minion ] ) - - def done( s ): - return all( [ x.done() for x in s.master ] ) - - # Run simulation - - run_sim( TestHarness() ) diff --git a/pymtl3/stdlib/queues/__init__.py b/pymtl3/stdlib/queues/__init__.py deleted file mode 100644 index 3d9bd6a81..000000000 --- a/pymtl3/stdlib/queues/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -from .cl_queues import BypassQueueCL, NormalQueueCL, PipeQueueCL -from .enq_deq_ifcs import DeqIfcRTL, EnqIfcRTL -from .queues import BypassQueueRTL, NormalQueueRTL, PipeQueueRTL - -# from .enrdy_queues import BypassQueue1RTL, NormalQueue1RTL, PipeQueue1RTL -# from .valrdy_queues import ( - # BypassQueue1RTL, - # NormalQueue1RTL, - # NormalQueueRTL, - # PipeQueue1RTL, -# ) diff --git a/pymtl3/stdlib/queues/cl_queues.py b/pymtl3/stdlib/queues/cl_queues.py deleted file mode 100644 index 2fec21818..000000000 --- a/pymtl3/stdlib/queues/cl_queues.py +++ /dev/null @@ -1,109 +0,0 @@ -""" -======================================================================== -queues.py -======================================================================== -This file contains cycle-level queues. - -Author : Shunning Jiang, Yanghui Ou -Date : Mar 10, 2018 -""" - -from collections import deque - -from pymtl3 import * - -#------------------------------------------------------------------------- -# PipeQueueCL -#------------------------------------------------------------------------- - -class PipeQueueCL( Component ): - - def construct( s, num_entries=1 ): - s.queue = deque( maxlen=num_entries ) - - s.add_constraints( - M( s.peek ) < M( s.enq ), - M( s.deq ) < M( s.enq ) - ) - - @non_blocking( lambda s: len( s.queue ) < s.queue.maxlen ) - def enq( s, msg ): - s.queue.appendleft( msg ) - - @non_blocking( lambda s: len( s.queue ) > 0 ) - def deq( s ): - return s.queue.pop() - - @non_blocking( lambda s: len( s.queue ) > 0 ) - def peek( s ): - return s.queue[-1] - - def line_trace( s ): - return "{}( ){}".format( s.enq, s.deq ) - -#------------------------------------------------------------------------- -# BypassQueueCL -#------------------------------------------------------------------------- - -class BypassQueueCL( Component ): - - def construct( s, num_entries=1 ): - s.queue = deque( maxlen=num_entries ) - - s.add_constraints( - M( s.enq ) < M( s.peek ), - M( s.enq ) < M( s.deq ), - ) - - @non_blocking( lambda s: len( s.queue ) < s.queue.maxlen ) - def enq( s, msg ): - s.queue.appendleft( msg ) - - @non_blocking( lambda s: len( s.queue ) > 0 ) - def deq( s ): - return s.queue.pop() - - @non_blocking( lambda s: len( s.queue ) > 0 ) - def peek( s ): - return s.queue[-1] - - def line_trace( s ): - return "{}( ){}".format( s.enq, s.deq ) - -#------------------------------------------------------------------------- -# NormalQueueCL -#------------------------------------------------------------------------- - -class NormalQueueCL( Component ): - - def construct( s, num_entries=1 ): - s.queue = deque( maxlen=num_entries ) - s.enq_rdy = False - s.deq_rdy = False - - @update - def up_pulse(): - s.enq_rdy = len( s.queue ) < s.queue.maxlen - s.deq_rdy = len( s.queue ) > 0 - - s.add_constraints( - U( up_pulse ) < M( s.enq.rdy ), - U( up_pulse ) < M( s.deq.rdy ), - M( s.peek ) < M( s.deq.rdy ), - M( s.peek ) < M( s.enq.rdy ) - ) - - @non_blocking( lambda s: s.enq_rdy ) - def enq( s, msg ): - s.queue.appendleft( msg ) - - @non_blocking( lambda s: s.deq_rdy ) - def deq( s ): - return s.queue.pop() - - @non_blocking( lambda s: len( s.queue ) > 0 ) - def peek( s ): - return s.queue[-1] - - def line_trace( s ): - return "{}( ){}".format( s.enq, s.deq ) diff --git a/pymtl3/stdlib/queues/enq_deq_ifcs.py b/pymtl3/stdlib/queues/enq_deq_ifcs.py deleted file mode 100644 index 97aad3710..000000000 --- a/pymtl3/stdlib/queues/enq_deq_ifcs.py +++ /dev/null @@ -1,31 +0,0 @@ -""" -======================================================================== -EnqDeqIfc.py -======================================================================== -RTL implementation of deq and enq interface. - -Author: Yanghui Ou - Date: Mar 21, 2019 -""" -from pymtl3 import * -from pymtl3.stdlib.ifcs import GiveIfcFL, GiveIfcRTL, RecvIfcFL, RecvIfcRTL - -#------------------------------------------------------------------------- -# EnqIfcRTL -#------------------------------------------------------------------------- - -class EnqIfcRTL( RecvIfcRTL ): - pass - -#------------------------------------------------------------------------- -# DeqIfcRTL -#------------------------------------------------------------------------- - -class DeqIfcRTL( GiveIfcRTL ): - pass - -class EnqIfcFL( RecvIfcFL ): - pass - -class DeqIfcFL( GiveIfcFL ): - pass diff --git a/pymtl3/stdlib/queues/enrdy_queues.py b/pymtl3/stdlib/queues/enrdy_queues.py deleted file mode 100644 index 807722de0..000000000 --- a/pymtl3/stdlib/queues/enrdy_queues.py +++ /dev/null @@ -1,117 +0,0 @@ -""" -======================================================================== -enrdy_queues.py -======================================================================== -This file contains queues with EnRdy (send/recv) interface. - -Author : Shunning Jiang -Date : Mar 9, 2018 -""" - -from pymtl3 import * -from pymtl3.stdlib.primitive import Mux, Reg, RegEn, RegRst -from pymtl3.stdlib.ifcs import RecvIfcRTL, SendIfcRTL - - -class PipeQueue1RTL( Component ): - - def construct( s, Type ): - s.enq = RecvIfcRTL( Type ) - s.deq = SendIfcRTL( Type ) - - s.buffer = m = RegEn( Type ) - m.en //= s.enq.en - m.in_ //= s.enq.msg - m.out //= s.deq.msg - - s.full = Reg( Bits1 ) - - @update - def up_pipeq_use_deq_rdy(): - s.deq.en @= s.full.out & s.deq.rdy - s.enq.rdy @= ~s.full.out | s.deq.rdy - - @update - def up_pipeq_full(): - s.full.in_ @= s.enq.en | (s.full.out & ~s.deq.rdy ) - - def line_trace( s ): - return s.buffer.line_trace() - -class BypassQueue1RTL( Component ): - - def construct( s, Type ): - s.enq = RecvIfcRTL( Type ) - s.deq = SendIfcRTL( Type ) - - s.buffer = RegEn( Type ) - s.buffer.in_ //= s.enq.msg - - s.full = RegRst( Bits1, reset_value = 0 ) - - s.byp_mux = m = Mux( Type, 2 ) - m.out //= s.deq.msg - m.in_[0] //= s.enq.msg - m.in_[1] //= s.buffer.out - m.sel //= s.full.out # full -- buffer.out, empty -- bypass - - @update - def up_bypq_set_enq_rdy(): - s.enq.rdy @= ~s.full.out - - @update - def up_bypq_use_enq_en(): - s.deq.en @= (s.enq.en | s.full.out) & s.deq.rdy - s.buffer.en @= s.enq.en & ~s.deq.en - s.full.in_ @= (s.enq.en | s.full.out) & ~s.deq.en - - def line_trace( s ): - return s.buffer.line_trace() - -class BypassQueue2RTL( Component ): - - def construct( s, MsgType, queue_size=2 ): - assert queue_size == 2 - s.enq = RecvIfcRTL( MsgType ) - s.deq = SendIfcRTL( MsgType ) - s.q1 = BypassQueue1RTL( MsgType ) - s.q2 = BypassQueue1RTL( MsgType ) - - s.enq //= s.q1.enq - s.q1.deq //= s.q2.enq - s.q2.deq //= s.deq - - def line_trace( s ): - return "{}({}){}".format( s.enq, s.q1.deq, s.deq ) - -class NormalQueue1RTL( Component ): - - def construct( s, Type ): - s.enq = RecvIfcRTL( Type ) - s.deq = SendIfcRTL( Type ) - - # Now since enq.en depends on enq.rdy, enq.en == 1 actually means - # we will enq some stuff - s.buffer = m = RegEn( Type ) - m.en //= s.enq.en - m.in_ //= s.enq.msg - m.out //= s.deq.msg - s.full = Reg( Bits1 ) - - @update - def up_normq_set_enq_rdy(): - s.enq.rdy @= ~s.full.out - - @update - def up_normq_full(): - # When the bufferf is full and deq side is ready, we enable deq - s.deq.en @= s.full.out & s.deq.rdy - - # not full and valid enq, or no deque and enq, or no deque and already full - s.full.in_ @= (~s.full.out & s.enq.en) | \ - (~s.deq.rdy & s.enq.en) | \ - (~s.deq.rdy & s.full.out) - - - def line_trace( s ): - return s.buffer.line_trace() diff --git a/pymtl3/stdlib/queues/queues.py b/pymtl3/stdlib/queues/queues.py deleted file mode 100644 index c29b2d06b..000000000 --- a/pymtl3/stdlib/queues/queues.py +++ /dev/null @@ -1,542 +0,0 @@ -""" -------------------------------------------------------------------------- -Library of RTL queues -------------------------------------------------------------------------- - -Author : Yanghui Ou - Date : Mar 23, 2019 -""" - - -from pymtl3 import * -from pymtl3.stdlib.primitive import Mux, RegisterFile - -from .enq_deq_ifcs import DeqIfcRTL, EnqIfcRTL - -#------------------------------------------------------------------------- -# Dpath and Ctrl for NormalQueueRTL -#------------------------------------------------------------------------- - -class NormalQueueDpathRTL( Component ): - - def construct( s, EntryType, num_entries=2 ): - - # Interface - - s.enq_msg = InPort( EntryType ) - s.deq_ret = OutPort( EntryType ) - - s.wen = InPort() - s.waddr = InPort( clog2( num_entries ) ) - s.raddr = InPort( clog2( num_entries ) ) - - # Component - - s.queue = m = RegisterFile( EntryType, num_entries ) - m.raddr[0] //= s.raddr - m.rdata[0] //= s.deq_ret - m.wen[0] //= s.wen - m.waddr[0] //= s.waddr - m.wdata[0] //= s.enq_msg - -class NormalQueueCtrlRTL( Component ): - - def construct( s, num_entries=2 ): - - # Constants - - addr_nbits = clog2 ( num_entries ) - count_nbits = clog2 ( num_entries+1 ) - PtrType = mk_bits ( addr_nbits ) - CountType = mk_bits ( count_nbits ) - s.last_idx = PtrType ( num_entries-1 ) - s.num_entries = CountType( num_entries ) - - # Interface - - s.enq_en = InPort () - s.enq_rdy = OutPort() - s.deq_en = InPort () - s.deq_rdy = OutPort() - s.count = OutPort( CountType ) - - s.wen = OutPort() - s.waddr = OutPort( PtrType ) - s.raddr = OutPort( PtrType ) - - # Registers - - s.head = Wire( PtrType ) - s.tail = Wire( PtrType ) - - # Wires - - s.enq_xfer = Wire( Bits1 ) - s.deq_xfer = Wire( Bits1 ) - - # Connections - - connect( s.wen, s.enq_xfer ) - connect( s.waddr, s.tail ) - connect( s.raddr, s.head ) - - s.enq_rdy //= lambda: ~s.reset & ( s.count < s.num_entries ) - s.deq_rdy //= lambda: ~s.reset & ( s.count > CountType(0) ) - - s.enq_xfer //= lambda: s.enq_en & s.enq_rdy - s.deq_xfer //= lambda: s.deq_en & s.deq_rdy - - @update_ff - def up_reg(): - - if s.reset: - s.head <<= PtrType(0) - s.tail <<= PtrType(0) - s.count <<= CountType(0) - - else: - if s.deq_xfer: - s.head <<= s.head + PtrType(1) if s.head < s.last_idx else PtrType(0) - - if s.enq_xfer: - s.tail <<= s.tail + PtrType(1) if s.tail < s.last_idx else PtrType(0) - - if s.enq_xfer & ~s.deq_xfer: - s.count <<= s.count + CountType(1) - if ~s.enq_xfer & s.deq_xfer: - s.count <<= s.count - CountType(1) - -#------------------------------------------------------------------------- -# NormalQueueRTL -#------------------------------------------------------------------------- - -class NormalQueueRTL( Component ): - - def construct( s, EntryType, num_entries=2 ): - - # Interface - - s.enq = EnqIfcRTL( EntryType ) - s.deq = DeqIfcRTL( EntryType ) - s.count = OutPort( mk_bits( clog2( num_entries+1 ) ) ) - - # Components - - assert num_entries > 0 - if num_entries == 1: - s.q = NormalQueue1EntryRTL( EntryType ) - connect( s.enq, s.q.enq ) - connect( s.deq, s.q.deq ) - connect( s.count, s.q.count ) - - else: - s.ctrl = NormalQueueCtrlRTL ( num_entries ) - s.dpath = NormalQueueDpathRTL( EntryType, num_entries ) - - # Connect ctrl to data path - - connect( s.ctrl.wen, s.dpath.wen ) - connect( s.ctrl.waddr, s.dpath.waddr ) - connect( s.ctrl.raddr, s.dpath.raddr ) - - # Connect to interface - - connect( s.enq.en, s.ctrl.enq_en ) - connect( s.enq.rdy, s.ctrl.enq_rdy ) - connect( s.deq.en, s.ctrl.deq_en ) - connect( s.deq.rdy, s.ctrl.deq_rdy ) - connect( s.count, s.ctrl.count ) - connect( s.enq.msg, s.dpath.enq_msg ) - connect( s.deq.ret, s.dpath.deq_ret ) - - # Line trace - - def line_trace( s ): - return f"{s.enq}({s.count}){s.deq}" - -#------------------------------------------------------------------------- -# Ctrl for PipeQueue -#------------------------------------------------------------------------- - -class PipeQueueCtrlRTL( Component ): - - def construct( s, num_entries=2 ): - - # Constants - - addr_nbits = clog2 ( num_entries ) - count_nbits = clog2 ( num_entries+1 ) - PtrType = mk_bits ( addr_nbits ) - CountType = mk_bits ( count_nbits ) - s.last_idx = PtrType ( num_entries-1 ) - s.num_entries = CountType( num_entries ) - - # Interface - - s.enq_en = InPort ( Bits1 ) - s.enq_rdy = OutPort( Bits1 ) - s.deq_en = InPort ( Bits1 ) - s.deq_rdy = OutPort( Bits1 ) - s.count = OutPort( CountType ) - - s.wen = OutPort( Bits1 ) - s.waddr = OutPort( PtrType ) - s.raddr = OutPort( PtrType ) - - # Registers - - s.head = Wire( PtrType ) - s.tail = Wire( PtrType ) - - # Wires - - s.enq_xfer = Wire( Bits1 ) - s.deq_xfer = Wire( Bits1 ) - - # Connections - - connect( s.wen, s.enq_xfer ) - connect( s.waddr, s.tail ) - connect( s.raddr, s.head ) - - s.deq_rdy //= lambda: ~s.reset & ( s.count > CountType(0) ) - s.enq_rdy //= lambda: ~s.reset & ( ( s.count < s.num_entries ) | s.deq_en ) - - s.enq_xfer //= lambda: s.enq_en & s.enq_rdy - s.deq_xfer //= lambda: s.deq_en & s.deq_rdy - - @update_ff - def up_reg(): - - if s.reset: - s.head <<= PtrType(0) - s.tail <<= PtrType(0) - s.count <<= CountType(0) - - else: - if s.deq_xfer: - s.head <<= s.head + PtrType(1) if s.head < s.last_idx else PtrType(0) - - if s.enq_xfer: - s.tail <<= s.tail + PtrType(1) if s.tail < s.last_idx else PtrType(0) - - if s.enq_xfer & ~s.deq_xfer: - s.count <<= s.count + CountType(1) - if ~s.enq_xfer & s.deq_xfer: - s.count <<= s.count - CountType(1) - -#------------------------------------------------------------------------- -# PipeQueueRTL -#------------------------------------------------------------------------- - -class PipeQueueRTL( Component ): - - def construct( s, EntryType, num_entries=2 ): - - # Interface - - s.enq = EnqIfcRTL( EntryType ) - s.deq = DeqIfcRTL( EntryType ) - s.count = OutPort( mk_bits( clog2( num_entries+1 ) ) ) - - # Components - - assert num_entries > 0 - if num_entries == 1: - s.q = PipeQueue1EntryRTL( EntryType ) - connect( s.enq, s.q.enq ) - connect( s.deq, s.q.deq ) - connect( s.count, s.q.count ) - - else: - s.ctrl = PipeQueueCtrlRTL ( num_entries ) - s.dpath = NormalQueueDpathRTL( EntryType, num_entries ) - - # Connect ctrl to data path - - connect( s.ctrl.wen, s.dpath.wen ) - connect( s.ctrl.waddr, s.dpath.waddr ) - connect( s.ctrl.raddr, s.dpath.raddr ) - - # Connect to interface - - connect( s.enq.en, s.ctrl.enq_en ) - connect( s.enq.rdy, s.ctrl.enq_rdy ) - connect( s.deq.en, s.ctrl.deq_en ) - connect( s.deq.rdy, s.ctrl.deq_rdy ) - connect( s.count, s.ctrl.count ) - connect( s.enq.msg, s.dpath.enq_msg ) - connect( s.deq.ret, s.dpath.deq_ret ) - - # Line trace - - def line_trace( s ): - return "{}({}){}".format( s.enq, s.count, s.deq ) - -#------------------------------------------------------------------------- -# Ctrl and Dpath for BypassQueue -#------------------------------------------------------------------------- - -class BypassQueueDpathRTL( Component ): - - def construct( s, EntryType, num_entries=2 ): - - # Interface - - s.enq_msg = InPort( EntryType ) - s.deq_ret = OutPort( EntryType ) - - s.wen = InPort( Bits1 ) - s.waddr = InPort( mk_bits( clog2( num_entries ) ) ) - s.raddr = InPort( mk_bits( clog2( num_entries ) ) ) - s.mux_sel = InPort( Bits1 ) - - # Component - - s.queue = m = RegisterFile( EntryType, num_entries ) - m.raddr[0] //= s.raddr - m.wen[0] //= s.wen - m.waddr[0] //= s.waddr - m.wdata[0] //= s.enq_msg - - s.mux = m = Mux( EntryType, 2 ) - m.sel //= s.mux_sel - m.in_[0] //= s.queue.rdata[0] - m.in_[1] //= s.enq_msg - m.out //= s.deq_ret - -class BypassQueueCtrlRTL( Component ): - - def construct( s, num_entries=2 ): - - # Constants - - addr_nbits = clog2 ( num_entries ) - count_nbits = clog2 ( num_entries+1 ) - PtrType = mk_bits ( addr_nbits ) - CountType = mk_bits ( count_nbits ) - s.last_idx = PtrType ( num_entries-1 ) - s.num_entries = CountType( num_entries ) - - # Interface - - s.enq_en = InPort ( Bits1 ) - s.enq_rdy = OutPort( Bits1 ) - s.deq_en = InPort ( Bits1 ) - s.deq_rdy = OutPort( Bits1 ) - s.count = OutPort( CountType ) - - s.wen = OutPort( Bits1 ) - s.waddr = OutPort( PtrType ) - s.raddr = OutPort( PtrType ) - s.mux_sel = OutPort( Bits1 ) - - # Registers - - s.head = Wire( PtrType ) - s.tail = Wire( PtrType ) - - # Wires - - s.enq_xfer = Wire( Bits1 ) - s.deq_xfer = Wire( Bits1 ) - - # Connections - - connect( s.wen, s.enq_xfer ) - connect( s.waddr, s.tail ) - connect( s.raddr, s.head ) - - s.enq_rdy //= lambda: ~s.reset & ( s.count < s.num_entries ) - s.deq_rdy //= lambda: ~s.reset & ( (s.count > CountType(0) ) | s.enq_en ) - - s.mux_sel //= lambda: s.count == CountType(0) - - s.enq_xfer //= lambda: s.enq_en & s.enq_rdy - s.deq_xfer //= lambda: s.deq_en & s.deq_rdy - - @update_ff - def up_reg(): - - if s.reset: - s.head <<= PtrType(0) - s.tail <<= PtrType(0) - s.count <<= CountType(0) - - else: - if s.deq_xfer: - s.head <<= s.head + PtrType(1) if s.head < s.last_idx else PtrType(0) - - if s.enq_xfer: - s.tail <<= s.tail + PtrType(1) if s.tail < s.last_idx else PtrType(0) - - if s.enq_xfer & ~s.deq_xfer: - s.count <<= s.count + CountType(1) - if ~s.enq_xfer & s.deq_xfer: - s.count <<= s.count - CountType(1) - -#------------------------------------------------------------------------- -# BypassQueueRTL -#------------------------------------------------------------------------- - -class BypassQueueRTL( Component ): - - def construct( s, EntryType, num_entries=2 ): - - # Interface - - s.enq = EnqIfcRTL( EntryType ) - s.deq = DeqIfcRTL( EntryType ) - s.count = OutPort( mk_bits( clog2( num_entries+1 ) ) ) - - # Components - - assert num_entries > 0 - if num_entries == 1: - s.q = BypassQueue1EntryRTL( EntryType ) - connect( s.enq, s.q.enq ) - connect( s.deq, s.q.deq ) - connect( s.count, s.q.count ) - - else: - s.ctrl = BypassQueueCtrlRTL ( num_entries ) - s.dpath = BypassQueueDpathRTL( EntryType, num_entries ) - - # Connect ctrl to data path - - connect( s.ctrl.wen, s.dpath.wen ) - connect( s.ctrl.waddr, s.dpath.waddr ) - connect( s.ctrl.raddr, s.dpath.raddr ) - connect( s.ctrl.mux_sel, s.dpath.mux_sel ) - - # Connect to interface - - connect( s.enq.en, s.ctrl.enq_en ) - connect( s.enq.rdy, s.ctrl.enq_rdy ) - connect( s.deq.en, s.ctrl.deq_en ) - connect( s.deq.rdy, s.ctrl.deq_rdy ) - connect( s.count, s.ctrl.count ) - connect( s.enq.msg, s.dpath.enq_msg ) - connect( s.deq.ret, s.dpath.deq_ret ) - - # Line trace - - def line_trace( s ): - return f"{s.enq}({s.count}){s.deq}" - -#------------------------------------------------------------------------- -# NormalQueue1EntryRTL -#------------------------------------------------------------------------- - -class NormalQueue1EntryRTL( Component ): - - def construct( s, EntryType ): - - # Interface - - s.enq = EnqIfcRTL( EntryType ) - s.deq = DeqIfcRTL( EntryType ) - s.count = OutPort ( Bits1 ) - - # Components - - s.entry = Wire( EntryType ) - s.full = Wire( Bits1 ) - - # Logic - - s.count //= s.full - - s.deq.ret //= s.entry - - s.enq.rdy //= lambda: ~s.reset & ~s.full - s.deq.rdy //= lambda: ~s.reset & s.full - - @update_ff - def ff_normal1(): - s.full <<= ~s.reset & ( ~s.deq.en & (s.enq.en | s.full) ) - if s.enq.en: - s.entry <<= s.enq.msg - - def line_trace( s ): - return f"{s.enq}({s.full}){s.deq}" - -#------------------------------------------------------------------------- -# PipeQueue1EntryRTL -#------------------------------------------------------------------------- - -class PipeQueue1EntryRTL( Component ): - - def construct( s, EntryType ): - - # Interface - - s.enq = EnqIfcRTL( EntryType ) - s.deq = DeqIfcRTL( EntryType ) - s.count = OutPort ( Bits1 ) - - # Components - - s.entry = Wire( EntryType ) - s.full = Wire( Bits1 ) - - # Logic - - s.count //= s.full - - s.deq.ret //= s.entry - - s.enq.rdy //= lambda: ~s.reset & ( ~s.full | s.deq.en ) - s.deq.rdy //= lambda: s.full & ~s.reset - - @update_ff - def ff_pipe1(): - s.full <<= ~s.reset & ( s.enq.en | s.full & ~s.deq.en ) - - if s.enq.en: - s.entry <<= s.enq.msg - - def line_trace( s ): - return f"{s.enq}({s.full}){s.deq}" - -#------------------------------------------------------------------------- -# BypassQueue1EntryRTL -#------------------------------------------------------------------------- - -class BypassQueue1EntryRTL( Component ): - - def construct( s, EntryType ): - - # Interface - - s.enq = EnqIfcRTL( EntryType ) - s.deq = DeqIfcRTL( EntryType ) - s.count = OutPort ( Bits1 ) - - # Components - - s.entry = Wire( EntryType ) - s.full = Wire( Bits1 ) - - s.bypass_mux = m = Mux( EntryType, 2 ) - m.in_[0] //= s.enq.msg - m.in_[1] //= s.entry - m.out //= s.deq.ret - m.sel //= s.full - - # Logic - - s.count //= s.full - - s.enq.rdy //= lambda: ~s.reset & ~s.full - s.deq.rdy //= lambda: ~s.reset & ( s.full | s.enq.en ) - - @update_ff - def ff_bypass1(): - s.full <<= ~s.reset & ( ~s.deq.en & (s.enq.en | s.full) ) - - if s.enq.en & ~s.deq.en: - s.entry <<= s.enq.msg - - def line_trace( s ): - return f"{s.enq}({s.full}){s.deq}" diff --git a/pymtl3/stdlib/queues/test/__init__.py b/pymtl3/stdlib/queues/test/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pymtl3/stdlib/queues/test/cl_queues_test.py b/pymtl3/stdlib/queues/test/cl_queues_test.py deleted file mode 100644 index 4b06e999b..000000000 --- a/pymtl3/stdlib/queues/test/cl_queues_test.py +++ /dev/null @@ -1,98 +0,0 @@ -""" -======================================================================== -Tests for CL queues -======================================================================== - -Author: Yanghui Ou - Date: Mar 20, 2019 -""" -import pytest - -from pymtl3 import * -from pymtl3.stdlib.test_utils import TestSinkCL, TestSrcCL, run_sim - -from ..cl_queues import BypassQueueCL, NormalQueueCL, PipeQueueCL - -#------------------------------------------------------------------------- -# TestHarness -#------------------------------------------------------------------------- - -class TestHarness( Component ): - - def construct( s, MsgType, DutType, src_msgs, sink_msgs ): - - s.src = TestSrcCL ( MsgType, src_msgs ) - s.dut = DutType() - s.sink = TestSinkCL( MsgType, sink_msgs ) - - connect( s.src.send, s.dut.enq ) - - @update_once - def up_deq_send(): - if s.dut.deq.rdy() and s.sink.recv.rdy(): - s.sink.recv( s.dut.deq() ) - - def done( s ): - return s.src.done() and s.sink.done() - - def line_trace( s ): - return "{} ({}) {}".format( - s.src.line_trace(), s.dut.line_trace(), s.sink.line_trace() ) - -#------------------------------------------------------------------------- -# Test cases -#------------------------------------------------------------------------- - -test_msgs = [ Bits16( 0 ), Bits16( 1 ), Bits16( 2 ), Bits16( 3 ) ] - -arrival_pipe = [ 2, 3, 4, 5 ] -arrival_bypass = [ 1, 2, 3, 4 ] -arrival_normal = [ 2, 4, 6, 8 ] - -def test_pipe_simple(): - th = TestHarness( Bits16, PipeQueueCL, test_msgs, test_msgs ) - th.set_param( "top.dut.construct", num_entries=1 ) - th.set_param( "top.sink.construct", arrival_time=arrival_pipe ) - run_sim( th ) - -def test_bypass_simple(): - th = TestHarness( Bits16, BypassQueueCL, test_msgs, test_msgs ) - th.set_param( "top.dut.construct", num_entries=1 ) - th.set_param( "top.sink.construct", arrival_time=arrival_bypass ) - run_sim( th ) - -def test_normal_simple(): - th = TestHarness( Bits16, NormalQueueCL, test_msgs, test_msgs ) - th.set_param( "top.dut.construct", num_entries=1 ) - th.set_param( "top.sink.construct", arrival_time=arrival_normal ) - run_sim( th ) - -def test_normal2_simple(): - th = TestHarness( Bits16, NormalQueueCL, test_msgs, test_msgs ) - th.set_param( "top.dut.construct", num_entries=2 ) - th.set_param( "top.sink.construct", arrival_time=arrival_pipe ) - run_sim( th ) - -@pytest.mark.parametrize( - ( 'QType', 'qsize', 'src_init', 'src_intv', - 'sink_init', 'sink_intv', 'arrival_time' ), - [ - ( PipeQueueCL, 2, 1, 1, 0, 0, [ 3, 5, 7, 9 ] ), - ( BypassQueueCL, 1, 0, 4, 3, 1, [ 3, 6, 11, 16 ] ), - ( NormalQueueCL, 1, 0, 0, 5, 0, [ 5, 7, 9, 11 ] ) - ] -) -def test_delay( QType, qsize, src_init, src_intv, - sink_init, sink_intv, arrival_time ): - th = TestHarness( Bits16, QType, test_msgs, test_msgs ) - th.set_param( "top.src.construct", - initial_delay = src_init, - interval_delay = src_intv, - ) - th.set_param( "top.dut.construct", num_entries=qsize ) - th.set_param( "top.sink.construct", - initial_delay = sink_init, - interval_delay = sink_intv, - arrival_time = arrival_time, - ) - run_sim( th ) diff --git a/pymtl3/stdlib/queues/test/enrdy_queues_test.py b/pymtl3/stdlib/queues/test/enrdy_queues_test.py deleted file mode 100644 index 34539d12f..000000000 --- a/pymtl3/stdlib/queues/test/enrdy_queues_test.py +++ /dev/null @@ -1,68 +0,0 @@ -""" -======================================================================== -enrdy_queues_test.py -======================================================================== - -Author : Shunning Jiang -Date : Mar 9, 2018 -""" -import pytest - -from pymtl3 import * -from pymtl3.stdlib.test_utils import TestSinkCL, TestSrcCL, run_sim - -from ..enrdy_queues import * - -#------------------------------------------------------------------------- -# TestHarness -#------------------------------------------------------------------------- - -class TestHarness( Component ): - - def construct( s, Type, q, src_msgs, sink_msgs, src_interval, sink_interval ): - - # Instantiate models - - s.src = TestSrcCL( Type, src_msgs, interval_delay=src_interval ) - s.q = q - s.sink = TestSinkCL( Type, sink_msgs, interval_delay=sink_interval ) - - # Connect - - connect( s.src.send, s.q.enq ) - connect( s.sink.recv, s.q.deq ) - - def done( s ): - return s.src.done() and s.sink.done() - - def line_trace(s ): - return s.src.line_trace() + " " + s.q.line_trace() + " " + s.sink.line_trace() - -F = lambda x: Bits32(x) - -req = list(map( F, [1,2,3,4,5,6,7,8,9,10] )) -resp = list(map( F, [1,2,3,4,5,6,7,8,9,10] )) - -def test_normal_queue(): - run_sim( TestHarness( Bits32, NormalQueue1RTL(Bits32), req, resp, 0, 0 ) ) - -def test_normal_queue_stall(): - run_sim( TestHarness( Bits32, NormalQueue1RTL(Bits32), req, resp, 3, 4 ) ) - -def test_pipe_queue(): - run_sim( TestHarness( Bits32, PipeQueue1RTL(Bits32), req, resp, 0, 0 ) ) - -def test_pipe_queue_stall(): - run_sim( TestHarness( Bits32, PipeQueue1RTL(Bits32), req, resp, 3, 4 ) ) - -def test_bypass_queue(): - run_sim( TestHarness( Bits32, BypassQueue1RTL(Bits32), req, resp, 0, 0 ) ) - -def test_bypass_queue_stall(): - run_sim( TestHarness( Bits32, BypassQueue1RTL(Bits32), req, resp, 3, 4 ) ) - -def test_bypass_queue2(): - run_sim( TestHarness( Bits32, BypassQueue2RTL(Bits32), req, resp, 0, 0 ) ) - -def test_bypass_queue2_stall(): - run_sim( TestHarness( Bits32, BypassQueue2RTL(Bits32), req, resp, 3, 4 ) ) diff --git a/pymtl3/stdlib/queues/test/queues_test.py b/pymtl3/stdlib/queues/test/queues_test.py deleted file mode 100644 index 74302f270..000000000 --- a/pymtl3/stdlib/queues/test/queues_test.py +++ /dev/null @@ -1,179 +0,0 @@ -""" -======================================================================== -Tests for CL queues -======================================================================== - -Author: Yanghui Ou - Date: Mar 24, 2019 -""" -from itertools import product - -import pytest - -from pymtl3 import * -from pymtl3.stdlib.test_utils import TestSinkCL, TestSrcCL, TestVectorSimulator, run_sim - -from ..queues import ( - BypassQueue1EntryRTL, - BypassQueueRTL, - NormalQueue1EntryRTL, - NormalQueueRTL, - PipeQueue1EntryRTL, - PipeQueueRTL, -) - -#------------------------------------------------------------------------- -# TestVectorSimulator test -#------------------------------------------------------------------------- - -def run_tv_test( dut, test_vectors ): - - # Define input/output functions - - def tv_in( dut, tv ): - dut.enq.en @= tv[0] - dut.enq.msg @= tv[2] - dut.deq.en @= tv[3] - - def tv_out( dut, tv ): - if tv[1] != '?': assert dut.enq.rdy == tv[1] - if tv[4] != '?': assert dut.deq.rdy == tv[4] - if tv[5] != '?': assert dut.deq.ret == tv[5] - - # Run the test - - sim = TestVectorSimulator( dut, test_vectors, tv_in, tv_out ) - sim.run_test() - -def test_pipe_Bits(): - - B1 = mk_bits(1) - B32 = mk_bits(32) - run_tv_test( NormalQueueRTL( Bits32, 2 ), [ - # enq.en enq.rdy enq.msg deq.en deq.rdy deq.ret - [ B1(1), B1(1), B32(123), B1(0), B1(0), '?' ], - [ B1(1), B1(1), B32(345), B1(0), B1(1), B32(123) ], - [ B1(0), B1(0), B32(567), B1(0), B1(1), B32(123) ], - [ B1(0), B1(0), B32(567), B1(1), B1(1), B32(123) ], - [ B1(0), B1(1), B32(567), B1(1), B1(1), B32(345) ], - [ B1(1), B1(1), B32(567), B1(0), B1(0), '?' ], - [ B1(1), B1(1), B32(0 ), B1(1), B1(1), B32(567) ], - [ B1(1), B1(1), B32(1 ), B1(1), B1(1), B32(0 ) ], - [ B1(1), B1(1), B32(2 ), B1(1), B1(1), B32(1 ) ], - [ B1(0), B1(1), B32(2 ), B1(1), B1(1), B32(2 ) ], -] ) - -#------------------------------------------------------------------------- -# TestHarness -#------------------------------------------------------------------------- - -class TestHarness( Component ): - - def construct( s, MsgType, QType, src_msgs, sink_msgs ): - - s.src = TestSrcCL ( MsgType, src_msgs ) - s.dut = QType( MsgType ) - - s.sink = TestSinkCL( MsgType, sink_msgs ) - - connect( s.src.send, s.dut.enq ) - - @update_once - def dut2sink(): - s.dut.deq.en @= 0 - - if s.dut.deq.rdy and s.sink.recv.rdy(): - s.dut.deq.en @= 1 - s.sink.recv( s.dut.deq.ret ) - - def done( s ): - return s.src.done() and s.sink.done() - - def line_trace( s ): - return "{} ({}) {}".format( - s.src.line_trace(), s.dut.line_trace(), s.sink.line_trace() ) - -#------------------------------------------------------------------------- -# Test cases -#------------------------------------------------------------------------- - -test_msgs = [ Bits16( 4 ), Bits16( 1 ), Bits16( 2 ), Bits16( 3 ) ] - -arrival_normal = [ 2, 4, 6, 8 ] -arrival_pipe = [ 2, 3, 4, 5 ] -arrival_bypass = [ 1, 2, 3, 4 ] - - -def test_normal1_simple(): - th = TestHarness( Bits16, NormalQueueRTL, test_msgs, test_msgs ) - th.set_param( "top.sink.construct", arrival_time = arrival_normal ) - th.set_param( "top.dut.construct", num_entries = 1 ) - run_sim( th ) - -def test_normal2_simple(): - th = TestHarness( Bits16, NormalQueueRTL, test_msgs, test_msgs ) - th.set_param( "top.sink.construct", arrival_time = arrival_pipe ) - th.set_param( "top.dut.construct", num_entries = 2 ) - run_sim( th ) - -def test_pipe1_simple(): - th = TestHarness( Bits16, PipeQueueRTL, test_msgs, test_msgs ) - th.set_param( "top.sink.construct", arrival_time = arrival_pipe ) - th.set_param( "top.dut.construct", num_entries = 1 ) - run_sim( th ) - -def test_pipe1_backpressure(): - th = TestHarness( Bits16, PipeQueueRTL, test_msgs, test_msgs ) - th.set_param( "top.sink.construct", initial_delay = 20 ) - th.set_param( "top.dut.construct", num_entries = 1 ) - run_sim( th ) - -def test_pipe2_backpressure(): - th = TestHarness( Bits16, PipeQueueRTL, test_msgs, test_msgs ) - th.set_param( "top.sink.construct", initial_delay = 20 ) - th.set_param( "top.dut.construct", num_entries = 2 ) - run_sim( th ) - -def test_bypass1_simple(): - th = TestHarness( Bits16, BypassQueueRTL, test_msgs, test_msgs ) - th.set_param( "top.sink.construct", arrival_time = arrival_bypass ) - th.set_param( "top.dut.construct", num_entries = 1 ) - run_sim( th ) - -def test_bypass1_backpressure(): - th = TestHarness( Bits16, BypassQueueRTL, test_msgs, test_msgs ) - th.set_param( "top.sink.construct", initial_delay = 20 ) - th.set_param( "top.dut.construct", num_entries = 1 ) - run_sim( th ) - -def test_bypass2_sparse(): - th = TestHarness( Bits16, BypassQueueRTL, test_msgs, test_msgs ) - th.set_param( "top.src.construct", interval_delay = 3 ) - th.set_param( "top.dut.construct", num_entries = 2 ) - run_sim( th ) - -@pytest.mark.parametrize( - 'QType, num_entries', - product( [ NormalQueueRTL, PipeQueueRTL, BypassQueueRTL ], [ 8, 10, 12, 16 ] ) -) -def test_large_backpressure( QType, num_entries ): - msgs = test_msgs * 8 - th = TestHarness( Bits16, QType, msgs, msgs ) - th.set_param( "top.sink.construct", initial_delay = 20 ) - th.set_param( "top.dut.construct", num_entries = 16 ) - run_sim( th ) - -@pytest.mark.parametrize( - 'QType', [ NormalQueue1EntryRTL, PipeQueue1EntryRTL, BypassQueue1EntryRTL ] -) -def test_single_simple( QType ): - th = TestHarness( Bits16, QType, test_msgs, test_msgs ) - run_sim( th ) - -@pytest.mark.parametrize( - 'QType', [ NormalQueue1EntryRTL, PipeQueue1EntryRTL, BypassQueue1EntryRTL ] -) -def test_single_backpressure( QType ): - th = TestHarness( Bits16, QType, test_msgs, test_msgs ) - th.set_param( "top.sink.construct", initial_delay = 10, interval_delay=2 ) - run_sim( th ) diff --git a/pymtl3/stdlib/queues/valrdy_queues.py b/pymtl3/stdlib/queues/valrdy_queues.py deleted file mode 100644 index 9e24f067c..000000000 --- a/pymtl3/stdlib/queues/valrdy_queues.py +++ /dev/null @@ -1,294 +0,0 @@ -from pymtl3 import * -from pymtl3.stdlib.primitive import Mux, Reg, RegEn, RegisterFile -from pymtl3.stdlib.ifcs import InValRdyIfc, OutValRdyIfc - - -class PipeQueue1RTL( Component ): - - def construct( s, Type ): - s.enq = InValRdyIfc ( Type ) - s.deq = OutValRdyIfc( Type ) - - s.buffer = m = RegEn( Type ) - m.out //= s.deq.msg - m.in_ //= s.enq.msg - - s.next_full = Wire( Bits1 ) - s.full = Wire( Bits1 ) - connect( s.full, s.deq.val ) - - @update_ff - def up_full(): - s.full <<= s.next_full - - @update - def up_pipeq_set_enq_rdy(): - s.enq.rdy @= ~s.full | s.deq.rdy - - @update - def up_pipeq_full(): - s.buffer.en @= s.enq.val & s.enq.rdy - s.next_full @= s.enq.val | (s.full & ~s.deq.rdy) - - def line_trace( s ): - return s.enq.line_trace() + " > " + \ - ("*" if s.full else " ") + " > " + \ - s.deq.line_trace() - -class BypassQueue1RTL( Component ): - - def construct( s, Type ): - s.enq = InValRdyIfc ( Type ) - s.deq = OutValRdyIfc( Type ) - - s.buffer = RegEn( Type ) - s.buffer.in_ //= s.enq.msg - - s.next_full = Wire() - s.full = Wire() - - s.byp_mux = m = Mux( Type, 2 ) - m.out //= s.deq.msg - m.in_[0] //= s.enq.msg - m.in_[1] //= s.buffer.out - m.sel //= s.full # full -- buffer.out, empty -- bypass - - @update_ff - def up_full(): - s.full <<= s.next_full - - @update - def up_bypq_set_enq_rdy(): - s.enq.rdy @= ~s.full - - @update - def up_bypq_internal(): - s.buffer.en @= (~s.deq.rdy) & (s.enq.val & s.enq.rdy) - s.next_full @= (~s.deq.rdy) & s.deq.val - - # this enables the sender to make enq.val depend on enq.rdy - @update - def up_bypq_set_deq_val(): - s.deq.val @= s.full | s.enq.val - - def line_trace( s ): - return s.enq.line_trace() + " > " + \ - ("*" if s.full else " ") + " > " + \ - s.deq.line_trace() - -class NormalQueue1RTL( Component ): - - def construct( s, Type ): - s.enq = InValRdyIfc( Type ) - s.deq = OutValRdyIfc( Type ) - - s.buffer = m = RegEn( Type ) - m.in_ //= s.enq.msg - m.out //= s.deq.msg - - s.next_full = Wire() - s.full = Wire() - connect( s.full, s.deq.val ) - - @update_ff - def up_full(): - s.full <<= s.next_full - - @update - def up_normq_set_enq_rdy(): - s.enq.rdy @= ~s.full - - @update - def up_normq_internal(): - s.buffer.en @= s.enq.val & s.enq.rdy - s.next_full @= (s.full & ~s.deq.rdy) | s.buffer.en - - def line_trace( s ): - return s.enq.line_trace() + " > " + \ - ("*" if s.full else " ") + " > " + \ - s.deq.line_trace() - -#----------------------------------------------------------------------- -# NormalQueueRTL -#----------------------------------------------------------------------- -class NormalQueueRTL( Component ): - - def construct( s, num_entries, Type ): - - s.enq = InValRdyIfc( Type ) - s.deq = OutValRdyIfc( Type ) - s.num_free_entries = OutPort( mk_bits( clog2(num_entries+1) ) ) - - # Ctrl and Dpath unit instantiation - - s.ctrl = NormalQueueRTLCtrl ( num_entries ) - s.dpath = NormalQueueRTLDpath( num_entries, Type ) - - # Ctrl unit connections - - connect( s.ctrl.enq_val, s.enq.val ) - connect( s.ctrl.enq_rdy, s.enq.rdy ) - connect( s.ctrl.deq_val, s.deq.val ) - connect( s.ctrl.deq_rdy, s.deq.rdy ) - connect( s.ctrl.num_free_entries, s.num_free_entries ) - - # Dpath unit connections - - connect( s.dpath.enq_bits, s.enq.msg ) - connect( s.dpath.deq_bits, s.deq.msg ) - - # Control Signal connections (ctrl -> dpath) - - connect( s.dpath.wen, s.ctrl.wen ) - connect( s.dpath.waddr, s.ctrl.waddr ) - connect( s.dpath.raddr, s.ctrl.raddr ) - - def line_trace( s ): - return "{} () {}".format( s.enq.line_trace(), s.deq.line_trace() ) - -#----------------------------------------------------------------------- -# NormalQueueRTLDpath -#----------------------------------------------------------------------- -class NormalQueueRTLDpath( Component ): - - def construct( s, num_entries, Type ): - - SizeType = mk_bits( clog2( num_entries+1 ) ) - AddrType = mk_bits( clog2( num_entries ) ) - - s.enq_bits = InPort ( Type ) - s.deq_bits = OutPort ( Type ) - - # Control signal (ctrl -> dpath) - s.wen = InPort ( Bits1 ) - s.waddr = InPort ( AddrType ) - s.raddr = InPort ( AddrType ) - - # Queue storage - - s.queue = RegisterFile( Type, num_entries ) - - # Connect queue storage - - connect( s.queue.raddr[0], s.raddr ) - connect( s.queue.rdata[0], s.deq_bits ) - connect( s.queue.wen[0], s.wen ) - connect( s.queue.waddr[0], s.waddr ) - connect( s.queue.wdata[0], s.enq_bits ) - -#----------------------------------------------------------------------- -# NormalQueueRTLCtrl -#----------------------------------------------------------------------- -class NormalQueueRTLCtrl( Component ): - - def construct( s, num_entries ): - - s.num_entries = num_entries - - SizeType = mk_bits( clog2( num_entries+1 ) ) - AddrType = mk_bits( clog2( num_entries ) ) - - # Interface Ports - - s.enq_val = InPort ( Bits1 ) - s.enq_rdy = OutPort ( Bits1 ) - s.deq_val = OutPort ( Bits1 ) - s.deq_rdy = InPort ( Bits1 ) - s.num_free_entries = OutPort ( SizeType ) - - # Control signal (ctrl -> dpath) - s.wen = OutPort ( Bits1 ) - s.waddr = OutPort ( AddrType ) - s.raddr = OutPort ( AddrType ) - - # Wires - - s.full = Wire ( Bits1 ) - s.empty = Wire ( Bits1 ) - s.do_enq = Wire ( Bits1 ) - s.do_deq = Wire ( Bits1 ) - s.enq_ptr = Wire ( AddrType ) - s.deq_ptr = Wire ( AddrType ) - s.enq_ptr_next = Wire ( AddrType ) - s.deq_ptr_next = Wire ( AddrType ) - # TODO: can't infer these temporaries due to if statement, fix - s.enq_ptr_inc = Wire ( AddrType ) - s.deq_ptr_inc = Wire ( AddrType ) - s.full_next_cycle = Wire ( Bits1 ) - - s.last_idx = AddrType( num_entries - 1 ) - - @update - def comb(): - - # only enqueue/dequeue if valid and ready - - s.do_enq @= s.enq_rdy & s.enq_val - s.do_deq @= s.deq_rdy & s.deq_val - - # write enable - - s.wen @= s.do_enq - - # enq ptr incrementer - - if s.enq_ptr == s.last_idx: s.enq_ptr_inc @= 0 - else: s.enq_ptr_inc @= s.enq_ptr + 1 - - # deq ptr incrementer - - if s.deq_ptr == s.last_idx: s.deq_ptr_inc @= 0 - else: s.deq_ptr_inc @= s.deq_ptr + 1 - - # set the next ptr value - - if s.do_enq: s.enq_ptr_next @= s.enq_ptr_inc - else: s.enq_ptr_next @= s.enq_ptr - - if s.do_deq: s.deq_ptr_next @= s.deq_ptr_inc - else: s.deq_ptr_next @= s.deq_ptr - - # number of free entries calculation - - if s.reset: - s.num_free_entries @= s.num_entries - elif s.full: - s.num_free_entries @= 0 - elif s.empty: - s.num_free_entries @= s.num_entries - elif s.enq_ptr > s.deq_ptr: - s.num_free_entries @= s.num_entries - zext( s.enq_ptr - s.deq_ptr, SizeType) - elif s.deq_ptr > s.enq_ptr: - s.num_free_entries @= zext( s.deq_ptr - s.enq_ptr, SizeType ) - - s.full_next_cycle @= s.do_enq & ~s.do_deq & (s.enq_ptr_next == s.deq_ptr) - - @update - def up_ctrl_signals(): - - # set output signals - - s.empty @= ~s.full & (s.enq_ptr == s.deq_ptr) - - s.enq_rdy @= ~s.full - s.deq_val @= ~s.empty - - # set control signals - - s.waddr @= s.enq_ptr - s.raddr @= s.deq_ptr - - @update_ff - def seq(): - - if s.reset: - s.deq_ptr <<= AddrType( 0 ) - s.enq_ptr <<= AddrType( 0 ) - else: - s.deq_ptr <<= s.deq_ptr_next - s.enq_ptr <<= s.enq_ptr_next - - if s.reset: s.full <<= Bits1(0) - elif s.full_next_cycle: s.full <<= Bits1(1) - elif (s.do_deq & s.full): s.full <<= Bits1(0) - else: s.full <<= s.full diff --git a/pymtl3/stdlib/stream/test/memory_fl_test.py b/pymtl3/stdlib/stream/test/memory_fl_test.py deleted file mode 100644 index 8204a0cb7..000000000 --- a/pymtl3/stdlib/stream/test/memory_fl_test.py +++ /dev/null @@ -1,333 +0,0 @@ -#========================================================================= -# TestMemory_test.py -#========================================================================= - -import random -import struct - -import pytest - -from pymtl3 import * -from pymtl3.stdlib.test_utils import mk_test_case_table, run_sim -from pymtl3.stdlib.mem.MemMsg import MemMsgType, mk_mem_msg - -from ..SourceRTL import SourceRTL -from ..SinkRTL import SinkRTL - -from ..magic_memory import MagicMemoryRTL - -#------------------------------------------------------------------------- -# TestHarness -#------------------------------------------------------------------------- - -class TestHarness( Component ): - - def construct( s, cls, nports, PortTypes, src_msgs, sink_msgs, - stall_prob, mem_extra_latency, src_initial, src_interval, sink_initial, sink_interval, - arrival_time=None ): - assert len(PortTypes) == nports - s.srcs = [ SourceRTL( PortTypes[i][0], src_msgs[i], src_initial, src_interval ) - for i in range(nports) ] - s.mem = cls( nports, PortTypes, stall_prob, mem_extra_latency ) - s.sinks = [ SinkRTL( PortTypes[i][1], sink_msgs[i], sink_initial, sink_interval, - arrival_time ) for i in range(nports) ] - - # Connections - for i in range(nports): - s.srcs[i].send //= s.mem.ifc[i].req - s.mem.ifc[i].resp //= s.sinks[i].recv - - def done( s ): - return all([x.done() for x in s.srcs] + [x.done() for x in s.sinks]) - - def line_trace( s ): - return "{} >>> {} >>> {}".format( - "|".join( [ x.line_trace() for x in s.srcs ] ), - s.mem.line_trace(), - "|".join( [ x.line_trace() for x in s.sinks ] ) ) - -#------------------------------------------------------------------------- -# make messages -#------------------------------------------------------------------------- - -req_type_dict = { - 'rd': MemMsgType.READ, - 'wr': MemMsgType.WRITE, - 'ad': MemMsgType.AMO_ADD, - 'an': MemMsgType.AMO_AND, - 'or': MemMsgType.AMO_OR, - 'xg': MemMsgType.AMO_SWAP, - 'mn': MemMsgType.AMO_MIN, -} - -resp_type_dict = { - 'rd': MemMsgType.READ, - 'wr': MemMsgType.WRITE, - 'ad': MemMsgType.AMO_ADD, - 'an': MemMsgType.AMO_AND, - 'or': MemMsgType.AMO_OR, - 'xg': MemMsgType.AMO_SWAP, - 'mn': MemMsgType.AMO_MIN, -} - -req_cls, resp_cls = mk_mem_msg( 8, 32, 32 ) - -def req( type_, opaque, addr, len, data ): - return req_cls( req_type_dict[type_], opaque, addr, len, b32(data) ) - -def resp( type_, opaque, len, data ): - return resp_cls( resp_type_dict[type_], opaque, 0, len, b32(data) ) - -#---------------------------------------------------------------------- -# Test Case: basic -#---------------------------------------------------------------------- - -def basic_msgs( base_addr ): - return [ - req( 'wr', 0x0, base_addr, 0, 0xdeadbeef ), resp( 'wr', 0x0, 0, 0 ), - req( 'rd', 0x1, base_addr, 0, 0 ), resp( 'rd', 0x1, 0, 0xdeadbeef ), - ] - -#---------------------------------------------------------------------- -# Test Case: stream -#---------------------------------------------------------------------- - -def stream_msgs( base_addr ): - - msgs = [] - for i in range(20): - msgs.extend([ - req( 'wr', i, base_addr+4*i, 0, i ), resp( 'wr', i, 0, 0 ), - req( 'rd', i, base_addr+4*i, 0, 0 ), resp( 'rd', i, 0, i ), - ]) - - return msgs - -#---------------------------------------------------------------------- -# Test Case: subword reads -#---------------------------------------------------------------------- - -def subword_rd_msgs( base_addr ): - return [ - - req( 'wr', 0x0, base_addr+0, 0, 0xdeadbeef ), resp( 'wr', 0x0, 0, 0 ), - - req( 'rd', 0x1, base_addr+0, 1, 0 ), resp( 'rd', 0x1, 1, 0x000000ef ), - req( 'rd', 0x2, base_addr+1, 1, 0 ), resp( 'rd', 0x2, 1, 0x000000be ), - req( 'rd', 0x3, base_addr+2, 1, 0 ), resp( 'rd', 0x3, 1, 0x000000ad ), - req( 'rd', 0x4, base_addr+3, 1, 0 ), resp( 'rd', 0x4, 1, 0x000000de ), - - req( 'rd', 0x5, base_addr+0, 2, 0 ), resp( 'rd', 0x5, 2, 0x0000beef ), - req( 'rd', 0x6, base_addr+1, 2, 0 ), resp( 'rd', 0x6, 2, 0x0000adbe ), - req( 'rd', 0x7, base_addr+2, 2, 0 ), resp( 'rd', 0x7, 2, 0x0000dead ), - - req( 'rd', 0x8, base_addr+0, 3, 0 ), resp( 'rd', 0x8, 3, 0x00adbeef ), - req( 'rd', 0x9, base_addr+1, 3, 0 ), resp( 'rd', 0x9, 3, 0x00deadbe ), - - req( 'rd', 0xa, base_addr+0, 0, 0 ), resp( 'rd', 0xa, 0, 0xdeadbeef ), - - ] - -#---------------------------------------------------------------------- -# Test Case: subword writes -#---------------------------------------------------------------------- - -def subword_wr_msgs( base_addr ): - return [ - - req( 'wr', 0x0, base_addr+0, 1, 0x000000ef ), resp( 'wr', 0x0, 0, 0 ), - req( 'wr', 0x1, base_addr+1, 1, 0x000000be ), resp( 'wr', 0x1, 0, 0 ), - req( 'wr', 0x2, base_addr+2, 1, 0x000000ad ), resp( 'wr', 0x2, 0, 0 ), - req( 'wr', 0x3, base_addr+3, 1, 0x000000de ), resp( 'wr', 0x3, 0, 0 ), - req( 'rd', 0x4, base_addr+0, 0, 0 ), resp( 'rd', 0x4, 0, 0xdeadbeef ), - - req( 'wr', 0x5, base_addr+0, 2, 0x0000abcd ), resp( 'wr', 0x5, 0, 0 ), - req( 'wr', 0x6, base_addr+2, 2, 0x0000ef01 ), resp( 'wr', 0x6, 0, 0 ), - req( 'rd', 0x7, base_addr+0, 0, 0 ), resp( 'rd', 0x7, 0, 0xef01abcd ), - - req( 'wr', 0x8, base_addr+1, 2, 0x00002345 ), resp( 'wr', 0x8, 0, 0 ), - req( 'rd', 0xa, base_addr+0, 0, 0 ), resp( 'rd', 0xa, 0, 0xef2345cd ), - - req( 'wr', 0xb, base_addr+0, 3, 0x00cafe02 ), resp( 'wr', 0xb, 0, 0 ), - req( 'rd', 0xc, base_addr+0, 0, 0 ), resp( 'rd', 0xc, 0, 0xefcafe02 ), - - ] - -#---------------------------------------------------------------------- -# Test Case: amos -#---------------------------------------------------------------------- - -def amo_msgs( base_addr ): - return [ - # load some initial data - req( 'wr', 0x0, base_addr , 0, 0x01234567 ), resp( 'wr', 0x0, 0, 0 ), - req( 'wr', 0x0, base_addr+4 , 0, 0x98765432 ), resp( 'wr', 0x0, 0, 0 ), - req( 'wr', 0x0, base_addr+8 , 0, 0x22002200 ), resp( 'wr', 0x0, 0, 0 ), - req( 'wr', 0x0, base_addr+12, 0, 0x00112233 ), resp( 'wr', 0x0, 0, 0 ), - req( 'wr', 0x0, base_addr+16, 0, 0x44556677 ), resp( 'wr', 0x0, 0, 0 ), - req( 'wr', 0x0, base_addr+20, 0, 0x01230123 ), resp( 'wr', 0x0, 0, 0 ), - # amo.add - req( 'ad', 0x1, base_addr , 0, 0x12345678 ), resp( 'ad', 0x1, 0, 0x01234567 ), - req( 'rd', 0x2, base_addr , 0, 0 ), resp( 'rd', 0x2, 0, 0x13579bdf ), - # amo.and - req( 'an', 0x3, base_addr+4 , 0, 0x23456789 ), resp( 'an', 0x3, 0, 0x98765432 ), - req( 'rd', 0x4, base_addr+4 , 0, 0 ), resp( 'rd', 0x4, 0, 0x00444400 ), - # amo.or - req( 'or', 0x5, base_addr+8 , 0, 0x01230123 ), resp( 'or', 0x5, 0, 0x22002200 ), - req( 'rd', 0x6, base_addr+8 , 0, 0 ), resp( 'rd', 0x6, 0, 0x23232323 ), - # amo.xchg - req( 'xg', 0x5, base_addr+12, 0, 0xdeadbeef ), resp( 'xg', 0x5, 0, 0x00112233 ), - req( 'rd', 0x6, base_addr+12, 0, 0 ), resp( 'rd', 0x6, 0, 0xdeadbeef ), - # amo.min -- mem is smaller - req( 'mn', 0x7, base_addr+16, 0, 0xcafebabe ), resp( 'mn', 0x7, 0, 0x44556677 ), - req( 'rd', 0x8, base_addr+16, 0, 0 ), resp( 'rd', 0x8, 0, 0xcafebabe ), - # amo.min -- arg is smaller - req( 'mn', 0x9, base_addr+20, 0, 0x01201234 ), resp( 'mn', 0x9, 0, 0x01230123 ), - req( 'rd', 0xa, base_addr+20, 0, 0 ), resp( 'rd', 0xa, 0, 0x01201234 ), - ] - -#---------------------------------------------------------------------- -# Test Case: random -#---------------------------------------------------------------------- - -def random_msgs( base_addr ): - - rgen = random.Random() - rgen.seed(0xa4e28cc2) - - vmem = [ rgen.randint(0,0xffffffff) for _ in range(20) ] - msgs = [] - - for i in range(20): - msgs.extend([ - req( 'wr', i, base_addr+4*i, 0, vmem[i] ), resp( 'wr', i, 0, 0 ), - ]) - - for i in range(1): - idx = rgen.randint(0,19) - - if rgen.randint(0,1): - - correct_data = vmem[idx] - msgs.extend([ - req( 'rd', i, base_addr+4*idx, 0, 0 ), resp( 'rd', i, 0, correct_data ), - ]) - - else: - - new_data = rgen.randint(0,0xffffffff) - vmem[idx] = new_data - msgs.extend([ - req( 'wr', i, base_addr+4*idx, 0, new_data ), resp( 'wr', i, 0, 0 ), - ]) - - return msgs - -#------------------------------------------------------------------------- -# Test Case Table -#------------------------------------------------------------------------- - -test_case_table = mk_test_case_table([ - ( "msg_func stall extra_lat src_init src_intv sink_init sink_intv"), - [ "basic", basic_msgs, 0.0, 0, 0, 0, 0, 0 ], - [ "stream", stream_msgs, 0.0, 0, 0, 0, 0, 0 ], - [ "subword_rd", subword_rd_msgs, 0.0, 0, 0, 0, 0, 0 ], - [ "subword_wr", subword_wr_msgs, 0.0, 0, 0, 0, 0, 0 ], - [ "amo", amo_msgs, 0.0, 0, 0, 0, 0, 0 ], - [ "random", random_msgs, 0.0, 0, 0, 0, 0, 0 ], - [ "random_3x14", random_msgs, 0.0, 0, 5, 3, 7, 14 ], - [ "stream_stall0.5_extralat0", stream_msgs, 0.5, 0, 0, 0, 0, 0 ], - [ "stream_stall0.0_extralat4", stream_msgs, 0.0, 4, 0, 0, 0, 0 ], - [ "stream_stall0.5_extralat4", stream_msgs, 0.5, 4, 0, 0, 0, 0 ], - [ "random_stall0.5_extralat4_3x14", random_msgs, 0.5, 4, 5, 14, 7, 14 ], -]) - -#------------------------------------------------------------------------- -# Test cases for 1 port -#------------------------------------------------------------------------- - -@pytest.mark.parametrize( **test_case_table ) -def test_1port( test_params, cmdline_opts ): - msgs = test_params.msg_func(0x1000) - run_sim( TestHarness( MagicMemoryRTL, 1, [(req_cls, resp_cls)], - [ msgs[::2] ], - [ msgs[1::2] ], - test_params.stall, test_params.extra_lat, - test_params.src_init, test_params.src_intv, - test_params.sink_init, test_params.sink_intv ), cmdline_opts ) - -#------------------------------------------------------------------------- -# Test cases for 2 port -#------------------------------------------------------------------------- - -@pytest.mark.parametrize( **test_case_table ) -def test_2port( test_params, cmdline_opts ): - msgs0 = test_params.msg_func(0x1000) - msgs1 = test_params.msg_func(0x2000) - run_sim( TestHarness( MagicMemoryRTL, 2, [(req_cls, resp_cls)]*2, - [ msgs0[::2], msgs1[::2] ], - [ msgs0[1::2], msgs1[1::2] ], - test_params.stall, test_params.extra_lat, - test_params.src_init, test_params.src_intv, - test_params.sink_init, test_params.sink_intv ), cmdline_opts ) - -@pytest.mark.parametrize( **test_case_table ) -def test_20port( test_params, cmdline_opts ): - msgs = [ test_params.msg_func(0x1000*i) for i in range(20) ] - run_sim( TestHarness( MagicMemoryRTL, 20, [(req_cls, resp_cls)]*20, - [ x[::2] for x in msgs ], - [ x[1::2] for x in msgs ], - test_params.stall, test_params.extra_lat, - test_params.src_init, test_params.src_intv, - test_params.sink_init, test_params.sink_intv ), cmdline_opts ) - -#------------------------------------------------------------------------- -# Test Read/Write Mem -#------------------------------------------------------------------------- - -def test_read_write_mem( cmdline_opts ): - - rgen = random.Random() - rgen.seed(0x05a3e95b) - - # Test data we want to write into memory - - data = [ rgen.randrange(-(2**31),2**31) for _ in range(20) ] - - # Convert test data into byte array - - data_bytes = struct.pack("<{}i".format(len(data)),*data) - - # Create memory messages to read and verify memory contents - - msgs = [] - for i, item in enumerate(data): - msgs.extend([ - req( 'rd', 0x1, 0x1000+4*i, 0, 0 ), resp( 'rd', 0x1, 0, item ), - ]) - - # Create test harness with above memory messages - - th = TestHarness( MagicMemoryRTL, 2, [(req_cls, resp_cls)]*2, [msgs[::2], []], [msgs[1::2], []], - 0, 0, 0, 0, 0, 0 ) - th.elaborate() - - # Write the data into the test memory - - th.mem.write_mem( 0x1000, data_bytes ) - - # Run the test - - run_sim( th ) - - # Read the data back out of the test memory - - result_bytes = th.mem.read_mem( 0x1000, len(data_bytes) ) - - # Convert result bytes into list of ints - - result = list(struct.unpack("<{}i".format(len(data)),result_bytes)) - - # Compare result to original data - - assert result == data diff --git a/pymtl3/stdlib/stream/valrdy_master_minion_ifcs.py b/pymtl3/stdlib/stream/valrdy_master_minion_ifcs.py deleted file mode 100644 index 26397b319..000000000 --- a/pymtl3/stdlib/stream/valrdy_master_minion_ifcs.py +++ /dev/null @@ -1,152 +0,0 @@ -""" -========================================================================== - master_minion_ifc.py -========================================================================== -Master/minion send/recv interface implementations at CL and RTL. - Author: Shunning Jiang - Date: Jan 28, 2020 -""" -from pymtl3 import * - -from .ifcs import RecvIfcRTL, SendIfcRTL -from pymtl3.extra import clone_deepcopy - -#------------------------------------------------------------------------- -# CL interfaces -#------------------------------------------------------------------------- - -# There is no custom connect method in CL ifcs. -# For MasterCL-MinionRTL and MasterRTL-MinionCL connections, we just need -# to connect by name and leverage the custom connect method of the nested -# Send/RecvIfc. - -class MasterIfcCL( Interface ): - def construct( s, ReqType, RespType, *, req=None, req_rdy=None, resp=None, resp_rdy=None ): - s.ReqType = ReqType - s.RespType = RespType - s.req = CalleeIfcCL( Type=ReqType, method=req, rdy=req_rdy ) - s.resp = CalleeIfcCL( Type=RespType, method=resp, rdy=resp_rdy ) - def __str__( s ): - return f"{s.req}|{s.resp}" - -class MinionIfcCL( Interface ): - def construct( s, ReqType, RespType ): - s.ReqType = ReqType - s.RespType = RespType - s.req = CallerIfcCL( Type=ReqType ) - s.resp = CallerIfcCL( Type=RespType ) - def __str__( s ): - return f"{s.req}|{s.resp}" - -#------------------------------------------------------------------------- -# RTL interfaces -#------------------------------------------------------------------------- -# There is no custom connect method in CL ifcs. -# For MasterCL-MinionRTL and MasterRTL-MinionCL connections, we just need -# to connect by name and leverage the custom connect method of the nested -# Send/RecvIfc. - -class MasterIfcRTL( Interface ): - def construct( s, ReqType, RespType ): - s.ReqType = ReqType - s.RespType = RespType - s.req = RecvIfcRTL( Type=ReqType ) - s.resp = SendIfcRTL( Type=RespType ) - def __str__( s ): - return f"{s.req}|{s.resp}" - - def connect( s, other, parent ): - if isinstance( other, MinionIfcCL ): - m = ValRdyMasterMinionRTL2CLAdapter( other.ReqType, other.RespType ) - - if hasattr( parent, "ValRdyMasterMinionRTL2CLAdapter_count" ): - count = parent.XcelIfcFL2RTL_count - setattr( parent, "ValRdyMasterMinionRTL2CLAdapter_count" + str( count ), m ) - else: - parent.ValRdyMasterMinionRTL2CLAdapter_count = 0 - parent.ValRdyMasterMinionRTL2CLAdapter_0 = m - - s //= m.left - m.right //= other - parent.ValRdyMasterMinionRTL2CLAdapter_count += 1 - return True - - return False - -class MinionIfcRTL( Interface ): - def construct( s, ReqType, RespType ): - s.ReqType = ReqType - s.RespType = RespType - s.req = RecvIfcRTL( Type=ReqType ) - s.resp = SendIfcRTL( Type=RespType ) - def __str__( s ): - return f"{s.req}|{s.resp}" - -class ValRdyMasterMinionRTL2CLAdapter( Component ): - - def req_rdy( s ): - return s.req_entry is not None - - def req( s ): - assert s.req_entry is not None - ret = s.req_entry - s.req_entry = None - return ret - - def resp_rdy( s ): - return s.resp_entry is None - - def resp( s, msg ): - s.resp_entry = clone_deepcopy( msg ) - - def construct( s, ReqType, RespType ): - s.left = MinionIfcRTL( ReqType, RespType ) - s.right = MasterIfcCL( ReqType, RespType, req=s.req, req_rdy=s.req_rdy, resp=s.resp, resp_rdy=s.resp_rdy ) - - # Req side - - s.req_entry = None - - @update_ff - def up_left_req_rdy(): - s.left.req.rdy <<= (s.req_entry is None) - - @update_once - def up_left_req_msg(): - if s.req_entry is None: - if s.left.req.val: - s.req_entry = clone_deepcopy( s.left.req.msg ) - - s.add_constraints( - U( up_left_req_msg ) < M( s.req ), - U( up_left_req_msg ) < M( s.req_rdy ), - ) - - # Resp side - - s.resp_entry = None - s.resp_sent = Wire() - - @update_once - def up_right_resp(): - if s.resp_entry is None: - s.left.resp.val @= 0 - else: - s.left.resp.val @= 1 - s.left.resp.msg @= s.resp_entry - - @update_ff - def up_resp_sent(): - s.resp_sent <<= s.left.resp.val & s.left.resp.rdy - - @update_once - def up_clear(): - if s.resp_sent: # constraints reverse this - s.resp_entry = None - - s.add_constraints( - U( up_clear ) < M( s.resp ), - U( up_clear ) < M( s.resp_rdy ), - M( s.resp ) < U( up_right_resp ), - M( s.resp_rdy ) < U( up_right_resp ) - ) diff --git a/pymtl3/stdlib/stream/valrdy_test_masters.py b/pymtl3/stdlib/stream/valrdy_test_masters.py deleted file mode 100644 index e6dc2c31c..000000000 --- a/pymtl3/stdlib/stream/valrdy_test_masters.py +++ /dev/null @@ -1,30 +0,0 @@ -""" -========================================================================== - test_masters.py -========================================================================== - - Author: Shunning Jiang - Date: May 27, 2020 -""" -from pymtl3 import * -from pymtl3.stdlib.ifcs.valrdy_master_minion_ifcs import MasterIfcRTL - -from .valrdy_test_sinks import TestSinkRTL -from .valrdy_test_srcs import TestSrcRTL - - -class TestMasterRTL( Component ): - - def construct( s, ReqType, RespType, MasterIfc=MasterIfcRTL ): - s.master = MasterIfc( ReqType, RespType ) - s.src = TestSrcRTL( ReqType ) - s.sink = TestSinkRTL( RespType ) - - s.src.out //= s.master.req - s.sink.in_ //= s.master.resp - - def done( s ): - return s.src.done() and s.sink.done() - - def line_trace( s ): - return str(s.master) diff --git a/pymtl3/stdlib/test_utils/test_sinks.py b/pymtl3/stdlib/test_utils/test_sinks.py deleted file mode 100644 index 65d3890a7..000000000 --- a/pymtl3/stdlib/test_utils/test_sinks.py +++ /dev/null @@ -1,149 +0,0 @@ -""" -======================================================================== -Test sinks -======================================================================== -Test sinks with CL and RTL interfaces. - -Author : Yanghui Ou - Date : Mar 11, 2019 -""" - -from pymtl3 import * -from pymtl3.stdlib.dstruct.ifcs import RecvIfc -from pymtl3.stdlib.dstruct.ifcs.ifcs import RecvRTL2SendCL - - -class PyMTLTestSinkError( Exception ): pass - -#------------------------------------------------------------------------- -# BehavioralTestSink -#------------------------------------------------------------------------- - -class BehavioralTestSink( Component ): - - def construct( s, Type, msgs, initial_delay=0, interval_delay=0, - arrival_time=None, cmp_fn=lambda a, b : a == b ): - - s.recv.Type = Type - - # [msgs] and [arrival_time] must have the same length. - if arrival_time is not None: - assert len( msgs ) == len( arrival_time ) - - s.idx = 0 - s.cycle_count = 0 - s.msgs = list( msgs ) - s.arrival_time = None if not arrival_time else list( arrival_time ) - s.cmp_fn = cmp_fn - s.error_msg = '' - - s.all_msg_recved = False - s.done_flag = False - - s.count = initial_delay - s.intv = interval_delay - - s.recv_called = False - - @update_once - def up_sink_count(): - # Raise exception at the start of next cycle so that the errored - # line trace gets printed out - if s.error_msg: - raise PyMTLTestSinkError( s.error_msg ) - - # Tick one more cycle after all message is received so that the - # exception gets thrown - if s.all_msg_recved: - s.done_flag = True - - if s.idx >= len( s.msgs ): - s.all_msg_recved = True - - if not s.reset: - s.cycle_count += 1 - else: - s.cycle_count = 0 - - # if recv was called in previous cycle - if s.recv_called: - s.count = s.intv - elif s.count != 0: - s.count -= 1 - else: - s.count = 0 - - s.recv_called = False - - s.add_constraints( - U( up_sink_count ) < M( s.recv ), - U( up_sink_count ) < M( s.recv.rdy ) - ) - - @non_blocking( lambda s: s.count==0 ) - def recv( s, msg ): - assert s.count == 0, "Invalid en/rdy transaction! Sink is stalled (not ready), but receives a message." - - # Sanity check - if s.idx >= len( s.msgs ): - s.error_msg = ( 'Test Sink received more msgs than expected!\n' - f'Received : {msg}' ) - - # Check correctness first - elif not s.cmp_fn( msg, s.msgs[ s.idx ] ): - s.error_msg = ( - f'Test sink {s} received WRONG message!\n' - f'Expected : { s.msgs[ s.idx ] }\n' - f'Received : { msg }' - ) - - # Check timing if performance regeression is turned on - elif s.arrival_time and s.cycle_count > s.arrival_time[ s.idx ]: - s.error_msg = ( - f'Test sink {s} received message LATER than expected!\n' - f'Expected msg : {s.msgs[ s.idx ]}\n' - f'Expected at : {s.arrival_time[ s.idx ]}\n' - f'Received msg : {msg}\n' - f'Received at : {s.cycle_count}' - ) - - else: - s.idx += 1 - s.recv_called = True - - def done( s ): - return s.done_flag - - # Line trace - def line_trace( s ): - return "{}".format( s.recv ) - -#------------------------------------------------------------------------- -# TestSinkFL -#------------------------------------------------------------------------- - -class TestSinkFL( Component ): - - def construct( s, Type, msgs, initial_delay=0, interval_delay=0, - arrival_time=None, cmp_fn=lambda a, b : a == b ): - - # Interface - - s.recv = RecvIfc( Type ) - - # Components - - s.sink = BehavioralTestSink( Type, msgs, initial_delay, interval_delay, - arrival_time, cmp_fn ) - s.adapter = RecvRTL2SendCL( Type ) - - connect( s.recv, s.adapter.recv ) - connect( s.adapter.send, s.sink.recv ) - - def done( s ): - return s.sink.done() - - # Line trace - - def line_trace( s ): - return "{}".format( s.recv ) diff --git a/pymtl3/stdlib/test_utils/test_srcs.py b/pymtl3/stdlib/test_utils/test_srcs.py deleted file mode 100644 index a8d930b71..000000000 --- a/pymtl3/stdlib/test_utils/test_srcs.py +++ /dev/null @@ -1,73 +0,0 @@ -""" -======================================================================== -SrcRTL -======================================================================== -Test sources with CL or RTL interfaces. - -Author : Yanghui Ou - Date : Mar 11, 2019 -""" -from collections import deque - -from pymtl3 import * -from pymtl3.stdlib.dstruct.ifcs import SendIfc -from pymtl3.stdlib.dstruct.ifcs.ifcs import RecvCL2SendRTL - -#------------------------------------------------------------------------- -# BehavioralTestSource -#------------------------------------------------------------------------- - -class BehavioralTestSource( Component ): - - def construct( s, Type, msgs, initial_delay=0, interval_delay=0 ): - - s.send = CallerIfcCL( Type=Type ) - s.msgs = deque( msgs ) - - s.count = initial_delay - s.delay = interval_delay - - @update_once - def up_src_send(): - if s.count > 0: - s.count -= 1 - elif not s.reset: - if s.send.rdy() and s.msgs: - s.send( s.msgs.popleft() ) - s.count = s.delay # reset count after a message is sent - - def done( s ): - return not s.msgs - - # Line trace - - def line_trace( s ): - return "{}".format( s.send ) - -#------------------------------------------------------------------------- -# TestSourceFL -#------------------------------------------------------------------------- - -class TestSourceFL( Component ): - - def construct( s, Type, msgs, initial_delay=0, interval_delay=0 ): - - # Interface - - s.send = SendIfc( Type ) - - # Components - - s.src = BehavioralTestSource( Type, msgs, initial_delay, interval_delay ) - s.adapter = RecvCL2SendRTL( Type ) - - connect( s.src.send, s.adapter.recv ) - connect( s.adapter.send, s.send ) - - def done( s ): - return s.src.done() - - # Line trace - - def line_trace( s ): - return "{}".format( s.send ) diff --git a/pymtl3/stdlib/test_utils/valrdy_test_srcs.py b/pymtl3/stdlib/test_utils/valrdy_test_srcs.py deleted file mode 100644 index b859806d5..000000000 --- a/pymtl3/stdlib/test_utils/valrdy_test_srcs.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -======================================================================== -Test sources -======================================================================== -Test sources with CL or RTL interfaces. - -Author : Yanghui Ou - Date : Mar 11, 2019 -""" -from collections import deque -from copy import deepcopy - -from pymtl3 import * -from pymtl3.stdlib.ifcs import OutValRdyIfc - - -class TestSrcRTL( Component ): - - def construct( s, Type, msgs, initial_delay=0, interval_delay=0 ): - - # Interface - - s.out = OutValRdyIfc( Type ) - - # Data - - s.msgs = deepcopy(msgs) - - # TODO: use wires and ROM to make it translatable - s.idx = 0 - s.num_msgs = len(s.msgs) - s.count = 0 - - @update_ff - def up_src(): - if s.reset: - s.idx = 0 - s.count = initial_delay - s.out.val <<= 0 - - else: - if s.out.val & s.out.rdy: - s.idx += 1 - s.count = interval_delay - - if s.count > 0: - s.count -= 1 - s.out.val <<= 0 - - else: # s.count == 0 - if s.idx < s.num_msgs: - s.out.val <<= 1 - s.out.msg <<= s.msgs[s.idx] - else: - s.out.val <<= 0 - - - def done( s ): - return s.idx >= s.num_msgs - - # Line trace - - def line_trace( s ): - return f"{s.out}" diff --git a/pymtl3/stdlib/ifcs/XcelMsg.py b/pymtl3/stdlib/xcel/XcelMsg.py similarity index 100% rename from pymtl3/stdlib/ifcs/XcelMsg.py rename to pymtl3/stdlib/xcel/XcelMsg.py diff --git a/pymtl3/stdlib/xcel/__init__.py b/pymtl3/stdlib/xcel/__init__.py new file mode 100644 index 000000000..c64e9ca94 --- /dev/null +++ b/pymtl3/stdlib/xcel/__init__.py @@ -0,0 +1 @@ +from .XcelMsg import XcelMsgType, mk_xcel_msg, mk_xcel_req_msg, mk_xcel_resp_msg diff --git a/pymtl3/stdlib/xcel/ifcs/__init__.py b/pymtl3/stdlib/xcel/ifcs/__init__.py new file mode 100644 index 000000000..de8ed887d --- /dev/null +++ b/pymtl3/stdlib/xcel/ifcs/__init__.py @@ -0,0 +1 @@ +from .ifcs import XcelRequesterIfc, XcelResponderIfc diff --git a/pymtl3/stdlib/xcel/ifcs/ifcs.py b/pymtl3/stdlib/xcel/ifcs/ifcs.py new file mode 100644 index 000000000..131d20cc1 --- /dev/null +++ b/pymtl3/stdlib/xcel/ifcs/ifcs.py @@ -0,0 +1,8 @@ +from pymtl3 import * +from pymtl3.stdlib.reqresp.ifcs import RequesterIfc, ResponderIfc + +class XcelRequesterIfc( RequesterIfc ): + pass + +class XcelResponderIfc( ResponderIfc ): + pass diff --git a/pymtl3/stdlib/ifcs/test/XcelMsg_test.py b/pymtl3/stdlib/xcel/test/XcelMsg_test.py similarity index 100% rename from pymtl3/stdlib/ifcs/test/XcelMsg_test.py rename to pymtl3/stdlib/xcel/test/XcelMsg_test.py diff --git a/pymtl3/stdlib/delays/test/__init__.py b/pymtl3/stdlib/xcel/test/__init__.py similarity index 100% rename from pymtl3/stdlib/delays/test/__init__.py rename to pymtl3/stdlib/xcel/test/__init__.py diff --git a/pymtl3/stdlib/xcel/test/xcel_ifcs_test.py b/pymtl3/stdlib/xcel/test/xcel_ifcs_test.py new file mode 100644 index 000000000..04dc137a6 --- /dev/null +++ b/pymtl3/stdlib/xcel/test/xcel_ifcs_test.py @@ -0,0 +1,157 @@ +""" +========================================================================== + xcel_ifcs_test.py +========================================================================== + +Author : Yanghui Ou + Date : June 4, 2019 +""" + +from pymtl3 import * +from pymtl3.stdlib.primitive import RegisterFile +from pymtl3.stdlib.xcel import XcelMsgType, mk_xcel_msg +from pymtl3.stdlib.stream import StreamNormalQueue +from pymtl3.stdlib.test_utils import run_sim + +from ..ifcs.ifcs import XcelRequesterIfc, XcelResponderIfc + +#------------------------------------------------------------------------- +# RTL requester/responder +#------------------------------------------------------------------------- + +class SomeRequester( Component ): + + def construct( s, ReqType, RespType, nregs=16 ): + + # Interface + + s.xcel = XcelRequesterIfc( ReqType, RespType ) + + # Local parameters + + DataType = ReqType.get_field_type( 'data' ) + assert DataType is RespType.get_field_type( 'data' ) + AddrType = ReqType.get_field_type( 'addr' ) + + s.nregs = nregs + + # Components + + s.addr = Wire( AddrType ) + s.count = Wire( Bits16 ) + s.flag = Wire( Bits1 ) + + @update_ff + def up_rtl_addr(): + if s.reset: + s.addr <<= AddrType(0) + elif s.xcel.reqstream.val and not s.flag: + s.addr <<= s.addr + AddrType(1) + + @update_ff + def up_rtl_flag(): + if s.reset: + s.flag <<= Bits1(1) + elif s.xcel.reqstream.val: + s.flag <<= ~s.flag + + @update_ff + def up_rtl_count(): + if s.reset: + s.count <<= Bits16(0) + elif s.xcel.respstream.val and s.xcel.respstream.msg.type_ == XcelMsgType.READ: + s.count <<= s.count + Bits16(1) + + @update + def up_req(): + s.xcel.reqstream.val @= ~s.reset & s.xcel.reqstream.rdy + s.xcel.reqstream.msg.type_ @= XcelMsgType.WRITE if s.flag else XcelMsgType.READ + s.xcel.reqstream.msg.addr @= s.addr + s.xcel.reqstream.msg.data @= 0xface0000 | int(s.addr) + + @update + def up_resp(): + s.xcel.respstream.rdy @= 1 + + def done( s ): + return s.count == s.nregs + + def line_trace( s ): + return "{}({}){}".format( s.xcel.reqstream, s.flag, s.xcel.respstream ) + +class SomeResponder( Component ): + + def construct( s, ReqType, RespType, nregs=16 ): + + # Interface + + s.xcel = XcelResponderIfc( ReqType, RespType ) + + # Local parameters + + DataType = ReqType.get_field_type( 'data' ) + assert DataType is RespType.get_field_type( 'data' ) + + s.nregs = nregs + + # Components + + s.req_q = StreamNormalQueue( ReqType, num_entries=1 ) + s.wen = Wire( Bits1 ) + + s.reg_file = m = RegisterFile( DataType, nregs ) + m.raddr[0] //= s.req_q.ostream.msg.addr + m.rdata[0] //= s.xcel.respstream.msg.data + m.wen[0] //= s.wen + m.waddr[0] //= s.req_q.ostream.msg.addr + m.wdata[0] //= s.req_q.ostream.msg.data + + connect( s.xcel.reqstream, s.req_q.istream ) + connect( s.xcel.respstream.msg.type_, s.req_q.ostream.msg.type_ ) + + @update + def up_wen(): + s.wen @= s.req_q.ostream.rdy & (s.req_q.ostream.msg.type_ == XcelMsgType.WRITE) + + @update + def up_resp(): + s.xcel.respstream.val @= s.req_q.ostream.val & s.xcel.respstream.rdy + s.req_q.ostream.rdy @= s.req_q.ostream.val & s.xcel.respstream.rdy + + def line_trace( s ): + return str(s.xcel) + +#------------------------------------------------------------------------- +# TestHarness +#------------------------------------------------------------------------- + +class TestHarness( Component ): + + def construct( s, + MasterType = SomeRequester, + MinionType = SomeResponder, + nregs = 16 ): + ReqType, RespType = mk_xcel_msg( addr=clog2(nregs), data=32 ) + s.master = MasterType( ReqType, RespType, nregs=nregs ) + s.minion = MinionType( ReqType, RespType, nregs=nregs ) + + connect( s.master.xcel, s.minion.xcel ) + + def line_trace( s ): + return "{} > {}".format( s.master.line_trace(), s.minion.line_trace() ) + + def done( s ): + return s.master.done() + +#------------------------------------------------------------------------- +# RTL-RTL composition +#------------------------------------------------------------------------- + +def test_xcel_rtl_rtl(): + th = TestHarness() + th.set_param( "top.construct", + MasterType = SomeRequester, + MinionType = SomeResponder, + nregs = 8, + ) + run_sim( th ) From 686db8c2320fb64a614eea9da6bf405f9420280d Mon Sep 17 00:00:00 2001 From: pp482 Date: Mon, 29 Aug 2022 18:52:51 -0400 Subject: [PATCH 005/101] [WIP] Remove out dated tests --- ...slationImport_closed_loop_directed_test.py | 79 ------------------- ...slationImport_closed_loop_directed_test.py | 71 ----------------- 2 files changed, 150 deletions(-) delete mode 100644 pymtl3/passes/backends/verilog/test/TranslationImport_closed_loop_directed_test.py delete mode 100644 pymtl3/passes/backends/yosys/test/TranslationImport_closed_loop_directed_test.py diff --git a/pymtl3/passes/backends/verilog/test/TranslationImport_closed_loop_directed_test.py b/pymtl3/passes/backends/verilog/test/TranslationImport_closed_loop_directed_test.py deleted file mode 100644 index 613b0c0ee..000000000 --- a/pymtl3/passes/backends/verilog/test/TranslationImport_closed_loop_directed_test.py +++ /dev/null @@ -1,79 +0,0 @@ -#========================================================================= -# TranslationImport_closed_loop_directed_test.py -#========================================================================= -# Author : Peitian Pan -# Date : Jun 5, 2019 -"""Closed-loop test with SystemVerilog translation and import.""" - -from pymtl3.datatypes import Bits1, mk_bits -from pymtl3.passes.PassGroups import DefaultPassGroup -from pymtl3.passes.rtlir.util.test_utility import do_test -from pymtl3.stdlib.queues.test.enrdy_queues_test import ( - test_bypass_queue as _bypass_queue, -) -from pymtl3.stdlib.queues.test.enrdy_queues_test import ( - test_bypass_queue_stall as _bypass_queue_stall, -) -from pymtl3.stdlib.queues.test.enrdy_queues_test import ( - test_normal_queue as _normal_queue, -) -from pymtl3.stdlib.queues.test.enrdy_queues_test import ( - test_normal_queue_stall as _normal_queue_stall, -) -from pymtl3.stdlib.queues.test.enrdy_queues_test import test_pipe_queue as _pipe_queue -from pymtl3.stdlib.queues.test.enrdy_queues_test import ( - test_pipe_queue_stall as _pipe_queue_stall, -) - -from .. import VerilogTranslationImportPass -from ..util.test_utility import closed_loop_component_input_test - -#------------------------------------------------------------------------- -# Valrdy queue tests -#------------------------------------------------------------------------- - -def run_sim( _th ): - _th.elaborate() - _th.q.set_metadata( VerilogTranslationImportPass.enable, True ) - th = VerilogTranslationImportPass()( _th ) - - try: - th.apply( DefaultPassGroup() ) - th.sim_reset() - - while not th.done() and th.sim_cycle_count() < 1000: - th.sim_tick() - - assert th.sim_cycle_count() < 1000 - - th.sim_tick() - th.sim_tick() - th.sim_tick() - finally: - th.q.finalize() - -def _run_queue_test_replace_run_sim( run_sim, test_func ): - original_run_sim = test_func.__globals__['run_sim'] - test_func.__globals__['run_sim'] = run_sim - try: - test_func() - finally: - test_func.__globals__['run_sim'] = original_run_sim - -def test_normal_queue(): - _run_queue_test_replace_run_sim( run_sim, _normal_queue ) - -def test_normal_queue_stall(): - _run_queue_test_replace_run_sim( run_sim, _normal_queue_stall ) - -def test_pipe_queue(): - _run_queue_test_replace_run_sim( run_sim, _pipe_queue ) - -def test_pipe_queue_stall(): - _run_queue_test_replace_run_sim( run_sim, _pipe_queue_stall ) - -def test_bypass_queue(): - _run_queue_test_replace_run_sim( run_sim, _bypass_queue ) - -def test_bypass_queue_stall(): - _run_queue_test_replace_run_sim( run_sim, _bypass_queue_stall ) diff --git a/pymtl3/passes/backends/yosys/test/TranslationImport_closed_loop_directed_test.py b/pymtl3/passes/backends/yosys/test/TranslationImport_closed_loop_directed_test.py deleted file mode 100644 index ad3e7adc1..000000000 --- a/pymtl3/passes/backends/yosys/test/TranslationImport_closed_loop_directed_test.py +++ /dev/null @@ -1,71 +0,0 @@ -#========================================================================= -# TranslationImport_closed_loop_directed_test.py -#========================================================================= -# Author : Peitian Pan -# Date : Jun 5, 2019 -"""Closed-loop test with SystemVerilog translation and import.""" - -from pymtl3.passes.backends.verilog.test.TranslationImport_closed_loop_directed_test import ( - _run_queue_test_replace_run_sim, -) -from pymtl3.passes.PassGroups import DefaultPassGroup -from pymtl3.stdlib.queues.test.enrdy_queues_test import ( - test_bypass_queue as _bypass_queue, -) -from pymtl3.stdlib.queues.test.enrdy_queues_test import ( - test_bypass_queue_stall as _bypass_queue_stall, -) -from pymtl3.stdlib.queues.test.enrdy_queues_test import ( - test_normal_queue as _normal_queue, -) -from pymtl3.stdlib.queues.test.enrdy_queues_test import ( - test_normal_queue_stall as _normal_queue_stall, -) -from pymtl3.stdlib.queues.test.enrdy_queues_test import test_pipe_queue as _pipe_queue -from pymtl3.stdlib.queues.test.enrdy_queues_test import ( - test_pipe_queue_stall as _pipe_queue_stall, -) - -from ..YosysTranslationImportPass import YosysTranslationImportPass - -#------------------------------------------------------------------------- -# Valrdy queue tests -#------------------------------------------------------------------------- - -def yosys_run_sim( _th ): - _th.elaborate() - _th.q.set_metadata( YosysTranslationImportPass.enable, True ) - th = YosysTranslationImportPass()( _th ) - - try: - th.apply( DefaultPassGroup() ) - th.sim_reset() - - while not th.done() and th.sim_cycle_count() < 1000: - th.sim_tick() - - assert th.sim_cycle_count() < 1000 - - th.sim_tick() - th.sim_tick() - th.sim_tick() - finally: - th.q.finalize() - -def test_normal_queue(): - _run_queue_test_replace_run_sim( yosys_run_sim, _normal_queue ) - -def test_normal_queue_stall(): - _run_queue_test_replace_run_sim( yosys_run_sim, _normal_queue_stall ) - -def test_pipe_queue(): - _run_queue_test_replace_run_sim( yosys_run_sim, _pipe_queue ) - -def test_pipe_queue_stall(): - _run_queue_test_replace_run_sim( yosys_run_sim, _pipe_queue_stall ) - -def test_bypass_queue(): - _run_queue_test_replace_run_sim( yosys_run_sim, _bypass_queue ) - -def test_bypass_queue_stall(): - _run_queue_test_replace_run_sim( yosys_run_sim, _bypass_queue_stall ) From 6f12baf52ebb264dcc11fd4ee84ab79ef15574bb Mon Sep 17 00:00:00 2001 From: pp482 Date: Mon, 29 Aug 2022 19:04:41 -0400 Subject: [PATCH 006/101] Remove TestMasterCL --- pymtl3/stdlib/test_utils/test_masters.py | 30 ------------------------ 1 file changed, 30 deletions(-) delete mode 100644 pymtl3/stdlib/test_utils/test_masters.py diff --git a/pymtl3/stdlib/test_utils/test_masters.py b/pymtl3/stdlib/test_utils/test_masters.py deleted file mode 100644 index 16d188574..000000000 --- a/pymtl3/stdlib/test_utils/test_masters.py +++ /dev/null @@ -1,30 +0,0 @@ -""" -========================================================================== - test_masters.py -========================================================================== - - Author: Shunning Jiang - Date: May 27, 2020 -""" -from pymtl3 import * -from pymtl3.stdlib.ifcs import MasterIfcCL - -from .test_sinks import TestSinkFL -from .test_srcs import TestSourceFL - - -class TestMasterCL( Component ): - - def construct( s, ReqType, RespType, MasterIfc=MasterIfcCL ): - s.master = MasterIfc( ReqType, RespType ) - s.src = TestSourceFL( ReqType ) - s.sink = TestSinkFL( RespType ) - - s.src.send //= s.master.req - s.sink.recv //= s.master.resp - - def done( s ): - return s.src.done() and s.sink.done() - - def line_trace( s ): - return str(s.master) From 406ff8c9c4c71ce46f98cca223854378971dd8dd Mon Sep 17 00:00:00 2001 From: pp482 Date: Wed, 31 Aug 2022 16:19:29 -0400 Subject: [PATCH 007/101] Add back the RTL-FL adapters --- pymtl3/stdlib/mem/__init__.py | 1 + pymtl3/stdlib/mem/adapters.py | 102 +++++++++++++++++++++ pymtl3/stdlib/stream/__init__.py | 1 + pymtl3/stdlib/stream/adapters.py | 67 ++++++++++++++ pymtl3/stdlib/stream/test/adapters_test.py | 72 +++++++++++++++ pymtl3/stdlib/xcel/__init__.py | 1 + pymtl3/stdlib/xcel/adapters.py | 83 +++++++++++++++++ 7 files changed, 327 insertions(+) create mode 100644 pymtl3/stdlib/mem/adapters.py create mode 100644 pymtl3/stdlib/stream/adapters.py create mode 100644 pymtl3/stdlib/stream/test/adapters_test.py create mode 100644 pymtl3/stdlib/xcel/adapters.py diff --git a/pymtl3/stdlib/mem/__init__.py b/pymtl3/stdlib/mem/__init__.py index f2845ef7c..24a3e53bf 100644 --- a/pymtl3/stdlib/mem/__init__.py +++ b/pymtl3/stdlib/mem/__init__.py @@ -1,3 +1,4 @@ from .MemoryFL import MemoryFL from .MemMsg import MemMsgType, mk_mem_msg, mk_mem_req_msg, mk_mem_resp_msg from .ROM import CombinationalROM, SequentialROM +from .adapters import MemRequesterAdapterFL diff --git a/pymtl3/stdlib/mem/adapters.py b/pymtl3/stdlib/mem/adapters.py new file mode 100644 index 000000000..f7eb45fa8 --- /dev/null +++ b/pymtl3/stdlib/mem/adapters.py @@ -0,0 +1,102 @@ +import greenlet + +from pymtl3 import * +from pymtl3.stdlib.mem.MemMsg import MemMsgType +from pymtl3.stdlib.reqresp.ifcs import RequesterIfc + +class MemRequesterAdapterFL( Component ): + + @blocking + def read( s, addr, nbytes ): + while s.req_entry is not None: + greenlet.getcurrent().parent.switch(0) + + s.req_entry = s.create_req( MemMsgType.READ, 0, addr, nbytes ) + + while s.resp_entry is None: + greenlet.getcurrent().parent.switch(0) + + ret = s.resp_entry.data[0:nbytes<<3] + s.resp_entry = None + return ret + + @blocking + def write( s, addr, nbytes, data ): + while s.req_entry is not None: + greenlet.getcurrent().parent.switch(0) + + s.req_entry = s.create_req( MemMsgType.WRITE, 0, addr, nbytes, data ) + + while s.resp_entry is None: + greenlet.getcurrent().parent.switch(0) + + s.resp_entry = None + + @blocking + def amo( s, amo_type, addr, nbytes, data ): + while s.req_entry is not None: + greenlet.getcurrent().parent.switch(0) + + s.req_entry = s.create_req( amo_type, 0, addr, nbytes, data ) + + while s.resp_entry is None: + greenlet.getcurrent().parent.switch(0) + + ret = s.resp_entry.data[0:nbytes<<3] + s.resp_entry = None + return ret + + def construct( s, ReqType, RespType ): + s.requester = RequesterIfc( ReqType, RespType ) + + # Use create_req to handle type mismatch + Tlen = ReqType.get_field_type('len') + Tdata = ReqType.get_field_type('data') + s.create_req = lambda a,b,c,d,e=0: ReqType( a, b, c, Tlen(d, trunc_int=True), Tdata(int(e)) ) + + s.req_entry = None + s.resp_entry = None + + # req path + + s.req_sent = Wire() + + @update_ff + def up_req_sent(): + s.req_sent <<= s.requester.reqstream.val & s.requester.reqstream.rdy + + @update + def up_clear_req(): + if s.req_sent: + s.req_entry = None + + @update_once + def up_send_req(): + if s.req_entry is None: + s.requester.reqstream.val @= 0 + else: + s.requester.reqstream.val @= 1 + s.requester.reqstream.msg @= s.req_entry + + # resp path + @update_once + def up_resp_rdy(): + s.requester.respstream.rdy @= (s.resp_entry is None) + + @update_once + def up_resp_msg(): + if (s.resp_entry is None) & s.requester.respstream.val: + s.resp_entry = clone_deepcopy( s.requester.respstream.msg ) + + s.add_constraints( U( up_clear_req ) < M(s.read), + U( up_clear_req ) < M(s.write), + U( up_clear_req ) < M(s.amo), + + M( s.read ) < U( up_send_req ), + M( s.write ) < U( up_send_req ), + M( s.amo ) < U( up_send_req ), + + M( s.read ) < U( up_resp_rdy ), + M( s.write ) < U( up_resp_rdy ), + M( s.amo ) < U( up_resp_rdy ), + U( up_resp_rdy ) < U( up_resp_msg ) ) diff --git a/pymtl3/stdlib/stream/__init__.py b/pymtl3/stdlib/stream/__init__.py index 101ff27b8..440a9e244 100644 --- a/pymtl3/stdlib/stream/__init__.py +++ b/pymtl3/stdlib/stream/__init__.py @@ -2,3 +2,4 @@ from .StreamSourceFL import StreamSourceFL from . import ifcs from .queues import StreamNormalQueue, StreamPipeQueue, StreamBypassQueue +from .adapters import IStreamDeqAdapterFL, OStreamEnqAdapterFL diff --git a/pymtl3/stdlib/stream/adapters.py b/pymtl3/stdlib/stream/adapters.py new file mode 100644 index 000000000..e4a3d13f8 --- /dev/null +++ b/pymtl3/stdlib/stream/adapters.py @@ -0,0 +1,67 @@ +import greenlet + +from pymtl3 import * +from pymtl3.extra import clone_deepcopy +from .ifcs.ifcs import IStreamIfc, OStreamIfc + +class IStreamDeqAdapterFL( Component ): + + @non_blocking( lambda s: s.entry is not None ) + def deq( s ): + ret = s.entry + s.entry = None + return ret + + def construct( s, Type ): + s.istream = IStreamIfc( Type ) + s.entry = None + + @update_once + def up_recv_rdy(): + s.istream.rdy @= (s.entry is None) + + @update_once + def up_recv_msg(): + if (s.entry is None) & s.istream.val: + s.entry = clone_deepcopy( s.istream.msg ) + + s.add_constraints( M( s.deq ) < U( up_recv_rdy ), # deq before recv in a cycle -- pipe behavior + M( s.deq.rdy ) < U( up_recv_rdy ), + U( up_recv_rdy ) < U( up_recv_msg ) ) + + +class OStreamEnqAdapterFL( Component ): + + @non_blocking( lambda s: s.entry is None ) + def enq( s, msg ): + s.entry = clone_deepcopy( msg ) + + def construct( s, Type ): + s.ostream = OStreamIfc( Type ) + + s.entry = None + s.sent = Wire() + + @update_once + def up_send(): + if s.entry is None: + s.ostream.val @= 0 + else: + s.ostream.val @= 1 + s.ostream.msg @= s.entry + + @update_ff + def up_sent(): + s.sent <<= s.ostream.val & s.ostream.rdy + + @update_once + def up_clear(): + if s.sent: # constraints reverse this + s.entry = None + + s.add_constraints( + U( up_clear ) < M( s.enq ), + U( up_clear ) < M( s.enq.rdy ), + M( s.enq ) < U( up_send ), + M( s.enq.rdy ) < U( up_send ) + ) diff --git a/pymtl3/stdlib/stream/test/adapters_test.py b/pymtl3/stdlib/stream/test/adapters_test.py new file mode 100644 index 000000000..c7432066a --- /dev/null +++ b/pymtl3/stdlib/stream/test/adapters_test.py @@ -0,0 +1,72 @@ +import pytest + +from pymtl3 import * +from pymtl3.stdlib.stream import StreamSourceFL, StreamSinkFL +from pymtl3.stdlib.stream.ifcs import IStreamIfc, OStreamIfc +from pymtl3.stdlib.test_utils import run_sim + +from ..adapters import IStreamDeqAdapterFL, OStreamEnqAdapterFL + +class SimplePassthroughFL( Component ): + def construct( s, Type ): + s.istream = IStreamIfc( Type ) + s.ostream = OStreamIfc( Type ) + s.ideq_adapter = IStreamDeqAdapterFL( Type ) + s.oenq_adapter = OStreamEnqAdapterFL( Type ) + + s.istream //= s.ideq_adapter.istream + s.oenq_adapter.ostream //= s.ostream + + @update_once + def up_passthrough(): + if s.ideq_adapter.deq.rdy() and s.oenq_adapter.enq.rdy(): + msg = s.ideq_adapter.deq() + s.oenq_adapter.enq( msg ) + +class TestHarness( Component ): + def construct( s, Type, src_msgs, sink_msgs ): + s.src = StreamSourceFL( Type, src_msgs ) + s.sink = StreamSinkFL( Type, sink_msgs ) + s.dut = SimplePassthroughFL( Type ) + + s.src.ostream //= s.dut.istream + s.dut.ostream //= s.sink.istream + + def done( s ): + return s.src.done() and s.sink.done() + + def line_trace( s ): + return f"{s.src.line_trace()} > {s.sink.line_trace()}" + +bit_msgs = [ Bits16( 0 ), Bits16( 1 ), Bits16( 2 ), Bits16( 3 ), + Bits16( 0 ), Bits16( 1 ), Bits16( 2 ), Bits16( 3 ), + Bits16( 0 ), Bits16( 1 ), Bits16( 2 ), Bits16( 3 ) ] + +arrival0 = [ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ] +arrival1 = [ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 ] +arrival2 = [ 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35 ] +arrival3 = [ 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35 ] +arrival4 = [ 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65 ] + +@pytest.mark.parametrize( + ('Type', 'msgs', 'src_init', 'src_intv', + 'sink_init', 'sink_intv' ), + [ + ( Bits16, bit_msgs, 0, 0, 0, 0 ), + ( Bits16, bit_msgs, 10, 1, 0, 0 ), + ( Bits16, bit_msgs, 10, 0, 0, 1 ), + ( Bits16, bit_msgs, 3, 4, 5, 3 ) + ] +) +def test_src_sink_fl_adapter( Type, msgs, src_init, src_intv, sink_init, + sink_intv ): + th = TestHarness( Type, msgs, msgs ) + th.set_param( "top.src.construct", + initial_delay = src_init, + interval_delay = src_intv, + ) + th.set_param( "top.sink.construct", + initial_delay = sink_init, + interval_delay = sink_intv, + ) + run_sim( th ) diff --git a/pymtl3/stdlib/xcel/__init__.py b/pymtl3/stdlib/xcel/__init__.py index c64e9ca94..65e914003 100644 --- a/pymtl3/stdlib/xcel/__init__.py +++ b/pymtl3/stdlib/xcel/__init__.py @@ -1 +1,2 @@ from .XcelMsg import XcelMsgType, mk_xcel_msg, mk_xcel_req_msg, mk_xcel_resp_msg +from .adapters import XcelRequesterAdapterFL diff --git a/pymtl3/stdlib/xcel/adapters.py b/pymtl3/stdlib/xcel/adapters.py new file mode 100644 index 000000000..aa321247a --- /dev/null +++ b/pymtl3/stdlib/xcel/adapters.py @@ -0,0 +1,83 @@ +import greenlet + +from pymtl3 import * +from pymtl3.stdlib.reqresp.ifcs import RequesterIfc + +class XcelRequesterAdapterFL( Component ): + + @blocking + def read( s, addr ): + while s.req_entry is not None: + greenlet.getcurrent().parent.switch(0) + + s.req_entry = s.create_req( 0, addr ) + + while s.resp_entry is None: + greenlet.getcurrent().parent.switch(0) + + ret = s.resp_entry.data + s.resp_entry = None + return ret + + @blocking + def write( s, addr, data ): + while s.req_entry is not None: + greenlet.getcurrent().parent.switch(0) + + s.req_entry = s.create_req( 1, addr, data ) + + while s.resp_entry is None: + greenlet.getcurrent().parent.switch(0) + + s.resp_entry = None + + def construct( s, ReqType, RespType ): + s.requester = RequesterIfc( ReqType, RespType ) + + # Use create_req to handle type mismatch + Tdata = ReqType.get_field_type('data') + s.create_req = lambda a,b,c=0: ReqType( a, b, Tdata(int(c)) ) + + s.req_entry = None + s.resp_entry = None + + # req path + + s.req_sent = Wire() + + @update_ff + def up_req_sent(): + s.req_sent <<= s.requester.reqstream.val & s.requester.reqstream.rdy + + @update + def up_clear_req(): + if s.req_sent: + s.req_entry = None + + @update_once + def up_send_req(): + if s.req_entry is None: + s.requester.reqstream.val @= 0 + else: + s.requester.reqstream.val @= 1 + s.requester.reqstream.msg @= s.req_entry + + # resp path + @update_once + def up_resp_rdy(): + s.requester.respstream.rdy @= (s.resp_entry is None) + + @update_once + def up_resp_msg(): + if (s.resp_entry is None) & s.requester.respstream.val: + s.resp_entry = clone_deepcopy( s.requester.respstream.msg ) + + s.add_constraints( U( up_clear_req ) < M(s.read), + U( up_clear_req ) < M(s.write), + + M( s.read ) < U( up_send_req ), + M( s.write ) < U( up_send_req ), + + M( s.read ) < U( up_resp_rdy ), + M( s.write ) < U( up_resp_rdy ), + U( up_resp_rdy ) < U( up_resp_msg ) ) From bed393fcfbdae0a2f2cbf2d99f77c6477fb41cff Mon Sep 17 00:00:00 2001 From: pp482 Date: Wed, 31 Aug 2022 17:37:47 -0400 Subject: [PATCH 008/101] Split adapter implementation into single files --- .../{adapters.py => MemRequesterAdapterFL.py} | 0 pymtl3/stdlib/mem/__init__.py | 2 +- pymtl3/stdlib/stream/IStreamDeqAdapterFL.py | 28 +++++++++++++++++ .../{adapters.py => OStreamEnqAdapterFL.py} | 30 +------------------ pymtl3/stdlib/stream/__init__.py | 3 +- pymtl3/stdlib/stream/test/adapters_test.py | 3 +- ...{adapters.py => XcelRequesterAdapterFL.py} | 0 pymtl3/stdlib/xcel/__init__.py | 2 +- 8 files changed, 35 insertions(+), 33 deletions(-) rename pymtl3/stdlib/mem/{adapters.py => MemRequesterAdapterFL.py} (100%) create mode 100644 pymtl3/stdlib/stream/IStreamDeqAdapterFL.py rename pymtl3/stdlib/stream/{adapters.py => OStreamEnqAdapterFL.py} (52%) rename pymtl3/stdlib/xcel/{adapters.py => XcelRequesterAdapterFL.py} (100%) diff --git a/pymtl3/stdlib/mem/adapters.py b/pymtl3/stdlib/mem/MemRequesterAdapterFL.py similarity index 100% rename from pymtl3/stdlib/mem/adapters.py rename to pymtl3/stdlib/mem/MemRequesterAdapterFL.py diff --git a/pymtl3/stdlib/mem/__init__.py b/pymtl3/stdlib/mem/__init__.py index 24a3e53bf..c9c97f57f 100644 --- a/pymtl3/stdlib/mem/__init__.py +++ b/pymtl3/stdlib/mem/__init__.py @@ -1,4 +1,4 @@ from .MemoryFL import MemoryFL from .MemMsg import MemMsgType, mk_mem_msg, mk_mem_req_msg, mk_mem_resp_msg from .ROM import CombinationalROM, SequentialROM -from .adapters import MemRequesterAdapterFL +from .MemRequesterAdapterFL import MemRequesterAdapterFL diff --git a/pymtl3/stdlib/stream/IStreamDeqAdapterFL.py b/pymtl3/stdlib/stream/IStreamDeqAdapterFL.py new file mode 100644 index 000000000..7d43c4afb --- /dev/null +++ b/pymtl3/stdlib/stream/IStreamDeqAdapterFL.py @@ -0,0 +1,28 @@ +from pymtl3 import * +from pymtl3.extra import clone_deepcopy +from .ifcs.ifcs import IStreamIfc + +class IStreamDeqAdapterFL( Component ): + + @non_blocking( lambda s: s.entry is not None ) + def deq( s ): + ret = s.entry + s.entry = None + return ret + + def construct( s, Type ): + s.istream = IStreamIfc( Type ) + s.entry = None + + @update_once + def up_recv_rdy(): + s.istream.rdy @= (s.entry is None) + + @update_once + def up_recv_msg(): + if (s.entry is None) & s.istream.val: + s.entry = clone_deepcopy( s.istream.msg ) + + s.add_constraints( M( s.deq ) < U( up_recv_rdy ), # deq before recv in a cycle -- pipe behavior + M( s.deq.rdy ) < U( up_recv_rdy ), + U( up_recv_rdy ) < U( up_recv_msg ) ) diff --git a/pymtl3/stdlib/stream/adapters.py b/pymtl3/stdlib/stream/OStreamEnqAdapterFL.py similarity index 52% rename from pymtl3/stdlib/stream/adapters.py rename to pymtl3/stdlib/stream/OStreamEnqAdapterFL.py index e4a3d13f8..9a4f506fa 100644 --- a/pymtl3/stdlib/stream/adapters.py +++ b/pymtl3/stdlib/stream/OStreamEnqAdapterFL.py @@ -1,34 +1,6 @@ -import greenlet - from pymtl3 import * from pymtl3.extra import clone_deepcopy -from .ifcs.ifcs import IStreamIfc, OStreamIfc - -class IStreamDeqAdapterFL( Component ): - - @non_blocking( lambda s: s.entry is not None ) - def deq( s ): - ret = s.entry - s.entry = None - return ret - - def construct( s, Type ): - s.istream = IStreamIfc( Type ) - s.entry = None - - @update_once - def up_recv_rdy(): - s.istream.rdy @= (s.entry is None) - - @update_once - def up_recv_msg(): - if (s.entry is None) & s.istream.val: - s.entry = clone_deepcopy( s.istream.msg ) - - s.add_constraints( M( s.deq ) < U( up_recv_rdy ), # deq before recv in a cycle -- pipe behavior - M( s.deq.rdy ) < U( up_recv_rdy ), - U( up_recv_rdy ) < U( up_recv_msg ) ) - +from .ifcs.ifcs import OStreamIfc class OStreamEnqAdapterFL( Component ): diff --git a/pymtl3/stdlib/stream/__init__.py b/pymtl3/stdlib/stream/__init__.py index 440a9e244..54791530b 100644 --- a/pymtl3/stdlib/stream/__init__.py +++ b/pymtl3/stdlib/stream/__init__.py @@ -2,4 +2,5 @@ from .StreamSourceFL import StreamSourceFL from . import ifcs from .queues import StreamNormalQueue, StreamPipeQueue, StreamBypassQueue -from .adapters import IStreamDeqAdapterFL, OStreamEnqAdapterFL +from .IStreamDeqAdapterFL import IStreamDeqAdapterFL +from .OStreamEnqAdapterFL import OStreamEnqAdapterFL diff --git a/pymtl3/stdlib/stream/test/adapters_test.py b/pymtl3/stdlib/stream/test/adapters_test.py index c7432066a..4cffa0164 100644 --- a/pymtl3/stdlib/stream/test/adapters_test.py +++ b/pymtl3/stdlib/stream/test/adapters_test.py @@ -5,7 +5,8 @@ from pymtl3.stdlib.stream.ifcs import IStreamIfc, OStreamIfc from pymtl3.stdlib.test_utils import run_sim -from ..adapters import IStreamDeqAdapterFL, OStreamEnqAdapterFL +from ..IStreamDeqAdapterFL import IStreamDeqAdapterFL +from ..OStreamEnqAdapterFL import OStreamEnqAdapterFL class SimplePassthroughFL( Component ): def construct( s, Type ): diff --git a/pymtl3/stdlib/xcel/adapters.py b/pymtl3/stdlib/xcel/XcelRequesterAdapterFL.py similarity index 100% rename from pymtl3/stdlib/xcel/adapters.py rename to pymtl3/stdlib/xcel/XcelRequesterAdapterFL.py diff --git a/pymtl3/stdlib/xcel/__init__.py b/pymtl3/stdlib/xcel/__init__.py index 65e914003..6ecd61c1f 100644 --- a/pymtl3/stdlib/xcel/__init__.py +++ b/pymtl3/stdlib/xcel/__init__.py @@ -1,2 +1,2 @@ from .XcelMsg import XcelMsgType, mk_xcel_msg, mk_xcel_req_msg, mk_xcel_resp_msg -from .adapters import XcelRequesterAdapterFL +from .XcelRequesterAdapterFL import XcelRequesterAdapterFL From 519c711b97e941a5385b3703c3dd087e145f86fa Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 8 Sep 2022 12:42:20 -0400 Subject: [PATCH 009/101] Hold stream source when current message is None --- pymtl3/stdlib/stream/StreamSourceFL.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pymtl3/stdlib/stream/StreamSourceFL.py b/pymtl3/stdlib/stream/StreamSourceFL.py index 3b934c396..ac7932829 100644 --- a/pymtl3/stdlib/stream/StreamSourceFL.py +++ b/pymtl3/stdlib/stream/StreamSourceFL.py @@ -29,6 +29,7 @@ def construct( s, Type, msgs, initial_delay=0, interval_delay=0 ): # TODO: use wires and ROM to make it translatable s.idx = 0 s.count = 0 + s.prev_is_none = False @update_ff def up_src(): @@ -38,7 +39,7 @@ def up_src(): s.ostream.val <<= 0 else: - if s.ostream.val & s.ostream.rdy: + if (s.ostream.val & s.ostream.rdy) or s.prev_is_none: s.idx += 1 s.count = interval_delay @@ -48,8 +49,13 @@ def up_src(): else: # s.count == 0 if s.idx < len(s.msgs): - s.ostream.val <<= 1 - s.ostream.msg <<= s.msgs[s.idx] + if s.msgs[s.idx] is None: + s.ostream.val <<= 0 + s.prev_is_none = True + else: + s.ostream.val <<= 1 + s.ostream.msg <<= s.msgs[s.idx] + s.prev_is_none = False else: s.ostream.val <<= 0 From 25c37519dc99eef54cdef4aacccb20db7b736cc5 Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 8 Sep 2022 12:42:34 -0400 Subject: [PATCH 010/101] Add line_trace to I/OStreamIfc --- pymtl3/stdlib/stream/ifcs/ifcs.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/pymtl3/stdlib/stream/ifcs/ifcs.py b/pymtl3/stdlib/stream/ifcs/ifcs.py index b1a6dfdc3..54c3b7893 100644 --- a/pymtl3/stdlib/stream/ifcs/ifcs.py +++ b/pymtl3/stdlib/stream/ifcs/ifcs.py @@ -24,11 +24,17 @@ def construct( s, Type ): s.val = InPort() s.rdy = OutPort() - s.trace_len = len(str(Type())) + if isinstance( Type, int ): + s.trace_len = len(str(mk_bits(Type)())) + else: + s.trace_len = len(str(Type())) def __str__( s ): return valrdy_to_str( s.msg, s.val, s.rdy, s.trace_len ) + def line_trace( s ): + return str( s ) + class OStreamIfc( Interface ): def construct( s, Type ): @@ -37,7 +43,13 @@ def construct( s, Type ): s.val = OutPort() s.rdy = InPort() - s.trace_len = len(str(Type())) + if isinstance( Type, int ): + s.trace_len = len(str(mk_bits(Type)())) + else: + s.trace_len = len(str(Type())) def __str__( s ): return valrdy_to_str( s.msg, s.val, s.rdy, s.trace_len ) + + def line_trace( s ): + return str( s ) From b72cdea5d727f83b3003030a3b0612d2bfcc2c07 Mon Sep 17 00:00:00 2001 From: pp482 Date: Sat, 10 Sep 2022 14:30:13 -0400 Subject: [PATCH 011/101] [ProcFL] Add back ProcFL with adapters --- examples/ex03_proc/ProcFL.py | 158 +++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 examples/ex03_proc/ProcFL.py diff --git a/examples/ex03_proc/ProcFL.py b/examples/ex03_proc/ProcFL.py new file mode 100644 index 000000000..163f42693 --- /dev/null +++ b/examples/ex03_proc/ProcFL.py @@ -0,0 +1,158 @@ +""" +========================================================================== +ProcFL +========================================================================== +TinyRV0 FL proc. + +Author : Shunning Jiang, Peitian Pan + Date : Sep 9, 2022 +""" + +from pymtl3 import * +from pymtl3.stdlib.stream.ifcs import IStreamIfc, OStreamIfc +from pymtl3.stdlib.xcel import mk_xcel_msg +from pymtl3.stdlib.xcel.ifcs import XcelRequesterIfc +from pymtl3.stdlib.mem import mk_mem_msg +from pymtl3.stdlib.mem.ifc import MemRequesterIfc + +from .tinyrv0_encoding import RegisterFile, TinyRV0Inst, disassemble_inst + + +class ProcFL( Component ): + + def construct( s ): + + req_class, resp_class = mk_mem_msg( 8, 32, 32 ) + xreq_class, xresp_class = mk_xcel_msg( 5, 32 ) + + # Interface, Buffers to hold request/response messages + + s.commit_inst = OutPort( Bits1 ) + + s.imem = MemRequesterIfc( req_class, resp_class ) + s.dmem = MemRequesterIfc( req_class, resp_class ) + s.xcel = XcelRequesterIfc( xreq_class, xresp_class ) + + s.proc2mngr = OStreamIfc( Bits32 ) + s.mngr2proc = IStreamIfc( Bits32 ) + + # Adapters to convert ports into callable methods + + s.imem_adapter = MemRequesterAdapterFL( req_class, resp_class ) + s.dmem_adapter = MemRequesterAdapterFL( req_class, resp_class ) + s.xcel_adapter = XcelRequesterAdapterFL( xreq_class, xresp_class ) + + connect( s.imem, s.imem_adapter.requester ) + connect( s.dmem, s.dmem_adapter.requester ) + connect( s.xcel, s.xcel_adapter.requester ) + + s.proc2mngr_q = OStreamEnqAdapterFL( Bits32 ) + s.mngr2proc_q = IStreamDeqAdapterFL( Bits32 ) + + connect( s.mngr2proc, s.mngr2proc_q.istream ) + connect( s.proc2mngr_q.ostream, s.proc2mngr ) + + # Internal data structures + + s.PC = b32( 0x200 ) + + s.R = RegisterFile(32) + s.raw_inst = None + + @update_once + def up_ProcFL(): + if s.reset: + s.PC = b32( 0x200 ) + return + + s.commit_inst @= 0 + + try: + s.raw_inst = s.imem_adapter.read( s.PC, 4 ) # line trace + + inst = TinyRV0Inst( s.raw_inst ) + inst_name = inst.name + + if inst_name == "nop": + s.PC += 4 + elif inst_name == "add": + s.R[inst.rd] = s.R[inst.rs1] + s.R[inst.rs2] + s.PC += 4 + elif inst_name == "sll": + s.R[inst.rd] = s.R[inst.rs1] << (s.R[inst.rs2] & 0x1F) + s.PC += 4 + elif inst_name == "srl": + s.R[inst.rd] = s.R[inst.rs1] >> (s.R[inst.rs2].uint() & 0x1F) + s.PC += 4 + + # ''' TUTORIAL TASK '''''''''''''''''''''''''''''''''''''''''''''' + # Implement instruction AND in FL processor + # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''\/ + #; Make an "elif" statement here to implement instruction AND + #; that applies bit-wise "and" operator to rs1 and rs2 and stores + #; the result to rd + + elif inst_name == "and": + s.R[inst.rd] = s.R[inst.rs1] & s.R[inst.rs2] + s.PC += 4 + + # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''/\ + + elif inst_name == "addi": + s.R[inst.rd] = s.R[inst.rs1] + sext( inst.i_imm, 32 ) + s.PC += 4 + elif inst_name == "sw": + addr = s.R[inst.rs1] + sext( inst.s_imm, 32 ) + s.dmem_adapter.write( addr, 4, s.R[inst.rs2] ) + s.PC += 4 + elif inst_name == "lw": + addr = s.R[inst.rs1] + sext( inst.i_imm, 32 ) + s.R[inst.rd] = s.dmem_adapter.read( addr, 4 ) + s.PC += 4 + elif inst_name == "bne": + if s.R[inst.rs1] != s.R[inst.rs2]: + s.PC = s.PC + sext( inst.b_imm, 32 ) + else: + s.PC += 4 + + elif inst_name == "csrw": + if inst.csrnum == 0x7C0: + if not s.proc2mngr_q.enq.rdy(): + return + s.proc2mngr.enq( s.R[inst.rs1] ) + elif 0x7E0 <= inst.csrnum <= 0x7FF: + s.xcel_adapter.write( inst.csrnum[0:5], s.R[inst.rs1] ) + else: + raise TinyRV2Semantics.IllegalInstruction( + "Unrecognized CSR register ({}) for csrw at PC={}" \ + .format(inst.csrnum.uint(),s.PC) ) + s.PC += 4 + + elif inst_name == "csrr": + if inst.csrnum == 0xFC0: + if not s.mngr2proc_q.deq.rdy(): + return + s.R[inst.rd] = s.mngr2proc_q.deq() + elif 0x7E0 <= inst.csrnum <= 0x7FF: + s.R[inst.rd] = s.xcel_adapter.read( inst.csrnum[0:5] ) + else: + raise TinyRV2Semantics.IllegalInstruction( + "Unrecognized CSR register ({}) for csrr at PC={}" \ + .format(inst.csrnum.uint(),s.PC) ) + + s.PC += 4 + + except: + print( "Unexpected error at PC={:0>8s}!".format( str(s.PC) ) ) + raise + + s.commit_inst @= 1 + + #----------------------------------------------------------------------- + # line_trace + #----------------------------------------------------------------------- + + def line_trace( s ): + if s.commit_inst: + return "[{:0>8s} {: <24}]".format( str(s.PC), disassemble_inst( s.raw_inst ) ) + return "[{}]".format( "#".ljust(33) ) From 3a6a375d4373b191ceddd177f4e5b661d18c6ce8 Mon Sep 17 00:00:00 2001 From: cb535 Date: Sun, 11 Sep 2022 20:33:51 -0400 Subject: [PATCH 012/101] get tests running on ProcFL --- examples/ex03_proc/ProcFL.py | 12 +- examples/ex03_proc/test/ProcFL_test.py | 252 +++++++++++++++++++ examples/ex03_proc/test/ProcRTL_test.py | 6 +- examples/ex03_proc/test/ProcVRTL_test.py | 10 +- pymtl3/stdlib/mem/MemRequesterAdapterFL.py | 1 + pymtl3/stdlib/xcel/XcelRequesterAdapterFL.py | 1 + 6 files changed, 274 insertions(+), 8 deletions(-) create mode 100644 examples/ex03_proc/test/ProcFL_test.py diff --git a/examples/ex03_proc/ProcFL.py b/examples/ex03_proc/ProcFL.py index 163f42693..dd64fb155 100644 --- a/examples/ex03_proc/ProcFL.py +++ b/examples/ex03_proc/ProcFL.py @@ -9,11 +9,13 @@ """ from pymtl3 import * +from pymtl3.extra import clone_deepcopy from pymtl3.stdlib.stream.ifcs import IStreamIfc, OStreamIfc -from pymtl3.stdlib.xcel import mk_xcel_msg -from pymtl3.stdlib.xcel.ifcs import XcelRequesterIfc -from pymtl3.stdlib.mem import mk_mem_msg -from pymtl3.stdlib.mem.ifc import MemRequesterIfc +from pymtl3.stdlib.stream import IStreamDeqAdapterFL, OStreamEnqAdapterFL +from pymtl3.stdlib.xcel.ifcs import XcelRequesterIfc +from pymtl3.stdlib.xcel import mk_xcel_msg, XcelRequesterAdapterFL +from pymtl3.stdlib.mem.ifcs import MemRequesterIfc +from pymtl3.stdlib.mem import mk_mem_msg, MemRequesterAdapterFL from .tinyrv0_encoding import RegisterFile, TinyRV0Inst, disassemble_inst @@ -119,7 +121,7 @@ def up_ProcFL(): if inst.csrnum == 0x7C0: if not s.proc2mngr_q.enq.rdy(): return - s.proc2mngr.enq( s.R[inst.rs1] ) + s.proc2mngr_q.enq( s.R[inst.rs1] ) elif 0x7E0 <= inst.csrnum <= 0x7FF: s.xcel_adapter.write( inst.csrnum[0:5], s.R[inst.rs1] ) else: diff --git a/examples/ex03_proc/test/ProcFL_test.py b/examples/ex03_proc/test/ProcFL_test.py new file mode 100644 index 000000000..135782b72 --- /dev/null +++ b/examples/ex03_proc/test/ProcFL_test.py @@ -0,0 +1,252 @@ +""" +========================================================================= +ProcRTL_test.py +========================================================================= +Includes test cases for the RTL TinyRV0 processor. + +Author : Shunning Jiang, Yanghui Ou + Date : June 12, 2019 +""" +import random + +import pytest + +from examples.ex03_proc.ProcFL import ProcFL +from pymtl3 import * +from pymtl3.stdlib.test_utils import run_sim + +from . import ( + inst_add, + inst_addi, + inst_and, + inst_bne, + inst_csr, + inst_lw, + inst_sll, + inst_srl, + inst_sw, + inst_xcel, +) +from .harness import TestHarness, asm_test, assemble + +random.seed(0xdeadbeef) + +#------------------------------------------------------------------------- +# ProcFL_Tests +#------------------------------------------------------------------------- + +@pytest.mark.usefixtures("cmdline_opts") +class ProcFL_Tests: + + # [setup_class] will be called by pytest before running all the tests in + # the test class. Here we specify the type of the processor that is used + # in all test cases. We can easily reuse all these test cases in simply + # by creating a new test class that inherits from this class and + # overwrite the setup_class to provide a different processor type. + @classmethod + def setup_class( cls ): + cls.ProcType = ProcFL + + # [run_sim] is a helper function in the test suite that creates a + # simulator and runs test. We can overwrite this function when + # inheriting from the test class to apply different passes to the DUT. + def run_sim( s, th, gen_test ): + + th.elaborate() + + # Assemble the program + mem_image = assemble( gen_test() ) + + # Load the program into memory + th.load( mem_image ) + + run_sim( th, s.__class__.cmdline_opts ) + + #----------------------------------------------------------------------- + # add + #----------------------------------------------------------------------- + + @pytest.mark.parametrize( "name,test", [ + asm_test( inst_add.gen_add_basic_test ) , + asm_test( inst_add.gen_dest_dep_test ) , + asm_test( inst_add.gen_src0_dep_test ) , + asm_test( inst_add.gen_src1_dep_test ) , + asm_test( inst_add.gen_srcs_dep_test ) , + asm_test( inst_add.gen_srcs_dest_test ) , + asm_test( inst_add.gen_value_test ) , + asm_test( inst_add.gen_random_test ) , + ]) + def test_add( s, name, test ): + th = TestHarness( s.ProcType ) + s.run_sim( th, test ) + + def test_add_rand_delays( s ): + th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, + mem_stall_prob=0.5, mem_latency=3 ) + s.run_sim( th, inst_add.gen_random_test ) + + #----------------------------------------------------------------------- + # and + #----------------------------------------------------------------------- + + @pytest.mark.parametrize( "name,test", [ + asm_test( inst_and.gen_and_basic_test ) , + ]) + def test_and( s, name, test ): + th = TestHarness( s.ProcType ) + s.run_sim( th, test ) + + #----------------------------------------------------------------------- + # sll + #----------------------------------------------------------------------- + + @pytest.mark.parametrize( "name,test", [ + asm_test( inst_sll.gen_basic_test ) , + asm_test( inst_sll.gen_random_test ) , + ]) + def test_sll( s, name, test ): + th = TestHarness( s.ProcType ) + s.run_sim( th, test ) + + def test_sll_rand_delays( s ): + th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, + mem_stall_prob=0.5, mem_latency=3 ) + s.run_sim( th, inst_sll.gen_random_test ) + + #----------------------------------------------------------------------- + # srl + #----------------------------------------------------------------------- + + @pytest.mark.parametrize( "name,test", [ + asm_test( inst_srl.gen_basic_test ) , + asm_test( inst_srl.gen_random_test ) , + ]) + def test_srl( s, name, test ): + th = TestHarness( s.ProcType ) + s.run_sim( th, test ) + + def test_srl_rand_delays( s ): + th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, + mem_stall_prob=0.5, mem_latency=3 ) + s.run_sim( th, inst_srl.gen_random_test ) + + #----------------------------------------------------------------------- + # bne + #----------------------------------------------------------------------- + + @pytest.mark.parametrize( "name,test", [ + asm_test( inst_bne.gen_basic_test ), + asm_test( inst_bne.gen_src0_dep_taken_test ), + asm_test( inst_bne.gen_src0_dep_nottaken_test ), + asm_test( inst_bne.gen_src1_dep_taken_test ), + asm_test( inst_bne.gen_src1_dep_nottaken_test ), + asm_test( inst_bne.gen_srcs_dep_taken_test ), + asm_test( inst_bne.gen_srcs_dep_nottaken_test ), + asm_test( inst_bne.gen_src0_eq_src1_test ), + asm_test( inst_bne.gen_value_test ), + asm_test( inst_bne.gen_random_test ), + ]) + def test_bne( s, name, test ): + th = TestHarness( s.ProcType ) + s.run_sim( th, test ) + + def test_bne_rand_delays( s ): + th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, + mem_stall_prob=0.5, mem_latency=3 ) + s.run_sim( th, inst_bne.gen_random_test ) + + #------------------------------------------------------------------------- + # addi + #------------------------------------------------------------------------- + + @pytest.mark.parametrize( "name,test", [ + asm_test( inst_addi.gen_basic_test ), + asm_test( inst_addi.gen_dest_dep_test ), + asm_test( inst_addi.gen_src_dep_test ), + asm_test( inst_addi.gen_srcs_dest_test ), + asm_test( inst_addi.gen_value_test ), + asm_test( inst_addi.gen_random_test ), + ]) + def test_addi( s, name, test ): + th = TestHarness( s.ProcType ) + s.run_sim( th, test ) + + def test_addi_rand_delays( s ): + th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, + mem_stall_prob=0.5, mem_latency=3 ) + s.run_sim( th, inst_addi.gen_random_test ) + + #----------------------------------------------------------------------- + # lw + #----------------------------------------------------------------------- + + @pytest.mark.parametrize( "name,test", [ + asm_test( inst_lw.gen_basic_test ), + asm_test( inst_lw.gen_dest_dep_test ), + asm_test( inst_lw.gen_base_dep_test ), + asm_test( inst_lw.gen_srcs_dest_test ), + asm_test( inst_lw.gen_value_test ), + asm_test( inst_lw.gen_random_test ), + ]) + def test_lw( s, name, test ): + th = TestHarness( s.ProcType ) + s.run_sim( th, test ) + + def test_lw_rand_delays( s ): + th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, + mem_stall_prob=0.5, mem_latency=3 ) + s.run_sim( th, inst_lw.gen_random_test ) + + #----------------------------------------------------------------------- + # sw + #----------------------------------------------------------------------- + + + @pytest.mark.parametrize( "name,test", [ + asm_test( inst_sw.gen_basic_test ), + asm_test( inst_sw.gen_random_test ), + ]) + def test_sw( s, name, test ): + th = TestHarness( s.ProcType ) + s.run_sim( th, test ) + + def test_sw_rand_delays( s ): + th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, + mem_stall_prob=0.5, mem_latency=3 ) + s.run_sim( th, inst_sw.gen_random_test ) + + #----------------------------------------------------------------------- + # csr + #----------------------------------------------------------------------- + + @pytest.mark.parametrize( "name,test", [ + asm_test( inst_csr.gen_basic_test ), + asm_test( inst_csr.gen_bypass_test ), + asm_test( inst_csr.gen_value_test ), + asm_test( inst_csr.gen_random_test ), + ]) + def test_csr( s, name, test ): + th = TestHarness( s.ProcType ) + s.run_sim( th, test ) + + def test_csr_rand_delays( s ): + th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, + mem_stall_prob=0.5, mem_latency=3 ) + s.run_sim( th, inst_csr.gen_random_test ) + + #----------------------------------------------------------------------- + # xcel + #----------------------------------------------------------------------- + + @pytest.mark.parametrize( "name,test", [ + asm_test( inst_xcel.gen_basic_test ), + asm_test( inst_xcel.gen_multiple_test ), + ]) + def test_xcel( s, name, test ): + th = TestHarness( s.ProcType ) + s.run_sim( th, test ) + + def test_xcel_rand_delays( s ): + th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, + mem_stall_prob=0.5, mem_latency=3 ) + s.run_sim( th, inst_xcel.gen_multiple_test ) diff --git a/examples/ex03_proc/test/ProcRTL_test.py b/examples/ex03_proc/test/ProcRTL_test.py index 1f7b13b6c..dd2704ac0 100644 --- a/examples/ex03_proc/test/ProcRTL_test.py +++ b/examples/ex03_proc/test/ProcRTL_test.py @@ -35,9 +35,13 @@ #------------------------------------------------------------------------- # ProcRTL_Tests #------------------------------------------------------------------------- +# It is as simple as inheriting from FL tests. No need to overwrite +# the [run_sim], since the version for ProcFL should work fine. + +from .ProcFL_test import ProcFL_Tests as BaseTests @pytest.mark.usefixtures("cmdline_opts") -class ProcRTL_Tests: +class ProcRTL_Tests( BaseTests ): # [setup_class] will be called by pytest before running all the tests in # the test class. Here we specify the type of the processor that is used diff --git a/examples/ex03_proc/test/ProcVRTL_test.py b/examples/ex03_proc/test/ProcVRTL_test.py index ab1362586..174fb2c0d 100644 --- a/examples/ex03_proc/test/ProcVRTL_test.py +++ b/examples/ex03_proc/test/ProcVRTL_test.py @@ -25,14 +25,18 @@ #------------------------------------------------------------------------- # ProcVRTL_Tests #------------------------------------------------------------------------- -# It is as simple as inheriting from RTL tests and overwrite [run_sim] +# It is as simple as inheriting from FL tests and overwriting the run_sim # function to apply the translation and import pass. -from .ProcRTL_test import ProcRTL_Tests as BaseTests +from .ProcFL_test import ProcFL_Tests as BaseTests @pytest.mark.usefixtures("cmdline_opts") class ProcVRTL_Tests( BaseTests ): + @classmethod + def setup_class( cls ): + cls.ProcType = ProcRTL + def run_sim( s, th, gen_test ): vcd_file_name = s.__class__.cmdline_opts["dump_vcd"] @@ -78,3 +82,5 @@ def test_proc_translate(): from os.path import dirname script_path = dirname(dirname(__file__)) + '/proc-translate' os.system(f'python {script_path}') + + diff --git a/pymtl3/stdlib/mem/MemRequesterAdapterFL.py b/pymtl3/stdlib/mem/MemRequesterAdapterFL.py index f7eb45fa8..7d3ad9955 100644 --- a/pymtl3/stdlib/mem/MemRequesterAdapterFL.py +++ b/pymtl3/stdlib/mem/MemRequesterAdapterFL.py @@ -1,6 +1,7 @@ import greenlet from pymtl3 import * +from pymtl3.extra import clone_deepcopy from pymtl3.stdlib.mem.MemMsg import MemMsgType from pymtl3.stdlib.reqresp.ifcs import RequesterIfc diff --git a/pymtl3/stdlib/xcel/XcelRequesterAdapterFL.py b/pymtl3/stdlib/xcel/XcelRequesterAdapterFL.py index aa321247a..336b1cf52 100644 --- a/pymtl3/stdlib/xcel/XcelRequesterAdapterFL.py +++ b/pymtl3/stdlib/xcel/XcelRequesterAdapterFL.py @@ -1,6 +1,7 @@ import greenlet from pymtl3 import * +from pymtl3.extra import clone_deepcopy from pymtl3.stdlib.reqresp.ifcs import RequesterIfc class XcelRequesterAdapterFL( Component ): From 9834cc8758eca79016a242397b0ca81608df5309 Mon Sep 17 00:00:00 2001 From: cb535 Date: Sun, 11 Sep 2022 20:39:58 -0400 Subject: [PATCH 013/101] forgot to commit this update --- examples/ex03_proc/test/ProcRTL_test.py | 204 ------------------------ 1 file changed, 204 deletions(-) diff --git a/examples/ex03_proc/test/ProcRTL_test.py b/examples/ex03_proc/test/ProcRTL_test.py index dd2704ac0..d53993394 100644 --- a/examples/ex03_proc/test/ProcRTL_test.py +++ b/examples/ex03_proc/test/ProcRTL_test.py @@ -51,207 +51,3 @@ class ProcRTL_Tests( BaseTests ): @classmethod def setup_class( cls ): cls.ProcType = ProcRTL - - # [run_sim] is a helper function in the test suite that creates a - # simulator and runs test. We can overwrite this function when - # inheriting from the test class to apply different passes to the DUT. - def run_sim( s, th, gen_test ): - - th.elaborate() - - # Assemble the program - mem_image = assemble( gen_test() ) - - # Load the program into memory - th.load( mem_image ) - - run_sim( th, s.__class__.cmdline_opts ) - - #----------------------------------------------------------------------- - # add - #----------------------------------------------------------------------- - - @pytest.mark.parametrize( "name,test", [ - asm_test( inst_add.gen_add_basic_test ) , - asm_test( inst_add.gen_dest_dep_test ) , - asm_test( inst_add.gen_src0_dep_test ) , - asm_test( inst_add.gen_src1_dep_test ) , - asm_test( inst_add.gen_srcs_dep_test ) , - asm_test( inst_add.gen_srcs_dest_test ) , - asm_test( inst_add.gen_value_test ) , - asm_test( inst_add.gen_random_test ) , - ]) - def test_add( s, name, test ): - th = TestHarness( s.ProcType ) - s.run_sim( th, test ) - - def test_add_rand_delays( s ): - th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, - mem_stall_prob=0.5, mem_latency=3 ) - s.run_sim( th, inst_add.gen_random_test ) - - #----------------------------------------------------------------------- - # and - #----------------------------------------------------------------------- - - @pytest.mark.parametrize( "name,test", [ - asm_test( inst_and.gen_and_basic_test ) , - ]) - def test_and( s, name, test ): - th = TestHarness( s.ProcType ) - s.run_sim( th, test ) - - #----------------------------------------------------------------------- - # sll - #----------------------------------------------------------------------- - - @pytest.mark.parametrize( "name,test", [ - asm_test( inst_sll.gen_basic_test ) , - asm_test( inst_sll.gen_random_test ) , - ]) - def test_sll( s, name, test ): - th = TestHarness( s.ProcType ) - s.run_sim( th, test ) - - def test_sll_rand_delays( s ): - th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, - mem_stall_prob=0.5, mem_latency=3 ) - s.run_sim( th, inst_sll.gen_random_test ) - - #----------------------------------------------------------------------- - # srl - #----------------------------------------------------------------------- - - @pytest.mark.parametrize( "name,test", [ - asm_test( inst_srl.gen_basic_test ) , - asm_test( inst_srl.gen_random_test ) , - ]) - def test_srl( s, name, test ): - th = TestHarness( s.ProcType ) - s.run_sim( th, test ) - - def test_srl_rand_delays( s ): - th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, - mem_stall_prob=0.5, mem_latency=3 ) - s.run_sim( th, inst_srl.gen_random_test ) - - #----------------------------------------------------------------------- - # bne - #----------------------------------------------------------------------- - - @pytest.mark.parametrize( "name,test", [ - asm_test( inst_bne.gen_basic_test ), - asm_test( inst_bne.gen_src0_dep_taken_test ), - asm_test( inst_bne.gen_src0_dep_nottaken_test ), - asm_test( inst_bne.gen_src1_dep_taken_test ), - asm_test( inst_bne.gen_src1_dep_nottaken_test ), - asm_test( inst_bne.gen_srcs_dep_taken_test ), - asm_test( inst_bne.gen_srcs_dep_nottaken_test ), - asm_test( inst_bne.gen_src0_eq_src1_test ), - asm_test( inst_bne.gen_value_test ), - asm_test( inst_bne.gen_random_test ), - ]) - def test_bne( s, name, test ): - th = TestHarness( s.ProcType ) - s.run_sim( th, test ) - - def test_bne_rand_delays( s ): - th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, - mem_stall_prob=0.5, mem_latency=3 ) - s.run_sim( th, inst_bne.gen_random_test ) - - #------------------------------------------------------------------------- - # addi - #------------------------------------------------------------------------- - - @pytest.mark.parametrize( "name,test", [ - asm_test( inst_addi.gen_basic_test ), - asm_test( inst_addi.gen_dest_dep_test ), - asm_test( inst_addi.gen_src_dep_test ), - asm_test( inst_addi.gen_srcs_dest_test ), - asm_test( inst_addi.gen_value_test ), - asm_test( inst_addi.gen_random_test ), - ]) - def test_addi( s, name, test ): - th = TestHarness( s.ProcType ) - s.run_sim( th, test ) - - def test_addi_rand_delays( s ): - th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, - mem_stall_prob=0.5, mem_latency=3 ) - s.run_sim( th, inst_addi.gen_random_test ) - - #----------------------------------------------------------------------- - # lw - #----------------------------------------------------------------------- - - @pytest.mark.parametrize( "name,test", [ - asm_test( inst_lw.gen_basic_test ), - asm_test( inst_lw.gen_dest_dep_test ), - asm_test( inst_lw.gen_base_dep_test ), - asm_test( inst_lw.gen_srcs_dest_test ), - asm_test( inst_lw.gen_value_test ), - asm_test( inst_lw.gen_random_test ), - ]) - def test_lw( s, name, test ): - th = TestHarness( s.ProcType ) - s.run_sim( th, test ) - - def test_lw_rand_delays( s ): - th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, - mem_stall_prob=0.5, mem_latency=3 ) - s.run_sim( th, inst_lw.gen_random_test ) - - #----------------------------------------------------------------------- - # sw - #----------------------------------------------------------------------- - - - @pytest.mark.parametrize( "name,test", [ - asm_test( inst_sw.gen_basic_test ), - asm_test( inst_sw.gen_random_test ), - ]) - def test_sw( s, name, test ): - th = TestHarness( s.ProcType ) - s.run_sim( th, test ) - - def test_sw_rand_delays( s ): - th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, - mem_stall_prob=0.5, mem_latency=3 ) - s.run_sim( th, inst_sw.gen_random_test ) - - #----------------------------------------------------------------------- - # csr - #----------------------------------------------------------------------- - - @pytest.mark.parametrize( "name,test", [ - asm_test( inst_csr.gen_basic_test ), - asm_test( inst_csr.gen_bypass_test ), - asm_test( inst_csr.gen_value_test ), - asm_test( inst_csr.gen_random_test ), - ]) - def test_csr( s, name, test ): - th = TestHarness( s.ProcType ) - s.run_sim( th, test ) - - def test_csr_rand_delays( s ): - th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, - mem_stall_prob=0.5, mem_latency=3 ) - s.run_sim( th, inst_csr.gen_random_test ) - - #----------------------------------------------------------------------- - # xcel - #----------------------------------------------------------------------- - - @pytest.mark.parametrize( "name,test", [ - asm_test( inst_xcel.gen_basic_test ), - asm_test( inst_xcel.gen_multiple_test ), - ]) - def test_xcel( s, name, test ): - th = TestHarness( s.ProcType ) - s.run_sim( th, test ) - - def test_xcel_rand_delays( s ): - th = TestHarness( s.ProcType, src_delay=3, sink_delay=14, - mem_stall_prob=0.5, mem_latency=3 ) - s.run_sim( th, inst_xcel.gen_multiple_test ) From 4f5a8b18fe66bdc0c7524019b2f0b5d7f7f1b0be Mon Sep 17 00:00:00 2001 From: cb535 Date: Wed, 21 Sep 2022 21:56:53 -0400 Subject: [PATCH 014/101] add random delays to src/sink --- pymtl3/stdlib/stream/StreamSinkFL.py | 8 ++++- pymtl3/stdlib/stream/StreamSourceFL.py | 9 ++++-- pymtl3/stdlib/stream/test/src_sink_test.py | 35 +++++++++++++--------- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/pymtl3/stdlib/stream/StreamSinkFL.py b/pymtl3/stdlib/stream/StreamSinkFL.py index 65bd21a4c..09fb7e9ba 100644 --- a/pymtl3/stdlib/stream/StreamSinkFL.py +++ b/pymtl3/stdlib/stream/StreamSinkFL.py @@ -8,6 +8,8 @@ Date : Aug 26, 2022 """ +from random import randint + from pymtl3 import * from .ifcs import IStreamIfc @@ -21,6 +23,7 @@ class PyMTLTestSinkError( Exception ): pass class StreamSinkFL( Component ): def construct( s, Type, msgs, initial_delay=0, interval_delay=0, + interval_delay_mode='fixed', arrival_time=None, cmp_fn=lambda a, b : a == b ): # Interface @@ -103,7 +106,10 @@ def up_sink(): ) s.idx += 1 - s.count = interval_delay + if ( interval_delay_mode == 'random' ): + s.count = randint(0,interval_delay) + else: + s.count = interval_delay if s.count > 0: s.count -= 1 diff --git a/pymtl3/stdlib/stream/StreamSourceFL.py b/pymtl3/stdlib/stream/StreamSourceFL.py index ac7932829..01be1b387 100644 --- a/pymtl3/stdlib/stream/StreamSourceFL.py +++ b/pymtl3/stdlib/stream/StreamSourceFL.py @@ -9,6 +9,7 @@ """ from collections import deque from copy import deepcopy +from random import randint from pymtl3 import * from .ifcs import OStreamIfc @@ -16,7 +17,8 @@ class StreamSourceFL( Component ): - def construct( s, Type, msgs, initial_delay=0, interval_delay=0 ): + def construct( s, Type, msgs, initial_delay=0, interval_delay=0, + interval_delay_mode='fixed' ): # Interface @@ -41,7 +43,10 @@ def up_src(): else: if (s.ostream.val & s.ostream.rdy) or s.prev_is_none: s.idx += 1 - s.count = interval_delay + if ( interval_delay_mode == 'random' ): + s.count = randint(0,interval_delay) + else: + s.count = interval_delay if s.count > 0: s.count -= 1 diff --git a/pymtl3/stdlib/stream/test/src_sink_test.py b/pymtl3/stdlib/stream/test/src_sink_test.py index eb54a84f6..e1679693b 100644 --- a/pymtl3/stdlib/stream/test/src_sink_test.py +++ b/pymtl3/stdlib/stream/test/src_sink_test.py @@ -52,27 +52,34 @@ def line_trace( s ): @pytest.mark.parametrize( - ('Type', 'msgs', 'src_init', 'src_intv', - 'sink_init', 'sink_intv', 'arrival_time' ), + ('Type', 'msgs', 'src_init', 'src_intv', 'src_mode', + 'sink_init', 'sink_intv', 'sink_mode', 'arrival_time' ), [ - ( Bits16, bit_msgs, 0, 0, 0, 0, arrival0 ), - # ( int, int_msgs, 10, 0, 0, 0, arrival1 ), - ( Bits16, bit_msgs, 10, 1, 0, 0, arrival2 ), - ( Bits16, bit_msgs, 10, 0, 0, 1, arrival3 ), - ( Bits16, bit_msgs, 3, 4, 5, 3, arrival4 ) + ( Bits16, bit_msgs, 0, 0, 'fixed', 0, 0, 'fixed', arrival0 ), + ( Bits16, bit_msgs, 10, 1, 'fixed', 0, 0, 'fixed', arrival2 ), + ( Bits16, bit_msgs, 10, 0, 'fixed', 0, 1, 'fixed', arrival3 ), + ( Bits16, bit_msgs, 3, 4, 'fixed', 5, 3, 'fixed', arrival4 ), + ( Bits16, bit_msgs, 0, 10, 'random', 0, 0, 'fixed', None ), + ( Bits16, bit_msgs, 0, 40, 'random', 0, 0, 'fixed', None ), + ( Bits16, bit_msgs, 0, 0, 'fixed', 0, 10, 'random', None ), + ( Bits16, bit_msgs, 0, 0, 'fixed', 0, 40, 'random', None ), + ( Bits16, bit_msgs, 0, 10, 'random', 0, 10, 'random', None ), + ( Bits16, bit_msgs, 0, 40, 'random', 0, 40, 'random', None ), ] ) -def test_src_sink_rtl( Type, msgs, src_init, src_intv, - sink_init, sink_intv, arrival_time ): +def test_src_sink_rtl( Type, msgs, src_init, src_intv, src_mode, + sink_init, sink_intv, sink_mode, arrival_time ): th = TestHarnessSimple( Type, StreamSourceFL, StreamSinkFL, msgs, msgs ) th.set_param( "top.src.construct", - initial_delay = src_init, - interval_delay = src_intv, + initial_delay = src_init, + interval_delay = src_intv, + interval_delay_mode = src_mode, ) th.set_param( "top.sink.construct", - initial_delay = sink_init, - interval_delay = sink_intv, - arrival_time = arrival_time, + initial_delay = sink_init, + interval_delay = sink_intv, + interval_delay_mode = sink_mode, + arrival_time = arrival_time, ) run_sim( th ) From a70db046b9df12f78f7c2f24bf6955c6911d154a Mon Sep 17 00:00:00 2001 From: pp482 Date: Tue, 25 Oct 2022 22:00:42 -0400 Subject: [PATCH 015/101] [dev] Add py module to dependency --- requirements/release.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements/release.txt b/requirements/release.txt index 7b35e17f6..bfb3044d1 100644 --- a/requirements/release.txt +++ b/requirements/release.txt @@ -4,3 +4,4 @@ pytest hypothesis cffi greenlet +py From 6db6ceeffa3676a81495086b090fd4b4046599d8 Mon Sep 17 00:00:00 2001 From: cb535 Date: Sun, 27 Nov 2022 14:58:58 -0500 Subject: [PATCH 016/101] add ordered option to stream sink --- pymtl3/stdlib/stream/StreamSinkFL.py | 56 +++++++++++++++------- pymtl3/stdlib/stream/test/src_sink_test.py | 27 +++++++++++ 2 files changed, 67 insertions(+), 16 deletions(-) diff --git a/pymtl3/stdlib/stream/StreamSinkFL.py b/pymtl3/stdlib/stream/StreamSinkFL.py index 09fb7e9ba..ceb69f439 100644 --- a/pymtl3/stdlib/stream/StreamSinkFL.py +++ b/pymtl3/stdlib/stream/StreamSinkFL.py @@ -24,7 +24,8 @@ class StreamSinkFL( Component ): def construct( s, Type, msgs, initial_delay=0, interval_delay=0, interval_delay_mode='fixed', - arrival_time=None, cmp_fn=lambda a, b : a == b ): + arrival_time=None, cmp_fn=lambda a, b : a == b, + ordered=True ): # Interface @@ -87,23 +88,46 @@ def up_sink(): f'Received : {msg}' ) else: - # Check correctness first - if not cmp_fn( msg, s.msgs[ s.idx ] ): - s.error_msg = ( - f'Test sink {s} received WRONG message!\n' - f'Expected : { s.msgs[ s.idx ] }\n' - f'Received : { msg }' - ) + + # Check correctness assuming sink messages are ordered + + if ordered: + + if not cmp_fn( msg, s.msgs[ s.idx ] ): + s.error_msg = ( + f'Test sink {s} received WRONG message!\n' + f'Expected : { s.msgs[ s.idx ] }\n' + f'Received : { msg }' + ) + + # Check correctness assuming sink messages are unordered + + else: + + found = False + for ref_msg in s.msgs: + if cmp_fn( msg, ref_msg ): + found = True + break + + if not found: + s.error_msg = ( + f'Test sink {s} received WRONG message!' + f'Received : { msg }\n' + f'Sink is in not checking message order, so the received\n' + f'message was compared against all expected messages' + ) # Check timing if performance regeression is turned on - elif s.arrival_time and s.cycle_count > s.arrival_time[ s.idx ]: - s.error_msg = ( - f'Test sink {s} received message LATER than expected!\n' - f'Expected msg : {s.msgs[ s.idx ]}\n' - f'Expected at : {s.arrival_time[ s.idx ]}\n' - f'Received msg : {msg}\n' - f'Received at : {s.cycle_count}' - ) + if not s.error_msg: + if s.arrival_time and s.cycle_count > s.arrival_time[ s.idx ]: + s.error_msg = ( + f'Test sink {s} received message LATER than expected!\n' + f'Expected msg : {s.msgs[ s.idx ]}\n' + f'Expected at : {s.arrival_time[ s.idx ]}\n' + f'Received msg : {msg}\n' + f'Received at : {s.cycle_count}' + ) s.idx += 1 if ( interval_delay_mode == 'random' ): diff --git a/pymtl3/stdlib/stream/test/src_sink_test.py b/pymtl3/stdlib/stream/test/src_sink_test.py index e1679693b..cbf45022e 100644 --- a/pymtl3/stdlib/stream/test/src_sink_test.py +++ b/pymtl3/stdlib/stream/test/src_sink_test.py @@ -137,3 +137,30 @@ def test_customized_cmp(): ) th.set_param( 'top.sink.construct', cmp_fn=lambda a, b: a[0:2] == b[0:2] ) run_sim( th ) + +#------------------------------------------------------------------------- +# Test unordered sink +#------------------------------------------------------------------------- + +def test_unordered_sink(): + th = TestHarnessSimple( + Bits4, StreamSourceFL, StreamSinkFL, + src_msgs = [ b4(4), b4(3), b4(2), b4(1) ], + sink_msgs = [ b4(1), b4(2), b4(3), b4(4) ], + ) + th.set_param( 'top.sink.construct', ordered=False ) + run_sim( th ) + +def test_error_unordered_sink(): + try: + th = TestHarnessSimple( + Bits4, StreamSourceFL, StreamSinkFL, + src_msgs = [ b4(4), b4(3), b4(2), b4(1) ], + sink_msgs = [ b4(1), b4(0), b4(3), b4(4) ], + ) + th.set_param( 'top.sink.construct', ordered=False ) + run_sim( th ) + except PyMTLTestSinkError as e: + return + raise Exception( 'Fail to detect error!') + From 326559ee79c85a738b0a9c4e6eafcbfe00240a40 Mon Sep 17 00:00:00 2001 From: cb535 Date: Thu, 1 Dec 2022 15:41:17 -0500 Subject: [PATCH 017/101] temporary fix to change mem msg type field to 3bits --- pymtl3/stdlib/mem/MemMsg.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/pymtl3/stdlib/mem/MemMsg.py b/pymtl3/stdlib/mem/MemMsg.py index 93454f60b..eeab01f33 100644 --- a/pymtl3/stdlib/mem/MemMsg.py +++ b/pymtl3/stdlib/mem/MemMsg.py @@ -55,7 +55,14 @@ def mk_mem_req_msg( o, a, d ): @bitstruct class MemReqMsg: - type_ : Bits4 + + # Temporary Fix! Had to switch this back to 3-bit type field because + # the type field in the verilog memory message is only 3 bits. We + # should change the verilog memory message eventually. -cbatten + + # type_ : Bits4 + + type_ : Bits3 opaque : mk_bits( o ) addr : mk_bits( a ) len : mk_bits( clog2(d>>3) ) @@ -78,7 +85,14 @@ def mk_mem_resp_msg( o, d ): @bitstruct class MemRespMsg: - type_ : Bits4 + + # Temporary Fix! Had to switch this back to 3-bit type field because + # the type field in the verilog memory message is only 3 bits. We + # should change the verilog memory message eventually. -cbatten + + # type_ : Bits4 + + type_ : Bits3 opaque : mk_bits( o ) test : Bits2 len : mk_bits( clog2(d>>3) ) From 37882bceb9ce81b711b5e02b8c72464341a56334 Mon Sep 17 00:00:00 2001 From: pp482 Date: Mon, 27 Feb 2023 17:58:07 -0500 Subject: [PATCH 018/101] Fixing double/single underscore issue in translation --- .../structural/StructuralTranslatorL3.py | 24 ++++++---- .../structural/StructuralTranslatorL4.py | 47 ++++++++++++------- .../test/TestStructuralTranslator.py | 30 ++++++------ .../behavioral/VBehavioralTranslatorL1.py | 42 ++++++++++++++++- .../behavioral/VBehavioralTranslatorL3.py | 1 + .../behavioral/VBehavioralTranslatorL4.py | 24 +++++++++- .../behavioral/VBehavioralTranslatorL5.py | 7 ++- .../structural/VStructuralTranslatorL3.py | 38 +++++++++++---- .../structural/VStructuralTranslatorL4.py | 44 +++++++++-------- .../structural/YosysStructuralTranslatorL3.py | 10 ++-- .../structural/YosysStructuralTranslatorL4.py | 28 +++++------ .../structural/StructuralRTLIRSignalExpr.py | 34 +++++++++++--- 12 files changed, 226 insertions(+), 103 deletions(-) diff --git a/pymtl3/passes/backends/generic/structural/StructuralTranslatorL3.py b/pymtl3/passes/backends/generic/structural/StructuralTranslatorL3.py index 6b1859e8e..730e09c6a 100644 --- a/pymtl3/passes/backends/generic/structural/StructuralTranslatorL3.py +++ b/pymtl3/passes/backends/generic/structural/StructuralTranslatorL3.py @@ -70,12 +70,13 @@ def translate_decls( s, m ): ) ) ifc_decls.append( s.rtlir_tr_interface_decl( + m, ifc_id, ifc_rtype, s.rtlir_tr_unpacked_array_type( array_rtype ), - s.rtlir_tr_interface_port_decls( ports ) + s.rtlir_tr_interface_port_decls( m, ports ), ) ) - s.structural.decl_ifcs[m] = s.rtlir_tr_interface_decls( ifc_decls ) + s.structural.decl_ifcs[m] = s.rtlir_tr_interface_decls( m, ifc_decls ) super().translate_decls( m ) @@ -90,11 +91,16 @@ def rtlir_signal_expr_translation( s, expr, m, status = 'intermediate' ): Add support for the following operations at L3: sexp.InterfaceAttr """ if isinstance( expr, sexp.InterfaceAttr ): - return s.rtlir_tr_interface_attr( + # expr.get_base() is an interface. `parent' is the component object that + # owns this interface. + parent = expr.get_base() + while not isinstance(parent, (sexp.CurCompAttr, sexp.SubCompAttr)): + parent = parent.get_base() + return s.rtlir_tr_interface_attr( parent.get_base().get_object(), s.rtlir_signal_expr_translation(expr.get_base(), m), expr.get_attr(), status) elif isinstance( expr, sexp.InterfaceViewIndex ): - return s.rtlir_tr_interface_array_index( + return s.rtlir_tr_interface_array_index( m, s.rtlir_signal_expr_translation(expr.get_base(), m), expr.get_index(), status) else: @@ -105,21 +111,21 @@ def rtlir_signal_expr_translation( s, expr, m, status = 'intermediate' ): #----------------------------------------------------------------------- # Declarations - def rtlir_tr_interface_port_decls( s, port_decls ): + def rtlir_tr_interface_port_decls( s, m, port_decls ): raise NotImplementedError() def rtlir_tr_interface_port_decl( s, m, port_id, port_rtype, port_array_type ): raise NotImplementedError() - def rtlir_tr_interface_decls( s, ifc_decls ): + def rtlir_tr_interface_decls( s, m, ifc_decls ): raise NotImplementedError() - def rtlir_tr_interface_decl( s, ifc_id, ifc_rtype, array_type, port_decls ): + def rtlir_tr_interface_decl( s, m, ifc_id, ifc_rtype, array_type, port_decls ): raise NotImplementedError() # Signal operations - def rtlir_tr_interface_array_index( s, base_signal, index, status ): + def rtlir_tr_interface_array_index( s, m, base_signal, index, status ): raise NotImplementedError() - def rtlir_tr_interface_attr( s, base_signal, attr, status ): + def rtlir_tr_interface_attr( s, m, base_signal, attr, status ): raise NotImplementedError() diff --git a/pymtl3/passes/backends/generic/structural/StructuralTranslatorL4.py b/pymtl3/passes/backends/generic/structural/StructuralTranslatorL4.py index 55839d69e..586c2c8ba 100644 --- a/pymtl3/passes/backends/generic/structural/StructuralTranslatorL4.py +++ b/pymtl3/passes/backends/generic/structural/StructuralTranslatorL4.py @@ -5,6 +5,8 @@ # Date : Apr 4, 2019 """Provide L4 structural translator.""" +from pymtl3.dsl.Component import Component + from pymtl3.passes.rtlir import RTLIRType as rt from pymtl3.passes.rtlir import StructuralRTLIRSignalExpr as sexp from pymtl3.passes.rtlir.structural.StructuralRTLIRGenL4Pass import ( @@ -54,9 +56,14 @@ def translate_decls( s, m ): if isinstance( _c_rtype, rt.Array ): c_array_rtype = _c_rtype c_rtype = _c_rtype.get_sub_type() + c = getattr(m, c_id) + while isinstance(c, list): + assert len(c) != 0 + c = c[0] else: c_array_rtype = None c_rtype = _c_rtype + c = getattr(m, c_id) # Translate ports of the subcomponent port_conns, ifc_conns = [], [] @@ -69,7 +76,7 @@ def translate_decls( s, m ): port_rtype = _port_rtype port_conns.append( s.rtlir_tr_subcomp_port_decl( - m, + m, c, c_id, c_rtype, s.rtlir_tr_unpacked_array_type( c_array_rtype ), port_id, port_rtype, s.rtlir_data_type_translation( m, port_rtype.get_dtype() ), @@ -97,7 +104,7 @@ def translate_decls( s, m ): # Translate a single port of the current interface ports.append( s.rtlir_tr_subcomp_ifc_port_decl( - m, + m, c, '{c_id}', c_rtype, s.rtlir_tr_unpacked_array_type( c_array_rtype ), ifc_port_id, ifc_port_rtype, s.rtlir_tr_unpacked_array_type( ifc_port_array_rtype ), @@ -107,21 +114,21 @@ def translate_decls( s, m ): # Assemble all ports of the current interface into a complete interface ifc_conns.append( s.rtlir_tr_subcomp_ifc_decl( - m, + m, c, '{c_id}', c_rtype, s.rtlir_tr_unpacked_array_type( c_array_rtype ), ifc_port_id, ifc_port_rtype, s.rtlir_tr_unpacked_array_type( ifc_port_array_rtype ), - s.rtlir_tr_subcomp_ifc_port_decls( ports ) + s.rtlir_tr_subcomp_ifc_port_decls( m, c, ports ) ) ) # Generate a list of ports and interfaces subcomp_decls.append( s.rtlir_tr_subcomp_decl( - m, + m, c, c_id, c_rtype, s.rtlir_tr_unpacked_array_type( c_array_rtype ), - s.rtlir_tr_subcomp_port_decls( port_conns ), - s.rtlir_tr_subcomp_ifc_decls( ifc_conns ) + s.rtlir_tr_subcomp_port_decls( m, c, port_conns ), + s.rtlir_tr_subcomp_ifc_decls( m, c, ifc_conns ) ) ) - s.structural.decl_subcomps[m] = s.rtlir_tr_subcomp_decls( subcomp_decls ) + s.structural.decl_subcomps[m] = s.rtlir_tr_subcomp_decls( m, subcomp_decls ) #----------------------------------------------------------------------- # rtlir_signal_expr_translation @@ -133,12 +140,16 @@ def rtlir_signal_expr_translation( s, expr, m, status = 'intermediate' ): Add support for the following operations at L4: sexp.SubCompAttr """ if isinstance( expr, sexp.SubCompAttr ): + c = expr.get_base().get_object() + assert isinstance(c, Component) return s.rtlir_tr_subcomp_attr( + m, c, s.rtlir_signal_expr_translation( expr.get_base(), m ), expr.get_attr(), status ) elif isinstance( expr, sexp.ComponentIndex ): return s.rtlir_tr_component_array_index( + m, s.rtlir_signal_expr_translation( expr.get_base(), m ), expr.get_index(), status ) @@ -151,37 +162,37 @@ def rtlir_signal_expr_translation( s, expr, m, status = 'intermediate' ): #----------------------------------------------------------------------- # Declarations - def rtlir_tr_subcomp_port_decls( s, port_decls ): + def rtlir_tr_subcomp_port_decls( s, m, c, port_decls ): raise NotImplementedError() - def rtlir_tr_subcomp_port_decl( s, m, c_id, c_rtype, c_array_type, port_id, port_rtype, + def rtlir_tr_subcomp_port_decl( s, m, c, c_id, c_rtype, c_array_type, port_id, port_rtype, port_dtype, port_array_type ): raise NotImplementedError() - def rtlir_tr_subcomp_ifc_decls( s, ifc_decls ): + def rtlir_tr_subcomp_ifc_decls( s, m, c, ifc_decls ): raise NotImplementedError() - def rtlir_tr_subcomp_ifc_decl( s, m, c_id, c_rtype, c_array_type, ifc_port_id, + def rtlir_tr_subcomp_ifc_decl( s, m, c, c_id, c_rtype, c_array_type, ifc_port_id, ifc_port_rtype, ifc_port_array_type, ports ): raise NotImplementedError() - def rtlir_tr_subcomp_ifc_port_decls( s, ifc_port_decls ): + def rtlir_tr_subcomp_ifc_port_decls( s, m, c, ifc_port_decls ): raise NotImplementedError() - def rtlir_tr_subcomp_ifc_port_decl( s, m, c_id, c_rtype, c_array_type, + def rtlir_tr_subcomp_ifc_port_decl( s, m, c, c_id, c_rtype, c_array_type, ifc_id, ifc_rtype, ifc_array_rtype, port_id, port_rtype, port_array_type ): raise NotImplementedError() - def rtlir_tr_subcomp_decls( s, subcomps ): + def rtlir_tr_subcomp_decls( s, m, subcomps ): raise NotImplementedError() - def rtlir_tr_subcomp_decl( s, m, c_id, c_rtype, c_array_type, port_conns, ifc_conns ): + def rtlir_tr_subcomp_decl( s, m, c, c_id, c_rtype, c_array_type, port_conns, ifc_conns ): raise NotImplementedError() # Signal operations - def rtlir_tr_component_array_index( s, base_signal, index, status ): + def rtlir_tr_component_array_index( s, m, base_signal, index, status ): raise NotImplementedError() - def rtlir_tr_subcomp_attr( s, base_signal, attr, status ): + def rtlir_tr_subcomp_attr( s, m, c, base_signal, attr, status ): raise NotImplementedError() diff --git a/pymtl3/passes/backends/generic/structural/test/TestStructuralTranslator.py b/pymtl3/passes/backends/generic/structural/test/TestStructuralTranslator.py index c6248b8da..ea584a7a7 100644 --- a/pymtl3/passes/backends/generic/structural/test/TestStructuralTranslator.py +++ b/pymtl3/passes/backends/generic/structural/test/TestStructuralTranslator.py @@ -70,7 +70,7 @@ def rtlir_tr_const_decl( s, id_, Type, array_type, dtype, value ): array_type = repr(Type) if not array_type else array_type return [f'const_decl: {id_} {array_type}'] - def rtlir_tr_interface_port_decls( s, port_decls ): + def rtlir_tr_interface_port_decls( s, m, port_decls ): decls = [['interface_ports:']] for decl in port_decls: make_indent( decl, 1 ) @@ -81,7 +81,7 @@ def rtlir_tr_interface_port_decl( s, m, id_, rtype, array_type ): rtype = repr(rtype) if not array_type else array_type return [f'interface_port: {id_} {rtype}'] - def rtlir_tr_interface_decls( s, ifc_decls ): + def rtlir_tr_interface_decls( s, m, ifc_decls ): decls = '' for decl in ifc_decls: if decl: @@ -89,7 +89,7 @@ def rtlir_tr_interface_decls( s, ifc_decls ): decls += '\n' + '\n'.join( decl ) return f'interface_decls:{decls}\n' - def rtlir_tr_interface_decl( s, ifc_id, ifc_rtype, array_type, port_decls ): + def rtlir_tr_interface_decl( s, m, ifc_id, ifc_rtype, array_type, port_decls ): ifc_rtype = str(ifc_rtype) if not array_type else array_type ret = [f'interface_decl: {ifc_id} {ifc_rtype}'] for decl in port_decls: @@ -97,7 +97,7 @@ def rtlir_tr_interface_decl( s, ifc_id, ifc_rtype, array_type, port_decls ): ret.append( decl[0] ) return ret - def rtlir_tr_subcomp_port_decls( s, port_decls ): + def rtlir_tr_subcomp_port_decls( s, m, c, port_decls ): decls = [['component_ports:']] for decl in port_decls: if decl: @@ -105,7 +105,7 @@ def rtlir_tr_subcomp_port_decls( s, port_decls ): decls.append( [ decl[0] ] ) return decls - def rtlir_tr_subcomp_port_decl( s, m, c_id, c_rtype, c_array_type, port_id, + def rtlir_tr_subcomp_port_decl( s, m, c, c_id, c_rtype, c_array_type, port_id, port_rtype, port_dtype, array_type ): if port_id not in ["clk", "reset"]: port_rtype = repr(port_rtype) if not array_type else array_type @@ -113,7 +113,7 @@ def rtlir_tr_subcomp_port_decl( s, m, c_id, c_rtype, c_array_type, port_id, else: return "" - def rtlir_tr_subcomp_ifc_port_decls( s, ifc_port_decls ): + def rtlir_tr_subcomp_ifc_port_decls( s, m, c, ifc_port_decls ): decls = [['component_ifc_ports:']] for decl in ifc_port_decls: if decl: @@ -121,13 +121,13 @@ def rtlir_tr_subcomp_ifc_port_decls( s, ifc_port_decls ): decls.append( [ decl[0] ] ) return decls - def rtlir_tr_subcomp_ifc_port_decl( s, m, c_id, c_rtype, c_array_type, + def rtlir_tr_subcomp_ifc_port_decl( s, m, c, c_id, c_rtype, c_array_type, ifc_id, ifc_rtype, ifc_array_type, port_id, port_rtype, port_array_type ): port_rtype = repr(port_rtype) if not port_array_type else port_array_type return [f'component_ifc_port: {port_id} {port_rtype}'] - def rtlir_tr_subcomp_ifc_decls( s, ifc_decls ): + def rtlir_tr_subcomp_ifc_decls( s, m, c, ifc_decls ): decls = [['component_ifcs:']] for ifc_decl in ifc_decls: for decl in ifc_decl: @@ -136,7 +136,7 @@ def rtlir_tr_subcomp_ifc_decls( s, ifc_decls ): decls.append( [ decl[0] ] ) return decls - def rtlir_tr_subcomp_ifc_decl( s, m, c_id, c_rtype, c_array_type, ifc_id, + def rtlir_tr_subcomp_ifc_decl( s, m, c, c_id, c_rtype, c_array_type, ifc_id, ifc_rtype, ifc_array_type, ports ): ifc_rtype = repr(ifc_rtype) if not ifc_array_type else ifc_array_type decls = [[f'component_ifc: {ifc_id} {ifc_rtype}']] @@ -146,14 +146,14 @@ def rtlir_tr_subcomp_ifc_decl( s, m, c_id, c_rtype, c_array_type, ifc_id, decls.append( [ decl[0] ] ) return decls - def rtlir_tr_subcomp_decls( s, subcomps ): + def rtlir_tr_subcomp_decls( s, m, subcomps ): decls = '' for decl in subcomps: make_indent( decl, 1 ) decls += '\n' + '\n'.join( decl ) return f'component_decls:{decls}\n' - def rtlir_tr_subcomp_decl( s, m, c_id, c_rtype, c_array_type, port_conns, ifc_conns ): + def rtlir_tr_subcomp_decl( s, m, c, c_id, c_rtype, c_array_type, port_conns, ifc_conns ): c_rtype = str(c_rtype) if not c_array_type else c_array_type ret = [f'component_decl: {c_id} {c_rtype}'] for port in port_conns: @@ -194,19 +194,19 @@ def rtlir_tr_const_array_index( s, base_signal, index, status ): def rtlir_tr_packed_index( s, base_signal, index, status ): return f'PackedIndex {base_signal} {index}' - def rtlir_tr_interface_array_index( s, base_signal, index, status ): + def rtlir_tr_interface_array_index( s, m, base_signal, index, status ): return f'IfcArrayIdx {base_signal} {index}' - def rtlir_tr_component_array_index( s, base_signal, index, status ): + def rtlir_tr_component_array_index( s, m, base_signal, index, status ): return f'CompArrayIdx {base_signal} {index}' def rtlir_tr_struct_attr( s, base_signal, attr, status ): return f'StructAttr {base_signal} {attr}' - def rtlir_tr_interface_attr( s, base_signal, attr, status ): + def rtlir_tr_interface_attr( s, m, base_signal, attr, status ): return f'IfcAttr {base_signal} {attr}' - def rtlir_tr_subcomp_attr( s, base_signal, attr, status ): + def rtlir_tr_subcomp_attr( s, m, c, base_signal, attr, status ): return f'SubCompAttr {base_signal} {attr}' def rtlir_tr_current_comp_attr( s, base_signal, attr, status ): diff --git a/pymtl3/passes/backends/verilog/translation/behavioral/VBehavioralTranslatorL1.py b/pymtl3/passes/backends/verilog/translation/behavioral/VBehavioralTranslatorL1.py index 7ca2ca51c..9f08123f9 100644 --- a/pymtl3/passes/backends/verilog/translation/behavioral/VBehavioralTranslatorL1.py +++ b/pymtl3/passes/backends/verilog/translation/behavioral/VBehavioralTranslatorL1.py @@ -8,6 +8,7 @@ from collections import deque from contextlib import contextmanager +from pymtl3.dsl.Component import Component from pymtl3.datatypes import Bits, Bits32 from pymtl3.passes.backends.generic.behavioral.BehavioralTranslatorL1 import ( BehavioralTranslatorL1, @@ -118,6 +119,8 @@ def visit( s, node, *args ): # Customized epilogue processing method = 'visit_' + node.__class__.__name__ visitor = getattr( s, method, s.generic_visit ) + node._owning_component = None + node._obj = None ret = visitor( node, *args ) return s.process_epilogue( node, ret ) @@ -237,7 +240,11 @@ def visit_SeqUpblk( s, node ): def visit_Assign( s, node ): for target in node.targets: target._top_expr = True + target._owning_component = None + target._obj = None node.value._top_expr = True + node.value._owning_component = None + node.value._obj = None with s.register_assign_LHS(): targets = [ s.visit( target ) for target in node.targets ] @@ -471,6 +478,7 @@ def visit_Attribute( s, node ): value = s.visit( node.value ) s.check_res( node, attr ) + s._update_node_attr( node ) if isinstance( node.value, bir.Base ): # The base of this attribute node is the component 's'. @@ -496,7 +504,8 @@ def visit_Index( s, node ): idx = s.visit( node.idx ) value = s.visit( node.value ) - Type = node.value.Type + Type = node.value.Type + s._update_node_index( node ) # Unpacked index if isinstance( Type, rt.Array ): @@ -576,6 +585,8 @@ def visit_Slice( s, node ): #----------------------------------------------------------------------- def visit_Base( s, node ): + node._owning_component = node.base + node._obj = node.base return str( node.base ) #----------------------------------------------------------------------- @@ -606,3 +617,32 @@ def visit_TmpVar( s, node ): def visit_LoopVarDecl( s, node ): raise VerilogTranslationError( s.blk, node, "LoopVarDecl not supported at L1" ) + + #----------------------------------------------------------------------- + # Helpers + #----------------------------------------------------------------------- + + def _update_node_attr( s, node ): + # Update _owning_component and _obj. + if hasattr( node.value._obj, node.attr ): + node._obj = getattr(node.value._obj, node.attr) + if isinstance(node._obj, Component): + node._owning_component = node._obj + else: + node._owning_component = node.value._owning_component + else: + node._obj = None + node._owning_component = node.value._owning_component + + def _update_node_index( s, node ): + # Update _owning_component and _obj. + if isinstance(node.value._obj, list) and len(node.value._obj) > 0: + node._obj = node.value._obj[0] + if isinstance(node._obj, Component): + node._owning_component = node._obj + else: + node._owning_component = node.value._owning_component + else: + node._obj = None + node._owning_component = node.value._owning_component + diff --git a/pymtl3/passes/backends/verilog/translation/behavioral/VBehavioralTranslatorL3.py b/pymtl3/passes/backends/verilog/translation/behavioral/VBehavioralTranslatorL3.py index be07edade..5d3f66771 100644 --- a/pymtl3/passes/backends/verilog/translation/behavioral/VBehavioralTranslatorL3.py +++ b/pymtl3/passes/backends/verilog/translation/behavioral/VBehavioralTranslatorL3.py @@ -73,6 +73,7 @@ def visit_Attribute( s, node ): value = s.visit( node.value ) attr = node.attr s.check_res( node, attr ) + s._update_node_attr( node ) dtype = node.Type.get_dtype() if isinstance(node.Type, rt.Const) and isinstance(dtype, rdt.Vector): return f"{dtype.get_length()}'( {value}.{attr} )" diff --git a/pymtl3/passes/backends/verilog/translation/behavioral/VBehavioralTranslatorL4.py b/pymtl3/passes/backends/verilog/translation/behavioral/VBehavioralTranslatorL4.py index 820c48b45..e9b168d69 100644 --- a/pymtl3/passes/backends/verilog/translation/behavioral/VBehavioralTranslatorL4.py +++ b/pymtl3/passes/backends/verilog/translation/behavioral/VBehavioralTranslatorL4.py @@ -10,6 +10,9 @@ ) from pymtl3.passes.rtlir import RTLIRType as rt +from pymtl3.passes.backends.verilog.VerilogPlaceholder import VerilogPlaceholder +from pymtl3.passes.backends.verilog.VerilogPlaceholderPass import VerilogPlaceholderPass + from ...errors import VerilogTranslationError from .VBehavioralTranslatorL3 import ( BehavioralRTLIRToVVisitorL3, @@ -45,9 +48,11 @@ def visit_Attribute( s, node ): value = s.visit( node.value ) attr = node.attr s.check_res( node, attr ) + s._update_node_attr( node ) + sep = s._get_separator_symbol( node.value._owning_component ) return s.process_unpacked_q( node, - f'{value}__{attr}', - f'{value}__{attr}{{}}' ) + f'{value}{sep}{attr}', + f'{value}{sep}{attr}{{}}' ) else: return super().visit_Attribute( node ) @@ -61,7 +66,22 @@ def visit_Index( s, node ): idx = s.visit( node.idx ) s._unpacked_q.appendleft(idx) value = s.visit( node.value ) + s._update_node_index( node ) return value else: return super().visit_Index( node ) + + #----------------------------------------------------------------------- + # Helpers + #----------------------------------------------------------------------- + + @staticmethod + def _get_separator_symbol( m ): + if isinstance( m, VerilogPlaceholder ): + # If the given component is a placeholder, we use whatever separator + # symbol the user provides through placeholder configuration. + ph_cfg = m.get_metadata( VerilogPlaceholderPass.placeholder_config ) + return ph_cfg.separator + # Otherwise we default to a double underscore. + return "__" diff --git a/pymtl3/passes/backends/verilog/translation/behavioral/VBehavioralTranslatorL5.py b/pymtl3/passes/backends/verilog/translation/behavioral/VBehavioralTranslatorL5.py index 51b78a5ef..0880986de 100644 --- a/pymtl3/passes/backends/verilog/translation/behavioral/VBehavioralTranslatorL5.py +++ b/pymtl3/passes/backends/verilog/translation/behavioral/VBehavioralTranslatorL5.py @@ -47,9 +47,11 @@ def visit_Attribute( s, node ): value = s.visit( node.value ) attr = node.attr s.check_res( node, attr ) + s._update_node_attr( node ) + sep = s._get_separator_symbol( node._owning_component ) return s.process_unpacked_q( node, - f'{value}__{attr}', - f'{value}__{attr}{{}}' ) + f'{value}{sep}{attr}', + f'{value}{sep}{attr}{{}}' ) return super().visit_Attribute( node ) @@ -63,6 +65,7 @@ def visit_Index( s, node ): idx = s.visit( node.idx ) s._unpacked_q.appendleft(idx) value = s.visit( node.value ) + s._update_node_index( node ) return value else: diff --git a/pymtl3/passes/backends/verilog/translation/structural/VStructuralTranslatorL3.py b/pymtl3/passes/backends/verilog/translation/structural/VStructuralTranslatorL3.py index e803e32df..f3c661097 100644 --- a/pymtl3/passes/backends/verilog/translation/structural/VStructuralTranslatorL3.py +++ b/pymtl3/passes/backends/verilog/translation/structural/VStructuralTranslatorL3.py @@ -9,6 +9,9 @@ from pymtl3.passes.rtlir import RTLIRDataType as rdt from pymtl3.passes.rtlir import RTLIRType as rt +from pymtl3.passes.backends.verilog.VerilogPlaceholder import VerilogPlaceholder +from pymtl3.passes.backends.verilog.VerilogPlaceholderPass import VerilogPlaceholderPass + from ...util.utility import make_indent, pretty_concat from .VStructuralTranslatorL2 import VStructuralTranslatorL2 @@ -20,7 +23,7 @@ class VStructuralTranslatorL3( # Declarations #----------------------------------------------------------------------- - def rtlir_tr_interface_port_decls( s, port_decls ): + def rtlir_tr_interface_port_decls( s, m, port_decls ): return sum( port_decls, [] ) def rtlir_tr_interface_port_decl( s, m, port_id, port_rtype, port_array_type ): @@ -46,6 +49,7 @@ def rtlir_tr_interface_port_decl( s, m, port_id, port_rtype, port_array_type ): } ] else: # Nested interface + sep = s._get_separator_symbol( m ) dscps = [] all_properties = port_rtype.get_all_properties_packed() for name, _rtype in all_properties: @@ -56,25 +60,26 @@ def rtlir_tr_interface_port_decl( s, m, port_id, port_rtype, port_array_type ): array_type = None rtype = _rtype # Name-mangle the nested interface - dscp = s.rtlir_tr_interface_port_decl(m, f'{port_id}__{name}', rtype, array_type) + dscp = s.rtlir_tr_interface_port_decl(m, f'{port_id}{sep}{name}', rtype, array_type) # Add unpacked_type of the current interface to the unpacked_type of the port for tr in dscp: tr['unpacked_type'] = port_array_type['unpacked_type'] + tr['unpacked_type'] dscps += dscp return dscps - def rtlir_tr_interface_decls( s, ifc_decls ): + def rtlir_tr_interface_decls( s, m, ifc_decls ): all_decls = sum( ifc_decls, [] ) make_indent( all_decls, 1 ) return ',\n'.join( all_decls ) - def rtlir_tr_interface_decl( s, ifc_id, ifc_rtype, array_type, port_decls ): + def rtlir_tr_interface_decl( s, m, ifc_id, ifc_rtype, array_type, port_decls ): decls = [] + sep = s._get_separator_symbol( m ) for tr in port_decls: direc = tr['direction'] data_type = tr['data_type'] packed_type = tr['packed_type'] - id = f"{ifc_id}__{tr['id']}" + id = f"{ifc_id}{sep}{tr['id']}" unpacked_type = array_type['unpacked_type'] + tr['unpacked_type'] decls.append(pretty_concat(direc, data_type, packed_type, id, unpacked_type)) return decls @@ -83,12 +88,27 @@ def rtlir_tr_interface_decl( s, ifc_id, ifc_rtype, array_type, port_decls ): # Signal operations #----------------------------------------------------------------------- - def rtlir_tr_interface_array_index( s, base_signal, index, status ): + def rtlir_tr_interface_array_index( s, m, base_signal, index, status ): s._rtlir_tr_unpacked_q.append( index ) return base_signal - def rtlir_tr_interface_attr( s, base_signal, attr, status ): + def rtlir_tr_interface_attr( s, m, base_signal, attr, status ): + sep = s._get_separator_symbol( m ) return s._rtlir_tr_process_unpacked( - f'{base_signal}__{attr}', - f'{base_signal}__{attr}{{}}', + f'{base_signal}{sep}{attr}', + f'{base_signal}{sep}{attr}{{}}', status, ('status') ) + + #----------------------------------------------------------------------- + # Helpers + #----------------------------------------------------------------------- + + @staticmethod + def _get_separator_symbol( m ): + if isinstance( m, VerilogPlaceholder ): + # If the given component is a placeholder, we use whatever separator + # symbol the user provides through placeholder configuration. + ph_cfg = m.get_metadata( VerilogPlaceholderPass.placeholder_config ) + return ph_cfg.separator + # Otherwise we default to a double underscore. + return "__" diff --git a/pymtl3/passes/backends/verilog/translation/structural/VStructuralTranslatorL4.py b/pymtl3/passes/backends/verilog/translation/structural/VStructuralTranslatorL4.py index 8e0813e53..d039888f7 100644 --- a/pymtl3/passes/backends/verilog/translation/structural/VStructuralTranslatorL4.py +++ b/pymtl3/passes/backends/verilog/translation/structural/VStructuralTranslatorL4.py @@ -25,10 +25,10 @@ class VStructuralTranslatorL4( # Declarations #----------------------------------------------------------------------- - def rtlir_tr_subcomp_port_decls( s, _port_decls ): + def rtlir_tr_subcomp_port_decls( s, m, c, _port_decls ): return _port_decls - def rtlir_tr_subcomp_port_decl( s, m, c_id, c_rtype, c_array_type, port_id, + def rtlir_tr_subcomp_port_decl( s, m, c, c_id, c_rtype, c_array_type, port_id, port_rtype, port_dtype, port_array_type ): obj = c_rtype.obj if obj.has_metadata(s._placeholder_pass.placeholder_config): @@ -50,19 +50,22 @@ def rtlir_tr_subcomp_port_decl( s, m, c_id, c_rtype, c_array_type, port_id, 'unpacked_type' : port_array_type['unpacked_type'], } - def rtlir_tr_subcomp_ifc_port_decls( s, _ifc_port_decls ): + def rtlir_tr_subcomp_ifc_port_decls( s, m, c, _ifc_port_decls ): return sum(_ifc_port_decls, []) - def rtlir_tr_subcomp_ifc_port_decl( s, m, c_id, c_rtype, c_array_type, + def rtlir_tr_subcomp_ifc_port_decl( s, m, c, c_id, c_rtype, c_array_type, ifc_id, ifc_rtype, ifc_array_type, port_id, port_rtype, port_array_type ): + sep = s._get_separator_symbol( c ) if isinstance( port_rtype, rt.Port ): obj = c_rtype.obj if obj.has_metadata(s._placeholder_pass.placeholder_config): pmap = obj.get_metadata(s._placeholder_pass.placeholder_config).get_port_map() else: pmap = lambda x: x - vname = f'{ifc_id}__{port_id}' - pyname = vname.replace('__', '.') + + vname = f'{ifc_id}{sep}{port_id}' + pyname = f'{ifc_id}.{port_id}' + # `pmap` is a function that maps the _full name_ of a port object to its # Verilog name. `pyname` is simply a name in the local scope. We need to # build up the full name by concatenating `str(obj)` and `pyname`. @@ -99,27 +102,29 @@ def rtlir_tr_subcomp_ifc_port_decl( s, m, c_id, c_rtype, c_array_type, 'n_dim' : ifc_array_type['n_dim'] + port_array_type['n_dim'], } - ret += s.rtlir_tr_subcomp_ifc_port_decl( m, + ret += s.rtlir_tr_subcomp_ifc_port_decl( m, c, # Component: nested interface does not change the component c_id, c_rtype, c_array_type, # Interface: nested interface appends its id and array_type - f'{ifc_id}__{port_id}', port_rtype, combined_ifc_array_type, + f'{ifc_id}{sep}{port_id}', port_rtype, combined_ifc_array_type, # Port: use the id, rtype, and array_type of the port _port_id, _port_rtype, s.rtlir_tr_unpacked_array_type(_port_array_rtype)) return ret - def rtlir_tr_subcomp_ifc_decls( s, _ifc_decls ): + def rtlir_tr_subcomp_ifc_decls( s, m, c, _ifc_decls ): return sum(_ifc_decls, []) - def rtlir_tr_subcomp_ifc_decl( s, m, c_id, c_rtype, c_array_type, + def rtlir_tr_subcomp_ifc_decl( s, m, c, c_id, c_rtype, c_array_type, ifc_id, ifc_rtype, ifc_array_type, ports ): return ports - def rtlir_tr_subcomp_decls( s, subcomps ): + def rtlir_tr_subcomp_decls( s, m, subcomps ): subcomp_decls = sum( subcomps, [] ) return '\n\n'.join( subcomp_decls ) - def rtlir_tr_subcomp_decl( s, m, c_id, c_rtype, c_array_type, port_conns, ifc_conns ): + def rtlir_tr_subcomp_decl( s, m, c, c_id, c_rtype, c_array_type, port_conns, ifc_conns ): + + sep = s._get_separator_symbol( c ) def pretty_comment( string ): comments = [ @@ -130,7 +135,7 @@ def pretty_comment( string ): return '\n'.join(comments) def gen_subcomp_array_decl( c_id, port_conns, ifc_conns, n_dim, c_n_dim ): - nonlocal m, s + nonlocal s, m, c, sep tplt = dedent( """\ {c_name} {c_id} @@ -191,7 +196,7 @@ def gen_subcomp_array_decl( c_id, port_conns, ifc_conns, n_dim, c_n_dim ): comma = ',\n' if i != len(conns)-1 else '' port_name = dscp['id'] ph_port_name = dscp['ph_id'] - port_wire = f"{orig_c_id}__{dscp['id']}{unpacked_str}" + port_wire = f"{orig_c_id}{sep}{dscp['id']}{unpacked_str}" if (port_name == 'clk' and no_clk) or (port_name == 'reset' and no_reset): comma = ',\n' if i != len(conns)-1 else '\n' newline = '\n' if i != len(conns)-1 else '' @@ -218,7 +223,7 @@ def gen_subcomp_array_decl( c_id, port_conns, ifc_conns, n_dim, c_n_dim ): for dscp in port_conns + ifc_conns: defs.append(pretty_concat(dscp['data_type'], dscp['packed_type'], - f"{c_id}__{dscp['id']}", f"{c_array_type['unpacked_type']}{dscp['unpacked_type']}", ';')) + f"{c_id}{sep}{dscp['id']}", f"{c_array_type['unpacked_type']}{dscp['unpacked_type']}", ';')) make_indent( defs, 1 ) defs = ['\n'.join(defs)] @@ -234,12 +239,13 @@ def gen_subcomp_array_decl( c_id, port_conns, ifc_conns, n_dim, c_n_dim ): # Signal operations #----------------------------------------------------------------------- - def rtlir_tr_component_array_index( s, base_signal, index, status ): + def rtlir_tr_component_array_index( s, m, base_signal, index, status ): s._rtlir_tr_unpacked_q.append( index ) return base_signal - def rtlir_tr_subcomp_attr( s, base_signal, attr, status ): + def rtlir_tr_subcomp_attr( s, m, c, base_signal, attr, status ): + sep = s._get_separator_symbol( c ) return s._rtlir_tr_process_unpacked( - f'{base_signal}__{attr}', - f'{base_signal}__{attr}{{}}', + f'{base_signal}{sep}{attr}', + f'{base_signal}{sep}{attr}{{}}', status, ('status') ) diff --git a/pymtl3/passes/backends/yosys/translation/structural/YosysStructuralTranslatorL3.py b/pymtl3/passes/backends/yosys/translation/structural/YosysStructuralTranslatorL3.py index 9155ffb8e..620a1eaf4 100644 --- a/pymtl3/passes/backends/yosys/translation/structural/YosysStructuralTranslatorL3.py +++ b/pymtl3/passes/backends/yosys/translation/structural/YosysStructuralTranslatorL3.py @@ -56,7 +56,7 @@ def ifc_conn_gen( s, d, cpid, _pid, cwid, _wid, idx, n_dim ): # Declarations #----------------------------------------------------------------------- - def rtlir_tr_interface_port_decls( s, _port_decls ): + def rtlir_tr_interface_port_decls( s, m, _port_decls ): port_decl_list = sum( _port_decls, [] ) port_decls, wire_decls, connections = [], [], [] for dct in port_decl_list: @@ -106,7 +106,7 @@ def _gen_ifc( id_, ifc, n_dim ): n_dim = port_array_type["n_dim"] return _gen_ifc( port_id, port_rtype, n_dim ) - def rtlir_tr_interface_decls( s, ifc_decls ): + def rtlir_tr_interface_decls( s, m, ifc_decls ): port_decls, wire_decls, connections = [], [], [] for ifc_decl in ifc_decls: port_decls += ifc_decl["port_decls"] @@ -121,7 +121,7 @@ def rtlir_tr_interface_decls( s, ifc_decls ): "connections" : "\n".join( connections ), } - def rtlir_tr_interface_decl( s, ifc_id, ifc_rtype, array_type, ports ): + def rtlir_tr_interface_decl( s, m, ifc_id, ifc_rtype, array_type, ports ): ifc_ndim = array_type["n_dim"] wire_template = "logic {packed_type: <8} {id_}{array_dim_str};" @@ -167,13 +167,13 @@ def rtlir_tr_interface_decl( s, ifc_id, ifc_rtype, array_type, ports ): # Signal operations #----------------------------------------------------------------------- - def rtlir_tr_interface_array_index( s, base_signal, index, status ): + def rtlir_tr_interface_array_index( s, m, base_signal, index, status ): # Interface array index s.deq[-1]['s_index'] += "[{}]" s.deq[-1]['index'].append( int(index) ) return f"{base_signal}[{index}]" - def rtlir_tr_interface_attr( s, base_signal, attr, status ): + def rtlir_tr_interface_attr( s, m, base_signal, attr, status ): # Interface attribute s.deq[-1]['s_attr'] += "__{}" s.deq[-1]['attr'].append( attr ) diff --git a/pymtl3/passes/backends/yosys/translation/structural/YosysStructuralTranslatorL4.py b/pymtl3/passes/backends/yosys/translation/structural/YosysStructuralTranslatorL4.py index 472bb17e3..8380d3b73 100644 --- a/pymtl3/passes/backends/yosys/translation/structural/YosysStructuralTranslatorL4.py +++ b/pymtl3/passes/backends/yosys/translation/structural/YosysStructuralTranslatorL4.py @@ -22,24 +22,24 @@ class YosysStructuralTranslatorL4( # Declarations #--------------------------------------------------------------------- - def rtlir_tr_subcomp_port_decls( s, port_decls ): - return s.rtlir_tr_interface_port_decls( port_decls ) + def rtlir_tr_subcomp_port_decls( s, m, c, port_decls ): + return s.rtlir_tr_interface_port_decls( m, port_decls ) - def rtlir_tr_subcomp_port_decl( s, m, c_id, c_rtype, c_array_type, port_id, + def rtlir_tr_subcomp_port_decl( s, m, c, c_id, c_rtype, c_array_type, port_id, port_rtype, port_dtype, port_array_type ): return s.rtlir_tr_interface_port_decl( m, port_id, port_rtype, port_array_type ) - def rtlir_tr_subcomp_ifc_port_decls( s, ifc_port_decls ): - return s.rtlir_tr_interface_port_decls( ifc_port_decls ) + def rtlir_tr_subcomp_ifc_port_decls( s, m, c, ifc_port_decls ): + return s.rtlir_tr_interface_port_decls( m, ifc_port_decls ) - def rtlir_tr_subcomp_ifc_port_decl( s, m, c_id, c_rtype, c_array_type, + def rtlir_tr_subcomp_ifc_port_decl( s, m, c, c_id, c_rtype, c_array_type, ifc_id, ifc_rtype, ifc_array_type, port_id, port_rtype, port_array_type ): return s.rtlir_tr_interface_port_decl( m, port_id, port_rtype, port_array_type ) - def rtlir_tr_subcomp_ifc_decls( s, ifc_decls ): + def rtlir_tr_subcomp_ifc_decls( s, m, c, ifc_decls ): port_decls, wire_decls, connections = [], [], [] for ifc_decl in ifc_decls: port_decls += ifc_decl["port_decls"] @@ -51,7 +51,7 @@ def rtlir_tr_subcomp_ifc_decls( s, ifc_decls ): "connections" : connections } - def rtlir_tr_subcomp_ifc_decl( s, m, c_id, c_rtype, c_array_type, + def rtlir_tr_subcomp_ifc_decl( s, m, c, c_id, c_rtype, c_array_type, ifc_id, ifc_rtype, ifc_array_type, ports ): def _subcomp_ifc_port_gen( d, msb, ifc_id, id_, n_dim ): @@ -118,7 +118,7 @@ def _subcomp_ifc_conn_gen( d, cpid, _pid, cwid, _wid, idx, n_dim ): "connections" : connections } - def rtlir_tr_subcomp_decls( s, subcomp_decls ): + def rtlir_tr_subcomp_decls( s, m, subcomp_decls ): port_decls, wire_decls, conns = [], [], [] for subcomp_decl in subcomp_decls: port_decls.extend( subcomp_decl["port_decls"] ) @@ -132,7 +132,7 @@ def rtlir_tr_subcomp_decls( s, subcomp_decls ): "connections" : "\n".join( conns ) } - def rtlir_tr_subcomp_decl( s, m, c_id, c_rtype, c_array_type, port_conns, ifc_conns ): + def rtlir_tr_subcomp_decl( s, m, c, c_id, c_rtype, c_array_type, port_conns, ifc_conns ): def _subcomp_port_gen( c_name, c_id, n_dim, port_decls ): p_wire_tplt = "logic {packed_type: <8} {id_};" @@ -226,10 +226,6 @@ def _subcomp_conn_gen( d, cpid, _pid, cwid, _wid, idx, n_dim ): else: c_name = s.rtlir_tr_component_unique_name( c_rtype ) - # c_name = s.rtlir_tr_component_unique_name( c_rtype ) - # if c_name == 'IntDivPRTL_noparam': - # import pdb;pdb.set_trace() - port_decls = _subcomp_port_gen( c_name, c_id, c_n_dim, _port_decls ) # Add sub-component info to wire declarations and generate declarations @@ -257,13 +253,13 @@ def _subcomp_conn_gen( d, cpid, _pid, cwid, _wid, idx, n_dim ): # Signal operations #----------------------------------------------------------------------- - def rtlir_tr_component_array_index( s, base_signal, index, status ): + def rtlir_tr_component_array_index( s, m, base_signal, index, status ): # Component array index s.deq[-1]['s_index'] += "[{}]" s.deq[-1]['index'].append( int(index) ) return f'{base_signal}[{index}]' - def rtlir_tr_subcomp_attr( s, base_signal, attr, status ): + def rtlir_tr_subcomp_attr( s, m, c, base_signal, attr, status ): # Sub-component attribute s.deq[-1]['s_attr'] += "__{}" s.deq[-1]['attr'].append( attr ) diff --git a/pymtl3/passes/rtlir/structural/StructuralRTLIRSignalExpr.py b/pymtl3/passes/rtlir/structural/StructuralRTLIRSignalExpr.py index a76654db7..14022d936 100644 --- a/pymtl3/passes/rtlir/structural/StructuralRTLIRSignalExpr.py +++ b/pymtl3/passes/rtlir/structural/StructuralRTLIRSignalExpr.py @@ -13,13 +13,17 @@ class BaseSignalExpr: """Base abstract class of RTLIR signal expressions.""" - def __init__( s, rtype ): + def __init__( s, rtype, obj = None ): assert isinstance( rtype, rt.BaseRTLIRType ), f"non-RTLIR type {rtype} encountered!" s.rtype = rtype + s.obj = obj def get_rtype( s ): return s.rtype + def get_object( s ): + return s.obj + def __eq__( s, other ): return type(s) is type(other) and s.rtype == other.rtype @@ -38,7 +42,15 @@ class _Index( BaseSignalExpr ): data type array or a bit selection. """ def __init__( s, index_base, index, rtype ): - super().__init__( rtype ) + try: + if isinstance(index, BaseSignalExpr): + idx = index.get_object() + else: + idx = int(index) + obj = index_base.get_object()[idx] + except: + obj = None + super().__init__( rtype, obj ) s.index = index s.base = index_base @@ -83,7 +95,7 @@ def __init__( s, slice_base, start, stop ): rtype = rt.Wire( rdt.Vector( stop-start ) ) else: assert False, f"unrecognized signal type {base_rtype} for slicing" - super().__init__( rtype ) + super().__init__( rtype, None ) s.base = slice_base s.slice = ( start, stop ) @@ -108,7 +120,11 @@ class _Attribute( BaseSignalExpr ): an interface, or a field in a struct signal. """ def __init__( s, attr_base, attr, rtype ): - super().__init__( rtype ) + try: + obj = getattr(attr_base.get_object(), attr) + except: + obj = None + super().__init__( rtype, obj ) s.attr = attr s.base = attr_base @@ -137,7 +153,7 @@ class ConstInstance( BaseSignalExpr ): an attribute of a component. """ def __init__( s, obj, value ): - super().__init__(rt.Const(rdt.get_rtlir_dtype( obj ))) + super().__init__(rt.Const(rdt.get_rtlir_dtype( obj )), obj) s.value = value def __eq__( s, other ): @@ -159,7 +175,8 @@ class CurComp( BaseSignalExpr ): is the same as the current component's name. """ def __init__( s, comp, comp_id ): - super().__init__(comp.get_metadata(StructuralRTLIRGenL0Pass.rtlir_type)) + super().__init__(comp.get_metadata(StructuralRTLIRGenL0Pass.rtlir_type), comp) + s.comp = comp s.comp_id = comp_id def __eq__( s, other ): @@ -169,6 +186,9 @@ def __eq__( s, other ): def __hash__( s ): return hash((type(s), s.rtype, s.comp_id)) + def get_component( s ): + return s.comp + def get_component_id( s ): return s.comp_id @@ -189,7 +209,7 @@ class ComponentIndex( _UnpackedIndex ): class PackedIndex( _Index ): """IR class for indexing on a signal of packed data type.""" - def __init__( s, index_base, index ): + def __init__( s, index_base, index, ): base_rtype = index_base.get_rtype() dtype = base_rtype.get_dtype() if isinstance( base_rtype, rt.Port ): From 0e8109776d2e0835acd05d9e1239b9fe14a12a15 Mon Sep 17 00:00:00 2001 From: pp482 Date: Wed, 1 Mar 2023 10:10:12 -0500 Subject: [PATCH 019/101] Add non-blocking stream FL adapters --- .../stdlib/stream/IStreamBlockingAdapterFL.py | 30 +++++++++++ pymtl3/stdlib/stream/IStreamDeqAdapterFL.py | 3 ++ .../stream/IStreamNonBlockingAdapterFL.py | 28 ++++++++++ .../stdlib/stream/OStreamBlockingAdapterFL.py | 39 ++++++++++++++ pymtl3/stdlib/stream/OStreamEnqAdapterFL.py | 3 ++ .../stream/OStreamNonBlockingAdapterFL.py | 39 ++++++++++++++ pymtl3/stdlib/stream/test/adapters_test.py | 51 +++++++++++++------ 7 files changed, 178 insertions(+), 15 deletions(-) create mode 100644 pymtl3/stdlib/stream/IStreamBlockingAdapterFL.py create mode 100644 pymtl3/stdlib/stream/IStreamNonBlockingAdapterFL.py create mode 100644 pymtl3/stdlib/stream/OStreamBlockingAdapterFL.py create mode 100644 pymtl3/stdlib/stream/OStreamNonBlockingAdapterFL.py diff --git a/pymtl3/stdlib/stream/IStreamBlockingAdapterFL.py b/pymtl3/stdlib/stream/IStreamBlockingAdapterFL.py new file mode 100644 index 000000000..4854c0c8a --- /dev/null +++ b/pymtl3/stdlib/stream/IStreamBlockingAdapterFL.py @@ -0,0 +1,30 @@ +import greenlet +from pymtl3 import * +from pymtl3.extra import clone_deepcopy +from .ifcs.ifcs import IStreamIfc + +class IStreamBlockingAdapterFL( Component ): + + @blocking + def deq( s ): + while s.entry is None: + greenlet.getcurrent().parent.switch(0) + ret = s.entry + s.entry = None + return ret + + def construct( s, Type ): + s.istream = IStreamIfc( Type ) + s.entry = None + + @update_once + def up_recv_rdy(): + s.istream.rdy @= (s.entry is None) + + @update_once + def up_recv_msg(): + if (s.entry is None) & s.istream.val: + s.entry = clone_deepcopy( s.istream.msg ) + + s.add_constraints( M( s.deq ) < U( up_recv_rdy ), # deq before recv in a cycle -- pipe behavior + U( up_recv_rdy ) < U( up_recv_msg ) ) diff --git a/pymtl3/stdlib/stream/IStreamDeqAdapterFL.py b/pymtl3/stdlib/stream/IStreamDeqAdapterFL.py index 7d43c4afb..2549e7956 100644 --- a/pymtl3/stdlib/stream/IStreamDeqAdapterFL.py +++ b/pymtl3/stdlib/stream/IStreamDeqAdapterFL.py @@ -3,6 +3,9 @@ from .ifcs.ifcs import IStreamIfc class IStreamDeqAdapterFL( Component ): + # TODO (PP): remove this file during the future clean up to ensure consistent + # naming across adapters. This adapter is now deprecated and we should use + # IStreamNonBlockingAdapterFL instead. @non_blocking( lambda s: s.entry is not None ) def deq( s ): diff --git a/pymtl3/stdlib/stream/IStreamNonBlockingAdapterFL.py b/pymtl3/stdlib/stream/IStreamNonBlockingAdapterFL.py new file mode 100644 index 000000000..a966fbae7 --- /dev/null +++ b/pymtl3/stdlib/stream/IStreamNonBlockingAdapterFL.py @@ -0,0 +1,28 @@ +from pymtl3 import * +from pymtl3.extra import clone_deepcopy +from .ifcs.ifcs import IStreamIfc + +class IStreamNonBlockingAdapterFL( Component ): + + @non_blocking( lambda s: s.entry is not None ) + def deq( s ): + ret = s.entry + s.entry = None + return ret + + def construct( s, Type ): + s.istream = IStreamIfc( Type ) + s.entry = None + + @update_once + def up_recv_rdy(): + s.istream.rdy @= (s.entry is None) + + @update_once + def up_recv_msg(): + if (s.entry is None) & s.istream.val: + s.entry = clone_deepcopy( s.istream.msg ) + + s.add_constraints( M( s.deq ) < U( up_recv_rdy ), # deq before recv in a cycle -- pipe behavior + M( s.deq.rdy ) < U( up_recv_rdy ), + U( up_recv_rdy ) < U( up_recv_msg ) ) diff --git a/pymtl3/stdlib/stream/OStreamBlockingAdapterFL.py b/pymtl3/stdlib/stream/OStreamBlockingAdapterFL.py new file mode 100644 index 000000000..f700b7b44 --- /dev/null +++ b/pymtl3/stdlib/stream/OStreamBlockingAdapterFL.py @@ -0,0 +1,39 @@ +import greenlet +from pymtl3 import * +from pymtl3.extra import clone_deepcopy +from .ifcs.ifcs import OStreamIfc + +class OStreamBlockingAdapterFL( Component ): + + @blocking + def enq( s, msg ): + while s.entry is not None: + greenlet.getcurrent().parent.switch(0) + + s.entry = clone_deepcopy(msg) + + def construct( s, Type ): + s.ostream = OStreamIfc( Type ) + s.entry = None + + s.sent = Wire() + + @update + def up_send(): + if s.entry is None: + s.ostream.val @= 0 + else: + s.ostream.val @= 1 + s.ostream.msg @= s.entry + + @update_ff + def up_sent(): + s.sent <<= s.ostream.val & s.ostream.rdy + + @update_once + def up_clear(): + if s.sent: # constraints reverse this + s.entry = None + + s.add_constraints( U( up_clear ) < M( s.enq ), + M( s.enq ) < U( up_send ) ) diff --git a/pymtl3/stdlib/stream/OStreamEnqAdapterFL.py b/pymtl3/stdlib/stream/OStreamEnqAdapterFL.py index 9a4f506fa..ffa224b29 100644 --- a/pymtl3/stdlib/stream/OStreamEnqAdapterFL.py +++ b/pymtl3/stdlib/stream/OStreamEnqAdapterFL.py @@ -3,6 +3,9 @@ from .ifcs.ifcs import OStreamIfc class OStreamEnqAdapterFL( Component ): + # TODO (PP): remove this file during the future clean up to ensure consistent + # naming across adapters. This adapter is now deprecated and we should use + # OStreamNonBlockingAdapterFL instead. @non_blocking( lambda s: s.entry is None ) def enq( s, msg ): diff --git a/pymtl3/stdlib/stream/OStreamNonBlockingAdapterFL.py b/pymtl3/stdlib/stream/OStreamNonBlockingAdapterFL.py new file mode 100644 index 000000000..19e26e9b2 --- /dev/null +++ b/pymtl3/stdlib/stream/OStreamNonBlockingAdapterFL.py @@ -0,0 +1,39 @@ +from pymtl3 import * +from pymtl3.extra import clone_deepcopy +from .ifcs.ifcs import OStreamIfc + +class OStreamNonBlockingAdapterFL( Component ): + + @non_blocking( lambda s: s.entry is None ) + def enq( s, msg ): + s.entry = clone_deepcopy( msg ) + + def construct( s, Type ): + s.ostream = OStreamIfc( Type ) + + s.entry = None + s.sent = Wire() + + @update_once + def up_send(): + if s.entry is None: + s.ostream.val @= 0 + else: + s.ostream.val @= 1 + s.ostream.msg @= s.entry + + @update_ff + def up_sent(): + s.sent <<= s.ostream.val & s.ostream.rdy + + @update_once + def up_clear(): + if s.sent: # constraints reverse this + s.entry = None + + s.add_constraints( + U( up_clear ) < M( s.enq ), + U( up_clear ) < M( s.enq.rdy ), + M( s.enq ) < U( up_send ), + M( s.enq.rdy ) < U( up_send ) + ) diff --git a/pymtl3/stdlib/stream/test/adapters_test.py b/pymtl3/stdlib/stream/test/adapters_test.py index 4cffa0164..e62f44b35 100644 --- a/pymtl3/stdlib/stream/test/adapters_test.py +++ b/pymtl3/stdlib/stream/test/adapters_test.py @@ -5,15 +5,17 @@ from pymtl3.stdlib.stream.ifcs import IStreamIfc, OStreamIfc from pymtl3.stdlib.test_utils import run_sim -from ..IStreamDeqAdapterFL import IStreamDeqAdapterFL -from ..OStreamEnqAdapterFL import OStreamEnqAdapterFL +from ..IStreamNonBlockingAdapterFL import IStreamNonBlockingAdapterFL +from ..OStreamNonBlockingAdapterFL import OStreamNonBlockingAdapterFL +from ..IStreamBlockingAdapterFL import IStreamBlockingAdapterFL +from ..OStreamBlockingAdapterFL import OStreamBlockingAdapterFL -class SimplePassthroughFL( Component ): +class SimpleNonBlockingPassthroughFL( Component ): def construct( s, Type ): s.istream = IStreamIfc( Type ) s.ostream = OStreamIfc( Type ) - s.ideq_adapter = IStreamDeqAdapterFL( Type ) - s.oenq_adapter = OStreamEnqAdapterFL( Type ) + s.ideq_adapter = IStreamNonBlockingAdapterFL( Type ) + s.oenq_adapter = OStreamNonBlockingAdapterFL( Type ) s.istream //= s.ideq_adapter.istream s.oenq_adapter.ostream //= s.ostream @@ -24,11 +26,26 @@ def up_passthrough(): msg = s.ideq_adapter.deq() s.oenq_adapter.enq( msg ) +class SimpleBlockingPassthroughFL( Component ): + def construct( s, Type ): + s.istream = IStreamIfc( Type ) + s.ostream = OStreamIfc( Type ) + s.ideq_adapter = IStreamBlockingAdapterFL( Type ) + s.oenq_adapter = OStreamBlockingAdapterFL( Type ) + + s.istream //= s.ideq_adapter.istream + s.oenq_adapter.ostream //= s.ostream + + @update_once + def up_passthrough(): + msg = s.ideq_adapter.deq() + s.oenq_adapter.enq( msg ) + class TestHarness( Component ): - def construct( s, Type, src_msgs, sink_msgs ): + def construct( s, DutClass, Type, src_msgs, sink_msgs ): s.src = StreamSourceFL( Type, src_msgs ) s.sink = StreamSinkFL( Type, sink_msgs ) - s.dut = SimplePassthroughFL( Type ) + s.dut = DutClass( Type ) s.src.ostream //= s.dut.istream s.dut.ostream //= s.sink.istream @@ -50,18 +67,22 @@ def line_trace( s ): arrival4 = [ 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65 ] @pytest.mark.parametrize( - ('Type', 'msgs', 'src_init', 'src_intv', + ('DutClass', 'Type', 'msgs', 'src_init', 'src_intv', 'sink_init', 'sink_intv' ), [ - ( Bits16, bit_msgs, 0, 0, 0, 0 ), - ( Bits16, bit_msgs, 10, 1, 0, 0 ), - ( Bits16, bit_msgs, 10, 0, 0, 1 ), - ( Bits16, bit_msgs, 3, 4, 5, 3 ) + ( SimpleNonBlockingPassthroughFL, Bits16, bit_msgs, 0, 0, 0, 0 ), + ( SimpleNonBlockingPassthroughFL, Bits16, bit_msgs, 10, 1, 0, 0 ), + ( SimpleNonBlockingPassthroughFL, Bits16, bit_msgs, 10, 0, 0, 1 ), + ( SimpleNonBlockingPassthroughFL, Bits16, bit_msgs, 3, 4, 5, 3 ), + ( SimpleBlockingPassthroughFL, Bits16, bit_msgs, 0, 0, 0, 0 ), + ( SimpleBlockingPassthroughFL, Bits16, bit_msgs, 10, 1, 0, 0 ), + ( SimpleBlockingPassthroughFL, Bits16, bit_msgs, 10, 0, 0, 1 ), + ( SimpleBlockingPassthroughFL, Bits16, bit_msgs, 3, 4, 5, 3 ), ] ) -def test_src_sink_fl_adapter( Type, msgs, src_init, src_intv, sink_init, - sink_intv ): - th = TestHarness( Type, msgs, msgs ) +def test_src_sink_fl_adapter( DutClass, Type, msgs, src_init, src_intv, + sink_init, sink_intv ): + th = TestHarness( DutClass, Type, msgs, msgs ) th.set_param( "top.src.construct", initial_delay = src_init, interval_delay = src_intv, From 0eab5e3c361623f9c686b9c7b45829ddd2bcfd57 Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 2 Mar 2023 18:15:06 -0500 Subject: [PATCH 020/101] Exposing stream-to-FL adapters in stdlib.stream --- pymtl3/stdlib/stream/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pymtl3/stdlib/stream/__init__.py b/pymtl3/stdlib/stream/__init__.py index 54791530b..dbdef5687 100644 --- a/pymtl3/stdlib/stream/__init__.py +++ b/pymtl3/stdlib/stream/__init__.py @@ -4,3 +4,7 @@ from .queues import StreamNormalQueue, StreamPipeQueue, StreamBypassQueue from .IStreamDeqAdapterFL import IStreamDeqAdapterFL from .OStreamEnqAdapterFL import OStreamEnqAdapterFL +from .IStreamNonBlockingAdapterFL import IStreamNonBlockingAdapterFL +from .OStreamNonBlockingAdapterFL import OStreamNonBlockingAdapterFL +from .IStreamBlockingAdapterFL import IStreamBlockingAdapterFL +from .OStreamBlockingAdapterFL import OStreamBlockingAdapterFL From 85300ff3c77b2ce6fd84be60bd5242c7edd4d68b Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 2 Mar 2023 18:20:00 -0500 Subject: [PATCH 021/101] Add line trace to stream-to-FL adapters --- pymtl3/stdlib/stream/IStreamBlockingAdapterFL.py | 3 +++ pymtl3/stdlib/stream/IStreamNonBlockingAdapterFL.py | 3 +++ pymtl3/stdlib/stream/OStreamBlockingAdapterFL.py | 3 +++ pymtl3/stdlib/stream/OStreamNonBlockingAdapterFL.py | 3 +++ pymtl3/stdlib/stream/test/adapters_test.py | 8 +++++++- 5 files changed, 19 insertions(+), 1 deletion(-) diff --git a/pymtl3/stdlib/stream/IStreamBlockingAdapterFL.py b/pymtl3/stdlib/stream/IStreamBlockingAdapterFL.py index 4854c0c8a..f565c58ae 100644 --- a/pymtl3/stdlib/stream/IStreamBlockingAdapterFL.py +++ b/pymtl3/stdlib/stream/IStreamBlockingAdapterFL.py @@ -28,3 +28,6 @@ def up_recv_msg(): s.add_constraints( M( s.deq ) < U( up_recv_rdy ), # deq before recv in a cycle -- pipe behavior U( up_recv_rdy ) < U( up_recv_msg ) ) + + def line_trace( s ): + return f"{s.istream}({s.entry})" diff --git a/pymtl3/stdlib/stream/IStreamNonBlockingAdapterFL.py b/pymtl3/stdlib/stream/IStreamNonBlockingAdapterFL.py index a966fbae7..5f7033b4a 100644 --- a/pymtl3/stdlib/stream/IStreamNonBlockingAdapterFL.py +++ b/pymtl3/stdlib/stream/IStreamNonBlockingAdapterFL.py @@ -26,3 +26,6 @@ def up_recv_msg(): s.add_constraints( M( s.deq ) < U( up_recv_rdy ), # deq before recv in a cycle -- pipe behavior M( s.deq.rdy ) < U( up_recv_rdy ), U( up_recv_rdy ) < U( up_recv_msg ) ) + + def line_trace( s ): + return f"{s.istream}({s.entry})" diff --git a/pymtl3/stdlib/stream/OStreamBlockingAdapterFL.py b/pymtl3/stdlib/stream/OStreamBlockingAdapterFL.py index f700b7b44..302586226 100644 --- a/pymtl3/stdlib/stream/OStreamBlockingAdapterFL.py +++ b/pymtl3/stdlib/stream/OStreamBlockingAdapterFL.py @@ -37,3 +37,6 @@ def up_clear(): s.add_constraints( U( up_clear ) < M( s.enq ), M( s.enq ) < U( up_send ) ) + + def line_trace( s ): + return f"{s.ostream}({s.entry})" diff --git a/pymtl3/stdlib/stream/OStreamNonBlockingAdapterFL.py b/pymtl3/stdlib/stream/OStreamNonBlockingAdapterFL.py index 19e26e9b2..8f2413867 100644 --- a/pymtl3/stdlib/stream/OStreamNonBlockingAdapterFL.py +++ b/pymtl3/stdlib/stream/OStreamNonBlockingAdapterFL.py @@ -37,3 +37,6 @@ def up_clear(): M( s.enq ) < U( up_send ), M( s.enq.rdy ) < U( up_send ) ) + + def line_trace( s ): + return f"{s.ostream}({s.entry})" diff --git a/pymtl3/stdlib/stream/test/adapters_test.py b/pymtl3/stdlib/stream/test/adapters_test.py index e62f44b35..89bcad8e9 100644 --- a/pymtl3/stdlib/stream/test/adapters_test.py +++ b/pymtl3/stdlib/stream/test/adapters_test.py @@ -26,6 +26,9 @@ def up_passthrough(): msg = s.ideq_adapter.deq() s.oenq_adapter.enq( msg ) + def line_trace( s ): + return f"{s.ideq_adapter.line_trace()} | {s.oenq_adapter.line_trace()}" + class SimpleBlockingPassthroughFL( Component ): def construct( s, Type ): s.istream = IStreamIfc( Type ) @@ -41,6 +44,9 @@ def up_passthrough(): msg = s.ideq_adapter.deq() s.oenq_adapter.enq( msg ) + def line_trace( s ): + return f"{s.ideq_adapter.line_trace()} | {s.oenq_adapter.line_trace()}" + class TestHarness( Component ): def construct( s, DutClass, Type, src_msgs, sink_msgs ): s.src = StreamSourceFL( Type, src_msgs ) @@ -54,7 +60,7 @@ def done( s ): return s.src.done() and s.sink.done() def line_trace( s ): - return f"{s.src.line_trace()} > {s.sink.line_trace()}" + return f"{s.src.line_trace()} ({s.dut.line_trace()}) {s.sink.line_trace()}" bit_msgs = [ Bits16( 0 ), Bits16( 1 ), Bits16( 2 ), Bits16( 3 ), Bits16( 0 ), Bits16( 1 ), Bits16( 2 ), Bits16( 3 ), From 661c0bd04a6d4ec9911192641369c006dd8241e2 Mon Sep 17 00:00:00 2001 From: pp482 Date: Wed, 26 Oct 2022 11:46:11 -0400 Subject: [PATCH 022/101] [mem] Add INV and FLUSH support to MemoryFL --- pymtl3/stdlib/mem/MemoryFL.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pymtl3/stdlib/mem/MemoryFL.py b/pymtl3/stdlib/mem/MemoryFL.py index 3d4ab0699..a1e2fdb7e 100644 --- a/pymtl3/stdlib/mem/MemoryFL.py +++ b/pymtl3/stdlib/mem/MemoryFL.py @@ -192,6 +192,14 @@ def up_mem(): resp = resp_classes[i]( req.type_, req.opaque, 0, req.len, s.mem.amo( req.type_, req.addr, len_, req.data ) ) + # INV + elif req.type_ == MemMsgType.INV: + resp = resp_classes[i]( req.type_, req.opaque, 0, 0, 0 ) + + # FLUSH + elif req.type_ == MemMsgType.FLUSH: + resp = resp_classes[i]( req.type_, req.opaque, 0, 0, 0 ) + # Invalid type else: assert False From 86500e9d28549fd03a2475544f2a6dff4c056c51 Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 27 Oct 2022 13:32:54 -0400 Subject: [PATCH 023/101] [mem] Add error message to out-of-bound accesses --- pymtl3/stdlib/mem/BehavioralMemory.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pymtl3/stdlib/mem/BehavioralMemory.py b/pymtl3/stdlib/mem/BehavioralMemory.py index c143609e1..51dedd0f7 100644 --- a/pymtl3/stdlib/mem/BehavioralMemory.py +++ b/pymtl3/stdlib/mem/BehavioralMemory.py @@ -28,10 +28,16 @@ def up_clear_trace(): s.trace = " " def read( s, addr, nbytes ): + assert len(s.mem) > (int(addr) + int(nbytes)), \ + f"Out-of-bound memory read of {int(nbytes)} bytes @ 0x{int(addr):#08x} detected at behavioral memory {s}!" s.trace = "[rd ]" return read_bytearray_bits( s.mem, addr, nbytes ) def write( s, addr, nbytes, data ): + assert isinstance(data, Bits), \ + f"Write operand {data} needs to be Bits to indicate write length!" + assert len(s.mem) > (int(addr) + data.nbits // 8), \ + f"Out-of-bound memory write of {data.nbits//8} bytes @ 0x{int(addr):#08x} detected at behavioral memory {s}!" s.trace = "[wr ]" write_bytearray_bits( s.mem, addr, nbytes, data ) @@ -51,11 +57,15 @@ def amo( s, amo, addr, nbytes, data ): return ret def read_mem( s, addr, size ): - assert len(s.mem) > (addr + size) + assert len(s.mem) > (int(addr) + int(size)), \ + f"Out-of-bound memory read of {int(size)} bytes @ 0x{int(addr):#08x} detected at behavioral memory {s}!" return s.mem[ addr : addr + size ] def write_mem( s, addr, data ): - assert len(s.mem) > (addr + len(data)) + assert isinstance(data, bytes), \ + f"Write operand {data} needs to be bytearray!" + assert len(s.mem) > (int(addr) + len(data)), \ + f"Out-of-bound memory write of {len(data)} bytes @ 0x{int(addr):#08x} detected at behavioral memory {s}!" s.mem[ addr : addr + len(data) ] = data def line_trace( s ): From 8c961c7f728a9281565979cc5cca9f8e8df844ea Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 27 Oct 2022 17:13:19 -0400 Subject: [PATCH 024/101] [verilator] Force signals to toggle on posedge CLK --- .../import_/verilator_wrapper_c_template.py | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py index 2fd4f605c..584822797 100644 --- a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py +++ b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py @@ -206,15 +206,34 @@ #if DUMP_VCD if ( m->_vcd_en && (ON_DEMAND_VCD_ENABLE || !ON_DEMAND_DUMP_VCD) ) {{ - // update simulation time only on clock toggle - m->trace_time += {half_cycle_time}; - g_main_time += {half_cycle_time}; + // PP: this is hacky -- we want the waveform to look like all signals + // except the CLK has toggled. We temporarily set the CLK signal + // back to 1 (as if it has not toggled) and dump VCD. + #if HAS_CLK + model->clk = 1; + #endif // dump current signal values VerilatedVcdC * tfp = (VerilatedVcdC *) m->tfp; tfp->dump( m->trace_time ); tfp->flush(); + // PP: now that we have generated the VCD we need to set CLK back to 0. + // We need to dump VCD again to register this clock toggle. + m->trace_time += {half_cycle_time}; + g_main_time += {half_cycle_time}; + + #if HAS_CLK + model->clk = 0; + #endif + + // This eval() here is necessary to propagate the CLK signal. All other + // signals will not toggle. + model->eval(); + + tfp->dump( m->trace_time ); + tfp->flush(); + }} #endif @@ -229,12 +248,7 @@ // update simulation time only on clock toggle m->trace_time += {half_cycle_time}; - g_main_time += {half_cycle_time}; - - // dump current signal values - VerilatedVcdC * tfp = (VerilatedVcdC *) m->tfp; - tfp->dump( m->trace_time ); - tfp->flush(); + g_main_time += {half_cycle_time}; }} #endif From a27b176fdade90e060f07e0a8d35643459184e02 Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 27 Oct 2022 17:25:13 -0400 Subject: [PATCH 025/101] [verilator] Add support for verilator 4.228 --- .github/workflows/python-package-ci.yml | 4 +- README.md | 6 +- .../import_/VerilogVerilatorImportConfigs.py | 67 ++++++++++++++++--- .../import_/test/ImportedObject_test.py | 1 + .../import_/verilator_wrapper_c_template.py | 11 ++- 5 files changed, 73 insertions(+), 16 deletions(-) diff --git a/.github/workflows/python-package-ci.yml b/.github/workflows/python-package-ci.yml index f15786f36..bd5540e1a 100644 --- a/.github/workflows/python-package-ci.yml +++ b/.github/workflows/python-package-ci.yml @@ -34,8 +34,8 @@ jobs: - name: Install Verilator run: | - wget https://github.com/cornell-brg/verilator-travisci-cache/raw/master/verilator-travis-4.036.tar.gz - tar -C ${HOME} -xzf verilator-travis-4.036.tar.gz + wget https://github.com/cornell-brg/verilator-travisci-cache/raw/master/verilator-travis-4.228.tar.gz + tar -C ${HOME} -xzf verilator-travis-4.228.tar.gz echo "VERILATOR_ROOT=${HOME}/verilator" >> $GITHUB_ENV echo "PYMTL_VERILATOR_INCLUDE_DIR=${HOME}/verilator/share/verilator/include" >> $GITHUB_ENV echo "${HOME}/verilator/bin" >> $GITHUB_PATH diff --git a/README.md b/README.md index 5fa10603b..1137955dc 100644 --- a/README.md +++ b/README.md @@ -95,9 +95,9 @@ install Verilator from source using the following commands: $ sudo apt-get install git make autoconf g++ libfl-dev bison $ mkdir -p ${HOME}/src $ cd ${HOME}/src - $ wget http://www.veripool.org/ftp/verilator-4.036.tgz - $ tar -xzvf verilator-4.036.tgz - $ cd verilator-4.036 + $ git clone https://github.com/verilator/verilator.git + $ cd verilator + $ git checkout v4.228 $ ./configure $ make $ sudo make install diff --git a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py index b5dbe6751..332c6ec00 100644 --- a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py +++ b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py @@ -14,7 +14,8 @@ from pymtl3.passes.PassConfigs import BasePassConfigs, Checker from pymtl3.passes.PlaceholderConfigs import expand -from ..util.utility import get_hash_of_lean_verilog +from ..errors import VerilogImportError +from ..util.utility import get_hash_of_lean_verilog, wrap from .VerilogVerilatorImportPass import VerilogVerilatorImportPass @@ -380,24 +381,25 @@ def _get_all_includes( s ): return includes def _get_c_src_files( s ): - top_module = s.translated_top_module + top_module = s.translated_top_module.replace('__', '___05F') vl_mk_dir = s.vl_mk_dir vl_class_mk = f"{vl_mk_dir}/V{top_module}_classes.mk" + cxx_inputs = [] # Add C wrapper o0 = [] o1 = copy.copy(s.c_srcs) + [ s.get_c_wrapper_path() ] + objs = [] # Add files listed in class makefile with open(vl_class_mk) as class_mk: all_lines = class_mk.readlines() - o1 += s._get_srcs_from_vl_class_mk( all_lines, vl_mk_dir, "VM_CLASSES_FAST") - o1 += s._get_srcs_from_vl_class_mk( all_lines, vl_mk_dir, "VM_SUPPORT_FAST") - o1 += s._get_srcs_from_vl_class_mk( all_lines, s.vl_include_dir, "VM_GLOBAL_FAST") - - o0 += s._get_srcs_from_vl_class_mk( all_lines, vl_mk_dir, "VM_CLASSES_SLOW") - o0 += s._get_srcs_from_vl_class_mk( all_lines, vl_mk_dir, "VM_SUPPORT_SLOW") - o0 += s._get_srcs_from_vl_class_mk( all_lines, s.vl_include_dir, "VM_GLOBAL_SLOW") + o1 += s._get_srcs_from_vl_class_mk( all_lines, vl_mk_dir, "VM_CLASSES_FAST" ) + o1 += s._get_srcs_from_vl_class_mk( all_lines, vl_mk_dir, "VM_SUPPORT_FAST" ) + o0 += s._get_srcs_from_vl_class_mk( all_lines, vl_mk_dir, "VM_CLASSES_SLOW" ) + o0 += s._get_srcs_from_vl_class_mk( all_lines, vl_mk_dir, "VM_SUPPORT_SLOW" ) + objs += s._compile_vl_srcs_from_vl_class_mk( all_lines, s.vl_include_dir, "VM_GLOBAL_FAST" ) + objs += s._compile_vl_srcs_from_vl_class_mk( all_lines, s.vl_include_dir, "VM_GLOBAL_SLOW" ) with open(f"{top_module}_v__ALL_pickled.cpp", 'w') as out: @@ -417,7 +419,9 @@ def _get_c_src_files( s ): out.write('\n'.join( [ f'#include "{x}"' for x in o0 ])) out.write('\n#pragma GCC pop_options\n') - return [ f"{top_module}_v__ALL_pickled.cpp" ] + cxx_inputs += [ f"{top_module}_v__ALL_pickled.cpp" ] + cxx_inputs += objs + return cxx_inputs def _get_srcs_from_vl_class_mk( s, all_lines, path, label ): """Return all files under `path` directory in `label` section of `mk`.""" @@ -432,3 +436,46 @@ def _get_srcs_from_vl_class_mk( s, all_lines, path, label ): file_name = line.strip()[:-2] srcs.append( path + "/" + file_name + ".cpp" ) return srcs + + def _compile_vl_srcs_from_vl_class_mk( s, all_lines, path, label ): + """Return compiled objects from Verilator sources under `path` directory in `label` section of `mk`.""" + srcs, found = [], False + for line in all_lines: + if line.startswith(label): + found = True + elif found: + if line.strip() == "": + found = False + else: + file_name = line.strip()[:-2] + srcs.append( (path + "/" + file_name + ".cpp", file_name) ) + + objs = [] + cxx_includes = " ".join(map(lambda x: "-I"+x, s._get_all_includes())) + for src in srcs: + file_path, obj_name = src + cxx_cmd = f"g++ {cxx_includes} -O1 -fPIC -c -o {obj_name}.o {file_path}" + if os.path.exists(f"{obj_name}.o"): + s.vprint(f"Skip compiling {obj_name}.o because it already exists.") + else: + s.vprint(f"Compiling {obj_name}.o with command: {cxx_cmd}") + # Invoke CXX to compile objects + try: + subprocess.check_output(cxx_cmd, + stderr = subprocess.STDOUT, + shell = True, + universal_newlines = True) + succeeds = True + except subprocess.CalledProcessError as e: + succeeds = False + err_msg = e.output if not isinstance(e.output, bytes) else \ + e.output.decode('utf-8') + import_err_msg = \ + f"Internal error: failed to compile Verilator source files:\n"\ + f" CXX command:\n{indent(cxx_cmd, ' ')}\n\n"\ + f" CXX output:\n{indent(wrap(err_msg), ' ')}\n" + if not succeeds: + raise RuntimeError(import_err_msg) + objs.append(f"{obj_name}.o") + + return objs diff --git a/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py b/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py index e967d33ee..6f27c145e 100644 --- a/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py +++ b/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py @@ -75,6 +75,7 @@ def construct( s ): s.in_ : "d", s.out : "q", } ) s.set_metadata( VerilogVerilatorImportPass.vl_xinit, 'ones' ) + s.set_metadata( VerilogVerilatorImportPass.vl_Wno_list, ['LATCH']) a = VUninit() a._tvs = [ [ 0, 4294967295 ], diff --git a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py index 584822797..cc7530c89 100644 --- a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py +++ b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py @@ -307,8 +307,17 @@ // PP: also note that since we add a wrapper around the external Verilog // module, the scope we are trying to set is actually the _wrapped_ module // which is called `v`. + // svSetScope( &model->__VlSymsp->__Vscope_{vl_component_name}__v ); + + // PP update: the above (commented) way of setting scope is not + // compatible with newer versions of Verilator because it relies on + // Verilator's internal implementation. It is recommended to get scope through + // a dotted hierarchical name as shown below. + + const svScope dut_scope = svGetScopeFromName("TOP.{vl_component_name}.v"); + assert( dut_scope ); + svSetScope( dut_scope ); - svSetScope( &model->__VlSymsp->__Vscope_{vl_component_name}__v ); model->line_trace( words ); // Note that the way the line tracing works, the line tracing function From 77e708de872b0759dc748e055fbb2a58b61a17c0 Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 27 Oct 2022 17:29:47 -0400 Subject: [PATCH 026/101] [verilator] Update use of verilator variable name in C wrapper --- .../import_/verilator_wrapper_c_template.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py index cc7530c89..c870bd31a 100644 --- a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py +++ b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py @@ -7,7 +7,7 @@ // Verilator-generated C++ model can be driven from Python. // This templated is based on PyMTL v2. -#include "obj_dir_{component_name}/V{component_name}.h" +#include "obj_dir_{component_name}/V{vl_component_name}.h" #include "stdio.h" #include "stdint.h" #include "verilated.h" @@ -30,7 +30,7 @@ #define VLINETRACE {external_trace} #if VLINETRACE -#include "obj_dir_{component_name}/V{component_name}__Syms.h" +#include "obj_dir_{component_name}/V{vl_component_name}__Syms.h" #include "svdpi.h" #endif @@ -96,14 +96,14 @@ V{component_name}_t * create_model( const char *vcd_filename ) {{ - V{component_name}_t * m; - V{component_name} * model; + V{component_name}_t * m; + V{vl_component_name} * model; Verilated::randReset( {verilator_xinit_value} ); Verilated::randSeed( {verilator_xinit_seed} ); m = (V{component_name}_t *) malloc( sizeof(V{component_name}_t) ); - model = new V{component_name}(); + model = new V{vl_component_name}(); m->model = (void *) model; @@ -144,7 +144,7 @@ VerilatedCov::write( "coverage.dat" ); #endif - V{component_name} * model = (V{component_name} *) m->model; + V{vl_component_name} * model = (V{vl_component_name} *) m->model; // finalize verilator simulation model->final(); @@ -168,7 +168,7 @@ void comb_eval( V{component_name}_t * m ) {{ - V{component_name} * model = (V{component_name} *) m->model; + V{vl_component_name} * model = (V{vl_component_name} *) m->model; // evaluate one time step model->eval(); @@ -193,7 +193,7 @@ void seq_eval( V{component_name}_t * m ) {{ - V{component_name} * model = (V{component_name} *) m->model; + V{vl_component_name} * model = (V{vl_component_name} *) m->model; // evaluate one time cycle @@ -276,7 +276,7 @@ #if VLINETRACE void trace( V{component_name}_t * m, char* str ) {{ - V{component_name} * model = (V{component_name} *) m->model; + V{vl_component_name} * model = (V{vl_component_name} *) m->model; const int nchars = 512; const int nwords = nchars/4; From cf51f50d0a26114c80a188ca2040bd0200f02c2b Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 27 Oct 2022 18:24:33 -0400 Subject: [PATCH 027/101] [test] Fix segfault due to same toplevel name --- .../passes/adhoc_transform/test/AddDebugSignalPass_test.py | 6 +++--- .../verilog/import_/verilator_wrapper_py_template.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pymtl3/passes/adhoc_transform/test/AddDebugSignalPass_test.py b/pymtl3/passes/adhoc_transform/test/AddDebugSignalPass_test.py index 06cd46550..d67cb3fa4 100644 --- a/pymtl3/passes/adhoc_transform/test/AddDebugSignalPass_test.py +++ b/pymtl3/passes/adhoc_transform/test/AddDebugSignalPass_test.py @@ -45,14 +45,14 @@ class FullChip(Component): def construct( s ): s.corechip = CoreChip() - class Top(Component): + class AddDebugSignalTop(Component): def construct( s ): s.fullchip = FullChip() def line_trace( s ): return str(s.debug_0) - top1 = Top() + top1 = AddDebugSignalTop() top1.set_param( "top.fullchip.corechip.tile0.core.dpath.mult.construct", init=0x100 ) # top.set_param( "top.fullchip.corechip.tile1.core.dpath.mult.construct", init=0x888 ) top1.elaborate() @@ -67,7 +67,7 @@ def line_trace( s ): for i in range(10): top1.sim_tick() - top2 = Top() + top2 = AddDebugSignalTop() top2.set_param( "top.fullchip.corechip.tile0.core.dpath.mult.construct", init=0x100 ) # top2.set_param( "top.fullchip.corechip.tile1.core.dpath.mult.construct", init=0x888 ) top2.elaborate() diff --git a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py index e71281f61..7b6acaaf4 100644 --- a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py +++ b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py @@ -49,8 +49,8 @@ def __init__( s, *args, **kwargs ): """) # Print the modification time stamp of the shared lib - # print 'Modification time of {{}}: {{}}'.format( - # '{lib_file}', os.path.getmtime( './{lib_file}' ) ) + # print('Modification time of {{}}: {{}}'.format( + # '{lib_file}', os.path.getmtime( './{lib_file}' ) )) # Import the shared library containing the model. We defer # construction to the elaborate_logic function to allow the user to From d628f6a554be42f7be9b4c7b10731ec9525c395a Mon Sep 17 00:00:00 2001 From: pp482 Date: Tue, 1 Nov 2022 09:54:00 -0400 Subject: [PATCH 028/101] [test] Temporarily disable tests in yosys backend --- .../passes/backends/yosys/import_/test/ImportedObject_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pymtl3/passes/backends/yosys/import_/test/ImportedObject_test.py b/pymtl3/passes/backends/yosys/import_/test/ImportedObject_test.py index 8a9291ff3..7b6b11a94 100644 --- a/pymtl3/passes/backends/yosys/import_/test/ImportedObject_test.py +++ b/pymtl3/passes/backends/yosys/import_/test/ImportedObject_test.py @@ -12,9 +12,9 @@ test_normal_queue_interface, test_normal_queue_params, test_reg, - test_reg_external_trace, + # test_reg_external_trace, test_reg_incomplete_portmap, - test_reg_infer_external_trace, + # test_reg_infer_external_trace, test_vl_uninit, ) from pymtl3.passes.backends.yosys import YosysTranslationImportPass From 151edf5e7b2dac5931b8bd4c34cf216a05557c86 Mon Sep 17 00:00:00 2001 From: pp482 Date: Tue, 22 Nov 2022 12:57:09 -0500 Subject: [PATCH 029/101] [mem] Improve error messages for behavioral mem --- pymtl3/stdlib/mem/BehavioralMemory.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pymtl3/stdlib/mem/BehavioralMemory.py b/pymtl3/stdlib/mem/BehavioralMemory.py index 51dedd0f7..6d2e2a7f1 100644 --- a/pymtl3/stdlib/mem/BehavioralMemory.py +++ b/pymtl3/stdlib/mem/BehavioralMemory.py @@ -62,8 +62,8 @@ def read_mem( s, addr, size ): return s.mem[ addr : addr + size ] def write_mem( s, addr, data ): - assert isinstance(data, bytes), \ - f"Write operand {data} needs to be bytearray!" + assert isinstance(data, (bytes, bytearray, list)), \ + f"Write operand {data} needs to be bytes, bytearray, or list of bytes!" assert len(s.mem) > (int(addr) + len(data)), \ f"Out-of-bound memory write of {len(data)} bytes @ 0x{int(addr):#08x} detected at behavioral memory {s}!" s.mem[ addr : addr + len(data) ] = data From fdaaad2c210315ede95b4b54666851f601817711 Mon Sep 17 00:00:00 2001 From: pp482 Date: Tue, 22 Nov 2022 12:57:31 -0500 Subject: [PATCH 030/101] [error msg] Add error messages for double configuring a model --- pymtl3/passes/rtlir/rtype/RTLIRType.py | 8 ++++---- pymtl3/stdlib/test_utils/test_helpers.py | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/pymtl3/passes/rtlir/rtype/RTLIRType.py b/pymtl3/passes/rtlir/rtype/RTLIRType.py index 5e2ac9da4..e639e769d 100644 --- a/pymtl3/passes/rtlir/rtype/RTLIRType.py +++ b/pymtl3/passes/rtlir/rtype/RTLIRType.py @@ -324,12 +324,12 @@ def _gen_parameters( s, obj ): # _dsl.kwargs: all named arguments supplied to construct() try: argspec = inspect.getfullargspec( obj.construct ) - assert not argspec.varkw, "keyword args are not allowed for construct!" - assert not argspec.kwonlyargs, "keyword args are not allowed for construct!" + assert not argspec.varkw, f"In object {obj}: keyword args are not allowed for construct!" + assert not argspec.kwonlyargs, f"In object {obj}: keyword args are not allowed for construct!" except AttributeError: argspec = inspect.getargspec( obj.construct ) - assert not argspec.keywords, "keyword args are not allowed for construct!" - assert not argspec.varargs, "varargs are not allowed for construct!" + assert not argspec.keywords, f"In object {obj}: keyword args are not allowed for construct!" + assert not argspec.varargs, f"In object {obj}: varargs are not allowed for construct!" arg_names = argspec.args[1:] defaults = argspec.defaults or () diff --git a/pymtl3/stdlib/test_utils/test_helpers.py b/pymtl3/stdlib/test_utils/test_helpers.py index e979866e2..bfb6054a0 100644 --- a/pymtl3/stdlib/test_utils/test_helpers.py +++ b/pymtl3/stdlib/test_utils/test_helpers.py @@ -63,7 +63,24 @@ def _recursive_set_vl_trace( m, dump_vcd ): for child in m.get_child_components(): _recursive_set_vl_trace( child, dump_vcd ) +# Define a singleton metadata key to check if a component has been configured. +IsComponentConfigured = MetadataKey(bool) + def config_model_with_cmdline_opts( top, cmdline_opts, duts ): + # First, check to make sure if this model has not been configured yet. + if not isinstance(top, Component): + raise ValueError(f"Expecting a PyMTL3 component but got {top}!") + + is_configured = top.has_metadata(IsComponentConfigured) and \ + top.get_metadata(IsComponentConfigured) + + if is_configured: + raise RuntimeError(f"It appears that model {top} has already been configured by " + f"`config_model_with_cmdline_opts'! Double configuring may cause " + f"unexpected behaviors. If you simulate your model with " + f"`pymtl3.stdlib.test_utils.run_sim' or " + f"`pymtl3.stdlib.test_utils.run_test_vector_sim', " + f"they will configure the model so you don't have to.") test_verilog = cmdline_opts['test_verilog'] if 'test_verilog' in cmdline_opts else False test_yosys_verilog = cmdline_opts['test_yosys_verilog'] if 'test_yosys_verilog' in cmdline_opts else False @@ -151,6 +168,9 @@ def config_model_with_cmdline_opts( top, cmdline_opts, duts ): if dump_textwave: top.set_metadata( PrintTextWavePass.enable, True ) + # All done -- mark the model as configured to avoid double configuring. + top.set_metadata(IsComponentConfigured, True) + return top #------------------------------------------------------------------------------ From fbaa10c7f4a25412ab4eb7dfaab2e2dca6ec8af6 Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 8 Dec 2022 13:12:18 -0500 Subject: [PATCH 031/101] src_file: defaults to filename where class is defined --- .../backends/verilog/VerilogPlaceholderPass.py | 10 ++++++++-- .../verilog/import_/test/ImportedObject_test.py | 13 +++++++++++++ .../verilog/tbgen/test/VerilogTBGenPass_test.py | 2 ++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/pymtl3/passes/backends/verilog/VerilogPlaceholderPass.py b/pymtl3/passes/backends/verilog/VerilogPlaceholderPass.py index 285c42df7..818d4d9ca 100644 --- a/pymtl3/passes/backends/verilog/VerilogPlaceholderPass.py +++ b/pymtl3/passes/backends/verilog/VerilogPlaceholderPass.py @@ -131,8 +131,14 @@ def setup_default_configs( s, m, irepr ): # Only try to infer the name of Verilog source file if both # flist and the source file are not specified. if not cfg.src_file and not cfg.v_flist: - parent_dir = os.path.dirname(inspect.getfile(m.__class__)) - cfg.src_file = f"{parent_dir}{os.sep}{cfg.top_module}.v" + # parent_dir = os.path.dirname(inspect.getfile(m.__class__)) + # cfg.src_file = f"{parent_dir}{os.sep}{cfg.top_module}.v" + + # Use the file in which m.__class__ is defined as src_file. + file_path = os.path.abspath(inspect.getfile(m.__class__)) + parent_dir = os.path.dirname(file_path) + module_name = os.path.splitext(os.path.basename(file_path))[0] + cfg.src_file = f"{parent_dir}{os.sep}{module_name}.v" # What is the original file/flist of the pickled source file? if cfg.src_file: diff --git a/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py b/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py index 6f27c145e..59dcde782 100644 --- a/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py +++ b/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py @@ -47,6 +47,7 @@ def construct( s ): s.clk : "clk", s.reset : "reset", s.in_ : "d", s.out : "q", } ) + s.set_metadata( VerilogPlaceholderPass.src_file, dirname(__file__)+'/VReg.v' ) a = VReg() a._tvs = [ [ 1, '*' ], @@ -71,6 +72,7 @@ class VUninit( Component, VerilogPlaceholder ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits32 ) + s.set_metadata( VerilogPlaceholderPass.src_file, dirname(__file__)+'/VUninit.v' ) s.set_metadata( VerilogPlaceholderPass.port_map, { s.in_ : "d", s.out : "q", } ) @@ -98,6 +100,7 @@ def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits32 ) + s.set_metadata( VerilogPlaceholderPass.src_file, dirname(__file__)+'/VReg.v' ) s.set_metadata( VerilogPlaceholderPass.port_map, { s.in_ : "d", s.out : "q", } ) @@ -132,6 +135,7 @@ def construct( s ): s.cin = InPort( Bits1 ) s.out = OutPort( Bits32 ) s.cout = OutPort( Bits1 ) + s.set_metadata( VerilogPlaceholderPass.src_file, dirname(__file__)+'/VAdder.v' ) a = VAdder() a._tvs = [ [ 1, 1, 1, 3, 0 ], @@ -165,6 +169,7 @@ def construct( s, data_width, num_entries, count_width ): s.enq_en = InPort( Bits1 ) s.enq_rdy = OutPort( Bits1 ) s.enq_msg = InPort( mk_bits( data_width ) ) + s.set_metadata( VerilogPlaceholderPass.src_file, dirname(__file__)+'/VQueue.v' ) num_entries = 1 q = VQueue( data_width = 32, @@ -264,6 +269,7 @@ def construct( s, data_width, num_entries, count_width ): s.count = OutPort( mk_bits( count_width ) ) s.deq = DequeueIfc( mk_bits( data_width ) ) s.enq = EnqueueIfc( mk_bits( data_width ) ) + s.set_metadata( VerilogPlaceholderPass.src_file, dirname(__file__)+'/VQueue.v' ) num_entries = 1 q = VQueue( data_width = 32, @@ -297,6 +303,7 @@ class VPassThrough( Component, VerilogPlaceholder ): def construct( s, nports, nbits ): s.in_ = [ InPort( mk_bits(nbits) ) for _ in range(nports) ] s.out = [ OutPort( mk_bits(nbits) ) for _ in range(nports) ] + s.set_metadata( VerilogPlaceholderPass.src_file, dirname(__file__)+'/VPassThrough.v' ) s.set_metadata( VerilogPlaceholderPass.params, { 'num_ports' : nports, 'bitwidth' : nbits, @@ -328,6 +335,7 @@ class VPassThrough( Component, VerilogPlaceholder ): def construct( s, nports, nbits ): s.in_ = [ InPort( mk_bits(nbits) ) for _ in range(nports) ] s.out = [ OutPort( mk_bits(nbits) ) for _ in range(nports) ] + s.set_metadata( VerilogPlaceholderPass.src_file, dirname(__file__)+'/VPassThrough.v' ) s.set_metadata( VerilogPlaceholderPass.params, { 'num_ports' : nports, 'bitwidth' : nbits, @@ -353,6 +361,7 @@ class VPassThrough( Component, VerilogPlaceholder ): def construct( s, nports, nbits ): s.in_ = [ InPort( mk_bits(nbits) ) for _ in range(nports) ] s.out = [ OutPort( mk_bits(nbits) ) for _ in range(nports) ] + s.set_metadata( VerilogPlaceholderPass.src_file, dirname(__file__)+'/VPassThrough.v' ) s.set_metadata( VerilogPlaceholderPass.params, { 'num_ports' : nports, 'bitwidth' : nbits, @@ -400,6 +409,7 @@ class VReg( Component, VerilogPlaceholder ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits32 ) + s.set_metadata( VerilogPlaceholderPass.src_file, dirname(__file__)+'/VReg.v' ) s.set_metadata( VerilogPlaceholderPass.port_map, { s.in_ : "d", s.out : "q", } ) @@ -433,6 +443,7 @@ class VRegTrace( Component, VerilogPlaceholder ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits32 ) + s.set_metadata( VerilogPlaceholderPass.src_file, dirname(__file__)+'/VRegTrace.v' ) s.set_metadata( VerilogPlaceholderPass.port_map, { s.in_ : "d", s.out : "q", } ) @@ -464,6 +475,7 @@ class VRegTrace( Component, VerilogPlaceholder ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits32 ) + s.set_metadata( VerilogPlaceholderPass.src_file, dirname(__file__)+'/VRegTrace.v' ) s.set_metadata( VerilogPlaceholderPass.port_map, { s.in_ : "d", s.out : "q", } ) @@ -494,6 +506,7 @@ def construct( s ): s.in_ = InPort( 10 ) s.out = OutPort( 10 ) s.vcd_en = OutPort() + s.set_metadata( VerilogPlaceholderPass.src_file, dirname(__file__)+'/VIncr.v' ) s.set_metadata( VerilogPlaceholderPass.has_clk, False ) s.set_metadata( VerilogPlaceholderPass.has_reset, False ) s.set_metadata( VerilogVerilatorImportPass.vl_trace, True ) diff --git a/pymtl3/passes/backends/verilog/tbgen/test/VerilogTBGenPass_test.py b/pymtl3/passes/backends/verilog/tbgen/test/VerilogTBGenPass_test.py index 3b547ecb4..0937a05e8 100644 --- a/pymtl3/passes/backends/verilog/tbgen/test/VerilogTBGenPass_test.py +++ b/pymtl3/passes/backends/verilog/tbgen/test/VerilogTBGenPass_test.py @@ -6,6 +6,7 @@ """Test if the imported object works correctly.""" import pytest +from os.path import dirname from pymtl3 import DefaultPassGroup from pymtl3.datatypes import Bits1, Bits32, Bits48, Bits64, clog2, mk_bits @@ -47,6 +48,7 @@ def construct( s, data_width, num_entries, count_width ): s.enq_en = InPort( Bits1 ) s.enq_rdy = OutPort( Bits1 ) s.enq_msg = InPort( mk_bits( data_width ) ) + s.set_metadata( VerilogPlaceholderPass.src_file, dirname(__file__)+'/VQueue.v' ) s.set_metadata( VerilogTranslationImportPass.enable, True ) num_entries = 1 q = VQueue( From fbb982c145f32304c05f445ba809b576788975ae Mon Sep 17 00:00:00 2001 From: pp482 Date: Mon, 23 Oct 2023 16:55:46 -0400 Subject: [PATCH 032/101] [verilator] Use Verilator context in C/Python wrapper --- .../import_/verilator_wrapper_c_template.py | 31 +++++++++------- .../import_/verilator_wrapper_py_template.py | 35 +++++++++++++------ 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py index c870bd31a..36f9a5140 100644 --- a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py +++ b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py @@ -47,6 +47,7 @@ // Verilator model void * model; + VerilatedContext * context_ptr; // VCD state int _vcd_en; @@ -64,7 +65,7 @@ void destroy_model( V{component_name}_t *); void comb_eval( V{component_name}_t * ); void seq_eval( V{component_name}_t * ); - void assert_en( bool en ); + void assert_en( V{component_name}_t *, bool ); #if VLINETRACE void trace( V{component_name}_t *, char * ); @@ -75,16 +76,14 @@ //------------------------------------------------------------------------ // sc_time_stamp //------------------------------------------------------------------------ -// Must be defined so the simulator knows the current time. Called by -// $time in Verilog. See: -// http://www.veripool.org/projects/verilator/wiki/Faq - -vluint64_t g_main_time = 0; +// This is now a lgeacy function required only so linking works on Cygwin +// and MSVC++: +// https://github.com/verilator/verilator/blob/master/examples/make_tracing_c/sim_main.cpp double sc_time_stamp() {{ - return g_main_time; + return 0; }} @@ -98,12 +97,16 @@ V{component_name}_t * m; V{vl_component_name} * model; + VerilatedContext * context_ptr; + + context_ptr = new VerilatedContext; - Verilated::randReset( {verilator_xinit_value} ); - Verilated::randSeed( {verilator_xinit_seed} ); + context_ptr->debug(0); + context_ptr->randReset( {verilator_xinit_value} ); + context_ptr->randSeed( {verilator_xinit_seed} ); m = (V{component_name}_t *) malloc( sizeof(V{component_name}_t) ); - model = new V{vl_component_name}(); + model = new V{vl_component_name}(context_ptr); m->model = (void *) model; @@ -114,7 +117,7 @@ #if DUMP_VCD if ( strlen( vcd_filename ) != 0 ) {{ m->_vcd_en = 1; - Verilated::traceEverOn( true ); + context_ptr->traceEverOn( true ); VerilatedVcdC * tfp = new VerilatedVcdC(); model->trace( tfp, 99 ); @@ -159,6 +162,8 @@ delete model; + free(m); + }} //------------------------------------------------------------------------ @@ -259,9 +264,9 @@ //------------------------------------------------------------------------ // Enable or disable assertions controlled by --assert -void assert_en( bool en ) {{ +void assert_en( V{component_name}_t * m, bool en ) {{ - Verilated::assertOn(en); + m->context_ptr->assertOn(en); }} diff --git a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py index 7b6acaaf4..cbb29f5dd 100644 --- a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py +++ b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py @@ -80,18 +80,28 @@ def finalize( s ): assert s._finalization_count == 0,\ 'Imported component can only be finalized once!' s._finalization_count += 1 + + # Clean up python side FFI references + # del s._line_trace_str + s._ffi_inst.destroy_model( s._ffi_m ) s.ffi.dlclose( s._ffi_inst ) - s.ffi = None - s._ffi_inst = None + + del s._ffi_inst + del s.ffi def __del__( s ): if s._finalization_count == 0: s._finalization_count += 1 + + # Clean up python side FFI references + # del s._line_trace_str + s._ffi_inst.destroy_model( s._ffi_m ) s.ffi.dlclose( s._ffi_inst ) - s.ffi = None - s._ffi_inst = None + + del s._ffi_inst + del s.ffi def construct( s, *args, **kwargs ): # Set up the VCD file name @@ -106,11 +116,15 @@ def construct( s, *args, **kwargs ): verilator_vcd_file = verilator_vcd_file.encode('ascii') # Construct the model - s._ffi_m = s._ffi_inst.create_model( s.ffi.new("char[]", verilator_vcd_file) ) + # PP: we need to keep the new'ed object alive by assigning it to + # a variable. See more about this: + # https://cffi.readthedocs.io/en/stable/ref.html#ffi-new + ffi_vl_vcd_file = s.ffi.new("char[]", verilator_vcd_file) + s._ffi_m = s._ffi_inst.create_model( ffi_vl_vcd_file ) # Buffer for line tracing - s._line_trace_str = s.ffi.new('char[512]') - s._convert_string = s.ffi.string + # s._line_trace_str = s.ffi.new('char[512]') + # s._convert_string = s.ffi.string # Use non-attribute varialbe to reduce CPython bytecode count _ffi_m = s._ffi_m @@ -150,12 +164,13 @@ def assert_en( s, en ): # at this moment I'm not sure if the C API's are compatible between # PyPy and CPython). assert isinstance( en, bool ) - s._ffi_inst.assert_en( en ) + s._ffi_inst.assert_en( s._ffi_m, en ) def line_trace( s ): if {external_trace}: - s._ffi_inst.trace( s._ffi_m, s._line_trace_str ) - return s._convert_string( s._line_trace_str ).decode('ascii') + # s._ffi_inst.trace( s._ffi_m, s._line_trace_str ) + # return s._convert_string( s._line_trace_str ).decode('ascii') + print('no implemented') else: {line_trace} From 6f0f52fee9e7dc8607511496c3cb0a0b368182f2 Mon Sep 17 00:00:00 2001 From: pp482 Date: Mon, 23 Oct 2023 17:15:01 -0400 Subject: [PATCH 033/101] [import] Release line trace string at final --- .../import_/verilator_wrapper_py_template.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py index cbb29f5dd..d235b6451 100644 --- a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py +++ b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py @@ -82,7 +82,7 @@ def finalize( s ): s._finalization_count += 1 # Clean up python side FFI references - # del s._line_trace_str + del s._line_trace_str s._ffi_inst.destroy_model( s._ffi_m ) s.ffi.dlclose( s._ffi_inst ) @@ -95,7 +95,7 @@ def __del__( s ): s._finalization_count += 1 # Clean up python side FFI references - # del s._line_trace_str + del s._line_trace_str s._ffi_inst.destroy_model( s._ffi_m ) s.ffi.dlclose( s._ffi_inst ) @@ -123,8 +123,8 @@ def construct( s, *args, **kwargs ): s._ffi_m = s._ffi_inst.create_model( ffi_vl_vcd_file ) # Buffer for line tracing - # s._line_trace_str = s.ffi.new('char[512]') - # s._convert_string = s.ffi.string + s._line_trace_str = s.ffi.new('char[512]') + s._convert_string = s.ffi.string # Use non-attribute varialbe to reduce CPython bytecode count _ffi_m = s._ffi_m @@ -168,9 +168,8 @@ def assert_en( s, en ): def line_trace( s ): if {external_trace}: - # s._ffi_inst.trace( s._ffi_m, s._line_trace_str ) - # return s._convert_string( s._line_trace_str ).decode('ascii') - print('no implemented') + s._ffi_inst.trace( s._ffi_m, s._line_trace_str ) + return s._convert_string( s._line_trace_str ).decode('ascii') else: {line_trace} From 44002c1fa73770fa77267e96171f229c0d89e655 Mon Sep 17 00:00:00 2001 From: pp482 Date: Wed, 25 Oct 2023 15:04:47 -0400 Subject: [PATCH 034/101] [import] Use linker version script to avoid UNIQUE symbols --- .../import_/VerilogVerilatorImportConfigs.py | 6 +++++ .../backends/verilog/import_/test/VRegTrace.v | 2 ++ ...verilator_import_linker_version_script.lds | 11 +++++++++ .../import_/verilator_wrapper_c_template.py | 8 +++---- .../import_/verilator_wrapper_py_template.py | 24 ++++++++++++++++--- 5 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 pymtl3/passes/backends/verilog/import_/verilator_import_linker_version_script.lds diff --git a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py index 332c6ec00..b85492f17 100644 --- a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py +++ b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py @@ -333,6 +333,12 @@ def create_cc_cmd( s ): coverage = "-DVM_COVERAGE" if s.vl_coverage or \ s.vl_line_coverage or \ s.vl_toggle_coverage else "" + + # PP: try not to introduce GNU unique symbols to the shared library. + linker_version_script_filename = "verilator_import_linker_version_script.lds" + dir_to_version_script = os.path.abspath(os.path.dirname(__file__)) + ld_flags += f" -Wl,--version-script={os.path.join(dir_to_version_script, linker_version_script_filename)}" + return f"g++ {c_flags} {c_include_path} {ld_flags}"\ f" -o {out_file} {c_src_files} {ld_libs} {coverage}" diff --git a/pymtl3/passes/backends/verilog/import_/test/VRegTrace.v b/pymtl3/passes/backends/verilog/import_/test/VRegTrace.v index 745645538..b2fae4b5e 100644 --- a/pymtl3/passes/backends/verilog/import_/test/VRegTrace.v +++ b/pymtl3/passes/backends/verilog/import_/test/VRegTrace.v @@ -14,6 +14,8 @@ module test_VRegTrace( logic [512*8-1:0] q_str; `VC_TRACE_BEGIN begin + // PP: we should not use $ system tasks because + // they seem to be generating segfaults at end of pytest?? /* $display("q = %d\n", q); */ $sformat(q_str, "%d", q); vc_trace.append_str( trace_str, "q = " ); diff --git a/pymtl3/passes/backends/verilog/import_/verilator_import_linker_version_script.lds b/pymtl3/passes/backends/verilog/import_/verilator_import_linker_version_script.lds new file mode 100644 index 000000000..3fe73472d --- /dev/null +++ b/pymtl3/passes/backends/verilog/import_/verilator_import_linker_version_script.lds @@ -0,0 +1,11 @@ +VERS_0.1 { + global: + create_model; + destroy_model; + comb_eval; + seq_eval; + assert_en; + trace; + local: + *; +}; diff --git a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py index 36f9a5140..b9a1d69a4 100644 --- a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py +++ b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py @@ -108,7 +108,8 @@ m = (V{component_name}_t *) malloc( sizeof(V{component_name}_t) ); model = new V{vl_component_name}(context_ptr); - m->model = (void *) model; + m->model = (void *) model; + m->context_ptr = context_ptr; // Enable tracing. We have added a feature where if the vcd_filename is // "" then we don't do any VCD dumping even if DUMP_VCD is true. @@ -161,6 +162,7 @@ #endif delete model; + delete m->context_ptr; free(m); @@ -226,7 +228,6 @@ // PP: now that we have generated the VCD we need to set CLK back to 0. // We need to dump VCD again to register this clock toggle. m->trace_time += {half_cycle_time}; - g_main_time += {half_cycle_time}; #if HAS_CLK model->clk = 0; @@ -253,7 +254,6 @@ // update simulation time only on clock toggle m->trace_time += {half_cycle_time}; - g_main_time += {half_cycle_time}; }} #endif @@ -323,7 +323,7 @@ assert( dut_scope ); svSetScope( dut_scope ); - model->line_trace( words ); + V{vl_component_name}::line_trace( words ); // Note that the way the line tracing works, the line tracing function // will store how the last character used in the line trace in the diff --git a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py index d235b6451..bd54c3c96 100644 --- a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py +++ b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py @@ -9,7 +9,10 @@ normal PyMTL model. This template is based on PyMTL v2. """ +import copy import os +import gc +import weakref from cffi import FFI @@ -77,12 +80,15 @@ def finalize( s ): you might need to call `imported_object.finalize()` at the end of each test to ensure correct behaviors. """ + # print(f"In finalize() method of an instance of {{str(s.__class__)}}") assert s._finalization_count == 0,\ 'Imported component can only be finalized once!' s._finalization_count += 1 # Clean up python side FFI references - del s._line_trace_str + s.ffi.release(s._line_trace_str) + + s._convert_string = None s._ffi_inst.destroy_model( s._ffi_m ) s.ffi.dlclose( s._ffi_inst ) @@ -90,12 +96,17 @@ def finalize( s ): del s._ffi_inst del s.ffi + gc.collect() + # print("End of finalize()") + def __del__( s ): + # print(f"In __del__() method of an instance of {{str(s.__class__)}}") if s._finalization_count == 0: s._finalization_count += 1 # Clean up python side FFI references - del s._line_trace_str + s.ffi.release(s._line_trace_str) + s._convert_string = None s._ffi_inst.destroy_model( s._ffi_m ) s.ffi.dlclose( s._ffi_inst ) @@ -103,6 +114,9 @@ def __del__( s ): del s._ffi_inst del s.ffi + gc.collect() + # print("End of __del__") + def construct( s, *args, **kwargs ): # Set up the VCD file name verilator_vcd_file = "" @@ -123,9 +137,13 @@ def construct( s, *args, **kwargs ): s._ffi_m = s._ffi_inst.create_model( ffi_vl_vcd_file ) # Buffer for line tracing - s._line_trace_str = s.ffi.new('char[512]') + line_trace_str = s.ffi.new('char[512]') + s._line_trace_str = line_trace_str s._convert_string = s.ffi.string + global_weakref_dict = weakref.WeakKeyDictionary() + global_weakref_dict[s] = (line_trace_str) + # Use non-attribute varialbe to reduce CPython bytecode count _ffi_m = s._ffi_m _ffi_inst_comb_eval = s._ffi_inst.comb_eval From b034129cda3f011c5f6c9f102d97c6bbf55792d2 Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 26 Oct 2023 14:17:48 -0400 Subject: [PATCH 035/101] [import] Fix segfault on exit in import line trace test --- .../import_/VerilogVerilatorImportConfigs.py | 21 ++++++++++++++---- .../import_/test/ImportedObject_test.py | 22 +++++++++++++++++-- .../import_/verilator_wrapper_c_template.py | 2 ++ 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py index b85492f17..348428a5a 100644 --- a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py +++ b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py @@ -130,7 +130,7 @@ class VerilogVerilatorImportConfigs( BasePassConfigs ): # We enforce the GNU makefile implicit rule that `LDLIBS` should only # include library linker flags/names such as `-lfoo`. - "ld_libs" : "", + "ld_libs" : "-lpthread", "c_flags" : "", } @@ -269,6 +269,7 @@ def create_vl_cmd( s ): opt_level = "-O3" loop_unroll = "--unroll-count 1000000" stmt_unroll = "--unroll-stmts 1000000" + thread = "--threads 1" trace = "--trace" if s.vl_trace else "" coverage = "--coverage" if s.vl_coverage else "" line_cov = "--coverage-line" if s.vl_line_coverage else "" @@ -278,7 +279,7 @@ def create_vl_cmd( s ): all_opts = [ top_module, mk_dir, include, en_assert, opt_level, loop_unroll, # stmt_unroll, trace, warnings, flist, src, coverage, - stmt_unroll, trace, warnings, src, vlibs, coverage, + stmt_unroll, thread, trace, warnings, src, vlibs, coverage, line_cov, toggle_cov, ] @@ -325,6 +326,8 @@ def create_cc_cmd( s ): if not s.is_default("c_flags"): c_flags += f" {expand(s.c_flags)}" + c_flags += f" -std=c++11 -pthread -DVL_TIME_CONTEXT" + c_include_path = " ".join("-I"+p for p in s._get_all_includes() if p) out_file = s.get_shared_lib_path() c_src_files = " ".join(s._get_c_src_files()) @@ -334,7 +337,17 @@ def create_cc_cmd( s ): s.vl_line_coverage or \ s.vl_toggle_coverage else "" - # PP: try not to introduce GNU unique symbols to the shared library. + # PP: eliminate GNU unique symbols in the compiled shared library. + # See https://stackoverflow.com/a/76664985 + # ^ this says if a shared library has UNIQUE symbols in it a dlclose + # may become a no-op. The solution is to mark all symbols as `hidden` + # except for those we want to access in the C wrapper. Note that I've + # tried adding the g++ flag `-fvisibility=hidden` and + # __attribute__((visibility("default"))) to APIs, but the compiled + # shared library still contains UNIQUE symbols: + # % readelf -sW --dyn-syms libVRegTrace_noparam_v.so | grep '\bUNIQUE\b' + # The solution proposed in the above post is to use a linker version + # script which specifies visibility of all symbols. linker_version_script_filename = "verilator_import_linker_version_script.lds" dir_to_version_script = os.path.abspath(os.path.dirname(__file__)) ld_flags += f" -Wl,--version-script={os.path.join(dir_to_version_script, linker_version_script_filename)}" @@ -460,7 +473,7 @@ def _compile_vl_srcs_from_vl_class_mk( s, all_lines, path, label ): cxx_includes = " ".join(map(lambda x: "-I"+x, s._get_all_includes())) for src in srcs: file_path, obj_name = src - cxx_cmd = f"g++ {cxx_includes} -O1 -fPIC -c -o {obj_name}.o {file_path}" + cxx_cmd = f"g++ {cxx_includes} -std=c++11 -DVL_TIME_CONTEXT -O1 -fPIC -pthread -lpthread -c -o {obj_name}.o {file_path}" if os.path.exists(f"{obj_name}.o"): s.vprint(f"Skip compiling {obj_name}.o because it already exists.") else: diff --git a/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py b/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py index 59dcde782..d8d0291a8 100644 --- a/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py +++ b/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py @@ -467,7 +467,25 @@ def construct( s ): # 0xFFFFFFFF unsigned assert a.line_trace() == 'q = 4294967295' a.sim_tick() - a.finalize() + + # PP: This is a workaround to the $sformat issue I was seeing with Verilator + # 5.016. If I enable this finalize below, the C library will segfault after + # all pytest tests have finished. Digging deeper I found this could be + # reliably eliminated/reproduced by removing/adding the `thread_local` + # specifier to the temporary string used in VL_SFORMAT_X (verilated.h). Note + # that the program finishes just fine when this thread-local string is never + # used (VL_SFORMAT_X is never invoked). My theory is this could be a + # double-free situation: somehow the dynamically allocated memory that + # belongs to this thread-local string gets deleted twice: once when we unload + # the dynamic library (through a.finalize()) and once at the end of program + # execution. + # + # One stackoverflow post suggests this might be a compiler bug: + # https://stackoverflow.com/a/58366171 + # I tried recompiling with the latest g++ on BRG server (scl-11) but the + # segfault persists. + + # a.finalize() def test_reg_infer_external_trace( do_test ): # Test Verilog line trace @@ -498,7 +516,7 @@ def construct( s ): # 0xFFFFFFFF unsigned assert a.line_trace() == 'q = 4294967295' a.sim_tick() - a.finalize() + # a.finalize() def test_incr_on_demand_vcd( do_test ): class VIncr( Component, VerilogPlaceholder ): diff --git a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py index b9a1d69a4..b16039cc6 100644 --- a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py +++ b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py @@ -201,6 +201,7 @@ void seq_eval( V{component_name}_t * m ) {{ V{vl_component_name} * model = (V{vl_component_name} *) m->model; + VerilatedContext * context_ptr = m->context_ptr; // evaluate one time cycle @@ -248,6 +249,7 @@ #endif model->eval(); + context_ptr->timeInc(1); #if DUMP_VCD if ( m->_vcd_en && (ON_DEMAND_VCD_ENABLE || !ON_DEMAND_DUMP_VCD) ) {{ From e837b43865a199a01c1aef510cf3124f8d09f0bb Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 2 Nov 2023 14:26:53 -0400 Subject: [PATCH 036/101] [gitignore] Ignore dist directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0c61c01eb..b8b59233e 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ # still have it appear clean to git. build*/ +dist/ autom4te.cache/ .pytest_cache/ *__pycache__ From e619c4ad9659a4754c688e15d830f437ed211372 Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 12 Oct 2023 13:01:09 -0400 Subject: [PATCH 037/101] [verilog] Refactor translation pass --- .../translation/VerilogTranslationPass.py | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/pymtl3/passes/backends/verilog/translation/VerilogTranslationPass.py b/pymtl3/passes/backends/verilog/translation/VerilogTranslationPass.py index 46c8bd7c6..47f8ce2f2 100644 --- a/pymtl3/passes/backends/verilog/translation/VerilogTranslationPass.py +++ b/pymtl3/passes/backends/verilog/translation/VerilogTranslationPass.py @@ -144,33 +144,6 @@ def traverse_hierarchy( s, m ): filename = fname.split('.sv')[0] else: filename = fname - - output_file = filename + '.v' - temporary_file = filename + '.v.tmp' - - # First write the file to a temporary file - is_same = False - with open( temporary_file, 'w' ) as output: - output.write( s.translator.hierarchy.src ) - output.flush() - os.fsync( output ) - output.close() - - # `is_same` is set if there exists a file that has the same filename as - # `output_file`, and that file is the same as the temporary file - if ( os.path.exists(output_file) ): - is_same = verilog_cmp( temporary_file, output_file ) - - # Rename the temporary file to the output file - os.rename( temporary_file, output_file ) - - # Expose some attributes about the translation process - m.set_metadata( c.is_same, is_same ) - m.set_metadata( c.translator, s.translator ) - m.set_metadata( c.translated, True ) - m.set_metadata( c.translated_filename, output_file ) - m.set_metadata( c.translated_top_module, module_name ) - else: filename = f"{module_name}__pickled" From e6fcf7dcc30b9d489405a37d2932b2470a9cd75a Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 12 Oct 2023 13:47:16 -0400 Subject: [PATCH 038/101] [verilog] Use tempfile library to create tmp files --- .../translation/VerilogTranslationPass.py | 51 ++++++++++--------- .../passes/backends/verilog/util/utility.py | 13 +++-- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/pymtl3/passes/backends/verilog/translation/VerilogTranslationPass.py b/pymtl3/passes/backends/verilog/translation/VerilogTranslationPass.py index 47f8ce2f2..fb5298aac 100644 --- a/pymtl3/passes/backends/verilog/translation/VerilogTranslationPass.py +++ b/pymtl3/passes/backends/verilog/translation/VerilogTranslationPass.py @@ -4,7 +4,7 @@ # Author : Peitian Pan # Date : March 12, 2019 """Translate a PyMTL component hierarhcy into SystemVerilog source code.""" -import os +import os, tempfile from pymtl3 import MetadataKey from pymtl3.passes.BasePass import BasePass @@ -148,30 +148,35 @@ def traverse_hierarchy( s, m ): filename = f"{module_name}__pickled" output_file = filename + '.v' - temporary_file = filename + '.v.tmp' - # First write the file to a temporary file + # Create a temporary file under the current directory. is_same = False - with open( temporary_file, 'w' ) as output: - output.write( s.translator.hierarchy.src ) - output.flush() - os.fsync( output ) - output.close() - - # `is_same` is set if there exists a file that has the same filename as - # `output_file`, and that file is the same as the temporary file - if ( os.path.exists(output_file) ): - is_same = verilog_cmp( temporary_file, output_file ) - - # Rename the temporary file to the output file - os.rename( temporary_file, output_file ) - - # Expose some attributes about the translation process - m.set_metadata( c.is_same, is_same ) - m.set_metadata( c.translator, s.translator ) - m.set_metadata( c.translated, True ) - m.set_metadata( c.translated_filename, output_file ) - m.set_metadata( c.translated_top_module, module_name ) + tmp_fd, tmp_path = tempfile.mkstemp(dir=os.curdir, text=True) + + with open(tmp_path, "w+") as tmp_file: + tmp_file.write( s.translator.hierarchy.src ) + tmp_file.flush() + os.fsync( tmp_file ) + + # `is_same` is set if there exists a file that has the same filename as + # `output_file`, and that file is the same as the temporary file. + if ( os.path.exists(output_file) ): + is_same = verilog_cmp( tmp_file, output_file ) + + # Rename the temporary file to the output file. + os.replace( tmp_path, output_file ) + + # Expose some attributes about the translation process. + m.set_metadata( c.is_same, is_same ) + m.set_metadata( c.translator, s.translator ) + m.set_metadata( c.translated, True ) + m.set_metadata( c.translated_filename, output_file ) + m.set_metadata( c.translated_top_module, module_name ) + + # Clean up the temporary file. + os.close( tmp_fd ) + if os.path.exists( tmp_path ): + os.remove( tmp_path ) else: for child in m.get_child_components(repr): diff --git a/pymtl3/passes/backends/verilog/util/utility.py b/pymtl3/passes/backends/verilog/util/utility.py index 7ab6b9a09..edb98c46d 100644 --- a/pymtl3/passes/backends/verilog/util/utility.py +++ b/pymtl3/passes/backends/verilog/util/utility.py @@ -61,10 +61,15 @@ def get_file_hash( file_path ): hash_inst.update(string) return hash_inst.hexdigest() -def get_lean_verilog_file( file_path ): - with open(file_path) as fd: - file_v = [x for x in fd.readlines() if x != '\n' and not x.startswith('//')] - return file_v +def get_lean_verilog( fd ): + return [x for x in fd.readlines() if x != '\n' and not x.startswith('//')] + +def get_lean_verilog_file( file_path_or_fd ): + if hasattr( file_path_or_fd, 'readlines' ): + return get_lean_verilog(file_path_or_fd) + else: + with open(file_path_or_fd) as fd: + return get_lean_verilog(fd) def get_hash_of_lean_verilog( file_path ): hash_inst = blake2b() From 27e36b46208b79a99b7624260f94c9ae62319c1f Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 12 Oct 2023 16:02:13 -0400 Subject: [PATCH 039/101] [verilog] Initial xdist support in translation/import --- .../import_/VerilogVerilatorImportConfigs.py | 3 +- .../import_/VerilogVerilatorImportPass.py | 218 +++++++++--------- .../test/TranslationImport_xdist_test.py | 36 +++ .../translation/VerilogTranslationPass.py | 10 +- 4 files changed, 159 insertions(+), 108 deletions(-) create mode 100644 pymtl3/passes/backends/verilog/test/TranslationImport_xdist_test.py diff --git a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py index 348428a5a..0c1dd7924 100644 --- a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py +++ b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py @@ -26,7 +26,8 @@ class VerilogVerilatorImportConfigs( BasePassConfigs ): "enable" : False, # Enable verbose mode? - "verbose" : False, + # "verbose" : False, + "verbose" : True, # Trade off compilation time for fast simulation performance? # controls: vl_opt_level, vl_unroll_count diff --git a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py index 4c5272e5f..f0c6b2a82 100644 --- a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py +++ b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py @@ -6,9 +6,11 @@ """Provide a pass that imports arbitrary SystemVerilog modules.""" import copy +import fasteners import importlib import json import linecache +import multiprocessing import os import shutil import subprocess @@ -259,6 +261,7 @@ def __call__( s, top ): if not top._dsl.constructed: raise VerilogImportError( top, f"please elaborate design {top} before applying the import pass!" ) + ret = s.traverse_hierarchy( top ) if ret is None: ret = top @@ -366,15 +369,30 @@ def get_imported_object( s, m ): ph_cfg.has_clk, ph_cfg.has_reset, ph_cfg.separator ) - cached, config_file, cfg_d = s.is_cached( m, ip_cfg ) - - s.create_verilator_model( m, ph_cfg, ip_cfg, cached ) - - port_cdefs = s.create_verilator_c_wrapper( m, ph_cfg, ip_cfg, ports, cached ) - - s.create_shared_lib( m, ph_cfg, ip_cfg, cached ) + cache_lock = fasteners.InterProcessLock('cache.lock') + cache_lock.acquire() + try: + cached, config_file, cfg_d = s.is_cached( m, ip_cfg ) + # Dump configuration dict to config_file + with open( config_file, 'w' ) as fd: + json.dump( cfg_d, fd, indent = 4 ) + finally: + cache_lock.release() - symbols = s.create_py_wrapper( m, ph_cfg, ip_cfg, rtype, ports, port_cdefs, cached ) + if not cached: + s.lock = fasteners.InterProcessLock('verilog_import.lock') + s.lock.acquire() + try: + s.create_verilator_model( m, ph_cfg, ip_cfg ) + port_cdefs = s.create_verilator_c_wrapper( m, ph_cfg, ip_cfg, ports ) + s.create_shared_lib( m, ph_cfg, ip_cfg ) + symbols = s.create_py_wrapper( m, ph_cfg, ip_cfg, rtype, ports, port_cdefs ) + finally: + s.lock.release() + else: + ip_cfg.vprint(f"{ip_cfg.translated_top_module} is cached!", 2) + port_cdefs = s.create_verilator_c_wrapper( m, ph_cfg, ip_cfg, ports ) + symbols = s.create_py_wrapper( m, ph_cfg, ip_cfg, rtype, ports, port_cdefs ) imp = s.import_component( m, ph_cfg, ip_cfg, symbols ) @@ -382,63 +400,55 @@ def get_imported_object( s, m ): imp._ph_cfg = ph_cfg imp._ports = ports - # Dump configuration dict to config_file - with open( config_file, 'w' ) as fd: - json.dump( cfg_d, fd, indent = 4 ) - return imp #----------------------------------------------------------------------- # create_verilator_model #----------------------------------------------------------------------- - def create_verilator_model( s, m, ph_cfg, ip_cfg, cached ): + def create_verilator_model( s, m, ph_cfg, ip_cfg ): # Verilate module `m`. ip_cfg.vprint("\n=====Verilate model=====") - if not cached: - # Generate verilator command - cmd = ip_cfg.create_vl_cmd() - - # Remove obj_dir directory if it already exists. - # obj_dir is where the verilator output ( C headers and sources ) is stored - obj_dir = ip_cfg.vl_mk_dir - if os.path.exists( obj_dir ): - shutil.rmtree( obj_dir ) + # Generate verilator command + cmd = ip_cfg.create_vl_cmd() - succeeds = True + # Remove obj_dir directory if it already exists. + # obj_dir is where the verilator output ( C headers and sources ) is stored + obj_dir = ip_cfg.vl_mk_dir + if os.path.exists( obj_dir ): + shutil.rmtree( obj_dir, ignore_errors=True ) - # Try to call verilator - try: - ip_cfg.vprint(f"Verilating {ip_cfg.translated_top_module} with command:", 2) - ip_cfg.vprint(f"{cmd}", 4) - t0 = timeit.default_timer() - subprocess.check_output( - cmd, stderr = subprocess.STDOUT, shell = True ) - ip_cfg.vprint(f"verilate time: {timeit.default_timer()-t0}") - except subprocess.CalledProcessError as e: - succeeds = False - err_msg = e.output if not isinstance(e.output, bytes) else \ - e.output.decode('utf-8') - import_err_msg = \ - f"Fail to verilate model {ip_cfg.translated_top_module}\n"\ - f" Verilator command:\n{indent(cmd, ' ')}\n\n"\ - f" Verilator output:\n{indent(wrap(err_msg), ' ')}\n" - - if not succeeds: - raise VerilogImportError(m, import_err_msg) - - ip_cfg.vprint("Successfully verilated the given model!", 2) + succeeds = True - else: - ip_cfg.vprint(f"{ip_cfg.translated_top_module} not verilated because it's cached!", 2) + # Try to call verilator + try: + ip_cfg.vprint(f"Verilating {ip_cfg.translated_top_module} with command:", 2) + ip_cfg.vprint(f"{cmd}", 4) + t0 = timeit.default_timer() + subprocess.check_output( + cmd, stderr = subprocess.STDOUT, shell = True ) + ip_cfg.vprint(f"verilate time: {timeit.default_timer()-t0}") + except subprocess.CalledProcessError as e: + succeeds = False + err_msg = e.output if not isinstance(e.output, bytes) else \ + e.output.decode('utf-8') + import_err_msg = \ + f"Fail to verilate model {ip_cfg.translated_top_module}\n"\ + f" Verilator command:\n{indent(cmd, ' ')}\n\n"\ + f" Verilator output:\n{indent(wrap(err_msg), ' ')}\n" + + if not succeeds: + raise VerilogImportError(m, import_err_msg) + + ip_cfg.vprint("Successfully verilated the given model!", 2) #----------------------------------------------------------------------- # create_verilator_c_wrapper #----------------------------------------------------------------------- - def create_verilator_c_wrapper( s, m, ph_cfg, ip_cfg, ports, cached ): + def create_verilator_c_wrapper( s, m, ph_cfg, ip_cfg, ports, dump=True ): # Return the file name of generated C component wrapper. # Create a C wrapper that calls verilator C API and provides interfaces # that can be later called through CFFI. @@ -482,8 +492,9 @@ def create_verilator_c_wrapper( s, m, ph_cfg, ip_cfg, ports, cached ): port_inits = '\n'.join( port_inits ) # Fill in the C wrapper template - with open( wrapper_name, 'w' ) as output: - output.write( c_template.format( **locals() ) ) + if dump: + with open( wrapper_name, 'w' ) as output: + output.write( c_template.format( **locals() ) ) ip_cfg.vprint(f"Successfully generated C wrapper {wrapper_name}!", 2) return port_cdefs @@ -492,54 +503,50 @@ def create_verilator_c_wrapper( s, m, ph_cfg, ip_cfg, ports, cached ): # create_shared_lib #----------------------------------------------------------------------- - def create_shared_lib( s, m, ph_cfg, ip_cfg, cached ): + def create_shared_lib( s, m, ph_cfg, ip_cfg ): # Return the name of compiled shared lib. full_name = ip_cfg.translated_top_module dump_vcd = ip_cfg.vl_trace ip_cfg.vprint("\n=====Compile shared library=====") - if not cached: - cmd = ip_cfg.create_cc_cmd() - - succeeds = True + cmd = ip_cfg.create_cc_cmd() - # Try to call the C compiler - try: - ip_cfg.vprint("Compiling shared library with command:", 2) - ip_cfg.vprint(f"{cmd}", 4) - t0 = timeit.default_timer() - subprocess.check_output( - cmd, - stderr = subprocess.STDOUT, - shell = True, - universal_newlines = True - ) - ip_cfg.vprint(f"shared library compilation time: {timeit.default_timer()-t0}") - except subprocess.CalledProcessError as e: - succeeds = False - err_msg = e.output if not isinstance(e.output, bytes) else \ - e.output.decode('utf-8') - import_err_msg = \ - f"Failed to compile Verilated model into a shared library:\n"\ - f" C compiler command:\n{indent(cmd, ' ')}\n\n"\ - f" C compiler output:\n{indent(wrap(err_msg), ' ')}\n" - - if not succeeds: - raise VerilogImportError(m, import_err_msg) - - ip_cfg.vprint(f"Successfully compiled shared library "\ - f"{ip_cfg.get_shared_lib_path()}!", 2) + succeeds = True - else: - ip_cfg.vprint("Didn't compile shared library because it's cached!", 2) + # Try to call the C compiler + try: + ip_cfg.vprint("Compiling shared library with command:", 2) + ip_cfg.vprint(f"{cmd}", 4) + t0 = timeit.default_timer() + subprocess.check_output( + cmd, + stderr = subprocess.STDOUT, + shell = True, + universal_newlines = True + ) + ip_cfg.vprint(f"shared library compilation time: {timeit.default_timer()-t0}") + except subprocess.CalledProcessError as e: + succeeds = False + err_msg = e.output if not isinstance(e.output, bytes) else \ + e.output.decode('utf-8') + import_err_msg = \ + f"Failed to compile Verilated model into a shared library:\n"\ + f" C compiler command:\n{indent(cmd, ' ')}\n\n"\ + f" C compiler output:\n{indent(wrap(err_msg), ' ')}\n" + + if not succeeds: + raise VerilogImportError(m, import_err_msg) + + ip_cfg.vprint(f"Successfully compiled shared library "\ + f"{ip_cfg.get_shared_lib_path()}!", 2) #----------------------------------------------------------------------- # create_py_wrapper #----------------------------------------------------------------------- # Return the file name of the generated PyMTL component wrapper. - def create_py_wrapper( s, m, ph_cfg, ip_cfg, rtype, ports, port_cdefs, cached ): + def create_py_wrapper( s, m, ph_cfg, ip_cfg, rtype, ports, port_cdefs, dump=True ): ip_cfg.vprint("\n=====Generate PyMTL wrapper=====") wrapper_name = ip_cfg.get_py_wrapper_path() @@ -572,28 +579,29 @@ def create_py_wrapper( s, m, ph_cfg, ip_cfg, rtype, ports, port_cdefs, cached ): external_trace_c_def = '' # Fill in the python wrapper template - with open( wrapper_name, 'w' ) as output: - py_wrapper = py_template.format( - component_name = ip_cfg.translated_top_module, - has_clk = int(ph_cfg.has_clk), - clk = 'inv_clk' if not ph_cfg.has_clk else \ - next(filter(lambda x: x[0][0]=='clk', ports))[1], - lib_file = ip_cfg.get_shared_lib_path(), - port_cdefs = (' '*4+'\n').join( port_cdefs ), - port_defs = '\n'.join( port_defs ), - structs_input = '\n'.join( structs_input ), - structs_output = '\n'.join( structs_output ), - set_comb_input = '\n'.join( set_comb_input ), - set_comb_output = '\n'.join( set_comb_output ), - line_trace = line_trace, - in_line_trace = in_line_trace, - dump_vcd = int(ip_cfg.vl_trace), - has_vl_trace_filename = bool(ip_cfg.vl_trace_filename), - vl_trace_filename = ip_cfg.vl_trace_filename, - external_trace = int(ip_cfg.vl_line_trace), - trace_c_def = external_trace_c_def, - ) - output.write( py_wrapper ) + if dump: + with open( wrapper_name, 'w' ) as output: + py_wrapper = py_template.format( + component_name = ip_cfg.translated_top_module, + has_clk = int(ph_cfg.has_clk), + clk = 'inv_clk' if not ph_cfg.has_clk else \ + next(filter(lambda x: x[0][0]=='clk', ports))[1], + lib_file = ip_cfg.get_shared_lib_path(), + port_cdefs = (' '*4+'\n').join( port_cdefs ), + port_defs = '\n'.join( port_defs ), + structs_input = '\n'.join( structs_input ), + structs_output = '\n'.join( structs_output ), + set_comb_input = '\n'.join( set_comb_input ), + set_comb_output = '\n'.join( set_comb_output ), + line_trace = line_trace, + in_line_trace = in_line_trace, + dump_vcd = int(ip_cfg.vl_trace), + has_vl_trace_filename = bool(ip_cfg.vl_trace_filename), + vl_trace_filename = ip_cfg.vl_trace_filename, + external_trace = int(ip_cfg.vl_line_trace), + trace_c_def = external_trace_c_def, + ) + output.write( py_wrapper ) ip_cfg.vprint(f"Successfully generated PyMTL wrapper {wrapper_name}!", 2) return symbols diff --git a/pymtl3/passes/backends/verilog/test/TranslationImport_xdist_test.py b/pymtl3/passes/backends/verilog/test/TranslationImport_xdist_test.py new file mode 100644 index 000000000..c60af1fdd --- /dev/null +++ b/pymtl3/passes/backends/verilog/test/TranslationImport_xdist_test.py @@ -0,0 +1,36 @@ +#========================================================================= +# TranslationImport_xdist_test.py +#========================================================================= +# Author : Peitian Pan +# Date : Oct 11th, 2023 +"""Test if Verilated models can be simulated in parallel.""" + +import pytest, multiprocessing + +from pymtl3.datatypes import Bits32 +from pymtl3.passes import DefaultPassGroup +from pymtl3.passes.backends.verilog import VerilogPlaceholderPass, VerilogTranslationImportPass + +from ..testcases import Bits32VRegComp + +@pytest.mark.parametrize( + 'test_id', list(range(500)) +) +def test_verilator_co_simulation( test_id ): + n_cycles = 100 + + m = Bits32VRegComp() + m.elaborate() + m.set_metadata( VerilogTranslationImportPass.enable, True ) + m.apply( VerilogPlaceholderPass() ) + m = VerilogTranslationImportPass()( m ) + m.apply( DefaultPassGroup() ) + + m.sim_reset() + + for i in range(n_cycles): + m.d @= Bits32(test_id + i) + m.sim_tick() + assert m.q == Bits32(test_id + i) + + # m.finalize() diff --git a/pymtl3/passes/backends/verilog/translation/VerilogTranslationPass.py b/pymtl3/passes/backends/verilog/translation/VerilogTranslationPass.py index fb5298aac..325199c59 100644 --- a/pymtl3/passes/backends/verilog/translation/VerilogTranslationPass.py +++ b/pymtl3/passes/backends/verilog/translation/VerilogTranslationPass.py @@ -4,7 +4,7 @@ # Author : Peitian Pan # Date : March 12, 2019 """Translate a PyMTL component hierarhcy into SystemVerilog source code.""" -import os, tempfile +import os, tempfile, fasteners from pymtl3 import MetadataKey from pymtl3.passes.BasePass import BasePass @@ -156,15 +156,21 @@ def traverse_hierarchy( s, m ): with open(tmp_path, "w+") as tmp_file: tmp_file.write( s.translator.hierarchy.src ) tmp_file.flush() + tmp_file.seek(0) os.fsync( tmp_file ) + lock = fasteners.InterProcessLock('translation.lock') + lock.acquire() + # `is_same` is set if there exists a file that has the same filename as # `output_file`, and that file is the same as the temporary file. if ( os.path.exists(output_file) ): is_same = verilog_cmp( tmp_file, output_file ) # Rename the temporary file to the output file. - os.replace( tmp_path, output_file ) + # os.replace( tmp_path, output_file ) + + lock.release() # Expose some attributes about the translation process. m.set_metadata( c.is_same, is_same ) From d4d0d6a8451a84e58e74871916b300d57f13671b Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 2 Nov 2023 17:32:59 -0400 Subject: [PATCH 040/101] [xdist] Support parallel Verilog co-sim --- .../import_/VerilogVerilatorImportPass.py | 41 +++++++++++-------- .../test/TranslationImport_xdist_test.py | 11 +++-- .../translation/VerilogTranslationPass.py | 13 +++--- 3 files changed, 37 insertions(+), 28 deletions(-) diff --git a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py index f0c6b2a82..3924afb80 100644 --- a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py +++ b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py @@ -6,7 +6,6 @@ """Provide a pass that imports arbitrary SystemVerilog modules.""" import copy -import fasteners import importlib import json import linecache @@ -16,6 +15,7 @@ import subprocess import sys import timeit +from fasteners import InterProcessLock from functools import reduce from importlib import reload from itertools import cycle @@ -369,30 +369,37 @@ def get_imported_object( s, m ): ph_cfg.has_clk, ph_cfg.has_reset, ph_cfg.separator ) - cache_lock = fasteners.InterProcessLock('cache.lock') - cache_lock.acquire() - try: - cached, config_file, cfg_d = s.is_cached( m, ip_cfg ) - # Dump configuration dict to config_file - with open( config_file, 'w' ) as fd: - json.dump( cfg_d, fd, indent = 4 ) - finally: - cache_lock.release() + cached, config_file, cfg_d = s.is_cached( m, ip_cfg ) if not cached: - s.lock = fasteners.InterProcessLock('verilog_import.lock') - s.lock.acquire() - try: + lock = InterProcessLock("_verilog_import_pass.lock") + lock.acquire() + + # The build could have been finished by another process after the first + # is_cached check. + cached, _, _ = s.is_cached( m, ip_cfg ) + + if not cached: + # Dump configuration dict to config_file + with open( config_file, 'w' ) as fd: + json.dump( cfg_d, fd, indent = 4 ) + + # Build the Verilated model s.create_verilator_model( m, ph_cfg, ip_cfg ) port_cdefs = s.create_verilator_c_wrapper( m, ph_cfg, ip_cfg, ports ) s.create_shared_lib( m, ph_cfg, ip_cfg ) symbols = s.create_py_wrapper( m, ph_cfg, ip_cfg, rtype, ports, port_cdefs ) - finally: - s.lock.release() + + lock.release() + else: ip_cfg.vprint(f"{ip_cfg.translated_top_module} is cached!", 2) - port_cdefs = s.create_verilator_c_wrapper( m, ph_cfg, ip_cfg, ports ) - symbols = s.create_py_wrapper( m, ph_cfg, ip_cfg, rtype, ports, port_cdefs ) + + # Re-generate the necessary data structure but don't dump to files + # because they have been cached. + if cached: + port_cdefs = s.create_verilator_c_wrapper( m, ph_cfg, ip_cfg, ports, dump=False ) + symbols = s.create_py_wrapper( m, ph_cfg, ip_cfg, rtype, ports, port_cdefs, dump=False ) imp = s.import_component( m, ph_cfg, ip_cfg, symbols ) diff --git a/pymtl3/passes/backends/verilog/test/TranslationImport_xdist_test.py b/pymtl3/passes/backends/verilog/test/TranslationImport_xdist_test.py index c60af1fdd..4d34821c7 100644 --- a/pymtl3/passes/backends/verilog/test/TranslationImport_xdist_test.py +++ b/pymtl3/passes/backends/verilog/test/TranslationImport_xdist_test.py @@ -5,7 +5,7 @@ # Date : Oct 11th, 2023 """Test if Verilated models can be simulated in parallel.""" -import pytest, multiprocessing +import os, pytest, multiprocessing from pymtl3.datatypes import Bits32 from pymtl3.passes import DefaultPassGroup @@ -13,8 +13,13 @@ from ..testcases import Bits32VRegComp +if 'CI' in os.environ: + MAX_XDIST_TEST_INSTS = 10 +else: + MAX_XDIST_TEST_INSTS = 500 + @pytest.mark.parametrize( - 'test_id', list(range(500)) + 'test_id', list(range(MAX_XDIST_TEST_INSTS)) ) def test_verilator_co_simulation( test_id ): n_cycles = 100 @@ -33,4 +38,4 @@ def test_verilator_co_simulation( test_id ): m.sim_tick() assert m.q == Bits32(test_id + i) - # m.finalize() + m.finalize() diff --git a/pymtl3/passes/backends/verilog/translation/VerilogTranslationPass.py b/pymtl3/passes/backends/verilog/translation/VerilogTranslationPass.py index 325199c59..4118c0c10 100644 --- a/pymtl3/passes/backends/verilog/translation/VerilogTranslationPass.py +++ b/pymtl3/passes/backends/verilog/translation/VerilogTranslationPass.py @@ -4,7 +4,7 @@ # Author : Peitian Pan # Date : March 12, 2019 """Translate a PyMTL component hierarhcy into SystemVerilog source code.""" -import os, tempfile, fasteners +import os, tempfile from pymtl3 import MetadataKey from pymtl3.passes.BasePass import BasePass @@ -159,18 +159,15 @@ def traverse_hierarchy( s, m ): tmp_file.seek(0) os.fsync( tmp_file ) - lock = fasteners.InterProcessLock('translation.lock') - lock.acquire() - # `is_same` is set if there exists a file that has the same filename as # `output_file`, and that file is the same as the temporary file. if ( os.path.exists(output_file) ): is_same = verilog_cmp( tmp_file, output_file ) - # Rename the temporary file to the output file. - # os.replace( tmp_path, output_file ) - - lock.release() + if not is_same: + # Rename the temporary file to the output file. os.replace() is an + # atomic operation as required by POSIX. + os.replace( tmp_path, output_file ) # Expose some attributes about the translation process. m.set_metadata( c.is_same, is_same ) From 777ea4dd215d5a5851e563a878cee7d77d3f97d5 Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 2 Nov 2023 17:40:27 -0400 Subject: [PATCH 041/101] [import] Set Verilog import to verbose --- .../backends/verilog/import_/VerilogVerilatorImportConfigs.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py index 0c1dd7924..348428a5a 100644 --- a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py +++ b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py @@ -26,8 +26,7 @@ class VerilogVerilatorImportConfigs( BasePassConfigs ): "enable" : False, # Enable verbose mode? - # "verbose" : False, - "verbose" : True, + "verbose" : False, # Trade off compilation time for fast simulation performance? # controls: vl_opt_level, vl_unroll_count From d9b4efa0671da7618c3da04cfe55e2929d70e850 Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 23 Nov 2023 00:02:08 -0500 Subject: [PATCH 042/101] [import] Remove optimization flags from g++ command --- .../import_/VerilogVerilatorImportConfigs.py | 45 ++----------------- 1 file changed, 4 insertions(+), 41 deletions(-) diff --git a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py index 348428a5a..5d30baedf 100644 --- a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py +++ b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py @@ -319,14 +319,14 @@ def create_cc_cmd( s ): # (7/9/2020): Use -O0 by default so that normally the tests are super fast and don't corrupt cffi, # but when the user gives a "fast" flag, it uses -O1. if s.fast: - c_flags = "-O1 -fno-guess-branch-probability -fno-reorder-blocks -fno-if-conversion -fno-if-conversion2 -fno-dce -fno-delayed-branch -fno-dse -fno-auto-inc-dec -fno-branch-count-reg -fno-combine-stack-adjustments -fno-cprop-registers -fno-forward-propagate -fno-inline-functions-called-once -fno-ipa-profile -fno-ipa-pure-const -fno-ipa-reference -fno-move-loop-invariants -fno-omit-frame-pointer -fno-split-wide-types -fno-tree-bit-ccp -fno-tree-ccp -fno-tree-ch -fno-tree-coalesce-vars -fno-tree-copy-prop -fno-tree-dce -fno-tree-dominator-opts -fno-tree-dse -fno-tree-fre -fno-tree-phiprop -fno-tree-pta -fno-tree-scev-cprop -fno-tree-sink -fno-tree-slsr -fno-tree-sra -fno-tree-ter -fno-tree-reassoc -fPIC -fno-gnu-unique -shared" + c_flags = "-O1" else: - c_flags = "-O0 -fno-guess-branch-probability -fno-reorder-blocks -fno-if-conversion -fno-if-conversion2 -fno-dce -fno-delayed-branch -fno-dse -fno-auto-inc-dec -fno-branch-count-reg -fno-combine-stack-adjustments -fno-cprop-registers -fno-forward-propagate -fno-inline-functions-called-once -fno-ipa-profile -fno-ipa-pure-const -fno-ipa-reference -fno-move-loop-invariants -fno-omit-frame-pointer -fno-split-wide-types -fno-tree-bit-ccp -fno-tree-ccp -fno-tree-ch -fno-tree-coalesce-vars -fno-tree-copy-prop -fno-tree-dce -fno-tree-dominator-opts -fno-tree-dse -fno-tree-fre -fno-tree-phiprop -fno-tree-pta -fno-tree-scev-cprop -fno-tree-sink -fno-tree-slsr -fno-tree-sra -fno-tree-ter -fno-tree-reassoc -fPIC -fno-gnu-unique -shared" + c_flags = "-O0" if not s.is_default("c_flags"): c_flags += f" {expand(s.c_flags)}" - c_flags += f" -std=c++11 -pthread -DVL_TIME_CONTEXT" + c_flags += f" -fPIC -shared -std=c++11 -pthread" c_include_path = " ".join("-I"+p for p in s._get_all_includes() if p) out_file = s.get_shared_lib_path() @@ -337,21 +337,6 @@ def create_cc_cmd( s ): s.vl_line_coverage or \ s.vl_toggle_coverage else "" - # PP: eliminate GNU unique symbols in the compiled shared library. - # See https://stackoverflow.com/a/76664985 - # ^ this says if a shared library has UNIQUE symbols in it a dlclose - # may become a no-op. The solution is to mark all symbols as `hidden` - # except for those we want to access in the C wrapper. Note that I've - # tried adding the g++ flag `-fvisibility=hidden` and - # __attribute__((visibility("default"))) to APIs, but the compiled - # shared library still contains UNIQUE symbols: - # % readelf -sW --dyn-syms libVRegTrace_noparam_v.so | grep '\bUNIQUE\b' - # The solution proposed in the above post is to use a linker version - # script which specifies visibility of all symbols. - linker_version_script_filename = "verilator_import_linker_version_script.lds" - dir_to_version_script = os.path.abspath(os.path.dirname(__file__)) - ld_flags += f" -Wl,--version-script={os.path.join(dir_to_version_script, linker_version_script_filename)}" - return f"g++ {c_flags} {c_include_path} {ld_flags}"\ f" -o {out_file} {c_src_files} {ld_libs} {coverage}" @@ -473,28 +458,6 @@ def _compile_vl_srcs_from_vl_class_mk( s, all_lines, path, label ): cxx_includes = " ".join(map(lambda x: "-I"+x, s._get_all_includes())) for src in srcs: file_path, obj_name = src - cxx_cmd = f"g++ {cxx_includes} -std=c++11 -DVL_TIME_CONTEXT -O1 -fPIC -pthread -lpthread -c -o {obj_name}.o {file_path}" - if os.path.exists(f"{obj_name}.o"): - s.vprint(f"Skip compiling {obj_name}.o because it already exists.") - else: - s.vprint(f"Compiling {obj_name}.o with command: {cxx_cmd}") - # Invoke CXX to compile objects - try: - subprocess.check_output(cxx_cmd, - stderr = subprocess.STDOUT, - shell = True, - universal_newlines = True) - succeeds = True - except subprocess.CalledProcessError as e: - succeeds = False - err_msg = e.output if not isinstance(e.output, bytes) else \ - e.output.decode('utf-8') - import_err_msg = \ - f"Internal error: failed to compile Verilator source files:\n"\ - f" CXX command:\n{indent(cxx_cmd, ' ')}\n\n"\ - f" CXX output:\n{indent(wrap(err_msg), ' ')}\n" - if not succeeds: - raise RuntimeError(import_err_msg) - objs.append(f"{obj_name}.o") + objs.append(file_path) return objs From 06174c302743f14edcc0ab752b403df3f404ff32 Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 23 Nov 2023 00:10:25 -0500 Subject: [PATCH 043/101] [import] Update C++/Python wrapper to avoid unloading the shared lib --- .../import_/VerilogVerilatorImportPass.py | 2 +- .../import_/verilator_wrapper_c_template.py | 140 ++++++++++-------- .../import_/verilator_wrapper_py_template.py | 68 +++++---- 3 files changed, 111 insertions(+), 99 deletions(-) diff --git a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py index 3924afb80..d9c6dc232 100644 --- a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py +++ b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py @@ -581,7 +581,7 @@ def create_py_wrapper( s, m, ph_cfg, ip_cfg, rtype, ports, port_cdefs, dump=True # External trace function definition if ip_cfg.vl_line_trace: - external_trace_c_def = f'void trace( V{ip_cfg.translated_top_module}_t *, char * );' + external_trace_c_def = f'void V{ip_cfg.translated_top_module}_line_trace( V{ip_cfg.translated_top_module}_t *, char * );' else: external_trace_c_def = '' diff --git a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py index b16039cc6..0033457e7 100644 --- a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py +++ b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py @@ -45,55 +45,49 @@ // Exposed port interface {port_defs} + // The following variables have a _cffi_ prefix to avoid name conflicts + // with the port names. + // Verilator model - void * model; - VerilatedContext * context_ptr; + void * _cffi_model; + + // Verilator simulation context + void * _cffi_context_ptr; // VCD state - int _vcd_en; + int _cffi_vcd_en; - // VCD tracing helpers - #if DUMP_VCD - void * tfp; - unsigned int trace_time; - #endif + // VCD tracing helpers. Note that these fields are not used if DUMP_VCD + // is 0. + void * _cffi_tfp; + unsigned int _cffi_trace_time; + + // Verilog line trace buffer. Refer to the comments to the trace function + // below for more details. + char _cffi_line_trace_str[512]; }} V{component_name}_t; // Exposed methods - V{component_name}_t * create_model( const char * ); - void destroy_model( V{component_name}_t *); - void comb_eval( V{component_name}_t * ); - void seq_eval( V{component_name}_t * ); - void assert_en( V{component_name}_t *, bool ); + V{component_name}_t * V{component_name}_create_model( const char * ); + void V{component_name}_destroy_model( V{component_name}_t *); + void V{component_name}_comb_eval( V{component_name}_t * ); + void V{component_name}_seq_eval( V{component_name}_t * ); + void V{component_name}_assert_en( V{component_name}_t *, bool ); #if VLINETRACE - void trace( V{component_name}_t *, char * ); + void V{component_name}_line_trace( V{component_name}_t *, char * ); #endif }} -//------------------------------------------------------------------------ -// sc_time_stamp -//------------------------------------------------------------------------ -// This is now a lgeacy function required only so linking works on Cygwin -// and MSVC++: -// https://github.com/verilator/verilator/blob/master/examples/make_tracing_c/sim_main.cpp - -double sc_time_stamp() -{{ - - return 0; - -}} - //------------------------------------------------------------------------ // create_model() //------------------------------------------------------------------------ // Construct a new verilator simulation, initialize interface signals // exposed via CFFI, and setup VCD tracing if enabled. -V{component_name}_t * create_model( const char *vcd_filename ) {{ +V{component_name}_t * V{component_name}_create_model( const char *vcd_filename ) {{ V{component_name}_t * m; V{vl_component_name} * model; @@ -105,19 +99,19 @@ context_ptr->randReset( {verilator_xinit_value} ); context_ptr->randSeed( {verilator_xinit_seed} ); - m = (V{component_name}_t *) malloc( sizeof(V{component_name}_t) ); + m = new V{component_name}_t; model = new V{vl_component_name}(context_ptr); - m->model = (void *) model; - m->context_ptr = context_ptr; + m->_cffi_model = (void *) model; + m->_cffi_context_ptr = (void *) context_ptr; // Enable tracing. We have added a feature where if the vcd_filename is // "" then we don't do any VCD dumping even if DUMP_VCD is true. - m->_vcd_en = 0; + m->_cffi_vcd_en = 0; #if DUMP_VCD if ( strlen( vcd_filename ) != 0 ) {{ - m->_vcd_en = 1; + m->_cffi_vcd_en = 1; context_ptr->traceEverOn( true ); VerilatedVcdC * tfp = new VerilatedVcdC(); @@ -125,9 +119,12 @@ tfp->spTrace()->set_time_resolution( "{vcd_timescale}" ); tfp->open( vcd_filename ); - m->tfp = (void *) tfp; - m->trace_time = 0; + m->_cffi_tfp = (void *) tfp; + m->_cffi_trace_time = 0; }} + #else + m->_cffi_tfp = NULL; + m->_cffi_trace_time = 0; #endif // initialize exposed model interface pointers @@ -142,29 +139,30 @@ //------------------------------------------------------------------------ // Finalize the Verilator simulation, close files, call destructors. -void destroy_model( V{component_name}_t * m ) {{ +void V{component_name}_destroy_model( V{component_name}_t * m ) {{ #if VM_COVERAGE VerilatedCov::write( "coverage.dat" ); #endif - V{vl_component_name} * model = (V{vl_component_name} *) m->model; + V{vl_component_name} * model = (V{vl_component_name} *) m->_cffi_model; + VerilatedContext * context_ptr = (VerilatedContext *) m->_cffi_context_ptr; // finalize verilator simulation model->final(); #if DUMP_VCD - if ( m->_vcd_en ) {{ - // printf("DESTROYING %d\\n", m->trace_time); - VerilatedVcdC * tfp = (VerilatedVcdC *) m->tfp; + if ( m->_cffi_vcd_en ) {{ + // printf("DESTROYING %d\\n", m->_cffi_trace_time); + VerilatedVcdC * tfp = (VerilatedVcdC *) m->_cffi_tfp; tfp->close(); + delete tfp; }} #endif delete model; - delete m->context_ptr; - - free(m); + delete context_ptr; + delete m; }} @@ -173,9 +171,9 @@ //------------------------------------------------------------------------ // Simulate one time-step in the Verilated model. -void comb_eval( V{component_name}_t * m ) {{ +void V{component_name}_comb_eval( V{component_name}_t * m ) {{ - V{vl_component_name} * model = (V{vl_component_name} *) m->model; + V{vl_component_name} * model = (V{vl_component_name} *) m->_cffi_model; // evaluate one time step model->eval(); @@ -183,10 +181,10 @@ // Shunning: calling dump multiple times leads to unsuppressable warning // under verilator 4.036 // #if DUMP_VCD - // if ( m->_vcd_en ) {{ + // if ( m->_cffi_vcd_en ) {{ // // dump current signal values - // VerilatedVcdC * tfp = (VerilatedVcdC *) m->tfp; - // tfp->dump( m->trace_time ); + // VerilatedVcdC * tfp = (VerilatedVcdC *) m->_cffi_tfp; + // tfp->dump( m->_cffi_trace_time ); // tfp->flush(); // }} // #endif @@ -198,10 +196,10 @@ //------------------------------------------------------------------------ // Simulate the positive clock edge in the Verilated model. -void seq_eval( V{component_name}_t * m ) {{ +void V{component_name}_seq_eval( V{component_name}_t * m ) {{ - V{vl_component_name} * model = (V{vl_component_name} *) m->model; - VerilatedContext * context_ptr = m->context_ptr; + V{vl_component_name} * model = (V{vl_component_name} *) m->_cffi_model; + VerilatedContext * context_ptr = (VerilatedContext *) m->_cffi_context_ptr; // evaluate one time cycle @@ -212,7 +210,7 @@ model->eval(); #if DUMP_VCD - if ( m->_vcd_en && (ON_DEMAND_VCD_ENABLE || !ON_DEMAND_DUMP_VCD) ) {{ + if ( m->vcd_en && (ON_DEMAND_VCD_ENABLE || !ON_DEMAND_DUMP_VCD) ) {{ // PP: this is hacky -- we want the waveform to look like all signals // except the CLK has toggled. We temporarily set the CLK signal @@ -222,13 +220,13 @@ #endif // dump current signal values - VerilatedVcdC * tfp = (VerilatedVcdC *) m->tfp; - tfp->dump( m->trace_time ); + VerilatedVcdC * tfp = (VerilatedVcdC *) m->_cffi_tfp; + tfp->dump( m->_cffi_trace_time ); tfp->flush(); // PP: now that we have generated the VCD we need to set CLK back to 0. // We need to dump VCD again to register this clock toggle. - m->trace_time += {half_cycle_time}; + m->_cffi_trace_time += {half_cycle_time}; #if HAS_CLK model->clk = 0; @@ -238,7 +236,7 @@ // signals will not toggle. model->eval(); - tfp->dump( m->trace_time ); + tfp->dump( m->_cffi_trace_time ); tfp->flush(); }} @@ -252,10 +250,10 @@ context_ptr->timeInc(1); #if DUMP_VCD - if ( m->_vcd_en && (ON_DEMAND_VCD_ENABLE || !ON_DEMAND_DUMP_VCD) ) {{ + if ( m->_cffi_vcd_en && (ON_DEMAND_VCD_ENABLE || !ON_DEMAND_DUMP_VCD) ) {{ // update simulation time only on clock toggle - m->trace_time += {half_cycle_time}; + m->_cffi_trace_time += {half_cycle_time}; }} #endif @@ -266,9 +264,11 @@ //------------------------------------------------------------------------ // Enable or disable assertions controlled by --assert -void assert_en( V{component_name}_t * m, bool en ) {{ +void V{component_name}_assert_en( V{component_name}_t * m, bool en ) {{ + + VerilatedContext * context_ptr = (VerilatedContext *) m->_cffi_context_ptr; - m->context_ptr->assertOn(en); + context_ptr->assertOn(en); }} @@ -281,9 +281,9 @@ // it everywhere. #if VLINETRACE -void trace( V{component_name}_t * m, char* str ) {{ +void V{component_name}_line_trace( V{component_name}_t * m, char* str ) {{ - V{vl_component_name} * model = (V{vl_component_name} *) m->model; + V{vl_component_name} * model = (V{vl_component_name} *) m->_cffi_model; const int nchars = 512; const int nwords = nchars/4; @@ -357,4 +357,18 @@ }} #endif + +//------------------------------------------------------------------------ +// sc_time_stamp +//------------------------------------------------------------------------ +// This is now a lgeacy function required only so linking works on Cygwin +// and MSVC++: +// https://github.com/verilator/verilator/blob/master/examples/make_tracing_c/sim_main.cpp + +double sc_time_stamp() +{{ + + return 0; + +}} ''' diff --git a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py index bd54c3c96..bb63fea5c 100644 --- a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py +++ b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py @@ -38,15 +38,28 @@ def __init__( s, *args, **kwargs ): {port_cdefs} // Verilator model - void * model; + void * _cffi_model; + + // Verilator simulation context + void * _cffi_context_ptr; + + // VCD state + int _cffi_vcd_en; + + // VCD tracing helpers + void * _cffi_tfp; + unsigned int _cffi_trace_time; + + // Verilog line trace buffer + char _cffi_line_trace_str[512]; }} V{component_name}_t; - V{component_name}_t * create_model( const char * ); - void destroy_model( V{component_name}_t *); - void comb_eval( V{component_name}_t * ); - void seq_eval( V{component_name}_t * ); - void assert_en( bool en ); + V{component_name}_t * V{component_name}_create_model( const char * ); + void V{component_name}_destroy_model( V{component_name}_t *); + void V{component_name}_comb_eval( V{component_name}_t * ); + void V{component_name}_seq_eval( V{component_name}_t * ); + void V{component_name}_assert_en( bool en ); {trace_c_def} """) @@ -58,7 +71,12 @@ def __init__( s, *args, **kwargs ): # Import the shared library containing the model. We defer # construction to the elaborate_logic function to allow the user to # set the vcd_file. - s._ffi_inst = s.ffi.dlopen('./{lib_file}') + # NOTE: the RTLD_NODELETE flag is necessary in this dlopen() to make sure + # all loaded shared libraries stick to the current processes (i.e., cannot + # be unloaded) until the exit of the main process. This behavior is necessary + # to avoid segfaults at exits due to destruction of thread-local variables, + # which are heavily used in Verilator's runtime library. + s._ffi_inst = s.ffi.dlopen('./{lib_file}', s.ffi.RTLD_NODELETE | s.ffi.RTLD_NOW) # increment instance count {component_name}.id_ += 1 @@ -86,17 +104,9 @@ def finalize( s ): s._finalization_count += 1 # Clean up python side FFI references - s.ffi.release(s._line_trace_str) - s._convert_string = None - s._ffi_inst.destroy_model( s._ffi_m ) - s.ffi.dlclose( s._ffi_inst ) - - del s._ffi_inst - del s.ffi - - gc.collect() + s._ffi_inst.V{component_name}_destroy_model( s._ffi_m ) # print("End of finalize()") def __del__( s ): @@ -105,16 +115,9 @@ def __del__( s ): s._finalization_count += 1 # Clean up python side FFI references - s.ffi.release(s._line_trace_str) s._convert_string = None - s._ffi_inst.destroy_model( s._ffi_m ) - s.ffi.dlclose( s._ffi_inst ) - - del s._ffi_inst - del s.ffi - - gc.collect() + s._ffi_inst.V{component_name}_destroy_model( s._ffi_m ) # print("End of __del__") def construct( s, *args, **kwargs ): @@ -134,20 +137,15 @@ def construct( s, *args, **kwargs ): # a variable. See more about this: # https://cffi.readthedocs.io/en/stable/ref.html#ffi-new ffi_vl_vcd_file = s.ffi.new("char[]", verilator_vcd_file) - s._ffi_m = s._ffi_inst.create_model( ffi_vl_vcd_file ) + s._ffi_m = s._ffi_inst.V{component_name}_create_model( ffi_vl_vcd_file ) # Buffer for line tracing - line_trace_str = s.ffi.new('char[512]') - s._line_trace_str = line_trace_str s._convert_string = s.ffi.string - global_weakref_dict = weakref.WeakKeyDictionary() - global_weakref_dict[s] = (line_trace_str) - # Use non-attribute varialbe to reduce CPython bytecode count _ffi_m = s._ffi_m - _ffi_inst_comb_eval = s._ffi_inst.comb_eval - _ffi_inst_seq_eval = s._ffi_inst.seq_eval + _ffi_inst_comb_eval = s._ffi_inst.V{component_name}_comb_eval + _ffi_inst_seq_eval = s._ffi_inst.V{component_name}_seq_eval # declare the port interface {port_defs} @@ -182,12 +180,12 @@ def assert_en( s, en ): # at this moment I'm not sure if the C API's are compatible between # PyPy and CPython). assert isinstance( en, bool ) - s._ffi_inst.assert_en( s._ffi_m, en ) + s._ffi_inst.V{component_name}_assert_en( s._ffi_m, en ) def line_trace( s ): if {external_trace}: - s._ffi_inst.trace( s._ffi_m, s._line_trace_str ) - return s._convert_string( s._line_trace_str ).decode('ascii') + s._ffi_inst.V{component_name}_line_trace( s._ffi_m, s._ffi_m._cffi_line_trace_str ) + return s._convert_string( s._ffi_m._cffi_line_trace_str ).decode('ascii') else: {line_trace} From 2ccab35b5ccdecd7ef621ff3369b7d349e615157 Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 23 Nov 2023 00:39:15 -0500 Subject: [PATCH 044/101] [unit-test] Use unique DUT class name to facilitate import --- .../backends/generic/testcases/test_cases.py | 128 +-- .../import_/test/ImportedObject_test.py | 27 +- .../tbgen/test/VerilogTBGenPass_test.py | 5 +- .../TranslationImport_dynlib_close_test.py | 50 +- .../backends/verilog/testcases/test_cases.py | 173 ++-- .../test/TranslationImport_adhoc_test.py | 6 +- .../backends/yosys/testcases/test_cases.py | 76 +- pymtl3/passes/testcases/test_cases.py | 774 ++++++++++++------ 8 files changed, 733 insertions(+), 506 deletions(-) diff --git a/pymtl3/passes/backends/generic/testcases/test_cases.py b/pymtl3/passes/backends/generic/testcases/test_cases.py index cad3eef6c..e65f21e52 100644 --- a/pymtl3/passes/backends/generic/testcases/test_cases.py +++ b/pymtl3/passes/backends/generic/testcases/test_cases.py @@ -74,7 +74,7 @@ 'freevars:\n', 'REF_SRC', '''\ - component DUT + component TwoUpblksSliceComp ( port_decls: port_decl: in_ Port of Vector4 @@ -110,7 +110,7 @@ ''', 'REF_SRC', '''\ - component DUT + component TwoUpblksFreevarsComp ( port_decls: port_decl: out Array[2] of Port @@ -147,7 +147,7 @@ ''', 'REF_SRC', '''\ - component DUT + component Bits32TmpWireComp ( port_decls: port_decl: in_ Port of Vector32 @@ -185,7 +185,7 @@ ''', 'REF_SRC', '''\ - component DUT + component Bits32TmpWireAliasComp ( port_decls: port_decl: in_ Port of Vector32 @@ -224,7 +224,7 @@ ''', 'REF_SRC', '''\ - component DUT + component Bits32MultiTmpWireComp ( port_decls: port_decl: in_ Port of Vector32 @@ -264,7 +264,7 @@ ''', 'REF_SRC', '''\ - component DUT + component Bits32FreeVarToTmpVarComp ( port_decls: port_decl: out Port of Vector32 @@ -300,7 +300,7 @@ ''', 'REF_SRC', '''\ - component DUT + component Bits32ConstBitsToTmpVarComp ( port_decls: port_decl: out Port of Vector32 @@ -335,7 +335,7 @@ ''', 'REF_SRC', '''\ - component DUT + component Bits32ConstIntToTmpVarComp ( port_decls: port_decl: out Port of Vector32 @@ -371,7 +371,7 @@ 'REF_SRC', '''\ struct Bits32Foo__foo_32 - component DUT + component StructTmpWireComp ( port_decls: port_decl: in_ Port of Struct Bits32Foo__foo_32 @@ -411,7 +411,7 @@ '''\ struct Bits32Foo__foo_32 struct Bits32Bar__bar_32 - component DUT + component TwoUpblksStructTmpWireComp ( port_decls: port_decl: in_bar Port of Struct Bits32Bar__bar_32 @@ -451,7 +451,7 @@ ''', 'REF_SRC', '''\ - component DUT + component Bits32IfcTmpVarOutComp ( port_decls: port_decl: out Port of Vector32 @@ -490,7 +490,7 @@ 'REF_SRC', '''\ struct Bits32Foo__foo_32 - component DUT + component StructIfcTmpVarOutComp ( port_decls: port_decl: out Port of Vector32 @@ -546,7 +546,7 @@ endcomponent - component DUT + component SubCompTmpDrivenComp ( port_decls: port_decl: out Port of Vector32 @@ -605,7 +605,7 @@ endcomponent - component DUT + component SubCompFreeVarDrivenComp ( port_decls: port_decl: out Port of Vector32 @@ -631,10 +631,10 @@ CaseComponentArgsComp = set_attributes( CaseComponentArgsComp, 'REF_NAME', - 'DUT__foo_0__bar_002a', + 'ComponentArgsComp__foo_0__bar_002a', 'REF_SRC', '''\ - component A__foo_0__bar_002a + component ComponentArgsComp__foo_0__bar_002a ( port_decls: interface_decls: @@ -653,10 +653,10 @@ CaseComponentDefaultArgsComp = set_attributes( CaseComponentDefaultArgsComp, 'REF_NAME', - 'DUT__foo_0__bar_002a', + 'ComponentDefaultArgsComp__foo_0__bar_002a', 'REF_SRC', '''\ - component A__foo_0__bar_002a + component ComponentDefaultArgsComp__foo_0__bar_002a ( port_decls: interface_decls: @@ -675,10 +675,10 @@ CaseMixedDefaultArgsComp = set_attributes( CaseMixedDefaultArgsComp, 'REF_NAME', - 'DUT__foo_0__bar_002a__woo_00000000', + 'MixedDefaultArgsComp__foo_0__bar_002a__woo_00000000', 'REF_SRC', '''\ - component A__foo_0__bar_002a__woo_00000000 + component MixedDefaultArgsComp__foo_0__bar_002a__woo_00000000 ( port_decls: interface_decls: @@ -697,7 +697,7 @@ CaseBits32PortOnly = set_attributes( CaseBits32PortOnly, 'REF_NAME', - 'DUT', + 'Bits32PortOnly', 'REF_PORT', '''\ port_decls: @@ -713,7 +713,7 @@ [ (rdt.Vector(1), 'Vector1'), (rdt.Vector(32), 'Vector32') ], 'REF_SRC', '''\ - component DUT + component Bits32PortOnly ( port_decls: port_decl: in_ Port of Vector32 @@ -733,7 +733,7 @@ CaseBits32x5PortOnly = set_attributes( CaseBits32x5PortOnly, 'REF_NAME', - 'DUT', + 'Bits32x5PortOnly', 'REF_PORT', '''\ port_decls: @@ -741,7 +741,7 @@ ''', 'REF_SRC', '''\ - component DUT + component Bits32x5PortOnly ( port_decls: port_decl: in_ Array[5] of Port @@ -761,7 +761,7 @@ CaseWiresDrivenComp = set_attributes( CaseWiresDrivenComp, 'REF_NAME', - 'DUT', + 'WiresDrivenComp', 'REF_PORT', 'port_decls:\n', 'REF_WIRE', @@ -772,7 +772,7 @@ ''', 'REF_SRC', '''\ - component DUT + component WiresDrivenComp ( port_decls: interface_decls: @@ -794,7 +794,7 @@ CaseBits32Wirex5DrivenComp = set_attributes( CaseBits32Wirex5DrivenComp, 'REF_NAME', - 'DUT', + 'Bits32Wirex5DrivenComp', 'REF_PORT', 'port_decls:\n', 'REF_WIRE', @@ -804,7 +804,7 @@ ''', 'REF_SRC', '''\ - component DUT + component Bits32Wirex5DrivenComp ( port_decls: interface_decls: @@ -825,7 +825,7 @@ CaseBits32ClosureConstruct = set_attributes( CaseBits32ClosureConstruct, 'REF_NAME', - 'DUT', + 'Bits32ClosureConstruct', 'REF_PORT', '''\ port_decls: @@ -842,7 +842,7 @@ # Note that const_decls become emtpy because the constant s.fvar_ref # was not used in any upblks! '''\ - component DUT + component Bits32ClosureConstruct ( port_decls: port_decl: out Port of Vector32 @@ -874,7 +874,7 @@ CaseBits32ArrayClosureConstruct = set_attributes( CaseBits32ArrayClosureConstruct, 'REF_NAME', - 'DUT', + 'Bits32ArrayClosureConstruct', 'REF_PORT', '''\ port_decls: @@ -886,7 +886,7 @@ 'const_decls:\n', 'REF_SRC', '''\ - component DUT + component Bits32ArrayClosureConstruct ( port_decls: port_decl: out Port of Vector32 @@ -914,7 +914,7 @@ CaseConnectBitSelToOutComp = set_attributes( CaseConnectBitSelToOutComp, 'REF_NAME', - 'DUT', + 'ConnectBitSelToOutComp', 'REF_PORT', '''\ port_decls: @@ -932,7 +932,7 @@ ''', 'REF_SRC', '''\ - component DUT + component ConnectBitSelToOutComp ( port_decls: port_decl: in_ Port of Vector32 @@ -954,7 +954,7 @@ CaseConnectSliceToOutComp = set_attributes( CaseConnectSliceToOutComp, 'REF_NAME', - 'DUT', + 'ConnectSliceToOutComp', 'REF_PORT', '''\ port_decls: @@ -972,7 +972,7 @@ ''', 'REF_SRC', '''\ - component DUT + component ConnectSliceToOutComp ( port_decls: port_decl: in_ Port of Vector32 @@ -994,7 +994,7 @@ CaseConnectPortIndexComp = set_attributes( CaseConnectPortIndexComp, 'REF_NAME', - 'DUT', + 'ConnectPortIndexComp', 'REF_PORT', '''\ port_decls: @@ -1012,7 +1012,7 @@ ''', 'REF_SRC', '''\ - component DUT + component ConnectPortIndexComp ( port_decls: port_decl: in_ Array[5] of Port @@ -1034,7 +1034,7 @@ CaseConnectInToWireComp = set_attributes( CaseConnectInToWireComp, 'REF_NAME', - 'DUT', + 'ConnectInToWireComp', 'REF_PORT', '''\ port_decls: @@ -1060,7 +1060,7 @@ ''', 'REF_SRC', '''\ - component DUT + component ConnectInToWireComp ( port_decls: port_decl: in_ Array[5] of Port @@ -1088,7 +1088,7 @@ CaseConnectConstToOutComp = set_attributes( CaseConnectConstToOutComp, 'REF_NAME', - 'DUT', + 'ConnectConstToOutComp', 'REF_PORT', '''\ port_decls: @@ -1108,7 +1108,7 @@ ''', 'REF_SRC', '''\ - component DUT + component ConnectConstToOutComp ( port_decls: port_decl: out Port of Vector32 @@ -1129,7 +1129,7 @@ CaseStructPortOnly = set_attributes( CaseStructPortOnly, 'REF_NAME', - 'DUT', + 'StructPortOnly', 'REF_PORT', '''\ port_decls: @@ -1144,7 +1144,7 @@ 'REF_SRC', '''\ struct Bits32Foo__foo_32 - component DUT + component StructPortOnly ( port_decls: port_decl: in_ Port of Struct Bits32Foo__foo_32 @@ -1166,7 +1166,7 @@ CaseStructWireDrivenComp = set_attributes( CaseStructWireDrivenComp, 'REF_NAME', - 'DUT', + 'StructWireDrivenComp', 'REF_PORT', 'port_decls:\n', 'REF_WIRE', @@ -1181,7 +1181,7 @@ 'REF_SRC', '''\ struct Bits32Foo__foo_32 - component DUT + component StructWireDrivenComp ( port_decls: interface_decls: @@ -1204,7 +1204,7 @@ CaseStructConstComp = set_attributes( CaseStructConstComp, 'REF_NAME', - 'DUT', + 'StructConstComp', 'REF_PORT', 'port_decls:\n', 'REF_WIRE', @@ -1218,7 +1218,7 @@ 'connections:\n', 'REF_SRC', '''\ - component DUT + component StructConstComp ( port_decls: interface_decls: @@ -1239,7 +1239,7 @@ CaseStructx5PortOnly = set_attributes( CaseStructx5PortOnly, 'REF_NAME', - 'DUT', + 'Structx5PortOnly', 'REF_PORT', '''\ port_decls: @@ -1254,7 +1254,7 @@ 'REF_SRC', '''\ struct Bits32Foo__foo_32 - component DUT + component Structx5PortOnly ( port_decls: port_decl: in_ Array[5] of Port @@ -1276,7 +1276,7 @@ CaseNestedStructPortOnly = set_attributes( CaseNestedStructPortOnly, 'REF_NAME', - 'DUT', + 'NestedStructPortOnly', 'REF_PORT', '''\ port_decls: @@ -1292,7 +1292,7 @@ '''\ struct Bits32Foo__foo_32 struct NestedBits32Foo__foo_Bits32Foo__foo_32 - component DUT + component NestedStructPortOnly ( port_decls: port_decl: in_ Port of Struct NestedBits32Foo__foo_Bits32Foo__foo_32 @@ -1317,7 +1317,7 @@ CaseNestedPackedArrayStructComp = set_attributes( CaseNestedPackedArrayStructComp, 'REF_NAME', - 'DUT', + 'NestedPackedArrayStructComp', 'REF_PORT', '''\ port_decls: @@ -1337,7 +1337,7 @@ '''\ struct Bits32x5Foo__foo_32x5 struct NestedStructPackedArray__foo_Bits32x5Foo__foo_32x5x5 - component DUT + component NestedPackedArrayStructComp ( port_decls: port_decl: in_ Port of Struct NestedStructPackedArray__foo_Bits32x5Foo__foo_32x5x5 @@ -1364,7 +1364,7 @@ CaseConnectValRdyIfcComp = set_attributes( CaseConnectValRdyIfcComp, 'REF_NAME', - 'DUT', + 'ConnectValRdyIfcComp', 'REF_IFC', '''\ interface_decls: @@ -1388,7 +1388,7 @@ ''', 'REF_SRC', '''\ - component DUT + component ConnectValRdyIfcComp ( port_decls: interface_decls: @@ -1420,7 +1420,7 @@ CaseArrayBits32IfcInComp = set_attributes( CaseArrayBits32IfcInComp, 'REF_NAME', - 'DUT', + 'ArrayBits32IfcInComp', 'REF_IFC', '''\ interface_decls: @@ -1435,7 +1435,7 @@ ''', 'REF_SRC', '''\ - component DUT + component ArrayBits32IfcInComp ( port_decls: port_decl: out Port of Vector32 @@ -1459,7 +1459,7 @@ CaseConnectArrayNestedIfcComp = set_attributes( CaseConnectArrayNestedIfcComp, 'REF_NAME', - 'DUT', + 'ConnectArrayNestedIfcComp', 'REF_IFC', '''\ interface_decls: @@ -1486,7 +1486,7 @@ ''', 'REF_SRC', '''\ - component DUT + component ConnectArrayNestedIfcComp ( port_decls: interface_decls: @@ -1521,7 +1521,7 @@ CaseBits32ConnectSubCompAttrComp = set_attributes( CaseBits32ConnectSubCompAttrComp, 'REF_NAME', - 'DUT', + 'Bits32ConnectSubCompAttrComp', 'REF_CONN', '''\ connections: @@ -1554,7 +1554,7 @@ endcomponent - component DUT + component Bits32ConnectSubCompAttrComp ( port_decls: port_decl: out Port of Vector32 @@ -1579,7 +1579,7 @@ CaseConnectSubCompIfcHierarchyComp = set_attributes( CaseConnectSubCompIfcHierarchyComp, 'REF_NAME', - 'DUT', + 'ConnectSubCompIfcHierarchyComp', 'REF_CONN', '''\ connections: @@ -1627,7 +1627,7 @@ endcomponent - component DUT + component ConnectSubCompIfcHierarchyComp ( port_decls: port_decl: out Port of Vector32 diff --git a/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py b/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py index d8d0291a8..7a124b788 100644 --- a/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py +++ b/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py @@ -160,7 +160,7 @@ def tv_out( m, tv ): assert m.deq_rdy == Bits1( tv[5] ) if tv[5] != '*': assert m.deq_msg == Bits32( tv[4] ) - class VQueue( Component, VerilogPlaceholder ): + class VQueueWithPorts( Component, VerilogPlaceholder ): def construct( s, data_width, num_entries, count_width ): s.count = OutPort( mk_bits( count_width ) ) s.deq_en = InPort( Bits1 ) @@ -170,8 +170,9 @@ def construct( s, data_width, num_entries, count_width ): s.enq_rdy = OutPort( Bits1 ) s.enq_msg = InPort( mk_bits( data_width ) ) s.set_metadata( VerilogPlaceholderPass.src_file, dirname(__file__)+'/VQueue.v' ) + s.set_metadata( VerilogPlaceholderPass.top_module, 'VQueue' ) num_entries = 1 - q = VQueue( + q = VQueueWithPorts( data_width = 32, num_entries = num_entries, count_width = clog2(num_entries+1)) @@ -467,25 +468,7 @@ def construct( s ): # 0xFFFFFFFF unsigned assert a.line_trace() == 'q = 4294967295' a.sim_tick() - - # PP: This is a workaround to the $sformat issue I was seeing with Verilator - # 5.016. If I enable this finalize below, the C library will segfault after - # all pytest tests have finished. Digging deeper I found this could be - # reliably eliminated/reproduced by removing/adding the `thread_local` - # specifier to the temporary string used in VL_SFORMAT_X (verilated.h). Note - # that the program finishes just fine when this thread-local string is never - # used (VL_SFORMAT_X is never invoked). My theory is this could be a - # double-free situation: somehow the dynamically allocated memory that - # belongs to this thread-local string gets deleted twice: once when we unload - # the dynamic library (through a.finalize()) and once at the end of program - # execution. - # - # One stackoverflow post suggests this might be a compiler bug: - # https://stackoverflow.com/a/58366171 - # I tried recompiling with the latest g++ on BRG server (scl-11) but the - # segfault persists. - - # a.finalize() + a.finalize() def test_reg_infer_external_trace( do_test ): # Test Verilog line trace @@ -516,7 +499,7 @@ def construct( s ): # 0xFFFFFFFF unsigned assert a.line_trace() == 'q = 4294967295' a.sim_tick() - # a.finalize() + a.finalize() def test_incr_on_demand_vcd( do_test ): class VIncr( Component, VerilogPlaceholder ): diff --git a/pymtl3/passes/backends/verilog/tbgen/test/VerilogTBGenPass_test.py b/pymtl3/passes/backends/verilog/tbgen/test/VerilogTBGenPass_test.py index 0937a05e8..ffab1548d 100644 --- a/pymtl3/passes/backends/verilog/tbgen/test/VerilogTBGenPass_test.py +++ b/pymtl3/passes/backends/verilog/tbgen/test/VerilogTBGenPass_test.py @@ -39,7 +39,7 @@ def tv_out( m, tv ): if tv[2] != '*': assert m.enq_rdy == tv[2] if tv[4] != '*': assert m.deq_rdy == tv[5] if tv[5] != '*': assert m.deq_msg == tv[4] - class VQueue( Component, Placeholder ): + class VQueueWithPorts( Component, Placeholder ): def construct( s, data_width, num_entries, count_width ): s.count = OutPort( mk_bits( count_width ) ) s.deq_en = InPort( Bits1 ) @@ -49,9 +49,10 @@ def construct( s, data_width, num_entries, count_width ): s.enq_rdy = OutPort( Bits1 ) s.enq_msg = InPort( mk_bits( data_width ) ) s.set_metadata( VerilogPlaceholderPass.src_file, dirname(__file__)+'/VQueue.v' ) + s.set_metadata( VerilogPlaceholderPass.top_module, 'VQueue' ) s.set_metadata( VerilogTranslationImportPass.enable, True ) num_entries = 1 - q = VQueue( + q = VQueueWithPorts( data_width = 32, num_entries = num_entries, count_width = clog2(num_entries+1)) diff --git a/pymtl3/passes/backends/verilog/test/TranslationImport_dynlib_close_test.py b/pymtl3/passes/backends/verilog/test/TranslationImport_dynlib_close_test.py index 28387673e..0dbb6f73b 100644 --- a/pymtl3/passes/backends/verilog/test/TranslationImport_dynlib_close_test.py +++ b/pymtl3/passes/backends/verilog/test/TranslationImport_dynlib_close_test.py @@ -23,55 +23,53 @@ def run_test( _m ): sim.run_test() def test_dynlib_close(): - class Comb: - class A( Component ): - def construct( s ): - s.in_ = InPort( Bits32 ) - s.out = OutPort( Bits32 ) - @update - def upblk(): - s.out @= s.in_ - class Seq: - class A( Component ): - def construct( s ): - s.in_ = InPort( Bits32 ) - s.out = OutPort( Bits32 ) - @update_ff - def upblk(): - s.out <<= s.in_ + class TestDynlibCloseComb( Component ): + def construct( s ): + s.in_ = InPort( Bits32 ) + s.out = OutPort( Bits32 ) + @update + def upblk(): + s.out @= s.in_ + class TestDynlibCloseSeq( Component ): + def construct( s ): + s.in_ = InPort( Bits32 ) + s.out = OutPort( Bits32 ) + @update_ff + def upblk(): + s.out <<= s.in_ - comb_a = Comb.A() + comb = TestDynlibCloseComb() # TestVectorSimulator properties def tv_in( m, tv ): m.in_ @= Bits32(tv[0]) def tv_out( m, tv ): assert m.out == Bits32(tv[1]) - comb_a._tvs = [ + comb._tvs = [ [ 1, 1 ], [ 42, 42 ], [ 24, 24 ], [ -2, -2 ], [ -1, -1 ], ] - comb_a._tv_in, comb_a._tv_out = tv_in, tv_out - run_test( comb_a ) - comb_hash = get_file_hash('A_noparam.verilator1.vcd') + comb._tv_in, comb._tv_out = tv_in, tv_out + run_test( comb ) + comb_hash = get_file_hash('TestDynlibCloseComb_noparam.verilator1.vcd') - seq_a = Seq.A() + seq = TestDynlibCloseSeq() def tv_in( m, tv ): m.in_ @= Bits32(tv[0]) def tv_out( m, tv ): if tv[1] != '*': assert m.out == Bits32(tv[1]) - seq_a._tvs = [ + seq._tvs = [ [ 1, '*' ], [ 42, 1 ], [ 24, 42 ], [ -2, 24 ], [ -1, -2 ], ] - seq_a._tv_in, seq_a._tv_out = tv_in, tv_out - run_test( seq_a ) - seq_hash = get_file_hash('A_noparam.verilator1.vcd') + seq._tv_in, seq._tv_out = tv_in, tv_out + run_test( seq ) + seq_hash = get_file_hash('TestDynlibCloseSeq_noparam.verilator1.vcd') assert comb_hash != seq_hash diff --git a/pymtl3/passes/backends/verilog/testcases/test_cases.py b/pymtl3/passes/backends/verilog/testcases/test_cases.py index 72fcdf262..2864b1ff8 100644 --- a/pymtl3/passes/backends/verilog/testcases/test_cases.py +++ b/pymtl3/passes/backends/verilog/testcases/test_cases.py @@ -128,7 +128,7 @@ class CasePlaceholderTranslationVReg: ] class CasePlaceholderTranslationRegIncr: - class DUT( Component ): + class PlaceholderTranslationRegIncr( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits32 ) @@ -147,9 +147,10 @@ def construct( s ): [ -1, 43 ], [ 0, 0 ], ] + DUT = PlaceholderTranslationRegIncr class CaseVIncludePopulation: - class DUT( Component ): + class VIncludePopulation( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits32 ) @@ -168,9 +169,10 @@ def construct( s ): [ -1, 42 ], [ 0, -1 ], ] + DUT = VIncludePopulation class CaseVLibsTranslation: - class DUT( VerilogPlaceholder, Component ): + class VLibsTranslation( VerilogPlaceholder, Component ): def construct( s ): s.d = InPort( Bits32 ) s.q = OutPort( Bits32 ) @@ -189,9 +191,10 @@ def construct( s ): [ -1, 42 ], [ 0, -1 ], ] + DUT = VLibsTranslation class CaseMultiPlaceholderImport: - class DUT( Component ): + class MultiPlaceholderImport( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits32 ) @@ -211,27 +214,7 @@ def construct( s ): [ 0, 42 ], [ 0, -1 ], ] - -class CasePlaceholderTranslationRegIncr: - class DUT( Component ): - def construct( s ): - s.in_ = InPort( Bits32 ) - s.out = OutPort( Bits32 ) - s.reg_ = Bits32VRegComp() - s.reg_.d //= s.in_ - s.out //= lambda: s.reg_.q + Bits32(1) - TV_IN = \ - _set( 'in_', Bits32, 0 ) - TV_OUT = \ - _check( 'out', Bits32, 1 ) - TV = \ - [ - [ 1, 1 ], - [ 2, 2 ], - [ 42, 3 ], - [ -1, 43 ], - [ 0, 0 ], - ] + DUT = MultiPlaceholderImport CaseSizeCastPaddingStructPort = set_attributes( CaseSizeCastPaddingStructPort, 'REF_UPBLK', @@ -246,7 +229,7 @@ def construct( s ): logic [31:0] foo; } Bits32Foo__foo_32; - module DUT_noparam + module SizeCastPaddingStructPort_noparam ( input logic [0:0] clk, input Bits32Foo__foo_32 in_, @@ -271,7 +254,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module PassThroughComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -296,7 +279,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module SequentialPassThroughComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -329,7 +312,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT__2ae3b6d12e9855d1 + module ConnectPassThroughLongNameComp__2ae3b6d12e9855d1 ( input logic [0:0] clk, input logic [31:0] in_, @@ -355,7 +338,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module LambdaConnectComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -380,7 +363,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module LambdaConnectWithListComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -405,7 +388,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module Bits32x2ConcatComp_noparam ( input logic [0:0] clk, input logic [31:0] in_1, @@ -431,7 +414,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module Bits32x2ConcatConstComp_noparam ( input logic [0:0] clk, output logic [63:0] out, @@ -455,7 +438,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module Bits32x2ConcatMixedComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -480,7 +463,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module Bits64SextInComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -505,7 +488,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module Bits64ZextInComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -534,7 +517,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module Bits64TruncInComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -559,7 +542,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module Bits32x2ConcatFreeVarComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -585,7 +568,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module Bits32x2ConcatUnpackedSignalComp_noparam ( input logic [0:0] clk, input logic [31:0] in_ [0:1], @@ -610,7 +593,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module Bits32BitSelUpblkComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -635,7 +618,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module Bits64PartSelUpblkComp_noparam ( input logic [0:0] clk, input logic [63:0] in_, @@ -672,7 +655,7 @@ def construct( s ): logic [31:0] b_bar; } ST__b_foo_16__b_bar_32; - module DUT_noparam + module StructUnique_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -702,7 +685,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module PythonClassAttr_noparam ( input logic [0:0] clk, output logic [31:0] out1, @@ -734,7 +717,7 @@ def construct( s ): logic [31:0] foo; } Bits32Foo__foo_32; - module DUT_noparam + module TypeBundle_noparam ( input logic [0:0] clk, output logic [31:0] out1, @@ -765,7 +748,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module TmpVarInUpdateffComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -799,7 +782,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module BoolTmpVarComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -830,7 +813,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module DefaultBitsComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -859,7 +842,7 @@ def construct( s ): logic [31:0] foo; } Bits32Foo__foo_32; - module DUT_noparam + module Bits32FooToBits32Comp_noparam ( input logic [0:0] clk, input Bits32Foo__foo_32 in_, @@ -888,7 +871,7 @@ def construct( s ): logic [31:0] foo; } Bits32Foo__foo_32; - module DUT_noparam + module Bits32ToBits32FooComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -917,7 +900,7 @@ def construct( s ): logic [31:0] foo; } Bits32Foo__foo_32; - module DUT_noparam + module IntToBits32FooComp_noparam ( input logic [0:0] clk, output Bits32Foo__foo_32 out, @@ -941,7 +924,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module ReducesInx3OutComp_noparam ( input logic [0:0] clk, input logic [31:0] in_1, @@ -972,7 +955,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module IfBasicComp_noparam ( input logic [0:0] clk, input logic [15:0] in_, @@ -1007,7 +990,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module IfDanglingElseInnerComp_noparam ( input logic [0:0] clk, input logic [31:0] in_1, @@ -1045,7 +1028,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module IfDanglingElseOutterComp_noparam ( input logic [0:0] clk, input logic [31:0] in_1, @@ -1084,7 +1067,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module ElifBranchComp_noparam ( input logic [0:0] clk, input logic [31:0] in_1, @@ -1136,7 +1119,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module NestedIfComp_noparam ( input logic [0:0] clk, input logic [31:0] in_1, @@ -1183,7 +1166,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module ForLoopEmptySequenceComp_noparam ( input logic [0:0] clk, output logic [3:0] out, @@ -1212,7 +1195,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module ForRangeLowerUpperStepPassThroughComp_noparam ( input logic [0:0] clk, input logic [31:0] in_ [0:4], @@ -1240,7 +1223,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module IfExpBothImplicitComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -1266,7 +1249,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module IfExpInForStmtComp_noparam ( input logic [0:0] clk, input logic [31:0] in_ [0:4], @@ -1293,7 +1276,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module IfExpUnaryOpInForStmtComp_noparam ( input logic [0:0] clk, input logic [31:0] in_ [0:4], @@ -1324,7 +1307,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module IfBoolOpInForStmtComp_noparam ( input logic [0:0] clk, input logic [31:0] in_ [0:4], @@ -1361,7 +1344,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module IfTmpVarInForStmtComp_noparam ( input logic [0:0] clk, input logic [31:0] in_ [0:4], @@ -1394,7 +1377,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module InterfaceArrayNonStaticIndexComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -1420,7 +1403,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module FixedSizeSliceComp_noparam ( input logic [0:0] clk, input logic [15:0] in_, @@ -1450,7 +1433,7 @@ def construct( s ): logic [31:0] foo; } Bits32Foo__foo_32; - module DUT_noparam + module Bits32FooInBits32OutComp_noparam ( input logic [0:0] clk, input Bits32Foo__foo_32 in_, @@ -1475,7 +1458,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module ConstStructInstComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -1503,7 +1486,7 @@ def construct( s ): logic [4:0][31:0] foo; } Bits32x5Foo__foo_32x5; - module DUT_noparam + module StructPackedArrayUpblkComp_noparam ( input logic [0:0] clk, input Bits32x5Foo__foo_32x5 in_, @@ -1538,7 +1521,7 @@ def construct( s ): Bits32Foo__foo_32 woo; } NestedStructPackedPlusScalar__c467caf2a4dfbfb2; - module DUT_noparam + module NestedStructPackedArrayUpblkComp_noparam ( input logic [0:0] clk, input NestedStructPackedPlusScalar__c467caf2a4dfbfb2 in_, @@ -1565,7 +1548,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module ConnectValRdyIfcUpblkComp_noparam ( input logic [0:0] clk, input logic [0:0] reset, @@ -1596,7 +1579,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module ArrayBits32IfcInUpblkComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -1632,7 +1615,7 @@ def construct( s ): endmodule - module DUT_noparam + module Bits32SubCompAttrUpblkComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -1680,7 +1663,7 @@ def construct( s ): endmodule - module DUT_noparam + module Bits32ArraySubCompAttrUpblkComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -1770,7 +1753,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module ConnectInToWireComp_noparam ( input logic [0:0] clk, input logic [31:0] in_ [0:4], @@ -1807,7 +1790,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module ConnectBitsConstToOutComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -1840,7 +1823,7 @@ def construct( s ): 'REF_SRC', # const_ is not used in upblks so will be optimized away '''\ - module DUT_noparam + module ConnectConstToOutComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -1871,7 +1854,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module ConnectBitSelToOutComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -1903,7 +1886,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module ConnectSliceToOutComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -1935,7 +1918,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module BitSelOverBitSelComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -1967,7 +1950,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module BitSelOverPartSelComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -1999,7 +1982,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module PartSelOverBitSelComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -2031,7 +2014,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module PartSelOverPartSelComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -2071,7 +2054,7 @@ def construct( s ): # struct definition will be eliminated because it's not used # in an upblk '''\ - module DUT_noparam + module ConnectConstStructAttrToOutComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -2124,7 +2107,7 @@ def construct( s ): Bits32Foo__foo_32 woo; } NestedStructPackedPlusScalar__c467caf2a4dfbfb2; - module DUT_noparam + module ConnectLiteralStructComp_noparam ( input logic [0:0] clk, output NestedStructPackedPlusScalar__c467caf2a4dfbfb2 out, @@ -2166,7 +2149,7 @@ def construct( s ): logic [4:0][31:0] foo; } Bits32x5Foo__foo_32x5; - module DUT_noparam + module ConnectArrayStructAttrToOutComp_noparam ( input logic [0:0] clk, input Bits32x5Foo__foo_32x5 in_, @@ -2223,7 +2206,7 @@ def construct( s ): Bits32Foo__foo_32 woo; } NestedStructPackedPlusScalar__c467caf2a4dfbfb2; - module DUT_noparam + module ConnectNestedStructPackedArrayComp_noparam ( input logic [0:0] clk, input NestedStructPackedPlusScalar__c467caf2a4dfbfb2 in_, @@ -2257,7 +2240,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module ConnectValRdyIfcComp_noparam ( input logic [0:0] clk, input logic [0:0] reset, @@ -2294,7 +2277,7 @@ def construct( s ): logic [31:0] foo; } Bits32Foo__foo_32; - module DUT_noparam + module ConnectArrayBits32FooIfcComp_noparam ( input logic [0:0] clk, input logic [0:0] reset, @@ -2335,7 +2318,7 @@ def construct( s ): ''', 'REF_SRC', '''\ - module DUT_noparam + module ConnectArrayNestedIfcComp_noparam ( input logic [0:0] clk, input logic [0:0] reset, @@ -2389,7 +2372,7 @@ def construct( s ): endmodule - module DUT_noparam + module Bits32ConnectSubCompAttrComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -2448,7 +2431,7 @@ def construct( s ): endmodule - module DUT_noparam + module ConnectArraySubCompArrayStructIfcComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -2533,7 +2516,7 @@ def construct( s ): endmodule - module DUT_noparam + module Bits32ArrayConnectSubCompAttrComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -2758,7 +2741,7 @@ def construct( s ): end endmodule - module DUT_noparam + module HeteroCompArrayComp_noparam ( input logic [0:0] clk, input logic [31:0] in_1, @@ -2845,7 +2828,7 @@ def construct( s ): endmodule - module DUT_noparam + module BehavioralArraySubCompArrayStructIfcComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -2902,7 +2885,7 @@ def construct( s ): logic [31:0] foo ; } Bits32Foo__foo_32; - module DUT_noparam + module Bits32FooNoArgBehavioralComp_noparam ( input logic [0:0] clk, output Bits32Foo__foo_32 out, diff --git a/pymtl3/passes/backends/yosys/test/TranslationImport_adhoc_test.py b/pymtl3/passes/backends/yosys/test/TranslationImport_adhoc_test.py index 52723ecb2..6f97411d6 100644 --- a/pymtl3/passes/backends/yosys/test/TranslationImport_adhoc_test.py +++ b/pymtl3/passes/backends/yosys/test/TranslationImport_adhoc_test.py @@ -40,10 +40,14 @@ from ..YosysTranslationImportPass import YosysTranslationImportPass XFAILED_TESTS = [ - # incoherent translation result of Yosys backend + # Incoherent translation result of Yosys backend # the translated design is correct after minor transformation # in logic synthesis, but verilator simulation is incorrect 'CaseConnectLiteralStructComp', + # Negative-positive MULTIDRIVEN warning from Verilator: the translated + # module is functionally correct and the reported multiple drivers + # are actually the same signal. + 'CaseConnectArrayBits32FooIfcComp', ] def run_test( case ): diff --git a/pymtl3/passes/backends/yosys/testcases/test_cases.py b/pymtl3/passes/backends/yosys/testcases/test_cases.py index a1a126e13..4c7aefff8 100644 --- a/pymtl3/passes/backends/yosys/testcases/test_cases.py +++ b/pymtl3/passes/backends/yosys/testcases/test_cases.py @@ -89,7 +89,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module SizeCastPaddingStructPort_noparam ( input logic [0:0] clk, input logic [31:0] in___foo, @@ -117,7 +117,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module LambdaConnectWithListComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -147,7 +147,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module Bits32x2ConcatFreeVarComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -166,7 +166,7 @@ CaseBits32x2ConcatUnpackedSignalComp = set_attributes( CaseBits32x2ConcatUnpackedSignalComp, 'REF_SRC', '''\ - module DUT_noparam + module Bits32x2ConcatUnpackedSignalComp_noparam ( input logic [0:0] clk, input logic [31:0] in___0, @@ -190,7 +190,7 @@ CaseConnectConstToOutComp = set_attributes( CaseConnectConstToOutComp, 'REF_SRC', '''\ - module DUT_noparam + module ConnectConstToOutComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -217,7 +217,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module ForRangeLowerUpperStepPassThroughComp_noparam ( input logic [0:0] clk, input logic [31:0] in___0, @@ -271,7 +271,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module IfExpInForStmtComp_noparam ( input logic [0:0] clk, input logic [31:0] in___0, @@ -314,7 +314,7 @@ CaseIfExpUnaryOpInForStmtComp = set_attributes( CaseIfExpUnaryOpInForStmtComp, 'REF_SRC', '''\ - module DUT_noparam + module IfExpUnaryOpInForStmtComp_noparam ( input logic [0:0] clk, input logic [31:0] in___0, @@ -370,7 +370,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module IfBoolOpInForStmtComp_noparam ( input logic [0:0] clk, input logic [31:0] in___0, @@ -434,7 +434,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module IfTmpVarInForStmtComp_noparam ( input logic [0:0] clk, input logic [31:0] in___0, @@ -497,7 +497,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module FixedSizeSliceComp_noparam ( input logic [0:0] clk, input logic [15:0] in_, @@ -532,7 +532,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module Bits32FooInBits32OutComp_noparam ( input logic [0:0] clk, input logic [31:0] in___foo, @@ -560,7 +560,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module ConstStructInstComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -584,7 +584,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module StructPackedArrayUpblkComp_noparam ( input logic [0:0] clk, input logic [31:0] in___foo__0, @@ -628,7 +628,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module NestedStructPackedArrayUpblkComp_noparam ( input logic [0:0] clk, input logic [31:0] in___foo, @@ -669,7 +669,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module ArrayBits32IfcInUpblkComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -707,7 +707,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module InterfaceArrayNonStaticIndexComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -751,7 +751,7 @@ endmodule - module DUT_noparam + module Bits32ArraySubCompAttrUpblkComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -894,7 +894,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module ConnectInToWireComp_noparam ( input logic [0:0] clk, input logic [31:0] in___0, @@ -950,7 +950,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module ConnectBitsConstToOutComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -986,7 +986,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module ConnectConstToOutComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -1023,7 +1023,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module ConnectBitSelToOutComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -1061,7 +1061,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module ConnectSliceToOutComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -1098,7 +1098,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module ConnectConstStructAttrToOutComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -1147,7 +1147,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module ConnectLiteralStructComp_noparam ( input logic [0:0] clk, output logic [31:0] out__foo, @@ -1214,7 +1214,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module ConnectArrayStructAttrToOutComp_noparam ( input logic [0:0] clk, input logic [31:0] in___foo__0, @@ -1284,7 +1284,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module ConnectNestedStructPackedArrayComp_noparam ( input logic [0:0] clk, input logic [31:0] in___foo, @@ -1395,7 +1395,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module ConnectValRdyIfcComp_noparam ( input logic [0:0] clk, input logic [0:0] reset, @@ -1449,7 +1449,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module ConnectArrayBits32FooIfcComp_noparam ( input logic [0:0] clk, input logic [0:0] reset, @@ -1543,7 +1543,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module ConnectArrayNestedIfcComp_noparam ( input logic [0:0] clk, input logic [0:0] reset, @@ -1638,7 +1638,7 @@ endmodule - module DUT_noparam + module Bits32ConnectSubCompAttrComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -1717,7 +1717,7 @@ endmodule - module DUT_noparam + module ConnectArraySubCompArrayStructIfcComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -1858,7 +1858,7 @@ endmodule - module DUT_noparam + module Bits32ArrayConnectSubCompAttrComp_noparam ( input logic [0:0] clk, output logic [31:0] out, @@ -1990,7 +1990,7 @@ endmodule - module DUT_noparam + module BehavioralArraySubCompArrayStructIfcComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -2070,7 +2070,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module TypeBundle_noparam ( input logic [0:0] clk, output logic [31:0] out1, @@ -2104,7 +2104,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module Bits32FooToBits32Comp_noparam ( input logic [0:0] clk, input logic [31:0] in___foo, @@ -2133,7 +2133,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module Bits32ToBits32FooComp_noparam ( input logic [0:0] clk, input logic [31:0] in_, @@ -2162,7 +2162,7 @@ ''', 'REF_SRC', '''\ - module DUT_noparam + module IntToBits32FooComp_noparam ( input logic [0:0] clk, output logic [31:0] out__foo, diff --git a/pymtl3/passes/testcases/test_cases.py b/pymtl3/passes/testcases/test_cases.py index 1d8fb492f..fd5603399 100644 --- a/pymtl3/passes/testcases/test_cases.py +++ b/pymtl3/passes/testcases/test_cases.py @@ -279,67 +279,79 @@ def construct( s ): #------------------------------------------------------------------------- class CaseBits32PortOnly: - class DUT( Component ): + class Bits32PortOnly( Component ): def construct( s ): s.in_ = InPort( Bits32 ) + DUT = Bits32PortOnly class CaseStructPortOnly: - class DUT( Component ): + class StructPortOnly( Component ): def construct( s ): s.in_ = InPort( Bits32Foo ) + DUT = StructPortOnly class CaseNestedStructPortOnly: - class DUT( Component ): + class NestedStructPortOnly( Component ): def construct( s ): s.in_ = InPort( NestedBits32Foo ) + DUT = NestedStructPortOnly class CasePackedArrayStructPortOnly: - class DUT( Component ): + class PackedArrayStructPortOnly( Component ): def construct( s ): s.in_ = InPort( Bits32x5Foo ) + DUT = PackedArrayStructPortOnly class CaseBits32x5PortOnly: - class DUT( Component ): + class Bits32x5PortOnly( Component ): def construct( s ): s.in_ = [ InPort( Bits32 ) for _ in range(5) ] + DUT = Bits32x5PortOnly class CaseBits32x5WireOnly: - class DUT( Component ): + class Bits32x5WireOnly( Component ): def construct( s ): s.in_ = [ Wire( Bits32 ) for _ in range(5) ] + DUT = Bits32x5WireOnly class CaseStructx5PortOnly: - class DUT( Component ): + class Structx5PortOnly( Component ): def construct( s ): s.in_ = [ InPort( Bits32Foo ) for _ in range(5) ] + DUT = Structx5PortOnly class CaseBits32x5ConstOnly: - class DUT( Component ): + class Bits32x5ConstOnly( Component ): def construct( s ): s.in_ = [ Bits32(42) for _ in range(5) ] + DUT = Bits32x5ConstOnly class CaseBits32MsgRdyIfcOnly: - class DUT( Component ): + class Bits32MsgRdyIfcOnly( Component ): def construct( s ): s.in_ = [ Bits32MsgRdyIfc() for _ in range(5) ] + DUT = Bits32MsgRdyIfcOnly class CaseBits32InOutx5CompOnly: - class DUT( Component ): + class Bits32InOutx5CompOnly( Component ): def construct( s ): s.b = [ Bits32InOutComp() for _ in range(5) ] + DUT = Bits32InOutx5CompOnly class CaseBits32Outx3x2x1PortOnly: - class DUT( Component ): + class Bits32Outx3x2x1PortOnly( Component ): def construct( s ): s.out = [[[OutPort(Bits32) for _ in range(1)] for _ in range(2)] for _ in range(3)] + DUT = Bits32Outx3x2x1PortOnly class CaseBits32WireIfcOnly: - class DUT( Component ): + class Bits32WireIfcOnly( Component ): def construct( s ): s.in_ = Bits32FooWireBarInIfc() + DUT = Bits32WireIfcOnly class CaseBits32ClosureConstruct: - class DUT( Component ): + class Bits32ClosureConstruct( Component ): def construct( s ): foo = Bits32( 42 ) s.fvar_ref = foo @@ -347,26 +359,29 @@ def construct( s ): @update def upblk(): s.out @= foo + DUT = Bits32ClosureConstruct class CaseBits32ArrayClosureConstruct: - class DUT( Component ): + class Bits32ArrayClosureConstruct( Component ): def construct( s ): foo = [ Bits32(42) for _ in range(5) ] s.out = OutPort( Bits32 ) @update def upblk(): s.out @= foo[2] + DUT = Bits32ArrayClosureConstruct class CaseBits32ClosureGlobal: - class DUT( Component ): + class Bits32ClosureGlobal( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): s.out @= pymtl_Bits_global_freevar + DUT = Bits32ClosureGlobal class CaseStructClosureGlobal: - class DUT( Component ): + class StructClosureGlobal( Component ): def construct( s ): foo = InPort( Bits32Foo ) s._foo = foo @@ -374,9 +389,10 @@ def construct( s ): @update def upblk(): s.out @= foo.foo + DUT = StructClosureGlobal class CaseStructUnique: - class DUT( Component ): + class StructUnique( Component ): def construct( s ): s.out = OutPort( Bits32 ) s.ST_A_wire = Wire( StructUniqueA.ST ) @@ -394,9 +410,10 @@ def upblk(): [ 6 ], [ 6 ], ] + DUT = StructUnique class CasePythonClassAttr: - class DUT( Component ): + class PythonClassAttr( Component ): def construct( s ): class Enum: int_attr = 42 @@ -418,9 +435,10 @@ def upblk(): [ 42, 1, ], [ 42, 1, ], ] + DUT = PythonClassAttr class CaseTypeBundle: - class DUT( Component ): + class TypeBundle( Component ): def construct( s ): class TypeBundle: BitsType = Bits32 @@ -445,9 +463,10 @@ def upblk(): [ 42, 1, 1, ], [ 42, 1, 1, ], ] + DUT = TypeBundle class CaseBoolTmpVarComp: - class DUT( Component ): + class BoolTmpVarComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits32 ) @@ -472,9 +491,10 @@ def upblk(): [ 1, 0, ], [ 0, 1, ], ] + DUT = BoolTmpVarComp class CaseTmpVarInUpdateffComp: - class DUT( Component ): + class TmpVarInUpdateffComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update_ff @@ -493,9 +513,10 @@ def upblk(): [ 42, ], [ 42, ], ] + DUT = TmpVarInUpdateffComp class CaseBits32FooToBits32Comp: - class DUT( Component ): + class Bits32FooToBits32Comp( Component ): def construct( s ): s.in_ = InPort( Bits32Foo ) s.out = OutPort( Bits32 ) @@ -516,9 +537,10 @@ def upblk(): [ 42, 42, ], [ -42, -42, ], ] + DUT = Bits32FooToBits32Comp class CaseBits32ToBits32FooComp: - class DUT( Component ): + class Bits32ToBits32FooComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits32Foo ) @@ -539,9 +561,10 @@ def upblk(): [ 42, 42, ], [ -42, -42, ], ] + DUT = Bits32ToBits32FooComp class CaseIntToBits32FooComp: - class DUT( Component ): + class IntToBits32FooComp( Component ): def construct( s ): s.out = OutPort( Bits32Foo ) @update @@ -558,9 +581,10 @@ def upblk(): [ 42 ], [ 42 ], ] + DUT = IntToBits32FooComp class CaseReducesInx3OutComp: - class DUT( Component ): + class ReducesInx3OutComp( Component ): def construct( s ): s.in_1 = InPort( Bits32 ) s.in_2 = InPort( Bits32 ) @@ -584,9 +608,10 @@ def v_reduce(): [ 9, 8, 7, 1 ], [ 9, 8, 0, 0 ], ] + DUT = ReducesInx3OutComp class CaseBits16IndexBasicComp: - class DUT( Component ): + class Bits16IndexBasicComp( Component ): def construct( s ): s.in_ = [ InPort( Bits16 ) for _ in range( 4 ) ] s.out = [ OutPort( Bits16 ) for _ in range( 2 ) ] @@ -594,18 +619,20 @@ def construct( s ): def index_basic(): s.out[ 0 ] @= s.in_[ 0 ] + s.in_[ 1 ] s.out[ 1 ] @= s.in_[ 2 ] + s.in_[ 3 ] + DUT = Bits16IndexBasicComp class CaseBits8Bits16MismatchAssignComp: - class DUT( Component ): + class Bits8Bits16MismatchAssignComp( Component ): def construct( s ): s.in_ = InPort( Bits16 ) s.out = OutPort( Bits8 ) @update def mismatch_width_assign(): s.out @= s.in_ + DUT = Bits8Bits16MismatchAssignComp class CaseBits32Bits64SlicingBasicComp: - class DUT( Component ): + class Bits32Bits64SlicingBasicComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits64 ) @@ -613,18 +640,20 @@ def construct( s ): def slicing_basic(): s.out[ 0:16 ] @= s.in_[ 16:32 ] s.out[ 16:32 ] @= s.in_[ 0:16 ] + DUT = Bits32Bits64SlicingBasicComp class CaseBits16ConstAddComp: - class DUT( Component ): + class Bits16ConstAddComp( Component ): def construct( s ): s.in_ = InPort( Bits16 ) s.out = OutPort( Bits16 ) @update def bits_basic(): s.out @= s.in_ + Bits16( 10 ) + DUT = Bits16ConstAddComp class CaseSlicingOverIndexComp: - class DUT( Component ): + class SlicingOverIndexComp( Component ): def construct( s ): s.in_ = [ InPort( Bits16 ) for _ in range( 10 ) ] s.out = [ OutPort( Bits16 ) for _ in range( 5 ) ] @@ -632,9 +661,10 @@ def construct( s ): def index_bits_slicing(): s.out[0][0:8] @= s.in_[1][8:16] + s.in_[2][0:8] + 10 s.out[1] @= s.in_[3][0:16] + s.in_[4] + 1 + DUT = SlicingOverIndexComp class CaseSubCompAddComp: - class DUT( Component ): + class SubCompAddComp( Component ): def construct( s ): s.in_ = InPort( Bits16 ) s.out = OutPort( Bits16 ) @@ -644,9 +674,10 @@ def construct( s ): @update def multi_components_A(): s.out @= s.in_ + s.b.out + DUT = SubCompAddComp class CaseIfBasicComp: - class DUT( Component ): + class IfBasicComp( Component ): def construct( s ): s.in_ = InPort( Bits16 ) s.out = OutPort( Bits8 ) @@ -669,9 +700,10 @@ def if_basic(): [ 42, 0, ], [ 0, 0, ], ] + DUT = IfBasicComp class CaseIfDanglingElseInnerComp: - class DUT( Component ): + class IfDanglingElseInnerComp( Component ): def construct( s ): s.in_1 = InPort( Bits32 ) s.in_2 = InPort( Bits32 ) @@ -698,9 +730,10 @@ def upblk(): [ -2, 24, 24 ], [ -1, -2, -2 ], ] + DUT = IfDanglingElseInnerComp class CaseIfDanglingElseOutterComp: - class DUT( Component ): + class IfDanglingElseOutterComp( Component ): def construct( s ): s.in_1 = InPort( Bits32 ) s.in_2 = InPort( Bits32 ) @@ -727,9 +760,10 @@ def upblk(): [ -2, 24, 0 ], [ -1, -2, 0 ], ] + DUT = IfDanglingElseOutterComp class CaseElifBranchComp: - class DUT( Component ): + class ElifBranchComp( Component ): def construct( s ): s.in_1 = InPort( Bits32 ) s.in_2 = InPort( Bits32 ) @@ -759,9 +793,10 @@ def upblk(): [ -2, 24, -2, -2 ], [ -1, -2, -1, -1 ], ] + DUT = ElifBranchComp class CaseNestedIfComp: - class DUT( Component ): + class NestedIfComp( Component ): def construct( s ): s.in_1 = InPort( Bits32 ) s.in_2 = InPort( Bits32 ) @@ -800,9 +835,10 @@ def upblk(): [ -2, 24, -2, 24 ], [ -1, -2, -1, -2 ], ] + DUT = NestedIfComp class CaseForBasicComp: - class DUT( Component ): + class ForBasicComp( Component ): def construct( s ): s.in_ = InPort( Bits16 ) s.out = OutPort( Bits8 ) @@ -810,9 +846,10 @@ def construct( s ): def for_basic(): for i in range( 8 ): s.out[ 2*i:2*i+1 ] @= s.in_[ 2*i:2*i+1 ] + s.in_[ 2*i+1:(2*i+1)+1 ] + DUT = ForBasicComp class CaseForFreeVarStepComp: - class DUT( Component ): + class ForFreeVarStepComp( Component ): def construct( s ): s.in_ = InPort( Bits16 ) s.out = OutPort( Bits8 ) @@ -821,16 +858,18 @@ def construct( s ): def upblk(): for i in range( 0, 2, freevar ): s.out @= s.in_[0:8] + DUT = ForFreeVarStepComp class CaseTypeNameAsFieldNameComp: - class DUT( Component ): + class TypeNameAsFieldNameComp( Component ): def construct( s ): s.in_ = InPort( StructTypeNameAsFieldName ) s.out = OutPort( StructTypeNameAsFieldName ) s.in_ //= s.out + DUT = TypeNameAsFieldNameComp class CaseForRangeLowerUpperStepPassThroughComp: - class DUT( Component ): + class ForRangeLowerUpperStepPassThroughComp( Component ): def construct( s ): s.in_ = [ InPort( Bits32 ) for _ in range(5) ] s.out = [ OutPort( Bits32 ) for _ in range(5) ] @@ -864,9 +903,10 @@ def upblk(): [ -2, 24, -2, 24, -2, -2, 24, -2, 24, -2, ], [ -1, -2, -1, -2, -1, -1, -2, -1, -2, -1, ], ] + DUT = ForRangeLowerUpperStepPassThroughComp class CaseForLoopEmptySequenceComp: - class DUT( Component ): + class ForLoopEmptySequenceComp( Component ): def construct( s ): s.out = OutPort( Bits4 ) @update @@ -881,9 +921,10 @@ def upblk(): [0xF], [0xF], ] + DUT = ForLoopEmptySequenceComp class CaseIfExpBothImplicitComp: - class DUT( Component ): + class IfExpBothImplicitComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits32 ) @@ -899,9 +940,10 @@ def upblk(): [ 1, 1, ], [ 0, 0, ], ] + DUT = IfExpBothImplicitComp class CaseIfExpInForStmtComp: - class DUT( Component ): + class IfExpInForStmtComp( Component ): def construct( s ): s.in_ = [ InPort( Bits32 ) for _ in range(5) ] s.out = [ OutPort( Bits32 ) for _ in range(5) ] @@ -933,9 +975,10 @@ def upblk(): [ -2, 24, -2, 24, -2, -2, 24, -2, -2, -2, ], [ -1, -2, -1, -2, -1, -1, -2, -1, -1, -1, ], ] + DUT = IfExpInForStmtComp class CaseIfExpUnaryOpInForStmtComp: - class DUT( Component ): + class IfExpUnaryOpInForStmtComp( Component ): def construct( s ): s.in_ = [ InPort( Bits32 ) for _ in range(5) ] s.out = [ OutPort( Bits32 ) for _ in range(5) ] @@ -967,9 +1010,10 @@ def upblk(): [ -2, 24, -2, 24, -2, -2, ~24, -2, -2, -2, ], [ -1, -2, -1, -2, -1, -1, ~-2, -1, -1, -1, ], ] + DUT = IfExpUnaryOpInForStmtComp class CaseIfBoolOpInForStmtComp: - class DUT( Component ): + class IfBoolOpInForStmtComp( Component ): def construct( s ): s.in_ = [ InPort( Bits32 ) for _ in range(5) ] s.out = [ OutPort( Bits32 ) for _ in range(5) ] @@ -1004,9 +1048,10 @@ def upblk(): [ -2, 24, -2, 24, -2, -2, 24, -2, 24, -2, ], [ -1, -2, -1, -2, -1, -1, -2, -1, -2, -1, ], ] + DUT = IfBoolOpInForStmtComp class CaseIfTmpVarInForStmtComp: - class DUT( Component ): + class IfTmpVarInForStmtComp( Component ): def construct( s ): s.in_ = [ InPort( Bits32 ) for _ in range(5) ] s.out = [ OutPort( Bits32 ) for _ in range(5) ] @@ -1042,9 +1087,10 @@ def upblk(): [ -2, 24, -2, 24, -2, -2, 24, -2, 24, -2, ], [ -1, -2, -1, -2, -1, -1, -2, -1, -2, -1, ], ] + DUT = IfTmpVarInForStmtComp class CaseFixedSizeSliceComp: - class DUT( Component ): + class FixedSizeSliceComp( Component ): def construct( s ): s.in_ = InPort( Bits16 ) s.out = [ OutPort( Bits8 ) for _ in range(2) ] @@ -1068,9 +1114,10 @@ def upblk(): [ 0x3412, 0x12, 0x34 ], [ 0x9876, 0x76, 0x98 ], ] + DUT = FixedSizeSliceComp class CaseTwoUpblksSliceComp: - class DUT( Component ): + class TwoUpblksSliceComp( Component ): def construct( s ): s.in_ = InPort( Bits4 ) s.out = OutPort( Bits8 ) @@ -1080,9 +1127,10 @@ def multi_upblks_1(): @update def multi_upblks_2(): s.out[ 4:8 ] @= s.in_ + DUT = TwoUpblksSliceComp class CaseTwoUpblksFreevarsComp: - class DUT( Component ): + class TwoUpblksFreevarsComp( Component ): def construct( s ): STATE_IDLE = Bits2(0) STATE_WORK = Bits2(1) @@ -1093,9 +1141,10 @@ def multi_upblks_1(): @update def multi_upblks_2(): s.out[1] @= STATE_WORK + DUT = TwoUpblksFreevarsComp class CaseTwoUpblksStructTmpWireComp: - class DUT( Component ): + class TwoUpblksStructTmpWireComp( Component ): def construct( s ): s.in_foo = InPort( Bits32Foo ) s.in_bar = InPort( Bits32Bar ) @@ -1109,9 +1158,10 @@ def multi_upblks_1(): def multi_upblks_2(): u = s.in_bar s.out_bar @= u.bar + DUT = TwoUpblksStructTmpWireComp class CaseFlipFlopAdder: - class DUT( Component ): + class FlipFlopAdder( Component ): def construct( s ): s.in0 = InPort( Bits32 ) s.in1 = InPort( Bits32 ) @@ -1119,9 +1169,10 @@ def construct( s ): @update_ff def update_ff_upblk(): s.out0 <<= s.in0 + s.in1 + DUT = FlipFlopAdder class CaseConstSizeSlicingComp: - class DUT( Component ): + class ConstSizeSlicingComp( Component ): def construct( s ): s.in_ = InPort( Bits16 ) s.out = [ OutPort( Bits8 ) for _ in range(2) ] @@ -1129,9 +1180,10 @@ def construct( s ): def upblk(): for i in range(2): s.out[i] @= s.in_[i*8 : i*8 + 8] + DUT = ConstSizeSlicingComp class CaseBits32TmpWireComp: - class DUT( Component ): + class Bits32TmpWireComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits32 ) @@ -1139,9 +1191,10 @@ def construct( s ): def upblk(): u = s.in_ + 42 s.out @= u + DUT = Bits32TmpWireComp class CaseBits32MultiTmpWireComp: - class DUT( Component ): + class Bits32MultiTmpWireComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits32 ) @@ -1151,9 +1204,10 @@ def upblk(): v = s.in_ + 40 s.out @= u s.out @= v + DUT = Bits32MultiTmpWireComp class CaseBits32FreeVarToTmpVarComp: - class DUT( Component ): + class Bits32FreeVarToTmpVarComp( Component ): def construct( s ): # STATE_IDLE = Bits32(0) s.out = OutPort( Bits32 ) @@ -1161,27 +1215,30 @@ def construct( s ): def upblk(): u = STATE_IDLE s.out @= u + DUT = Bits32FreeVarToTmpVarComp class CaseBits32ConstBitsToTmpVarComp: - class DUT( Component ): + class Bits32ConstBitsToTmpVarComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): u = Bits32(0) s.out @= u + DUT = Bits32ConstBitsToTmpVarComp class CaseBits32ConstIntToTmpVarComp: - class DUT( Component ): + class Bits32ConstIntToTmpVarComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): u = 1 s.out @= u + DUT = Bits32ConstIntToTmpVarComp class CaseBits32TmpWireAliasComp: - class DUT( Component ): + class Bits32TmpWireAliasComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = [ OutPort( Bits32 ) for _ in range(5) ] @@ -1193,9 +1250,10 @@ def multi_upblks_1(): def multi_upblks_2(): u = s.in_ + 42 s.out[1] @= u + DUT = Bits32TmpWireAliasComp class CaseStructTmpWireComp: - class DUT( Component ): + class StructTmpWireComp( Component ): def construct( s ): s.in_ = InPort( Bits32Foo ) s.out = OutPort( Bits32 ) @@ -1203,9 +1261,10 @@ def construct( s ): def upblk(): u = s.in_ s.out @= u.foo + DUT = StructTmpWireComp class CaseTmpWireOverwriteConflictComp: - class DUT( Component ): + class TmpWireOverwriteConflictComp( Component ): def construct( s ): s.in_1 = InPort( Bits32 ) s.in_2 = InPort( Bits16 ) @@ -1215,9 +1274,10 @@ def upblk(): u = s.in_1 + 42 u = s.in_2 + 1 s.out @= u + DUT = TmpWireOverwriteConflictComp class CaseScopeTmpWireOverwriteConflictComp: - class DUT( Component ): + class ScopeTmpWireOverwriteConflictComp( Component ): def construct( s ): s.in_1 = InPort( Bits32 ) s.in_2 = InPort( Bits16 ) @@ -1230,9 +1290,10 @@ def upblk(): else: u = s.in_2 + 1 s.out @= u + DUT = ScopeTmpWireOverwriteConflictComp class CaseHeteroCompArrayComp: - class DUT( Component ): + class HeteroCompArrayComp( Component ): def construct( s ): s.in_1 = InPort( Bits32 ) s.in_2 = InPort( Bits32 ) @@ -1260,9 +1321,10 @@ def construct( s ): [ 42, 0, 42, 42], [ -1, 42, -1, 84], ] + DUT = HeteroCompArrayComp class CaseChildExplicitModuleName: - class DUT( Component ): + class ChildExplicitModuleName( Component ): def construct( s ): s.in_ = InPort( 32 ) s.child = TestCompExplicitModuleName() @@ -1273,13 +1335,14 @@ def construct( s ): [ [ 0 ], ] + DUT = ChildExplicitModuleName #------------------------------------------------------------------------- # Test cases without errors #------------------------------------------------------------------------- class CaseSizeCastPaddingStructPort: - class DUT( Component ): + class SizeCastPaddingStructPort( Component ): def construct( s ): s.in_ = InPort( Bits32Foo ) s.out = OutPort( Bits64 ) @@ -1298,9 +1361,10 @@ def upblk(): [ 42, concat( Bits32(0), Bits32(42) ) ], [ -1, concat( Bits32(0), Bits32(-1) ) ], ] + DUT = SizeCastPaddingStructPort class CaseBits32x2ConcatComp: - class DUT( Component ): + class Bits32x2ConcatComp( Component ): def construct( s ): s.in_1 = InPort( Bits32 ) s.in_2 = InPort( Bits32 ) @@ -1322,9 +1386,10 @@ def upblk(): [ 42, 42, concat( Bits32(42), Bits32(42) ) ], [ -1, 42, concat( Bits32(-1), Bits32(42) ) ], ] + DUT = Bits32x2ConcatComp class CaseBits32x2ConcatConstComp: - class DUT( Component ): + class Bits32x2ConcatConstComp( Component ): def construct( s ): s.out = OutPort( Bits64 ) @update @@ -1338,9 +1403,10 @@ def upblk(): [ [ concat( Bits32(42), Bits32(0) ) ], ] + DUT = Bits32x2ConcatConstComp class CaseBits32x2ConcatMixedComp: - class DUT( Component ): + class Bits32x2ConcatMixedComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits64 ) @@ -1358,9 +1424,10 @@ def upblk(): [ -2, concat( Bits32(-2), Bits32(0) ) ], [ 2, concat( Bits32(2), Bits32(0) ) ], ] + DUT = Bits32x2ConcatMixedComp class CaseBits32x2ConcatFreeVarComp: - class DUT( Component ): + class Bits32x2ConcatFreeVarComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits33 ) @@ -1378,9 +1445,10 @@ def upblk(): [ -2, concat( Bits32(-2), Bits1(0) ) ], [ 2, concat( Bits32(2), Bits1(0) ) ], ] + DUT = Bits32x2ConcatFreeVarComp class CaseBits32x2ConcatUnpackedSignalComp: - class DUT( Component ): + class Bits32x2ConcatUnpackedSignalComp( Component ): def construct( s ): s.in_ = [ InPort( Bits32 ) for _ in range(2) ] s.out = OutPort( Bits64 ) @@ -1401,9 +1469,10 @@ def upblk(): [ -2, -1, concat( Bits32(-2), Bits32(-1) ) ], [ 2, -2, concat( Bits32(2), Bits32(-2) ) ], ] + DUT = Bits32x2ConcatUnpackedSignalComp class CaseBits64SextInComp: - class DUT( Component ): + class Bits64SextInComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits64 ) @@ -1421,9 +1490,10 @@ def upblk(): [ -1, sext( Bits32(-1), 64 ) ], [ 2, sext( Bits32(2), 64 ) ], ] + DUT = Bits64SextInComp class CaseBits64ZextInComp: - class DUT( Component ): + class Bits64ZextInComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits64 ) @@ -1441,9 +1511,10 @@ def upblk(): [ -1, zext( Bits32(-1), 64 ) ], [ 2, zext( Bits32(2), 64 ) ], ] + DUT = Bits64ZextInComp class CaseBits64TruncInComp: - class DUT( Component ): + class Bits64TruncInComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits8 ) @@ -1461,9 +1532,10 @@ def upblk(): [ -1, trunc( Bits32(-1), 8 ) ], [ 2, trunc( Bits32(2), 8 ) ], ] + DUT = Bits64TruncInComp class CaseBits32BitSelUpblkComp: - class DUT( Component ): + class Bits32BitSelUpblkComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits1 ) @@ -1481,9 +1553,10 @@ def upblk(): [ -2, 1 ], [ 2, 1 ], ] + DUT = Bits32BitSelUpblkComp class CaseBits64PartSelUpblkComp: - class DUT( Component ): + class Bits64PartSelUpblkComp( Component ): def construct( s ): s.in_ = InPort( Bits64 ) s.out = OutPort( Bits32 ) @@ -1504,9 +1577,10 @@ def upblk(): [ -32, -2 ], [ -64, -4 ], ] + DUT = Bits64PartSelUpblkComp class CasePassThroughComp: - class DUT( Component ): + class PassThroughComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits32 ) @@ -1525,9 +1599,10 @@ def upblk(): [ -2, -2 ], [ -1, -1 ], ] + DUT = PassThroughComp class CaseSequentialPassThroughComp: - class DUT( Component ): + class SequentialPassThroughComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits32 ) @@ -1546,9 +1621,10 @@ def upblk(): [ -2, 24 ], [ -1, -2 ], ] + DUT = SequentialPassThroughComp class CaseConnectPassThroughLongNameComp: - class DUT( Component ): + class ConnectPassThroughLongNameComp( Component ): def construct( s, T1, T2, T3, T4, T5, T6, T7 ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits32 ) @@ -1565,9 +1641,10 @@ def construct( s, T1, T2, T3, T4, T5, T6, T7 ): [ -2, -2 ], [ -1, -1 ], ] + DUT = ConnectPassThroughLongNameComp class CaseLambdaConnectComp: - class DUT( Component ): + class LambdaConnectComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits32 ) @@ -1586,13 +1663,15 @@ def construct( s ): [ -42, 0 ], [ -41, 1 ], ] + DUT = LambdaConnectComp class CaseLambdaConnectWithListComp: - class DUT( Component ): + class LambdaConnectWithListComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = [ OutPort( Bits32 ) for _ in range(2) ] s.out[1] //= lambda: s.in_ + 42 + DUT = LambdaConnectWithListComp TV_IN = \ _set( 'in_', Bits32, 0 ) @@ -1610,7 +1689,7 @@ def construct( s ): ] class CaseBits32FooInBits32OutComp: - class DUT( Component ): + class Bits32FooInBits32OutComp( Component ): def construct( s ): s.in_ = InPort( Bits32Foo ) s.out = OutPort( Bits32 ) @@ -1630,25 +1709,28 @@ def upblk(): [ 10, 10 ], [ 256, 256 ], ] + DUT = Bits32FooInBits32OutComp class CaseBits32FooKwargComp: - class DUT( Component ): + class Bits32FooKwargComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): s.out @= Bits32Foo( foo = 42 ) + DUT = Bits32FooKwargComp class CaseBits32FooInstantiationComp: - class DUT( Component ): + class Bits32FooInstantiationComp( Component ): def construct( s ): s.out = OutPort( Bits32Foo ) @update def upblk(): s.out @= Bits32Foo( 42 ) + DUT = Bits32FooInstantiationComp class CaseConstStructInstComp: - class DUT( Component ): + class ConstStructInstComp( Component ): def construct( s ): s.in_ = Bits32Foo() s.out = OutPort( Bits32 ) @@ -1663,9 +1745,10 @@ def upblk(): [ [ 0 ], ] + DUT = ConstStructInstComp class CaseStructPackedArrayUpblkComp: - class DUT( Component ): + class StructPackedArrayUpblkComp( Component ): def construct( s ): s.in_ = InPort( Bits32x5Foo ) s.out = OutPort( Bits96 ) @@ -1684,9 +1767,10 @@ def upblk(): [ [ b32(42),b32(42),b32(42),b32(0),b32(0) ], concat( b32(42), b32(42), b32(42) ) ], [ [ b32(-1),b32(-1),b32(42),b32(0),b32(0) ], concat( b32(-1), b32(-1), b32(42) ) ], ] + DUT = StructPackedArrayUpblkComp class CaseConnectLiteralStructComp: - class DUT( Component ): + class ConnectLiteralStructComp( Component ): def construct( s ): s.out = OutPort( NestedStructPackedPlusScalar ) connect( s.out, NestedStructPackedPlusScalar( 42, [ b32(1), b32(2) ], Bits32Foo(3) ) ) @@ -1698,9 +1782,10 @@ def construct( s ): [ [ NestedStructPackedPlusScalar(42, [ Bits32(1) , Bits32(2) ], Bits32Foo(3) ) ], ] + DUT = ConnectLiteralStructComp class CaseNestedStructPackedArrayUpblkComp: - class DUT( Component ): + class NestedStructPackedArrayUpblkComp( Component ): def construct( s ): s.in_ = InPort( NestedStructPackedPlusScalar ) s.out = OutPort( Bits96 ) @@ -1719,9 +1804,10 @@ def upblk(): [ NestedStructPackedPlusScalar(42, [ Bits32(42), Bits32(43) ], Bits32Foo(8) ), concat( Bits32(42), Bits32(8), Bits32(42) ) ], [ NestedStructPackedPlusScalar(42, [ Bits32(-1), Bits32(-2) ], Bits32Foo(9) ), concat( Bits32(-1), Bits32(9), Bits32(42) ) ], ] + DUT = NestedStructPackedArrayUpblkComp class CaseConnectNestedStructPackedArrayComp: - class DUT( Component ): + class ConnectNestedStructPackedArrayComp( Component ): def construct( s ): s.in_ = InPort( NestedStructPackedPlusScalar ) s.out = OutPort( Bits96 ) @@ -1740,45 +1826,50 @@ def construct( s ): [ NestedStructPackedPlusScalar(42, [ Bits32(42), Bits32(43) ], Bits32Foo(8) ), concat( Bits32(42), Bits32(8), Bits32(42) ) ], [ NestedStructPackedPlusScalar(42, [ Bits32(-1), Bits32(-2) ], Bits32Foo(9) ), concat( Bits32(-1), Bits32(9), Bits32(42) ) ], ] + DUT = ConnectNestedStructPackedArrayComp class CaseInterfaceAttributeComp: - class DUT( Component ): + class InterfaceAttributeComp( Component ): def construct( s ): s.in_ = Bits32OutIfc() s.out = OutPort( Bits32 ) @update def upblk(): s.out @= s.in_.foo + DUT = InterfaceAttributeComp class CaseArrayInterfacesComp: - class DUT( Component ): + class ArrayInterfacesComp( Component ): def construct( s ): s.in_ = [ Bits32OutIfc() for _ in range(4) ] s.out = OutPort( Bits32 ) @update def upblk(): s.out @= s.in_[2].foo + DUT = ArrayInterfacesComp class CaseBits32SubCompPassThroughComp: - class DUT( Component ): + class Bits32SubCompPassThroughComp( Component ): def construct( s ): s.comp = Bits32OutComp() s.out = OutPort( Bits32 ) @update def upblk(): s.out @= s.comp.out + DUT = Bits32SubCompPassThroughComp class CaseArrayBits32SubCompPassThroughComp: - class DUT( Component ): + class ArrayBits32SubCompPassThroughComp( Component ): def construct( s ): s.comp = [ Bits32OutComp() for _ in range(4) ] s.out = OutPort( Bits32 ) @update def upblk(): s.out @= s.comp[2].out + DUT = ArrayBits32SubCompPassThroughComp class CaseSubCompTmpDrivenComp: - class DUT( Component ): + class SubCompTmpDrivenComp( Component ): def construct( s ): s.subcomp = Bits32OutTmpDrivenComp() s.out = OutPort( Bits32 ) @@ -1786,9 +1877,10 @@ def construct( s ): def upblk(): u = s.subcomp.out s.out @= u + DUT = SubCompTmpDrivenComp class CaseSubCompFreeVarDrivenComp: - class DUT( Component ): + class SubCompFreeVarDrivenComp( Component ): def construct( s ): s.subcomp = Bits32OutFreeVarDrivenComp() s.out = OutPort( Bits32 ) @@ -1798,14 +1890,16 @@ def upblk(): s.out @= s.subcomp.out else: s.out @= STATE_IDLE + DUT = SubCompFreeVarDrivenComp class CaseConstBits32AttrComp: - class DUT( Component ): + class ConstBits32AttrComp( Component ): def construct( s ): s.const = [ Bits32(42) for _ in range(5) ] + DUT = ConstBits32AttrComp class CaseInx2Outx2ConnectComp: - class DUT( Component ): + class Inx2Outx2ConnectComp( Component ): def construct( s ): s.in_1 = InPort( Bits32 ) s.in_2 = InPort( Bits32 ) @@ -1813,16 +1907,18 @@ def construct( s ): s.out2 = OutPort( Bits32 ) connect( s.in_1, s.out1 ) connect( s.in_2, s.out2 ) + DUT = Inx2Outx2ConnectComp class CaseConnectPortIndexComp: - class DUT( Component ): + class ConnectPortIndexComp( Component ): def construct( s ): s.in_ = [ InPort( Bits32 ) for _ in range(5) ] s.out = OutPort( Bits32 ) connect( s.in_[2], s.out ) + DUT = ConnectPortIndexComp class CaseConnectInToWireComp: - class DUT( Component ): + class ConnectInToWireComp( Component ): def construct( s ): s.in_ = [ InPort( Bits32 ) for _ in range(5) ] s.wire_ = [ Wire( Bits32 ) for _ in range(5) ] @@ -1848,9 +1944,10 @@ def construct( s ): [ 0, 0, -2, 0, 0, -2 ], [ 0, 0, -1, 0, 0, -1 ], ] + DUT = ConnectInToWireComp class CaseWiresDrivenComp: - class DUT( Component ): + class WiresDrivenComp( Component ): def construct( s ): s.foo = Wire( Bits32 ) s.bar = Wire( Bits4 ) @@ -1858,38 +1955,43 @@ def construct( s ): def upblk(): s.foo @= 42 s.bar @= 0 + DUT = WiresDrivenComp class CaseBits32Wirex5DrivenComp: - class DUT( Component ): + class Bits32Wirex5DrivenComp( Component ): def construct( s ): s.foo = [ Wire( Bits32 ) for _ in range(5) ] @update def upblk(): for i in range(5): s.foo[i] @= 0 + DUT = Bits32Wirex5DrivenComp class CaseStructWireDrivenComp: - class DUT( Component ): + class StructWireDrivenComp( Component ): def construct( s ): s.foo = Wire( Bits32Foo ) @update def upblk(): s.foo.foo @= 42 + DUT = StructWireDrivenComp class CaseStructConstComp: - class DUT( Component ): + class StructConstComp( Component ): def construct( s ): s.struct_const = Bits32Foo() + DUT = StructConstComp class CaseNestedPackedArrayStructComp: - class DUT( Component ): + class NestedPackedArrayStructComp( Component ): def construct( s ): s.in_ = InPort( NestedStructPackedArray ) s.out = OutPort( Bits32x5Foo ) connect( s.in_.foo[1], s.out ) + DUT = NestedPackedArrayStructComp class CaseConnectConstToOutComp: - class DUT( Component ): + class ConnectConstToOutComp( Component ): def construct( s ): s.const_ = [ 42 for _ in range(5) ] s.out = OutPort( Bits32 ) @@ -1902,9 +2004,10 @@ def construct( s ): [ [ 42 ], ] + DUT = ConnectConstToOutComp class CaseConnectBitSelToOutComp: - class DUT( Component ): + class ConnectBitSelToOutComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits1 ) @@ -1922,9 +2025,10 @@ def construct( s ): [ -1, 1, ], [ -2, 0, ], ] + DUT = ConnectBitSelToOutComp class CaseConnectSliceToOutComp: - class DUT( Component ): + class ConnectSliceToOutComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits4 ) @@ -1942,9 +2046,10 @@ def construct( s ): [ -1, -1, ], [ -2, -1, ], ] + DUT = ConnectSliceToOutComp class CaseConnectBitsConstToOutComp: - class DUT( Component ): + class ConnectBitsConstToOutComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) connect( s.out, Bits32(0) ) @@ -1956,16 +2061,18 @@ def construct( s ): [ [ 0 ], ] + DUT = ConnectBitsConstToOutComp class CaseConnectStructAttrToOutComp: - class DUT( Component ): + class ConnectStructAttrToOutComp( Component ): def construct( s ): s.in_ = InPort( Bits32Foo ) s.out = OutPort( Bits32 ) connect( s.out, s.in_.foo ) + DUT = ConnectStructAttrToOutComp class CaseConnectArrayStructAttrToOutComp: - class DUT( Component ): + class ConnectArrayStructAttrToOutComp( Component ): def construct( s ): s.in_ = InPort( Bits32x5Foo ) s.out = OutPort( Bits32 ) @@ -1982,9 +2089,10 @@ def construct( s ): [ [ b32(42),b32(42),b32(42),b32(0),b32(0) ], 42, ], [ [ b32(-1),b32(-1),b32(42),b32(0),b32(0) ], -1, ], ] + DUT = ConnectArrayStructAttrToOutComp class CaseConnectConstStructAttrToOutComp: - class DUT( Component ): + class ConnectConstStructAttrToOutComp( Component ): def construct( s ): s.in_ = Bits32Foo( 42 ) s.out = OutPort( Bits32 ) @@ -1994,23 +2102,26 @@ def construct( s ): TV_OUT = \ _check( 'out', Bits32, 0 ) TV =[ [ 42 ] ] + DUT = ConnectConstStructAttrToOutComp class CaseBits32IfcInComp: - class DUT( Component ): + class Bits32IfcInComp( Component ): def construct( s ): s.in_ = Bits32InIfc() s.out = OutPort( Bits32 ) connect( s.out, s.in_.foo ) + DUT = Bits32IfcInComp class CaseArrayBits32IfcInComp: - class DUT( Component ): + class ArrayBits32IfcInComp( Component ): def construct( s ): s.in_ = [ Bits32InIfc() for _ in range(2) ] s.out = OutPort( Bits32 ) connect( s.in_[1].foo, s.out ) + DUT = ArrayBits32IfcInComp class CaseBits32FooNoArgBehavioralComp: - class DUT( Component ): + class Bits32FooNoArgBehavioralComp( Component ): def construct( s ): s.out = OutPort( Bits32Foo ) @update @@ -2024,9 +2135,10 @@ def upblk(): [ 0, ], [ 0, ], ] + DUT = Bits32FooNoArgBehavioralComp class CaseArrayBits32IfcInUpblkComp: - class DUT( Component ): + class ArrayBits32IfcInUpblkComp( Component ): def construct( s ): s.in_ = [ Bits32InIfc() for _ in range(5) ] s.out = OutPort( Bits32 ) @@ -2048,9 +2160,10 @@ def upblk(): [ -2, -1, -1 ], [ -1, -2, -2 ], ] + DUT = ArrayBits32IfcInUpblkComp class CaseConnectValRdyIfcComp: - class DUT( Component ): + class ConnectValRdyIfcComp( Component ): def construct( s ): s.in_ = Bits32InValRdyIfc() s.out = Bits32OutValRdyIfc() @@ -2077,9 +2190,10 @@ def construct( s ): [ 0, 2, 0, 0, 2, 0 ], [ 1, 24, 1, 1, 24, 1 ], ] + DUT = ConnectValRdyIfcComp class CaseConnectValRdyIfcUpblkComp: - class DUT( Component ): + class ConnectValRdyIfcUpblkComp( Component ): def construct( s ): s.in_ = Bits32InValRdyIfc() s.out = Bits32OutValRdyIfc() @@ -2108,9 +2222,10 @@ def upblk(): [ 0, 2, 0, 0, 2, 0 ], [ 1, 24, 1, 1, 24, 1 ], ] + DUT = ConnectValRdyIfcUpblkComp class CaseConnectArrayBits32FooIfcComp: - class DUT( Component ): + class ConnectArrayBits32FooIfcComp( Component ): def construct( s ): s.in_ = [ Bits32FooInIfc() for _ in range(2) ] s.out = [ Bits32FooOutIfc() for _ in range(2) ] @@ -2134,9 +2249,10 @@ def construct( s ): [ 1, -1, 1, -1, ], [ 1, -2, 1, -2, ], ] + DUT = ConnectArrayBits32FooIfcComp class CaseConnectArrayBits32Comp: - class DUT( Component ): + class ConnectArrayBits32Comp( Component ): def construct( s ): s.in_ = [ InPort(32) for _ in range(2) ] s.out = [ OutPort(32) for _ in range(2) ] @@ -2160,9 +2276,10 @@ def construct( s ): [ 1, -1, 1, -1, ], [ 1, -2, 1, -2, ], ] + DUT = ConnectArrayBits32Comp class CaseConnectArrayNestedIfcComp: - class DUT( Component ): + class ConnectArrayNestedIfcComp( Component ): def construct( s ): s.in_ = [ MemReqIfc() for _ in range(2) ] s.out = [ MemRespIfc() for _ in range(2) ] @@ -2198,9 +2315,10 @@ def construct( s ): [ 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, ], [ 1, 1, -2, 0, 1, 1, -2, 0, 1, 1, -2, 0, 1, 1, -2, 0, ], ] + DUT = ConnectArrayNestedIfcComp class CaseBits32IfcTmpVarOutComp: - class DUT( Component ): + class Bits32IfcTmpVarOutComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) s.ifc = Bits32OutIfc() @@ -2208,9 +2326,10 @@ def construct( s ): def upblk(): u = s.ifc.foo s.out @= u + DUT = Bits32IfcTmpVarOutComp class CaseStructIfcTmpVarOutComp: - class DUT( Component ): + class StructIfcTmpVarOutComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) s.ifc = Bits32FooInIfc() @@ -2218,9 +2337,10 @@ def construct( s ): def upblk(): u = s.ifc.foo s.out @= u.foo + DUT = StructIfcTmpVarOutComp class CaseBits32ConnectSubCompAttrComp: - class DUT( Component ): + class Bits32ConnectSubCompAttrComp( Component ): def construct( s ): s.b = Bits32OutDrivenComp() s.out = OutPort( Bits32 ) @@ -2230,9 +2350,10 @@ def construct( s ): TV_OUT = \ _check( 'out', Bits32, 0 ) TV =[ [ 42 ] ] + DUT = Bits32ConnectSubCompAttrComp class CaseBits32SubCompAttrUpblkComp: - class DUT( Component ): + class Bits32SubCompAttrUpblkComp( Component ): def construct( s ): s.b = Bits32OutDrivenComp() s.out = OutPort( Bits32 ) @@ -2247,9 +2368,10 @@ def upblk(): [ [ 42 ], ] + DUT = Bits32SubCompAttrUpblkComp class CaseConnectSubCompIfcHierarchyComp: - class DUT( Component ): + class ConnectSubCompIfcHierarchyComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) s.ifc = Bits32OutValRdyIfc() @@ -2265,9 +2387,10 @@ def construct( s ): 'ifc.val', Bits1, 2, ) TV =[ [ 42, 42, 1 ] ] + DUT = ConnectSubCompIfcHierarchyComp class CaseConnectArraySubCompArrayStructIfcComp: - class DUT( Component ): + class ConnectArraySubCompArrayStructIfcComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits32 ) @@ -2285,9 +2408,10 @@ def construct( s ): [ -1, -1 ], [ -2, -2 ], ] + DUT = ConnectArraySubCompArrayStructIfcComp class CaseBehavioralArraySubCompArrayStructIfcComp: - class DUT( Component ): + class BehavioralArraySubCompArrayStructIfcComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits32 ) @@ -2309,9 +2433,10 @@ def upblk(): [ -1, -1 ], [ -2, -2 ], ] + DUT = BehavioralArraySubCompArrayStructIfcComp class CaseBits32ArrayConnectSubCompAttrComp: - class DUT( Component ): + class Bits32ArrayConnectSubCompAttrComp( Component ): def construct( s ): s.b = [ Bits32OutDrivenComp() for _ in range(5) ] s.out = OutPort( Bits32 ) @@ -2323,9 +2448,10 @@ def construct( s ): 'out', Bits32, 0, ) TV =[ [ 42 ] ] + DUT = Bits32ArrayConnectSubCompAttrComp class CaseBits32ArraySubCompAttrUpblkComp: - class DUT( Component ): + class Bits32ArraySubCompAttrUpblkComp( Component ): def construct( s ): s.b = [ Bits32OutDrivenComp() for _ in range(5) ] s.out = OutPort( Bits32 ) @@ -2340,24 +2466,28 @@ def upblk(): [ [ 42 ], ] + DUT = Bits32ArraySubCompAttrUpblkComp class CaseComponentArgsComp: - class DUT( Component ): + class ComponentArgsComp( Component ): def construct( s, foo, bar ): pass + DUT = ComponentArgsComp class CaseComponentDefaultArgsComp: - class DUT( Component ): + class ComponentDefaultArgsComp( Component ): def construct( s, foo, bar = Bits16(42) ): pass + DUT = ComponentDefaultArgsComp class CaseMixedDefaultArgsComp: - class DUT( Component ): + class MixedDefaultArgsComp( Component ): def construct( s, foo, bar, woo = Bits32(0) ): pass + DUT = MixedDefaultArgsComp class CaseGenericAdderComp: - class DUT( Component ): + class GenericAdderComp( Component ): def construct( s, Type ): s.in_1 = InPort( Type ) s.in_2 = InPort( Type ) @@ -2366,9 +2496,10 @@ def construct( s, Type ): def add_upblk(): s.out @= s.in_1 + s.in_2 def line_trace( s ): return 'sum = ' + str(s.out) + DUT = GenericAdderComp class CaseGenericMuxComp: - class DUT( Component ): + class GenericMuxComp( Component ): def construct( s, Type, n_ports ): s.in_ = [ InPort( Type ) for _ in range(n_ports) ] s.sel = InPort( mk_bits( clog2(n_ports) ) ) @@ -2377,17 +2508,19 @@ def construct( s, Type, n_ports ): def add_upblk(): s.out @= s.in_[ s.sel ] def line_trace( s ): return "out = " + str( s.out ) + DUT = GenericMuxComp class CaseStructConnectWireComp: - class DUT( Component ): + class StructConnectWireComp( Component ): def construct( s ): s.in_ = InPort( Bits32Foo ) s.out = OutPort( Bits32 ) connect( s.out, s.in_.foo ) def line_trace( s ): return "out = " + str( s.out ) + DUT = StructConnectWireComp class CaseNestedStructConnectWireComp: - class DUT( Component ): + class NestedStructConnectWireComp( Component ): def construct( s ): s.in_ = InPort( MultiDimPackedArrayStruct ) s.out_foo = OutPort( Bits32 ) @@ -2402,9 +2535,10 @@ def upblk(): connect( s.out_foo, s.in_.foo ) connect( s.out_bar, s.in_.inner.bar ) def line_trace( s ): return "out_sum = " + str( s.out_sum ) + DUT = NestedStructConnectWireComp class CaseNestedStructConnectWireSubComp: - class DUT( Component ): + class NestedStructConnectWireSubComp( Component ): def construct( s ): s.b = Bits32OutDrivenComp() s.in_ = InPort( MultiDimPackedArrayStruct ) @@ -2420,9 +2554,10 @@ def upblk(): connect( s.out_foo, s.b.out ) connect( s.out_bar, s.in_.inner.bar ) def line_trace( s ): return "out_sum = " + str( s.out_sum ) + DUT = NestedStructConnectWireSubComp class CaseGenericConditionalDriveComp: - class DUT( Component ): + class GenericConditionalDriveComp( Component ): def construct( s, Type ): s.in_ = [InPort ( Type ) for _ in range(2)] s.out = [OutPort( Type ) for _ in range(2)] @@ -2438,9 +2573,10 @@ def line_trace( s ): return "s.in0 = " + str( s.in_[0] ) +\ "s.in1 = " + str( s.in_[1] ) +\ "s.out0 = " + str( s.out[0] ) +\ "s.out1 = " + str( s.out[1] ) + DUT = GenericConditionalDriveComp class CaseBitSelOverBitSelComp: - class DUT( Component ): + class BitSelOverBitSelComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits1 ) @@ -2455,9 +2591,10 @@ def construct( s ): [ 4, 0 ], [ 5, 0 ], ] + DUT = BitSelOverBitSelComp class CaseBitSelOverPartSelComp: - class DUT( Component ): + class BitSelOverPartSelComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits1 ) @@ -2472,9 +2609,10 @@ def construct( s ): [ 4, 0 ], [ 5, 1 ], ] + DUT = BitSelOverPartSelComp class CasePartSelOverBitSelComp: - class DUT( Component ): + class PartSelOverBitSelComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits1 ) @@ -2489,9 +2627,10 @@ def construct( s ): [ 4, 0 ], [ 5, 0 ], ] + DUT = PartSelOverBitSelComp class CasePartSelOverPartSelComp: - class DUT( Component ): + class PartSelOverPartSelComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits1 ) @@ -2506,9 +2645,10 @@ def construct( s ): [ 4, 0 ], [ 5, 1 ], ] + DUT = PartSelOverPartSelComp class CaseDefaultBitsComp: - class DUT( Component ): + class DefaultBitsComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update @@ -2521,30 +2661,33 @@ def upblk(): [ 0 ], [ 0 ], ] + DUT = DefaultBitsComp #------------------------------------------------------------------------- # Test cases that contain SystemVerilog translator errors #------------------------------------------------------------------------- class CaseVerilogReservedComp: - class DUT( Component ): + class VerilogReservedComp( Component ): def construct( s ): s.buf = InPort( Bits32 ) s.out = OutPort( Bits32 ) @update def upblk(): s.out @= s.buf + DUT = VerilogReservedComp class CaseUpdateffMixAssignComp: - class DUT( Component ): + class UpdateffMixAssignComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): tmpvar = s.out = Bits32(42) + DUT = UpdateffMixAssignComp class CaseInterfaceArrayNonStaticIndexComp: - class DUT( Component ): + class InterfaceArrayNonStaticIndexComp( Component ): def construct( s ): s.in_ = [ Bits32InIfc() for _ in range(2) ] s.out = OutPort( Bits32 ) @@ -2566,85 +2709,94 @@ def upblk(): [ 1, -1, -1 ], [ 1, 42, 42 ], ] + DUT = InterfaceArrayNonStaticIndexComp #------------------------------------------------------------------------- # Test cases that contain errors #------------------------------------------------------------------------- class CaseStructBitsUnagreeableComp: - class DUT( Component ): + class StructBitsUnagreeableComp( Component ): def construct( s ): s.in_ = InPort( Bits32Foo ) s.out = OutPort( Bits16 ) @update def upblk(): s.out @= s.in_ + DUT = StructBitsUnagreeableComp class CaseConcatComponentComp: - class DUT( Component ): + class ConcatComponentComp( Component ): def construct( s ): s.in_ = InPort( Bits8 ) s.out = OutPort( Bits16 ) @update def upblk(): s.out @= concat( s, s.in_ ) + DUT = ConcatComponentComp class CaseZextVaribleNbitsComp: - class DUT( Component ): + class ZextVaribleNbitsComp( Component ): def construct( s ): s.in_ = InPort( Bits8 ) s.out = OutPort( Bits16 ) @update def upblk(): s.out @= zext( s.in_, s.in_ ) + DUT = ZextVaribleNbitsComp class CaseZextSmallNbitsComp: - class DUT( Component ): + class ZextSmallNbitsComp( Component ): def construct( s ): s.in_ = InPort( Bits8 ) s.out = OutPort( Bits16 ) @update def upblk(): s.out @= zext( s.in_, 4 ) + DUT = ZextSmallNbitsComp class CaseSextVaribleNbitsComp: - class DUT( Component ): + class SextVaribleNbitsComp( Component ): def construct( s ): s.in_ = InPort( Bits8 ) s.out = OutPort( Bits16 ) @update def upblk(): s.out @= sext( s.in_, s.in_ ) + DUT = SextVaribleNbitsComp class CaseSextSmallNbitsComp: - class DUT( Component ): + class SextSmallNbitsComp( Component ): def construct( s ): s.in_ = InPort( Bits8 ) s.out = OutPort( Bits16 ) @update def upblk(): s.out @= sext( s.in_, 4 ) + DUT = SextSmallNbitsComp class CaseTruncVaribleNbitsComp: - class DUT( Component ): + class TruncVaribleNbitsComp( Component ): def construct( s ): s.in_ = InPort( Bits8 ) s.out = OutPort( Bits16 ) @update def upblk(): s.out @= trunc( s.in_, s.in_ ) + DUT = TruncVaribleNbitsComp class CaseTruncLargeNbitsComp: - class DUT( Component ): + class TruncLargeNbitsComp( Component ): def construct( s ): s.in_ = InPort( Bits8 ) s.out = OutPort( Bits16 ) @update def upblk(): s.out @= trunc( s.in_, 16 ) + DUT = TruncLargeNbitsComp class CaseDroppedAttributeComp: - class DUT( Component ): + class DroppedAttributeComp( Component ): def construct( s ): # s.in_ is not recognized by RTLIR and will be dropped s.in_ = 'string' @@ -2652,9 +2804,10 @@ def construct( s ): @update def upblk(): s.out @= s.in_ + DUT = DroppedAttributeComp class CaseL1UnsupportedSubCompAttrComp: - class DUT( Component ): + class L1UnsupportedSubCompAttrComp( Component ): def construct( s ): s.in_ = InPort( Bits4 ) s.out = [ OutPort( Bits1 ) for _ in range(5) ] @@ -2662,54 +2815,60 @@ def construct( s ): @update def upblk(): s.out @= s.comp_array[ 0 ].out + DUT = L1UnsupportedSubCompAttrComp class CaseIndexOutOfRangeComp: - class DUT( Component ): + class IndexOutOfRangeComp( Component ): def construct( s ): s.in_ = [ InPort( Bits1 ) for _ in range(4) ] s.out = OutPort( Bits1 ) @update def upblk(): s.out @= s.in_[4] + DUT = IndexOutOfRangeComp class CaseBitSelOutOfRangeComp: - class DUT( Component ): + class BitSelOutOfRangeComp( Component ): def construct( s ): s.in_ = InPort( Bits4 ) s.out = OutPort( Bits1 ) @update def upblk(): s.out @= s.in_[4] + DUT = BitSelOutOfRangeComp class CaseIndexOnStructComp: - class DUT( Component ): + class IndexOnStructComp( Component ): def construct( s ): s.in_ = InPort( Bits32x5Foo ) s.out = OutPort( Bits1 ) @update def upblk(): s.out @= s.in_[16] + DUT = IndexOnStructComp class CaseSliceOnStructComp: - class DUT( Component ): + class SliceOnStructComp( Component ): def construct( s ): s.in_ = InPort( Bits32x5Foo ) s.out = OutPort( Bits1 ) @update def upblk(): s.out @= s.in_[0:16] + DUT = SliceOnStructComp class CaseSliceBoundLowerComp: - class DUT( Component ): + class SliceBoundLowerComp( Component ): def construct( s ): s.in_ = InPort( Bits4 ) s.out = OutPort( Bits1 ) @update def upblk(): s.out @= s.in_[4:0] + DUT = SliceBoundLowerComp class CaseSliceVariableBoundComp: - class DUT( Component ): + class SliceVariableBoundComp( Component ): def construct( s ): s.in_ = InPort( Bits4 ) s.slice_l = InPort( Bits2 ) @@ -2718,92 +2877,103 @@ def construct( s ): @update def upblk(): s.out @= s.in_[s.slice_l:s.slice_r] + DUT = SliceVariableBoundComp class CaseSliceOutOfRangeComp: - class DUT( Component ): + class SliceOutOfRangeComp( Component ): def construct( s ): s.in_ = InPort( Bits4 ) s.out = OutPort( Bits1 ) @update def upblk(): s.out @= s.in_[0:5] + DUT = SliceOutOfRangeComp class CaseLHSConstComp: - class DUT( Component ): + class LHSConstComp( Component ): def construct( s ): u, s.v = 42, 42 @update def upblk(): s.v @= u + DUT = LHSConstComp class CaseLHSComponentComp: - class DUT( Component ): + class LHSComponentComp( Component ): def construct( s ): s.u = Bits16InOutPassThroughComp() @update def upblk(): s.u @= 42 + DUT = LHSComponentComp class CaseRHSComponentComp: - class DUT( Component ): + class RHSComponentComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): s.out @= s + DUT = RHSComponentComp class CaseZextOnComponentComp: - class DUT( Component ): + class ZextOnComponentComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): s.out @= zext( s, 1 ) + DUT = ZextOnComponentComp class CaseSextOnComponentComp: - class DUT( Component ): + class SextOnComponentComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): s.out @= sext( s, 1 ) + DUT = SextOnComponentComp class CaseSizeCastComponentComp: - class DUT( Component ): + class SizeCastComponentComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): s.out @= Bits32( s ) + DUT = SizeCastComponentComp class CaseAttributeSignalComp: - class DUT( Component ): + class AttributeSignalComp( Component ): def construct( s ): s.in_ = InPort( Bits32Foo ) s.out = OutPort( Bits32 ) @update def upblk(): s.out @= s.in_.foo + DUT = AttributeSignalComp class CaseComponentInIndexComp: - class DUT( Component ): + class ComponentInIndexComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits32 ) @update def upblk(): s.out @= s.in_[ s ] + DUT = ComponentInIndexComp class CaseComponentBaseIndexComp: - class DUT( Component ): + class ComponentBaseIndexComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): s.out @= s[ 1 ] + DUT = ComponentBaseIndexComp class CaseComponentLowerSliceComp: - class DUT( Component ): + class ComponentLowerSliceComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.idx = Bits16InOutPassThroughComp() @@ -2811,9 +2981,10 @@ def construct( s ): @update def upblk(): s.out @= s.in_[ s.idx:4 ] + DUT = ComponentLowerSliceComp class CaseComponentHigherSliceComp: - class DUT( Component ): + class ComponentHigherSliceComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.idx = Bits16InOutPassThroughComp() @@ -2821,394 +2992,445 @@ def construct( s ): @update def upblk(): s.out @= s.in_[ 0:s.idx ] + DUT = ComponentHigherSliceComp class CaseSliceOnComponentComp: - class DUT( Component ): + class SliceOnComponentComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): s.out @= s[ 0:4 ] + DUT = SliceOnComponentComp class CaseUpblkArgComp: - class DUT( Component ): + class UpblkArgComp( Component ): def construct( s ): @update def upblk( number ): u = number + DUT = UpblkArgComp class CaseAssignMultiTargetComp: - class DUT( Component ): + class AssignMultiTargetComp( Component ): def construct( s ): @update def upblk(): u = v = x = y + DUT = AssignMultiTargetComp class CaseCopyArgsComp: - class DUT( Component ): + class CopyArgsComp( Component ): def construct( s ): @update def upblk(): u = copy(42, 10) + DUT = CopyArgsComp class CaseDeepcopyArgsComp: - class DUT( Component ): + class DeepcopyArgsComp( Component ): def construct( s ): @update def upblk(): u = deepcopy(42, 10) + DUT = DeepcopyArgsComp class CaseSliceWithStepsComp: - class DUT( Component ): + class SliceWithStepsComp( Component ): def construct( s ): v = 42 @update def upblk(): u = v[ 0:16:4 ] + DUT = SliceWithStepsComp class CaseExtendedSubscriptComp: - class DUT( Component ): + class ExtendedSubscriptComp( Component ): def construct( s ): v = 42 @update def upblk(): u = v[ 0:8, 16:24 ] + DUT = ExtendedSubscriptComp class CaseTmpComponentComp: - class DUT( Component ): + class TmpComponentComp( Component ): def construct( s ): v = Bits16InOutPassThroughComp() @update def upblk(): u = v + DUT = TmpComponentComp class CaseUntypedTmpComp: - class DUT( Component ): + class UntypedTmpComp( Component ): def construct( s ): @update def upblk(): u = 42 + DUT = UntypedTmpComp class CaseStarArgsComp: - class DUT( Component ): + class StarArgsComp( Component ): def construct( s ): @update def upblk(): x = x(*x) + DUT = StarArgsComp class CaseDoubleStarArgsComp: - class DUT( Component ): + class DoubleStarArgsComp( Component ): def construct( s ): @update def upblk(): x = x(**x) + DUT = DoubleStarArgsComp class CaseKwArgsComp: - class DUT( Component ): + class KwArgsComp( Component ): def construct( s ): xx = 42 @update def upblk(): x = x(x=x) + DUT = KwArgsComp class CaseNonNameCalledComp: - class DUT( Component ): + class NonNameCalledComp( Component ): def construct( s ): import copy s.out = OutPort( Bits32 ) @update def upblk(): s.out @= copy.copy( 42 ) + DUT = NonNameCalledComp class CaseFuncNotFoundComp: - class DUT( Component ): + class FuncNotFoundComp( Component ): def construct( s ): @update def upblk(): t = undefined_func(u) + DUT = FuncNotFoundComp class CaseBitsArgsComp: - class DUT( Component ): + class BitsArgsComp( Component ): def construct( s ): @update def upblk(): x = Bits32( 42, 42 ) + DUT = BitsArgsComp class CaseConcatNoArgsComp: - class DUT( Component ): + class ConcatNoArgsComp( Component ): def construct( s ): @update def upblk(): x = concat() + DUT = ConcatNoArgsComp class CaseZextTwoArgsComp: - class DUT( Component ): + class ZextTwoArgsComp( Component ): def construct( s ): @update def upblk(): x = zext( s ) + DUT = ZextTwoArgsComp class CaseSextTwoArgsComp: - class DUT( Component ): + class SextTwoArgsComp( Component ): def construct( s ): @update def upblk(): x = sext( s ) + DUT = SextTwoArgsComp class CaseUnrecognizedFuncComp: - class DUT( Component ): + class UnrecognizedFuncComp( Component ): def construct( s ): def foo(): pass @update def upblk(): x = foo() + DUT = UnrecognizedFuncComp class CaseStandaloneExprComp: - class DUT( Component ): + class StandaloneExprComp( Component ): def construct( s ): @update def upblk(): 42 + DUT = StandaloneExprComp class CaseLambdaFuncComp: - class DUT( Component ): + class LambdaFuncComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): s.out @= lambda: 42 + DUT = LambdaFuncComp class CaseDictComp: - class DUT( Component ): + class DictComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): s.out @= { 1:42 } + DUT = DictComp class CaseSetComp: - class DUT( Component ): + class SetComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): s.out @= { 42 } + DUT = SetComp class CaseListComp: - class DUT( Component ): + class ListComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): s.out @= [ 42 ] + DUT = ListComp class CaseTupleComp: - class DUT( Component ): + class TupleComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): s.out @= ( 42, ) + DUT = TupleComp class CaseListComprehensionComp: - class DUT( Component ): + class ListComprehensionComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): s.out @= [ 42 for _ in range(1) ] + DUT = ListComprehensionComp class CaseSetComprehensionComp: - class DUT( Component ): + class SetComprehensionComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): s.out @= { 42 for _ in range(1) } + DUT = SetComprehensionComp class CaseDictComprehensionComp: - class DUT( Component ): + class DictComprehensionComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): s.out @= { 1:42 for _ in range(1) } + DUT = DictComprehensionComp class CaseGeneratorExprComp: - class DUT( Component ): + class GeneratorExprComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): s.out @= ( 42 for _ in range(1) ) + DUT = GeneratorExprComp class CaseYieldComp: - class DUT( Component ): + class YieldComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): s.out @= yield + DUT = YieldComp class CaseReprComp: - class DUT( Component ): + class ReprComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): # Python 2 only: s.out = `42` s.out @= repr(42) + DUT = ReprComp class CaseStrComp: - class DUT( Component ): + class StrComp( Component ): def construct( s ): s.out = OutPort( Bits32 ) @update def upblk(): s.out @= '42' + DUT = StrComp class CaseClassdefComp: - class DUT( Component ): + class ClassdefComp( Component ): def construct( s ): @update def upblk(): class c: pass + DUT = ClassdefComp class CaseDeleteComp: - class DUT( Component ): + class DeleteComp( Component ): def construct( s ): @update def upblk(): del u + DUT = DeleteComp class CaseWithComp: - class DUT( Component ): + class WithComp( Component ): def construct( s ): @update def upblk(): with u: 42 + DUT = WithComp class CaseRaiseComp: - class DUT( Component ): + class RaiseComp( Component ): def construct( s ): @update def upblk(): raise 42 + DUT = RaiseComp class CaseTryExceptComp: - class DUT( Component ): + class TryExceptComp( Component ): def construct( s ): @update def upblk(): try: 42 except: 42 + DUT = TryExceptComp class CaseTryFinallyComp: - class DUT( Component ): + class TryFinallyComp( Component ): def construct( s ): @update def upblk(): try: 42 finally: 42 + DUT = TryFinallyComp class CaseImportComp: - class DUT( Component ): + class ImportComp( Component ): def construct( s ): x = 42 @update def upblk(): import x + DUT = ImportComp class CaseImportFromComp: - class DUT( Component ): + class ImportFromComp( Component ): def construct( s ): x = 42 @update def upblk(): from x import x + DUT = ImportFromComp class CaseExecComp: - class DUT( Component ): + class ExecComp( Component ): def construct( s ): @update def upblk(): # Python 2 only: exec 42 exec(42) + DUT = ExecComp class CaseGlobalComp: - class DUT( Component ): + class GlobalComp( Component ): def construct( s ): u = 42 @update def upblk(): global u + DUT = GlobalComp class CasePassComp: - class DUT( Component ): + class PassComp( Component ): def construct( s ): @update def upblk(): pass + DUT = PassComp class CaseWhileComp: - class DUT( Component ): + class WhileComp( Component ): def construct( s ): @update def upblk(): while 42: 42 + DUT = WhileComp class CaseExtSliceComp: - class DUT( Component ): + class ExtSliceComp( Component ): def construct( s ): @update def upblk(): 42[ 1:2:3, 2:4:6 ] + DUT = ExtSliceComp class CaseAddComponentComp: - class DUT( Component ): + class AddComponentComp( Component ): def construct( s ): s.out = OutPort( Bits1 ) @update def upblk(): s.out @= Bits1( 1 ) + s + DUT = AddComponentComp class CaseInvComponentComp: - class DUT( Component ): + class InvComponentComp( Component ): def construct( s ): s.out = OutPort( Bits1 ) @update def upblk(): s.out @= ~s + DUT = InvComponentComp class CaseComponentStartRangeComp: - class DUT( Component ): + class ComponentStartRangeComp( Component ): def construct( s ): s.out = OutPort( Bits1 ) @update def upblk(): for i in range( s, 8, 1 ): s.out @= ~Bits1( 1 ) + DUT = ComponentStartRangeComp class CaseComponentEndRangeComp: - class DUT( Component ): + class ComponentEndRangeComp( Component ): def construct( s ): s.out = OutPort( Bits1 ) @update def upblk(): for i in range( 0, s, 1 ): s.out @= ~Bits1( 1 ) + DUT = ComponentEndRangeComp class CaseComponentStepRangeComp: - class DUT( Component ): + class ComponentStepRangeComp( Component ): def construct( s ): s.out = OutPort( Bits1 ) @update def upblk(): for i in range( 0, 8, s ): s.out @= ~Bits1( 1 ) + DUT = ComponentStepRangeComp class CaseComponentIfCondComp: - class DUT( Component ): + class ComponentIfCondComp( Component ): def construct( s ): s.out = OutPort( Bits1 ) @update @@ -3217,33 +3439,37 @@ def upblk(): s.out @= Bits1( 1 ) else: s.out @= ~Bits1( 1 ) + DUT = ComponentIfCondComp class CaseComponentIfExpCondComp: - class DUT( Component ): + class ComponentIfExpCondComp( Component ): def construct( s ): s.out = OutPort( Bits1 ) @update def upblk(): s.out @= Bits1(1) if s else ~Bits1(1) + DUT = ComponentIfExpCondComp class CaseComponentIfExpBodyComp: - class DUT( Component ): + class ComponentIfExpBodyComp( Component ): def construct( s ): s.out = OutPort( Bits1 ) @update def upblk(): s.out @= s if 1 else ~Bits1(1) + DUT = ComponentIfExpBodyComp class CaseComponentIfExpElseComp: - class DUT( Component ): + class ComponentIfExpElseComp( Component ): def construct( s ): s.out = OutPort( Bits1 ) @update def upblk(): s.out @= Bits1(1) if 1 else s + DUT = ComponentIfExpElseComp class CaseStructIfCondComp: - class DUT( Component ): + class StructIfCondComp( Component ): def construct( s ): s.in_ = InPort( Bits32Foo ) s.out = OutPort( Bits1 ) @@ -3253,18 +3479,20 @@ def upblk(): s.out @= Bits1(1) else: s.out @= ~Bits1(1) + DUT = StructIfCondComp class CaseZeroStepRangeComp: - class DUT( Component ): + class ZeroStepRangeComp( Component ): def construct( s ): s.out = OutPort( Bits1 ) @update def upblk(): for i in range( 0, 4, 0 ): s.out @= Bits1( 1 ) + DUT = ZeroStepRangeComp class CaseVariableStepRangeComp: - class DUT( Component ): + class VariableStepRangeComp( Component ): def construct( s ): s.in_ = InPort( Bits2 ) s.out = OutPort( Bits1 ) @@ -3272,71 +3500,79 @@ def construct( s ): def upblk(): for i in range( 0, 4, s.in_ ): s.out @= Bits1( 1 ) + DUT = VariableStepRangeComp class CaseStructIfExpCondComp: - class DUT( Component ): + class StructIfExpCondComp( Component ): def construct( s ): s.in_ = InPort( Bits32Foo ) s.out = OutPort( Bits1 ) @update def upblk(): s.out @= Bits1(1) if s.in_ else ~Bits1(1) + DUT = StructIfExpCondComp class CaseDifferentTypesIfExpComp: - class DUT( Component ): + class DifferentTypesIfExpComp( Component ): def construct( s ): s.in_ = InPort( Bits32Foo ) s.out = OutPort( Bits1 ) @update def upblk(): s.out @= Bits1(1) if 1 else s.in_ + DUT = DifferentTypesIfExpComp class CaseNotStructComp: - class DUT( Component ): + class NotStructComp( Component ): def construct( s ): s.in_ = InPort( Bits32Foo ) s.out = OutPort( Bits1 ) @update def upblk(): s.out @= ~ s.in_ + DUT = NotStructComp class CaseAndStructComp: - class DUT( Component ): + class AndStructComp( Component ): def construct( s ): s.in_ = InPort( Bits32Foo ) s.out = OutPort( Bits1 ) @update def upblk(): s.out @= Bits1( 1 ) & s.in_ + DUT = AndStructComp class CaseAddStructBits1Comp: - class DUT( Component ): + class AddStructBits1Comp( Component ): def construct( s ): s.in_ = InPort( Bits32Foo ) s.out = OutPort( Bits1 ) @update def upblk(): s.out @= Bits1( 1 ) + s.in_ + DUT = AddStructBits1Comp class CaseExplicitBoolComp: - class DUT( Component ): + class ExplicitBoolComp( Component ): def construct( s ): Bool = rdt.Bool s.out = OutPort( Bits1 ) @update def upblk(): s.out @= Bool( Bits1(1) ) + DUT = ExplicitBoolComp class CaseTmpVarUsedBeforeAssignmentComp: - class DUT( Component ): + class TmpVarUsedBeforeAssignmentComp( Component ): def construct( s ): s.out = OutPort( Bits4 ) @update def upblk(): s.out @= u + Bits4( 1 ) + DUT = TmpVarUsedBeforeAssignmentComp class CaseForLoopElseComp: - class DUT( Component ): + class ForLoopElseComp( Component ): def construct( s ): s.out = OutPort( Bits4 ) @update @@ -3345,9 +3581,10 @@ def upblk(): s.out @= Bits4( 1 ) else: s.out @= Bits4( 1 ) + DUT = ForLoopElseComp class CaseSignalAsLoopIndexComp: - class DUT( Component ): + class SignalAsLoopIndexComp( Component ): def construct( s ): s.in_ = Wire( Bits4 ) s.out = OutPort( Bits4 ) @@ -3355,9 +3592,10 @@ def construct( s ): def upblk(): for s.in_ in range(4): s.out @= Bits4( 1 ) + DUT = SignalAsLoopIndexComp class CaseRedefLoopIndexComp: - class DUT( Component ): + class RedefLoopIndexComp( Component ): def construct( s ): s.out = OutPort( Bits4 ) @update @@ -3365,9 +3603,10 @@ def upblk(): for i in range(4): for i in range(4): s.out @= Bits4( 1 ) + DUT = RedefLoopIndexComp class CaseSignalAfterInComp: - class DUT( Component ): + class SignalAfterInComp( Component ): def construct( s ): s.in_ = InPort( Bits2 ) s.out = OutPort( Bits4 ) @@ -3375,9 +3614,10 @@ def construct( s ): def upblk(): for i in s.in_: s.out @= Bits4( 1 ) + DUT = SignalAfterInComp class CaseFuncCallAfterInComp: - class DUT( Component ): + class FuncCallAfterInComp( Component ): def construct( s ): s.out = OutPort( Bits4 ) def foo(): pass @@ -3385,138 +3625,156 @@ def foo(): pass def upblk(): for i in foo(): s.out @= Bits4( 1 ) + DUT = FuncCallAfterInComp class CaseNoArgsToRangeComp: - class DUT( Component ): + class NoArgsToRangeComp( Component ): def construct( s ): s.out = OutPort( Bits4 ) @update def upblk(): for i in range(): s.out @= Bits4( 1 ) + DUT = NoArgsToRangeComp class CaseTooManyArgsToRangeComp: - class DUT( Component ): + class TooManyArgsToRangeComp( Component ): def construct( s ): s.out = OutPort( Bits4 ) @update def upblk(): for i in range( 0, 4, 1, 1 ): s.out @= Bits4( 1 ) + DUT = TooManyArgsToRangeComp class CaseInvalidIsComp: - class DUT( Component ): + class InvalidIsComp( Component ): def construct( s ): s.out = OutPort( Bits4 ) @update def upblk(): s.out @= Bits1( 1 ) is Bits1( 1 ) + DUT = InvalidIsComp class CaseInvalidIsNotComp: - class DUT( Component ): + class InvalidIsNotComp( Component ): def construct( s ): s.out = OutPort( Bits4 ) @update def upblk(): s.out @= Bits1( 1 ) is not Bits1( 1 ) + DUT = InvalidIsNotComp class CaseInvalidInComp: - class DUT( Component ): + class InvalidInComp( Component ): def construct( s ): s.out = OutPort( Bits4 ) @update def upblk(): s.out @= Bits1( 1 ) in Bits1( 1 ) + DUT = InvalidInComp class CaseInvalidNotInComp: - class DUT( Component ): + class InvalidNotInComp( Component ): def construct( s ): s.out = OutPort( Bits4 ) @update def upblk(): s.out @= Bits1( 1 ) not in Bits1( 1 ) + DUT = InvalidNotInComp class CaseInvalidDivComp: - class DUT( Component ): + class InvalidDivComp( Component ): def construct( s ): s.out = OutPort( Bits4 ) @update def upblk(): s.out @= Bits1( 1 ) // Bits1( 1 ) + DUT = InvalidDivComp class CaseMultiOpComparisonComp: - class DUT( Component ): + class MultiOpComparisonComp( Component ): def construct( s ): s.out = OutPort( Bits4 ) @update def upblk(): s.out @= Bits1( 0 ) <= Bits2( 1 ) <= Bits2( 2 ) + DUT = MultiOpComparisonComp class CaseInvalidBreakComp: - class DUT( Component ): + class InvalidBreakComp( Component ): def construct( s ): @update def upblk(): for i in range(42): break + DUT = InvalidBreakComp class CaseInvalidContinueComp: - class DUT( Component ): + class InvalidContinueComp( Component ): def construct( s ): @update def upblk(): for i in range(42): continue + DUT = InvalidContinueComp class CaseBitsAttributeComp: - class DUT( Component ): + class BitsAttributeComp( Component ): def construct( s ): s.in_ = InPort( Bits32 ) s.out = OutPort( Bits1 ) @update def upblk(): s.out @= s.in_.foo + DUT = BitsAttributeComp class CaseStructMissingAttributeComp: - class DUT( Component ): + class StructMissingAttributeComp( Component ): def construct( s ): s.in_ = InPort( Bits32Foo ) s.out = OutPort( Bits1 ) @update def upblk(): s.out @= s.in_.bar + DUT = StructMissingAttributeComp class CaseInterfaceMissingAttributeComp: - class DUT( Component ): + class InterfaceMissingAttributeComp( Component ): def construct( s ): s.in_ = Bits32OutIfc() s.out = OutPort( Bits32 ) @update def upblk(): s.out @= s.in_.bar + DUT = InterfaceMissingAttributeComp class CaseSubCompMissingAttributeComp: - class DUT( Component ): + class SubCompMissingAttributeComp( Component ): def construct( s ): s.comp = Bits32OutComp() s.out = OutPort( Bits32 ) @update def upblk(): s.out @= s.comp.bar + DUT = SubCompMissingAttributeComp class CaseCrossHierarchyAccessComp: - class DUT( Component ): + class CrossHierarchyAccessComp( Component ): def construct( s ): s.comp = WrappedBits32OutComp() s.a_out = OutPort( Bits32 ) @update def upblk(): s.a_out @= s.comp.comp.out + DUT = CrossHierarchyAccessComp class CaseStarArgComp: - class DUT( Component ): + class StarArgComp( Component ): def construct( s, *args ): pass + DUT = StarArgComp class CaseDoubleStarArgComp: - class DUT( Component ): + class DoubleStarArgComp( Component ): def construct( s, **kwargs ): pass + DUT = DoubleStarArgComp From 77ae1e1eedc8e32eefff2d827938234037a400eb Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 23 Nov 2023 01:14:42 -0500 Subject: [PATCH 045/101] [import] Fix variable reference in C++ wrapper --- .../backends/verilog/import_/verilator_wrapper_c_template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py index 0033457e7..1bb1f7f9b 100644 --- a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py +++ b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py @@ -210,7 +210,7 @@ model->eval(); #if DUMP_VCD - if ( m->vcd_en && (ON_DEMAND_VCD_ENABLE || !ON_DEMAND_DUMP_VCD) ) {{ + if ( m->_cffi_vcd_en && (ON_DEMAND_VCD_ENABLE || !ON_DEMAND_DUMP_VCD) ) {{ // PP: this is hacky -- we want the waveform to look like all signals // except the CLK has toggled. We temporarily set the CLK signal From 8647882d422b6ce9f9c750f4976f5b715abd11ed Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 23 Nov 2023 14:32:35 -0500 Subject: [PATCH 046/101] [dependency] Add fasteners to dependency --- requirements/release.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements/release.txt b/requirements/release.txt index bfb3044d1..cd29cc899 100644 --- a/requirements/release.txt +++ b/requirements/release.txt @@ -5,3 +5,4 @@ hypothesis cffi greenlet py +fasteners From 6b3997c3ef4a3a77104eaec5da56e60426a3cf6d Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 23 Nov 2023 15:33:28 -0500 Subject: [PATCH 047/101] [ci] Upgrade to Verilator 5.016 in CI --- .github/workflows/python-package-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-package-ci.yml b/.github/workflows/python-package-ci.yml index bd5540e1a..3f49f3e04 100644 --- a/.github/workflows/python-package-ci.yml +++ b/.github/workflows/python-package-ci.yml @@ -34,8 +34,8 @@ jobs: - name: Install Verilator run: | - wget https://github.com/cornell-brg/verilator-travisci-cache/raw/master/verilator-travis-4.228.tar.gz - tar -C ${HOME} -xzf verilator-travis-4.228.tar.gz + wget https://github.com/cornell-brg/verilator-travisci-cache/raw/master/verilator-github-actions-5.016.tar.gz + tar -C ${HOME} -xzf verilator-github-actions-5.016.tar.gz echo "VERILATOR_ROOT=${HOME}/verilator" >> $GITHUB_ENV echo "PYMTL_VERILATOR_INCLUDE_DIR=${HOME}/verilator/share/verilator/include" >> $GITHUB_ENV echo "${HOME}/verilator/bin" >> $GITHUB_PATH From 609e4547a0eefcd92d5f0fcb0bf13431e4043da2 Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 23 Nov 2023 16:18:14 -0500 Subject: [PATCH 048/101] [ci] Remove VERILATOR_ROOT var in CI script --- .github/workflows/python-package-ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/python-package-ci.yml b/.github/workflows/python-package-ci.yml index 3f49f3e04..60eb4f29d 100644 --- a/.github/workflows/python-package-ci.yml +++ b/.github/workflows/python-package-ci.yml @@ -36,13 +36,11 @@ jobs: run: | wget https://github.com/cornell-brg/verilator-travisci-cache/raw/master/verilator-github-actions-5.016.tar.gz tar -C ${HOME} -xzf verilator-github-actions-5.016.tar.gz - echo "VERILATOR_ROOT=${HOME}/verilator" >> $GITHUB_ENV echo "PYMTL_VERILATOR_INCLUDE_DIR=${HOME}/verilator/share/verilator/include" >> $GITHUB_ENV echo "${HOME}/verilator/bin" >> $GITHUB_PATH - name: Check Verilator run: | - echo ${VERILATOR_ROOT} echo ${PYMTL_VERILATOR_INCLUDE_DIR} verilator --version From 2f023d559c92fb12db52264d09d318820723e797 Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 23 Nov 2023 16:42:43 -0500 Subject: [PATCH 049/101] [ci] Create symlink to Verilator include directory --- .github/workflows/python-package-ci.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/python-package-ci.yml b/.github/workflows/python-package-ci.yml index 60eb4f29d..c8fc5f0fe 100644 --- a/.github/workflows/python-package-ci.yml +++ b/.github/workflows/python-package-ci.yml @@ -36,11 +36,26 @@ jobs: run: | wget https://github.com/cornell-brg/verilator-travisci-cache/raw/master/verilator-github-actions-5.016.tar.gz tar -C ${HOME} -xzf verilator-github-actions-5.016.tar.gz + # We need to create a symlink to verilator/share/verilator/include. + # This is because the Verilator binaries are compiled on an EC2 + # instance, and that the executable contains hard-coded paths which can + # only be bypassed by defining $VERILATOR_ROOT. See a similar issue at: + # https://github.com/verilator/verilator/issues/4035 + # But when $VERILATOR_ROOT is present, Verilator assumes a different + # directory hierarchy by looking into $VERILATOR_ROOT/include, which is + # different from verilator/share/verilator/include. Verilator devs have + # mentioned this will be annoying to fix and I don't quite understand + # why; my current workaround is to symlink the correct include + # directory into the place Verilator is looking at. + ln -s ${HOME}/verilator/share/verilator/include ${HOME}/verilator/include + echo "VERILATOR_ROOT=${HOME}/verilator" >> $GITHUB_ENV echo "PYMTL_VERILATOR_INCLUDE_DIR=${HOME}/verilator/share/verilator/include" >> $GITHUB_ENV echo "${HOME}/verilator/bin" >> $GITHUB_PATH - name: Check Verilator run: | + echo ${VERILATOR_ROOT} + ls ${VERILATOR_ROOT}/include echo ${PYMTL_VERILATOR_INCLUDE_DIR} verilator --version From a317ae8e383b06e706142b1e7b2fad6ffb04dd58 Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 23 Nov 2023 17:02:31 -0500 Subject: [PATCH 050/101] [ci] Split Verilog and Yosys backend runs --- .github/workflows/python-package-ci.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-package-ci.yml b/.github/workflows/python-package-ci.yml index c8fc5f0fe..0f70e4cbe 100644 --- a/.github/workflows/python-package-ci.yml +++ b/.github/workflows/python-package-ci.yml @@ -69,7 +69,12 @@ jobs: - name: Test with pytest run: | mkdir build && cd build - pytest --cov-report xml --cov=pymtl3 .. --tb=short --hypothesis-profile CI + # Run all unit tests under pymtl3 directory except for those in the + # yosys backend (we run yosys tests in the second run). This is + # necessary to avoid using the same component name for different + # shared libraries (Verilog and Yosys backend translation result). + pytest --cov-report xml --cov=pymtl3 .. --ignore=../pymtl3/passes/backends/yosys --tb=short --hypothesis-profile CI + pytest --cov-report xml --cov=pymtl3 ../pymtl3/passes/backends/yosys --tb=short --hypothesis-profile CI - name: Upload code coverage report run: | From c1e2dfbeb781e12bcc28383a6df63d2615cab3e9 Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 23 Nov 2023 17:12:18 -0500 Subject: [PATCH 051/101] [ci] Fix Yosys test path --- .github/workflows/python-package-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package-ci.yml b/.github/workflows/python-package-ci.yml index 0f70e4cbe..ee86dc53f 100644 --- a/.github/workflows/python-package-ci.yml +++ b/.github/workflows/python-package-ci.yml @@ -73,7 +73,7 @@ jobs: # yosys backend (we run yosys tests in the second run). This is # necessary to avoid using the same component name for different # shared libraries (Verilog and Yosys backend translation result). - pytest --cov-report xml --cov=pymtl3 .. --ignore=../pymtl3/passes/backends/yosys --tb=short --hypothesis-profile CI + pytest --cov-report xml --cov=pymtl3 .. --ignore=pymtl3/passes/backends/yosys --tb=short --hypothesis-profile CI pytest --cov-report xml --cov=pymtl3 ../pymtl3/passes/backends/yosys --tb=short --hypothesis-profile CI - name: Upload code coverage report From 1e97cfd78b980c56a9cda63dc7d8be4cfe0621f7 Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 23 Nov 2023 17:22:55 -0500 Subject: [PATCH 052/101] [stdlib] Set mem msg field type_ to its correct width --- pymtl3/stdlib/mem/MemMsg.py | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/pymtl3/stdlib/mem/MemMsg.py b/pymtl3/stdlib/mem/MemMsg.py index eeab01f33..48eadb11f 100644 --- a/pymtl3/stdlib/mem/MemMsg.py +++ b/pymtl3/stdlib/mem/MemMsg.py @@ -56,13 +56,7 @@ def mk_mem_req_msg( o, a, d ): @bitstruct class MemReqMsg: - # Temporary Fix! Had to switch this back to 3-bit type field because - # the type field in the verilog memory message is only 3 bits. We - # should change the verilog memory message eventually. -cbatten - - # type_ : Bits4 - - type_ : Bits3 + type_ : Bits4 opaque : mk_bits( o ) addr : mk_bits( a ) len : mk_bits( clog2(d>>3) ) @@ -86,13 +80,7 @@ def mk_mem_resp_msg( o, d ): @bitstruct class MemRespMsg: - # Temporary Fix! Had to switch this back to 3-bit type field because - # the type field in the verilog memory message is only 3 bits. We - # should change the verilog memory message eventually. -cbatten - - # type_ : Bits4 - - type_ : Bits3 + type_ : Bits4 opaque : mk_bits( o ) test : Bits2 len : mk_bits( clog2(d>>3) ) From 9e365e48c283f02bf8e276308e54cd41221283f2 Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 23 Nov 2023 17:36:39 -0500 Subject: [PATCH 053/101] [ci] Fix Yosys test path --- .github/workflows/python-package-ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-package-ci.yml b/.github/workflows/python-package-ci.yml index ee86dc53f..9766f3ebf 100644 --- a/.github/workflows/python-package-ci.yml +++ b/.github/workflows/python-package-ci.yml @@ -68,13 +68,15 @@ jobs: - name: Test with pytest run: | + pwd mkdir build && cd build + pwd # Run all unit tests under pymtl3 directory except for those in the # yosys backend (we run yosys tests in the second run). This is # necessary to avoid using the same component name for different # shared libraries (Verilog and Yosys backend translation result). pytest --cov-report xml --cov=pymtl3 .. --ignore=pymtl3/passes/backends/yosys --tb=short --hypothesis-profile CI - pytest --cov-report xml --cov=pymtl3 ../pymtl3/passes/backends/yosys --tb=short --hypothesis-profile CI + pytest --cov-report xml --cov=pymtl3 pymtl3/passes/backends/yosys --tb=short --hypothesis-profile CI - name: Upload code coverage report run: | From d573db81f8e2a5ef91a8c5f3176205e82b52da32 Mon Sep 17 00:00:00 2001 From: pp482 Date: Wed, 6 Dec 2023 17:27:49 -0500 Subject: [PATCH 054/101] [ci] Merge coverage reports between pytest runs --- .github/workflows/python-package-ci.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/python-package-ci.yml b/.github/workflows/python-package-ci.yml index 9766f3ebf..c151f5cda 100644 --- a/.github/workflows/python-package-ci.yml +++ b/.github/workflows/python-package-ci.yml @@ -68,15 +68,13 @@ jobs: - name: Test with pytest run: | - pwd - mkdir build && cd build - pwd + mkdir -p build && cd build # Run all unit tests under pymtl3 directory except for those in the # yosys backend (we run yosys tests in the second run). This is # necessary to avoid using the same component name for different # shared libraries (Verilog and Yosys backend translation result). - pytest --cov-report xml --cov=pymtl3 .. --ignore=pymtl3/passes/backends/yosys --tb=short --hypothesis-profile CI - pytest --cov-report xml --cov=pymtl3 pymtl3/passes/backends/yosys --tb=short --hypothesis-profile CI + pytest --cov-report xml --cov=pymtl3 ../pymtl3 --ignore=../pymtl3/passes/backends/yosys --tb=short --hypothesis-profile CI + pytest --cov-report xml --cov=pymtl3 --cov-append ../pymtl3/passes/backends/yosys --tb=short --hypothesis-profile CI - name: Upload code coverage report run: | From 9ee08a531bbf5c35c10ea4e44742c167a3c94d09 Mon Sep 17 00:00:00 2001 From: pp482 Date: Wed, 6 Dec 2023 17:47:12 -0500 Subject: [PATCH 055/101] [yosys] Deprecate use of yosys backend in examples/ --- examples/ex02_cksum/cksum-translate | 16 +++++++------- examples/ex02_cksum/test/ChecksumVRTL_test.py | 18 +++++++-------- examples/ex03_proc/proc-sim | 6 ++--- examples/ex03_proc/proc-translate | 16 +++++++------- examples/ex03_proc/test/ProcVRTL_test.py | 12 +++++----- examples/ex04_xcel/proc-xcel-sim | 6 ++--- examples/ex04_xcel/proc-xcel-translate | 16 +++++++------- .../ex04_xcel/test/ChecksumXcelVRTL_test.py | 22 +++++++++---------- 8 files changed, 56 insertions(+), 56 deletions(-) diff --git a/examples/ex02_cksum/cksum-translate b/examples/ex02_cksum/cksum-translate index 2bfe1962f..3c773db8f 100755 --- a/examples/ex02_cksum/cksum-translate +++ b/examples/ex02_cksum/cksum-translate @@ -3,9 +3,9 @@ # cksum-translate [options] #========================================================================= # This script imports the RTL checksum unit from ChecksumRTL.py and -# translate it into yosys-compatible SystemVerilog. The generated -# SystemVerilog file will be dumped into the current directory ( if no -# output directory is specified ) or the specified output directory. +# translate it into SystemVerilog. The generated SystemVerilog file will +# be dumped into the current directory ( if no output directory is +# specified ) or the specified output directory. # # -h --help Display this message # @@ -18,8 +18,8 @@ import argparse import os import sys -# Import the translation pass from yosys backend -from pymtl3.passes.backends.yosys import YosysTranslationPass +# Import the translation pass from verilog backend +from pymtl3.passes.backends.verilog import VerilogTranslationPass # Hack to add project root to python path cur_dir = os.path.dirname( os.path.abspath( __file__ ) ) @@ -82,7 +82,7 @@ def main(): # Tag the checksum unit as to be translated - cksum.set_metadata( YosysTranslationPass.enable, True ) + cksum.set_metadata( VerilogTranslationPass.enable, True ) # Perform translation @@ -90,12 +90,12 @@ def main(): try: cksum.elaborate() - cksum.apply( YosysTranslationPass() ) + cksum.apply( VerilogTranslationPass() ) success = True finally: if success: path = os.getcwd() + \ - f"/{cksum.get_metadata(YosysTranslationPass.translated_filename)}" + f"/{cksum.get_metadata(VerilogTranslationPass.translated_filename)}" print("\nTranslation finished successfully!") print(f"You can find the generated SystemVerilog file at {path}.") else: diff --git a/examples/ex02_cksum/test/ChecksumVRTL_test.py b/examples/ex02_cksum/test/ChecksumVRTL_test.py index afbba1df8..14bceb12c 100644 --- a/examples/ex02_cksum/test/ChecksumVRTL_test.py +++ b/examples/ex02_cksum/test/ChecksumVRTL_test.py @@ -8,7 +8,7 @@ Date : June 6, 2019 """ from pymtl3 import * -from pymtl3.passes.backends.yosys import * +from pymtl3.passes.backends.verilog import * from pymtl3.passes.tracing import * from pymtl3.stdlib.test_utils.test_helpers import finalize_verilator @@ -32,10 +32,10 @@ def checksum_vrtl( words ): dut = ChecksumRTL() dut.elaborate() - # Translate the checksum unit and import it back in using the yosys + # Translate the checksum unit and import it back in using the verilog # backend - dut.set_metadata( YosysTranslationImportPass.enable, True ) - dut = YosysTranslationImportPass()( dut ) + dut.set_metadata( VerilogTranslationImportPass.enable, True ) + dut = VerilogTranslationImportPass()( dut ) # Create a simulator dut.elaborate() @@ -99,17 +99,17 @@ def run_sim( s, th ): # Check command line arguments for vcd dumping if vcd_file_name: th.set_metadata( VcdGenerationPass.vcd_file_name, vcd_file_name ) - th.dut.set_metadata( YosysVerilatorImportPass.vl_trace, True ) - th.dut.set_metadata( YosysVerilatorImportPass.vl_trace_filename, vcd_file_name ) + th.dut.set_metadata( VerilogVerilatorImportPass.vl_trace, True ) + th.dut.set_metadata( VerilogVerilatorImportPass.vl_trace_filename, vcd_file_name ) - # Translate the DUT and import it back in using the yosys backend. - th.dut.set_metadata( YosysTranslationImportPass.enable, True ) + # Translate the DUT and import it back in using the verilog backend. + th.dut.set_metadata( VerilogTranslationImportPass.enable, True ) # ''' TUTORIAL TASK '''''''''''''''''''''''''''''''''''''''''''''''''' # Apply the translation-import and simulation passes # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''\/ - th = YosysTranslationImportPass()( th ) + th = VerilogTranslationImportPass()( th ) th.apply( DefaultPassGroup(linetrace=False) ) # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''/\ diff --git a/examples/ex03_proc/proc-sim b/examples/ex03_proc/proc-sim index bb5ec93a9..6badb8455 100755 --- a/examples/ex03_proc/proc-sim +++ b/examples/ex03_proc/proc-sim @@ -121,10 +121,10 @@ def main(): # Apply translation pass and import pass if required if opts.translate: - from pymtl3.passes.backends.yosys import YosysTranslationImportPass + from pymtl3.passes.backends.verilog import VerilogTranslationImportPass model.elaborate() - model.proc.set_metadata( YosysTranslationImportPass.enable, True ) - model = YosysTranslationImportPass()( model ) + model.proc.set_metadata( VerilogTranslationImportPass.enable, True ) + model = VerilogTranslationImportPass()( model ) model.apply( DefaultPassGroup(linetrace=opts.trace) ) diff --git a/examples/ex03_proc/proc-translate b/examples/ex03_proc/proc-translate index 5f8ff7269..7256aaa11 100755 --- a/examples/ex03_proc/proc-translate +++ b/examples/ex03_proc/proc-translate @@ -3,9 +3,9 @@ # proc-translate [options] #========================================================================= # This script imports the RTL processor from ProcRTL.py and -# translate it into yosys-compatible SystemVerilog. The generated -# SystemVerilog file will be dumped into the current directory ( if no -# output directory is specified ) or the specified output directory. +# translate it into SystemVerilog. The generated SystemVerilog file will +# be dumped into the current directory ( if no output directory is +# specified ) or the specified output directory. # # -h --help Display this message # @@ -20,8 +20,8 @@ import argparse import os import sys -# Import the translation pass from yosys backend -from pymtl3.passes.backends.yosys import YosysTranslationPass +# Import the translation pass from verilog backend +from pymtl3.passes.backends.verilog import VerilogTranslationPass # Hack to add project root to python path cur_dir = os.path.dirname( os.path.abspath( __file__ ) ) @@ -84,7 +84,7 @@ def main(): # Tag the processor as to be translated - proc.set_metadata( YosysTranslationPass.enable, True ) + proc.set_metadata( VerilogTranslationPass.enable, True ) # Perform translation @@ -92,12 +92,12 @@ def main(): try: proc.elaborate() - proc.apply( YosysTranslationPass() ) + proc.apply( VerilogTranslationPass() ) success = True finally: if success: path = os.getcwd() + \ - f"/{proc.get_metadata(YosysTranslationPass.translated_filename)}" + f"/{proc.get_metadata(VerilogTranslationPass.translated_filename)}" print("\nTranslation finished successfully!") print(f"You can find the generated SystemVerilog file at {path}.") else: diff --git a/examples/ex03_proc/test/ProcVRTL_test.py b/examples/ex03_proc/test/ProcVRTL_test.py index 174fb2c0d..a339653f2 100644 --- a/examples/ex03_proc/test/ProcVRTL_test.py +++ b/examples/ex03_proc/test/ProcVRTL_test.py @@ -13,7 +13,7 @@ from examples.ex03_proc.ProcRTL import ProcRTL from pymtl3 import * -from pymtl3.passes.backends.yosys import * +from pymtl3.passes.backends.verilog import * from pymtl3.passes.tracing import * from pymtl3.stdlib.test_utils.test_helpers import finalize_verilator @@ -53,13 +53,13 @@ def run_sim( s, th, gen_test ): # Check command line arguments for vcd dumping if vcd_file_name: th.set_metadata( VcdGenerationPass.vcd_file_name, vcd_file_name ) - th.proc.set_metadata( YosysVerilatorImportPass.vl_trace, True ) - th.proc.set_metadata( YosysVerilatorImportPass.vl_trace_filename, vcd_file_name ) + th.proc.set_metadata( VerilogVerilatorImportPass.vl_trace, True ) + th.proc.set_metadata( VerilogVerilatorImportPass.vl_trace_filename, vcd_file_name ) - # Translate the DUT and import it back in using the yosys backend. - th.proc.set_metadata( YosysTranslationImportPass.enable, True ) + # Translate the DUT and import it back in using the verilog backend. + th.proc.set_metadata( VerilogTranslationImportPass.enable, True ) - th = YosysTranslationImportPass()( th ) + th = VerilogTranslationImportPass()( th ) # Create a simulator and run simulation th.apply( DefaultPassGroup(linetrace=True) ) diff --git a/examples/ex04_xcel/proc-xcel-sim b/examples/ex04_xcel/proc-xcel-sim index 81b56d58e..0bf50b8aa 100755 --- a/examples/ex04_xcel/proc-xcel-sim +++ b/examples/ex04_xcel/proc-xcel-sim @@ -207,10 +207,10 @@ def main(): # Apply translation pass and import pass if required if opts.translate: - from pymtl3.passes.backends.yosys import YosysTranslationImportPass + from pymtl3.passes.backends.verilog import VerilogTranslationImportPass model.elaborate() - model.dut.set_metadata( YosysTranslationImportPass.enable, True ) - model = YosysTranslationImportPass()( model ) + model.dut.set_metadata( VerilogTranslationImportPass.enable, True ) + model = VerilogTranslationImportPass()( model ) model.apply( DefaultPassGroup(linetrace=opts.trace) ) diff --git a/examples/ex04_xcel/proc-xcel-translate b/examples/ex04_xcel/proc-xcel-translate index e783b8913..8aa119bd1 100755 --- a/examples/ex04_xcel/proc-xcel-translate +++ b/examples/ex04_xcel/proc-xcel-translate @@ -3,9 +3,9 @@ # proc-xcel-translate [options] #========================================================================= # This script imports the RTL processor-accelerator unit and translate it -# into yosys-compatible SystemVerilog. The generated SystemVerilog file -# will be dumped into the current directory ( if no output directory is -# specified ) or the specified output directory. +# into SystemVerilog. The generated SystemVerilog file will be dumped into +# the current directory ( if no output directory is specified ) or the +# specified output directory. # # -h --help Display this message # @@ -34,8 +34,8 @@ from examples.ex03_proc.ProcRTL import ProcRTL from examples.ex04_xcel.ChecksumXcelRTL import ChecksumXcelRTL from examples.ex04_xcel.ProcXcel import ProcXcel -# Import the translation pass from yosys backend -from pymtl3.passes.backends.yosys import YosysTranslationPass +# Import the translation pass from Verilog backend +from pymtl3.passes.backends.verilog import VerilogTranslationPass #========================================================================= # Command line processing @@ -105,7 +105,7 @@ A valid output directory should be alloy-asic/designs//rtl # Tag the processor-accelerator unit as to be translated - proc_xcel.set_metadata( YosysTranslationPass.enable, True ) + proc_xcel.set_metadata( VerilogTranslationPass.enable, True ) # Perform translation @@ -113,12 +113,12 @@ A valid output directory should be alloy-asic/designs//rtl try: proc_xcel.elaborate() - proc_xcel.apply( YosysTranslationPass() ) + proc_xcel.apply( VerilogTranslationPass() ) success = True finally: if success: path = os.getcwd() + \ - f"/{proc_xcel.get_metadata(YosysTranslationPass.translated_filename)}" + f"/{proc_xcel.get_metadata(VerilogTranslationPass.translated_filename)}" if opts.output_dir: # Upon success, symlink the file to outputs/design.v which is the diff --git a/examples/ex04_xcel/test/ChecksumXcelVRTL_test.py b/examples/ex04_xcel/test/ChecksumXcelVRTL_test.py index 62989ef8c..aded7a31e 100644 --- a/examples/ex04_xcel/test/ChecksumXcelVRTL_test.py +++ b/examples/ex04_xcel/test/ChecksumXcelVRTL_test.py @@ -10,7 +10,7 @@ import pytest from pymtl3 import * -from pymtl3.passes.backends.yosys import YosysTranslationImportPass +from pymtl3.passes.backends.verilog import VerilogTranslationImportPass from pymtl3.stdlib.test_utils.test_helpers import finalize_verilator from ..ChecksumXcelRTL import ChecksumXcelRTL @@ -20,7 +20,7 @@ # Wrap Xcel into a function #------------------------------------------------------------------------- # [checksum_xcel_vrtl] creates an RTL checksum accelerator, translates -# it using the yosys backend and imports the translated model back, feeds +# it using the verilog backend and imports the translated model back, feeds # in the input, ticks it, gets the response, and returns the result. def checksum_xcel_vrtl( words ): @@ -30,10 +30,10 @@ def checksum_xcel_vrtl( words ): dut = ChecksumXcelRTL() dut.elaborate() - # Translate the checksum unit and import it back in using the yosys + # Translate the checksum unit and import it back in using the verilog # backend - dut.set_metadata( YosysTranslationImportPass.enable, True ) - dut = YosysTranslationImportPass()( dut ) + dut.set_metadata( VerilogTranslationImportPass.enable, True ) + dut = VerilogTranslationImportPass()( dut ) # Create a simulator dut.elaborate() @@ -92,19 +92,19 @@ def run_sim( s, th ): vcd_file_name = s.__class__.cmdline_opts["dump_vcd"] max_cycles = s.__class__.cmdline_opts["max_cycles"] or 10000 - # Translate the DUT and import it back in using the yosys backend. + # Translate the DUT and import it back in using the verilog backend. th.elaborate() # Check command line arguments for vcd dumping if vcd_file_name: th.set_metadata( VcdGenerationPass.vcd_file_name, vcd_file_name ) - th.dut.set_metadata( YosysVerilatorImportPass.vl_trace, True ) - th.dut.set_metadata( YosysVerilatorImportPass.vl_trace_filename, vcd_file_name ) + th.dut.set_metadata( VerilogVerilatorImportPass.vl_trace, True ) + th.dut.set_metadata( VerilogVerilatorImportPass.vl_trace_filename, vcd_file_name ) - # Translate the DUT and import it back in using the yosys backend. - th.dut.set_metadata( YosysTranslationImportPass.enable, True ) + # Translate the DUT and import it back in using the verilog backend. + th.dut.set_metadata( VerilogTranslationImportPass.enable, True ) - th = YosysTranslationImportPass()( th ) + th = VerilogTranslationImportPass()( th ) # Create a simulator th.apply( DefaultPassGroup() ) From 0edc9a7b83283459e3e812b1d1624cafa3ef7f6a Mon Sep 17 00:00:00 2001 From: pp482 Date: Wed, 6 Dec 2023 18:52:24 -0500 Subject: [PATCH 056/101] [err_msg] Use better message when slicing on Bitstruct signals --- pymtl3/dsl/Connectable.py | 5 ++++- pymtl3/dsl/test/DataStruct_test.py | 24 +++++++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/pymtl3/dsl/Connectable.py b/pymtl3/dsl/Connectable.py index d730a9697..6a8683251 100644 --- a/pymtl3/dsl/Connectable.py +++ b/pymtl3/dsl/Connectable.py @@ -251,7 +251,10 @@ def __setitem__( s, idx, v ): def __getitem__( s, idx ): if not issubclass( s._dsl.Type, Bits ): - raise InvalidConnectionError( "We don't allow slicing on non-Bits signals." ) + host = s.get_host_component() + name = s.get_field_name() + type_name = s._dsl.Type.__name__ + raise InvalidConnectionError( f"Slicing on Bitstruct signal ({name} of {type_name} in {str(host)}) is not allowed!" ) # Turn index into a slice if isinstance( idx, int ): diff --git a/pymtl3/dsl/test/DataStruct_test.py b/pymtl3/dsl/test/DataStruct_test.py index eca836a62..b20cfe862 100644 --- a/pymtl3/dsl/test/DataStruct_test.py +++ b/pymtl3/dsl/test/DataStruct_test.py @@ -6,12 +6,13 @@ Author : Shunning Jiang Date : Apr 16, 2018 """ -from pymtl3.datatypes import Bits16, Bits32, bitstruct +from pymtl3.datatypes import Bits16, Bits32, Bits64, bitstruct from pymtl3.dsl.ComponentLevel1 import update from pymtl3.dsl.ComponentLevel2 import update_ff from pymtl3.dsl.ComponentLevel3 import ComponentLevel3, connect from pymtl3.dsl.Connectable import InPort, OutPort, Wire from pymtl3.dsl.errors import ( + InvalidConnectionError, MultiWriterError, NoWriterError, UpdateFFBlockWriteError, @@ -581,3 +582,24 @@ def ffs(): print("{} is thrown\n{}".format( e.__class__.__name__, e )) return raise Exception("Should've thrown UpdateFFNonTopLevelSignalError.") + +def test_slicing_on_non_bits_error_msg(): + + class Bitstruct2Bits( ComponentLevel3 ): + def construct( s ): + s.pt_bitstruct = InPort ( SomeMsg ) + s.pt_bits = OutPort( Bits64 ) + @update + def upblk(): + s.pt_bits @= s.pt_bitstruct[0:64] + + m = Bitstruct2Bits() + try: + m.elaborate() + except InvalidConnectionError as e: + err_msg = str(e) + print("{} is thrown\n{}".format( e.__class__.__name__, err_msg )) + assert "SomeMsg" in err_msg + assert "pt_bitstruct" in err_msg + return + raise Exception("Should've thrown InvalidConnectionError.") From c23a673c1a9fce8b4eff09c8c4833372eb7e6817 Mon Sep 17 00:00:00 2001 From: pp482 Date: Wed, 6 Dec 2023 18:58:20 -0500 Subject: [PATCH 057/101] [README] Point users to compile Verilator v5.016 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1137955dc..042c02747 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ install Verilator from source using the following commands: $ cd ${HOME}/src $ git clone https://github.com/verilator/verilator.git $ cd verilator - $ git checkout v4.228 + $ git checkout v5.016 $ ./configure $ make $ sudo make install From 8e546d86a83d099038c8a549cb8971231a7fd545 Mon Sep 17 00:00:00 2001 From: pp482 Date: Wed, 6 Dec 2023 19:53:29 -0500 Subject: [PATCH 058/101] [dsl] Add support for non-s args to refer to self --- pymtl3/dsl/ComponentLevel2.py | 15 +++++++++- pymtl3/dsl/test/ComponentAPI_test.py | 45 +++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/pymtl3/dsl/ComponentLevel2.py b/pymtl3/dsl/ComponentLevel2.py index ad1c6604b..ca61167f3 100644 --- a/pymtl3/dsl/ComponentLevel2.py +++ b/pymtl3/dsl/ComponentLevel2.py @@ -65,6 +65,19 @@ def __new__( cls, *args, **kwargs ): inst._dsl.WR_U_constraints = defaultdict(set) inst._dsl.name_func = {} + # Check and make sure `construct` method exists. + if not hasattr(inst, "construct"): + raise NotImplementedError("construct method, where the design is built," + " is not implemented in {}".format( cls.__name__ ) ) + + # Keep track of the name that refers to `self` in `elaborate`. + args = inspect.getfullargspec(inst.construct).args + if len(args) == 0: + raise ValueError("the construct method does not have a positional argument" + " that refers to self! (class {})".format( cls.__name__ ) ) + + inst._dsl.elab_self = args[0] + return inst def _cache_func_meta( s, func, is_update_ff, given=None ): @@ -211,7 +224,7 @@ def lookup_variable( obj, name_depth, node_depth ): # Now we turn names into actual objects for obj_name, nodelist, op in names: - if obj_name[0][0] == "s": + if obj_name[0][0] == s._dsl.elab_self: objs = set() lookup_variable( s, 1, 1 ) diff --git a/pymtl3/dsl/test/ComponentAPI_test.py b/pymtl3/dsl/test/ComponentAPI_test.py index 5e4671cdd..b01811824 100644 --- a/pymtl3/dsl/test/ComponentAPI_test.py +++ b/pymtl3/dsl/test/ComponentAPI_test.py @@ -19,7 +19,7 @@ update, update_ff, ) -from pymtl3.dsl.errors import InvalidAPICallError +from pymtl3.dsl.errors import InvalidAPICallError, UpdateBlockWriteError from .sim_utils import simple_sim_pass @@ -405,6 +405,49 @@ def construct( s ): assert u[1].__name__ == "up_ff" assert u[2].__name__ == "up_out2" +def test_elab_self_instead_of_s(): + + class A( Component ): + def construct( self ): + self.in_ = InPort( Bits32 ) + self.out = OutPort( Bits32 ) + + @update_ff + def upblk(): + self.out <<= self.in_ + + class Top( Component ): + def construct( self ): + self.in_ = InPort( Bits32 ) + + self.a = A() + self.b = A() + + self.a.in_ //= self.in_ + self.b.in_ //= self.a.out + + a = Top() + a.elaborate() + +def test_elab_self_instead_of_s_blking_assign_in_update(): + + class Top( Component ): + def construct( self ): + self.in_ = InPort( Bits32 ) + self.out = OutPort( Bits32 ) + + @update + def upblk(): + self.out <<= self.in_ + + a = Top() + try: + a.elaborate() + except UpdateBlockWriteError as e: + return + + raise Exception("Should have thrown UpdateBlockWriteError!") + # def test_garbage_collection(): # class X( Component ): From 02b17d76211344dcba321e835b4128b16adecc50 Mon Sep 17 00:00:00 2001 From: Peitian Pan <31896789+ptpan@users.noreply.github.com> Date: Wed, 6 Dec 2023 16:55:51 -0800 Subject: [PATCH 059/101] [dsl] Fix typo --- pymtl3/dsl/ComponentLevel2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymtl3/dsl/ComponentLevel2.py b/pymtl3/dsl/ComponentLevel2.py index ca61167f3..caa2bfce5 100644 --- a/pymtl3/dsl/ComponentLevel2.py +++ b/pymtl3/dsl/ComponentLevel2.py @@ -70,7 +70,7 @@ def __new__( cls, *args, **kwargs ): raise NotImplementedError("construct method, where the design is built," " is not implemented in {}".format( cls.__name__ ) ) - # Keep track of the name that refers to `self` in `elaborate`. + # Keep track of the name that refers to `self` in `construct`. args = inspect.getfullargspec(inst.construct).args if len(args) == 0: raise ValueError("the construct method does not have a positional argument" From 0be16f7e04cd5ba72ffa01b31b98dc4d74eb222b Mon Sep 17 00:00:00 2001 From: pp482 Date: Wed, 6 Dec 2023 20:16:04 -0500 Subject: [PATCH 060/101] [yosys] Deprecate yosys backend --- pymtl3/passes/backends/verilog/util/test_utility.py | 2 +- pymtl3/passes/backends/yosys/__init__.py | 5 ++++- pymtl3/stdlib/test_utils/test_helpers.py | 6 ++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/pymtl3/passes/backends/verilog/util/test_utility.py b/pymtl3/passes/backends/verilog/util/test_utility.py index 9294bcd72..b0c0c6b72 100644 --- a/pymtl3/passes/backends/verilog/util/test_utility.py +++ b/pymtl3/passes/backends/verilog/util/test_utility.py @@ -15,7 +15,6 @@ from pymtl3.passes.rtlir import RTLIRType as rt from pymtl3.stdlib.test_utils import TestVectorSimulator -from ...yosys import YosysTranslationImportPass from .. import VerilogTranslationImportPass #========================================================================= @@ -313,6 +312,7 @@ def tv_out( model, test_vector ): dut.set_metadata( VerilogTranslationImportPass.enable, True ) imported_obj = VerilogTranslationImportPass()( dut ) elif backend == "yosys": + from ...yosys import YosysTranslationImportPass dut.set_metadata( YosysTranslationImportPass.enable, True ) imported_obj = YosysTranslationImportPass()( dut ) diff --git a/pymtl3/passes/backends/yosys/__init__.py b/pymtl3/passes/backends/yosys/__init__.py index 83eafef10..e54f16a49 100644 --- a/pymtl3/passes/backends/yosys/__init__.py +++ b/pymtl3/passes/backends/yosys/__init__.py @@ -1,6 +1,9 @@ +from warnings import warn +warn("The Yosys backend has been deprecated." + " Please consider using tools like sv2v for pure Verilog code!") from ..verilog.VerilogPlaceholder import VerilogPlaceholder as YosysPlaceholder from ..verilog.VerilogPlaceholderPass import ( - VerilogPlaceholderPass as YosysPlaceholderPass, + VerilogPlaceholderPass as YosysPlaceholderPass, ) from .import_.YosysVerilatorImportPass import YosysVerilatorImportPass from .translation.YosysTranslationPass import YosysTranslationPass diff --git a/pymtl3/stdlib/test_utils/test_helpers.py b/pymtl3/stdlib/test_utils/test_helpers.py index bfb6054a0..a4ad72e39 100644 --- a/pymtl3/stdlib/test_utils/test_helpers.py +++ b/pymtl3/stdlib/test_utils/test_helpers.py @@ -14,8 +14,6 @@ from pymtl3 import * from pymtl3.datatypes import is_bitstruct_class from pymtl3.passes.backends.verilog import * -from pymtl3.passes.backends.yosys.YosysTranslationImportPass import YosysTranslationImportPass -from pymtl3.passes.backends.yosys.import_.YosysVerilatorImportPass import YosysVerilatorImportPass from pymtl3.passes.tracing import VcdGenerationPass, PrintTextWavePass #------------------------------------------------------------------------- @@ -127,6 +125,9 @@ def config_model_with_cmdline_opts( top, cmdline_opts, duts ): dut.set_metadata( VerilogVerilatorImportPass.vl_trace_on_demand_portname, on_demand_vcd_portname ) elif test_yosys_verilog: + from pymtl3.passes.backends.yosys.YosysTranslationImportPass import YosysTranslationImportPass + from pymtl3.passes.backends.yosys.import_.YosysVerilatorImportPass import YosysVerilatorImportPass + dut.set_metadata( YosysTranslationImportPass.enable, True ) dut.set_metadata( YosysVerilatorImportPass.vl_xinit, test_yosys_verilog ) @@ -139,6 +140,7 @@ def config_model_with_cmdline_opts( top, cmdline_opts, duts ): dut.set_metadata( YosysVerilatorImportPass.vl_trace_on_demand_portname, on_demand_vcd_portname ) if test_yosys_verilog: + from pymtl3.passes.backends.yosys.YosysTranslationImportPass import YosysTranslationImportPass top.apply( VerilogPlaceholderPass() ) top = YosysTranslationImportPass()( top ) else: From 512557dab2652884ca2c845c9a8214cb4d06a11a Mon Sep 17 00:00:00 2001 From: pp482 Date: Wed, 6 Dec 2023 20:28:40 -0500 Subject: [PATCH 061/101] [coverage] Disable couldnt-parse coverage warning --- .coveragerc | 4 ++++ .github/workflows/python-package-ci.yml | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 000000000..c0649eec8 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,4 @@ +# Disable warnings about could not parsing files because PyMTL generates +# code (especially for update blocks) on the fly. +[run] +disable_warnings = couldnt-parse diff --git a/.github/workflows/python-package-ci.yml b/.github/workflows/python-package-ci.yml index c151f5cda..b131b7329 100644 --- a/.github/workflows/python-package-ci.yml +++ b/.github/workflows/python-package-ci.yml @@ -73,8 +73,12 @@ jobs: # yosys backend (we run yosys tests in the second run). This is # necessary to avoid using the same component name for different # shared libraries (Verilog and Yosys backend translation result). - pytest --cov-report xml --cov=pymtl3 ../pymtl3 --ignore=../pymtl3/passes/backends/yosys --tb=short --hypothesis-profile CI - pytest --cov-report xml --cov=pymtl3 --cov-append ../pymtl3/passes/backends/yosys --tb=short --hypothesis-profile CI + pytest --cov-config=../.coveragerc --cov-report xml --cov=pymtl3 \ + ../pymtl3 --ignore=../pymtl3/passes/backends/yosys --tb=short \ + --hypothesis-profile CI + pytest --cov-config=../.coveragerc --cov-report xml --cov=pymtl3 --cov-append \ + ../pymtl3/passes/backends/yosys --tb=short \ + --hypothesis-profile CI - name: Upload code coverage report run: | From 58ea59a1fd8f167bafb248e40752c34dbb6a3820 Mon Sep 17 00:00:00 2001 From: pp482 Date: Wed, 6 Dec 2023 20:30:20 -0500 Subject: [PATCH 062/101] [test] Remove return values from mamba unit tests --- pymtl3/passes/mamba/test/HeuTopoUnrollSim_test.py | 1 - pymtl3/passes/mamba/test/Mamba2020Pass_test.py | 1 - pymtl3/passes/mamba/test/UnrollSim_test.py | 1 - 3 files changed, 3 deletions(-) diff --git a/pymtl3/passes/mamba/test/HeuTopoUnrollSim_test.py b/pymtl3/passes/mamba/test/HeuTopoUnrollSim_test.py index 96b5154b8..bcdb27b7f 100644 --- a/pymtl3/passes/mamba/test/HeuTopoUnrollSim_test.py +++ b/pymtl3/passes/mamba/test/HeuTopoUnrollSim_test.py @@ -49,7 +49,6 @@ def line_trace( s ): assert A.out == T * N A.sim_tick() T += 1 - return A def test_equal_top_level(): class A(Component): diff --git a/pymtl3/passes/mamba/test/Mamba2020Pass_test.py b/pymtl3/passes/mamba/test/Mamba2020Pass_test.py index 460fe3083..0578d4fdc 100644 --- a/pymtl3/passes/mamba/test/Mamba2020Pass_test.py +++ b/pymtl3/passes/mamba/test/Mamba2020Pass_test.py @@ -50,7 +50,6 @@ def line_trace( s ): assert A.out == T * N A.sim_tick() T += 1 - return A def test_combinational_loop(): diff --git a/pymtl3/passes/mamba/test/UnrollSim_test.py b/pymtl3/passes/mamba/test/UnrollSim_test.py index a5ccfad66..0881a5b22 100644 --- a/pymtl3/passes/mamba/test/UnrollSim_test.py +++ b/pymtl3/passes/mamba/test/UnrollSim_test.py @@ -49,7 +49,6 @@ def line_trace( s ): assert A.out == T * N A.sim_tick() T += 1 - return A def test_equal_top_level(): class A(Component): From 7e55309aba074be78b387f9a6127953f60ce379e Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 7 Dec 2023 00:50:47 -0500 Subject: [PATCH 063/101] [import] Fix data type declaration for signals of different widths --- .../verilog/import_/VerilogVerilatorImportPass.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py index d9c6dc232..f943ce21c 100644 --- a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py +++ b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py @@ -699,13 +699,16 @@ def gen_signal_decl_c( s, name, port ): c_dim = s._get_c_dim( port ) nbits = s._get_c_nbits( port ) + + # According to the C++ spec, unsigned char, unsigned short, and unsigned + # long long have the width of 8, 16, 64 across the four most popular + # data models (LP32, ILP32, LLP64, LP64); unsigned long has width 32 + # with data model LP32, ILP32, LLP64 but not LP64. UNSIGNED_8 = 'unsigned char' UNSIGNED_16 = 'unsigned short' - UNSIGNED_32 = 'unsigned int' - if sys.maxsize > 2**32: - UNSIGNED_64 = 'unsigned long' - else: - UNSIGNED_64 = 'unsigned long long' + UNSIGNED_32 = 'unsigned int' if sys.maxsize > 2**31-1 else 'unsigned long' + UNSIGNED_64 = 'unsigned long long' + if nbits <= 8: data_type = UNSIGNED_8 elif nbits <= 16: data_type = UNSIGNED_16 elif nbits <= 32: data_type = UNSIGNED_32 From 7ce58d5b8fb3476966dc9644f1e64b67ffa323dd Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 7 Dec 2023 00:51:34 -0500 Subject: [PATCH 064/101] [setup] Add fasteners to dependency --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 6264d2b9d..a3b849d9d 100644 --- a/setup.py +++ b/setup.py @@ -87,6 +87,7 @@ def get_long_description(): 'cffi', 'greenlet', 'py', + 'fasteners', ], entry_points = { From c8e2ab556a7b6bb262b0d1fcdb72483c426a3f68 Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 7 Dec 2023 01:10:06 -0500 Subject: [PATCH 065/101] [import] Use explicitly sized uint types for signal declaration --- .../import_/VerilogVerilatorImportPass.py | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py index f943ce21c..0bda9efbc 100644 --- a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py +++ b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py @@ -699,21 +699,11 @@ def gen_signal_decl_c( s, name, port ): c_dim = s._get_c_dim( port ) nbits = s._get_c_nbits( port ) - - # According to the C++ spec, unsigned char, unsigned short, and unsigned - # long long have the width of 8, 16, 64 across the four most popular - # data models (LP32, ILP32, LLP64, LP64); unsigned long has width 32 - # with data model LP32, ILP32, LLP64 but not LP64. - UNSIGNED_8 = 'unsigned char' - UNSIGNED_16 = 'unsigned short' - UNSIGNED_32 = 'unsigned int' if sys.maxsize > 2**31-1 else 'unsigned long' - UNSIGNED_64 = 'unsigned long long' - - if nbits <= 8: data_type = UNSIGNED_8 - elif nbits <= 16: data_type = UNSIGNED_16 - elif nbits <= 32: data_type = UNSIGNED_32 - elif nbits <= 64: data_type = UNSIGNED_64 - else: data_type = UNSIGNED_32 + if nbits <= 8: data_type = 'uint8_t' + elif nbits <= 16: data_type = 'uint16_t' + elif nbits <= 32: data_type = 'uint32_t' + elif nbits <= 64: data_type = 'uint64_t' + else: data_type = 'uint32_t' name = s._verilator_name( name ) return f'{data_type} * {name}{c_dim};' From aa19bfb4951c9e0b875c3b899e0a4944b100d3d4 Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 7 Dec 2023 01:29:55 -0500 Subject: [PATCH 066/101] [rtlir] Add visit_Constant to behavioral visitor --- .../rtlir/behavioral/BehavioralRTLIRGenL1Pass.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pymtl3/passes/rtlir/behavioral/BehavioralRTLIRGenL1Pass.py b/pymtl3/passes/rtlir/behavioral/BehavioralRTLIRGenL1Pass.py index e0b8af481..599068a50 100644 --- a/pymtl3/passes/rtlir/behavioral/BehavioralRTLIRGenL1Pass.py +++ b/pymtl3/passes/rtlir/behavioral/BehavioralRTLIRGenL1Pass.py @@ -435,6 +435,14 @@ def visit_Num( s, node ): ret.ast = node return ret + def visit_Constant( s, node ): + val = node.value + if isinstance( val, int ): + ret = bir.Number( val ) + ret.ast = node + return ret + raise PyMTLSyntaxError( s.blk, node, 'invalid constant: not an integer!' ) + def visit_If( s, node ): raise NotImplementedError() def visit_For( s, node ): raise NotImplementedError() @@ -646,3 +654,6 @@ def visit_Name( s, node ): def visit_Num( s, node ): return node.n + + def visit_Constant( s, node ): + return node.value From 824ee65754498e7f5c8c84446c092bab86007cbd Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 7 Dec 2023 01:30:23 -0500 Subject: [PATCH 067/101] [test] Use variable instead of int in extslice test case --- pymtl3/passes/testcases/test_cases.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pymtl3/passes/testcases/test_cases.py b/pymtl3/passes/testcases/test_cases.py index fd5603399..8c6351d62 100644 --- a/pymtl3/passes/testcases/test_cases.py +++ b/pymtl3/passes/testcases/test_cases.py @@ -3378,7 +3378,8 @@ class ExtSliceComp( Component ): def construct( s ): @update def upblk(): - 42[ 1:2:3, 2:4:6 ] + a = None + a[ 1:2:3, 2:4:6 ] DUT = ExtSliceComp class CaseAddComponentComp: From 6da7397f212eafff9dab4505d53d9b18fff2465d Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 7 Dec 2023 01:45:37 -0500 Subject: [PATCH 068/101] [test] Fix visit_Str test --- pymtl3/passes/rtlir/behavioral/BehavioralRTLIRGenL1Pass.py | 2 +- .../passes/rtlir/behavioral/test/BehavioralRTLIRL1Pass_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pymtl3/passes/rtlir/behavioral/BehavioralRTLIRGenL1Pass.py b/pymtl3/passes/rtlir/behavioral/BehavioralRTLIRGenL1Pass.py index 599068a50..9fdfe8dcc 100644 --- a/pymtl3/passes/rtlir/behavioral/BehavioralRTLIRGenL1Pass.py +++ b/pymtl3/passes/rtlir/behavioral/BehavioralRTLIRGenL1Pass.py @@ -509,7 +509,7 @@ def visit_Repr( s, node ): raise PyMTLSyntaxError( s.blk, node, 'invalid operation: repr' ) def visit_Str( s, node ): - raise PyMTLSyntaxError( s.blk, node, 'invalid operation: str' ) + raise PyMTLSyntaxError( s.blk, node, 'invalid constant' ) def visit_ClassDef( s, node ): raise PyMTLSyntaxError( s.blk, node, 'invalid operation: classdef' ) diff --git a/pymtl3/passes/rtlir/behavioral/test/BehavioralRTLIRL1Pass_test.py b/pymtl3/passes/rtlir/behavioral/test/BehavioralRTLIRL1Pass_test.py index 1c8f9ff6f..2833d13a7 100644 --- a/pymtl3/passes/rtlir/behavioral/test/BehavioralRTLIRL1Pass_test.py +++ b/pymtl3/passes/rtlir/behavioral/test/BehavioralRTLIRL1Pass_test.py @@ -391,7 +391,7 @@ def test_Repr( do_test ): do_test( CaseReprComp ) def test_Str( do_test ): - with expected_failure( PyMTLSyntaxError, "invalid operation: str" ): + with expected_failure( PyMTLSyntaxError, "invalid constant" ): do_test( CaseStrComp ) def test_Classdef( do_test ): From 1bf1b7dfa623d119457640c42ec0337b5a16d66e Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 7 Dec 2023 02:45:11 -0500 Subject: [PATCH 069/101] [import] Add an import test that uses assertion --- .../import_/test/ImportedObject_test.py | 23 +++++++++++++++++++ .../backends/verilog/import_/test/VAssert.v | 12 ++++++++++ 2 files changed, 35 insertions(+) create mode 100644 pymtl3/passes/backends/verilog/import_/test/VAssert.v diff --git a/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py b/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py index 7a124b788..3160fa5f4 100644 --- a/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py +++ b/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py @@ -524,3 +524,26 @@ def construct( s ): a.sim_tick() a.finalize() + +def test_vl_assertion( do_test ): + class VAssert( Component, VerilogPlaceholder ): + def construct( s ): + s.in_ = InPort( 10 ) + s.out = OutPort( 10 ) + s.set_metadata( VerilogPlaceholderPass.src_file, dirname(__file__)+'/VAssert.v' ) + a = VAssert() + a.elaborate() + a.apply( VerilogPlaceholderPass() ) + a = VerilogTranslationImportPass()( a ) + a.apply( DefaultPassGroup(linetrace=True) ) + + try: + for i in range(16): + a.in_ @= Bits10(i) + a.sim_tick() + except AssertionError as e: + return + finally: + a.finalize() + + raise Exception("Should have thrown a Verilator assertion error!") diff --git a/pymtl3/passes/backends/verilog/import_/test/VAssert.v b/pymtl3/passes/backends/verilog/import_/test/VAssert.v new file mode 100644 index 000000000..796d89950 --- /dev/null +++ b/pymtl3/passes/backends/verilog/import_/test/VAssert.v @@ -0,0 +1,12 @@ +module test_VAssert ( + input [9:0] in_, + output logic [9:0] out +); + + assign out = in_; + + always_comb begin + assert (in_ != 4); + end + +endmodule From 25c075294073bb6e6a49ada212ea446190f9860e Mon Sep 17 00:00:00 2001 From: pp482 Date: Thu, 7 Dec 2023 02:45:33 -0500 Subject: [PATCH 070/101] [import] Implement Verilator assertion failure without aborting process --- .../import_/verilator_wrapper_c_template.py | 35 ++++++++++++++++--- .../import_/verilator_wrapper_py_template.py | 20 +++++------ 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py index 1bb1f7f9b..19fb202c9 100644 --- a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py +++ b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py @@ -73,7 +73,8 @@ void V{component_name}_destroy_model( V{component_name}_t *); void V{component_name}_comb_eval( V{component_name}_t * ); void V{component_name}_seq_eval( V{component_name}_t * ); - void V{component_name}_assert_en( V{component_name}_t *, bool ); + void V{component_name}_assert_on( V{component_name}_t *, bool ); + bool V{component_name}_has_assert_fired( V{component_name}_t * ); #if VLINETRACE void V{component_name}_line_trace( V{component_name}_t *, char * ); @@ -99,6 +100,11 @@ context_ptr->randReset( {verilator_xinit_value} ); context_ptr->randSeed( {verilator_xinit_seed} ); + // We enable assertions by default. We also prevent Verilator from calling + // the fatal std::abort() on error by default. + context_ptr->assertOn(true); + context_ptr->fatalOnError(false); + m = new V{component_name}_t; model = new V{vl_component_name}(context_ptr); @@ -260,15 +266,34 @@ }} //------------------------------------------------------------------------ -// assert_en() +// assert_on() +//------------------------------------------------------------------------ +// Enable or disable assertions controlled by --assert. Assertions are +// enabled by default. + +void V{component_name}_assert_on( V{component_name}_t * m, bool enable ) {{ + + VerilatedContext * context_ptr = (VerilatedContext *) m->_cffi_context_ptr; + + context_ptr->assertOn(enable); + + // We prevent the fatal std::abort() call on assertion failure. Instead, + // we query the error and finish status in the context pointer to determine + // if an assertion has fired. + context_ptr->fatalOnError(!enable); + +}} + +//------------------------------------------------------------------------ +// has_assert_fired() //------------------------------------------------------------------------ -// Enable or disable assertions controlled by --assert +// Return true if an assertion has fired in the current context. -void V{component_name}_assert_en( V{component_name}_t * m, bool en ) {{ +bool V{component_name}_has_assert_fired( V{component_name}_t * m ) {{ VerilatedContext * context_ptr = (VerilatedContext *) m->_cffi_context_ptr; - context_ptr->assertOn(en); + return context_ptr->gotError() && context_ptr->gotFinish(); }} diff --git a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py index bb63fea5c..8ed4d31c5 100644 --- a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py +++ b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py @@ -59,7 +59,8 @@ def __init__( s, *args, **kwargs ): void V{component_name}_destroy_model( V{component_name}_t *); void V{component_name}_comb_eval( V{component_name}_t * ); void V{component_name}_seq_eval( V{component_name}_t * ); - void V{component_name}_assert_en( bool en ); + void V{component_name}_assert_on( V{component_name}_t *, bool ); + bool V{component_name}_has_assert_fired( V{component_name}_t * ); {trace_c_def} """) @@ -170,17 +171,12 @@ def seq_upblk(): # seq_eval will automatically tick clock in C land _ffi_inst_seq_eval( _ffi_m ) - def assert_en( s, en ): - # TODO: for verilator, any assertion failure will cause the C simulator - # to abort, which results in a Python internal error. A better approach - # is to throw a Python exception at the time of assertion failure. - # Verilator allows user-defined `stop` function which is called when - # the simulation is expected to stop due to various reasons. We might - # be able to raise a Python exception through Python C API (although - # at this moment I'm not sure if the C API's are compatible between - # PyPy and CPython). - assert isinstance( en, bool ) - s._ffi_inst.V{component_name}_assert_en( s._ffi_m, en ) + if s._ffi_inst.V{component_name}_has_assert_fired( _ffi_m ): + raise AssertionError("A Verilog assertion fired in the Verilator simulation!") + + def assert_on( s, enable ): + assert isinstance( enable, bool ) + s._ffi_inst.V{component_name}_assert_on( s._ffi_m, enable ) def line_trace( s ): if {external_trace}: From adc90569aa3a4d130b9d5fe6202956f0e9c07cc1 Mon Sep 17 00:00:00 2001 From: pp482 Date: Mon, 29 Jan 2024 16:27:14 -0500 Subject: [PATCH 071/101] [import] Fix a bug in Verilog trace of generators --- .../import_/test/ImportedObject_test.py | 30 +++++++++++++++++++ .../verilog/import_/test/VRegTraceGenerator.v | 28 +++++++++++++++++ .../import_/verilator_wrapper_c_template.py | 2 +- 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 pymtl3/passes/backends/verilog/import_/test/VRegTraceGenerator.v diff --git a/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py b/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py index 3160fa5f4..24434235c 100644 --- a/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py +++ b/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py @@ -547,3 +547,33 @@ def construct( s ): a.finalize() raise Exception("Should have thrown a Verilator assertion error!") + +def test_vl_infer_external_trace_generator( do_test ): + class VRegTraceGenerator( Component, VerilogPlaceholder ): + def construct( s, p_nbits=32 ): + s.in_ = InPort( p_nbits ) + s.out = OutPort( p_nbits ) + s.set_metadata( VerilogPlaceholderPass.src_file, dirname(__file__)+'/VRegTraceGenerator.v' ) + s.set_metadata( VerilogPlaceholderPass.port_map, { + s.in_ : "d", s.out : "q", + } ) + s.set_metadata( VerilogTranslationImportPass.enable, True ) + a = VRegTraceGenerator() + a.elaborate() + a.apply( VerilogPlaceholderPass() ) + a = VerilogTranslationImportPass()( a ) + a.apply( DefaultPassGroup(linetrace=True) ) + + assert a.line_trace() == 'q = 0' + a.in_ @= Bits32(1) + a.sim_tick() + assert a.line_trace() == 'q = 1' + a.in_ @= Bits32(2) + a.sim_tick() + assert a.line_trace() == 'q = 2' + a.in_ @= Bits32(-1) + a.sim_tick() + # 0xFFFFFFFF unsigned + assert a.line_trace() == 'q = 4294967295' + a.sim_tick() + a.finalize() diff --git a/pymtl3/passes/backends/verilog/import_/test/VRegTraceGenerator.v b/pymtl3/passes/backends/verilog/import_/test/VRegTraceGenerator.v new file mode 100644 index 000000000..a80e45866 --- /dev/null +++ b/pymtl3/passes/backends/verilog/import_/test/VRegTraceGenerator.v @@ -0,0 +1,28 @@ +`include "trace.v" + +module test_VRegTraceGenerator #( + parameter p_nbits = 32 +)( + input logic clk, + input logic reset, + output logic [p_nbits-1:0] q, + input logic [p_nbits-1:0] d +); + always_ff @(posedge clk) begin + q <= d; + end + + logic [`VC_TRACE_NBITS-1:0] str; + logic [512*8-1:0] q_str; + `VC_TRACE_BEGIN + begin + // PP: we should not use $ system tasks because + // they seem to be generating segfaults at end of pytest?? + /* $display("q = %d\n", q); */ + $sformat(q_str, "%d", q); + vc_trace.append_str( trace_str, "q = " ); + vc_trace.append_str( trace_str, q_str ); + end + `VC_TRACE_END + +endmodule diff --git a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py index 19fb202c9..cc7fbbbe8 100644 --- a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py +++ b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py @@ -346,7 +346,7 @@ // Verilator's internal implementation. It is recommended to get scope through // a dotted hierarchical name as shown below. - const svScope dut_scope = svGetScopeFromName("TOP.{vl_component_name}.v"); + const svScope dut_scope = svGetScopeFromName("TOP.{component_name}.v"); assert( dut_scope ); svSetScope( dut_scope ); From 8c049574fae2c1f96a7d5783914b1090cc21f315 Mon Sep 17 00:00:00 2001 From: pp482 Date: Tue, 6 Feb 2024 14:34:06 -0500 Subject: [PATCH 072/101] [test] Fix bitstruct hypothesis test --- pymtl3/datatypes/test/strategies_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymtl3/datatypes/test/strategies_test.py b/pymtl3/datatypes/test/strategies_test.py index 64057d7ca..a78903fc8 100644 --- a/pymtl3/datatypes/test/strategies_test.py +++ b/pymtl3/datatypes/test/strategies_test.py @@ -223,7 +223,7 @@ def test_nnested_point_limited(): @hypothesis.given( bs = pst.bitstructs(NNestedPoint, limit_dict) ) - @hypothesis.settings( max_examples=16 ) + @hypothesis.settings( max_examples=30 ) def actual_test( bs ): assert isinstance( bs, NNestedPoint ) assert 0xe0 <= bs.p1.x < 0xef From 84621d2ca7a370ae4caafd47e46ef668632f3111 Mon Sep 17 00:00:00 2001 From: pp482 Date: Tue, 6 Feb 2024 14:34:21 -0500 Subject: [PATCH 073/101] [pytest] Skip yosys tests by default --- pytest.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytest.ini b/pytest.ini index 9dd4956f2..1fa434e26 100644 --- a/pytest.ini +++ b/pytest.ini @@ -26,4 +26,4 @@ python_functions = test test_* # error/warnings at the end; otherwise syntax errors won't really show # up. -addopts = --tb=no -r Ew +addopts = --tb=no -r Ew --ignore=../pymtl3/passes/backends/yosys From 86952f5046e3878a20060f6902bf8a078014c95f Mon Sep 17 00:00:00 2001 From: pp482 Date: Mon, 12 Feb 2024 14:30:09 -0500 Subject: [PATCH 074/101] [ci] Add new Python versions and remove 3.6 --- .github/workflows/python-package-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package-ci.yml b/.github/workflows/python-package-ci.yml index b131b7329..3e96a8c3a 100644 --- a/.github/workflows/python-package-ci.yml +++ b/.github/workflows/python-package-ci.yml @@ -23,7 +23,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ 3.6, 3.7, 3.8, 3.9 ] # FIXME github recognizes 3.10 as 3.1 lol + python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11", "3.12" ] steps: - uses: actions/checkout@v2 From 6cfd5b31729fe03a587f90271779cbc6ff13ca94 Mon Sep 17 00:00:00 2001 From: pp482 Date: Mon, 12 Feb 2024 15:01:38 -0500 Subject: [PATCH 075/101] [ci] Update to checkout@v4 and setup-python@v5 --- .github/workflows/python-package-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-package-ci.yml b/.github/workflows/python-package-ci.yml index 3e96a8c3a..7dc6f3102 100644 --- a/.github/workflows/python-package-ci.yml +++ b/.github/workflows/python-package-ci.yml @@ -26,9 +26,9 @@ jobs: python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11", "3.12" ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} From b8c71b84d23e4ecd31f7f1b17c51dacae50880f7 Mon Sep 17 00:00:00 2001 From: Kelvin Chung Date: Tue, 2 Apr 2024 11:01:15 +0100 Subject: [PATCH 076/101] Adding __len__ interface and formatting to black --- pymtl3/datatypes/PythonBits.py | 1128 ++++++++++++++++++-------------- 1 file changed, 620 insertions(+), 508 deletions(-) diff --git a/pymtl3/datatypes/PythonBits.py b/pymtl3/datatypes/PythonBits.py index 33bd47f23..3e221e467 100644 --- a/pymtl3/datatypes/PythonBits.py +++ b/pymtl3/datatypes/PythonBits.py @@ -9,523 +9,635 @@ """ # lower <= value <= upper -_upper = [ 0, 1 ] -_lower = [ 0, -1 ] +_upper = [0, 1] +_lower = [0, -1] for i in range(2, 1024): - _upper.append( (_upper[i-1] << 1) + 1 ) - _lower.append( _lower[i-1] << 1 ) + _upper.append((_upper[i - 1] << 1) + 1) + _lower.append(_lower[i - 1] << 1) object_new = object.__new__ -def _new_valid_bits( nbits, uint ): - ret = object_new( Bits ) - ret._nbits = nbits - ret._uint = uint - return ret -class Bits: - __slots__ = ( "_nbits", "_uint", "_next" ) - - @property - def nbits( self ): - return self._nbits - def __init__( self, nbits, v=0, trunc_int=False ): - nbits = int(nbits) - if nbits < 1 or nbits >= 1024: raise ValueError(f"Only support 1 <= nbits < 1024, not {nbits}") +def _new_valid_bits(nbits, uint): + ret = object_new(Bits) + ret._nbits = nbits + ret._uint = uint + return ret - self._nbits = nbits - if isinstance( v, Bits ): - if nbits != v.nbits: - if nbits < v.nbits: - raise ValueError( f"The Bits{v.nbits} object on RHS is too wide to be used to construct Bits{nbits}!\n" - f"- Suggestion: directly use trunc( value, {nbits}/Bits{nbits} )" ) - else: - raise ValueError( f"The Bits{v.nbits} object on RHS is too narrow to be used to construct Bits{nbits}!\n" - f"- Suggestion: directly use zext/sext(value, {nbits}/Bits{nbits} )" ) - self._uint = v._uint - else: - v = int(v) - up = _upper[nbits] - - if not trunc_int: - lo = _lower[nbits] - if v < lo or v > up: - raise ValueError( f"Value {hex(v)} is too wide for Bits{nbits}!\n" \ - f"(Bits{nbits} only accepts {hex(lo)} <= value <= {hex(up)})" ) - self._uint = v & up - - # PyMTL simulation specific - - def __ilshift__( self, v ): - nbits = self._nbits - try: - # Bits/Bitstruct - if v.nbits != nbits: - if v.nbits < nbits: - raise ValueError( f"Bitwidth of LHS must be equal to RHS during <<= non-blocking assignment, " \ - f"but here LHS Bits{nbits} > RHS Bits{v.nbits}.\n" - f"- Suggestion: LHS @= zext/sext(RHS, nbits/Type)" ) +class Bits: + __slots__ = ("_nbits", "_uint", "_next") + + @property + def nbits(self): + return self._nbits + + def __init__(self, nbits, v=0, trunc_int=False): + nbits = int(nbits) + if nbits < 1 or nbits >= 1024: + raise ValueError(f"Only support 1 <= nbits < 1024, not {nbits}") + + self._nbits = nbits + + if isinstance(v, Bits): + if nbits != v.nbits: + if nbits < v.nbits: + raise ValueError( + f"The Bits{v.nbits} object on RHS is too wide to be used to construct Bits{nbits}!\n" + f"- Suggestion: directly use trunc( value, {nbits}/Bits{nbits} )" + ) + else: + raise ValueError( + f"The Bits{v.nbits} object on RHS is too narrow to be used to construct Bits{nbits}!\n" + f"- Suggestion: directly use zext/sext(value, {nbits}/Bits{nbits} )" + ) + self._uint = v._uint else: - raise ValueError( f"Bitwidth of LHS must be equal to RHS during <<= non-blocking assignment, " \ - f"but here LHS Bits{nbits} < RHS Bits{v.nbits}.\n" - f"- Suggestion: LHS @= trunc(RHS, nbits/Type)" ) - self._next = v.to_bits()._uint - except AttributeError: - # Cast to int - v = int(v) - lo = _lower[nbits] - up = _upper[nbits] - - if v < lo or v > up: - raise ValueError( f"RHS value {hex(v)} of <<= is too wide for LHS Bits{nbits}!\n" \ - f"(Bits{nbits} only accepts {hex(lo)} <= value <= {hex(up)})" ) - self._next = v & up - - return self - - def _flip( self ): - self._uint = self._next - - def clone( self ): - return _new_valid_bits( self._nbits, self._uint ) - - def __deepcopy__( self, memo ): - return _new_valid_bits( self._nbits, self._uint ) - - def __imatmul__( self, v ): - nbits = self._nbits - try: - # Bits/Bitstruct - if v.nbits != nbits: - if v.nbits < nbits: - raise ValueError( f"Bitwidth of LHS must be equal to RHS during @= blocking assignment, " \ - f"but here LHS Bits{nbits} > RHS Bits{v.nbits}.\n" - f"- Suggestion: LHS @= zext/sext(RHS, nbits/Type)" ) + v = int(v) + up = _upper[nbits] + + if not trunc_int: + lo = _lower[nbits] + if v < lo or v > up: + raise ValueError( + f"Value {hex(v)} is too wide for Bits{nbits}!\n" + f"(Bits{nbits} only accepts {hex(lo)} <= value <= {hex(up)})" + ) + self._uint = v & up + + def __len__(self) -> int: + return self._nbits + + # PyMTL simulation specific + + def __ilshift__(self, v): + nbits = self._nbits + try: + # Bits/Bitstruct + if v.nbits != nbits: + if v.nbits < nbits: + raise ValueError( + f"Bitwidth of LHS must be equal to RHS during <<= non-blocking assignment, " + f"but here LHS Bits{nbits} > RHS Bits{v.nbits}.\n" + f"- Suggestion: LHS @= zext/sext(RHS, nbits/Type)" + ) + else: + raise ValueError( + f"Bitwidth of LHS must be equal to RHS during <<= non-blocking assignment, " + f"but here LHS Bits{nbits} < RHS Bits{v.nbits}.\n" + f"- Suggestion: LHS @= trunc(RHS, nbits/Type)" + ) + self._next = v.to_bits()._uint + except AttributeError: + # Cast to int + v = int(v) + lo = _lower[nbits] + up = _upper[nbits] + + if v < lo or v > up: + raise ValueError( + f"RHS value {hex(v)} of <<= is too wide for LHS Bits{nbits}!\n" + f"(Bits{nbits} only accepts {hex(lo)} <= value <= {hex(up)})" + ) + self._next = v & up + + return self + + def _flip(self): + self._uint = self._next + + def clone(self): + return _new_valid_bits(self._nbits, self._uint) + + def __deepcopy__(self, memo): + return _new_valid_bits(self._nbits, self._uint) + + def __imatmul__(self, v): + nbits = self._nbits + try: + # Bits/Bitstruct + if v.nbits != nbits: + if v.nbits < nbits: + raise ValueError( + f"Bitwidth of LHS must be equal to RHS during @= blocking assignment, " + f"but here LHS Bits{nbits} > RHS Bits{v.nbits}.\n" + f"- Suggestion: LHS @= zext/sext(RHS, nbits/Type)" + ) + else: + raise ValueError( + f"Bitwidth of LHS must be equal to RHS during @= blocking assignment, " + f"but here LHS Bits{nbits} < RHS Bits{v.nbits}.\n" + f"- Suggestion: LHS @= trunc(RHS, nbits/Type)" + ) + self._uint = v.to_bits()._uint + except AttributeError: + # Cast to int + v = int(v) + + lo = _lower[nbits] + up = _upper[nbits] + + if v < lo or v > up: + raise ValueError( + f"RHS value {hex(v)} of @= is too wide for LHS Bits{nbits}!\n" + f"(Bits{nbits} only accepts {hex(lo)} <= value <= {hex(up)})" + ) + self._uint = v & up + + return self + + def to_bits(self): + return self + + # Arithmetics + def __getitem__(self, idx): + + if isinstance(idx, slice): + if idx.step: + raise IndexError("Index cannot contain step") + try: + start, stop = int(idx.start or 0), int(idx.stop or self._nbits) + assert 0 <= start < stop <= self._nbits + except: + raise IndexError( + f"Invalid access: [{idx.start}:{idx.stop}] in a Bits{self._nbits} instance" + ) + + # Bypass check + nbits = stop - start + return _new_valid_bits(stop - start, (self._uint >> start) & _upper[nbits]) + + i = int(idx) + if i >= self._nbits or i < 0: + raise IndexError(f"Invalid access: [{i}] in a Bits{self._nbits} instance") + + # Bypass check + return _new_valid_bits(1, (self._uint >> i) & 1) + + def __setitem__(self, idx, v): + sv = int(self._uint) + + if isinstance(idx, slice): + if idx.step: + raise IndexError("Index cannot contain step") + try: + start, stop = int(idx.start or 0), int(idx.stop or self._nbits) + assert 0 <= start < stop <= self._nbits + except: + raise IndexError( + f"Invalid access: [{idx.start}:{idx.stop}] in a Bits{self._nbits} instance" + ) + + slice_nbits = stop - start + if isinstance(v, Bits): + if v.nbits != slice_nbits: + if v.nbits < slice_nbits: + raise ValueError( + f"Cannot fit a Bits{v.nbits} object into a {slice_nbits}-bit slice [{start}:{stop}]\n" + f"- Suggestion: sext/zext the RHS" + ) + else: + raise ValueError( + f"Cannot fit a Bits{v.nbits} object into a {slice_nbits}-bit slice [{start}:{stop}]\n" + f"- Suggestion: trunc the RHS" + ) + + self._uint = (sv & (~((1 << stop) - (1 << start)))) | ( + (v._uint & _upper[slice_nbits]) << start + ) + else: + # Cast to int + v = int(v) + lo = _lower[slice_nbits] + up = _upper[slice_nbits] + + if v < lo or v > up: + raise ValueError( + f"Cannot fit {v} into a Bits{slice_nbits} slice\n" + f"(Bits{slice_nbits} only accepts {hex(lo)} <= value <= {hex(up)})" + ) + + self._uint = (sv & (~((1 << stop) - (1 << start)))) | ( + (v & _upper[slice_nbits]) << start + ) + return + + i = int(idx) + if i >= self._nbits or i < 0: + raise IndexError(f"Invalid access: [{i}] in a Bits{self._nbits} instance") + + if isinstance(v, Bits): + if v.nbits > 1: + raise ValueError( + f"Cannot fit a Bits{v.nbits} object into the 1-bit slice" + ) + self._uint = (sv & ~(1 << i)) | ((v._uint & 1) << i) else: - raise ValueError( f"Bitwidth of LHS must be equal to RHS during @= blocking assignment, " \ - f"but here LHS Bits{nbits} < RHS Bits{v.nbits}.\n" - f"- Suggestion: LHS @= trunc(RHS, nbits/Type)" ) - self._uint = v.to_bits()._uint - except AttributeError: - # Cast to int - v = int(v) - - lo = _lower[nbits] - up = _upper[nbits] - - if v < lo or v > up: - raise ValueError( f"RHS value {hex(v)} of @= is too wide for LHS Bits{nbits}!\n" \ - f"(Bits{nbits} only accepts {hex(lo)} <= value <= {hex(up)})" ) - self._uint = v & up - - return self - - def to_bits( self ): - return self - - # Arithmetics - def __getitem__( self, idx ): - - if isinstance( idx, slice ): - if idx.step: - raise IndexError( "Index cannot contain step" ) - try: - start, stop = int(idx.start or 0), int(idx.stop or self._nbits) - assert 0 <= start < stop <= self._nbits - except: - raise IndexError( f"Invalid access: [{idx.start}:{idx.stop}] in a Bits{self._nbits} instance" ) - - # Bypass check - nbits = stop - start - return _new_valid_bits( stop-start, (self._uint >> start) & _upper[nbits] ) - - i = int(idx) - if i >= self._nbits or i < 0: - raise IndexError( f"Invalid access: [{i}] in a Bits{self._nbits} instance" ) - - # Bypass check - return _new_valid_bits( 1, (self._uint >> i) & 1 ) - - def __setitem__( self, idx, v ): - sv = int(self._uint) - - if isinstance( idx, slice ): - if idx.step: - raise IndexError( "Index cannot contain step" ) - try: - start, stop = int(idx.start or 0), int(idx.stop or self._nbits) - assert 0 <= start < stop <= self._nbits - except: - raise IndexError( f"Invalid access: [{idx.start}:{idx.stop}] in a Bits{self._nbits} instance" ) - - slice_nbits = stop - start - if isinstance( v, Bits ): - if v.nbits != slice_nbits: - if v.nbits < slice_nbits: - raise ValueError( f"Cannot fit a Bits{v.nbits} object into a {slice_nbits}-bit slice [{start}:{stop}]\n" - f"- Suggestion: sext/zext the RHS") - else: - raise ValueError( f"Cannot fit a Bits{v.nbits} object into a {slice_nbits}-bit slice [{start}:{stop}]\n" - f"- Suggestion: trunc the RHS") - - self._uint = (sv & (~((1 << stop) - (1 << start)))) | \ - ((v._uint & _upper[slice_nbits]) << start) - else: - # Cast to int - v = int(v) - lo = _lower[slice_nbits] - up = _upper[slice_nbits] - - if v < lo or v > up: - raise ValueError( f"Cannot fit {v} into a Bits{slice_nbits} slice\n" \ - f"(Bits{slice_nbits} only accepts {hex(lo)} <= value <= {hex(up)})" ) - - self._uint = (sv & (~((1 << stop) - (1 << start)))) | \ - ((v & _upper[slice_nbits]) << start) - return - - i = int(idx) - if i >= self._nbits or i < 0: - raise IndexError( f"Invalid access: [{i}] in a Bits{self._nbits} instance" ) - - if isinstance( v, Bits ): - if v.nbits > 1: - raise ValueError( f"Cannot fit a Bits{v.nbits} object into the 1-bit slice" ) - self._uint = (sv & ~(1 << i)) | ((v._uint & 1) << i) - else: - v = int(v) - if abs(v) > 1: - raise ValueError( f"Value {hex(v)} is too big for the 1-bit slice!\n" ) - self._uint = (sv & ~(1 << i)) | ((int(v) & 1) << i) - - def __add__( self, other ): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( f"Operands of '+' (add) operation must have matching bitwidth, "\ - f"but here Bits{nbits} != Bits{other.nbits}.\n" ) - return _new_valid_bits( nbits, (self._uint + other._uint) & _upper[nbits] ) - except AttributeError: - other = int(other) - up = _upper[ nbits ] - if other < 0 or other > up: - raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(up)}" ) - return _new_valid_bits( nbits, (self._uint + other) & up ) - - def __radd__( self, other ): - return self.__add__( other ) - - def __sub__( self, other ): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( f"Operands of '-' (sub) operation must have matching bitwidth, "\ - f"but here Bits{nbits} != Bits{other.nbits}.\n" ) - return _new_valid_bits( nbits, (self._uint - other._uint) & _upper[nbits] ) - except AttributeError: - other = int(other) - up = _upper[ nbits ] - if other < 0 or other > up: - raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(up)}" ) - return _new_valid_bits( nbits, (self._uint - other) & up ) - - def __rsub__( self, other ): - nbits = self._nbits - # Shouldn't be Bits - other = int(other) - up = _upper[ nbits ] - if other < 0 or other > up: - raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(up)}" ) - return _new_valid_bits( nbits, (other - self._uint) & up ) - - def __mul__( self, other ): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( f"Operands of '*' (mul) operation must have matching bitwidth, "\ - f"but here Bits{nbits} != Bits{other.nbits}.\n" ) - return _new_valid_bits( nbits, (self._uint * other._uint) & _upper[nbits] ) - except AttributeError: - other = int(other) - up = _upper[ nbits ] - if other < 0 or other > up: - raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(up)}" ) - return _new_valid_bits( nbits, (self._uint * other) & up) - - def __rmul__( self, other ): - return self.__mul__( other ) - - # no need to AND mask - def __and__( self, other ): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( f"Operands of '&' (and) operation must have matching bitwidth, "\ - f"but here Bits{nbits} != Bits{other.nbits}.\n" ) - return _new_valid_bits( nbits, self._uint & other._uint ) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[ nbits ]: - raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" ) - return _new_valid_bits( nbits, self._uint & other ) - - def __rand__( self, other ): - return self.__and__( other ) - - # no need to AND mask - def __or__( self, other ): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( f"Operands of '|' (or) operation must have matching bitwidth, "\ - f"but here Bits{nbits} != Bits{other.nbits}.\n" ) - return _new_valid_bits( nbits, self._uint | other._uint ) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[ nbits ]: - raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" ) - return _new_valid_bits( nbits, self._uint | other ) - - def __ror__( self, other ): - return self.__or__( other ) - - def __xor__( self, other ): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( f"Operands of '^' (xor) operation must have matching bitwidth, "\ - f"but here Bits{nbits} != Bits{other.nbits}.\n" ) - return _new_valid_bits( nbits, self._uint ^ other._uint ) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[ self._nbits ]: - raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" ) - return _new_valid_bits( nbits, self._uint ^ other ) - - def __rxor__( self, other ): - return self.__xor__( other ) - - def __floordiv__( self, other ): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( f"Operands of '//' (div) operation must have matching bitwidth, "\ - f"but here Bits{nbits} != Bits{other.nbits}.\n" ) - return _new_valid_bits( nbits, (self._uint // other._uint) & _upper[nbits] ) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[ self._nbits ]: - raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" ) - return _new_valid_bits( nbits, self._uint // other ) - - def __rfloordiv__( self, other ): - nbits = self._nbits - other = int(other) - up = _upper[ nbits ] - if other < 0 or other > up: - raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(up)}" ) - return _new_valid_bits( nbits, other // self._uint ) - - def __mod__( self, other ): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( f"Operands of '%' (mod) operation must have matching bitwidth, "\ - f"but here Bits{nbits} != Bits{other.nbits}.\n" ) - return _new_valid_bits( nbits, (self._uint % other._uint) & _upper[nbits] ) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[ nbits ]: - raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" ) - return _new_valid_bits( nbits, self._uint % other ) - - def __rmod__( self, other ): - nbits = self._nbits - other = int(other) - up = _upper[ nbits ] - if other < 0 or other > up: - raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(up)}" ) - return _new_valid_bits( nbits, other % self._uint ) - - def __invert__( self ): - nbits = self._nbits - return _new_valid_bits( nbits, ~self._uint & _upper[nbits] ) - - def __lshift__( self, other ): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( f"Operands of '<<' (lshift) operation must have matching bitwidth, "\ - f"but here Bits{nbits} != Bits{other.nbits}.\n" ) - uint = other._uint - if uint >= nbits: - return _new_valid_bits( self._nbits, 0 ) - return _new_valid_bits( nbits, (self._uint << uint) & _upper[nbits] ) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[ nbits ]: - raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" ) - if other >= nbits: - return _new_valid_bits( self._nbits, 0 ) - return _new_valid_bits( nbits, (self._uint << other) & _upper[nbits] ) - - def __rshift__( self, other ): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( f"Operands of '>>' (rshift) operation must have matching bitwidth, "\ - f"but here Bits{nbits} != Bits{other.nbits}.\n" ) - return _new_valid_bits( nbits, self._uint >> other._uint ) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[ nbits ]: - raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" ) - return _new_valid_bits( nbits, self._uint >> other ) - - def __eq__( self, other ): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( f"Operands of '==' (eq) operation must have matching bitwidth, "\ - f"but here Bits{nbits} != Bits{other.nbits}.\n" ) - return _new_valid_bits( 1, self._uint == other._uint ) - except AttributeError: - try: + v = int(v) + if abs(v) > 1: + raise ValueError(f"Value {hex(v)} is too big for the 1-bit slice!\n") + self._uint = (sv & ~(1 << i)) | ((int(v) & 1) << i) + + def __add__(self, other): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( + f"Operands of '+' (add) operation must have matching bitwidth, " + f"but here Bits{nbits} != Bits{other.nbits}.\n" + ) + return _new_valid_bits(nbits, (self._uint + other._uint) & _upper[nbits]) + except AttributeError: + other = int(other) + up = _upper[nbits] + if other < 0 or other > up: + raise ValueError( + f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(up)}" + ) + return _new_valid_bits(nbits, (self._uint + other) & up) + + def __radd__(self, other): + return self.__add__(other) + + def __sub__(self, other): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( + f"Operands of '-' (sub) operation must have matching bitwidth, " + f"but here Bits{nbits} != Bits{other.nbits}.\n" + ) + return _new_valid_bits(nbits, (self._uint - other._uint) & _upper[nbits]) + except AttributeError: + other = int(other) + up = _upper[nbits] + if other < 0 or other > up: + raise ValueError( + f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(up)}" + ) + return _new_valid_bits(nbits, (self._uint - other) & up) + + def __rsub__(self, other): + nbits = self._nbits + # Shouldn't be Bits + other = int(other) + up = _upper[nbits] + if other < 0 or other > up: + raise ValueError( + f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(up)}" + ) + return _new_valid_bits(nbits, (other - self._uint) & up) + + def __mul__(self, other): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( + f"Operands of '*' (mul) operation must have matching bitwidth, " + f"but here Bits{nbits} != Bits{other.nbits}.\n" + ) + return _new_valid_bits(nbits, (self._uint * other._uint) & _upper[nbits]) + except AttributeError: + other = int(other) + up = _upper[nbits] + if other < 0 or other > up: + raise ValueError( + f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(up)}" + ) + return _new_valid_bits(nbits, (self._uint * other) & up) + + def __rmul__(self, other): + return self.__mul__(other) + + # no need to AND mask + def __and__(self, other): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( + f"Operands of '&' (and) operation must have matching bitwidth, " + f"but here Bits{nbits} != Bits{other.nbits}.\n" + ) + return _new_valid_bits(nbits, self._uint & other._uint) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[nbits]: + raise ValueError( + f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" + ) + return _new_valid_bits(nbits, self._uint & other) + + def __rand__(self, other): + return self.__and__(other) + + # no need to AND mask + def __or__(self, other): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( + f"Operands of '|' (or) operation must have matching bitwidth, " + f"but here Bits{nbits} != Bits{other.nbits}.\n" + ) + return _new_valid_bits(nbits, self._uint | other._uint) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[nbits]: + raise ValueError( + f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" + ) + return _new_valid_bits(nbits, self._uint | other) + + def __ror__(self, other): + return self.__or__(other) + + def __xor__(self, other): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( + f"Operands of '^' (xor) operation must have matching bitwidth, " + f"but here Bits{nbits} != Bits{other.nbits}.\n" + ) + return _new_valid_bits(nbits, self._uint ^ other._uint) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[self._nbits]: + raise ValueError( + f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" + ) + return _new_valid_bits(nbits, self._uint ^ other) + + def __rxor__(self, other): + return self.__xor__(other) + + def __floordiv__(self, other): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( + f"Operands of '//' (div) operation must have matching bitwidth, " + f"but here Bits{nbits} != Bits{other.nbits}.\n" + ) + return _new_valid_bits(nbits, (self._uint // other._uint) & _upper[nbits]) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[self._nbits]: + raise ValueError( + f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" + ) + return _new_valid_bits(nbits, self._uint // other) + + def __rfloordiv__(self, other): + nbits = self._nbits other = int(other) - except: - return _new_valid_bits( 1, 0 ) - - if other < 0 or other > _upper[ nbits ]: - raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" ) - return _new_valid_bits( 1, self._uint == other ) - - def __ne__( self, other ): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( f"Operands of '!=' (ne) operation must have matching bitwidth, "\ - f"but here Bits{nbits} != Bits{other.nbits}.\n" ) - return _new_valid_bits( 1, self._uint != other._uint ) - except AttributeError: - try: + up = _upper[nbits] + if other < 0 or other > up: + raise ValueError( + f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(up)}" + ) + return _new_valid_bits(nbits, other // self._uint) + + def __mod__(self, other): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( + f"Operands of '%' (mod) operation must have matching bitwidth, " + f"but here Bits{nbits} != Bits{other.nbits}.\n" + ) + return _new_valid_bits(nbits, (self._uint % other._uint) & _upper[nbits]) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[nbits]: + raise ValueError( + f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" + ) + return _new_valid_bits(nbits, self._uint % other) + + def __rmod__(self, other): + nbits = self._nbits other = int(other) - except: - return _new_valid_bits( 1, 1 ) - - if other < 0 or other > _upper[ nbits ]: - raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" ) - return _new_valid_bits( 1, self._uint != other ) - - def __lt__( self, other ): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( f"Operands of '<' (lt) operation must have matching bitwidth, "\ - f"but here Bits{nbits} != Bits{other.nbits}.\n" ) - return _new_valid_bits( 1, self._uint < other._uint ) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[ self._nbits ]: - raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{self._nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ self._nbits ])}" ) - return _new_valid_bits( 1, self._uint < other ) - - def __le__( self, other ): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( f"Operands of '<=' (le) operation must have matching bitwidth, "\ - f"but here Bits{nbits} != Bits{other.nbits}.\n" ) - return _new_valid_bits( 1, self._uint <= other._uint ) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[ self._nbits ]: - raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{self._nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ self._nbits ])}" ) - return _new_valid_bits( 1, self._uint <= other ) - - def __gt__( self, other ): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( f"Operands of '>' (gt) operation must have matching bitwidth, "\ - f"but here Bits{nbits} != Bits{other.nbits}.\n" ) - return _new_valid_bits( 1, self._uint > other._uint ) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[ self._nbits ]: - raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{self._nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ self._nbits ])}" ) - return _new_valid_bits( 1, self._uint > other ) - - def __ge__( self, other ): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( f"Operands of '>=' (ge) operation must have matching bitwidth, "\ - f"but here Bits{nbits} != Bits{other.nbits}.\n" ) - return _new_valid_bits( 1, self._uint >= other._uint ) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[ self._nbits ]: - raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{self._nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ self._nbits ])}" ) - return _new_valid_bits( 1, self._uint >= other ) - - def __bool__( self ): - return self._uint != 0 - - def __int__( self ): - return int(self._uint) - - def int( self ): - if self._uint >> (self._nbits - 1): - return -int(~self + 1) - return self._uint - - def uint( self ): - return self._uint - - def __index__( self ): - return int(self._uint) - - def __hash__( self ): - return hash((self._nbits, self._uint)) - - # Print - - def __repr__(self): - return "Bits{}(0x{})".format( self._nbits, "{:x}".format(int(self._uint)).zfill(((self._nbits-1)//4)+1) ) - - def __str__(self): - str = "{:x}".format(int(self._uint)).zfill(((self._nbits-1)//4)+1) - return str - - def bin(self): - str = "{:b}".format(int(self._uint)).zfill(self._nbits) - return "0b"+str - - def oct( self ): - str = "{:o}".format(int(self._uint)).zfill(((self._nbits-1)//3)+1) - return "0o"+str - - def hex( self ): - str = "{:x}".format(int(self._uint)).zfill(((self._nbits-1)//4)+1) - return "0x"+str + up = _upper[nbits] + if other < 0 or other > up: + raise ValueError( + f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(up)}" + ) + return _new_valid_bits(nbits, other % self._uint) + + def __invert__(self): + nbits = self._nbits + return _new_valid_bits(nbits, ~self._uint & _upper[nbits]) + + def __lshift__(self, other): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( + f"Operands of '<<' (lshift) operation must have matching bitwidth, " + f"but here Bits{nbits} != Bits{other.nbits}.\n" + ) + uint = other._uint + if uint >= nbits: + return _new_valid_bits(self._nbits, 0) + return _new_valid_bits(nbits, (self._uint << uint) & _upper[nbits]) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[nbits]: + raise ValueError( + f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" + ) + if other >= nbits: + return _new_valid_bits(self._nbits, 0) + return _new_valid_bits(nbits, (self._uint << other) & _upper[nbits]) + + def __rshift__(self, other): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( + f"Operands of '>>' (rshift) operation must have matching bitwidth, " + f"but here Bits{nbits} != Bits{other.nbits}.\n" + ) + return _new_valid_bits(nbits, self._uint >> other._uint) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[nbits]: + raise ValueError( + f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" + ) + return _new_valid_bits(nbits, self._uint >> other) + + def __eq__(self, other): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( + f"Operands of '==' (eq) operation must have matching bitwidth, " + f"but here Bits{nbits} != Bits{other.nbits}.\n" + ) + return _new_valid_bits(1, self._uint == other._uint) + except AttributeError: + try: + other = int(other) + except: + return _new_valid_bits(1, 0) + + if other < 0 or other > _upper[nbits]: + raise ValueError( + f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" + ) + return _new_valid_bits(1, self._uint == other) + + def __ne__(self, other): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( + f"Operands of '!=' (ne) operation must have matching bitwidth, " + f"but here Bits{nbits} != Bits{other.nbits}.\n" + ) + return _new_valid_bits(1, self._uint != other._uint) + except AttributeError: + try: + other = int(other) + except: + return _new_valid_bits(1, 1) + + if other < 0 or other > _upper[nbits]: + raise ValueError( + f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" + ) + return _new_valid_bits(1, self._uint != other) + + def __lt__(self, other): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( + f"Operands of '<' (lt) operation must have matching bitwidth, " + f"but here Bits{nbits} != Bits{other.nbits}.\n" + ) + return _new_valid_bits(1, self._uint < other._uint) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[self._nbits]: + raise ValueError( + f"Integer {hex(other)} is not a valid binop operand with Bits{self._nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ self._nbits ])}" + ) + return _new_valid_bits(1, self._uint < other) + + def __le__(self, other): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( + f"Operands of '<=' (le) operation must have matching bitwidth, " + f"but here Bits{nbits} != Bits{other.nbits}.\n" + ) + return _new_valid_bits(1, self._uint <= other._uint) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[self._nbits]: + raise ValueError( + f"Integer {hex(other)} is not a valid binop operand with Bits{self._nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ self._nbits ])}" + ) + return _new_valid_bits(1, self._uint <= other) + + def __gt__(self, other): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( + f"Operands of '>' (gt) operation must have matching bitwidth, " + f"but here Bits{nbits} != Bits{other.nbits}.\n" + ) + return _new_valid_bits(1, self._uint > other._uint) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[self._nbits]: + raise ValueError( + f"Integer {hex(other)} is not a valid binop operand with Bits{self._nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ self._nbits ])}" + ) + return _new_valid_bits(1, self._uint > other) + + def __ge__(self, other): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( + f"Operands of '>=' (ge) operation must have matching bitwidth, " + f"but here Bits{nbits} != Bits{other.nbits}.\n" + ) + return _new_valid_bits(1, self._uint >= other._uint) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[self._nbits]: + raise ValueError( + f"Integer {hex(other)} is not a valid binop operand with Bits{self._nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ self._nbits ])}" + ) + return _new_valid_bits(1, self._uint >= other) + + def __bool__(self): + return self._uint != 0 + + def __int__(self): + return int(self._uint) + + def int(self): + if self._uint >> (self._nbits - 1): + return -int(~self + 1) + return self._uint + + def uint(self): + return self._uint + + def __index__(self): + return int(self._uint) + + def __hash__(self): + return hash((self._nbits, self._uint)) + + # Print + + def __repr__(self): + return "Bits{}(0x{})".format( + self._nbits, + "{:x}".format(int(self._uint)).zfill(((self._nbits - 1) // 4) + 1), + ) + + def __str__(self): + str = "{:x}".format(int(self._uint)).zfill(((self._nbits - 1) // 4) + 1) + return str + + def bin(self): + str = "{:b}".format(int(self._uint)).zfill(self._nbits) + return "0b" + str + + def oct(self): + str = "{:o}".format(int(self._uint)).zfill(((self._nbits - 1) // 3) + 1) + return "0o" + str + + def hex(self): + str = "{:x}".format(int(self._uint)).zfill(((self._nbits - 1) // 4) + 1) + return "0x" + str From 6cf12f8425537ac9a93422caf83fd8559491a001 Mon Sep 17 00:00:00 2001 From: Kelvin Chung Date: Tue, 2 Apr 2024 18:08:23 +0100 Subject: [PATCH 077/101] Undo formatting --- pymtl3/datatypes/PythonBits.py | 1131 +++++++++++++++----------------- 1 file changed, 511 insertions(+), 620 deletions(-) diff --git a/pymtl3/datatypes/PythonBits.py b/pymtl3/datatypes/PythonBits.py index 3e221e467..7c1f0dc7a 100644 --- a/pymtl3/datatypes/PythonBits.py +++ b/pymtl3/datatypes/PythonBits.py @@ -9,635 +9,526 @@ """ # lower <= value <= upper -_upper = [0, 1] -_lower = [0, -1] +_upper = [ 0, 1 ] +_lower = [ 0, -1 ] for i in range(2, 1024): - _upper.append((_upper[i - 1] << 1) + 1) - _lower.append(_lower[i - 1] << 1) + _upper.append( (_upper[i-1] << 1) + 1 ) + _lower.append( _lower[i-1] << 1 ) object_new = object.__new__ +def _new_valid_bits( nbits, uint ): + ret = object_new( Bits ) + ret._nbits = nbits + ret._uint = uint + return ret +class Bits: + __slots__ = ( "_nbits", "_uint", "_next" ) -def _new_valid_bits(nbits, uint): - ret = object_new(Bits) - ret._nbits = nbits - ret._uint = uint - return ret + @property + def nbits( self ): + return self._nbits + def __init__( self, nbits, v=0, trunc_int=False ): + nbits = int(nbits) + if nbits < 1 or nbits >= 1024: raise ValueError(f"Only support 1 <= nbits < 1024, not {nbits}") -class Bits: - __slots__ = ("_nbits", "_uint", "_next") - - @property - def nbits(self): - return self._nbits - - def __init__(self, nbits, v=0, trunc_int=False): - nbits = int(nbits) - if nbits < 1 or nbits >= 1024: - raise ValueError(f"Only support 1 <= nbits < 1024, not {nbits}") - - self._nbits = nbits - - if isinstance(v, Bits): - if nbits != v.nbits: - if nbits < v.nbits: - raise ValueError( - f"The Bits{v.nbits} object on RHS is too wide to be used to construct Bits{nbits}!\n" - f"- Suggestion: directly use trunc( value, {nbits}/Bits{nbits} )" - ) - else: - raise ValueError( - f"The Bits{v.nbits} object on RHS is too narrow to be used to construct Bits{nbits}!\n" - f"- Suggestion: directly use zext/sext(value, {nbits}/Bits{nbits} )" - ) - self._uint = v._uint + self._nbits = nbits + + if isinstance( v, Bits ): + if nbits != v.nbits: + if nbits < v.nbits: + raise ValueError( f"The Bits{v.nbits} object on RHS is too wide to be used to construct Bits{nbits}!\n" + f"- Suggestion: directly use trunc( value, {nbits}/Bits{nbits} )" ) else: - v = int(v) - up = _upper[nbits] - - if not trunc_int: - lo = _lower[nbits] - if v < lo or v > up: - raise ValueError( - f"Value {hex(v)} is too wide for Bits{nbits}!\n" - f"(Bits{nbits} only accepts {hex(lo)} <= value <= {hex(up)})" - ) - self._uint = v & up - - def __len__(self) -> int: - return self._nbits - - # PyMTL simulation specific - - def __ilshift__(self, v): - nbits = self._nbits - try: - # Bits/Bitstruct - if v.nbits != nbits: - if v.nbits < nbits: - raise ValueError( - f"Bitwidth of LHS must be equal to RHS during <<= non-blocking assignment, " - f"but here LHS Bits{nbits} > RHS Bits{v.nbits}.\n" - f"- Suggestion: LHS @= zext/sext(RHS, nbits/Type)" - ) - else: - raise ValueError( - f"Bitwidth of LHS must be equal to RHS during <<= non-blocking assignment, " - f"but here LHS Bits{nbits} < RHS Bits{v.nbits}.\n" - f"- Suggestion: LHS @= trunc(RHS, nbits/Type)" - ) - self._next = v.to_bits()._uint - except AttributeError: - # Cast to int - v = int(v) - lo = _lower[nbits] - up = _upper[nbits] - - if v < lo or v > up: - raise ValueError( - f"RHS value {hex(v)} of <<= is too wide for LHS Bits{nbits}!\n" - f"(Bits{nbits} only accepts {hex(lo)} <= value <= {hex(up)})" - ) - self._next = v & up - - return self - - def _flip(self): - self._uint = self._next - - def clone(self): - return _new_valid_bits(self._nbits, self._uint) - - def __deepcopy__(self, memo): - return _new_valid_bits(self._nbits, self._uint) - - def __imatmul__(self, v): - nbits = self._nbits - try: - # Bits/Bitstruct - if v.nbits != nbits: - if v.nbits < nbits: - raise ValueError( - f"Bitwidth of LHS must be equal to RHS during @= blocking assignment, " - f"but here LHS Bits{nbits} > RHS Bits{v.nbits}.\n" - f"- Suggestion: LHS @= zext/sext(RHS, nbits/Type)" - ) - else: - raise ValueError( - f"Bitwidth of LHS must be equal to RHS during @= blocking assignment, " - f"but here LHS Bits{nbits} < RHS Bits{v.nbits}.\n" - f"- Suggestion: LHS @= trunc(RHS, nbits/Type)" - ) - self._uint = v.to_bits()._uint - except AttributeError: - # Cast to int - v = int(v) - - lo = _lower[nbits] - up = _upper[nbits] - - if v < lo or v > up: - raise ValueError( - f"RHS value {hex(v)} of @= is too wide for LHS Bits{nbits}!\n" - f"(Bits{nbits} only accepts {hex(lo)} <= value <= {hex(up)})" - ) - self._uint = v & up - - return self - - def to_bits(self): - return self - - # Arithmetics - def __getitem__(self, idx): - - if isinstance(idx, slice): - if idx.step: - raise IndexError("Index cannot contain step") - try: - start, stop = int(idx.start or 0), int(idx.stop or self._nbits) - assert 0 <= start < stop <= self._nbits - except: - raise IndexError( - f"Invalid access: [{idx.start}:{idx.stop}] in a Bits{self._nbits} instance" - ) - - # Bypass check - nbits = stop - start - return _new_valid_bits(stop - start, (self._uint >> start) & _upper[nbits]) - - i = int(idx) - if i >= self._nbits or i < 0: - raise IndexError(f"Invalid access: [{i}] in a Bits{self._nbits} instance") - - # Bypass check - return _new_valid_bits(1, (self._uint >> i) & 1) - - def __setitem__(self, idx, v): - sv = int(self._uint) - - if isinstance(idx, slice): - if idx.step: - raise IndexError("Index cannot contain step") - try: - start, stop = int(idx.start or 0), int(idx.stop or self._nbits) - assert 0 <= start < stop <= self._nbits - except: - raise IndexError( - f"Invalid access: [{idx.start}:{idx.stop}] in a Bits{self._nbits} instance" - ) - - slice_nbits = stop - start - if isinstance(v, Bits): - if v.nbits != slice_nbits: - if v.nbits < slice_nbits: - raise ValueError( - f"Cannot fit a Bits{v.nbits} object into a {slice_nbits}-bit slice [{start}:{stop}]\n" - f"- Suggestion: sext/zext the RHS" - ) - else: - raise ValueError( - f"Cannot fit a Bits{v.nbits} object into a {slice_nbits}-bit slice [{start}:{stop}]\n" - f"- Suggestion: trunc the RHS" - ) - - self._uint = (sv & (~((1 << stop) - (1 << start)))) | ( - (v._uint & _upper[slice_nbits]) << start - ) - else: - # Cast to int - v = int(v) - lo = _lower[slice_nbits] - up = _upper[slice_nbits] - - if v < lo or v > up: - raise ValueError( - f"Cannot fit {v} into a Bits{slice_nbits} slice\n" - f"(Bits{slice_nbits} only accepts {hex(lo)} <= value <= {hex(up)})" - ) - - self._uint = (sv & (~((1 << stop) - (1 << start)))) | ( - (v & _upper[slice_nbits]) << start - ) - return - - i = int(idx) - if i >= self._nbits or i < 0: - raise IndexError(f"Invalid access: [{i}] in a Bits{self._nbits} instance") - - if isinstance(v, Bits): - if v.nbits > 1: - raise ValueError( - f"Cannot fit a Bits{v.nbits} object into the 1-bit slice" - ) - self._uint = (sv & ~(1 << i)) | ((v._uint & 1) << i) + raise ValueError( f"The Bits{v.nbits} object on RHS is too narrow to be used to construct Bits{nbits}!\n" + f"- Suggestion: directly use zext/sext(value, {nbits}/Bits{nbits} )" ) + self._uint = v._uint + else: + v = int(v) + up = _upper[nbits] + + if not trunc_int: + lo = _lower[nbits] + if v < lo or v > up: + raise ValueError( f"Value {hex(v)} is too wide for Bits{nbits}!\n" \ + f"(Bits{nbits} only accepts {hex(lo)} <= value <= {hex(up)})" ) + self._uint = v & up + + def __len__(self) -> int: + return self._nbits + + # PyMTL simulation specific + + def __ilshift__( self, v ): + nbits = self._nbits + try: + # Bits/Bitstruct + if v.nbits != nbits: + if v.nbits < nbits: + raise ValueError( f"Bitwidth of LHS must be equal to RHS during <<= non-blocking assignment, " \ + f"but here LHS Bits{nbits} > RHS Bits{v.nbits}.\n" + f"- Suggestion: LHS @= zext/sext(RHS, nbits/Type)" ) else: - v = int(v) - if abs(v) > 1: - raise ValueError(f"Value {hex(v)} is too big for the 1-bit slice!\n") - self._uint = (sv & ~(1 << i)) | ((int(v) & 1) << i) - - def __add__(self, other): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( - f"Operands of '+' (add) operation must have matching bitwidth, " - f"but here Bits{nbits} != Bits{other.nbits}.\n" - ) - return _new_valid_bits(nbits, (self._uint + other._uint) & _upper[nbits]) - except AttributeError: - other = int(other) - up = _upper[nbits] - if other < 0 or other > up: - raise ValueError( - f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(up)}" - ) - return _new_valid_bits(nbits, (self._uint + other) & up) - - def __radd__(self, other): - return self.__add__(other) - - def __sub__(self, other): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( - f"Operands of '-' (sub) operation must have matching bitwidth, " - f"but here Bits{nbits} != Bits{other.nbits}.\n" - ) - return _new_valid_bits(nbits, (self._uint - other._uint) & _upper[nbits]) - except AttributeError: - other = int(other) - up = _upper[nbits] - if other < 0 or other > up: - raise ValueError( - f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(up)}" - ) - return _new_valid_bits(nbits, (self._uint - other) & up) - - def __rsub__(self, other): - nbits = self._nbits - # Shouldn't be Bits - other = int(other) - up = _upper[nbits] - if other < 0 or other > up: - raise ValueError( - f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(up)}" - ) - return _new_valid_bits(nbits, (other - self._uint) & up) - - def __mul__(self, other): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( - f"Operands of '*' (mul) operation must have matching bitwidth, " - f"but here Bits{nbits} != Bits{other.nbits}.\n" - ) - return _new_valid_bits(nbits, (self._uint * other._uint) & _upper[nbits]) - except AttributeError: - other = int(other) - up = _upper[nbits] - if other < 0 or other > up: - raise ValueError( - f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(up)}" - ) - return _new_valid_bits(nbits, (self._uint * other) & up) - - def __rmul__(self, other): - return self.__mul__(other) - - # no need to AND mask - def __and__(self, other): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( - f"Operands of '&' (and) operation must have matching bitwidth, " - f"but here Bits{nbits} != Bits{other.nbits}.\n" - ) - return _new_valid_bits(nbits, self._uint & other._uint) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[nbits]: - raise ValueError( - f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" - ) - return _new_valid_bits(nbits, self._uint & other) - - def __rand__(self, other): - return self.__and__(other) - - # no need to AND mask - def __or__(self, other): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( - f"Operands of '|' (or) operation must have matching bitwidth, " - f"but here Bits{nbits} != Bits{other.nbits}.\n" - ) - return _new_valid_bits(nbits, self._uint | other._uint) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[nbits]: - raise ValueError( - f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" - ) - return _new_valid_bits(nbits, self._uint | other) - - def __ror__(self, other): - return self.__or__(other) - - def __xor__(self, other): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( - f"Operands of '^' (xor) operation must have matching bitwidth, " - f"but here Bits{nbits} != Bits{other.nbits}.\n" - ) - return _new_valid_bits(nbits, self._uint ^ other._uint) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[self._nbits]: - raise ValueError( - f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" - ) - return _new_valid_bits(nbits, self._uint ^ other) - - def __rxor__(self, other): - return self.__xor__(other) - - def __floordiv__(self, other): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( - f"Operands of '//' (div) operation must have matching bitwidth, " - f"but here Bits{nbits} != Bits{other.nbits}.\n" - ) - return _new_valid_bits(nbits, (self._uint // other._uint) & _upper[nbits]) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[self._nbits]: - raise ValueError( - f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" - ) - return _new_valid_bits(nbits, self._uint // other) - - def __rfloordiv__(self, other): - nbits = self._nbits + raise ValueError( f"Bitwidth of LHS must be equal to RHS during <<= non-blocking assignment, " \ + f"but here LHS Bits{nbits} < RHS Bits{v.nbits}.\n" + f"- Suggestion: LHS @= trunc(RHS, nbits/Type)" ) + self._next = v.to_bits()._uint + except AttributeError: + # Cast to int + v = int(v) + lo = _lower[nbits] + up = _upper[nbits] + + if v < lo or v > up: + raise ValueError( f"RHS value {hex(v)} of <<= is too wide for LHS Bits{nbits}!\n" \ + f"(Bits{nbits} only accepts {hex(lo)} <= value <= {hex(up)})" ) + self._next = v & up + + return self + + def _flip( self ): + self._uint = self._next + + def clone( self ): + return _new_valid_bits( self._nbits, self._uint ) + + def __deepcopy__( self, memo ): + return _new_valid_bits( self._nbits, self._uint ) + + def __imatmul__( self, v ): + nbits = self._nbits + try: + # Bits/Bitstruct + if v.nbits != nbits: + if v.nbits < nbits: + raise ValueError( f"Bitwidth of LHS must be equal to RHS during @= blocking assignment, " \ + f"but here LHS Bits{nbits} > RHS Bits{v.nbits}.\n" + f"- Suggestion: LHS @= zext/sext(RHS, nbits/Type)" ) + else: + raise ValueError( f"Bitwidth of LHS must be equal to RHS during @= blocking assignment, " \ + f"but here LHS Bits{nbits} < RHS Bits{v.nbits}.\n" + f"- Suggestion: LHS @= trunc(RHS, nbits/Type)" ) + self._uint = v.to_bits()._uint + except AttributeError: + # Cast to int + v = int(v) + + lo = _lower[nbits] + up = _upper[nbits] + + if v < lo or v > up: + raise ValueError( f"RHS value {hex(v)} of @= is too wide for LHS Bits{nbits}!\n" \ + f"(Bits{nbits} only accepts {hex(lo)} <= value <= {hex(up)})" ) + self._uint = v & up + + return self + + def to_bits( self ): + return self + + # Arithmetics + def __getitem__( self, idx ): + + if isinstance( idx, slice ): + if idx.step: + raise IndexError( "Index cannot contain step" ) + try: + start, stop = int(idx.start or 0), int(idx.stop or self._nbits) + assert 0 <= start < stop <= self._nbits + except: + raise IndexError( f"Invalid access: [{idx.start}:{idx.stop}] in a Bits{self._nbits} instance" ) + + # Bypass check + nbits = stop - start + return _new_valid_bits( stop-start, (self._uint >> start) & _upper[nbits] ) + + i = int(idx) + if i >= self._nbits or i < 0: + raise IndexError( f"Invalid access: [{i}] in a Bits{self._nbits} instance" ) + + # Bypass check + return _new_valid_bits( 1, (self._uint >> i) & 1 ) + + def __setitem__( self, idx, v ): + sv = int(self._uint) + + if isinstance( idx, slice ): + if idx.step: + raise IndexError( "Index cannot contain step" ) + try: + start, stop = int(idx.start or 0), int(idx.stop or self._nbits) + assert 0 <= start < stop <= self._nbits + except: + raise IndexError( f"Invalid access: [{idx.start}:{idx.stop}] in a Bits{self._nbits} instance" ) + + slice_nbits = stop - start + if isinstance( v, Bits ): + if v.nbits != slice_nbits: + if v.nbits < slice_nbits: + raise ValueError( f"Cannot fit a Bits{v.nbits} object into a {slice_nbits}-bit slice [{start}:{stop}]\n" + f"- Suggestion: sext/zext the RHS") + else: + raise ValueError( f"Cannot fit a Bits{v.nbits} object into a {slice_nbits}-bit slice [{start}:{stop}]\n" + f"- Suggestion: trunc the RHS") + + self._uint = (sv & (~((1 << stop) - (1 << start)))) | \ + ((v._uint & _upper[slice_nbits]) << start) + else: + # Cast to int + v = int(v) + lo = _lower[slice_nbits] + up = _upper[slice_nbits] + + if v < lo or v > up: + raise ValueError( f"Cannot fit {v} into a Bits{slice_nbits} slice\n" \ + f"(Bits{slice_nbits} only accepts {hex(lo)} <= value <= {hex(up)})" ) + + self._uint = (sv & (~((1 << stop) - (1 << start)))) | \ + ((v & _upper[slice_nbits]) << start) + return + + i = int(idx) + if i >= self._nbits or i < 0: + raise IndexError( f"Invalid access: [{i}] in a Bits{self._nbits} instance" ) + + if isinstance( v, Bits ): + if v.nbits > 1: + raise ValueError( f"Cannot fit a Bits{v.nbits} object into the 1-bit slice" ) + self._uint = (sv & ~(1 << i)) | ((v._uint & 1) << i) + else: + v = int(v) + if abs(v) > 1: + raise ValueError( f"Value {hex(v)} is too big for the 1-bit slice!\n" ) + self._uint = (sv & ~(1 << i)) | ((int(v) & 1) << i) + + def __add__( self, other ): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( f"Operands of '+' (add) operation must have matching bitwidth, "\ + f"but here Bits{nbits} != Bits{other.nbits}.\n" ) + return _new_valid_bits( nbits, (self._uint + other._uint) & _upper[nbits] ) + except AttributeError: + other = int(other) + up = _upper[ nbits ] + if other < 0 or other > up: + raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(up)}" ) + return _new_valid_bits( nbits, (self._uint + other) & up ) + + def __radd__( self, other ): + return self.__add__( other ) + + def __sub__( self, other ): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( f"Operands of '-' (sub) operation must have matching bitwidth, "\ + f"but here Bits{nbits} != Bits{other.nbits}.\n" ) + return _new_valid_bits( nbits, (self._uint - other._uint) & _upper[nbits] ) + except AttributeError: + other = int(other) + up = _upper[ nbits ] + if other < 0 or other > up: + raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(up)}" ) + return _new_valid_bits( nbits, (self._uint - other) & up ) + + def __rsub__( self, other ): + nbits = self._nbits + # Shouldn't be Bits + other = int(other) + up = _upper[ nbits ] + if other < 0 or other > up: + raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(up)}" ) + return _new_valid_bits( nbits, (other - self._uint) & up ) + + def __mul__( self, other ): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( f"Operands of '*' (mul) operation must have matching bitwidth, "\ + f"but here Bits{nbits} != Bits{other.nbits}.\n" ) + return _new_valid_bits( nbits, (self._uint * other._uint) & _upper[nbits] ) + except AttributeError: + other = int(other) + up = _upper[ nbits ] + if other < 0 or other > up: + raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(up)}" ) + return _new_valid_bits( nbits, (self._uint * other) & up) + + def __rmul__( self, other ): + return self.__mul__( other ) + + # no need to AND mask + def __and__( self, other ): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( f"Operands of '&' (and) operation must have matching bitwidth, "\ + f"but here Bits{nbits} != Bits{other.nbits}.\n" ) + return _new_valid_bits( nbits, self._uint & other._uint ) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[ nbits ]: + raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" ) + return _new_valid_bits( nbits, self._uint & other ) + + def __rand__( self, other ): + return self.__and__( other ) + + # no need to AND mask + def __or__( self, other ): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( f"Operands of '|' (or) operation must have matching bitwidth, "\ + f"but here Bits{nbits} != Bits{other.nbits}.\n" ) + return _new_valid_bits( nbits, self._uint | other._uint ) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[ nbits ]: + raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" ) + return _new_valid_bits( nbits, self._uint | other ) + + def __ror__( self, other ): + return self.__or__( other ) + + def __xor__( self, other ): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( f"Operands of '^' (xor) operation must have matching bitwidth, "\ + f"but here Bits{nbits} != Bits{other.nbits}.\n" ) + return _new_valid_bits( nbits, self._uint ^ other._uint ) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[ self._nbits ]: + raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" ) + return _new_valid_bits( nbits, self._uint ^ other ) + + def __rxor__( self, other ): + return self.__xor__( other ) + + def __floordiv__( self, other ): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( f"Operands of '//' (div) operation must have matching bitwidth, "\ + f"but here Bits{nbits} != Bits{other.nbits}.\n" ) + return _new_valid_bits( nbits, (self._uint // other._uint) & _upper[nbits] ) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[ self._nbits ]: + raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" ) + return _new_valid_bits( nbits, self._uint // other ) + + def __rfloordiv__( self, other ): + nbits = self._nbits + other = int(other) + up = _upper[ nbits ] + if other < 0 or other > up: + raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(up)}" ) + return _new_valid_bits( nbits, other // self._uint ) + + def __mod__( self, other ): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( f"Operands of '%' (mod) operation must have matching bitwidth, "\ + f"but here Bits{nbits} != Bits{other.nbits}.\n" ) + return _new_valid_bits( nbits, (self._uint % other._uint) & _upper[nbits] ) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[ nbits ]: + raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" ) + return _new_valid_bits( nbits, self._uint % other ) + + def __rmod__( self, other ): + nbits = self._nbits + other = int(other) + up = _upper[ nbits ] + if other < 0 or other > up: + raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(up)}" ) + return _new_valid_bits( nbits, other % self._uint ) + + def __invert__( self ): + nbits = self._nbits + return _new_valid_bits( nbits, ~self._uint & _upper[nbits] ) + + def __lshift__( self, other ): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( f"Operands of '<<' (lshift) operation must have matching bitwidth, "\ + f"but here Bits{nbits} != Bits{other.nbits}.\n" ) + uint = other._uint + if uint >= nbits: + return _new_valid_bits( self._nbits, 0 ) + return _new_valid_bits( nbits, (self._uint << uint) & _upper[nbits] ) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[ nbits ]: + raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" ) + if other >= nbits: + return _new_valid_bits( self._nbits, 0 ) + return _new_valid_bits( nbits, (self._uint << other) & _upper[nbits] ) + + def __rshift__( self, other ): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( f"Operands of '>>' (rshift) operation must have matching bitwidth, "\ + f"but here Bits{nbits} != Bits{other.nbits}.\n" ) + return _new_valid_bits( nbits, self._uint >> other._uint ) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[ nbits ]: + raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" ) + return _new_valid_bits( nbits, self._uint >> other ) + + def __eq__( self, other ): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( f"Operands of '==' (eq) operation must have matching bitwidth, "\ + f"but here Bits{nbits} != Bits{other.nbits}.\n" ) + return _new_valid_bits( 1, self._uint == other._uint ) + except AttributeError: + try: other = int(other) - up = _upper[nbits] - if other < 0 or other > up: - raise ValueError( - f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(up)}" - ) - return _new_valid_bits(nbits, other // self._uint) - - def __mod__(self, other): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( - f"Operands of '%' (mod) operation must have matching bitwidth, " - f"but here Bits{nbits} != Bits{other.nbits}.\n" - ) - return _new_valid_bits(nbits, (self._uint % other._uint) & _upper[nbits]) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[nbits]: - raise ValueError( - f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" - ) - return _new_valid_bits(nbits, self._uint % other) - - def __rmod__(self, other): - nbits = self._nbits + except: + return _new_valid_bits( 1, 0 ) + + if other < 0 or other > _upper[ nbits ]: + raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" ) + return _new_valid_bits( 1, self._uint == other ) + + def __ne__( self, other ): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( f"Operands of '!=' (ne) operation must have matching bitwidth, "\ + f"but here Bits{nbits} != Bits{other.nbits}.\n" ) + return _new_valid_bits( 1, self._uint != other._uint ) + except AttributeError: + try: other = int(other) - up = _upper[nbits] - if other < 0 or other > up: - raise ValueError( - f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(up)}" - ) - return _new_valid_bits(nbits, other % self._uint) - - def __invert__(self): - nbits = self._nbits - return _new_valid_bits(nbits, ~self._uint & _upper[nbits]) - - def __lshift__(self, other): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( - f"Operands of '<<' (lshift) operation must have matching bitwidth, " - f"but here Bits{nbits} != Bits{other.nbits}.\n" - ) - uint = other._uint - if uint >= nbits: - return _new_valid_bits(self._nbits, 0) - return _new_valid_bits(nbits, (self._uint << uint) & _upper[nbits]) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[nbits]: - raise ValueError( - f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" - ) - if other >= nbits: - return _new_valid_bits(self._nbits, 0) - return _new_valid_bits(nbits, (self._uint << other) & _upper[nbits]) - - def __rshift__(self, other): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( - f"Operands of '>>' (rshift) operation must have matching bitwidth, " - f"but here Bits{nbits} != Bits{other.nbits}.\n" - ) - return _new_valid_bits(nbits, self._uint >> other._uint) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[nbits]: - raise ValueError( - f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" - ) - return _new_valid_bits(nbits, self._uint >> other) - - def __eq__(self, other): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( - f"Operands of '==' (eq) operation must have matching bitwidth, " - f"but here Bits{nbits} != Bits{other.nbits}.\n" - ) - return _new_valid_bits(1, self._uint == other._uint) - except AttributeError: - try: - other = int(other) - except: - return _new_valid_bits(1, 0) - - if other < 0 or other > _upper[nbits]: - raise ValueError( - f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" - ) - return _new_valid_bits(1, self._uint == other) - - def __ne__(self, other): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( - f"Operands of '!=' (ne) operation must have matching bitwidth, " - f"but here Bits{nbits} != Bits{other.nbits}.\n" - ) - return _new_valid_bits(1, self._uint != other._uint) - except AttributeError: - try: - other = int(other) - except: - return _new_valid_bits(1, 1) - - if other < 0 or other > _upper[nbits]: - raise ValueError( - f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" - ) - return _new_valid_bits(1, self._uint != other) - - def __lt__(self, other): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( - f"Operands of '<' (lt) operation must have matching bitwidth, " - f"but here Bits{nbits} != Bits{other.nbits}.\n" - ) - return _new_valid_bits(1, self._uint < other._uint) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[self._nbits]: - raise ValueError( - f"Integer {hex(other)} is not a valid binop operand with Bits{self._nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ self._nbits ])}" - ) - return _new_valid_bits(1, self._uint < other) - - def __le__(self, other): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( - f"Operands of '<=' (le) operation must have matching bitwidth, " - f"but here Bits{nbits} != Bits{other.nbits}.\n" - ) - return _new_valid_bits(1, self._uint <= other._uint) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[self._nbits]: - raise ValueError( - f"Integer {hex(other)} is not a valid binop operand with Bits{self._nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ self._nbits ])}" - ) - return _new_valid_bits(1, self._uint <= other) - - def __gt__(self, other): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( - f"Operands of '>' (gt) operation must have matching bitwidth, " - f"but here Bits{nbits} != Bits{other.nbits}.\n" - ) - return _new_valid_bits(1, self._uint > other._uint) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[self._nbits]: - raise ValueError( - f"Integer {hex(other)} is not a valid binop operand with Bits{self._nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ self._nbits ])}" - ) - return _new_valid_bits(1, self._uint > other) - - def __ge__(self, other): - nbits = self._nbits - try: - if other.nbits != nbits: - raise ValueError( - f"Operands of '>=' (ge) operation must have matching bitwidth, " - f"but here Bits{nbits} != Bits{other.nbits}.\n" - ) - return _new_valid_bits(1, self._uint >= other._uint) - except AttributeError: - other = int(other) - if other < 0 or other > _upper[self._nbits]: - raise ValueError( - f"Integer {hex(other)} is not a valid binop operand with Bits{self._nbits}!\n" - f"Suggestion: 0 <= x <= {hex(_upper[ self._nbits ])}" - ) - return _new_valid_bits(1, self._uint >= other) - - def __bool__(self): - return self._uint != 0 - - def __int__(self): - return int(self._uint) - - def int(self): - if self._uint >> (self._nbits - 1): - return -int(~self + 1) - return self._uint - - def uint(self): - return self._uint - - def __index__(self): - return int(self._uint) - - def __hash__(self): - return hash((self._nbits, self._uint)) - - # Print - - def __repr__(self): - return "Bits{}(0x{})".format( - self._nbits, - "{:x}".format(int(self._uint)).zfill(((self._nbits - 1) // 4) + 1), - ) - - def __str__(self): - str = "{:x}".format(int(self._uint)).zfill(((self._nbits - 1) // 4) + 1) - return str - - def bin(self): - str = "{:b}".format(int(self._uint)).zfill(self._nbits) - return "0b" + str - - def oct(self): - str = "{:o}".format(int(self._uint)).zfill(((self._nbits - 1) // 3) + 1) - return "0o" + str - - def hex(self): - str = "{:x}".format(int(self._uint)).zfill(((self._nbits - 1) // 4) + 1) - return "0x" + str + except: + return _new_valid_bits( 1, 1 ) + + if other < 0 or other > _upper[ nbits ]: + raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ nbits ])}" ) + return _new_valid_bits( 1, self._uint != other ) + + def __lt__( self, other ): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( f"Operands of '<' (lt) operation must have matching bitwidth, "\ + f"but here Bits{nbits} != Bits{other.nbits}.\n" ) + return _new_valid_bits( 1, self._uint < other._uint ) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[ self._nbits ]: + raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{self._nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ self._nbits ])}" ) + return _new_valid_bits( 1, self._uint < other ) + + def __le__( self, other ): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( f"Operands of '<=' (le) operation must have matching bitwidth, "\ + f"but here Bits{nbits} != Bits{other.nbits}.\n" ) + return _new_valid_bits( 1, self._uint <= other._uint ) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[ self._nbits ]: + raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{self._nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ self._nbits ])}" ) + return _new_valid_bits( 1, self._uint <= other ) + + def __gt__( self, other ): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( f"Operands of '>' (gt) operation must have matching bitwidth, "\ + f"but here Bits{nbits} != Bits{other.nbits}.\n" ) + return _new_valid_bits( 1, self._uint > other._uint ) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[ self._nbits ]: + raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{self._nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ self._nbits ])}" ) + return _new_valid_bits( 1, self._uint > other ) + + def __ge__( self, other ): + nbits = self._nbits + try: + if other.nbits != nbits: + raise ValueError( f"Operands of '>=' (ge) operation must have matching bitwidth, "\ + f"but here Bits{nbits} != Bits{other.nbits}.\n" ) + return _new_valid_bits( 1, self._uint >= other._uint ) + except AttributeError: + other = int(other) + if other < 0 or other > _upper[ self._nbits ]: + raise ValueError( f"Integer {hex(other)} is not a valid binop operand with Bits{self._nbits}!\n" + f"Suggestion: 0 <= x <= {hex(_upper[ self._nbits ])}" ) + return _new_valid_bits( 1, self._uint >= other ) + + def __bool__( self ): + return self._uint != 0 + + def __int__( self ): + return int(self._uint) + + def int( self ): + if self._uint >> (self._nbits - 1): + return -int(~self + 1) + return self._uint + + def uint( self ): + return self._uint + + def __index__( self ): + return int(self._uint) + + def __hash__( self ): + return hash((self._nbits, self._uint)) + + # Print + + def __repr__(self): + return "Bits{}(0x{})".format( self._nbits, "{:x}".format(int(self._uint)).zfill(((self._nbits-1)//4)+1) ) + + def __str__(self): + str = "{:x}".format(int(self._uint)).zfill(((self._nbits-1)//4)+1) + return str + + def bin(self): + str = "{:b}".format(int(self._uint)).zfill(self._nbits) + return "0b"+str + + def oct( self ): + str = "{:o}".format(int(self._uint)).zfill(((self._nbits-1)//3)+1) + return "0o"+str + + def hex( self ): + str = "{:x}".format(int(self._uint)).zfill(((self._nbits-1)//4)+1) + return "0x"+str \ No newline at end of file From 22230ddb88dfb7f1aaad81a5c52b0ed2b678f7f4 Mon Sep 17 00:00:00 2001 From: Kelvin Chung Date: Thu, 4 Apr 2024 16:08:17 +0100 Subject: [PATCH 078/101] add test --- pymtl3/datatypes/test/bits_test.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pymtl3/datatypes/test/bits_test.py b/pymtl3/datatypes/test/bits_test.py index 242e560ed..a7bbd2394 100644 --- a/pymtl3/datatypes/test/bits_test.py +++ b/pymtl3/datatypes/test/bits_test.py @@ -760,3 +760,8 @@ def test_bin_oct_hex(): assert Bits(15,35).bin() == "0b000000000100011" assert Bits(15,35).oct() == "0o00043" assert Bits(15,35).hex() == "0x0023" + +def test_len_interface(): + a = Bits(4, 0) + assert len(a) == 4 + \ No newline at end of file From 49bfcb11bd00de8005d1043503bd0a742c05becc Mon Sep 17 00:00:00 2001 From: Yanghui Ou Date: Thu, 4 Apr 2024 11:22:31 -0400 Subject: [PATCH 079/101] Add new line at end of file --- pymtl3/datatypes/PythonBits.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymtl3/datatypes/PythonBits.py b/pymtl3/datatypes/PythonBits.py index 7c1f0dc7a..4f8a1584c 100644 --- a/pymtl3/datatypes/PythonBits.py +++ b/pymtl3/datatypes/PythonBits.py @@ -531,4 +531,4 @@ def oct( self ): def hex( self ): str = "{:x}".format(int(self._uint)).zfill(((self._nbits-1)//4)+1) - return "0x"+str \ No newline at end of file + return "0x"+str From d5bf3ba39cff3153c78fd9a9d54295b42a440474 Mon Sep 17 00:00:00 2001 From: Yanghui Ou Date: Thu, 4 Apr 2024 11:23:26 -0400 Subject: [PATCH 080/101] Remove trailing white spaces. --- pymtl3/datatypes/test/bits_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pymtl3/datatypes/test/bits_test.py b/pymtl3/datatypes/test/bits_test.py index a7bbd2394..d31bff02b 100644 --- a/pymtl3/datatypes/test/bits_test.py +++ b/pymtl3/datatypes/test/bits_test.py @@ -764,4 +764,3 @@ def test_bin_oct_hex(): def test_len_interface(): a = Bits(4, 0) assert len(a) == 4 - \ No newline at end of file From 2eb57d3c4ca87d7fdc3fda467a9a9dca294c2c75 Mon Sep 17 00:00:00 2001 From: Christopher Batten Date: Mon, 22 Apr 2024 14:24:24 -0700 Subject: [PATCH 081/101] Fix issue with pytest_cmdline_preparse The pytest_cmdline_preparse hook is now deprecated. I think there should be a way to achieve the same effect using pytest_load_initial_conftests https://docs.pytest.org/en/7.1.x/reference/reference.html#pytest.hookspec.pytest_cmdline_preparse However, were were using this to hook to prevent *.pyc and __pycache__ files from being generated. I think it is fine for now to just go back to generating these files. --- pytest_plugin/pytest_pymtl3.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pytest_plugin/pytest_pymtl3.py b/pytest_plugin/pytest_pymtl3.py index 144269001..913da2936 100644 --- a/pytest_plugin/pytest_pymtl3.py +++ b/pytest_plugin/pytest_pymtl3.py @@ -66,11 +66,6 @@ def pytest_configure(config): def pytest_unconfigure(config): pass -def pytest_cmdline_preparse(config, args): - """Don't write *.pyc and __pycache__ files.""" - import sys - sys.dont_write_bytecode = True - def pytest_runtest_setup(item): if _any_opts_present(item.config) and 'cmdline_opts' not in item.fixturenames: pytest.skip("'cmdline_opts' is required by pytest commandline but not used") From b39d5058312d657cdc2975a96d2da121f3888dea Mon Sep 17 00:00:00 2001 From: Kelvin Chung Date: Tue, 30 Apr 2024 11:26:08 +0100 Subject: [PATCH 082/101] Improve index handling in Bits and Signal classes --- pymtl3/datatypes/PythonBits.py | 2 ++ pymtl3/dsl/Connectable.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/pymtl3/datatypes/PythonBits.py b/pymtl3/datatypes/PythonBits.py index 33bd47f23..f65853800 100644 --- a/pymtl3/datatypes/PythonBits.py +++ b/pymtl3/datatypes/PythonBits.py @@ -133,6 +133,8 @@ def __getitem__( self, idx ): raise IndexError( "Index cannot contain step" ) try: start, stop = int(idx.start or 0), int(idx.stop or self._nbits) + if start is None: start = 0 + if stop is None: stop = self._nbits assert 0 <= start < stop <= self._nbits except: raise IndexError( f"Invalid access: [{idx.start}:{idx.stop}] in a Bits{self._nbits} instance" ) diff --git a/pymtl3/dsl/Connectable.py b/pymtl3/dsl/Connectable.py index 6a8683251..910c21ded 100644 --- a/pymtl3/dsl/Connectable.py +++ b/pymtl3/dsl/Connectable.py @@ -261,6 +261,8 @@ def __getitem__( s, idx ): start, stop = idx, idx + 1 elif isinstance( idx, slice ): start, stop = idx.start, idx.stop + if start is None: start = 0 + if stop is None: stop = s._dsl.Type.nbits else: assert False, f"The slice {idx} is invalid" if s._dsl.slice is None: From d0bc16ead0efc2e88aec77ab6de3a659c1db4999 Mon Sep 17 00:00:00 2001 From: Kelvin Chung Date: Tue, 30 Apr 2024 16:12:32 +0100 Subject: [PATCH 083/101] Adding test --- pymtl3/datatypes/test/bits_test.py | 6 ++++++ pymtl3/dsl/test/Slicing_test.py | 21 ++++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/pymtl3/datatypes/test/bits_test.py b/pymtl3/datatypes/test/bits_test.py index 242e560ed..4943b66fe 100644 --- a/pymtl3/datatypes/test/bits_test.py +++ b/pymtl3/datatypes/test/bits_test.py @@ -704,6 +704,12 @@ def test_slice_bits(): with pytest.raises( IndexError ): assert data[x:x] == 0b1 +def test_slice_bits_int(): + data = Bits(8, 0b1101) + assert data[:2] == 0b01 + assert data[2:] == 0b11 + + def test_clone(): a = Bits(4,3) b = a.clone() diff --git a/pymtl3/dsl/test/Slicing_test.py b/pymtl3/dsl/test/Slicing_test.py index fd0318c80..bea186838 100644 --- a/pymtl3/dsl/test/Slicing_test.py +++ b/pymtl3/dsl/test/Slicing_test.py @@ -127,9 +127,7 @@ def up_wr_30_31(): def up_rd_A(): print(s.A[0:17]) - m = Top() - m.elaborate() - simple_sim_pass( m, 0x123 ) + _test_model( Top ) # assert len(m._all_constraints) == 2 # _, x = list(m._all_constraints)[0] @@ -165,6 +163,23 @@ def up_rd_A(): return raise Exception("Should've thrown MultiWriterError.") +# test slicing without specified width +def test_write_two_slices_and_bit_without_specifying_width(): + + class Top( ComponentLevel3 ): + def construct( s ): + s.A = Wire( Bits32 ) + + @update + def up_wr_0_16(): + s.A[:16] @= 0xff + + @update + def up_wr_16_32(): + s.A[16:] @= 0xff + + _test_model(Top) + # write a slice and there are two reader def test_multiple_readers(): From 05d82acf8a5158f836b72aedbcdddde896164551 Mon Sep 17 00:00:00 2001 From: Kelvin Chung Date: Wed, 1 May 2024 10:22:33 +0100 Subject: [PATCH 084/101] update ast helper --- pymtl3/dsl/AstHelper.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pymtl3/dsl/AstHelper.py b/pymtl3/dsl/AstHelper.py index 1678f2318..1506ad4ad 100644 --- a/pymtl3/dsl/AstHelper.py +++ b/pymtl3/dsl/AstHelper.py @@ -61,8 +61,7 @@ def _get_full_name_up_to_py38( self, input_node ): if x in self.globals: up = (False, x) elif x in self.closure: up = (True, x) - if low is not None and up is not None: - slices.append( slice(low, up) ) + slices.append( slice(low, up) ) # FIXME # else: @@ -160,8 +159,7 @@ def _get_full_name_starting_py39( self, input_node ): if x in self.globals: up = (False, x) elif x in self.closure: up = (True, x) - if low is not None and up is not None: - slices.append( slice(low, up) ) + slices.append( slice(low, up) ) # FIXME # else: From ea0154785747f06bef9dc36dc1d86f12a508ae29 Mon Sep 17 00:00:00 2001 From: cb535 Date: Sat, 17 Aug 2024 12:06:51 -0400 Subject: [PATCH 085/101] Verilator now requires C++14 --- .../backends/verilog/import_/VerilogVerilatorImportConfigs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py index 5d30baedf..b0e5db15a 100644 --- a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py +++ b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py @@ -326,7 +326,7 @@ def create_cc_cmd( s ): if not s.is_default("c_flags"): c_flags += f" {expand(s.c_flags)}" - c_flags += f" -fPIC -shared -std=c++11 -pthread" + c_flags += f" -fPIC -shared -std=c++14 -pthread" c_include_path = " ".join("-I"+p for p in s._get_all_includes() if p) out_file = s.get_shared_lib_path() From 212f548754251b6f59bbd10284418177c9b27dc4 Mon Sep 17 00:00:00 2001 From: cb535 Date: Sat, 15 Feb 2025 14:13:30 -0500 Subject: [PATCH 086/101] add VTB_OUTPUT_DELAY for vtbgen --- .../verilog/tbgen/verilog_tbgen_v_template.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py b/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py index 0d9ce3161..480fecb78 100644 --- a/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py +++ b/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py @@ -1,11 +1,19 @@ template = \ ''' -// VT_INPUT_DELAY, VTB_OUTPUT_ASSERT_DELAY are timestamps relative to the rising edge. +// VTB_CYCLE_TIME : clock period in ns +// VTB_INPUT_DELAY : how long after rising clk edge should we write inputs +// VTB_OUTPUT_DELAY : how long before rising clk edge should we check outputs +// VTB_OUTPUT_ASSERT_DELAY = VTB_CYCLE_TIME - VTB_OUTPUT_DELAY +// setting VTB_OUTPUT_ASSERT_DELAY takes priority over VTB_OUTPUT_DELAY + +`define CYCLE_TIME 10 `define VTB_INPUT_DELAY 1 -`define VTB_OUTPUT_ASSERT_DELAY 3 +`define VTB_OUTPUT_DELAY 1 + +`ifndef VTB_OUTPUT_ASSERT_DELAY +`define VTB_OUTPUT_ASSERT_DELAY (`CYCLE_TIME-`VTB_OUTPUT_DELAY) +`endif -// CYCLE_TIME and INTRA_CYCLE_TIME are duration of time. -`define CYCLE_TIME 4 `define INTRA_CYCLE_TIME (`VTB_OUTPUT_ASSERT_DELAY-`VTB_INPUT_DELAY) `timescale 1ns/1ns From 5c56dbaf852b05ada37955d6d5ae53ff571a4caa Mon Sep 17 00:00:00 2001 From: cb535 Date: Sat, 15 Feb 2025 16:46:17 -0500 Subject: [PATCH 087/101] [tbgen] use Top as top module name --- .../passes/backends/verilog/tbgen/verilog_tbgen_v_template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py b/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py index 480fecb78..66d316f24 100644 --- a/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py +++ b/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py @@ -51,7 +51,7 @@ `VTB_TEST_FAIL(lineno, out, ref, port_name) \\ end -module {harness_name}; +module Top; // convention logic clk; logic reset; From 7f76befaafa6d176894a7d3a877d77d6f9e48d02 Mon Sep 17 00:00:00 2001 From: cb535 Date: Sat, 22 Feb 2025 12:51:19 -0500 Subject: [PATCH 088/101] [vtbgen] fix issue with intger CYCLE_TIME We were using this in our generated Verilog test bench: always #(`CYCLE_TIME/2) clk = ~clk; But if CYCLE_TIME is an odd integer (say 9) this would result in integer division and round down (i.e., with a cycle time of 9 we would end up with a clock period of 8!). This doesn't matter unless we are doing back- annotated gate-level simulation _and_ we use an odd integer clock period which is why we never detected this bug before. The fix is to simply force floating point division. always #((`CYCLE_TIME*1.0)/2) clk = ~clk; --- .../backends/verilog/tbgen/verilog_tbgen_v_template.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py b/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py index 66d316f24..9c4f1db37 100644 --- a/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py +++ b/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py @@ -72,8 +72,7 @@ end endtask - // use 25% clock cycle, so #1 for setup #2 for sim #1 for hold - always #(`CYCLE_TIME/2) clk = ~clk; + always #((`CYCLE_TIME*1.0)/2) clk = ~clk; // DUT name // By default we use the translated name of the Verilog component. But you can change @@ -103,7 +102,7 @@ cycle_count = 0; clk = 1'b0; // NEED TO DO THIS TO HAVE FALLING EDGE AT TIME 0 reset = 1'b1; // TODO reset active low/high - #(`CYCLE_TIME/2); + #((`CYCLE_TIME*1.0)/2); // Now we are talking #`VTB_INPUT_DELAY; From 87cf850de9125ad6026c40a8b591b67e162ddfe6 Mon Sep 17 00:00:00 2001 From: cb535 Date: Mon, 24 Feb 2025 23:19:42 -0500 Subject: [PATCH 089/101] [vtbgen] working on dumping saif --- .../verilog/tbgen/VerilogTBGenPass.py | 3 ++ .../verilog/tbgen/verilog_tbgen_v_template.py | 35 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/pymtl3/passes/backends/verilog/tbgen/VerilogTBGenPass.py b/pymtl3/passes/backends/verilog/tbgen/VerilogTBGenPass.py index 928c61bd2..74bc162cf 100644 --- a/pymtl3/passes/backends/verilog/tbgen/VerilogTBGenPass.py +++ b/pymtl3/passes/backends/verilog/tbgen/VerilogTBGenPass.py @@ -138,6 +138,9 @@ def traverse_hierarchy( m ): dut_reset_decl = '.reset(reset)' if x._ph_cfg.has_reset else '', dut_signal_decls = ",\n ".join(dut_signal_decls), # logic [31:0] xxx, -- packed array, # .x(x), -- packed array cases_file_name = f"{dut_name}_{case_name}_tb.v.cases", + saif_file_name = f"{dut_name}_{case_name}.saif", + saif_roi_define = "", + saif_roi_signal = "saif_roi_not_enabled", )) case_file = open( f"{dut_name}_{case_name}_tb.v.cases", "w" ) diff --git a/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py b/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py index 9c4f1db37..e9346fad0 100644 --- a/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py +++ b/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py @@ -72,6 +72,26 @@ end endtask + {saif_roi_define} + `ifdef VTB_DUMP_SAIF + `ifdef VTB_DUMP_SAIF_ROI + always @(posedge {saif_roi_signal} ) begin + if ( !reset ) begin + $set_gate_level_monitoring( "on" ); + $set_toggle_region( DUT ); + $toggle_start; + end + end + + always @(negedge {saif_roi_signal} ) begin + if ( !reset ) begin + $toggle_stop; + $toggle_report( "{saif_file_name}", 1e-12, DUT ); + end + end + `endif + `endif + always #((`CYCLE_TIME*1.0)/2) clk = ~clk; // DUT name @@ -113,8 +133,23 @@ // 2 cycles plus input delay reset = 1'b0; + `ifdef VTB_DUMP_SAIF + `ifndef VTB_DUMP_SAIF_ROI + $set_gate_level_monitoring( "on" ); + $set_toggle_region( DUT ); + $toggle_start; + `endif + `endif + `include "{cases_file_name}" + `ifdef VTB_DUMP_SAIF + `ifndef VTB_DUMP_SAIF_ROI + $toggle_stop; + $toggle_report( "{saif_file_name}", 1e-12, DUT ); + `endif + `endif + $display(""); $display(" [ passed ]"); $display(""); From e1803ab4182a78ad377ec73fbb45119954a30f5b Mon Sep 17 00:00:00 2001 From: cb535 Date: Tue, 25 Feb 2025 21:12:07 -0500 Subject: [PATCH 090/101] [vtbgen] add metadata for saif roi --- .../verilog/tbgen/VerilogTBGenPass.py | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/pymtl3/passes/backends/verilog/tbgen/VerilogTBGenPass.py b/pymtl3/passes/backends/verilog/tbgen/VerilogTBGenPass.py index 74bc162cf..d7e90e43a 100644 --- a/pymtl3/passes/backends/verilog/tbgen/VerilogTBGenPass.py +++ b/pymtl3/passes/backends/verilog/tbgen/VerilogTBGenPass.py @@ -31,6 +31,13 @@ class VerilogTBGenPass( BasePass ): #: Default value: "" case_name = MetadataKey(str) + #: tbgen saif enable port name + #: + #: Type: ``str``; input + #: + #: Default value: "" + saif_roi_signal = MetadataKey(str) + vtbgen_hooks = MetadataKey(list) def __call__( self, top ): @@ -126,6 +133,16 @@ def traverse_hierarchy( m ): dut_name = x._ip_cfg.translated_top_module with open( f"{dut_name}_{case_name}_tb.v", 'w' ) as output: + + saif_roi_define = "" + saif_roi_signal = "saif_roi_not_enabled" + if top.has_metadata( self.saif_roi_signal ): + print(f"saif_roi_signal {top.get_metadata(self.saif_roi_signal)}") + saif_roi_define = "`define VTB_DUMP_SAIF_ROI" + saif_roi_signal = top.get_metadata(self.saif_roi_signal) + else: + print(f"saif_roi_signal undefined") + output.write( tb_template.format( args_strs = ",".join([f"a{i}" for i in range(len(task_signal_decls))]), harness_name = dut_name + "_tb", @@ -139,8 +156,8 @@ def traverse_hierarchy( m ): dut_signal_decls = ",\n ".join(dut_signal_decls), # logic [31:0] xxx, -- packed array, # .x(x), -- packed array cases_file_name = f"{dut_name}_{case_name}_tb.v.cases", saif_file_name = f"{dut_name}_{case_name}.saif", - saif_roi_define = "", - saif_roi_signal = "saif_roi_not_enabled", + saif_roi_define = saif_roi_define, + saif_roi_signal = saif_roi_signal, )) case_file = open( f"{dut_name}_{case_name}_tb.v.cases", "w" ) From 610c592daa30caeb9d096be904d6bc217728f5c6 Mon Sep 17 00:00:00 2001 From: cb535 Date: Tue, 25 Feb 2025 21:38:47 -0500 Subject: [PATCH 091/101] [vtbgen] remove print --- pymtl3/passes/backends/verilog/tbgen/VerilogTBGenPass.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pymtl3/passes/backends/verilog/tbgen/VerilogTBGenPass.py b/pymtl3/passes/backends/verilog/tbgen/VerilogTBGenPass.py index d7e90e43a..411503a79 100644 --- a/pymtl3/passes/backends/verilog/tbgen/VerilogTBGenPass.py +++ b/pymtl3/passes/backends/verilog/tbgen/VerilogTBGenPass.py @@ -137,11 +137,8 @@ def traverse_hierarchy( m ): saif_roi_define = "" saif_roi_signal = "saif_roi_not_enabled" if top.has_metadata( self.saif_roi_signal ): - print(f"saif_roi_signal {top.get_metadata(self.saif_roi_signal)}") saif_roi_define = "`define VTB_DUMP_SAIF_ROI" saif_roi_signal = top.get_metadata(self.saif_roi_signal) - else: - print(f"saif_roi_signal undefined") output.write( tb_template.format( args_strs = ",".join([f"a{i}" for i in range(len(task_signal_decls))]), From 8fda8c078f7777f34cb6f8889158d1f9426644f3 Mon Sep 17 00:00:00 2001 From: cb535 Date: Tue, 25 Feb 2025 22:14:11 -0500 Subject: [PATCH 092/101] [vtbgen] saif filename uses define --- .../passes/backends/verilog/tbgen/verilog_tbgen_v_template.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py b/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py index e9346fad0..952df65dc 100644 --- a/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py +++ b/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py @@ -86,7 +86,7 @@ always @(negedge {saif_roi_signal} ) begin if ( !reset ) begin $toggle_stop; - $toggle_report( "{saif_file_name}", 1e-12, DUT ); + $toggle_report( "`VTB_DUMP_SAIF", 1e-12, DUT ); end end `endif @@ -146,7 +146,7 @@ `ifdef VTB_DUMP_SAIF `ifndef VTB_DUMP_SAIF_ROI $toggle_stop; - $toggle_report( "{saif_file_name}", 1e-12, DUT ); + $toggle_report( "`VTB_DUMP_SAIF", 1e-12, DUT ); `endif `endif From d20ec4d74373774d74ccb32fb010244a0e297824 Mon Sep 17 00:00:00 2001 From: cb535 Date: Tue, 25 Feb 2025 22:32:32 -0500 Subject: [PATCH 093/101] [vtbgen] saif filename fix --- .../backends/verilog/tbgen/verilog_tbgen_v_template.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py b/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py index 952df65dc..ae77167b7 100644 --- a/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py +++ b/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py @@ -18,6 +18,8 @@ `timescale 1ns/1ns +`define STRINGIFY(x) `"x`" + `define T({args_strs}) \\ t({args_strs},`__LINE__) @@ -86,7 +88,7 @@ always @(negedge {saif_roi_signal} ) begin if ( !reset ) begin $toggle_stop; - $toggle_report( "`VTB_DUMP_SAIF", 1e-12, DUT ); + $toggle_report( `STRINGIFY(`VTB_DUMP_SAIF), 1e-12, DUT ); end end `endif @@ -146,7 +148,7 @@ `ifdef VTB_DUMP_SAIF `ifndef VTB_DUMP_SAIF_ROI $toggle_stop; - $toggle_report( "`VTB_DUMP_SAIF", 1e-12, DUT ); + $toggle_report( `STRINGIFY(`VTB_DUMP_SAIF), 1e-12, DUT ); `endif `endif From c28536f835da6c2231e8be1c093513ee45cf46c2 Mon Sep 17 00:00:00 2001 From: cb535 Date: Thu, 27 Feb 2025 00:15:19 -0500 Subject: [PATCH 094/101] Fix issue with changing vcd file name multiple times We were seeing a bug where if you ran a bunch of tests with --dump-vcd PyMTL3 would only dump the VCD for the first test, although every once and a while it would indeed dump VCD files for the every test. This seems to have something to do with the shared library. The cleanest fix seems to be to avoid hard coding the VCD file name in the generated PyMTL wrapper and instead to dynamically get the VCD file name from the metadata. --- .../verilog/import_/verilator_wrapper_py_template.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py index 8ed4d31c5..c74caa4eb 100644 --- a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py +++ b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py @@ -18,6 +18,7 @@ from pymtl3.datatypes import * from pymtl3.dsl import Component, connect, InPort, OutPort, Wire, update, update_ff +from pymtl3.passes.backends.verilog import * #------------------------------------------------------------------------- # {component_name} @@ -124,9 +125,9 @@ def __del__( s ): def construct( s, *args, **kwargs ): # Set up the VCD file name verilator_vcd_file = "" - if {dump_vcd}: - if {has_vl_trace_filename}: - verilator_vcd_file = "{vl_trace_filename}.verilator1.vcd" + if s.has_metadata( VerilogVerilatorImportPass.vl_trace ): + if s.has_metadata( VerilogVerilatorImportPass.vl_trace_filename ): + verilator_vcd_file = f"{{s.has_metadata(VerilogVerilatorImportPass.vl_trace_filename)}}.verilator1.vcd" else: verilator_vcd_file = "{component_name}.verilator1.vcd" From 6fd77219ff57c253bc254976f7f064ecf4a557c0 Mon Sep 17 00:00:00 2001 From: cb535 Date: Thu, 27 Feb 2025 00:29:11 -0500 Subject: [PATCH 095/101] access metadata directly for vcd filename --- .../verilog/import_/verilator_wrapper_py_template.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py index c74caa4eb..059f9d74b 100644 --- a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py +++ b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_py_template.py @@ -125,9 +125,9 @@ def __del__( s ): def construct( s, *args, **kwargs ): # Set up the VCD file name verilator_vcd_file = "" - if s.has_metadata( VerilogVerilatorImportPass.vl_trace ): - if s.has_metadata( VerilogVerilatorImportPass.vl_trace_filename ): - verilator_vcd_file = f"{{s.has_metadata(VerilogVerilatorImportPass.vl_trace_filename)}}.verilator1.vcd" + if int(s._ip_cfg.vl_trace): + if bool(s._ip_cfg.vl_trace_filename): + verilator_vcd_file = f"{{s._ip_cfg.vl_trace_filename}}.verilator1.vcd" else: verilator_vcd_file = "{component_name}.verilator1.vcd" From 62a62e424e6227a8954cfbadb6d7aa93d5834d06 Mon Sep 17 00:00:00 2001 From: cb535 Date: Tue, 11 Mar 2025 22:10:13 -0400 Subject: [PATCH 096/101] [vtbgen] handle clock insertion source latency --- .../verilog/tbgen/verilog_tbgen_v_template.py | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py b/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py index ae77167b7..11176bd9f 100644 --- a/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py +++ b/pymtl3/passes/backends/verilog/tbgen/verilog_tbgen_v_template.py @@ -1,14 +1,16 @@ template = \ ''' -// VTB_CYCLE_TIME : clock period in ns -// VTB_INPUT_DELAY : how long after rising clk edge should we write inputs -// VTB_OUTPUT_DELAY : how long before rising clk edge should we check outputs -// VTB_OUTPUT_ASSERT_DELAY = VTB_CYCLE_TIME - VTB_OUTPUT_DELAY +// CYCLE_TIME : clock period in ns +// VTB_CLK_INS_SRC_LAT : clock insertion source latency in ns +// VTB_INPUT_DELAY : how long after rising clk edge should we write inputs +// VTB_OUTPUT_DELAY : how long before rising clk edge should we check outputs +// VTB_OUTPUT_ASSERT_DELAY = CYCLE_TIME - VTB_OUTPUT_DELAY // setting VTB_OUTPUT_ASSERT_DELAY takes priority over VTB_OUTPUT_DELAY `define CYCLE_TIME 10 `define VTB_INPUT_DELAY 1 `define VTB_OUTPUT_DELAY 1 +`define VTB_CLK_INS_SRC_LAT 0 `ifndef VTB_OUTPUT_ASSERT_DELAY `define VTB_OUTPUT_ASSERT_DELAY (`CYCLE_TIME-`VTB_OUTPUT_DELAY) @@ -121,18 +123,28 @@ assert(`VTB_OUTPUT_ASSERT_DELAY <= `CYCLE_TIME) else $fatal("\\n=====\\n\\nVTB_OUTPUT_ASSERT_DELAY should be smaller than or equal to CYCLE_TIME\\n\\n=====\\n"); + $display("CYCLE_TIME = %f",`CYCLE_TIME); + $display("VTB_CLK_INS_SRC_LAT = %f",`VTB_CLK_INS_SRC_LAT); + $display("VTB_INPUT_DELAY = %f",`VTB_INPUT_DELAY); + $display("VTB_OUTPUT_ASSERT_DELAY = %f",`VTB_OUTPUT_ASSERT_DELAY); + $display("INTRA_CYCLE_TIME = %f",`INTRA_CYCLE_TIME); + cycle_count = 0; clk = 1'b0; // NEED TO DO THIS TO HAVE FALLING EDGE AT TIME 0 reset = 1'b1; // TODO reset active low/high #((`CYCLE_TIME*1.0)/2); - // Now we are talking + // Delay input data by clock insertion source latency + input delay + + #(-(`VTB_CLK_INS_SRC_LAT)); #`VTB_INPUT_DELAY; + + // Reset sequence + #`CYCLE_TIME; cycle_count = 1; #`CYCLE_TIME; cycle_count = 2; - // 2 cycles plus input delay reset = 1'b0; `ifdef VTB_DUMP_SAIF From 487b6c218be335282f3dbfa71a8778f385e3936d Mon Sep 17 00:00:00 2001 From: cb535 Date: Sun, 4 May 2025 11:11:35 -0400 Subject: [PATCH 097/101] MemorFL: memory write responses now have request length --- pymtl3/stdlib/mem/MemoryFL.py | 8 +++++--- pymtl3/stdlib/mem/test/MemoryFL_test.py | 16 ++++++++-------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/pymtl3/stdlib/mem/MemoryFL.py b/pymtl3/stdlib/mem/MemoryFL.py index a1e2fdb7e..741c1705a 100644 --- a/pymtl3/stdlib/mem/MemoryFL.py +++ b/pymtl3/stdlib/mem/MemoryFL.py @@ -176,9 +176,11 @@ def up_mem(): elif req.type_ == MemMsgType.WRITE: s.mem.write( req.addr, len_, req.data[0:len_<<3] ) - # FIXME do we really set len=0 in response when doing subword wr? - # resp = resp_classes[i]( req.type_, req.opaque, 0, req.len, 0 ) - resp = resp_classes[i]( req.type_, req.opaque, 0, 0, 0 ) + # FIXME do we really set len=0 in response when doing subword + # wr? For now I am switching this back to response with the + # request length. -cbatten + # resp = resp_classes[i]( req.type_, req.opaque, 0, 0, 0 ) + resp = resp_classes[i]( req.type_, req.opaque, 0, req.len, 0 ) elif req.type_ == MemMsgType.AMO_ADD or \ req.type_ == MemMsgType.AMO_AND or \ diff --git a/pymtl3/stdlib/mem/test/MemoryFL_test.py b/pymtl3/stdlib/mem/test/MemoryFL_test.py index e6a6afd81..64479f4eb 100644 --- a/pymtl3/stdlib/mem/test/MemoryFL_test.py +++ b/pymtl3/stdlib/mem/test/MemoryFL_test.py @@ -133,20 +133,20 @@ def subword_rd_msgs( base_addr ): def subword_wr_msgs( base_addr ): return [ - req( 'wr', 0x0, base_addr+0, 1, 0x000000ef ), resp( 'wr', 0x0, 0, 0 ), - req( 'wr', 0x1, base_addr+1, 1, 0x000000be ), resp( 'wr', 0x1, 0, 0 ), - req( 'wr', 0x2, base_addr+2, 1, 0x000000ad ), resp( 'wr', 0x2, 0, 0 ), - req( 'wr', 0x3, base_addr+3, 1, 0x000000de ), resp( 'wr', 0x3, 0, 0 ), + req( 'wr', 0x0, base_addr+0, 1, 0x000000ef ), resp( 'wr', 0x0, 1, 0 ), + req( 'wr', 0x1, base_addr+1, 1, 0x000000be ), resp( 'wr', 0x1, 1, 0 ), + req( 'wr', 0x2, base_addr+2, 1, 0x000000ad ), resp( 'wr', 0x2, 1, 0 ), + req( 'wr', 0x3, base_addr+3, 1, 0x000000de ), resp( 'wr', 0x3, 1, 0 ), req( 'rd', 0x4, base_addr+0, 0, 0 ), resp( 'rd', 0x4, 0, 0xdeadbeef ), - req( 'wr', 0x5, base_addr+0, 2, 0x0000abcd ), resp( 'wr', 0x5, 0, 0 ), - req( 'wr', 0x6, base_addr+2, 2, 0x0000ef01 ), resp( 'wr', 0x6, 0, 0 ), + req( 'wr', 0x5, base_addr+0, 2, 0x0000abcd ), resp( 'wr', 0x5, 2, 0 ), + req( 'wr', 0x6, base_addr+2, 2, 0x0000ef01 ), resp( 'wr', 0x6, 2, 0 ), req( 'rd', 0x7, base_addr+0, 0, 0 ), resp( 'rd', 0x7, 0, 0xef01abcd ), - req( 'wr', 0x8, base_addr+1, 2, 0x00002345 ), resp( 'wr', 0x8, 0, 0 ), + req( 'wr', 0x8, base_addr+1, 2, 0x00002345 ), resp( 'wr', 0x8, 2, 0 ), req( 'rd', 0xa, base_addr+0, 0, 0 ), resp( 'rd', 0xa, 0, 0xef2345cd ), - req( 'wr', 0xb, base_addr+0, 3, 0x00cafe02 ), resp( 'wr', 0xb, 0, 0 ), + req( 'wr', 0xb, base_addr+0, 3, 0x00cafe02 ), resp( 'wr', 0xb, 3, 0 ), req( 'rd', 0xc, base_addr+0, 0, 0 ), resp( 'rd', 0xc, 0, 0xefcafe02 ), ] From 0dcef77379f1302b25fa2ca4390871940fa747b8 Mon Sep 17 00:00:00 2001 From: cb535 Date: Sun, 24 Aug 2025 16:16:20 -0400 Subject: [PATCH 098/101] Output VCD file compliant with the standard PyMTL3 was generating an incorrect VCD format that just happened to work with GTKWave. The old version would output nets as: b0b0 ! b0b0001 $ So every net had an extra b0 at the beginning. In addition, single-bit nets should be represented by either 0 or 1 and then the symbol with no space. This pull request fixes this issue so that PyMTL3 now outputs: 0! b0001 $ --- pymtl3/datatypes/PythonBits.py | 16 ++++++++++++++++ pymtl3/datatypes/test/bits_test.py | 20 ++++++++++++++++++++ pymtl3/passes/tracing/VcdGenerationPass.py | 16 ++++++++-------- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/pymtl3/datatypes/PythonBits.py b/pymtl3/datatypes/PythonBits.py index bf99d39aa..a43abc036 100644 --- a/pymtl3/datatypes/PythonBits.py +++ b/pymtl3/datatypes/PythonBits.py @@ -534,3 +534,19 @@ def oct( self ): def hex( self ): str = "{:x}".format(int(self._uint)).zfill(((self._nbits-1)//4)+1) return "0x"+str + + # Output string suitable for use in VCD file. Single-bit nets are + # encoded as a single 0/1 with no trailing space. Multi-bit nets are + # encoded as b0000 (where 0000 are the values for a four-bit net) and a + # trailing space. This enables the symbol to be concatentated at the + # end of this string and we either have the required space (i.e., for + # multi-bit nets) or we do not have any space (i.e., for single-bit + # nets). -cbatten + + def to_vcd_str( self ): + if self._nbits == 1: + str = f"{int(self._uint):b}" + else: + str = f"b{int(self._uint):0{self._nbits}b} " + return str + diff --git a/pymtl3/datatypes/test/bits_test.py b/pymtl3/datatypes/test/bits_test.py index 246250000..92c908d6b 100644 --- a/pymtl3/datatypes/test/bits_test.py +++ b/pymtl3/datatypes/test/bits_test.py @@ -770,3 +770,23 @@ def test_bin_oct_hex(): def test_len_interface(): a = Bits(4, 0) assert len(a) == 4 + +def test_to_vcd_str(): + + assert Bits(1,0).to_vcd_str() == "0" + assert Bits(1,1).to_vcd_str() == "1" + assert Bits(1,0).to_vcd_str() == "0" + assert Bits(1,1).to_vcd_str() == "1" + + assert Bits(2,0).to_vcd_str() == "b00 " + assert Bits(2,1).to_vcd_str() == "b01 " + assert Bits(2,2).to_vcd_str() == "b10 " + assert Bits(2,3).to_vcd_str() == "b11 " + assert Bits(2,0).to_vcd_str() == "b00 " + assert Bits(2,1).to_vcd_str() == "b01 " + assert Bits(2,2).to_vcd_str() == "b10 " + assert Bits(2,3).to_vcd_str() == "b11 " + + assert Bits(15,35).to_vcd_str() == "b000000000100011 " + assert Bits(15,35).to_vcd_str() == "b000000000100011 " + diff --git a/pymtl3/passes/tracing/VcdGenerationPass.py b/pymtl3/passes/tracing/VcdGenerationPass.py index 557fc26a6..534e9b4ae 100644 --- a/pymtl3/passes/tracing/VcdGenerationPass.py +++ b/pymtl3/passes/tracing/VcdGenerationPass.py @@ -202,9 +202,9 @@ def recurse_models( m, spaces ): for i, net in enumerate(trimmed_value_nets): # Convert everything to Bits to get around lack of bit struct support. # The first cycle VCD contains the default value - bin_str = net[0]._dsl.Type().to_bits().bin() + bin_str = net[0]._dsl.Type().to_bits().to_vcd_str() - print( f"b{bin_str} {net_symbol_mapping[i]}", file=vcd_file ) + print( f"{bin_str}{net_symbol_mapping[i]}", file=vcd_file ) # Set this to be the last cycle value str last_values[i] = bin_str @@ -221,7 +221,7 @@ def recurse_models( m, spaces ): if i != vcd_clock_net_idx ] # Flip clock for the first cycle - print( '\n#0\nb0b1 {}\n'.format( clock_symbol ), file=vcd_file, flush=True ) + print( '\n#0\n1{}\n'.format( clock_symbol ), file=vcd_file, flush=True ) # Returns a dump_vcd function that is ready to be appended to _sched. # TODO: type check? @@ -243,21 +243,21 @@ def dump_vcd_inner( s ): except Exception as e: raise TypeError(f'{e}\n - {signal} becomes another type. Please check your code.') - net_bits_bin_str = net_bits_bin.bin() + net_bits_bin_str = net_bits_bin.to_vcd_str() # `last_value` is the string form of a Bits object in binary - # e.g. '0b000' == Bits3(0).bin() + # e.g. '000' == Bits3(0).to_vcd_str() # We store strings instead of values ... if last_values[i] != net_bits_bin_str: last_values[i] = net_bits_bin_str - print( f'b{net_bits_bin_str} {symbol}', file=vcd_file ) + print( f'{net_bits_bin_str}{symbol}', file=vcd_file ) # Flop clock at the end of cycle next_neg_edge = 100 * vcd_sim_ncycles + 50 - print( f'\n#{next_neg_edge}\nb0b0 {clock_symbol}', file=vcd_file ) + print( f'\n#{next_neg_edge}\n0{clock_symbol}', file=vcd_file ) # Flip clock of the next cycle next_pos_edge = next_neg_edge + 50 - print( f'#{next_pos_edge}\nb0b1 {clock_symbol}\n', file=vcd_file, flush=True ) + print( f'#{next_pos_edge}\n1{clock_symbol}\n', file=vcd_file, flush=True ) vcd_sim_ncycles += 1 def gen_dump_vcd( s ): From 3a00c5e784c6ac436220c2002831ce7bb555ac49 Mon Sep 17 00:00:00 2001 From: cb535 Date: Sun, 24 Aug 2025 16:34:50 -0400 Subject: [PATCH 099/101] update github actions --- .github/workflows/python-package-ci.yml | 48 ++++++++----------------- 1 file changed, 14 insertions(+), 34 deletions(-) diff --git a/.github/workflows/python-package-ci.yml b/.github/workflows/python-package-ci.yml index 7dc6f3102..7e038ac11 100644 --- a/.github/workflows/python-package-ci.yml +++ b/.github/workflows/python-package-ci.yml @@ -1,29 +1,20 @@ -# This workflow will install Python dependencies, run tests and lint with a variety of Python versions -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions -name: Python package +name: Run Tests on: push: - branches: [ master ] + branches: [ master, pymtl4.0-dev ] pull_request: - branches: [ master ] + branches: [ master, pymtl4.0-dev ] jobs: build: - # The latest ubuntu release may exclude Python versions that have reached - # the end of life. An example of this is the upgrade from ubuntu-20.04 to - # ubuntu-22.04: support for Python 3.6.* and 3.7.* has been dropped and - # this broke our Github actions. We should periodically revisit this to - # make sure PyMTL works on the latest ubuntu LTS release. As of now I'm - # targeting ubuntu-20.04 because lots of PyMTL users still work with - # Python 3.6 and 3.7. - # runs-on: ubuntu-latest - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: - python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11", "3.12" ] + python-version: [ "3.9" ] + # python-version: [ "3.8", "3.9", "3.10", "3.11", "3.12" ] steps: - uses: actions/checkout@v4 @@ -34,30 +25,19 @@ jobs: - name: Install Verilator run: | - wget https://github.com/cornell-brg/verilator-travisci-cache/raw/master/verilator-github-actions-5.016.tar.gz - tar -C ${HOME} -xzf verilator-github-actions-5.016.tar.gz - # We need to create a symlink to verilator/share/verilator/include. - # This is because the Verilator binaries are compiled on an EC2 - # instance, and that the executable contains hard-coded paths which can - # only be bypassed by defining $VERILATOR_ROOT. See a similar issue at: - # https://github.com/verilator/verilator/issues/4035 - # But when $VERILATOR_ROOT is present, Verilator assumes a different - # directory hierarchy by looking into $VERILATOR_ROOT/include, which is - # different from verilator/share/verilator/include. Verilator devs have - # mentioned this will be annoying to fix and I don't quite understand - # why; my current workaround is to symlink the correct include - # directory into the place Verilator is looking at. - ln -s ${HOME}/verilator/share/verilator/include ${HOME}/verilator/include - echo "VERILATOR_ROOT=${HOME}/verilator" >> $GITHUB_ENV - echo "PYMTL_VERILATOR_INCLUDE_DIR=${HOME}/verilator/share/verilator/include" >> $GITHUB_ENV + wget --progress=dot:giga https://github.com/cornell-brg/verilator-ci-cache/raw/master/verilator-github-actions-5.032.tar.gz + echo $PWD + whoami + tar -C ${HOME} -xzf verilator-github-actions-5.032.tar.gz echo "${HOME}/verilator/bin" >> $GITHUB_PATH + echo "PKG_CONFIG_PATH=${HOME}/verilator/share/pkgconfig:${PKG_CONFIG_PATH}" >> $GITHUB_ENV - name: Check Verilator run: | - echo ${VERILATOR_ROOT} - ls ${VERILATOR_ROOT}/include - echo ${PYMTL_VERILATOR_INCLUDE_DIR} + which verilator verilator --version + pkg-config --modversion verilator + pkg-config --cflags verilator - name: Install dependencies run: | From 654797d267ae21d4a6cf9c461750eae463428d29 Mon Sep 17 00:00:00 2001 From: cb535 Date: Sun, 24 Aug 2025 16:35:36 -0400 Subject: [PATCH 100/101] point github actions to correct cached build of verilator --- .github/workflows/python-package-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package-ci.yml b/.github/workflows/python-package-ci.yml index 7e038ac11..ec0ba57c3 100644 --- a/.github/workflows/python-package-ci.yml +++ b/.github/workflows/python-package-ci.yml @@ -25,7 +25,7 @@ jobs: - name: Install Verilator run: | - wget --progress=dot:giga https://github.com/cornell-brg/verilator-ci-cache/raw/master/verilator-github-actions-5.032.tar.gz + wget --progress=dot:giga https://github.com/pymtl/verilator-ci-cache/raw/master/verilator-github-actions-5.032.tar.gz echo $PWD whoami tar -C ${HOME} -xzf verilator-github-actions-5.032.tar.gz From 2d92004cb808081e6a1391fca8c9afb471874e9e Mon Sep 17 00:00:00 2001 From: cb535 Date: Sun, 24 Aug 2025 17:25:53 -0400 Subject: [PATCH 101/101] Fix Verilator segfaults due to multi-instantiation We had to do this kind of craziness to avoid Verilator segfault: try: t( 0x00, '?' ) t( 0x13, 0x01 ) t( 0x27, 0x14 ) t( 0x00, 0x28 ) t( 0x00, 0x01 ) t( 0x00, 0x01 ) finally: model.finalize() And we saw segfaults when we tried instantiating the same Verilog module twice and then using PyMTL for the composition. Peitian dug into this and provided a fix. Peitian writes: "This is a change to the C wrapper such that we don't always create a new VerilatedContext everytime we need to create a new instance of the model; instead, this change uses a globally shared VerilatedContext guarded by a reference counter. Based on what I saw I think we were not managing contexts correctly... This specific segfault only seems to happen when two instances of the same model are finalized back to back." --- .../import_/verilator_wrapper_c_template.py | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py index cc7fbbbe8..23f4a9fca 100644 --- a/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py +++ b/pymtl3/passes/backends/verilog/import_/verilator_wrapper_c_template.py @@ -76,6 +76,14 @@ void V{component_name}_assert_on( V{component_name}_t *, bool ); bool V{component_name}_has_assert_fired( V{component_name}_t * ); + // Global VerilatedContext to be shared among all instances of the + // current model. This context object is guarded by a reference + // counter, which is decremented each time there's a call to + // destroy_model(). The context gets released when the counter reaches + // zero. -peitian + VerilatedContext * g_context_ptr = nullptr; + int g_context_ptr_use_cnt = 0; + #if VLINETRACE void V{component_name}_line_trace( V{component_name}_t *, char * ); #endif @@ -92,9 +100,12 @@ V{component_name}_t * m; V{vl_component_name} * model; - VerilatedContext * context_ptr; - context_ptr = new VerilatedContext; + if (g_context_ptr_use_cnt == 0) {{ + g_context_ptr = new VerilatedContext; + }} + g_context_ptr_use_cnt += 1; + VerilatedContext * context_ptr = g_context_ptr; context_ptr->debug(0); context_ptr->randReset( {verilator_xinit_value} ); @@ -167,7 +178,12 @@ #endif delete model; - delete context_ptr; + + g_context_ptr_use_cnt -= 1; + if (g_context_ptr_use_cnt == 0) {{ + delete context_ptr; + }} + delete m; }}