Skip to content

Commit

Permalink
Merge pull request #249 from pymtl/pp482-bitstruct-packed-array-fix
Browse files Browse the repository at this point in the history
Port the bitstruct packed array bug fix to master branch
  • Loading branch information
ptpan committed Nov 2, 2023
2 parents b35d76e + 4e7bd7b commit 0244456
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 7 deletions.
10 changes: 9 additions & 1 deletion .github/workflows/python-package-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
29 changes: 25 additions & 4 deletions pymtl3/dsl/Connectable.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
Expand Down
140 changes: 139 additions & 1 deletion pymtl3/passes/sim/test/DynamicSchedulePass_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ def _test_model( cls ):
A.apply( GenDAGPass() )
A.apply( DynamicSchedulePass() )
A.apply( PrepareSimPass() )

A.sim_reset()
A.sim_eval_combinational()

Expand Down Expand Up @@ -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 ):
Expand Down
2 changes: 1 addition & 1 deletion pymtl3/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "3.1.14"
__version__ = "3.1.15"

0 comments on commit 0244456

Please sign in to comment.