diff --git a/.github/workflows/python-package-ci.yml b/.github/workflows/python-package-ci.yml index ad45b3327..f15786f36 100644 --- a/.github/workflows/python-package-ci.yml +++ b/.github/workflows/python-package-ci.yml @@ -11,7 +11,15 @@ on: jobs: build: - runs-on: ubuntu-latest + # 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 strategy: fail-fast: false matrix: diff --git a/pymtl3/dsl/Connectable.py b/pymtl3/dsl/Connectable.py index c996df266..d730a9697 100644 --- a/pymtl3/dsl/Connectable.py +++ b/pymtl3/dsl/Connectable.py @@ -213,10 +213,31 @@ def __getattr__( s, name ): xd.top_level_signal = sd.top_level_signal xd.elaborate_top = sd.elaborate_top - xd.my_name = name + "".join([ f"[{y}]" for y in indices ]) - xd.full_name = f"{sd.full_name}.{name}" - xd._my_name = name - xd._my_indices = indices + # @bitstruct + # class SomeMsg: + # a: [ Bits8, Bits8 ] + # b: Bits32 + # ... + # class A ( Component ): + # def construct( s ): + # s.struct_port = InPort( SomeMsg ) + # + # Then, the newly created ports representing field a should be + # suffixed with the index, i.e., the name should be + # s.struct_port.a[0], s.struct_port.a[1]. Without the if + # statement below, both ports would have the same name + # s.struct_port.a, which can cause errors in simulation. + if is_bitstruct_class(s._dsl.Type): + xd.my_name = name + "".join([ f"[{y}]" for y in indices ]) + xd.full_name = f"{sd.full_name}.{name}"+"".join([ f"[{y}]" for y in indices ]) + xd._my_name = name + xd._my_indices = indices + + else: + xd.my_name = name + "".join([ f"[{y}]" for y in indices ]) + xd.full_name = f"{sd.full_name}.{name}" + xd._my_name = name + xd._my_indices = indices if parent_is_list: parent.append( x ) diff --git a/pymtl3/passes/sim/test/DynamicSchedulePass_test.py b/pymtl3/passes/sim/test/DynamicSchedulePass_test.py index 048d274e8..3e8641290 100644 --- a/pymtl3/passes/sim/test/DynamicSchedulePass_test.py +++ b/pymtl3/passes/sim/test/DynamicSchedulePass_test.py @@ -21,7 +21,6 @@ def _test_model( cls ): A.apply( GenDAGPass() ) A.apply( DynamicSchedulePass() ) A.apply( PrepareSimPass() ) - A.sim_reset() A.sim_eval_combinational() @@ -259,6 +258,145 @@ def construct( s ): return raise Exception("Should've thrown AssertionError") +def test_struct_with_list_field(): + + @bitstruct + class SomeMsg: + a: [ Bits8, Bits8 ] + b: [ Bits32, Bits32 ] + + class Top( Component ): + def construct( s ): + s.struct = Wire(SomeMsg) + s.a = Wire(32) + s.b = Wire(32) + + @update + def up_out(): + s.struct.a[0] @= 0 + s.struct.a[1] @= 1 + s.struct.b[0] @= 2 + s.struct.b[1] @= 3 + s.b @= s.a + 1 + + @update + def up_abcd(): + s.a @= s.struct.b[1] + 1 + + dut = Top() + dut.elaborate() + dut.apply( GenDAGPass() ) + dut.apply( DynamicSchedulePass() ) + dut.apply( PrepareSimPass(print_line_trace=False) ) + dut.sim_reset() + dut.sim_tick() + assert dut.a == 4 + assert dut.b == 5 + +def test_struct_with_nested_list_field(): + @bitstruct + class SomeMsgA: + a: [ Bits8, Bits8 ] + b: [ Bits32, Bits32 ] + + @bitstruct + class SomeMsgB: + msg_a: [ SomeMsgA, SomeMsgA ] + b: [ Bits32, Bits32 ] + + class Top( Component ): + def construct( s ): + s.struct = Wire(SomeMsgB) + s.a = Wire(32) + s.b = Wire(32) + + @update + def up_out(): + s.struct.msg_a[0].a[0] @= 1 + s.struct.msg_a[0].a[1] @= 2 + s.struct.msg_a[0].b[0] @= 3 + s.struct.msg_a[0].b[1] @= 4 + + s.struct.msg_a[1].a[0] @= 5 + s.struct.msg_a[1].a[1] @= 6 + s.struct.msg_a[1].b[0] @= 7 + s.struct.msg_a[1].b[1] @= 8 + + s.struct.b[0] @= 9 + s.struct.b[1] @= 10 + s.b @= s.a + 1 + + @update + def up_abcd(): + s.a @= s.struct.msg_a[1].b[1] + 1 + + dut = Top() + dut.elaborate() + dut.apply( GenDAGPass() ) + dut.apply( DynamicSchedulePass() ) + dut.apply( PrepareSimPass(print_line_trace=False) ) + dut.sim_reset() + dut.sim_tick() + assert dut.a == 9 + assert dut.b == 10 + +def test_list_struct_port_with_nested_list_field(): + @bitstruct + class SomeMsgA: + a: [ Bits8, Bits8 ] + b: [ Bits32, Bits32 ] + + @bitstruct + class SomeMsgB: + msg_a: [ SomeMsgA, SomeMsgA ] + b: [ Bits32, Bits32 ] + + class Top( Component ): + def construct( s ): + s.struct = [ Wire(SomeMsgB) for _ in range(2) ] + s.a = Wire(32) + s.b = Wire(32) + + @update + def up_out(): + s.struct[0].msg_a[0].a[0] @= 1 + s.struct[0].msg_a[0].a[1] @= 2 + s.struct[0].msg_a[0].b[0] @= 3 + s.struct[0].msg_a[0].b[1] @= 4 + s.struct[0].msg_a[1].a[0] @= 5 + s.struct[0].msg_a[1].a[1] @= 6 + s.struct[0].msg_a[1].b[0] @= 7 + s.struct[0].msg_a[1].b[1] @= 8 + s.struct[0].b[0] @= 9 + s.struct[0].b[1] @= 10 + + s.struct[1].msg_a[0].a[0] @= 11 + s.struct[1].msg_a[0].a[1] @= 12 + s.struct[1].msg_a[0].b[0] @= 13 + s.struct[1].msg_a[0].b[1] @= 14 + s.struct[1].msg_a[1].a[0] @= 15 + s.struct[1].msg_a[1].a[1] @= 16 + s.struct[1].msg_a[1].b[0] @= 17 + s.struct[1].msg_a[1].b[1] @= 18 + s.struct[1].b[0] @= 19 + s.struct[1].b[1] @= 20 + + s.b @= s.a + 1 + + @update + def up_abcd(): + s.a @= s.struct[0].msg_a[1].b[1] + s.struct[1].msg_a[0].b[0] + + dut = Top() + dut.elaborate() + dut.apply( GenDAGPass() ) + dut.apply( DynamicSchedulePass() ) + dut.apply( PrepareSimPass(print_line_trace=False) ) + dut.sim_reset() + dut.sim_tick() + assert dut.a == 21 + assert dut.b == 22 + def test_equal_top_level(): class A(Component): def construct( s ): diff --git a/pymtl3/version.py b/pymtl3/version.py index 72f67f385..1ac6974f9 100644 --- a/pymtl3/version.py +++ b/pymtl3/version.py @@ -1 +1 @@ -__version__ = "3.1.14" +__version__ = "3.1.15"