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 f15786f36..b131b7329 100644 --- a/.github/workflows/python-package-ci.yml +++ b/.github/workflows/python-package-ci.yml @@ -34,8 +34,20 @@ 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-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 @@ -43,6 +55,7 @@ jobs: - name: Check Verilator run: | echo ${VERILATOR_ROOT} + ls ${VERILATOR_ROOT}/include echo ${PYMTL_VERILATOR_INCLUDE_DIR} verilator --version @@ -55,8 +68,17 @@ jobs: - name: Test with pytest run: | - mkdir build && cd build - pytest --cov-report xml --cov=pymtl3 .. --tb=short --hypothesis-profile CI + 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-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: | 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__ diff --git a/README.md b/README.md index 5fa10603b..042c02747 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 v5.016 $ ./configure $ make $ sudo make install 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/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/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..14bceb12c 100644 --- a/examples/ex02_cksum/test/ChecksumVRTL_test.py +++ b/examples/ex02_cksum/test/ChecksumVRTL_test.py @@ -8,9 +8,8 @@ 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 import TestSinkCL, TestSrcCL from pymtl3.stdlib.test_utils.test_helpers import finalize_verilator from ..ChecksumFL import checksum @@ -33,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() @@ -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 @@ -100,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/MiscRTL.py b/examples/ex03_proc/MiscRTL.py index caa6b486c..dda930c4e 100644 --- a/examples/ex03_proc/MiscRTL.py +++ b/examples/ex03_proc/MiscRTL.py @@ -8,8 +8,8 @@ Date : June 13, 2019 """ from pymtl3 import * -from pymtl3.stdlib.ifcs import GetIfcRTL, GiveIfcRTL -from pymtl3.stdlib.basic_rtl import RegRst +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 0713788e8..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.basic_rtl import RegEn -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.primitive import RegEn +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/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/ex03_proc/ProcFL.py b/examples/ex03_proc/ProcFL.py index 647bca3b0..dd64fb155 100644 --- a/examples/ex03_proc/ProcFL.py +++ b/examples/ex03_proc/ProcFL.py @@ -4,13 +4,18 @@ ========================================================================== TinyRV0 FL proc. -Author : Shunning Jiang - Date : June 14, 2019 +Author : Shunning Jiang, Peitian Pan + Date : Sep 9, 2022 """ from pymtl3 import * -from pymtl3.stdlib.ifcs import GetIfcFL, SendIfcFL, XcelMasterIfcFL, mk_xcel_msg -from pymtl3.stdlib.mem import MemMasterIfcFL +from pymtl3.extra import clone_deepcopy +from pymtl3.stdlib.stream.ifcs import IStreamIfc, OStreamIfc +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 @@ -19,16 +24,35 @@ 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 = MemMasterIfcFL() - s.dmem = MemMasterIfcFL() - s.xcel = XcelMasterIfcFL() + 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 ) - s.proc2mngr = SendIfcFL() - s.mngr2proc = GetIfcFL() + connect( s.mngr2proc, s.mngr2proc_q.istream ) + connect( s.proc2mngr_q.ostream, s.proc2mngr ) # Internal data structures @@ -46,7 +70,7 @@ def up_ProcFL(): s.commit_inst @= 0 try: - s.raw_inst = s.imem.read( s.PC, 4 ) # line trace + s.raw_inst = s.imem_adapter.read( s.PC, 4 ) # line trace inst = TinyRV0Inst( s.raw_inst ) inst_name = inst.name @@ -81,11 +105,11 @@ def up_ProcFL(): 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.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.read( addr, 4 ) + 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]: @@ -95,9 +119,11 @@ def up_ProcFL(): elif inst_name == "csrw": if inst.csrnum == 0x7C0: - s.proc2mngr( s.R[inst.rs1] ) + if not s.proc2mngr_q.enq.rdy(): + return + s.proc2mngr_q.enq( s.R[inst.rs1] ) elif 0x7E0 <= inst.csrnum <= 0x7FF: - s.xcel.write( inst.csrnum[0:5], s.R[inst.rs1] ) + s.xcel_adapter.write( inst.csrnum[0:5], s.R[inst.rs1] ) else: raise TinyRV2Semantics.IllegalInstruction( "Unrecognized CSR register ({}) for csrw at PC={}" \ @@ -106,9 +132,11 @@ def up_ProcFL(): elif inst_name == "csrr": if inst.csrnum == 0xFC0: - s.R[inst.rd] = s.mngr2proc() + 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.read( inst.csrnum[0:5] ) + s.R[inst.rd] = s.xcel_adapter.read( inst.csrnum[0:5] ) else: raise TinyRV2Semantics.IllegalInstruction( "Unrecognized CSR register ({}) for csrr at PC={}" \ 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..6badb8455 --- 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,23 +110,23 @@ 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 ) # 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(print_line_trace=opts.trace) ) + model.apply( DefaultPassGroup(linetrace=opts.trace) ) # Load the program into the model 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/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 index 4b7bd84e7..135782b72 100644 --- a/examples/ex03_proc/test/ProcFL_test.py +++ b/examples/ex03_proc/test/ProcFL_test.py @@ -1,8 +1,8 @@ """ ========================================================================= -ProcFL_test.py +ProcRTL_test.py ========================================================================= -Includes test cases for the functional level TinyRV0 processor. +Includes test cases for the RTL TinyRV0 processor. Author : Shunning Jiang, Yanghui Ou Date : June 12, 2019 @@ -31,13 +31,9 @@ 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: diff --git a/examples/ex03_proc/test/ProcRTL_test.py b/examples/ex03_proc/test/ProcRTL_test.py index 5c7afd5f9..d53993394 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,19 @@ #------------------------------------------------------------------------- # ProcRTL_Tests #------------------------------------------------------------------------- -# It is as simple as inheriting from CL tests and change the ProcType to -# ProcRTL. +# 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 .ProcCL_test import ProcCL_Tests as BaseTests +from .ProcFL_test import ProcFL_Tests as BaseTests +@pytest.mark.usefixtures("cmdline_opts") 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 + # 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 diff --git a/examples/ex03_proc/test/ProcVRTL_test.py b/examples/ex03_proc/test/ProcVRTL_test.py index ab1362586..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 @@ -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"] @@ -49,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) ) @@ -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/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 d9df596ba..50cf0e478 100644 --- a/examples/ex04_xcel/ChecksumXcelRTL.py +++ b/examples/ex04_xcel/ChecksumXcelRTL.py @@ -9,9 +9,10 @@ """ 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.basic_rtl import Reg +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 # TODO: add more comments. @@ -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..0bf50b8aa --- 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] @@ -214,12 +207,12 @@ 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(print_line_trace=opts.trace) ) + model.apply( DefaultPassGroup(linetrace=opts.trace) ) # Load the program into the model 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/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..aded7a31e 100644 --- a/examples/ex04_xcel/test/ChecksumXcelVRTL_test.py +++ b/examples/ex04_xcel/test/ChecksumXcelVRTL_test.py @@ -10,17 +10,17 @@ 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 -from .ChecksumXcelCL_test import mk_xcel_transaction +from .ChecksumXcelRTL_test import mk_xcel_transaction #------------------------------------------------------------------------- # 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() @@ -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 @@ -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() ) diff --git a/pymtl3/dsl/ComponentLevel2.py b/pymtl3/dsl/ComponentLevel2.py index ad1c6604b..caa2bfce5 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 `construct`. + 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/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/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 ): 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.") 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/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/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/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_/VerilogVerilatorImportConfigs.py b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportConfigs.py index b5dbe6751..5d30baedf 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 @@ -129,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" : "", } @@ -268,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 "" @@ -277,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, ] @@ -317,13 +319,15 @@ 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" -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() c_src_files = " ".join(s._get_c_src_files()) @@ -332,6 +336,7 @@ def create_cc_cmd( s ): coverage = "-DVM_COVERAGE" if s.vl_coverage or \ s.vl_line_coverage or \ s.vl_toggle_coverage else "" + return f"g++ {c_flags} {c_include_path} {ld_flags}"\ f" -o {out_file} {c_src_files} {ld_libs} {coverage}" @@ -380,24 +385,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 +423,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 +440,24 @@ 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 + objs.append(file_path) + + return objs diff --git a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py index 4c5272e5f..0bda9efbc 100644 --- a/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py +++ b/pymtl3/passes/backends/verilog/import_/VerilogVerilatorImportPass.py @@ -9,11 +9,13 @@ import importlib import json import linecache +import multiprocessing import os import shutil import subprocess import sys import timeit +from fasteners import InterProcessLock from functools import reduce from importlib import reload from itertools import cycle @@ -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 @@ -368,13 +371,35 @@ def get_imported_object( s, m ): cached, config_file, cfg_d = s.is_cached( m, ip_cfg ) - s.create_verilator_model( m, ph_cfg, ip_cfg, cached ) + if not cached: + 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 ) - port_cdefs = s.create_verilator_c_wrapper( m, ph_cfg, ip_cfg, ports, cached ) + if not cached: + # Dump configuration dict to config_file + with open( config_file, 'w' ) as fd: + json.dump( cfg_d, fd, indent = 4 ) - s.create_shared_lib( m, ph_cfg, ip_cfg, cached ) + # 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 ) + + lock.release() + + else: + ip_cfg.vprint(f"{ip_cfg.translated_top_module} is cached!", 2) - symbols = s.create_py_wrapper( m, ph_cfg, ip_cfg, rtype, ports, port_cdefs, cached ) + # 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 ) @@ -382,63 +407,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 ) - - succeeds = 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) + # Generate verilator command + cmd = ip_cfg.create_vl_cmd() - else: - ip_cfg.vprint(f"{ip_cfg.translated_top_module} not verilated because it's cached!", 2) + # 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 ) + + succeeds = 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) #----------------------------------------------------------------------- # 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 +499,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 +510,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 - - # 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) + cmd = ip_cfg.create_cc_cmd() - else: - ip_cfg.vprint("Didn't compile shared library because it's cached!", 2) + succeeds = True + + # 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() @@ -567,33 +581,34 @@ def create_py_wrapper( s, m, ph_cfg, ip_cfg, rtype, ports, port_cdefs, cached ): # 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 = '' # 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 @@ -684,18 +699,11 @@ def gen_signal_decl_c( s, name, port ): c_dim = s._get_c_dim( port ) nbits = s._get_c_nbits( port ) - 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' - 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};' diff --git a/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py b/pymtl3/passes/backends/verilog/import_/test/ImportedObject_test.py index e967d33ee..3160fa5f4 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,10 +72,12 @@ 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", } ) s.set_metadata( VerilogVerilatorImportPass.vl_xinit, 'ones' ) + s.set_metadata( VerilogVerilatorImportPass.vl_Wno_list, ['LATCH']) a = VUninit() a._tvs = [ [ 0, 4294967295 ], @@ -97,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", } ) @@ -131,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 ], @@ -155,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 ) @@ -164,8 +169,10 @@ 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( VerilogPlaceholderPass.top_module, 'VQueue' ) num_entries = 1 - q = VQueue( + q = VQueueWithPorts( data_width = 32, num_entries = num_entries, count_width = clog2(num_entries+1)) @@ -263,6 +270,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, @@ -296,6 +304,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, @@ -327,6 +336,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, @@ -352,6 +362,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, @@ -399,6 +410,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", } ) @@ -432,6 +444,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", } ) @@ -463,6 +476,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", } ) @@ -493,6 +507,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 ) @@ -509,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 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 2fd4f605c..19fb202c9 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 @@ -45,85 +45,92 @@ // Exposed port interface {port_defs} + // The following variables have a _cffi_ prefix to avoid name conflicts + // with the port names. + // Verilator model - void * model; + 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( 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_on( V{component_name}_t *, bool ); + bool V{component_name}_has_assert_fired( V{component_name}_t * ); #if VLINETRACE - void trace( V{component_name}_t *, char * ); + void V{component_name}_line_trace( V{component_name}_t *, char * ); #endif }} -//------------------------------------------------------------------------ -// 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; - -double sc_time_stamp() -{{ - - return g_main_time; - -}} - //------------------------------------------------------------------------ // 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; + VerilatedContext * context_ptr; - V{component_name}_t * m; - V{component_name} * model; + 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{component_name}(); + // 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->model = (void *) model; + m = new V{component_name}_t; + model = new V{vl_component_name}(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; - Verilated::traceEverOn( true ); + m->_cffi_vcd_en = 1; + context_ptr->traceEverOn( true ); VerilatedVcdC * tfp = new VerilatedVcdC(); model->trace( tfp, 99 ); 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 @@ -138,26 +145,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{component_name} * model = (V{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 context_ptr; + delete m; }} @@ -166,9 +177,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{component_name} * model = (V{component_name} *) m->model; + V{vl_component_name} * model = (V{vl_component_name} *) m->_cffi_model; // evaluate one time step model->eval(); @@ -176,10 +187,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 @@ -191,9 +202,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{component_name} * model = (V{component_name} *) m->model; + V{vl_component_name} * model = (V{vl_component_name} *) m->_cffi_model; + VerilatedContext * context_ptr = (VerilatedContext *) m->_cffi_context_ptr; // evaluate one time cycle @@ -204,15 +216,33 @@ 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) ) {{ - // 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 ); + 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->_cffi_trace_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->_cffi_trace_time ); tfp->flush(); }} @@ -223,31 +253,47 @@ #endif model->eval(); + 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}; - g_main_time += {half_cycle_time}; - - // dump current signal values - VerilatedVcdC * tfp = (VerilatedVcdC *) m->tfp; - tfp->dump( m->trace_time ); - tfp->flush(); + m->_cffi_trace_time += {half_cycle_time}; }} #endif }} //------------------------------------------------------------------------ -// assert_en() +// assert_on() //------------------------------------------------------------------------ -// Enable or disable assertions controlled by --assert +// 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() +//------------------------------------------------------------------------ +// Return true if an assertion has fired in the current context. + +bool V{component_name}_has_assert_fired( V{component_name}_t * m ) {{ -void assert_en( bool en ) {{ + VerilatedContext * context_ptr = (VerilatedContext *) m->_cffi_context_ptr; - Verilated::assertOn(en); + return context_ptr->gotError() && context_ptr->gotFinish(); }} @@ -260,9 +306,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{component_name} * model = (V{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; @@ -293,9 +339,18 @@ // 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 ); - svSetScope( &model->__VlSymsp->__Vscope_{vl_component_name}__v ); - model->line_trace( words ); + // 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 ); + + 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 @@ -327,4 +382,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 e71281f61..8ed4d31c5 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 @@ -35,27 +38,46 @@ 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_on( V{component_name}_t *, bool ); + bool V{component_name}_has_assert_fired( V{component_name}_t * ); {trace_c_def} """) # 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 # 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 @@ -77,21 +99,27 @@ 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 - s._ffi_inst.destroy_model( s._ffi_m ) - s.ffi.dlclose( s._ffi_inst ) - s.ffi = None - s._ffi_inst = None + + # Clean up python side FFI references + s._convert_string = None + + s._ffi_inst.V{component_name}_destroy_model( s._ffi_m ) + # 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 - s._ffi_inst.destroy_model( s._ffi_m ) - s.ffi.dlclose( s._ffi_inst ) - s.ffi = None - s._ffi_inst = None + + # Clean up python side FFI references + s._convert_string = None + + s._ffi_inst.V{component_name}_destroy_model( s._ffi_m ) + # print("End of __del__") def construct( s, *args, **kwargs ): # Set up the VCD file name @@ -106,16 +134,19 @@ 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.V{component_name}_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 # 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} @@ -140,22 +171,17 @@ 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.assert_en( 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}: - 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} diff --git a/pymtl3/passes/backends/verilog/tbgen/test/VerilogTBGenPass_test.py b/pymtl3/passes/backends/verilog/tbgen/test/VerilogTBGenPass_test.py index 3b547ecb4..ffab1548d 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 @@ -38,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 ) @@ -47,9 +48,11 @@ 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( 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_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/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/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/passes/backends/verilog/test/TranslationImport_xdist_test.py b/pymtl3/passes/backends/verilog/test/TranslationImport_xdist_test.py new file mode 100644 index 000000000..4d34821c7 --- /dev/null +++ b/pymtl3/passes/backends/verilog/test/TranslationImport_xdist_test.py @@ -0,0 +1,41 @@ +#========================================================================= +# TranslationImport_xdist_test.py +#========================================================================= +# Author : Peitian Pan +# Date : Oct 11th, 2023 +"""Test if Verilated models can be simulated in parallel.""" + +import os, pytest, multiprocessing + +from pymtl3.datatypes import Bits32 +from pymtl3.passes import DefaultPassGroup +from pymtl3.passes.backends.verilog import VerilogPlaceholderPass, VerilogTranslationImportPass + +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(MAX_XDIST_TEST_INSTS)) +) +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/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/verilog/translation/VerilogTranslationPass.py b/pymtl3/passes/backends/verilog/translation/VerilogTranslationPass.py index 46c8bd7c6..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 +import os, tempfile from pymtl3 import MetadataKey from pymtl3.passes.BasePass import BasePass @@ -144,61 +144,42 @@ def traverse_hierarchy( s, m ): filename = fname.split('.sv')[0] else: filename = fname + else: + filename = f"{module_name}__pickled" + + output_file = filename + '.v' - output_file = filename + '.v' - temporary_file = filename + '.v.tmp' + # Create a temporary file under the current directory. + is_same = False + tmp_fd, tmp_path = tempfile.mkstemp(dir=os.curdir, text=True) - # 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() + 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 ) # `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 + # `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 ) + is_same = verilog_cmp( tmp_file, output_file ) - # Rename the temporary file to the output file - os.rename( temporary_file, output_file ) + 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 + # 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" - - 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 ) + # 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/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/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/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() 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/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 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/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 ) 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/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/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): diff --git a/pymtl3/passes/rtlir/behavioral/BehavioralRTLIRGenL1Pass.py b/pymtl3/passes/rtlir/behavioral/BehavioralRTLIRGenL1Pass.py index e0b8af481..9fdfe8dcc 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() @@ -501,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' ) @@ -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 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 ): 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/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 ): diff --git a/pymtl3/passes/testcases/test_cases.py b/pymtl3/passes/testcases/test_cases.py index 1d8fb492f..8c6351d62 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,446 @@ 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 ] + a = None + a[ 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 +3440,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 +3480,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 +3501,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 +3582,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 +3593,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 +3604,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 +3615,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 +3626,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 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/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/queues/queues.py b/pymtl3/stdlib/dstruct/queues.py similarity index 63% rename from pymtl3/stdlib/queues/queues.py rename to pymtl3/stdlib/dstruct/queues.py index fd4ccc63c..08f98b356 100644 --- a/pymtl3/stdlib/queues/queues.py +++ b/pymtl3/stdlib/dstruct/queues.py @@ -1,30 +1,35 @@ """ ------------------------------------------------------------------------- -Library of RTL queues +Queues ------------------------------------------------------------------------- +Common queue data structures. -Author : Yanghui Ou - Date : Mar 23, 2019 +Author : Yanghui Ou, Peitian Pan + Date : Aug 26, 2022 """ 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 +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 NormalQueueRTL +# Dpath and Ctrl for NormalQueue #------------------------------------------------------------------------- -class NormalQueueDpathRTL( Component ): +class NormalQueueDpath( Component ): def construct( s, EntryType, num_entries=2 ): # Interface s.enq_msg = InPort( EntryType ) - s.deq_ret = OutPort( EntryType ) + s.deq_msg = OutPort( EntryType ) s.wen = InPort() s.waddr = InPort( clog2( num_entries ) ) @@ -34,12 +39,12 @@ def construct( s, EntryType, num_entries=2 ): s.queue = m = RegisterFile( EntryType, num_entries ) m.raddr[0] //= s.raddr - m.rdata[0] //= s.deq_ret + m.rdata[0] //= s.deq_msg m.wen[0] //= s.wen m.waddr[0] //= s.waddr m.wdata[0] //= s.enq_msg -class NormalQueueCtrlRTL( Component ): +class NormalQueueCtrl( Component ): def construct( s, num_entries=2 ): @@ -107,31 +112,39 @@ def up_reg(): s.count <<= s.count - CountType(1) #------------------------------------------------------------------------- -# NormalQueueRTL +# NormalQueue #------------------------------------------------------------------------- -class NormalQueueRTL( Component ): +class NormalQueue( Component ): def construct( s, EntryType, num_entries=2 ): # Interface - s.enq = EnqIfcRTL( EntryType ) - s.deq = DeqIfcRTL( EntryType ) + 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 = NormalQueue1EntryRTL( EntryType ) - connect( s.enq, s.q.enq ) - connect( s.deq, s.q.deq ) + 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 = NormalQueueCtrlRTL ( num_entries ) - s.dpath = NormalQueueDpathRTL( EntryType, num_entries ) + s.ctrl = NormalQueueCtrl ( num_entries ) + s.dpath = NormalQueueDpath( EntryType, num_entries ) # Connect ctrl to data path @@ -141,24 +154,26 @@ def construct( s, EntryType, num_entries=2 ): # 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.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 ) + connect( s.enq_msg, s.dpath.enq_msg ) + connect( s.deq_msg, s.dpath.deq_msg ) # Line trace def line_trace( s ): - return f"{s.enq}({s.count}){s.deq}" + 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 PipeQueueCtrlRTL( Component ): +class PipeQueueCtrl( Component ): def construct( s, num_entries=2 ): @@ -226,31 +241,39 @@ def up_reg(): s.count <<= s.count - CountType(1) #------------------------------------------------------------------------- -# PipeQueueRTL +# PipeQueue #------------------------------------------------------------------------- -class PipeQueueRTL( Component ): +class PipeQueue( Component ): def construct( s, EntryType, num_entries=2 ): # Interface - s.enq = EnqIfcRTL( EntryType ) - s.deq = DeqIfcRTL( EntryType ) + 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 = PipeQueue1EntryRTL( EntryType ) - connect( s.enq, s.q.enq ) - connect( s.deq, s.q.deq ) + 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 = PipeQueueCtrlRTL ( num_entries ) - s.dpath = NormalQueueDpathRTL( EntryType, num_entries ) + s.ctrl = PipeQueueCtrl ( num_entries ) + s.dpath = NormalQueueDpath( EntryType, num_entries ) # Connect ctrl to data path @@ -260,31 +283,33 @@ def construct( s, EntryType, num_entries=2 ): # 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.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 ) + connect( s.enq_msg, s.dpath.enq_msg ) + connect( s.deq_msg, s.dpath.deq_msg ) # Line trace def line_trace( s ): - return "{}({}){}".format( s.enq, s.count, s.deq ) + 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 BypassQueueDpathRTL( Component ): +class BypassQueueDpath( Component ): def construct( s, EntryType, num_entries=2 ): # Interface s.enq_msg = InPort( EntryType ) - s.deq_ret = OutPort( EntryType ) + s.deq_msg = OutPort( EntryType ) s.wen = InPort( Bits1 ) s.waddr = InPort( mk_bits( clog2( num_entries ) ) ) @@ -303,9 +328,9 @@ def construct( s, EntryType, num_entries=2 ): m.sel //= s.mux_sel m.in_[0] //= s.queue.rdata[0] m.in_[1] //= s.enq_msg - m.out //= s.deq_ret + m.out //= s.deq_msg -class BypassQueueCtrlRTL( Component ): +class BypassQueueCtrl( Component ): def construct( s, num_entries=2 ): @@ -376,31 +401,39 @@ def up_reg(): s.count <<= s.count - CountType(1) #------------------------------------------------------------------------- -# BypassQueueRTL +# BypassQueue #------------------------------------------------------------------------- -class BypassQueueRTL( Component ): +class BypassQueue( Component ): def construct( s, EntryType, num_entries=2 ): # Interface - s.enq = EnqIfcRTL( EntryType ) - s.deq = DeqIfcRTL( EntryType ) + 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 = BypassQueue1EntryRTL( EntryType ) - connect( s.enq, s.q.enq ) - connect( s.deq, s.q.deq ) + 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 = BypassQueueCtrlRTL ( num_entries ) - s.dpath = BypassQueueDpathRTL( EntryType, num_entries ) + s.ctrl = BypassQueueCtrl ( num_entries ) + s.dpath = BypassQueueDpath( EntryType, num_entries ) # Connect ctrl to data path @@ -411,31 +444,37 @@ def construct( s, EntryType, num_entries=2 ): # 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.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 ) + connect( s.enq_msg, s.dpath.enq_msg ) + connect( s.deq_msg, s.dpath.deq_msg ) # Line trace def line_trace( s ): - return f"{s.enq}({s.count}){s.deq}" + 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}" #------------------------------------------------------------------------- -# NormalQueue1EntryRTL +# NormalQueue1Entry #------------------------------------------------------------------------- -class NormalQueue1EntryRTL( Component ): +class NormalQueue1Entry( Component ): def construct( s, EntryType ): # Interface - s.enq = EnqIfcRTL( EntryType ) - s.deq = DeqIfcRTL( EntryType ) + 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 @@ -447,32 +486,38 @@ def construct( s, EntryType ): s.count //= s.full - s.deq.ret //= s.entry + s.deq_msg //= s.entry - s.enq.rdy //= lambda: ~s.reset & ~s.full - s.deq.rdy //= lambda: ~s.reset & s.full + 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 + 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}" + 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}" #------------------------------------------------------------------------- -# PipeQueue1EntryRTL +# PipeQueue1Entry #------------------------------------------------------------------------- -class PipeQueue1EntryRTL( Component ): +class PipeQueue1Entry( Component ): def construct( s, EntryType ): # Interface - s.enq = EnqIfcRTL( EntryType ) - s.deq = DeqIfcRTL( EntryType ) + 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 @@ -484,33 +529,39 @@ def construct( s, EntryType ): s.count //= s.full - s.deq.ret //= s.entry + s.deq_msg //= s.entry - s.enq.rdy //= lambda: ~s.reset & ( ~s.full | s.deq.en ) - s.deq.rdy //= lambda: s.full & ~s.reset + 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 ) + s.full <<= ~s.reset & ( s.enq_en | s.full & ~s.deq_en ) - if s.enq.en: - s.entry <<= s.enq.msg + if s.enq_en: + s.entry <<= s.enq_msg def line_trace( s ): - return f"{s.enq}({s.full}){s.deq}" + 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}" #------------------------------------------------------------------------- -# BypassQueue1EntryRTL +# BypassQueue1Entry #------------------------------------------------------------------------- -class BypassQueue1EntryRTL( Component ): +class BypassQueue1Entry( Component ): def construct( s, EntryType ): # Interface - s.enq = EnqIfcRTL( EntryType ) - s.deq = DeqIfcRTL( EntryType ) + 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 @@ -519,24 +570,26 @@ def construct( s, EntryType ): s.full = Wire( Bits1 ) s.bypass_mux = m = Mux( EntryType, 2 ) - m.in_[0] //= s.enq.msg + m.in_[0] //= s.enq_msg m.in_[1] //= s.entry - m.out //= s.deq.ret + 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 ) + 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) ) + s.full <<= ~s.reset & ( ~s.deq_en & (s.enq_en | s.full) ) - if s.enq.en & ~s.deq.en: - s.entry <<= s.enq.msg + if s.enq_en & ~s.deq_en: + s.entry <<= s.enq_msg def line_trace( s ): - return f"{s.enq}({s.full}){s.deq}" + 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/basic_rtl/test/__init__.py b/pymtl3/stdlib/dstruct/test/__init__.py similarity index 100% rename from pymtl3/stdlib/basic_rtl/test/__init__.py rename to pymtl3/stdlib/dstruct/test/__init__.py diff --git a/pymtl3/stdlib/queues/test/queues_test.py b/pymtl3/stdlib/dstruct/test/queues_test.py similarity index 68% rename from pymtl3/stdlib/queues/test/queues_test.py rename to pymtl3/stdlib/dstruct/test/queues_test.py index 74302f270..6785b5de9 100644 --- a/pymtl3/stdlib/queues/test/queues_test.py +++ b/pymtl3/stdlib/dstruct/test/queues_test.py @@ -11,15 +11,16 @@ import pytest from pymtl3 import * -from pymtl3.stdlib.test_utils import TestSinkCL, TestSrcCL, TestVectorSimulator, run_sim +from pymtl3.stdlib.stream import StreamSinkFL, StreamSourceFL +from pymtl3.stdlib.test_utils import TestVectorSimulator, run_sim from ..queues import ( - BypassQueue1EntryRTL, - BypassQueueRTL, - NormalQueue1EntryRTL, - NormalQueueRTL, - PipeQueue1EntryRTL, - PipeQueueRTL, + BypassQueue1Entry, + BypassQueue, + NormalQueue1Entry, + NormalQueue, + PipeQueue1Entry, + PipeQueue, ) #------------------------------------------------------------------------- @@ -31,14 +32,14 @@ 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] + 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] + 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 @@ -49,8 +50,8 @@ 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 + 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) ], @@ -71,20 +72,22 @@ class TestHarness( Component ): def construct( s, MsgType, QType, src_msgs, sink_msgs ): - s.src = TestSrcCL ( MsgType, src_msgs ) + s.src = StreamSourceFL ( MsgType, src_msgs ) s.dut = QType( MsgType ) + s.sink = StreamSinkFL( MsgType, sink_msgs ) - s.sink = TestSinkCL( 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 ) - connect( s.src.send, s.dut.enq ) + @update + def upblk_enq(): + s.dut.enq_en @= s.dut.enq_rdy & s.src.ostream.val - @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 ) + @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() @@ -99,62 +102,62 @@ 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(): - th = TestHarness( Bits16, NormalQueueRTL, test_msgs, test_msgs ) + 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, NormalQueueRTL, test_msgs, test_msgs ) + 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, PipeQueueRTL, test_msgs, test_msgs ) + 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, PipeQueueRTL, test_msgs, test_msgs ) + 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, PipeQueueRTL, test_msgs, test_msgs ) + 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, BypassQueueRTL, test_msgs, test_msgs ) + 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, BypassQueueRTL, test_msgs, test_msgs ) + 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, BypassQueueRTL, test_msgs, test_msgs ) + 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( [ NormalQueueRTL, PipeQueueRTL, BypassQueueRTL ], [ 8, 10, 12, 16 ] ) + product( [ NormalQueue, PipeQueue, BypassQueue ], [ 8, 10, 12, 16 ] ) ) def test_large_backpressure( QType, num_entries ): msgs = test_msgs * 8 @@ -164,14 +167,14 @@ def test_large_backpressure( QType, num_entries ): run_sim( th ) @pytest.mark.parametrize( - 'QType', [ NormalQueue1EntryRTL, PipeQueue1EntryRTL, BypassQueue1EntryRTL ] + 'QType', [ NormalQueue1Entry, PipeQueue1Entry, BypassQueue1Entry ] ) def test_single_simple( QType ): th = TestHarness( Bits16, QType, test_msgs, test_msgs ) run_sim( th ) @pytest.mark.parametrize( - 'QType', [ NormalQueue1EntryRTL, PipeQueue1EntryRTL, BypassQueue1EntryRTL ] + 'QType', [ NormalQueue1Entry, PipeQueue1Entry, BypassQueue1Entry ] ) def test_single_backpressure( QType ): th = TestHarness( Bits16, QType, test_msgs, test_msgs ) 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/xcel_ifcs_test.py b/pymtl3/stdlib/ifcs/test/xcel_ifcs_test.py deleted file mode 100644 index 4ea16ab0d..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.basic_rtl 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/MagicMemoryFL.py b/pymtl3/stdlib/mem/BehavioralMemory.py similarity index 62% rename from pymtl3/stdlib/mem/MagicMemoryFL.py rename to pymtl3/stdlib/mem/BehavioralMemory.py index 34e1b1142..6d2e2a7f1 100644 --- a/pymtl3/stdlib/mem/MagicMemoryFL.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, @@ -18,23 +17,27 @@ 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(): 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 ) @@ -54,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, 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 def line_trace( s ): 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/MemMsg.py b/pymtl3/stdlib/mem/MemMsg.py index 93454f60b..48eadb11f 100644 --- a/pymtl3/stdlib/mem/MemMsg.py +++ b/pymtl3/stdlib/mem/MemMsg.py @@ -55,6 +55,7 @@ def mk_mem_req_msg( o, a, d ): @bitstruct class MemReqMsg: + type_ : Bits4 opaque : mk_bits( o ) addr : mk_bits( a ) @@ -78,6 +79,7 @@ def mk_mem_resp_msg( o, d ): @bitstruct class MemRespMsg: + type_ : Bits4 opaque : mk_bits( o ) test : Bits2 diff --git a/pymtl3/stdlib/mem/MemRequesterAdapterFL.py b/pymtl3/stdlib/mem/MemRequesterAdapterFL.py new file mode 100644 index 000000000..7d3ad9955 --- /dev/null +++ b/pymtl3/stdlib/mem/MemRequesterAdapterFL.py @@ -0,0 +1,103 @@ +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 + +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/magic_memory.py b/pymtl3/stdlib/mem/MemoryFL.py similarity index 76% rename from pymtl3/stdlib/stream/magic_memory.py rename to pymtl3/stdlib/mem/MemoryFL.py index 9fc9ef7f8..a1e2fdb7e 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,12 @@ 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.stream.ifcs import IStreamIfc, OStreamIfc + +from .ifcs.ifcs import MemResponderIfc # BRGTC2 custom MemMsg modified for RISC-V 32 @@ -51,10 +51,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 +67,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 +80,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 +92,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 @@ -137,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 = [ MinionIfcRTL( 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 @@ -150,23 +150,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].reqstream + # s.req_stalls[i].ostream //= s.req_qs[i].istream + s.resp_qs[i].ostream //= s.ifc[i].respstream - 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 @@ -192,11 +192,19 @@ 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 - s.resp_qs[i].recv.msg @= resp + s.resp_qs[i].istream.msg @= resp #----------------------------------------------------------------------- # line_trace @@ -204,5 +212,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..c9c97f57f 100644 --- a/pymtl3/stdlib/mem/__init__.py +++ b/pymtl3/stdlib/mem/__init__.py @@ -1,12 +1,4 @@ -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 +from .MemRequesterAdapterFL import MemRequesterAdapterFL 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/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/delays/test/__init__.py b/pymtl3/stdlib/primitive/test/__init__.py similarity index 100% rename from pymtl3/stdlib/delays/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/__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 086296021..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.basic_rtl 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/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/valrdy_queues.py b/pymtl3/stdlib/queues/valrdy_queues.py deleted file mode 100644 index 2eb97f0a5..000000000 --- a/pymtl3/stdlib/queues/valrdy_queues.py +++ /dev/null @@ -1,294 +0,0 @@ -from pymtl3 import * -from pymtl3.stdlib.basic_rtl 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/ifcs/test/__init__.py b/pymtl3/stdlib/reqresp/__init__.py similarity index 100% rename from pymtl3/stdlib/ifcs/test/__init__.py rename to pymtl3/stdlib/reqresp/__init__.py 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/IStreamBlockingAdapterFL.py b/pymtl3/stdlib/stream/IStreamBlockingAdapterFL.py new file mode 100644 index 000000000..f565c58ae --- /dev/null +++ b/pymtl3/stdlib/stream/IStreamBlockingAdapterFL.py @@ -0,0 +1,33 @@ +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 ) ) + + def line_trace( s ): + return f"{s.istream}({s.entry})" diff --git a/pymtl3/stdlib/stream/IStreamDeqAdapterFL.py b/pymtl3/stdlib/stream/IStreamDeqAdapterFL.py new file mode 100644 index 000000000..2549e7956 --- /dev/null +++ b/pymtl3/stdlib/stream/IStreamDeqAdapterFL.py @@ -0,0 +1,31 @@ +from pymtl3 import * +from pymtl3.extra import clone_deepcopy +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 ): + 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/IStreamNonBlockingAdapterFL.py b/pymtl3/stdlib/stream/IStreamNonBlockingAdapterFL.py new file mode 100644 index 000000000..5f7033b4a --- /dev/null +++ b/pymtl3/stdlib/stream/IStreamNonBlockingAdapterFL.py @@ -0,0 +1,31 @@ +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 ) ) + + def line_trace( s ): + return f"{s.istream}({s.entry})" diff --git a/pymtl3/stdlib/stream/OStreamBlockingAdapterFL.py b/pymtl3/stdlib/stream/OStreamBlockingAdapterFL.py new file mode 100644 index 000000000..302586226 --- /dev/null +++ b/pymtl3/stdlib/stream/OStreamBlockingAdapterFL.py @@ -0,0 +1,42 @@ +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 ) ) + + def line_trace( s ): + return f"{s.ostream}({s.entry})" diff --git a/pymtl3/stdlib/stream/OStreamEnqAdapterFL.py b/pymtl3/stdlib/stream/OStreamEnqAdapterFL.py new file mode 100644 index 000000000..ffa224b29 --- /dev/null +++ b/pymtl3/stdlib/stream/OStreamEnqAdapterFL.py @@ -0,0 +1,42 @@ +from pymtl3 import * +from pymtl3.extra import clone_deepcopy +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 ): + 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/OStreamNonBlockingAdapterFL.py b/pymtl3/stdlib/stream/OStreamNonBlockingAdapterFL.py new file mode 100644 index 000000000..8f2413867 --- /dev/null +++ b/pymtl3/stdlib/stream/OStreamNonBlockingAdapterFL.py @@ -0,0 +1,42 @@ +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 ) + ) + + def line_trace( s ): + return f"{s.ostream}({s.entry})" diff --git a/pymtl3/stdlib/stream/SinkRTL.py b/pymtl3/stdlib/stream/SinkRTL.py deleted file mode 100644 index 8cbfbc4d9..000000000 --- a/pymtl3/stdlib/stream/SinkRTL.py +++ /dev/null @@ -1,120 +0,0 @@ -""" -======================================================================== -SinkRTL -======================================================================== -Test sinks with RTL interfaces. - -Author : Shunning Jiang - Date : Feb 12, 2021 -""" - -from pymtl3 import * -from .ifcs import RecvIfcRTL - - -class PyMTLTestSinkError( Exception ): pass - -#------------------------------------------------------------------------- -# TestSinkRTL -#------------------------------------------------------------------------- - -class SinkRTL( 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 ) - - # Data - - # [msgs] and [arrival_time] must have the same length. - if arrival_time is None: - s.arrival_time = None - else: - assert len( msgs ) == len( arrival_time ) - s.arrival_time = list( arrival_time ) - - s.idx = 0 - s.count = 0 - s.cycle_count = 0 - s.msgs = list( msgs ) - - s.error_msg = '' - - s.received = False - s.all_msg_recved = False - s.done_flag = False - - @update_ff - def up_sink(): - # 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 - - s.received = False - - if s.reset: - s.cycle_count = 0 - - s.idx = 0 - s.count = initial_delay - s.recv.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 - - # Sanity check - if s.idx >= len(s.msgs): - s.error_msg = ( 'Test Sink received more msgs than expected!\n' - 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 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}' - ) - - s.idx += 1 - s.count = interval_delay - - if s.count > 0: - s.count -= 1 - s.recv.rdy <<= 0 - else: # s.count == 0 - s.recv.rdy <<= (s.idx < len(s.msgs)) - - def done( s ): - return s.done_flag - - # Line trace - - def line_trace( s ): - return f"{s.recv}" diff --git a/pymtl3/stdlib/stream/SourceRTL.py b/pymtl3/stdlib/stream/SourceRTL.py deleted file mode 100644 index 920d84dc4..000000000 --- a/pymtl3/stdlib/stream/SourceRTL.py +++ /dev/null @@ -1,63 +0,0 @@ -""" -======================================================================== -SourceRTL -======================================================================== -Test sources with RTL interfaces. - -Author : Shunning Jiang - Date : Feb 12, 2021 -""" -from collections import deque -from copy import deepcopy - -from pymtl3 import * -from .ifcs import SendIfcRTL - - -class SourceRTL( Component ): - - def construct( s, Type, msgs, initial_delay=0, interval_delay=0 ): - - # Interface - - s.send = SendIfcRTL( Type ) - - # Data - - s.msgs = deepcopy(msgs) - - # TODO: use wires and ROM to make it translatable - s.idx = 0 - s.count = 0 - - @update_ff - def up_src(): - if s.reset: - s.idx = 0 - s.count = initial_delay - s.send.val <<= 0 - - else: - if s.send.val & s.send.rdy: - s.idx += 1 - s.count = interval_delay - - if s.count > 0: - s.count -= 1 - s.send.val <<= 0 - - else: # s.count == 0 - if s.idx < len(s.msgs): - s.send.val <<= 1 - s.send.msg <<= s.msgs[s.idx] - else: - s.send.val <<= 0 - - - def done( s ): - return s.idx >= len(s.msgs) - - # Line trace - - def line_trace( s ): - return f"{s.send}" diff --git a/pymtl3/stdlib/stream/StreamSinkFL.py b/pymtl3/stdlib/stream/StreamSinkFL.py new file mode 100644 index 000000000..ceb69f439 --- /dev/null +++ b/pymtl3/stdlib/stream/StreamSinkFL.py @@ -0,0 +1,151 @@ +""" +======================================================================== +StremSinkFL +======================================================================== +Test sinks with port interfaces. + +Author : Shunning Jiang, Peitian Pan + Date : Aug 26, 2022 +""" + +from random import randint + +from pymtl3 import * +from .ifcs import IStreamIfc + + +class PyMTLTestSinkError( Exception ): pass + +#------------------------------------------------------------------------- +# StreamSinkFL +#------------------------------------------------------------------------- + +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, + ordered=True ): + + # Interface + + s.istream = IStreamIfc( Type ) + + # Data + + # [msgs] and [arrival_time] must have the same length. + if arrival_time is None: + s.arrival_time = None + else: + assert len( msgs ) == len( arrival_time ) + s.arrival_time = list( arrival_time ) + + s.idx = 0 + s.count = 0 + s.cycle_count = 0 + s.msgs = list( msgs ) + + s.error_msg = '' + + s.received = False + s.all_msg_recved = False + s.done_flag = False + + @update_ff + def up_sink(): + # 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 + + s.received = False + + if s.reset: + s.cycle_count = 0 + + s.idx = 0 + s.count = initial_delay + 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.istream.val & s.istream.rdy: + msg = s.istream.msg + + # Sanity check + if s.idx >= len(s.msgs): + s.error_msg = ( 'Test Sink received more msgs than expected!\n' + f'Received : {msg}' ) + + else: + + # 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 + 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' ): + s.count = randint(0,interval_delay) + else: + s.count = interval_delay + + if s.count > 0: + s.count -= 1 + s.istream.rdy <<= 0 + else: # s.count == 0 + # s.istream.rdy <<= (s.idx < len(s.msgs)) + s.istream.rdy <<= 1 + + def done( s ): + return s.done_flag + + # Line trace + + def line_trace( s ): + return f"{s.istream}" diff --git a/pymtl3/stdlib/stream/StreamSourceFL.py b/pymtl3/stdlib/stream/StreamSourceFL.py new file mode 100644 index 000000000..01be1b387 --- /dev/null +++ b/pymtl3/stdlib/stream/StreamSourceFL.py @@ -0,0 +1,74 @@ +""" +======================================================================== +StreamSourceFL +======================================================================== +Test sources with port interfaces. + +Author : Shunning Jiang, Peitian Pan + Date : Aug 26, 2022 +""" +from collections import deque +from copy import deepcopy +from random import randint + +from pymtl3 import * +from .ifcs import OStreamIfc + + +class StreamSourceFL( Component ): + + def construct( s, Type, msgs, initial_delay=0, interval_delay=0, + interval_delay_mode='fixed' ): + + # Interface + + s.ostream = OStreamIfc( Type ) + + # Data + + s.msgs = deepcopy(msgs) + + # 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(): + if s.reset: + s.idx = 0 + s.count = initial_delay + s.ostream.val <<= 0 + + else: + if (s.ostream.val & s.ostream.rdy) or s.prev_is_none: + s.idx += 1 + if ( interval_delay_mode == 'random' ): + s.count = randint(0,interval_delay) + else: + s.count = interval_delay + + if s.count > 0: + s.count -= 1 + s.ostream.val <<= 0 + + else: # s.count == 0 + if s.idx < len(s.msgs): + 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 + + + def done( s ): + return s.idx >= len(s.msgs) + + # Line trace + + def line_trace( s ): + return f"{s.ostream}" diff --git a/pymtl3/stdlib/stream/__init__.py b/pymtl3/stdlib/stream/__init__.py index 64fb7b68d..dbdef5687 100644 --- a/pymtl3/stdlib/stream/__init__.py +++ b/pymtl3/stdlib/stream/__init__.py @@ -1,7 +1,10 @@ -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 +from .IStreamDeqAdapterFL import IStreamDeqAdapterFL +from .OStreamEnqAdapterFL import OStreamEnqAdapterFL +from .IStreamNonBlockingAdapterFL import IStreamNonBlockingAdapterFL +from .OStreamNonBlockingAdapterFL import OStreamNonBlockingAdapterFL +from .IStreamBlockingAdapterFL import IStreamBlockingAdapterFL +from .OStreamBlockingAdapterFL import OStreamBlockingAdapterFL 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 53% rename from pymtl3/stdlib/stream/ifcs.py rename to pymtl3/stdlib/stream/ifcs/ifcs.py index 0912b517b..54c3b7893 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 ): @@ -24,12 +24,18 @@ 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 ) -class SendIfcRTL( Interface ): + def line_trace( s ): + return str( s ) + +class OStreamIfc( Interface ): def construct( s, Type ): @@ -37,25 +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 ) -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}" + def line_trace( s ): + return str( s ) 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 77004f9f0..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.basic_rtl import Mux, RegisterFile +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/adapters_test.py b/pymtl3/stdlib/stream/test/adapters_test.py new file mode 100644 index 000000000..89bcad8e9 --- /dev/null +++ b/pymtl3/stdlib/stream/test/adapters_test.py @@ -0,0 +1,100 @@ +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 ..IStreamNonBlockingAdapterFL import IStreamNonBlockingAdapterFL +from ..OStreamNonBlockingAdapterFL import OStreamNonBlockingAdapterFL +from ..IStreamBlockingAdapterFL import IStreamBlockingAdapterFL +from ..OStreamBlockingAdapterFL import OStreamBlockingAdapterFL + +class SimpleNonBlockingPassthroughFL( Component ): + def construct( s, Type ): + s.istream = IStreamIfc( Type ) + s.ostream = OStreamIfc( Type ) + s.ideq_adapter = IStreamNonBlockingAdapterFL( Type ) + s.oenq_adapter = OStreamNonBlockingAdapterFL( 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 ) + + 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 ) + 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 ) + + 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 ) + s.sink = StreamSinkFL( Type, sink_msgs ) + s.dut = DutClass( 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.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 ), + 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( + ('DutClass', 'Type', 'msgs', 'src_init', 'src_intv', + 'sink_init', 'sink_intv' ), + [ + ( 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( 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, + ) + th.set_param( "top.sink.construct", + initial_delay = sink_init, + interval_delay = sink_intv, + ) + run_sim( th ) diff --git a/pymtl3/stdlib/stream/test/magic_memory_test.py b/pymtl3/stdlib/stream/test/magic_memory_test.py deleted file mode 100644 index 8204a0cb7..000000000 --- a/pymtl3/stdlib/stream/test/magic_memory_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/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/stream/test/src_sink_test.py b/pymtl3/stdlib/stream/test/src_sink_test.py new file mode 100644 index 000000000..cbf45022e --- /dev/null +++ b/pymtl3/stdlib/stream/test/src_sink_test.py @@ -0,0 +1,166 @@ +""" +======================================================================== +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', 'src_mode', + 'sink_init', 'sink_intv', 'sink_mode', 'arrival_time' ), + [ + ( 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, 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, + interval_delay_mode = src_mode, + ) + th.set_param( "top.sink.construct", + initial_delay = sink_init, + interval_delay = sink_intv, + interval_delay_mode = sink_mode, + 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 ) + +#------------------------------------------------------------------------- +# 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!') + 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/__init__.py b/pymtl3/stdlib/test_utils/__init__.py index d626c7e41..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 TestSinkCL -from .test_srcs import TestSrcCL 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 3512b6ff4..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, TestSinkCL, TestSinkRTL -from ..test_srcs import TestSrcCL, TestSrcRTL - -#------------------------------------------------------------------------- -# 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, TestSrcCL, TestSinkCL, 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, TestSrcCL, TestSinkCL, 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, TestSrcRTL, TestSinkRTL, 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 = [ TestSrcCL ( 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 ) - 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 ) - for i in range(s.num_pairs) ] - elif sink_level == 'rtl': - s.sinks = [ TestSinkRTL( 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, TestSrcCL, TestSinkCL, - 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, TestSrcCL, TestSinkCL, - 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, TestSrcCL, TestSinkCL, - 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, TestSrcCL, TestSinkCL, - 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/test_helpers.py b/pymtl3/stdlib/test_utils/test_helpers.py index e979866e2..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 #------------------------------------------------------------------------- @@ -63,7 +61,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 @@ -110,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 ) @@ -122,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: @@ -151,6 +170,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 #------------------------------------------------------------------------------ diff --git a/pymtl3/stdlib/test_utils/test_masters.py b/pymtl3/stdlib/test_utils/test_masters.py deleted file mode 100644 index 9ec9e4eb2..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 TestSinkCL -from .test_srcs import TestSrcCL - - -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.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) diff --git a/pymtl3/stdlib/test_utils/test_sinks.py b/pymtl3/stdlib/test_utils/test_sinks.py deleted file mode 100644 index f9b8cacc3..000000000 --- a/pymtl3/stdlib/test_utils/test_sinks.py +++ /dev/null @@ -1,148 +0,0 @@ -""" -======================================================================== -Test sinks -======================================================================== -Test sinks with CL and RTL interfaces. - -Author : Yanghui Ou - Date : Mar 11, 2019 -""" - -from pymtl3 import * -from pymtl3.stdlib.ifcs import RecvIfcRTL, RecvRTL2SendCL - - -class PyMTLTestSinkError( Exception ): pass - -#------------------------------------------------------------------------- -# TestSinkCL -#------------------------------------------------------------------------- - -class TestSinkCL( 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 ) - -#------------------------------------------------------------------------- -# TestSinkRTL -#------------------------------------------------------------------------- - -class TestSinkRTL( 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 ) - - # Components - - s.sink = TestSinkCL( 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 68df842bf..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.ifcs import RecvCL2SendRTL, SendIfcRTL - -#------------------------------------------------------------------------- -# TestSrcCL -#------------------------------------------------------------------------- - -class TestSrcCL( 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 ) - -#------------------------------------------------------------------------- -# TestSrcRTL -#------------------------------------------------------------------------- -# TODO: deprecating TestSrcRTL. - -class TestSrcRTL( Component ): - - def construct( s, Type, msgs, initial_delay=0, interval_delay=0 ): - - # Interface - - s.send = SendIfcRTL( Type ) - - # Components - - s.src = TestSrcCL( 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/XcelRequesterAdapterFL.py b/pymtl3/stdlib/xcel/XcelRequesterAdapterFL.py new file mode 100644 index 000000000..336b1cf52 --- /dev/null +++ b/pymtl3/stdlib/xcel/XcelRequesterAdapterFL.py @@ -0,0 +1,84 @@ +import greenlet + +from pymtl3 import * +from pymtl3.extra import clone_deepcopy +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 ) ) diff --git a/pymtl3/stdlib/xcel/__init__.py b/pymtl3/stdlib/xcel/__init__.py new file mode 100644 index 000000000..6ecd61c1f --- /dev/null +++ b/pymtl3/stdlib/xcel/__init__.py @@ -0,0 +1,2 @@ +from .XcelMsg import XcelMsgType, mk_xcel_msg, mk_xcel_req_msg, mk_xcel_resp_msg +from .XcelRequesterAdapterFL import XcelRequesterAdapterFL 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/queues/test/__init__.py b/pymtl3/stdlib/xcel/test/__init__.py similarity index 100% rename from pymtl3/stdlib/queues/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 ) diff --git a/requirements/release.txt b/requirements/release.txt index 7b35e17f6..cd29cc899 100644 --- a/requirements/release.txt +++ b/requirements/release.txt @@ -4,3 +4,5 @@ pytest hypothesis cffi greenlet +py +fasteners 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 = {