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))