Merge pull request #18 from antmicro/v2x-tests v2x module tests
diff --git a/.gitignore b/.gitignore index 894a44c..5cce36f 100644 --- a/.gitignore +++ b/.gitignore
@@ -102,3 +102,5 @@ # mypy .mypy_cache/ + +*.sw*
diff --git a/.travis.yml b/.travis.yml index d5791ae..5c492fa 100644 --- a/.travis.yml +++ b/.travis.yml
@@ -2,14 +2,12 @@ matrix: include: - - python: 3.5 - env: TOXENV=py35 - - python: 3.6 - env: TOXENV=py36 - python: 3.7 env: TOXENV=py37 -install: pip install tox +install: + - . prepareenv.sh + - conda activate yosys-env script: tox
diff --git a/MANIFEST.in b/MANIFEST.in index b9318a3..d6ba619 100644 --- a/MANIFEST.in +++ b/MANIFEST.in
@@ -1,6 +1,7 @@ exclude .readthedocs.yml exclude tox.ini exclude *.swp +prune conf # Include the README include *.md @@ -13,6 +14,8 @@ recursive-include v2x *.py +# Include the preparation script +include prepareenv.sh # Docs recursive-include docs *.py
diff --git a/conf/environment.yml b/conf/environment.yml new file mode 100644 index 0000000..08fb66b --- /dev/null +++ b/conf/environment.yml
@@ -0,0 +1,5 @@ +name: yosys-env +channels: + - symbiflow +dependencies: + - yosys
diff --git a/prepareenv.sh b/prepareenv.sh new file mode 100755 index 0000000..5169d5a --- /dev/null +++ b/prepareenv.sh
@@ -0,0 +1,11 @@ +#!/bin/bash + +pip install tox +wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh +bash miniconda.sh -b -p $HOME/miniconda +source "$HOME/miniconda/etc/profile.d/conda.sh" +hash -r +conda config --set always_yes yes --set changeps1 no +conda install -q setuptools +conda update -q conda +conda env create --file conf/environment.yml
diff --git a/setup.py b/setup.py index f88cf63..9337d25 100644 --- a/setup.py +++ b/setup.py
@@ -14,9 +14,15 @@ long_description_content_type="text/markdown", url="https://github.com/SymbiFlow/python-symbiflow-v2x", packages=setuptools.find_packages(), - install_requires=['lxml', 'pyjson'], + install_requires=[ + 'lxml', + 'pyjson', + 'vtr_xml_utils @ git+https://github.com/Symbiflow/vtr-xml-utils', + ], setup_requires=["pytest-runner"], - tests_require=["pytest"], + tests_require=[ + 'pytest', + ], classifiers=[ "Programming Language :: Python :: 3", "License :: OSI Approved :: ISC License",
diff --git a/tests/dsp/dsp_in_registered/dsp_in_registered.sim.v b/tests/dsp/dsp_in_registered/dsp_in_registered.sim.v index d81f4c8..b940e44 100644 --- a/tests/dsp/dsp_in_registered/dsp_in_registered.sim.v +++ b/tests/dsp/dsp_in_registered/dsp_in_registered.sim.v
@@ -1,4 +1,4 @@ -`include "../fig42-dff/dff.sim.v" +`include "../../vtr/dff/dff.sim.v" `include "../dsp_combinational/dsp_combinational.sim.v" /* DSP Block with register on all inputs */
diff --git a/tests/dsp/dsp_in_registered/golden.model.xml b/tests/dsp/dsp_in_registered/golden.model.xml index 0073b45..f240a2d 100644 --- a/tests/dsp/dsp_in_registered/golden.model.xml +++ b/tests/dsp/dsp_in_registered/golden.model.xml
@@ -1,4 +1,4 @@ <models xmlns:xi="http://www.w3.org/2001/XInclude"> + <xi:include href="../../vtr/dff/dff.model.xml" xpointer="xpointer(models/child::node())"/> <xi:include href="../dsp_combinational/dsp_combinational.model.xml" xpointer="xpointer(models/child::node())"/> - <xi:include href="../fig42-dff/dff.model.xml" xpointer="xpointer(models/child::node())"/> </models>
diff --git a/tests/dsp/dsp_in_registered/golden.pb_type.xml b/tests/dsp/dsp_in_registered/golden.pb_type.xml index 977ec50..102e57f 100644 --- a/tests/dsp/dsp_in_registered/golden.pb_type.xml +++ b/tests/dsp/dsp_in_registered/golden.pb_type.xml
@@ -7,11 +7,11 @@ <output name="out" num_pins="4"/> <pb_type num_pb="2" name="q_a_ff"> <!--old_name DFF old_num_pb 1--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="2" name="q_b_ff"> <!--old_name DFF old_num_pb 1--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="comb"> <!--old_name DSP_COMBINATIONAL--> @@ -19,7 +19,7 @@ </pb_type> <pb_type num_pb="1" name="m_ff"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <interconnect> <direct>
diff --git a/tests/dsp/dsp_inout_registered/dsp_inout_registered.sim.v b/tests/dsp/dsp_inout_registered/dsp_inout_registered.sim.v index 0d06b8b..2417b82 100644 --- a/tests/dsp/dsp_inout_registered/dsp_inout_registered.sim.v +++ b/tests/dsp/dsp_inout_registered/dsp_inout_registered.sim.v
@@ -1,4 +1,4 @@ -`include "../fig42-dff/dff.sim.v" +`include "../../vtr/dff/dff.sim.v" `include "../dsp_combinational/dsp_combinational.sim.v" /* DSP Block with register on both the inputs and the output */
diff --git a/tests/dsp/dsp_inout_registered/golden.model.xml b/tests/dsp/dsp_inout_registered/golden.model.xml index 0073b45..f240a2d 100644 --- a/tests/dsp/dsp_inout_registered/golden.model.xml +++ b/tests/dsp/dsp_inout_registered/golden.model.xml
@@ -1,4 +1,4 @@ <models xmlns:xi="http://www.w3.org/2001/XInclude"> + <xi:include href="../../vtr/dff/dff.model.xml" xpointer="xpointer(models/child::node())"/> <xi:include href="../dsp_combinational/dsp_combinational.model.xml" xpointer="xpointer(models/child::node())"/> - <xi:include href="../fig42-dff/dff.model.xml" xpointer="xpointer(models/child::node())"/> </models>
diff --git a/tests/dsp/dsp_inout_registered/golden.pb_type.xml b/tests/dsp/dsp_inout_registered/golden.pb_type.xml index 5c2e747..84d80d1 100644 --- a/tests/dsp/dsp_inout_registered/golden.pb_type.xml +++ b/tests/dsp/dsp_inout_registered/golden.pb_type.xml
@@ -11,39 +11,39 @@ </pb_type> <pb_type num_pb="1" name="input_dffs_gen_q_a_ff_0"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="input_dffs_gen_q_b_ff_0"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="input_dffs_gen_q_a_ff_1"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="input_dffs_gen_q_b_ff_1"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="m_ff"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="output_dffs_gen_q_out_ff_0"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="output_dffs_gen_q_out_ff_1"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="output_dffs_gen_q_out_ff_2"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="output_dffs_gen_q_out_ff_3"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <interconnect> <direct>
diff --git a/tests/dsp/dsp_inout_registered_dualclk/dsp_inout_registered_dualclk.sim.v b/tests/dsp/dsp_inout_registered_dualclk/dsp_inout_registered_dualclk.sim.v index 5cad5bb..bb73b32 100644 --- a/tests/dsp/dsp_inout_registered_dualclk/dsp_inout_registered_dualclk.sim.v +++ b/tests/dsp/dsp_inout_registered_dualclk/dsp_inout_registered_dualclk.sim.v
@@ -1,4 +1,4 @@ -`include "../fig42-dff/dff.sim.v" +`include "../../vtr/dff/dff.sim.v" `include "../dsp_combinational/dsp_combinational.sim.v" /* DSP Block with register on both the inputs and the output, which use different clocks */
diff --git a/tests/dsp/dsp_inout_registered_dualclk/golden.model.xml b/tests/dsp/dsp_inout_registered_dualclk/golden.model.xml index 0073b45..f240a2d 100644 --- a/tests/dsp/dsp_inout_registered_dualclk/golden.model.xml +++ b/tests/dsp/dsp_inout_registered_dualclk/golden.model.xml
@@ -1,4 +1,4 @@ <models xmlns:xi="http://www.w3.org/2001/XInclude"> + <xi:include href="../../vtr/dff/dff.model.xml" xpointer="xpointer(models/child::node())"/> <xi:include href="../dsp_combinational/dsp_combinational.model.xml" xpointer="xpointer(models/child::node())"/> - <xi:include href="../fig42-dff/dff.model.xml" xpointer="xpointer(models/child::node())"/> </models>
diff --git a/tests/dsp/dsp_inout_registered_dualclk/golden.pb_type.xml b/tests/dsp/dsp_inout_registered_dualclk/golden.pb_type.xml index c5c69e0..46c5d22 100644 --- a/tests/dsp/dsp_inout_registered_dualclk/golden.pb_type.xml +++ b/tests/dsp/dsp_inout_registered_dualclk/golden.pb_type.xml
@@ -12,39 +12,39 @@ </pb_type> <pb_type num_pb="1" name="input_dffs_gen_q_a_ff_0"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="input_dffs_gen_q_b_ff_0"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="input_dffs_gen_q_a_ff_1"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="input_dffs_gen_q_b_ff_1"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="m_ff"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="output_dffs_gen_q_out_ff_0"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="output_dffs_gen_q_out_ff_1"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="output_dffs_gen_q_out_ff_2"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="output_dffs_gen_q_out_ff_3"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <interconnect> <direct>
diff --git a/tests/dsp/dsp_out_registered/dsp_out_registered.sim.v b/tests/dsp/dsp_out_registered/dsp_out_registered.sim.v index 6588797..c9f7b74 100644 --- a/tests/dsp/dsp_out_registered/dsp_out_registered.sim.v +++ b/tests/dsp/dsp_out_registered/dsp_out_registered.sim.v
@@ -1,4 +1,4 @@ -`include "../fig42-dff/dff.sim.v" +`include "../../vtr/dff/dff.sim.v" `include "../dsp_combinational/dsp_combinational.sim.v" /* DSP Block with register on the output */
diff --git a/tests/dsp/dsp_out_registered/golden.model.xml b/tests/dsp/dsp_out_registered/golden.model.xml index e9b223a..03e1f59 100644 --- a/tests/dsp/dsp_out_registered/golden.model.xml +++ b/tests/dsp/dsp_out_registered/golden.model.xml
@@ -1,5 +1,5 @@ <models xmlns:xi="http://www.w3.org/2001/XInclude"> <!-- https://docs.verilogtorouting.org/en/latest/tutorials/arch/timing_modeling/#mixed-sequential-combinational-block --> + <xi:include href="../../vtr/dff/dff.model.xml" xpointer="xpointer(models/child::node())"/> <xi:include href="../dsp_combinational/dsp_combinational.model.xml" xpointer="xpointer(models/child::node())"/> - <xi:include href="../fig42-dff/dff.model.xml" xpointer="xpointer(models/child::node())"/> </models>
diff --git a/tests/dsp/dsp_out_registered/golden.pb_type.xml b/tests/dsp/dsp_out_registered/golden.pb_type.xml index b52c2f6..ee1be45 100644 --- a/tests/dsp/dsp_out_registered/golden.pb_type.xml +++ b/tests/dsp/dsp_out_registered/golden.pb_type.xml
@@ -11,19 +11,19 @@ </pb_type> <pb_type num_pb="1" name="output_dffs_gen_q_out_ff_0"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="output_dffs_gen_q_out_ff_1"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="output_dffs_gen_q_out_ff_2"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="output_dffs_gen_q_out_ff_3"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <interconnect> <direct>
diff --git a/tests/dsp/dsp_partial_registered/dsp_partial_registered.sim.v b/tests/dsp/dsp_partial_registered/dsp_partial_registered.sim.v index 3b51f68..3ffc3b7 100644 --- a/tests/dsp/dsp_partial_registered/dsp_partial_registered.sim.v +++ b/tests/dsp/dsp_partial_registered/dsp_partial_registered.sim.v
@@ -1,4 +1,4 @@ -`include "../fig42-dff/dff.sim.v" +`include "../../vtr/dff/dff.sim.v" `include "../dsp_combinational/dsp_combinational.sim.v" /* DSP Block with register on only some inputs */
diff --git a/tests/dsp/dsp_partial_registered/golden.model.xml b/tests/dsp/dsp_partial_registered/golden.model.xml index 0073b45..f240a2d 100644 --- a/tests/dsp/dsp_partial_registered/golden.model.xml +++ b/tests/dsp/dsp_partial_registered/golden.model.xml
@@ -1,4 +1,4 @@ <models xmlns:xi="http://www.w3.org/2001/XInclude"> + <xi:include href="../../vtr/dff/dff.model.xml" xpointer="xpointer(models/child::node())"/> <xi:include href="../dsp_combinational/dsp_combinational.model.xml" xpointer="xpointer(models/child::node())"/> - <xi:include href="../fig42-dff/dff.model.xml" xpointer="xpointer(models/child::node())"/> </models>
diff --git a/tests/dsp/dsp_partial_registered/golden.pb_type.xml b/tests/dsp/dsp_partial_registered/golden.pb_type.xml index 759393b..57c8096 100644 --- a/tests/dsp/dsp_partial_registered/golden.pb_type.xml +++ b/tests/dsp/dsp_partial_registered/golden.pb_type.xml
@@ -11,19 +11,19 @@ </pb_type> <pb_type num_pb="1" name="dffs_gen_q_a_ff_0"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="dffs_gen_q_b_ff_0"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="dffs_gen_q_a_ff_1"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type num_pb="1" name="dffs_gen_q_b_ff_1"> <!--old_name DFF--> - <xi:include href="../fig42-dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../../vtr/dff/dff.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <interconnect> <direct>
diff --git a/tests/multiple_instance/golden.model.xml b/tests/multiple_instance/golden.model.xml index 29655ed..e3808ec 100644 --- a/tests/multiple_instance/golden.model.xml +++ b/tests/multiple_instance/golden.model.xml
@@ -1,3 +1,3 @@ <models xmlns:xi="http://www.w3.org/2001/XInclude"> - <xi:include href="../fig41-full-adder/adder.model.xml" xpointer="xpointer(models/child::node())"/> + <xi:include href="../vtr/full-adder/adder.model.xml" xpointer="xpointer(models/child::node())"/> </models>
diff --git a/tests/multiple_instance/golden.pb_type.xml b/tests/multiple_instance/golden.pb_type.xml index 6dea215..32124a9 100644 --- a/tests/multiple_instance/golden.pb_type.xml +++ b/tests/multiple_instance/golden.pb_type.xml
@@ -9,10 +9,10 @@ <input name="d" num_pins="4"/> <output name="sum" num_pins="8"/> <pb_type name="comb_apb" num_pb="4"> - <xi:include href="../fig41-full-adder/adder.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../vtr/full-adder/adder.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <pb_type name="comb_cpd" num_pb="4"> - <xi:include href="../fig41-full-adder/adder.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> + <xi:include href="../vtr/full-adder/adder.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/> </pb_type> <interconnect>
diff --git a/tests/multiple_instance/multiple_instance.sim.v b/tests/multiple_instance/multiple_instance.sim.v index cf31a68..46536e9 100644 --- a/tests/multiple_instance/multiple_instance.sim.v +++ b/tests/multiple_instance/multiple_instance.sim.v
@@ -1,4 +1,4 @@ -`include "../fig41-full-adder/adder.sim.v" +`include "../vtr/full-adder/adder.sim.v" module MULTIPLE_INSTANCE (a, b, c, d, cin, cout, sum); localparam DATA_WIDTH = 4;
diff --git a/tests/muxes/golden.model.xml b/tests/muxes/golden.model.xml index cfc9bc9..db8e57e 100644 --- a/tests/muxes/golden.model.xml +++ b/tests/muxes/golden.model.xml
@@ -1,4 +1,4 @@ <models xmlns:xi="http://www.w3.org/2001/XInclude"> - <xi:include href="routing/rmux.model.xml" xpointer="xpointer(models/child::node())"/> <xi:include href="../logicbox/logicbox.model.xml" xpointer="xpointer(models/child::node())"/> + <xi:include href="routing/rmux.model.xml" xpointer="xpointer(models/child::node())"/> </models>
diff --git a/tests/test_v2x.py b/tests/test_v2x.py new file mode 100644 index 0000000..127b9ce --- /dev/null +++ b/tests/test_v2x.py
@@ -0,0 +1,224 @@ +#!/usr/bin/env python3 + +from pathlib import Path +import os +from functools import cmp_to_key + +from v2x import vlog_to_model +from v2x import vlog_to_pbtype +from v2x.xmlinc import xmlinc +from v2x.mux_gen import mux_gen + +from vtr_xml_utils import convert + +from distutils.dir_util import copy_tree + +TEST_TMP_SUFFIX = 'build/' + + +def prepare_files(): + """Prepares the files for running tests. + + It copies all test files to the temporary build/ directory. + It also generates files that are required by the test files as XInclude and + are not part of any test. + """ + # Copy all files for tests to the build/ directory + scriptdir = os.path.dirname(__file__) + testdir = os.path.join(os.path.dirname(scriptdir), TEST_TMP_SUFFIX) + os.makedirs(testdir, exist_ok=True) + + for tdir in os.listdir(scriptdir): + s = os.path.join(scriptdir, tdir) + d = os.path.join(testdir, tdir) + if os.path.isdir(s): + copy_tree(s, d, update=1) + + # Generate muxes/routing for tests/muxes model + mux_gen(outdir=os.path.join(testdir, 'muxes/routing'), + outfilename='rmux', + datatype='routing', + width=2, + split_inputs=True, + name_output='O', + name_mux='RMUX', + name_inputs='I0,I1' + ) + # Generate tests/vtr/lutff-pair/omux for tests/vtr/lutff-pair + mux_gen(outdir=os.path.join(testdir, 'vtr/lutff-pair/omux'), + outfilename='omux', + datatype='routing', + width=2, + split_inputs=True, + name_output='O', + name_mux='omux', + name_inputs='L,F' + ) + + # Generate dff.pb_type.xml required by various tests + newpbfile = os.path.join(testdir, 'vtr/dff/dff.pb_type.xml') + pbtypeout = vlog_to_pbtype.vlog_to_pbtype( + [os.path.join(testdir, 'vtr/dff/dff.sim.v')], + newpbfile, + None) + with open(newpbfile, 'w') as model: + model.write(pbtypeout) + + # Generate ff.pb_type.xml required by various tests + newpbfile = os.path.join(testdir, 'vtr/lutff-pair/ff/ff.pb_type.xml') + pbtypeout = vlog_to_pbtype.vlog_to_pbtype( + [os.path.join(testdir, 'vtr/lutff-pair/ff/ff.sim.v')], + newpbfile, + 'DFF') + with open(newpbfile, 'w') as model: + model.write(pbtypeout) + + +def order_based_on_deps(left, right): + """Compares two Verilog files based on their dependencies. + + It checks if any of two files depends on the other and and applies an + order in which the Verilog file required by the other is processed first + during tests. If both files do not depend on each other, they are ordered + by their number of dependencies. + + Parameters + ---------- + left: str + The left operand in the comparison, which is a path to Verilog file. + right: str + The right operand in the comparison, which is a path to Verilog file. + """ + # TODO: this is overly simplified sorting over files with dependencies + # normally it should be solved with use of topological sort + with open(left, 'r') as leftfile: + relhref = xmlinc.make_relhref(left, right) + leftcontent = leftfile.read() + if relhref in leftcontent: + return 1 + leftinccount = leftcontent.count('`include') + with open(right, 'r') as rightfile: + relhref = xmlinc.make_relhref(right, left) + rightcontent = rightfile.read() + if relhref in rightcontent: + return -1 + rightinccount = rightcontent.count('`include') + return leftinccount - rightinccount + + +def find_files(pattern, rootdir): + """Finds files that match pattern in the given directory. + + Parameters + ---------- + pattern: str + A regex pattern for files in the rootdir. + rootdir: str + A directory in which the function should look for files. + """ + return [str(f) for f in Path(os.path.abspath(rootdir)).glob(pattern)] + + +def get_test_goldens(goldentype, testfile): + """Gets all files for testing that match a goldentype regex. + + Parameters + ---------- + goldentype: str + A suffix for golden* files that should be used for tests. + testfile: str + A path to test script. + """ + simfiles = sorted( + sorted(convert.get_filenames_containing('*.sim.v', testfile)), + key=cmp_to_key(order_based_on_deps)) + goldens = [] + for sim in simfiles: + res = find_files('golden.' + goldentype, os.path.dirname(sim)) + if len(res) == 1: + goldens.append({ + 'simfile': sim, + 'goldenfile': res[0]}) + return goldens + + +def pytest_generate_tests(metafunc): + """Scans for golden files and applies appropriate tests for those files. + + Parameters + ---------- + metafunc: A pytest object representing the test context. + """ + prepare_files() + scriptdir = os.path.dirname(__file__) + testdir = os.path.join(os.path.dirname(scriptdir), TEST_TMP_SUFFIX) + + if "modelcase" in metafunc.fixturenames: + models = get_test_goldens('model.xml', os.path.join(testdir, 'test')) + metafunc.parametrize("modelcase", + models, + ids=[i['simfile'] for i in models]) + if "pbtypecase" in metafunc.fixturenames: + models = get_test_goldens('pb_type.xml', os.path.join(testdir, 'test')) + metafunc.parametrize("pbtypecase", + models, + ids=[i['simfile'] for i in models]) + + +def test_model_generation_with_vlog_to_model(modelcase): + """Checks model.xml files produced by the vlog_to_model. + + Parameters + ---------- + modelcase : dict + A dict of the filename of the model.xml file that should be produced + by the corresponding sim.v file ('goldenfile') and the corresponding + sim.v file ('simfile') + """ + modelfile = modelcase['goldenfile'] + testdatadir = os.path.dirname(modelfile) + '/' + vlog_filename = modelcase['simfile'] + testname = vlog_filename.split('/')[-1].split('.')[0] + generatedmodelfile = os.path.join(testdatadir, testname) + '.model.xml' + modelout = vlog_to_model.vlog_to_model([vlog_filename], None, None, + generatedmodelfile) + with open(generatedmodelfile, 'w') as model: + model.write(modelout) + + convertedgolden = convert.vtr_stylize_xml(modelfile) + convertedmodel = convert.vtr_stylize_xml(generatedmodelfile) + + with open(generatedmodelfile + '.actual', 'w') as model: + model.write(convertedmodel) + + assert convertedmodel == convertedgolden + + +def test_pbtype_generation_with_vlog_to_pbtype(pbtypecase): + """Checks pb_type.xml files produced by the vlog_to_pbtype. + + Parameters + ---------- + pbtypecase : dict + A dict of the filename of the model.xml file that should be produced + by the corresponding sim.v file ('goldenfile') and the corresponding + sim.v file ('simfile') + """ + testdatafile = pbtypecase['goldenfile'] + testdatadir = os.path.dirname(testdatafile) + '/' + vlog_filename = pbtypecase['simfile'] + testname = vlog_filename.split('/')[-1].split('.')[0] + generatedmodelfile = os.path.join(testdatadir, testname) + '.pb_type.xml' + pbtypeout = vlog_to_pbtype.vlog_to_pbtype([vlog_filename], + generatedmodelfile, + None) + with open(generatedmodelfile, 'w') as model: + model.write(pbtypeout) + + convertedgolden = convert.vtr_stylize_xml(testdatafile) + convertedmodel = convert.vtr_stylize_xml(generatedmodelfile) + + with open(generatedmodelfile + '.actual', 'w') as model: + model.write(convertedmodel) + + assert convertedmodel == convertedgolden
diff --git a/tox.ini b/tox.ini index 23d3730..43456c6 100644 --- a/tox.ini +++ b/tox.ini
@@ -22,6 +22,10 @@ # use `python setup.py check -m -r -s` instead. python setup.py check -m -s flake8 . + pytest -vv [flake8] exclude = .tox,*.egg,build,data select = E,W,F +ignore = + W503, # line break before binary operator + W504, # line break after binary operator
diff --git a/tests/vtr/lutff-pair/ff/golden.pb_type.xml b/v2x/lib/__init__.py similarity index 100% rename from tests/vtr/lutff-pair/ff/golden.pb_type.xml rename to v2x/lib/__init__.py
diff --git a/v2x/lib/argparse_extra.py b/v2x/lib/argparse_extra.py new file mode 100644 index 0000000..9be1116 --- /dev/null +++ b/v2x/lib/argparse_extra.py
@@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +import argparse + + +class ActionStoreBool(argparse.Action): + """Convert a string argument into a boolean. + + Use like this for a default off argument which can be turned on; + >>> parser = argparse.ArgumentParser() + >>> parser.add_argument( + ... '--arg', action=ActionStoreBool, default=False) + ActionStoreBool(['--arg'], False) + >>> parser.parse_args([]).arg + False + >>> parser.parse_args(['--arg']).arg + True + >>> parser.parse_args(['--no-arg']).arg + False + >>> parser.parse_args(['--arg', 'yes']).arg + True + >>> parser.parse_args(['--arg', 'no']).arg + False + >>> + + Use like this for a default on argument which can be turned off; + >>> parser = argparse.ArgumentParser() + >>> parser.add_argument( + ... '--arg', action=ActionStoreBool, default=True) + ActionStoreBool(['--arg'], True) + >>> parser.parse_args([]).arg + True + >>> parser.parse_args(['--arg']).arg + True + >>> parser.parse_args(['--no-arg']).arg + False + >>> parser.parse_args(['--arg', 'yes']).arg + True + >>> parser.parse_args(['--arg', 'no']).arg + False + >>> + + Converts the following (in can capitalization) to `True`: + * yes + * y + * true + * t + * 1 + + Converts the following (in can capitalization) to `False`: + * no + * n + * false + * f + * 0 + """ + + def __init__( + self, + option_strings, + dest, + default=None, + required=False, + help=None, + metavar=None + ): + self.orig_option_strings = option_strings + new_option_strings = [] + for s in option_strings: + assert s.startswith("--"), s + new_option_strings.append(s) + new_option_strings.append("--no-" + s[2:]) + argparse.Action.__init__( + self, + new_option_strings, + dest=dest, + nargs='?', + const=[True], + default=default, + type=self.value, + choices=None, + required=required, + help=help, + metavar=metavar + ) + + def value(self, s): + if not s: + return self.default + elif s.lower() in ('yes', 'true', 't', 'y', '1'): + return [True] + elif s.lower() in ('no', 'false', 'f', 'n', '0'): + return [False] + else: + raise argparse.ArgumentTypeError('Boolean value expected.') + + def __call__(self, parser, namespace, values, option_string=None): + if len(values) == 0: + values = [self.default] + assert len(values) == 1 + if option_string.startswith("--no"): + values[0] = False + setattr(namespace, self.dest, values[0]) + + def __repr__(self): + return "ActionStoreBool({}, {})".format( + self.orig_option_strings, self.default + ) + + +if __name__ == "__main__": + import doctest + failure_count, test_count = doctest.testmod() + assert test_count > 0 + assert failure_count == 0, "Doctests failed!"
diff --git a/v2x/lib/asserts.py b/v2x/lib/asserts.py new file mode 100644 index 0000000..e341f32 --- /dev/null +++ b/v2x/lib/asserts.py
@@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +def __safe_call(f, a): + """Call a function and capture all exceptions.""" + try: + return f(a) + except Exception as e: + return "{}@{}({})".format(a.__class__.__name__, id(a), e) + + +def __safe_error(msg, a, b): + """Generate the error message for assert_XX without causing an error.""" + return "{} ({}) {} {} ({})".format( + __safe_call(str, a), + __safe_call(repr, a), + msg, + __safe_call(str, b), + __safe_call(repr, b), + ) + + +def assert_eq(a, b, msg=None): + """Assert equal with better error message.""" + assert a == b, msg or __safe_error("!=", a, b) + + +def assert_not_in(needle, haystack, msg=None): + """Assert equal with better error message.""" + assert needle not in haystack, msg or __safe_error( + "already in", needle, haystack + ) + + +def assert_is(a, b): + """Assert is with better error message.""" + assert a is b, __safe_error("is not", a, b) + + +def assert_type( + obj, cls, msg="{obj} ({obj!r}) should be a {cls}, not {objcls}" +): + """Raise a type error if obj is not an instance of cls.""" + if not isinstance(obj, cls): + raise TypeError(msg.format(obj=obj, objcls=type(obj), cls=cls)) + + +def assert_type_or_none(obj, classes): + """Raise a type error if obj is not an instance of cls or None.""" + if obj is not None: + assert_type(obj, classes) + + +def assert_len_eq(l): + """Check all lists in a list are equal length""" + # Sanity check + max_len = max(len(p) for p in l) + for i, p in enumerate(l): + assert len( + p + ) == max_len, "Length check failed!\n \ +l[{}] has {} elements != {} ({!r})\n{!r}".format( + i, len(p), max_len, p, l + )
diff --git a/v2x/lib/mux.py b/v2x/lib/mux.py new file mode 100644 index 0000000..700d02b --- /dev/null +++ b/v2x/lib/mux.py
@@ -0,0 +1,247 @@ +#!/usr/bin/env python3 +import lxml.etree as ET +from enum import Enum + + +def clog2(x): + """Ceiling log 2 of x. + + >>> clog2(0), clog2(1), clog2(2), clog2(3), clog2(4) + (0, 0, 1, 2, 2) + >>> clog2(5), clog2(6), clog2(7), clog2(8), clog2(9) + (3, 3, 3, 3, 4) + >>> clog2(1 << 31) + 31 + >>> clog2(1 << 63) + 63 + >>> clog2(1 << 11) + 11 + """ + x -= 1 + i = 0 + while True: + if x <= 0: + break + x = x >> 1 + i += 1 + return i + + +def add_metadata(tag, mtype, msubtype): + meta_root = ET.SubElement(tag, 'metadata') + meta_type = ET.SubElement(meta_root, 'meta', {'name': 'type'}) + meta_type.text = mtype + meta_subtype = ET.SubElement(meta_root, 'meta', {'name': 'subtype'}) + meta_subtype.text = msubtype + return meta_root + + +class MuxType(Enum): + LOGIC = 'BEL_MX' + ROUTING = 'BEL_RX' + + +class MuxPinType(Enum): + INPUT = 'i' + SELECT = 's' + OUTPUT = 'o' + + def verilog(self): + if self in (self.INPUT, self.SELECT): + return "input wire" + elif self == self.OUTPUT: + return "output wire" + else: + raise TypeError( + "Can't convert {} into verilog definition.".format(self) + ) + + def direction(self): + if self in (self.INPUT, self.SELECT): + return "input" + elif self == self.OUTPUT: + return "output" + else: + raise TypeError( + "Can't convert {} into verilog definition.".format(self) + ) + + def __str__(self): + return self.value + + +class ModulePort(object): + def __init__(self, pin_type, name, width, index, data_width=1): + self.name = name + self.pin_type = pin_type + self.width = width + self.index = index + self.data_width = data_width + + def getDefinition(self): + if self.width == 1: + if self.data_width is not None and self.data_width > 1: + return '\t%s [%d:0] %s;\n' % ( + self.pin_type.verilog(), self.data_width - 1, self.name + ) + else: + return '\t%s %s;\n' % (self.pin_type.verilog(), self.name) + else: + return '\t%s %s %s;\n' % ( + self.pin_type.verilog(), self.index, self.name + ) + + +def pb_type_xml(mux_type, mux_name, pins, subckt=None, num_pb=1, comment=""): + """Generate <pb_type> XML for a mux. + + Parameters + ---------- + mux_type: MuxType + Type of mux to create. + + mux_name: str + Name of the mux. + + pins: [(MuxPinType, str, int, int),] + List of tuples which contain (pin type, pin name, port width, index) + + subckt: str + Name of the blif_model for the mux. Only valid when mux_type == + MuxType.LOGIC. + + num_pb: int + Value for the num_pb value. Defaults to 1. + + comment: str + Optional comment for the mux. + + Returns + ------- + xml.etree.ElementTree + pb_type.xml for requested mux + """ + assert isinstance(comment, + str), "{} {}".format(type(comment), repr(comment)) + + if mux_type not in (MuxType.LOGIC, MuxType.ROUTING): + assert False, "Unknown type {}".format(mux_type) + + pb_type_xml = ET.Element( + 'pb_type', { + 'name': mux_name, + 'num_pb': str(num_pb), + } + ) + + if mux_type == MuxType.LOGIC: + add_metadata(pb_type_xml, 'bel', 'mux') + else: + add_metadata(pb_type_xml, 'bel', 'routing') + + if mux_type == MuxType.LOGIC: + model = ET.SubElement(pb_type_xml, "blif_model") + model.text = '.subckt {}'.format(subckt) + else: + assert not subckt, "Provided subckt={} for non-logic mux!".format( + subckt + ) + + if comment is not None: + pb_type_xml.append(ET.Comment(comment)) + + for port in pins: + # assert port.index < port.width, ( + # "Pin index {} >= width {} for pin {} {}".format( + # port.index, port.width, port.name, port.pin_type + # ) + # ) + if mux_type == MuxType.ROUTING and port.pin_type == MuxPinType.SELECT: + continue + + assert port.width == 1 or port.data_width == 1, ( + 'Only one of width(%d) or data_width(%d) may > 1 for pin %s' % + (port.width, port.data_width, port.name) + ) + + if port.width == 1 and port.data_width > 1: + num_pins = port.data_width + else: + num_pins = port.width + + mux = ET.SubElement( + pb_type_xml, + port.pin_type.direction(), + { + 'name': port.name, + 'num_pins': str(num_pins) + }, + ) + + if mux_type == MuxType.LOGIC: + for inport in pins: + if inport.pin_type not in (MuxPinType.INPUT, MuxPinType.SELECT): + continue + + for outport in pins: + if outport.pin_type not in (MuxPinType.OUTPUT, ): + continue + if inport.name.startswith('I'): + delay_inport = inport.name[1] + else: + # if it is not IX it must be S + delay_inport = "S0" + # XXX: temporary workaroud + if mux_name == "F6MUX": + maxdel = "10e-12" + else: + maxdel = "{{iopath_{}_OUT}}".format(delay_inport) + + ET.SubElement( + pb_type_xml, + 'delay_constant', + { + 'max': maxdel, + 'in_port': "%s" % inport.name, + 'out_port': "%s" % outport.name, + }, + ) + elif mux_type == MuxType.ROUTING: + interconnect = ET.SubElement(pb_type_xml, 'interconnect') + + inputs = [ + "{}.{}".format(mux_name, port.name) + for port in pins + if port.pin_type in (MuxPinType.INPUT, ) + ] + outputs = [ + "{}.{}".format(mux_name, port.name) + for port in pins + if port.pin_type in (MuxPinType.OUTPUT, ) + ] + assert len(outputs) == 1 + + mux = ET.SubElement( + interconnect, + 'mux', + { + 'name': '%s' % mux_name, + 'input': " ".join(inputs), + 'output': outputs[0], + }, + ) + meta_root = add_metadata(mux, 'bel', 'routing') + + meta_fasm_mux = ET.SubElement(meta_root, 'meta', {'key': 'fasm_mux'}) + meta_fasm_mux.text = "\n".join( + [""] + ["{0} = {0}".format(i) for i in inputs] + [""] + ) + + return pb_type_xml + + +if __name__ == "__main__": + import doctest + failure_count, test_count = doctest.testmod() + assert test_count > 0 + assert failure_count == 0, "Doctests failed!"
diff --git a/v2x/mux_gen.py b/v2x/mux_gen.py new file mode 100755 index 0000000..f24950f --- /dev/null +++ b/v2x/mux_gen.py
@@ -0,0 +1,591 @@ +#!/usr/bin/env python3 +""" +Generate MUX. + +MUXes come in two types, + 1) Configurable via logic signals, + 2) Statically configured by PnR (called "routing") muxes. +""" + +import argparse +import itertools +import lxml.etree as ET +import os +import sys + +from .lib import mux as mux_lib +from .lib.argparse_extra import ActionStoreBool +from .lib.asserts import assert_eq + +parser = argparse.ArgumentParser( + description='Generate a MUX wrapper.', + fromfile_prefix_chars='@', + prefix_chars='-~' +) + +parser.add_argument( + '--verbose', + '--no-verbose', + action=ActionStoreBool, + default=os.environ.get('V', '') == '1', + help="Print lots of information about the generation." +) + +parser.add_argument('--width', type=int, default=8, help="Width of the MUX.") + +parser.add_argument( + '--data-width', type=int, default=1, help="data width of the MUX." +) + +parser.add_argument( + '--type', + choices=['logic', 'routing'], + default='logic', + help="Type of MUX." +) + +parser.add_argument( + '--split-inputs', + action=ActionStoreBool, + default=False, + help="Split the inputs into separate signals" +) + +parser.add_argument( + '--split-selects', + action=ActionStoreBool, + default=False, + help="Split the selects into separate signals" +) + +parser.add_argument( + '--name-mux', type=str, default='MUX', help="Name of the mux." +) + +parser.add_argument( + '--name-input', + type=str, + default='I', + help="Name of the input values for the mux." +) + +parser.name_inputs = parser.add_argument( + '--name-inputs', + type=str, + default=None, + help= # noqa: E251 + "Comma deliminator list for the name of each input to the mux " + \ + "(implies --split-inputs)." +) + +parser.add_argument( + '--name-output', + type=str, + default='O', + help="Name of the output value for the mux." +) + +parser.add_argument( + '--name-select', + type=str, + default='S', + help="Name of the select parameter for the mux." +) + +parser.name_selects = parser.add_argument( + '--name-selects', + type=str, + default=None, + help= # noqa: E251 + "Comma deliminator list for the name of each select to the mux " + + "(implies --split-selects)." +) + +parser.add_argument( + '--order', + choices=[''.join(x) for x in itertools.permutations('ios')] + + [''.join(x) for x in itertools.permutations('io')], + default='iso', + help= # noqa: E251 + """Order of the arguments for the MUX. +(i - Inputs, o - Output, s - Select)""" +) + +parser.add_argument( + '--outdir', + default=None, + help="""Directory to output generated content too.""" +) + +parser.add_argument( + '--outfilename', + default=None, + help="""Filename to output generated content too.""" +) + +parser.add_argument( + '--comment', default=None, help="""Add some type of comment to the mux.""" +) + +parser.add_argument( + '--num_pb', default=1, help="""Set the num_pb for the mux.""" +) + +parser.add_argument( + '--subckt', default=None, help="""Override the subcircuit name.""" +) + + +def mux_gen(argv='Python function', + width=8, + data_width=1, + datatype='logic', + split_inputs=False, + split_selects=False, + name_mux='MUX', + name_input='I', + name_inputs=None, + name_output='O', + name_select='S', + name_selects=None, + order='iso', + outdir=None, + outfilename=None, + comment=None, + num_pb=1, + subckt=None, + verbose=False + ): + def output_block(name, s): + if verbose: + print() + print(name, '-' * (75 - (len(name) + 1))) + print(s, end="") + if s[-1] != '\n': + print() + print('-' * 75) + + width_bits = mux_lib.clog2(width) + + def normpath(p, to=None): + p = os.path.realpath(os.path.abspath(p)) + if to is None: + return p + return os.path.relpath(p, normpath(to)) + + mypath = normpath(__file__) + + if not outdir: + outdir = os.path.join(".", name_mux.lower()) + + outdir = normpath(outdir) + + mydir = normpath(os.path.dirname(mypath), to=outdir) + mux_dir = normpath(os.path.join(mydir, '..', 'vpr', 'muxes'), to=outdir) + + if data_width > 1 and not split_inputs: + assert False, "data_width(%d) > 1 requires using split_inputs" % ( + data_width + ) + + name_input_default = 'I' + name_inputs_default = None + name_select_default = 'S' + name_selects_default = None + + if name_inputs: + assert_eq(name_input, name_input_default) + name_input = None + split_inputs = True + + names = name_inputs.split(',') + assert len(names) == width, "%s input names, but %s needed." % ( + names, width + ) + name_inputs = names + elif split_inputs: + name_inputs = [ + name_input + str(i) for i in range(width) + ] + name_inputs_default = name_inputs + assert_eq(name_inputs_default, name_inputs) + + if name_selects: + assert_eq(name_select, name_select_default) + name_select = None + split_selects = True + + names = name_selects.split(',') + assert len(names) == width_bits, ( + "%s select names, but %s needed." % (names, width_bits) + ) + name_selects = names + elif split_selects: + name_selects = [ + name_select + str(i) for i in range(width_bits) + ] + name_selects.default = name_selects + assert_eq(name_selects_default, name_selects) + + os.makedirs(outdir, exist_ok=True) + + # Generated headers + generated_with = """ +Generated with %s +""" % mypath + + # XML Files can't have "--" in them, so instead we use ~~ + xml_comment = """ +Generated with %s +""" % "\n".join(argv).replace('--', '~~') + + if not outfilename: + outfilename = name_mux.lower() + + model_xml_filename = '%s.model.xml' % outfilename + pbtype_xml_filename = '%s.pb_type.xml' % outfilename + sim_filename = '%s.sim.v' % outfilename + + # ------------------------------------------------------------------------ + # Work out the port and their names + # ------------------------------------------------------------------------ + + port_names = [] + for i in order: + if i == 'i': + if split_inputs: + port_names.extend( + mux_lib.ModulePort( + mux_lib.MuxPinType.INPUT, name_inputs[j], 1, + '[%i]' % j, data_width + ) for j in range(width) + ) + else: + # verilog range bounds are inclusive and convention is + # [<width-1>:0] + port_names.append( + mux_lib.ModulePort( + mux_lib.MuxPinType.INPUT, name_input, width, + '[%i:0]' % (width - 1) + ) + ) + elif i == 's': + if split_selects: + port_names.extend( + mux_lib.ModulePort( + mux_lib.MuxPinType.SELECT, name_selects[j], 1, + '[%i]' % j + ) for j in range(width_bits) + ) + else: + # verilog range bounds are inclusive and convention is + # [<width-1>:0] + assert name_select is not None + port_names.append( + mux_lib.ModulePort( + mux_lib.MuxPinType.SELECT, name_select, + width_bits, '[%i:0]' % (width_bits - 1) + ) + ) + elif i == 'o': + port_names.append( + mux_lib.ModulePort( + mux_lib.MuxPinType.OUTPUT, name_output, 1, '', + data_width + ) + ) + + # ------------------------------------------------------------------------ + # Generate the techmap Verilog module + # ------------------------------------------------------------------------ + techmap_filename = '%s.techmap.v' % outfilename + techmap_pathname = os.path.join(outdir, techmap_filename) + if datatype == 'routing': + with open(techmap_pathname, "w") as f: + module_args = [] + for port in port_names: + if (datatype == 'routing' and + port.pin_type == mux_lib.MuxPinType.SELECT): + continue + module_args.append(port.name) + + f.write("/* ") + f.write("\n * ".join(generated_with.splitlines())) + f.write("\n */\n\n") + + f.write( + "module %s(%s);\n" % + (name_mux.upper(), ", ".join(module_args)) + ) + f.write('\tparameter MODE = "";\n') + + modes = [ + port.name + for port in port_names + if port.pin_type in (mux_lib.MuxPinType.INPUT, ) + ] + + outputs = [ + port.name + for port in port_names + if port.pin_type in (mux_lib.MuxPinType.OUTPUT, ) + ] + for port in port_names: + if port.pin_type != mux_lib.MuxPinType.SELECT: + f.write(port.getDefinition()) + + f.write('\tgenerate\n') + for i, mode in enumerate(modes): + f.write( + '\t\t%s ( MODE == "%s" )\n' % + (('if', 'else if')[i > 0], mode) + ) + f.write('\t\tbegin\n') + f.write('\t\t\tassign %s = %s;\n' % (outputs[0], mode)) + f.write('\t\tend\n') + f.write('\t\telse\n') + f.write('\t\tbegin\n') + f.write('\t\t\twire _TECHMAP_FAIL_ = 1;\n') + f.write('\t\tend\n') + f.write("\tendgenerate\n") + f.write("endmodule") + + # ------------------------------------------------------------------------ + # Generate the sim.v Verilog module + # ------------------------------------------------------------------------ + sim_pathname = os.path.join(outdir, sim_filename) + with open(sim_pathname, "w") as f: + module_args = [] + for port in port_names: + if (datatype == 'routing' and + port.pin_type == mux_lib.MuxPinType.SELECT): + continue + module_args.append(port.name) + + mux_class = {'logic': 'mux', 'routing': 'routing'}[datatype] + + f.write("/* ") + f.write("\n * ".join(generated_with.splitlines())) + f.write("\n */\n\n") + f.write("`default_nettype none\n") + f.write("\n") + + if datatype != 'routing': + f.write( + '`include "%s/%s/%smux%i/%smux%i.sim.v"\n' % ( + mux_dir, + 'logic', + '', + width, + '', + width, + ) + ) + f.write("\n") + f.write('(* CLASS="%s" *)\n' % mux_class) + + if datatype == 'routing': + modes = [ + port.name + for port in port_names + if port.pin_type in (mux_lib.MuxPinType.INPUT, ) + ] + outputs = [ + port.name + for port in port_names + if port.pin_type in (mux_lib.MuxPinType.OUTPUT, ) + ] + assert len( + outputs + ) == 1, "FIXME: routing muxes should only have 1 output." + f.write('(* MODES="%s" *)\n' % "; ".join(modes)) + + f.write('(* whitebox *)\n') + f.write( + "module %s(%s);\n" % + (name_mux.upper(), ", ".join(module_args)) + ) + previous_type = None + for port in port_names: + if previous_type != port.pin_type: + f.write("\n") + previous_type = port.pin_type + if (datatype == 'routing' and + port.pin_type == mux_lib.MuxPinType.SELECT): + f.write('\tparameter MODE = "";\n') + else: + f.write(port.getDefinition()) + + f.write("\n") + if data_width > 1: + f.write('\tgenvar\tii;\n') + f.write( + '\tfor(ii=0; ii<%d; ii++) begin: bitmux\n' % (data_width) + ) + + if datatype == 'logic': + f.write('\tMUX%s mux (\n' % width) + for i in range(0, width): + j = 0 + for port in port_names: + if port.pin_type != mux_lib.MuxPinType.INPUT: + continue + if j + port.width <= i: + j += port.width + continue + break + + if port.width == 1: + if data_width > 1: + f.write('\t\t.I%i(%s[ii]),\n' % (i, port.name)) + else: + f.write('\t\t.I%i(%s),\n' % (i, port.name)) + else: + f.write('\t\t.I%i(%s[%i]),\n' % (i, port.name, i - j)) + + for i in range(0, width_bits): + j = 0 + for port in port_names: + if port.pin_type != mux_lib.MuxPinType.SELECT: + continue + if j + port.width < i: + j += port.width + continue + break + + if port.width == 1: + f.write('\t\t.S%i(%s),\n' % (i, port.name)) + else: + f.write('\t\t.S%i(%s[%i]),\n' % (i, port.name, i - j)) + + for port in port_names: + if port.pin_type != mux_lib.MuxPinType.OUTPUT: + continue + break + assert_eq(port.width, 1) + if data_width > 1: + f.write('\t\t.O(%s[ii])\n\t);\n' % port.name) + else: + f.write('\t\t.O(%s)\n\t);\n' % port.name) + + elif datatype == 'routing': + f.write('\tgenerate\n') + for i, mode in enumerate(modes): + f.write( + '\t\t%s ( MODE == "%s" )\n' % + (('if', 'else if')[i > 0], mode) + ) + f.write('\t\tbegin:SELECT_%s\n' % mode) + f.write('\t\t\tassign %s = %s;\n' % (outputs[0], mode)) + f.write('\t\tend\n') + f.write('\t\telse\n') + f.write('\t\tbegin\n') + f.write( + ('\t\t\t//$error("%s: Invalid routing value %%s ' + + '(options are: %s)", MODE);\n') + % (name_mux, ", ".join(modes)) + ) + f.write('\t\tend\n') + f.write('\tendgenerate\n') + + if data_width > 1: + f.write('end\n') + + f.write('endmodule\n') + + output_block(sim_filename, open(sim_pathname).read()) + + if datatype == 'logic': + subckt = subckt or name_mux + assert subckt + elif datatype == 'routing': + assert subckt is None + subckt = None + + # ------------------------------------------------------------------------ + # Generate the Model XML form. + # ------------------------------------------------------------------------ + def xml_comment_indent(n, s): + return ("\n" + " " * n).join(s.splitlines() + [""]) + + if datatype == 'logic': + models_xml = ET.Element('models') + models_xml.append(ET.Comment(xml_comment_indent(4, xml_comment))) + + model_xml = ET.SubElement(models_xml, 'model', {'name': subckt}) + + input_ports = ET.SubElement(model_xml, 'input_ports') + output_ports = ET.SubElement(model_xml, 'output_ports') + for port in port_names: + if port.pin_type in (mux_lib.MuxPinType.INPUT, + mux_lib.MuxPinType.SELECT): + ET.SubElement( + input_ports, 'port', { + 'name': + port.name, + 'combinational_sink_ports': + ' '.join( + port.name for port in port_names if + port.pin_type in (mux_lib.MuxPinType.OUTPUT, ) + ), + } + ) + elif port.pin_type in (mux_lib.MuxPinType.OUTPUT, ): + ET.SubElement(output_ports, 'port', {'name': port.name}) + + models_str = ET.tostring(models_xml, pretty_print=True).decode('utf-8') + else: + models_str = "<models><!-- No models for routing elements.--></models>" + + output_block(model_xml_filename, models_str) + with open(os.path.join(outdir, model_xml_filename), "w") as f: + f.write(models_str) + + # ------------------------------------------------------------------------ + # Generate the pb_type XML form. + # ------------------------------------------------------------------------ + + pb_type_xml = mux_lib.pb_type_xml( + mux_lib.MuxType[datatype.upper()], + name_mux, + port_names, + subckt=subckt, + num_pb=num_pb, + comment=xml_comment_indent(4, xml_comment), + ) + + pb_type_str = ET.tostring(pb_type_xml, pretty_print=True).decode('utf-8') + output_block(pbtype_xml_filename, pb_type_str) + with open(os.path.join(outdir, pbtype_xml_filename), "w") as f: + f.write(pb_type_str) + + print("Generated mux {} in {}".format(name_mux, outdir)) + + +def main(argv): + args = parser.parse_args() + + mux_gen(argv=argv, + width=args.width, + data_width=args.data_width, + datatype=args.type, + split_inputs=args.split_inputs, + split_selects=args.split_selects, + name_mux=args.name_mux, + name_input=args.name_input, + name_inputs=args.name_inputs, + name_output=args.name_output, + name_select=args.name_select, + name_selects=args.name_selects, + order=args.order, + outdir=args.outdir, + outfilename=args.outfilename, + comment=args.comment, + num_pb=args.num_pb, + subckt=args.subckt, + verbose=args.verbose, + ) + + +if __name__ == "__main__": + sys.exit(main(sys.argv))