-
Notifications
You must be signed in to change notification settings - Fork 46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature consideration? #263
Comments
Thanks for these comments! I think many of your proposals are very related to the translation mechanism of PyMTL. Slicing, match statements, and variable naming are feasible with the current implementation of translators. Translation support for loop unrolling would be tricky though, especially if you are referring to loops in those behavioral I think a |
@KelvinChung2000 Excellent suggestions! We are very much interested in growing the base of PyMTL3 developers. If you are interested in maybe taking a stab at one of these and creating a pull request we would be happy to work with you to get it merged in ... I think supporting the extra slicing syntax would be a relatively straight-forward place to start? @ptpan do you agree? If so, and if @KelvinChung2000 you are interested let us know and we can point you to where in the code to maybe take a stab add support for the extra slicing syntax ... |
I am happy to do some development when I have time. |
Hi, I would like to know if there is any current implementation of the |
We don't currently have such an implementation ... would definitely be useful though ... not sure how much work it would be? @jsn1993 any thoughts? |
If you can point me to how to tell if a type is a |
Hi Kevin,
There is an `is_bistruct_inst` function to check if an object is a
bitstruct instance, and an `is_bitstruct_class` function to check if a type
is a bitstruct class. You can import them as the following:
from pymtl3.datatypes.bistructs import is_bitstruct_inst, is_bitstruct_class
Hope this helps!
Best,
Yanghui
…On Tue, Mar 26, 2024 at 11:40 AM Kelvin Chung ***@***.***> wrote:
If you can point me to how to tell if a type is a bitstruct type, I might
be able to do something.
—
Reply to this email directly, view it on GitHub
<#263 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AFFR62PQEIHPUPPVHPTKTUDY2GCE5AVCNFSM6AAAAABALOWFBCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAMRQG43TMNZQHE>
.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
I think this is where bitstructs get flattened? pymtl3/pymtl3/passes/tracing/VcdGenerationPass.py Lines 237 to 244 in 4b3bc18
So we would need to do an explicit check first to see if pymtl3/pymtl3/datatypes/bitstructs.py Lines 78 to 80 in 4b3bc18
To print out if the signal is a bitstruct ... if that works then we would need to recurse into the fields ... but just detecting bitstructs during VCD gen might be a first step? I gave this a shot. First, I a created the following simple test case:
Then after hacking around a bit I ended up adding this to the
As @yo96 mentioned I needed to import
Running my simple example produces this:
So that is at least a starting point? We would need to update where the header is written to the VCD file and also make sure to handle the default values too on cycle 0. I would get everything working with only simple fields (i.e., all fields are Bits, no fields that are lists or BitStructs). @yo96 @ptpan @jsn1993 do you think the approach I sketch above is the right way? Would be awesome to have some more PyMTL hackers though so if you are interested maybe we can work together to add this feature in a PR? |
Looks reasonable to me. Another thing to try is to translate to Verilog and
dump the VCD from Verilator, which supports bitstruct if I remember
correctly.
Best,
Yanghui
…On Tue, Mar 26, 2024 at 2:11 PM Christopher Batten ***@***.***> wrote:
I think this is where bitstructs get flattened?
https://github.com/pymtl/pymtl3/blob/4b3bc183b14ffacd069f48dc5997b00045aea53f/pymtl3/passes/tracing/VcdGenerationPass.py#L237-L244
So we would need to do an explicit check first to see if signal is a
bitstruct. So first thing would be to create a very simple minimal example
and then maybe use this:
https://github.com/pymtl/pymtl3/blob/4b3bc183b14ffacd069f48dc5997b00045aea53f/pymtl3/datatypes/bitstructs.py#L78-L80
To print out if the signal is a bitstruct ... if that works then we would
need to recurse into the fields ... but just detecting bitstructs during
VCD gen might be a first step? I gave this a shot. First, I a created the
following simple test case:
from pymtl3 import *
@bitstruct
class Pair:
a : Bits8
b : Bits8
class BitStructPassThru( Component ):
def construct( s ):
s.in_ = InPort ( Pair )
s.out = OutPort( Pair )
s.in_a = InPort ( Bits8 )
s.in_b = InPort ( Bits8 )
s.out_a = OutPort( Bits8 )
s.out_b = OutPort( Bits8 )
s.out //= s.in_
s.out_a //= s.in_a
s.out_b //= s.in_b
def line_trace( s ):
return f"{s.in_}|{s.in_a}|{s.in_a} () {s.out}|{s.out_a}|{s.out_b}"
top = BitStructPassThru()
top.apply( DefaultPassGroup(linetrace=True,vcdwave="pymtl3_bitstruct_vcd_test") )
top.sim_reset()
for input_value in [ (0x00,0x00), (0x01,0x10), (0xa0,0xb0), (0xab,0xbc) ]:
top.in_ @= Pair(input_value[0],input_value[1])
top.in_a @= input_value[0]
top.in_b @= input_value[1]
top.sim_tick()
top.sim_tick()
top.sim_tick()
top.sim_tick()
Then after hacking around a bit I ended up adding this to the
VcdGenerationPass.py:
...
for i, (signal, symbol) in enumerate( net_details ):
# If we encounter a BitStruct then dump it as a concatenation of
# all fields.
# TODO: treat each field in a BitStruct as a separate signal?
if is_bitstruct_class(signal.get_type()):
print(f"{signal} is of type {signal.get_type()} which is a bitstruct")
for field_name in eval(repr(signal)).__bitstruct_fields__.keys():
full_field_name = f"{repr(signal)}.{field_name}"
print(f" field {field_name} value is {eval(full_field_name)}")
try:
net_bits_bin = eval(repr(signal)).to_bits()
except Exception as e:
raise TypeError(f'{e}\n - {signal} becomes another type. Please check your code.')
...
As @yo96 <https://github.com/yo96> mentioned I needed to import
is_bitstruct_class like this:
from pymtl3.datatypes.bitstructs import is_bitstruct_class
Running my simple example produces this:
% python pymtl3_bitstruct_vcd_test.py
...
s.in_ is of type <class '__main__.Pair'> which is a bitstruct
field a value is 01
field b value is 10
5: a0:b0|a0|a0 () a0:b0|a0|b0
s.in_ is of type <class '__main__.Pair'> which is a bitstruct
field a value is a0
field b value is b0
6: ab:bc|ab|ab () ab:bc|ab|bc
...
So that is at least a starting point? We would need to update where the
header is written to the VCD file and also make sure to handle the default
values too on cycle 0. I would get everything working with only simple
fields (i.e., all fields are Bits, no fields that are lists or BitStructs).
@yo96 <https://github.com/yo96> @ptpan <https://github.com/ptpan> @jsn1993
<https://github.com/jsn1993> do you think the approach I sketch above is
the right way?
Would be awesome to have some more PyMTL hackers though so if you are
interested maybe we can work together to add this feature in a PR?
—
Reply to this email directly, view it on GitHub
<#263 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AFFR62JEA2A6KUSAFY2UZXLY2G24XAVCNFSM6AAAAABALOWFBCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAMRRGE2TIMZUGE>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
I am thinking of doing something similar. I will implement it if I reach a point where I cannot progress without seeing each of the struct fields. For now, I can still get away with putting the fields in a different order. I have considered moving to a more "traditional" flow, or eventually, I will need to, but I don't want to spend time learning new things if I can do everything well enough in a single language. |
Hi Kelvin,
With PyMTL3 you don't have to switch to a "traditional" flow to use
Verilator. You can translate your design to Verilog, import it back into
PyMTL3, and do Python-Verilator co-simulation. This way you can dump the
waveform of your Python simulation from Verilator. The only downside is
that you will lose the python line trace.
Here is a quick way to do it with Chris' example above, only with slight
modifications:
```
from pymtl3 import *
from pymtl3.stdlib.test_utils import run_sim, config_model_with_cmdline_opts
@bitstruct
class Pair:
a : Bits8
b : Bits8
class BitStructPassThru( Component ):
def construct( s ):
s.in_ = InPort ( Pair )
s.out = OutPort( Pair )
s.in_a = InPort ( Bits8 )
s.in_b = InPort ( Bits8 )
s.out_a = OutPort( Bits8 )
s.out_b = OutPort( Bits8 )
s.out //= s.in_
s.out_a //= s.in_a
s.out_b //= s.in_b
def line_trace( s ):
return f"{s.in_}|{s.in_a}|{s.in_a} () {s.out}|{s.out_a}|{s.out_b}"
top = BitStructPassThru()
top = config_model_with_cmdline_opts( top, cmdline_opts={
"test_verilog": "zeros", # xinit value, could be "zeros", "ones", or
"rand"
"dump_vcd": "pymtl3_bitstruct_vcd_test",
}, duts=[] )
top.apply( DefaultPassGroup(linetrace=True) )
top.sim_reset()
for input_value in [ (0x00,0x00), (0x01,0x10), (0xa0,0xb0), (0xab,0xbc) ]:
top.in_ @= Pair(input_value[0],input_value[1])
top.in_a @= input_value[0]
top.in_b @= input_value[1]
top.sim_tick()
top.sim_tick()
top.sim_tick()
top.sim_tick()
```
`config_model_with_cmdline_opts` is meant to be used with a pytest fixture
but here I am directly calling it to apply the passes and set
corresponding metadata. You can also manually set the metadata and apply
the translate-import pass like the following:
```
top.set_metadata( VerilogTranslationImportPass.enable, True )
top.set_metadata( VerilogVerilatorImportPass.vl_xinit, "zeros" )
top.set_metadata( VerilogVerilatorImportPass.vl_trace, True )
top.set_metadata( VerilogVerilatorImportPass.vl_trace_filename,
"pymtl3_bitstruct_vcd_test" )
top.apply( VerilogPlaceholderPass() )
top = VerilogTranslationImportPass()( top )
top.apply( DefaultPassGroup(linetrace=True) )
```
You will see a `pymtl3_bitstruct_vcd_test.verilator1.vcd` generated by
Verilator. This way you remain in the Python land but can get a more
full-featured VCD from Verilator.
Best,
Yanghui
…On Tue, Mar 26, 2024 at 5:11 PM Kelvin Chung ***@***.***> wrote:
I am thinking of doing something similar. I will implement it if I reach a
point where I cannot progress without seeing each of the struct fields. For
now, I can still get away with putting the fields in a different order.
I have considered moving to a more "traditional" flow, or eventually, I
will need to, but I don't want to spend time learning new things if I can
do everything well enough in a single language.
—
Reply to this email directly, view it on GitHub
<#263 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AFFR62NSC5LATPD7SRRLK4LY2HI5RAVCNFSM6AAAAABALOWFBCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAMRRGQ4DCMRXGM>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
I don't know this is available at all. Thanks for the information. |
My design is failing with the following assert
The connection that causes the problem is this |
Can you show what the line is that is causing the assertion? Is it something like this:
Is the |
This is the connection |
Ah ... ok ... that is not a hierarchical reference so ignore what I said above .... looks ok to me ... we would need to see a minimal failing example or at least your code to help debug ... I am assuming |
|
Oh man .. not sure about that code at all ... FYI, at least in my group, we have moved away from en/rdy and back to val/rdy ... I wonder if part of the issue is you are directly connecting the output rdy signal of an interface to the input en signal of that same interface ... that seems a little strange? And we also moved away from this CL stuff when just doing RTL modeling ... we now just use pure RTL everywhere when doing RTL modeling (including pure RTL sources/sinks for testing) .. simplifies things ... we could maybe take a closer look but you would have to push your code to github and provide step-by-step instructions on how to reproduce the error ... the smaller the failing test case the better :) |
In the current mast, under I am connecting it this way because I want to invalidate the output when I receive backpressure from the pipeline. This is the smallest example I can produce
|
I think that is because of a limitation of the translation pass. I remember running into this issue a few years ago as well. A workaround is to change the structural connection to a behavioral update block like the following:
Alternatively, you can create an intermediate signal like this:
Basically it confuses the translation pass when you are directly connecting the ports of the same component outside the component. I can't recall the exact details at the moment, but @ptpan is the expert of the translation pass and can provide a more in-depth explanation. I hope these workarounds help you resolve the issue and get your code up and running smoothly! |
At least internally I have started using the pymtl4.0-dev branch. The whole send/recv thing with en/rdy was a bit of an experiment ... but ultimately I ended up going back to val/rdy for streaming interfaces ... I just tried your example and was able to reproduce the issue ... if you use this:
It goes away and the translation works fine ... I am not quite sure why this fixes things to be honest? ... looks like @yo96 was helping debug at the same time! |
Thanks for the help. |
After exploring and playing with pymtl3, I can feel how this can speed up hardware development. I have some suggestions for features that might enhance the experience of using pymtl3.
Better slicing
Allow the
Bits
slice to work like Python slicing. For example allow,a[start:]
ora[:end]
for accessinga[start:nBits]
ora[0:end]
.Singed bit
Allow
Bits
to be signed or provide a$signed
like Verilog does. This is just something nice to have out of the box instead of me needing to write it when simulating and testing with negative values.Making more function and class static
For example, things like
b1
orb32
are statically available, which allows for static analysis from IDE and hinting. Things added in simulation pass likesim_tick
, letting those also be statically available. Just seeing what function is available by the IDE static analysis saves a lot of time in finding relevant information. Another benefit is you can document each of the individual functions within the code, and the IDE will display them, which results in less documentation needing to be maintained. A good example is Networkx. They have the best open-source documentation I have ever seen.Introducing
match
Starting from Python3.10, the
match
statement is available, and it would be nice if we could use them for constructing hardware similar to how Verilog have acase
statement.Variable naming control
Some constant variables will be named
__const__something
or__tmpvar__something
. It would be nice if passes were available for renaming them.Loop unrolling
Including passes that unroll for loop statements. This might defeat the purpose of generating readable Verilog. Sometimes, reading the statement as it is rolled is more difficult, but easier to construct with a loop.
Once again, thanks for the great work.
The text was updated successfully, but these errors were encountered: