import unittest
import random
import io
import sys
import six
import pyrtl
from pyrtl.importexport import _VerilogSanitizer
from pyrtl.rtllib import testingutils as utils


full_adder_blif = """\
# Generated by Yosys 0.3.0+ (git sha1 7e758d5, clang 3.4-1ubuntu3 -fPIC -Os)
.model full_adder
.inputs x y cin
.outputs sum cout
.names $false
.names $true
1
.names y $not$FA.v:12$3_Y
0 1
.names x $not$FA.v:11$1_Y
0 1
.names cin $not$FA.v:15$6_Y
0 1
.names ind3 ind4 sum
1- 1
-1 1
.names $not$FA.v:15$6_Y ind2 ind3
11 1
.names x $not$FA.v:12$3_Y ind1
11 1
.names ind2 $not$FA.v:16$8_Y
0 1
.names cin $not$FA.v:16$8_Y ind4
11 1
.names x y $and$FA.v:19$11_Y
11 1
.names ind0 ind1 ind2
1- 1
-1 1
.names cin ind2 $and$FA.v:19$12_Y
11 1
.names $and$FA.v:19$11_Y $and$FA.v:19$12_Y cout
1- 1
-1 1
.names $not$FA.v:11$1_Y y ind0
11 1
.end
"""

state_machine_blif = """\
# Generated by Yosys 0.5+     420 (git sha1 1d62f87, clang 7.0.2 -fPIC -Os)

.model statem
.inputs clk in reset
.outputs out[0] out[1] out[2] out[3]
.names $false
.names $true
1
.names $undef
.names in state[2] $abc$129$n11_1
11 1
.names $abc$129$n11_1 state[3] $auto$fsm_map.cc:238:map_fsm$30[0]
1- 1
-1 1
.names state[2] $abc$129$n13
0 1
.names state[0] $abc$129$n14_1
0 1
.names state[2] state[1] $abc$129$n15
00 1
.names $abc$129$n15 $abc$129$n14_1 $abc$129$n13 out[0]
-00 1
0-0 1
.names state[1] $abc$129$n17
0 1
.names $abc$129$n15 $abc$129$n14_1 $abc$129$n17 out[1]
-00 1
0-0 1
.names $abc$129$n15 $abc$129$n14_1 out[2]
11 1
.names in $abc$129$n13 $auto$fsm_map.cc:118:implement_pattern_cache$38
00 1
# .subckt $_DFF_PP1_ C=clk D=$auto$fsm_map.cc:238:map_fsm$30[0] Q=state[0] R=reset
# .subckt $_DFF_PP0_ C=clk D=$auto$fsm_map.cc:118:implement_pattern_cache$38 Q=state[1] R=reset
# .subckt $_DFF_PP0_ C=clk D=state[0] Q=state[2] R=reset
# .subckt $_DFF_PP0_ C=clk D=state[1] Q=state[3] R=reset
.names $false out[3]
1 1
.end
"""

# Manually set the .latch's init values from 2 to arbitrary non-1 numbers, for testing.
# Should result in the same logic, but allows for testing the parser.
counter4bit_blif = """\
# Generated by Yosys 0.9 (git sha1 UNKNOWN, clang 11.0.0 -fPIC -Os)

.model counter
.inputs clk rst en
.outputs count[0] count[1] count[2] count[3]
.names $false
.names $true
1
.names $undef
.names count[0] $add$counter.v:10$2_Y[0] en $procmux$3_Y[0]
1-0 1
-11 1
.names count[1] $add$counter.v:10$2_Y[1] en $procmux$3_Y[1]
1-0 1
-11 1
.names count[2] $add$counter.v:10$2_Y[2] en $procmux$3_Y[2]
1-0 1
-11 1
.names count[3] $add$counter.v:10$2_Y[3] en $procmux$3_Y[3]
1-0 1
-11 1
.names $procmux$3_Y[0] $false rst $0\count[3:0][0]
1-0 1
-11 1
.names $procmux$3_Y[1] $false rst $0\count[3:0][1]
1-0 1
-11 1
.names $procmux$3_Y[2] $false rst $0\count[3:0][2]
1-0 1
-11 1
.names $procmux$3_Y[3] $false rst $0\count[3:0][3]
1-0 1
-11 1
.latch $0\count[3:0][0] count[0] re clk 2
.latch $0\count[3:0][1] count[1] re clk 0
.latch $0\count[3:0][2] count[2] re clk 3
.latch $0\count[3:0][3] count[3] re clk
.names count[1] count[0] $techmap$add$counter.v:10$2.$auto$alumacc.cc:474:replace_alu$53.lcu.g[1]
11 1
.names count[2] $techmap$add$counter.v:10$2.$auto$alumacc.cc:474:replace_alu$53.lcu.g[1] $techmap$add$counter.v:10$2.$auto$alumacc.cc:474:replace_alu$53.lcu.g[2]
11 1
.names count[1] count[0] $add$counter.v:10$2_Y[1]
10 1
01 1
.names count[2] $techmap$add$counter.v:10$2.$auto$alumacc.cc:474:replace_alu$53.lcu.g[1] $add$counter.v:10$2_Y[2]
10 1
01 1
.names count[3] $techmap$add$counter.v:10$2.$auto$alumacc.cc:474:replace_alu$53.lcu.g[2] $add$counter.v:10$2_Y[3]
10 1
01 1
.names count[0] $true $add$counter.v:10$2_Y[0]
10 1
01 1
.names count[0] $techmap$add$counter.v:10$2.$auto$alumacc.cc:474:replace_alu$53.lcu.g[0]
1 1
.end
"""  # noqa

blif_with_output_as_arg = """
# Generated by Yosys 0.9+2406 (git sha1 aee43936, clang 11.0.3 -fPIC -Os)

.model Top
.inputs clk in[0] in[1]
.outputs out
.names $false
.names $true
1
.names $undef
.names out $techmap$add$test.v:9$6.$auto$alumacc.cc:485:replace_alu$60.X[0]
0 1
.names in[0] $not$test.v:6$1_Y[0]
0 1
.names r[0] $not$test.v:6$3_Y[0]
0 1
.latch $techmap$add$test.v:9$6.$auto$alumacc.cc:485:replace_alu$60.X[0] r[0] re clk 2
.names $not$test.v:6$1_Y[0] $not$test.v:6$3_Y[0] out
10 1
01 1
.names $true $not$test.v:6$3_Y[1]
1 1
.names $false $techmap$add$test.v:9$6.$auto$alumacc.cc:485:replace_alu$60.X[1]
1 1
.names $false $techmap$add$test.v:9$6.$auto$alumacc.cc:485:replace_alu$60.X[2]
1 1
.end
"""  # noqa

simple_unmerged_io_blif = """
# Generated by Yosys 0.9+2406 (git sha1 aee43936, clang 11.0.3 -fPIC -Os)

.model top
.inputs a[0] a[1] a[2] a[3]
.outputs b[0] b[1]
.names $false
.names $true
1
.names $undef
.names a[0] b[0]
1 1
.names a[2] b[1]
1 1
.end
"""  # noqa

four_bit_adder_multi_module = """
# Generated by Yosys 0.9+2406 (git sha1 aee43936, clang 11.0.3 -fPIC -Os)

.model four_bit_adder
.inputs a[0] a[1] a[2] a[3] b[0] b[1] b[2] b[3] cin
.outputs s[0] s[1] s[2] s[3] cout
.names $false
.names $true
1
.names $undef
.subckt full_adder a=a[0] b=b[0] cin=cin cout=cout0 s=s[0]
.subckt full_adder a=a[1] b=b[1] cin=cout0 cout=cout1 s=s[1]
.subckt full_adder a=a[2] b=b[2] cin=cout1 cout=cout2 s=s[2]
.subckt full_adder a=a[3] b=b[3] cin=cout2 cout=cout s=s[3]
.end

.model full_adder
.inputs a b cin
.outputs s cout
.names $false
.names $true
1
.names $undef
.names a b $techmap$add$four_bit_adder_subsub.v:34$1.$auto$alumacc.cc:485:replace_alu$46.X[0]
10 1
01 1
.names a b $techmap$add$four_bit_adder_subsub.v:34$1.$auto$alumacc.cc:485:replace_alu$46.CO[0]
11 1
.names $techmap$add$four_bit_adder_subsub.v:34$1.$auto$alumacc.cc:485:replace_alu$46.CO[0] $techmap$add$four_bit_adder_subsub.v:34$2.$auto$alumacc.cc:485:replace_alu$49.CO[0] x
10 1
01 1
.names cin $techmap$add$four_bit_adder_subsub.v:34$1.$auto$alumacc.cc:485:replace_alu$46.X[0] s
10 1
01 1
.names cin $techmap$add$four_bit_adder_subsub.v:34$1.$auto$alumacc.cc:485:replace_alu$46.X[0] $techmap$add$four_bit_adder_subsub.v:34$2.$auto$alumacc.cc:485:replace_alu$49.CO[0]
11 1
.subckt pass_through x=x y=y
.subckt pass_through x=y y=cout
.names $false $techmap$add$four_bit_adder_subsub.v:34$1.$auto$alumacc.cc:485:replace_alu$46.CO[1]
1 1
.names $false $techmap$add$four_bit_adder_subsub.v:34$1.$auto$alumacc.cc:485:replace_alu$46.X[1]
1 1
.end

.model pass_through
.inputs x
.outputs y
.names $false
.names $true
1
.names $undef
.names x y
1 1
.end
"""  # noqa

clock_passing_blif = """
# Generated by Yosys 0.9+2406 (git sha1 aee43936, clang 11.0.3 -fPIC -Os)

.model top
.inputs clk a[0] a[1] a[2] a[3] b[0] b[1] b[2] b[3]
.outputs c[0] c[1] c[2] c[3]
.names $false
.names $true
1
.names $undef
.names b[0] w2[0] c[0]
11 1
.names b[1] w2[1] c[1]
11 1
.names b[2] w2[2] c[2]
11 1
.names b[3] w2[3] c[3]
11 1
.subckt passthrough a[0]=a[0] a[1]=a[1] a[2]=a[2] a[3]=a[3] c[0]=w1[0] c[1]=w1[1] c[2]=w1[2] c[3]=w1[3] pclk=clk
.subckt passthrough a[0]=w1[0] a[1]=w1[1] a[2]=w1[2] a[3]=w1[3] c[0]=w2[0] c[1]=w2[1] c[2]=w2[2] c[3]=w2[3] pclk=clk
.end

.model my_4bit_dff
.inputs mclk din[0] din[1] din[2] din[3] en
.outputs q[0] q[1] q[2] q[3]
.names $false
.names $true
1
.names $undef
.names q[0] din[0] en $0\q[3:0][0]
1-0 1
-11 1
.names q[1] din[1] en $0\q[3:0][1]
1-0 1
-11 1
.names q[2] din[2] en $0\q[3:0][2]
1-0 1
-11 1
.names q[3] din[3] en $0\q[3:0][3]
1-0 1
-11 1
.latch $0\q[3:0][0] q[0] re mclk 2
.latch $0\q[3:0][1] q[1] re mclk 2
.latch $0\q[3:0][2] q[2] re mclk 2
.latch $0\q[3:0][3] q[3] re mclk 2
.end

.model passthrough
.inputs a[0] a[1] a[2] a[3] pclk
.outputs c[0] c[1] c[2] c[3]
.names $false
.names $true
1
.names $undef
.subckt my_4bit_dff din[0]=a[0] din[1]=a[1] din[2]=a[2] din[3]=a[3] en=$true mclk=pclk q[0]=c[0] q[1]=c[1] q[2]=c[2] q[3]=c[3]
.end
"""  # noqa


class TestInputFromBlif(unittest.TestCase):
    def setUp(self):
        pyrtl.reset_working_block()

    def test_combo_blif_input_has_correct_io_interface(self):
        pyrtl.input_from_blif(full_adder_blif)
        x, y, cin, sumw, cout, bad = [
            pyrtl.working_block().get_wirevector_by_name(s)
            for s in ['x', 'y', 'cin', 'sum', 'cout', 'bad']
        ]
        self.assertIsNotNone(x)
        self.assertIsNotNone(y)
        self.assertIsNotNone(cin)
        self.assertIsNotNone(sumw)
        self.assertIsNotNone(cout)
        self.assertIsNone(bad)
        self.assertEqual(len(x), 1)
        self.assertEqual(len(y), 1)
        self.assertEqual(len(cin), 1)
        self.assertEqual(len(sumw), 1)
        self.assertEqual(len(cout), 1)
        io_input = pyrtl.working_block().wirevector_subset(pyrtl.Input)
        self.assertIn(x, io_input)
        self.assertIn(y, io_input)
        self.assertIn(cin, io_input)
        io_output = pyrtl.working_block().wirevector_subset(pyrtl.Output)
        self.assertIn(sumw, io_output)
        self.assertIn(cout, io_output)

    def test_sequential_blif_input_has_correct_io_interface(self):
        pyrtl.input_from_blif(state_machine_blif)
        inw, reset, out = [
            pyrtl.working_block().get_wirevector_by_name(s)
            for s in ['in', 'reset', 'out']
        ]
        self.assertIsNotNone(inw)
        self.assertIsNotNone(reset)
        self.assertIsNotNone(out)
        self.assertEqual(len(inw), 1)
        self.assertEqual(len(reset), 1)
        self.assertEqual(len(out), 4)
        io_input = pyrtl.working_block().wirevector_subset(pyrtl.Input)
        self.assertIn(inw, io_input)
        self.assertIn(reset, io_input)
        io_output = pyrtl.working_block().wirevector_subset(pyrtl.Output)
        self.assertIn(out, io_output)

    def test_sequential_blif_input_has_correct_io_interface_counter(self):
        pyrtl.input_from_blif(counter4bit_blif)
        rst, en, count = [
            pyrtl.working_block().get_wirevector_by_name(s)
            for s in ['rst', 'en', 'count']
        ]
        self.assertIsNotNone(rst)
        self.assertIsNotNone(en)
        self.assertIsNotNone(count)
        self.assertEqual(len(rst), 1)
        self.assertEqual(len(en), 1)
        self.assertEqual(len(count), 4)
        io_input = pyrtl.working_block().wirevector_subset(pyrtl.Input)
        self.assertIn(rst, io_input)
        self.assertIn(en, io_input)
        io_output = pyrtl.working_block().wirevector_subset(pyrtl.Output)
        self.assertIn(count, io_output)

    def test_correct_interface_with_unmerged_io(self):
        pyrtl.input_from_blif(simple_unmerged_io_blif, merge_io_vectors=False)
        a0, a1, a2, a3, b0, b1 = [
            pyrtl.working_block().get_wirevector_by_name(s)
            for s in ['a[0]', 'a[1]', 'a[2]', 'a[3]', 'b[0]', 'b[1]']
        ]
        self.assertEqual(len(a0), 1)
        self.assertEqual(len(a1), 1)
        self.assertEqual(len(a2), 1)
        self.assertEqual(len(a3), 1)
        self.assertEqual(len(b0), 1)
        self.assertEqual(len(b1), 1)
        self.assertEqual({a0, a1, a2, a3}, pyrtl.working_block().wirevector_subset(pyrtl.Input))
        self.assertEqual({b0, b1}, pyrtl.working_block().wirevector_subset(pyrtl.Output))

    def test_blif_input_simulates_correctly_with_merged_outputs(self):
        # The 'counter_blif' string contains a model of a standard 4-bit synchronous-reset
        # counter with enable. In particular, the model has 4 1-bit outputs named "count[0]",
        # "count[1]", "count[2]", and "count[3]". The internal PyRTL representation will by
        # default convert these related 1-bit wires into a single 4-bit wire called "count".
        # This test simulates the design and, among other things, ensures that this output
        # wire conversion occurred correctly.
        pyrtl.input_from_blif(counter4bit_blif)
        io_vectors = pyrtl.working_block().wirevector_subset((pyrtl.Input, pyrtl.Output))
        sim_trace = pyrtl.SimulationTrace(wires_to_track=io_vectors)
        sim = pyrtl.Simulation(sim_trace)
        inputs = {
            'rst': [1] + [0] * 20,
            'en': [1] + [1] * 20,
        }
        expected = {
            'count': [0] + list(range(0, 16)) + list(range(0, 4))
        }
        sim.step_multiple(inputs, expected)

        correct_output = ("  --- Values in base 10 ---\n"
                          "count  0  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15  0  1  2  3\n"
                          "en     1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1\n"
                          "rst    1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0\n")
        output = six.StringIO()
        sim_trace.print_trace(output)
        self.assertEqual(output.getvalue(), correct_output)

    def test_blif_input_simulates_correctly_with_unmerged_outputs(self):
        pyrtl.input_from_blif(counter4bit_blif, merge_io_vectors=False)
        count0, count1, count2, count3 = [
            pyrtl.working_block().get_wirevector_by_name(s)
            for s in ['count[0]', 'count[1]', 'count[2]', 'count[3]']
        ]
        self.assertEqual(len(count0), 1)
        self.assertEqual(len(count1), 1)
        self.assertEqual(len(count2), 1)
        self.assertEqual(len(count3), 1)
        io_vectors = pyrtl.working_block().wirevector_subset((pyrtl.Input, pyrtl.Output))
        sim_trace = pyrtl.SimulationTrace(wires_to_track=io_vectors)
        sim = pyrtl.Simulation(sim_trace)
        inputs = {
            'rst': [1] + [0] * 20,
            'en': [1] + [1] * 20,
        }
        expected_merged = [0] + list(range(0, 16)) + list(range(0, 4))

        expected = {
            'count[0]': [n & 0b0001 for n in expected_merged],
            'count[1]': [(n & 0b0010) >> 1 for n in expected_merged],
            'count[2]': [(n & 0b0100) >> 2 for n in expected_merged],
            'count[3]': [(n & 0b1000) >> 3 for n in expected_merged],
        }
        sim.step_multiple(inputs, expected)

        correct_output = ("     --- Values in base 10 ---\n"
                          "count[0] 0 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1\n"
                          "count[1] 0 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1\n"
                          "count[2] 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0\n"
                          "count[3] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0\n"
                          "en       1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1\n"
                          "rst      1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n")
        output = six.StringIO()
        sim_trace.print_trace(output)
        self.assertEqual(output.getvalue(), correct_output)

    def test_blif_with_output_as_arg(self):
        pyrtl.input_from_blif(blif_with_output_as_arg)
        inw, outw = [
            pyrtl.working_block().get_wirevector_by_name(s)
            for s in ['in', 'out']
        ]
        self.assertIsNotNone(inw)
        self.assertIsNotNone(outw)
        self.assertEqual(len(inw), 2)
        self.assertEqual(len(outw), 1)
        io_input = pyrtl.working_block().wirevector_subset(pyrtl.Input)
        self.assertIn(inw, io_input)
        io_output = pyrtl.working_block().wirevector_subset(pyrtl.Output)
        self.assertIn(outw, io_output)

    def test_blif_with_multiple_modules_merged_io(self):
        pyrtl.input_from_blif(four_bit_adder_multi_module)
        a, b, cin, s, cout = [
            pyrtl.working_block().get_wirevector_by_name(s)
            for s in ['a', 'b', 'cin', 's', 'cout']
        ]
        io_input = pyrtl.working_block().wirevector_subset(pyrtl.Input)
        self.assertIn(a, io_input)
        self.assertIn(b, io_input)
        self.assertIn(cin, io_input)
        io_output = pyrtl.working_block().wirevector_subset(pyrtl.Output)
        self.assertIn(s, io_output)
        self.assertIn(cout, io_output)

        self.assertEqual(len(a), 4)
        self.assertEqual(len(b), 4)
        self.assertEqual(len(cin), 1)
        self.assertEqual(len(s), 4)
        self.assertEqual(len(cout), 1)

        avals = list(range(0, 16)) * 2
        bvals = list(range(0, 16)) * 2
        cinvals = [0] * 16 + [1] * 16

        res = utils.sim_and_ret_outws([a, b, cin], [avals, bvals, cinvals])
        self.assertEqual(
            res[s.name],
            [(av + bv + cinv) & 0xf for av, bv, cinv in zip(avals, bvals, cinvals)]
        )
        self.assertEqual(
            res[cout.name],
            [((av + bv + cinv) & 0x10) >> 4 for av, bv, cinv in zip(avals, bvals, cinvals)]
        )

    def test_blif_with_multiple_modules_unmerged_io(self):
        pyrtl.input_from_blif(four_bit_adder_multi_module, merge_io_vectors=False)
        a0, a1, a2, a3, b0, b1, b2, b3, cin, s0, s1, s2, s3, cout = [
            pyrtl.working_block().get_wirevector_by_name(s)
            for s in ['a[0]', 'a[1]', 'a[2]', 'a[3]',
                      'b[0]', 'b[1]', 'b[2]', 'b[3]',
                      'cin',
                      's[0]', 's[1]', 's[2]', 's[3]',
                      'cout']
        ]
        io_input = pyrtl.working_block().wirevector_subset(pyrtl.Input)
        self.assertEqual({a0, a1, a2, a3, b0, b1, b2, b3, cin}, io_input)
        io_output = pyrtl.working_block().wirevector_subset(pyrtl.Output)
        self.assertEqual({s0, s1, s2, s3, cout}, io_output)

        self.assertEqual(len(a0), 1)
        self.assertEqual(len(a1), 1)
        self.assertEqual(len(a2), 1)
        self.assertEqual(len(a3), 1)
        self.assertEqual(len(b0), 1)
        self.assertEqual(len(b1), 1)
        self.assertEqual(len(b2), 1)
        self.assertEqual(len(b3), 1)
        self.assertEqual(len(cin), 1)
        self.assertEqual(len(s0), 1)
        self.assertEqual(len(s1), 1)
        self.assertEqual(len(s2), 1)
        self.assertEqual(len(s3), 1)
        self.assertEqual(len(cout), 1)

        avals = list(range(0, 16)) * 2
        bvals = list(range(0, 16)) * 2
        cinvals = [0] * 16 + [1] * 16

        sim = pyrtl.Simulation()
        for a in range(0, 16):
            for b in range(0, 16):
                for cin in range(0, 1):
                    sim.step({
                        'a[0]': a & 0x1,
                        'a[1]': (a & 0x2) >> 1,
                        'a[2]': (a & 0x4) >> 2,
                        'a[3]': (a & 0x8) >> 3,
                        'b[0]': b & 0x1,
                        'b[1]': (b & 0x2) >> 1,
                        'b[2]': (b & 0x4) >> 2,
                        'b[3]': (b & 0x8) >> 3,
                        'cin': cin
                    })
                    res = a + b + cin
                    self.assertEqual(sim.inspect('s[0]'), res & 0x1)
                    self.assertEqual(sim.inspect('s[1]'), (res & 0x2) >> 1)
                    self.assertEqual(sim.inspect('s[2]'), (res & 0x4) >> 2)
                    self.assertEqual(sim.inspect('s[3]'), (res & 0x8) >> 3)
                    self.assertEqual(sim.inspect('cout'), (res & 0x10) >> 4)

    def test_blif_with_clock_passing(self):
        pyrtl.input_from_blif(clock_passing_blif)
        a, b, c = [
            pyrtl.working_block().get_wirevector_by_name(s) for s in ['a', 'b', 'c']
        ]
        io_input = pyrtl.working_block().wirevector_subset(pyrtl.Input)
        self.assertEqual({a, b}, io_input)
        io_output = pyrtl.working_block().wirevector_subset(pyrtl.Output)
        self.assertEqual({c}, io_output)

        sim = pyrtl.Simulation()
        sim.step_multiple({
            'a': [0, 3, 1, 1, 1, 1, 0],
            'b': [1, 7, 9, 9, 9, 9, 0],
        })
        cvals = sim.tracer.trace[c.name]
        self.assertEqual(cvals, [0, 0, 0, 1, 1, 1, 0])

    def test_blif_error_zeroes_in_offset(self):
        zeroes_in_offset = """\
        .model Top
        .inputs clk in[0] in[1]
        .outputs out
        .names in[0] in[1] out
        10 0
        .end
        """

        with six.assertRaisesRegex(self, pyrtl.PyrtlError, "Off-set found"):
            pyrtl.input_from_blif(zeroes_in_offset)

    def test_blif_error_bad_coverset(self):
        bad_coverset = """\
        .model Top
        .inputs clk in[0] in[1]
        .outputs out
        .names in[0] in[1] out
        10 1 1
        .end
        """
        with six.assertRaisesRegex(self, pyrtl.PyrtlError, "malformed cover set"):
            pyrtl.input_from_blif(bad_coverset)

    def test_blif_not_gate_correct(self):
        blif = """\
        .model Top
        .inputs a
        .outputs o
        .names a o
        0 1
        .end
        """
        pyrtl.input_from_blif(blif)
        block = pyrtl.working_block()
        self.assertEqual(len(block.logic_subset('~')), 1)
        sim = pyrtl.Simulation()
        sim.step_multiple({
            'a': '01',
        })
        self.assertEqual(sim.tracer.trace['o'], [1, 0])

    def test_blif_and_gate_correct(self):
        blif = """\
        .model Top
        .inputs a b
        .outputs o
        .names a b o
        11 1
        .end
        """
        pyrtl.input_from_blif(blif)
        block = pyrtl.working_block()
        self.assertEqual(len(block.logic_subset('&')), 1)
        sim = pyrtl.Simulation()
        sim.step_multiple({
            'a': '0011',
            'b': '0101',
        })
        self.assertEqual(sim.tracer.trace['o'], [0, 0, 0, 1])

    def test_blif_or_gate_correct(self):
        blif = """\
        .model Top
        .inputs a b
        .outputs o
        .names a b o
        1- 1
        -1 1
        .end
        """
        pyrtl.input_from_blif(blif)
        block = pyrtl.working_block()
        self.assertEqual(len(block.logic_subset('|')), 1)
        sim = pyrtl.Simulation()
        sim.step_multiple({
            'a': '0011',
            'b': '0101',
        })
        self.assertEqual(sim.tracer.trace['o'], [0, 1, 1, 1])

    def test_blif_nand_gate_to_primitives_correct(self):
        # This tests that there should be no NAND gates generated during BLIF import;
        # they should be converted to AND+NOT.
        blif = """\
        .model Top
        .inputs a b
        .outputs o
        .names a b o
        0- 1
        -0 1
        .end
        """
        pyrtl.input_from_blif(blif)
        block = pyrtl.working_block()
        self.assertEqual(len(block.logic_subset('n')), 0)
        self.assertEqual(len(block.logic_subset('&')), 1)
        self.assertEqual(len(block.logic_subset('~')), 1)
        sim = pyrtl.Simulation()
        sim.step_multiple({
            'a': '0011',
            'b': '0101',
        })
        self.assertEqual(sim.tracer.trace['o'], [1, 1, 1, 0])

    def test_blif_xor_gate_correct(self):
        blif = """\
        .model Top
        .inputs a b
        .outputs o
        .names a b o
        10 1
        01 1
        .end
        """
        pyrtl.input_from_blif(blif)
        block = pyrtl.working_block()
        self.assertEqual(len(block.logic_subset('^')), 1)
        sim = pyrtl.Simulation()
        sim.step_multiple({
            'a': '0011',
            'b': '0101',
        })
        self.assertEqual(sim.tracer.trace['o'], [0, 1, 1, 0])

    def test_blif_nor_gate_correct(self):
        # This is a non-primitive, so tests the last branch of cover list parsing
        blif = """\
        .model Top
        .inputs a b
        .outputs o
        .names a b o
        00 1
        .end
        """
        pyrtl.input_from_blif(blif)
        block = pyrtl.working_block()
        self.assertEqual(len(block.logic_subset('~')), 2)
        self.assertEqual(len(block.logic_subset('&')), 1)
        sim = pyrtl.Simulation()
        sim.step_multiple({
            'a': '0011',
            'b': '0101',
        })
        self.assertEqual(sim.tracer.trace['o'], [1, 0, 0, 0])


verilog_output_small = """\
// Generated automatically via PyRTL
// As one initial test of synthesis, map to FPGA with:
//   yosys -p "synth_xilinx -top toplevel" thisfile.v

module toplevel(clk, o);
    input clk;
    output[12:0] o;

    wire[3:0] const_0_12;
    wire[2:0] const_1_3;
    wire[5:0] k;
    wire[12:0] tmp0;

    // Combinational
    assign const_0_12 = 12;
    assign const_1_3 = 3;
    assign k = 38;
    assign o = tmp0;
    assign tmp0 = {const_0_12, const_1_3, k};

endmodule

"""


verilog_output_large = """\
// Generated automatically via PyRTL
// As one initial test of synthesis, map to FPGA with:
//   yosys -p "synth_xilinx -top toplevel" thisfile.v

module toplevel(clk, rst, a, o);
    input clk;
    input rst;
    input[3:0] a;
    output[5:0] o;

    reg[3:0] mem_0[3:0]; //z
    reg[3:0] mem_1[3:0]; //tmp0
    reg[3:0] mem_2[3:0]; //tmp1
    reg[3:0] mem_3[3:0]; //tmp2
    reg[3:0] mem_4[3:0]; //tmp3
    reg[3:0] mem_5[3:0]; //tmp4
    reg[3:0] mem_6[3:0]; //tmp5
    reg[3:0] mem_7[3:0]; //tmp6
    reg[3:0] mem_8[3:0]; //tmp7
    reg[3:0] mem_9[3:0]; //tmp8
    reg[3:0] mem_10[3:0]; //tmp9
    reg[3:0] mem_11[3:0]; //tmp10
    reg[3:0] mem_12[3:0]; //tmp11
    reg[3:0] r;
    reg[3:0] s;

    wire[1:0] const_0_0;
    wire[1:0] const_1_0;
    wire const_2_1;
    wire[1:0] const_3_1;
    wire const_4_1;
    wire const_5_0;
    wire[1:0] const_6_1;
    wire const_7_1;
    wire[1:0] const_8_0;
    wire[1:0] const_9_0;
    wire const_10_1;
    wire[1:0] const_11_1;
    wire const_12_1;
    wire const_13_0;
    wire[1:0] const_14_1;
    wire const_15_1;
    wire[1:0] const_16_0;
    wire[1:0] const_17_0;
    wire const_18_1;
    wire[1:0] const_19_1;
    wire const_20_1;
    wire const_21_0;
    wire[1:0] const_22_1;
    wire const_23_1;
    wire[1:0] const_24_0;
    wire[1:0] const_25_0;
    wire const_26_1;
    wire[1:0] const_27_1;
    wire const_28_1;
    wire const_29_0;
    wire[1:0] const_30_1;
    wire const_31_1;
    wire[1:0] const_32_0;
    wire[1:0] const_33_0;
    wire const_34_1;
    wire[1:0] const_35_1;
    wire const_36_1;
    wire const_37_0;
    wire[1:0] const_38_1;
    wire const_39_1;
    wire[1:0] const_40_0;
    wire[1:0] const_41_0;
    wire const_42_1;
    wire[1:0] const_43_1;
    wire const_44_1;
    wire const_45_0;
    wire[1:0] const_46_1;
    wire const_47_1;
    wire[1:0] const_48_0;
    wire[1:0] const_49_0;
    wire const_50_1;
    wire[1:0] const_51_1;
    wire const_52_1;
    wire const_53_0;
    wire[1:0] const_54_1;
    wire const_55_1;
    wire[1:0] const_56_0;
    wire[1:0] const_57_0;
    wire const_58_1;
    wire[1:0] const_59_1;
    wire const_60_1;
    wire const_61_0;
    wire[1:0] const_62_1;
    wire const_63_1;
    wire[1:0] const_64_0;
    wire[1:0] const_65_0;
    wire const_66_1;
    wire[1:0] const_67_1;
    wire const_68_1;
    wire const_69_0;
    wire[1:0] const_70_1;
    wire const_71_1;
    wire[1:0] const_72_0;
    wire[1:0] const_73_0;
    wire const_74_1;
    wire[1:0] const_75_1;
    wire const_76_1;
    wire const_77_0;
    wire[1:0] const_78_1;
    wire const_79_1;
    wire[1:0] const_80_0;
    wire[1:0] const_81_0;
    wire const_82_1;
    wire[1:0] const_83_1;
    wire const_84_1;
    wire const_85_0;
    wire[1:0] const_86_1;
    wire const_87_1;
    wire[1:0] const_88_0;
    wire[1:0] const_89_0;
    wire const_90_1;
    wire[1:0] const_91_1;
    wire const_92_1;
    wire const_93_0;
    wire[1:0] const_94_1;
    wire const_95_1;
    wire const_96_1;
    wire const_97_0;
    wire const_98_0;
    wire const_99_1;
    wire const_100_0;
    wire[1:0] const_101_0;
    wire[1:0] const_102_0;
    wire const_103_1;
    wire[3:0] const_104_9;
    wire[1:0] const_105_0;
    wire const_106_0;
    wire[1:0] const_107_0;
    wire const_108_0;
    wire[2:0] tmp12;
    wire[3:0] tmp13;
    wire[4:0] tmp14;
    wire[3:0] tmp15;
    wire[2:0] tmp16;
    wire[3:0] tmp17;
    wire[4:0] tmp18;
    wire[3:0] tmp19;
    wire[2:0] tmp20;
    wire[3:0] tmp21;
    wire[4:0] tmp22;
    wire[3:0] tmp23;
    wire[2:0] tmp24;
    wire[3:0] tmp25;
    wire[4:0] tmp26;
    wire[3:0] tmp27;
    wire[2:0] tmp28;
    wire[3:0] tmp29;
    wire[4:0] tmp30;
    wire[3:0] tmp31;
    wire[2:0] tmp32;
    wire[3:0] tmp33;
    wire[4:0] tmp34;
    wire[3:0] tmp35;
    wire[2:0] tmp36;
    wire[3:0] tmp37;
    wire[4:0] tmp38;
    wire[3:0] tmp39;
    wire[2:0] tmp40;
    wire[3:0] tmp41;
    wire[4:0] tmp42;
    wire[3:0] tmp43;
    wire[2:0] tmp44;
    wire[3:0] tmp45;
    wire[4:0] tmp46;
    wire[3:0] tmp47;
    wire[2:0] tmp48;
    wire[3:0] tmp49;
    wire[4:0] tmp50;
    wire[3:0] tmp51;
    wire[2:0] tmp52;
    wire[3:0] tmp53;
    wire[4:0] tmp54;
    wire[3:0] tmp55;
    wire[2:0] tmp56;
    wire[3:0] tmp57;
    wire[4:0] tmp58;
    wire[3:0] tmp59;
    wire[4:0] tmp60;
    wire[3:0] tmp61;
    wire[4:0] tmp62;
    wire[5:0] tmp63;
    wire[1:0] tmp64;
    wire[5:0] tmp65;
    wire[6:0] tmp66;
    wire[3:0] tmp67;
    wire[2:0] tmp68;
    wire[3:0] tmp69;
    wire[4:0] tmp70;
    wire[3:0] tmp71;
    wire[3:0] tmp72;
    wire tmp73;
    wire[4:0] tmp74;
    wire[5:0] tmp75;
    wire[3:0] tmp76;
    wire[1:0] tmp77;
    wire[5:0] tmp78;
    wire[6:0] tmp79;
    wire[5:0] tmp80;

    // Combinational
    assign const_0_0 = 0;
    assign const_1_0 = 0;
    assign const_2_1 = 1;
    assign const_3_1 = 1;
    assign const_4_1 = 1;
    assign const_5_0 = 0;
    assign const_6_1 = 1;
    assign const_7_1 = 1;
    assign const_8_0 = 0;
    assign const_9_0 = 0;
    assign const_10_1 = 1;
    assign const_11_1 = 1;
    assign const_12_1 = 1;
    assign const_13_0 = 0;
    assign const_14_1 = 1;
    assign const_15_1 = 1;
    assign const_16_0 = 0;
    assign const_17_0 = 0;
    assign const_18_1 = 1;
    assign const_19_1 = 1;
    assign const_20_1 = 1;
    assign const_21_0 = 0;
    assign const_22_1 = 1;
    assign const_23_1 = 1;
    assign const_24_0 = 0;
    assign const_25_0 = 0;
    assign const_26_1 = 1;
    assign const_27_1 = 1;
    assign const_28_1 = 1;
    assign const_29_0 = 0;
    assign const_30_1 = 1;
    assign const_31_1 = 1;
    assign const_32_0 = 0;
    assign const_33_0 = 0;
    assign const_34_1 = 1;
    assign const_35_1 = 1;
    assign const_36_1 = 1;
    assign const_37_0 = 0;
    assign const_38_1 = 1;
    assign const_39_1 = 1;
    assign const_40_0 = 0;
    assign const_41_0 = 0;
    assign const_42_1 = 1;
    assign const_43_1 = 1;
    assign const_44_1 = 1;
    assign const_45_0 = 0;
    assign const_46_1 = 1;
    assign const_47_1 = 1;
    assign const_48_0 = 0;
    assign const_49_0 = 0;
    assign const_50_1 = 1;
    assign const_51_1 = 1;
    assign const_52_1 = 1;
    assign const_53_0 = 0;
    assign const_54_1 = 1;
    assign const_55_1 = 1;
    assign const_56_0 = 0;
    assign const_57_0 = 0;
    assign const_58_1 = 1;
    assign const_59_1 = 1;
    assign const_60_1 = 1;
    assign const_61_0 = 0;
    assign const_62_1 = 1;
    assign const_63_1 = 1;
    assign const_64_0 = 0;
    assign const_65_0 = 0;
    assign const_66_1 = 1;
    assign const_67_1 = 1;
    assign const_68_1 = 1;
    assign const_69_0 = 0;
    assign const_70_1 = 1;
    assign const_71_1 = 1;
    assign const_72_0 = 0;
    assign const_73_0 = 0;
    assign const_74_1 = 1;
    assign const_75_1 = 1;
    assign const_76_1 = 1;
    assign const_77_0 = 0;
    assign const_78_1 = 1;
    assign const_79_1 = 1;
    assign const_80_0 = 0;
    assign const_81_0 = 0;
    assign const_82_1 = 1;
    assign const_83_1 = 1;
    assign const_84_1 = 1;
    assign const_85_0 = 0;
    assign const_86_1 = 1;
    assign const_87_1 = 1;
    assign const_88_0 = 0;
    assign const_89_0 = 0;
    assign const_90_1 = 1;
    assign const_91_1 = 1;
    assign const_92_1 = 1;
    assign const_93_0 = 0;
    assign const_94_1 = 1;
    assign const_95_1 = 1;
    assign const_96_1 = 1;
    assign const_97_0 = 0;
    assign const_98_0 = 0;
    assign const_99_1 = 1;
    assign const_100_0 = 0;
    assign const_101_0 = 0;
    assign const_102_0 = 0;
    assign const_103_1 = 1;
    assign const_104_9 = 9;
    assign const_105_0 = 0;
    assign const_106_0 = 0;
    assign const_107_0 = 0;
    assign const_108_0 = 0;
    assign o = tmp80;
    assign tmp12 = {const_5_0, const_5_0, const_5_0};
    assign tmp13 = {tmp12, const_4_1};
    assign tmp14 = r + tmp13;
    assign tmp15 = {tmp14[3], tmp14[2], tmp14[1], tmp14[0]};
    assign tmp16 = {const_13_0, const_13_0, const_13_0};
    assign tmp17 = {tmp16, const_12_1};
    assign tmp18 = r + tmp17;
    assign tmp19 = {tmp18[3], tmp18[2], tmp18[1], tmp18[0]};
    assign tmp20 = {const_21_0, const_21_0, const_21_0};
    assign tmp21 = {tmp20, const_20_1};
    assign tmp22 = r + tmp21;
    assign tmp23 = {tmp22[3], tmp22[2], tmp22[1], tmp22[0]};
    assign tmp24 = {const_29_0, const_29_0, const_29_0};
    assign tmp25 = {tmp24, const_28_1};
    assign tmp26 = r + tmp25;
    assign tmp27 = {tmp26[3], tmp26[2], tmp26[1], tmp26[0]};
    assign tmp28 = {const_37_0, const_37_0, const_37_0};
    assign tmp29 = {tmp28, const_36_1};
    assign tmp30 = r + tmp29;
    assign tmp31 = {tmp30[3], tmp30[2], tmp30[1], tmp30[0]};
    assign tmp32 = {const_45_0, const_45_0, const_45_0};
    assign tmp33 = {tmp32, const_44_1};
    assign tmp34 = r + tmp33;
    assign tmp35 = {tmp34[3], tmp34[2], tmp34[1], tmp34[0]};
    assign tmp36 = {const_53_0, const_53_0, const_53_0};
    assign tmp37 = {tmp36, const_52_1};
    assign tmp38 = r + tmp37;
    assign tmp39 = {tmp38[3], tmp38[2], tmp38[1], tmp38[0]};
    assign tmp40 = {const_61_0, const_61_0, const_61_0};
    assign tmp41 = {tmp40, const_60_1};
    assign tmp42 = r + tmp41;
    assign tmp43 = {tmp42[3], tmp42[2], tmp42[1], tmp42[0]};
    assign tmp44 = {const_69_0, const_69_0, const_69_0};
    assign tmp45 = {tmp44, const_68_1};
    assign tmp46 = r + tmp45;
    assign tmp47 = {tmp46[3], tmp46[2], tmp46[1], tmp46[0]};
    assign tmp48 = {const_77_0, const_77_0, const_77_0};
    assign tmp49 = {tmp48, const_76_1};
    assign tmp50 = r + tmp49;
    assign tmp51 = {tmp50[3], tmp50[2], tmp50[1], tmp50[0]};
    assign tmp52 = {const_85_0, const_85_0, const_85_0};
    assign tmp53 = {tmp52, const_84_1};
    assign tmp54 = r + tmp53;
    assign tmp55 = {tmp54[3], tmp54[2], tmp54[1], tmp54[0]};
    assign tmp56 = {const_93_0, const_93_0, const_93_0};
    assign tmp57 = {tmp56, const_92_1};
    assign tmp58 = r + tmp57;
    assign tmp59 = {tmp58[3], tmp58[2], tmp58[1], tmp58[0]};
    assign tmp60 = a + r;
    assign tmp61 = {const_97_0, const_97_0, const_97_0, const_97_0};
    assign tmp62 = {tmp61, const_96_1};
    assign tmp63 = tmp60 + tmp62;
    assign tmp64 = {const_98_0, const_98_0};
    assign tmp65 = {tmp64, s};
    assign tmp66 = tmp63 - tmp65;
    assign tmp67 = {tmp66[3], tmp66[2], tmp66[1], tmp66[0]};
    assign tmp68 = {const_100_0, const_100_0, const_100_0};
    assign tmp69 = {tmp68, const_99_1};
    assign tmp70 = a - tmp69;
    assign tmp71 = {tmp70[3], tmp70[2], tmp70[1], tmp70[0]};
    assign tmp73 = {const_106_0};
    assign tmp74 = {tmp73, tmp72};
    assign tmp75 = tmp60 + tmp74;
    assign tmp77 = {const_108_0, const_108_0};
    assign tmp78 = {tmp77, tmp76};
    assign tmp79 = tmp75 + tmp78;
    assign tmp80 = {tmp79[5], tmp79[4], tmp79[3], tmp79[2], tmp79[1], tmp79[0]};

    // Registers
    always @(posedge clk)
    begin
        if (rst) begin
            r <= 0;
            s <= 13;
        end
        else begin
            r <= tmp67;
            s <= tmp71;
        end
    end

    // Memory mem_0: z
    always @(posedge clk)
    begin
        if (const_103_1) begin
            mem_0[const_102_0] <= const_104_9;
        end
    end

    // Memory mem_1: tmp0
    always @(posedge clk)
    begin
        if (const_2_1) begin
            mem_1[const_1_0] <= a;
        end
        if (const_7_1) begin
            mem_1[const_6_1] <= tmp15;
        end
    end
    assign tmp72 = mem_1[const_105_0];

    // Memory mem_2: tmp1
    always @(posedge clk)
    begin
        if (const_10_1) begin
            mem_2[const_9_0] <= a;
        end
        if (const_15_1) begin
            mem_2[const_14_1] <= tmp19;
        end
    end
    assign tmp76 = mem_2[const_107_0];

    // Memory mem_3: tmp2
    always @(posedge clk)
    begin
        if (const_18_1) begin
            mem_3[const_17_0] <= a;
        end
        if (const_23_1) begin
            mem_3[const_22_1] <= tmp23;
        end
    end

    // Memory mem_4: tmp3
    always @(posedge clk)
    begin
        if (const_26_1) begin
            mem_4[const_25_0] <= a;
        end
        if (const_31_1) begin
            mem_4[const_30_1] <= tmp27;
        end
    end

    // Memory mem_5: tmp4
    always @(posedge clk)
    begin
        if (const_34_1) begin
            mem_5[const_33_0] <= a;
        end
        if (const_39_1) begin
            mem_5[const_38_1] <= tmp31;
        end
    end

    // Memory mem_6: tmp5
    always @(posedge clk)
    begin
        if (const_42_1) begin
            mem_6[const_41_0] <= a;
        end
        if (const_47_1) begin
            mem_6[const_46_1] <= tmp35;
        end
    end

    // Memory mem_7: tmp6
    always @(posedge clk)
    begin
        if (const_50_1) begin
            mem_7[const_49_0] <= a;
        end
        if (const_55_1) begin
            mem_7[const_54_1] <= tmp39;
        end
    end

    // Memory mem_8: tmp7
    always @(posedge clk)
    begin
        if (const_58_1) begin
            mem_8[const_57_0] <= a;
        end
        if (const_63_1) begin
            mem_8[const_62_1] <= tmp43;
        end
    end

    // Memory mem_9: tmp8
    always @(posedge clk)
    begin
        if (const_66_1) begin
            mem_9[const_65_0] <= a;
        end
        if (const_71_1) begin
            mem_9[const_70_1] <= tmp47;
        end
    end

    // Memory mem_10: tmp9
    always @(posedge clk)
    begin
        if (const_74_1) begin
            mem_10[const_73_0] <= a;
        end
        if (const_79_1) begin
            mem_10[const_78_1] <= tmp51;
        end
    end

    // Memory mem_11: tmp10
    always @(posedge clk)
    begin
        if (const_82_1) begin
            mem_11[const_81_0] <= a;
        end
        if (const_87_1) begin
            mem_11[const_86_1] <= tmp55;
        end
    end

    // Memory mem_12: tmp11
    always @(posedge clk)
    begin
        if (const_90_1) begin
            mem_12[const_89_0] <= a;
        end
        if (const_95_1) begin
            mem_12[const_94_1] <= tmp59;
        end
    end

endmodule

"""


verilog_output_counter_sync_reset = """\
// Generated automatically via PyRTL
// As one initial test of synthesis, map to FPGA with:
//   yosys -p "synth_xilinx -top toplevel" thisfile.v

module toplevel(clk, rst, o);
    input clk;
    input rst;
    output[3:0] o;

    reg[3:0] tmp0;

    wire const_0_1;
    wire const_1_0;
    wire[2:0] tmp1;
    wire[3:0] tmp2;
    wire[4:0] tmp3;
    wire[3:0] tmp4;

    // Combinational
    assign const_0_1 = 1;
    assign const_1_0 = 0;
    assign o = tmp0;
    assign tmp1 = {const_1_0, const_1_0, const_1_0};
    assign tmp2 = {tmp1, const_0_1};
    assign tmp3 = tmp0 + tmp2;
    assign tmp4 = {tmp3[3], tmp3[2], tmp3[1], tmp3[0]};

    // Registers
    always @(posedge clk)
    begin
        if (rst) begin
            tmp0 <= 2;
        end
        else begin
            tmp0 <= tmp4;
        end
    end

endmodule

"""


verilog_output_counter_async_reset = """\
// Generated automatically via PyRTL
// As one initial test of synthesis, map to FPGA with:
//   yosys -p "synth_xilinx -top toplevel" thisfile.v

module toplevel(clk, rst, o);
    input clk;
    input rst;
    output[3:0] o;

    reg[3:0] tmp0;

    wire const_0_1;
    wire const_1_0;
    wire[2:0] tmp1;
    wire[3:0] tmp2;
    wire[4:0] tmp3;
    wire[3:0] tmp4;

    // Combinational
    assign const_0_1 = 1;
    assign const_1_0 = 0;
    assign o = tmp0;
    assign tmp1 = {const_1_0, const_1_0, const_1_0};
    assign tmp2 = {tmp1, const_0_1};
    assign tmp3 = tmp0 + tmp2;
    assign tmp4 = {tmp3[3], tmp3[2], tmp3[1], tmp3[0]};

    // Registers
    always @(posedge clk or posedge rst)
    begin
        if (rst) begin
            tmp0 <= 2;
        end
        else begin
            tmp0 <= tmp4;
        end
    end

endmodule

"""


verilog_output_mems_with_no_writes = """\
// Generated automatically via PyRTL
// As one initial test of synthesis, map to FPGA with:
//   yosys -p "synth_xilinx -top toplevel" thisfile.v

module toplevel(clk, rst, in1, out1);
    input clk;
    input rst;
    input[2:0] in1;
    output[7:0] out1;

    reg[7:0] mem_0[7:0]; //tmp0
    reg[7:0] mem_1[255:0]; //tmp1

    wire const_0_1;
    wire[7:0] const_1_42;
    wire[7:0] tmp2;

    initial begin
        mem_0[0]=8'ha;
        mem_0[1]=8'h14;
        mem_0[2]=8'h1e;
        mem_0[3]=8'h28;
        mem_0[4]=8'h32;
        mem_0[5]=8'h3c;
        mem_0[6]=8'h0;
        mem_0[7]=8'h0;
    end

    // Combinational
    assign const_0_1 = 1;
    assign const_1_42 = 42;
    assign out1 = tmp2;

    // Memory mem_0: tmp0
    assign tmp2 = mem_0[in1];

    // Memory mem_1: tmp1
    always @(posedge clk)
    begin
        if (const_0_1) begin
            mem_1[tmp2] <= const_1_42;
        end
    end

endmodule

"""


verilog_output_counter_no_reset = """\
// Generated automatically via PyRTL
// As one initial test of synthesis, map to FPGA with:
//   yosys -p "synth_xilinx -top toplevel" thisfile.v

module toplevel(clk, o);
    input clk;
    output[3:0] o;

    reg[3:0] tmp0;

    wire const_0_1;
    wire const_1_0;
    wire[2:0] tmp1;
    wire[3:0] tmp2;
    wire[4:0] tmp3;
    wire[3:0] tmp4;

    // Combinational
    assign const_0_1 = 1;
    assign const_1_0 = 0;
    assign o = tmp0;
    assign tmp1 = {const_1_0, const_1_0, const_1_0};
    assign tmp2 = {tmp1, const_0_1};
    assign tmp3 = tmp0 + tmp2;
    assign tmp4 = {tmp3[3], tmp3[2], tmp3[1], tmp3[0]};

    // Registers
    always @(posedge clk)
    begin
        begin
            tmp0 <= tmp4;
        end
    end

endmodule

"""


verilog_custom_reset = """\
// Generated automatically via PyRTL
// As one initial test of synthesis, map to FPGA with:
//   yosys -p "synth_xilinx -top toplevel" thisfile.v

module toplevel(clk, rst);
    input clk;
    input rst;

    reg[3:0] r;

    wire const_0_1;
    wire const_1_0;
    wire const_2_0;
    wire const_3_0;
    wire[2:0] tmp0;
    wire[3:0] tmp1;
    wire[4:0] tmp2;
    wire[3:0] tmp3;
    wire[4:0] tmp4;
    wire[4:0] tmp5;
    wire[3:0] tmp6;

    // Combinational
    assign const_0_1 = 1;
    assign const_1_0 = 0;
    assign const_2_0 = 0;
    assign const_3_0 = 0;
    assign tmp0 = {const_1_0, const_1_0, const_1_0};
    assign tmp1 = {tmp0, const_0_1};
    assign tmp2 = r + tmp1;
    assign tmp3 = {const_3_0, const_3_0, const_3_0, const_3_0};
    assign tmp4 = {tmp3, const_2_0};
    assign tmp5 = rst ? tmp4 : tmp2;
    assign tmp6 = {tmp5[3], tmp5[2], tmp5[1], tmp5[0]};

    // Registers
    always @(posedge clk)
    begin
        begin
            r <= tmp6;
        end
    end

endmodule

"""


class TestVerilogNames(unittest.TestCase):
    def setUp(self):
        pyrtl.reset_working_block()
        self.vnames = _VerilogSanitizer("_sani_test")

    def checkname(self, name):
        self.assertEqual(self.vnames.make_valid_string(name), name)

    def assert_invalid_name(self, name):
        self.assertNotEqual(self.vnames.make_valid_string(name), name)

    def test_verilog_check_valid_name_good(self):
        self.checkname('abc')
        self.checkname('a')
        self.checkname('BC')
        self.checkname('Kabc')
        self.checkname('B_ac')
        self.checkname('_asdvqa')
        self.checkname('_Bs_')
        self.checkname('fd$oeoe')
        self.checkname('_B$$s')
        self.checkname('B')

    def test_verilog_check_valid_name_bad(self):
        self.assert_invalid_name('carne asda')
        self.assert_invalid_name('')
        self.assert_invalid_name('asd%kask')
        self.assert_invalid_name("flipin'")
        self.assert_invalid_name(' jklol')
        self.assert_invalid_name('a' * 2000)


class TestVerilogOutput(unittest.TestCase):
    def setUp(self):
        pyrtl.reset_working_block()
        # To compare textual consistency, need to make
        # sure we're starting at the same index for all
        # automatically created names.
        pyrtl.wire._reset_wire_indexers()
        pyrtl.memory._reset_memory_indexer()

    def test_romblock_does_not_throw_error(self):
        from pyrtl.corecircuits import _basic_add
        a = pyrtl.Input(bitwidth=3, name='a')
        b = pyrtl.Input(bitwidth=3, name='b')
        o = pyrtl.Output(bitwidth=3, name='o')
        res = _basic_add(a, b)
        rdat = {0: 1, 1: 2, 2: 5, 5: 0}
        mixtable = pyrtl.RomBlock(addrwidth=3, bitwidth=3, pad_with_zeros=True, romdata=rdat)
        o <<= mixtable[res[:-1]]
        with io.StringIO() as testbuffer:
            pyrtl.output_to_verilog(testbuffer)

    def test_textual_consistency_small(self):
        i = pyrtl.Const(0b1100)
        j = pyrtl.Const(0b011, bitwidth=3)
        k = pyrtl.Const(0b100110, name='k')
        o = pyrtl.Output(13, 'o')
        o <<= pyrtl.concat(i, j, k)

        buffer = io.StringIO()
        pyrtl.output_to_verilog(buffer, add_reset=False)

        self.assertEqual(buffer.getvalue(), verilog_output_small)

    def test_textual_consistency_large(self):
        # The following is a non-sensical program created to test
        # that the Verilog that is created is deterministic
        # in the order in which it presents the wire, register,
        # and memory declarations and the combinational and
        # sequential logic. Hence it creates many memories, and
        # makes sure at least two lines of code are created in
        # the always @ blocks associated with them (so we have
        # many different wire names to deal with and test against).
        a = pyrtl.Input(4, 'a')
        r = pyrtl.Register(4, name='r')
        s = pyrtl.Register(4, name='s', reset_value=13)
        # This will have mem id 0, so prints first despite actual name
        mt = pyrtl.MemBlock(4, 2, name='z')
        m = [pyrtl.MemBlock(4, 2, max_write_ports=2) for _ in range(12)]
        for mem in m:
            mem[0] <<= a
            mem[1] <<= (r + 1).truncate(4)
        b = a + r
        r.next <<= b + 1 - s
        s.next <<= a - 1
        mt[0] <<= 9
        o = pyrtl.Output(6, 'o')
        o <<= b + m[0][0] + m[1][0]

        buffer = io.StringIO()
        pyrtl.output_to_verilog(buffer)

        self.assertEqual(buffer.getvalue(), verilog_output_large)

    def test_mems_with_no_writes(self):
        rdata = {0: 10, 1: 20, 2: 30, 3: 40, 4: 50, 5: 60}
        rom = pyrtl.RomBlock(8, 3, rdata, pad_with_zeros=True)
        mem = pyrtl.MemBlock(8, 8)
        in1 = pyrtl.Input(3, 'in1')
        out1 = pyrtl.Output(8, 'out1')
        w = rom[in1]
        out1 <<= w
        mem[w] <<= 42

        buffer = io.StringIO()
        pyrtl.output_to_verilog(buffer)

        self.assertEqual(buffer.getvalue(), verilog_output_mems_with_no_writes)

    def check_counter_text(self, add_reset, expected):
        r = pyrtl.Register(4, reset_value=2)
        r.next <<= r + 1
        o = pyrtl.Output(4, 'o')
        o <<= r

        buffer = io.StringIO()
        pyrtl.output_to_verilog(buffer, add_reset)
        self.assertEqual(buffer.getvalue(), expected)

    def test_textual_consistency_with_sync_reset(self):
        self.check_counter_text(True, verilog_output_counter_sync_reset)

    def test_textual_consistency_with_async_reset(self):
        self.check_counter_text('asynchronous', verilog_output_counter_async_reset)

    def test_textual_consistency_with_no_reset(self):
        self.check_counter_text(False, verilog_output_counter_no_reset)

    def test_error_invalid_add_reset(self):
        buffer = io.StringIO()
        with six.assertRaisesRegex(self, pyrtl.PyrtlError, "Invalid add_reset option"):
            pyrtl.output_to_verilog(buffer, add_reset='foobar')

    def test_error_existing_reset_wire(self):
        buffer = io.StringIO()
        _rst = pyrtl.Input(1, 'rst')
        with six.assertRaisesRegex(self, pyrtl.PyrtlError,
                                   "Found a user-defined wire named 'rst'."):
            pyrtl.output_to_verilog(buffer)

    def test_existing_reset_wire_without_add_reset(self):
        buffer = io.StringIO()
        rst = pyrtl.Input(1, 'rst')
        r = pyrtl.Register(4, 'r')
        r.next <<= pyrtl.select(rst, 0, r + 1)
        pyrtl.output_to_verilog(buffer, add_reset=False)
        self.assertEqual(buffer.getvalue(), verilog_custom_reset)


verilog_input_counter = """\
module counter (clk, rst, en, count);

    input clk, rst, en;
    output reg [3:0] count;

    always @(posedge clk)
        if (rst)
            count <= 4'd0;
        else if (en)
            count <= count + 4'd1;

endmodule
"""

verilog_input_multi_module = """\
module foo (a, b, o);

    input a, b;
    output [1:0] o;
    assign o = a + b;

endmodule

module top (clk, o);
    input clk;
    reg a, b;
    output [1:0] o;
    foo f1(a, b, o);

    always @(posedge clk)
    begin
        a <= ~a;
        b <= ~b;
    end
endmodule
"""


class TestVerilogInput(unittest.TestCase):
    def setUp(self):
        import subprocess
        try:
            version = subprocess.check_output(['yosys', '-V'])
        except OSError:
            raise unittest.SkipTest('Testing Verilog input requires yosys')
        pyrtl.reset_working_block()

    def test_import_counter(self):
        pyrtl.input_from_verilog(verilog_input_counter)
        sim = pyrtl.Simulation()
        sim.step_multiple({'rst': '10000', 'en': '01111'})
        self.assertEqual(sim.tracer.trace['count'], [0, 0, 1, 2, 3])

    def test_import_small(self):
        pyrtl.input_from_verilog(verilog_output_small)
        sim = pyrtl.Simulation()
        sim.step({})
        self.assertEqual(sim.tracer.trace['o'][0], 0b1100011100110)

    def test_import_counter_with_reset(self):
        pyrtl.input_from_verilog(verilog_output_counter_sync_reset)
        sim = pyrtl.Simulation()
        sim.step_multiple({'rst': '1000'})
        self.assertEqual(sim.tracer.trace['o'], [0, 2, 3, 4])

    def test_import_multi_module_specified_module(self):
        # Import foo module because occurs first in file
        pyrtl.input_from_verilog(verilog_input_multi_module, toplevel="foo")
        sim = pyrtl.Simulation()
        sim.step_multiple({'a': '0011', 'b': '0101'})
        self.assertEqual(sim.tracer.trace['o'], [0, 1, 1, 2])

    def test_import_multi_module_auto_select_top_module(self):
        pyrtl.input_from_verilog(verilog_input_multi_module)
        sim = pyrtl.Simulation()
        sim.step_multiple(nsteps=5)
        self.assertEqual(sim.tracer.trace['o'], [0, 2, 0, 2, 0])

    def test_error_import_bad_file(self):
        with six.assertRaisesRegex(self, pyrtl.PyrtlError,
                                   "input_from_verilog expecting either open file or string"):
            pyrtl.input_from_verilog(3)


verilog_testbench = """\
module tb();
    reg clk;
    reg rst;
    reg[1:0] a100;
    reg[3:0] w1;
    reg[2:0] w12;
    wire[1:0] out1;
    wire[8:0] out10;

    integer tb_iter;
    toplevel block(.clk(clk), .rst(rst), .a100(a100), .w1(w1), .w12(w12), .out1(out1), .out10(out10));

    always
        #5 clk = ~clk;

    initial begin
        $dumpfile ("waveform.vcd");
        $dumpvars;

        clk = 0;
        rst = 0;
        block.r1 = 2;
        block.r2 = 3;
        block.tmp0 = 0;
        for (tb_iter = 0; tb_iter < 32; tb_iter++) begin block.mem_0[tb_iter] = 0; end
        block.mem_0[2] = 9;
        block.mem_0[9] = 12;
        a100 = 2'd0;
        w1 = 4'd0;
        w12 = 3'd0;

        #10
        a100 = 2'd1;
        w1 = 4'd4;
        w12 = 3'd1;

        #10
        a100 = 2'd3;
        w1 = 4'd2;
        w12 = 3'd7;

        #10
        a100 = 2'd2;
        w1 = 4'd3;
        w12 = 3'd4;

        #10
        $finish;
    end
endmodule
"""  # noqa

verilog_testbench_no_reset = """\
module tb();
    reg clk;
    reg[1:0] a100;
    reg[3:0] w1;
    reg[2:0] w12;
    wire[1:0] out1;
    wire[8:0] out10;

    integer tb_iter;
    toplevel block(.clk(clk), .a100(a100), .w1(w1), .w12(w12), .out1(out1), .out10(out10));

    always
        #5 clk = ~clk;

    initial begin
        $dumpfile ("waveform.vcd");
        $dumpvars;

        clk = 0;
        block.r1 = 2;
        block.r2 = 3;
        block.tmp0 = 0;
        for (tb_iter = 0; tb_iter < 32; tb_iter++) begin block.mem_0[tb_iter] = 0; end
        block.mem_0[2] = 9;
        block.mem_0[9] = 12;
        a100 = 2'd0;
        w1 = 4'd0;
        w12 = 3'd0;

        #10
        a100 = 2'd1;
        w1 = 4'd4;
        w12 = 3'd1;

        #10
        a100 = 2'd3;
        w1 = 4'd2;
        w12 = 3'd7;

        #10
        a100 = 2'd2;
        w1 = 4'd3;
        w12 = 3'd4;

        #10
        $finish;
    end
endmodule
"""

verilog_testbench_custom_reset = """\
module tb();
    reg clk;
    reg rst;

    integer tb_iter;
    toplevel block(.clk(clk), .rst(rst));

    always
        #5 clk = ~clk;

    initial begin
        $dumpfile ("waveform.vcd");
        $dumpvars;

        clk = 0;
        block.r = 0;
        $finish;
    end
endmodule
"""


class TestOutputTestbench(unittest.TestCase):
    def setUp(self):
        pyrtl.reset_working_block()
        # To compare textual consistency, need to make
        # sure we're starting at the same index for all
        # automatically created names.
        pyrtl.wire._reset_wire_indexers()
        pyrtl.memory._reset_memory_indexer()

    def test_verilog_testbench_does_not_throw_error(self):
        zero = pyrtl.Input(1, 'zero')
        counter_output = pyrtl.Output(3, 'counter_output')
        counter = pyrtl.Register(3, 'counter')
        counter.next <<= pyrtl.mux(zero, counter + 1, 0)
        counter_output <<= counter
        sim_trace = pyrtl.SimulationTrace([counter_output, zero])
        sim = pyrtl.Simulation(tracer=sim_trace)
        for cycle in range(15):
            sim.step({zero: random.choice([0, 0, 0, 1])})
        with io.StringIO() as tbfile:
            pyrtl.output_verilog_testbench(tbfile, sim_trace)

    def create_design(self):
        # Various wire names so we can verify they are printed
        # in deterministic order each time
        i1, i2, i3 = pyrtl.input_list('w1/4 w12/3 a100/2')
        r1, r2 = pyrtl.register_list('r1/3 r2/4')
        r3 = pyrtl.Register(8)
        mem = pyrtl.MemBlock(4, 5)
        o1, o2 = pyrtl.output_list('out1/2 out10/9')
        r1.next <<= i1 + i2
        r2.next <<= r1 * i3
        r3.next <<= r1 & r2
        mem[i1] <<= r1 + 3
        o1 <<= i3 - r2
        o2 <<= r1
        sim_trace = pyrtl.SimulationTrace()
        sim = pyrtl.Simulation(tracer=sim_trace, register_value_map={
            r1: 2,
            r2: 3,
        }, memory_value_map={
            mem: {
                2: 9,
                9: 12,
            },
        })
        sim.step_multiple({
            'w1': [0, 4, 2, 3],
            'w12': [0, 1, 7, 4],
            'a100': [0, 1, 3, 2],
        })
        return sim_trace

    def test_verilog_testbench_consistency(self):
        sim_trace = self.create_design()
        with io.StringIO() as tbfile:
            pyrtl.output_verilog_testbench(tbfile, sim_trace)
            self.assertEqual(tbfile.getvalue(), verilog_testbench)

    def test_verilog_testbench_no_reset_consistency(self):
        sim_trace = self.create_design()
        with io.StringIO() as tbfile:
            pyrtl.output_verilog_testbench(tbfile, sim_trace, add_reset=False)
            self.assertEqual(tbfile.getvalue(), verilog_testbench_no_reset)

    def test_error_verilog_testbench_invalid_add_reset(self):
        tbfile = io.StringIO()
        with six.assertRaisesRegex(self, pyrtl.PyrtlError, "Invalid add_reset option"):
            pyrtl.output_verilog_testbench(tbfile, add_reset='foobar')

    def test_error_verilog_testbench_existing_reset_wire(self):
        tbfile = io.StringIO()
        _rst = pyrtl.Input(1, 'rst')
        with six.assertRaisesRegex(self, pyrtl.PyrtlError,
                                   "Found a user-defined wire named 'rst'."):
            pyrtl.output_verilog_testbench(tbfile)

    def test_verilog_testbench_existing_reset_wire_without_add_reset(self):
        buffer = io.StringIO()
        rst = pyrtl.Input(1, 'rst')
        r = pyrtl.Register(4, 'r')
        r.next <<= pyrtl.select(rst, 0, r + 1)
        pyrtl.output_verilog_testbench(buffer, add_reset=False)
        self.assertEqual(buffer.getvalue(), verilog_testbench_custom_reset)


firrtl_output_concat_test = """\
circuit Example :
  module Example :
    input clock : Clock
    input reset : UInt<1>
    output o : UInt<13>
    wire tmp0 : UInt<13>
    wire tmp1 : UInt<7>
    wire tmp2 : UInt<13>
    node const_0_12 = UInt<4>(12)
    node const_1_3 = UInt<3>(3)
    node const_2_38 = UInt<6>(38)

    o <= tmp0
    tmp0 <= tmp2
    tmp1 <= cat(const_0_12, const_1_3)
    tmp2 <= cat(tmp1, const_2_38)
"""

firrtl_output_select_test = """\
circuit Example :
  module Example :
    input clock : Clock
    input reset : UInt<1>
    output b : UInt<6>
    wire tmp0 : UInt<6>
    wire tmp1 : UInt<1>
    wire tmp2 : UInt<1>
    wire tmp3 : UInt<1>
    wire tmp4 : UInt<1>
    wire tmp5 : UInt<1>
    wire tmp6 : UInt<1>
    wire tmp7 : UInt<6>
    wire tmp8 : UInt<2>
    wire tmp9 : UInt<3>
    wire tmp10 : UInt<4>
    wire tmp11 : UInt<5>
    wire tmp12 : UInt<6>
    node const_0_2893 = UInt<12>(2893)

    b <= tmp0
    tmp0 <= tmp7
    tmp1 <= bits(const_0_2893, 0, 0)
    tmp2 <= bits(const_0_2893, 2, 2)
    tmp3 <= bits(const_0_2893, 4, 4)
    tmp4 <= bits(const_0_2893, 6, 6)
    tmp5 <= bits(const_0_2893, 8, 8)
    tmp6 <= bits(const_0_2893, 10, 10)
    tmp7 <= tmp12
    tmp8 <= cat(tmp6, tmp5)
    tmp9 <= cat(tmp8, tmp4)
    tmp10 <= cat(tmp9, tmp3)
    tmp11 <= cat(tmp10, tmp2)
    tmp12 <= cat(tmp11, tmp1)
"""


class TestOutputFirrtl(unittest.TestCase):
    def setUp(self):
        pyrtl.reset_working_block()
        pyrtl.wire._reset_wire_indexers()
        pyrtl.memory._reset_memory_indexer()

    def test_textual_consistency_concats(self):
        i = pyrtl.Const(0b1100)
        j = pyrtl.Const(0b011, bitwidth=3)
        k = pyrtl.Const(0b100110)
        o = pyrtl.Output(13, 'o')
        o <<= pyrtl.concat(i, j, k)

        buffer = io.StringIO()
        pyrtl.output_to_firrtl(buffer)

        self.assertEqual(buffer.getvalue(), firrtl_output_concat_test)

    def test_textual_consistency_selects(self):
        a = pyrtl.Const(0b101101001101)
        b = pyrtl.Output(6, 'b')
        b <<= a[::2]

        buffer = io.StringIO()
        pyrtl.output_to_firrtl(buffer)

        self.assertEqual(buffer.getvalue(), firrtl_output_select_test)


iscas85_bench_c432 = """\
# c432
# 36 inputs
# 7 outputs
# 40 inverters
# 120 gates ( 4 ANDs + 79 NANDs + 19 NORs + 18 XORs )

INPUT(1)
INPUT(4)
INPUT(8)
INPUT(11)
INPUT(14)
INPUT(17)
INPUT(21)
INPUT(24)
INPUT(27)
INPUT(30)
INPUT(34)
INPUT(37)
INPUT(40)
INPUT(43)
INPUT(47)
INPUT(50)
INPUT(53)
INPUT(56)
INPUT(60)
INPUT(63)
INPUT(66)
INPUT(69)
INPUT(73)
INPUT(76)
INPUT(79)
INPUT(82)
INPUT(86)
INPUT(89)
INPUT(92)
INPUT(95)
INPUT(99)
INPUT(102)
INPUT(105)
INPUT(108)
INPUT(112)
INPUT(115)

OUTPUT(223)
OUTPUT(329)
OUTPUT(370)
OUTPUT(421)
OUTPUT(430)
OUTPUT(431)
OUTPUT(432)

118 = NOT(1)
119 = NOT(4)
122 = NOT(11)
123 = NOT(17)
126 = NOT(24)
127 = NOT(30)
130 = NOT(37)
131 = NOT(43)
134 = NOT(50)
135 = NOT(56)
138 = NOT(63)
139 = NOT(69)
142 = NOT(76)
143 = NOT(82)
146 = NOT(89)
147 = NOT(95)
150 = NOT(102)
151 = NOT(108)
154 = NAND(118, 4)
157 = NOR(8, 119)
158 = NOR(14, 119)
159 = NAND(122, 17)
162 = NAND(126, 30)
165 = NAND(130, 43)
168 = NAND(134, 56)
171 = NAND(138, 69)
174 = NAND(142, 82)
177 = NAND(146, 95)
180 = NAND(150, 108)
183 = NOR(21, 123)
184 = NOR(27, 123)
185 = NOR(34, 127)
186 = NOR(40, 127)
187 = NOR(47, 131)
188 = NOR(53, 131)
189 = NOR(60, 135)
190 = NOR(66, 135)
191 = NOR(73, 139)
192 = NOR(79, 139)
193 = NOR(86, 143)
194 = NOR(92, 143)
195 = NOR(99, 147)
196 = NOR(105, 147)
197 = NOR(112, 151)
198 = NOR(115, 151)
199 = AND(154, 159, 162, 165, 168, 171, 174, 177, 180)
203 = NOT(199)
213 = NOT(199)
223 = NOT(199)
224 = XOR(203, 154)
227 = XOR(203, 159)
230 = XOR(203, 162)
233 = XOR(203, 165)
236 = XOR(203, 168)
239 = XOR(203, 171)
242 = NAND(1, 213)
243 = XOR(203, 174)
246 = NAND(213, 11)
247 = XOR(203, 177)
250 = NAND(213, 24)
251 = XOR(203, 180)
254 = NAND(213, 37)
255 = NAND(213, 50)
256 = NAND(213, 63)
257 = NAND(213, 76)
258 = NAND(213, 89)
259 = NAND(213, 102)
260 = NAND(224, 157)
263 = NAND(224, 158)
264 = NAND(227, 183)
267 = NAND(230, 185)
270 = NAND(233, 187)
273 = NAND(236, 189)
276 = NAND(239, 191)
279 = NAND(243, 193)
282 = NAND(247, 195)
285 = NAND(251, 197)
288 = NAND(227, 184)
289 = NAND(230, 186)
290 = NAND(233, 188)
291 = NAND(236, 190)
292 = NAND(239, 192)
293 = NAND(243, 194)
294 = NAND(247, 196)
295 = NAND(251, 198)
296 = AND(260, 264, 267, 270, 273, 276, 279, 282, 285)
300 = NOT(263)
301 = NOT(288)
302 = NOT(289)
303 = NOT(290)
304 = NOT(291)
305 = NOT(292)
306 = NOT(293)
307 = NOT(294)
308 = NOT(295)
309 = NOT(296)
319 = NOT(296)
329 = NOT(296)
330 = XOR(309, 260)
331 = XOR(309, 264)
332 = XOR(309, 267)
333 = XOR(309, 270)
334 = NAND(8, 319)
335 = XOR(309, 273)
336 = NAND(319, 21)
337 = XOR(309, 276)
338 = NAND(319, 34)
339 = XOR(309, 279)
340 = NAND(319, 47)
341 = XOR(309, 282)
342 = NAND(319, 60)
343 = XOR(309, 285)
344 = NAND(319, 73)
345 = NAND(319, 86)
346 = NAND(319, 99)
347 = NAND(319, 112)
348 = NAND(330, 300)
349 = NAND(331, 301)
350 = NAND(332, 302)
351 = NAND(333, 303)
352 = NAND(335, 304)
353 = NAND(337, 305)
354 = NAND(339, 306)
355 = NAND(341, 307)
356 = NAND(343, 308)
357 = AND(348, 349, 350, 351, 352, 353, 354, 355, 356)
360 = NOT(357)
370 = NOT(357)
371 = NAND(14, 360)
372 = NAND(360, 27)
373 = NAND(360, 40)
374 = NAND(360, 53)
375 = NAND(360, 66)
376 = NAND(360, 79)
377 = NAND(360, 92)
378 = NAND(360, 105)
379 = NAND(360, 115)
380 = NAND(4, 242, 334, 371)
381 = NAND(246, 336, 372, 17)
386 = NAND(250, 338, 373, 30)
393 = NAND(254, 340, 374, 43)
399 = NAND(255, 342, 375, 56)
404 = NAND(256, 344, 376, 69)
407 = NAND(257, 345, 377, 82)
411 = NAND(258, 346, 378, 95)
414 = NAND(259, 347, 379, 108)
415 = NOT(380)
416 = AND(381, 386, 393, 399, 404, 407, 411, 414)
417 = NOT(393)
418 = NOT(404)
419 = NOT(407)
420 = NOT(411)
421 = NOR(415, 416)
422 = NAND(386, 417)
425 = NAND(386, 393, 418, 399)
428 = NAND(399, 393, 419)
429 = NAND(386, 393, 407, 420)
430 = NAND(381, 386, 422, 399)
431 = NAND(381, 386, 425, 428)
432 = NAND(381, 422, 425, 429)
"""

iscas89_bench_s208 = """\
# s208
# 11 inputs
# 2 outputs
# 8 D-type flipflops
# 35 inverters
# 61 gates (17 ANDs + 19 NANDs + 4 ORs + 21 NORs)

INPUT(X)
INPUT(Clear)
INPUT(C_8)
INPUT(C_7)
INPUT(C_6)
INPUT(C_5)
INPUT(C_4)
INPUT(C_3)
INPUT(C_2)
INPUT(C_1)
INPUT(C_0)

OUTPUT(W)
OUTPUT(Z)

Y_4 = DFF(II3)
Y_3 = DFF(II4)
Y_2 = DFF(II5)
Y_1 = DFF(II6)
Y_8 = DFF(II155)
Y_7 = DFF(II156)
Y_6 = DFF(II157)
Y_5 = DFF(II158)

II6 = NOT(II104)
II50 = NOT(II92)
II40 = NOT(X)
II41 = NOT(Y_4)
II42 = NOT(Y_3)
II43 = NOT(Y_2)
II44 = NOT(Y_1)
II3 = NOT(II46)
II4 = NOT(II47)
II158 = NOT(II256)
II202 = NOT(II244)
II192 = NOT(II1_1)
II193 = NOT(Y_8)
II194 = NOT(Y_7)
II195 = NOT(Y_6)
II196 = NOT(Y_5)
II155 = NOT(II198)
II156 = NOT(II199)
II307_1 = NOT(II341)
II318 = NOT(II341)
II309 = NOT(Y_1)
II310 = NOT(Y_2)
II311 = NOT(Y_3)
P_1 = NOT(II314)
P_3 = NOT(II316)
P_4 = NOT(II317)
II368 = NOT(Y_4)
II369 = NOT(Y_5)
II370 = NOT(Y_6)
II371 = NOT(Y_7)
II372 = NOT(Y_8)
P_6 = NOT(II374)
Z = NOT(II446)
II487 = NOT(II488)
II489 = NOT(II490)

II127_1 = AND(II41, Y_3, II109)
II127_2 = AND(II96, II113, Y_4)
II131_1 = AND(II113, II92, Y_3)
II131_2 = AND(II42, II109)
II279_1 = AND(II193, Y_7, II261)
II279_2 = AND(II248, II265, Y_8)
II283_1 = AND(II265, II244, Y_7)
II283_2 = AND(II194, II261)
II497_1 = AND(P_8, C_8)
II500_1 = AND(P_5, C_5)
II500_2 = AND(P_2, C_2)
II504_1 = AND(P_3, C_3)
II504_2 = AND(P_4, C_4)
II508_1 = AND(X, C_0)
II508_2 = AND(P_1, C_1)
II512_1 = AND(P_6, C_6)
II512_2 = AND(P_7, C_7)

II135_1 = OR(II43, II104)
II135_2 = OR(Y_2, II100)
II287_1 = OR(II195, II256)
II287_2 = OR(Y_6, II252)

II5 = NAND(II135_1, II135_2)
II92 = NAND(Y_2, Y_1)
II96 = NAND(Y_3, II50)
II100 = NAND(Y_1, II113)
II104 = NAND(II44, II113)
II157 = NAND(II287_1, II287_2)
II244 = NAND(Y_6, Y_5)
II248 = NAND(Y_7, II202)
II252 = NAND(Y_5, II265)
II256 = NAND(II196, II265)
II314 = NAND(X, Y_1)
II316 = NAND(Y_3, II347)
II317 = NAND(Y_4, II318)
II341 = NAND(II311, II347)
II350 = NAND(X, II309)
II374 = NAND(Y_6, II406)
II378 = NAND(II406, II370)
II409 = NAND(II368, II307_1)
II495 = NAND(II484, II494)

II1_1 = NOR(II41, II96)
II46 = NOR(II127_1, II127_2)
II47 = NOR(II131_1, II131_2)
II109 = NOR(II43, II100)
II113 = NOR(Clear, II40)
W = NOR(II193, II248)
II198 = NOR(II279_1, II279_2)
II199 = NOR(II283_1, II283_2)
II261 = NOR(II195, II252)
II265 = NOR(Clear, II192)
P_2 = NOR(II310, II350)
II347 = NOR(Y_2, II350)
P_5 = NOR(II369, II409)
P_7 = NOR(II371, II378)
P_8 = NOR(Y_7, II378, II372)
II406 = NOR(II409, Y_5)
II446 = NOR(II495, II487, II497_1)
II484 = NOR(II500_1, II500_2)
II488 = NOR(II504_1, II504_2)
II490 = NOR(II508_1, II508_2)
II494 = NOR(II512_1, II512_2, II489)
"""

iscas89_bench_s27 = """\
# 4 inputs
# 1 outputs
# 3 D-type flipflops
# 2 inverters
# 8 gates (1 ANDs + 1 NANDs + 2 ORs + 4 NORs)

INPUT(G0)
INPUT(G1)
INPUT(G2)
INPUT(G3)

OUTPUT(G17)

G5 = DFF(G10)
G6 = DFF(G11)
G7 = DFF(G13)

G14 = NOT(G0)
G17 = NOT(G11)

G8 = AND(G14, G6)

G15 = OR(G12, G8)
G16 = OR(G3, G8)

G9 = NAND(G16, G15)

G10 = NOR(G14, G11)
G11 = NOR(G5, G9)
G12 = NOR(G1, G7)
G13 = NOR(G2, G12)
"""

example_bench_with_io_same_name = """\
INPUT(G1)
INPUT(G2)
INPUT(G3)

OUTPUT(G3)
OUTPUT(G4)

G4 = OR(G1, G2)
"""


class TestInputISCASBench(unittest.TestCase):
    # NOTE: number of inverters = number of original inverters + number of NORs, since
    # we currently convert NORs to inverters and ORs. Similarly for number of total ORs.

    def setUp(self):
        pyrtl.reset_working_block()
        pyrtl.wire._reset_wire_indexers()
        pyrtl.memory._reset_memory_indexer()

    def check_io(self, cls, names):
        block = pyrtl.working_block()
        for name in names:
            wire = block.get_wirevector_by_name(name)
            self.assertIsNotNone(wire)
            self.assertIsInstance(wire, cls)
            self.assertEqual(len(wire), 1)
        self.assertEqual(
            len(block.wirevector_subset(cls)),
            len(names)
        )

    def check_gate_amounts(self, nots, ands, ors, nands, xors, dffs):
        block = pyrtl.working_block()
        self.assertEqual(len(block.logic_subset(op='~')), nots)
        self.assertEqual(len(block.logic_subset(op='&')), ands)
        self.assertEqual(len(block.logic_subset(op='|')), ors)
        self.assertEqual(len(block.logic_subset(op='n')), nands)
        self.assertEqual(len(block.logic_subset(op='^')), xors)
        self.assertEqual(len(block.wirevector_subset(pyrtl.Register)), dffs)

    def test_combinational_bench(self):
        pyrtl.input_from_iscas_bench(iscas85_bench_c432)

        input_names = [
            '1', '4', '8', '11', '14', '17', '21', '24', '27',
            '30', '34', '37', '40', '43', '47', '50', '53', '56',
            '60', '63', '66', '69', '73', '76', '79', '82', '86',
            '89', '92', '95', '99', '102', '105', '108', '112', '115'
        ]
        self.check_io(pyrtl.Input, input_names)

        output_names = ['223', '329', '370', '421', '430', '431', '432']
        self.check_io(pyrtl.Output, output_names)

        self.check_gate_amounts(59, 4, 19, 79, 18, 0)

    def test_sequential_bench(self):
        pyrtl.input_from_iscas_bench(iscas89_bench_s208)

        input_names = [
            'X', 'Clear', 'C_8', 'C_7', 'C_6', 'C_5', 'C_4', 'C_3', 'C_2', 'C_1', 'C_0'
        ]
        self.check_io(pyrtl.Input, input_names)

        output_names = ['W', 'Z']
        self.check_io(pyrtl.Output, output_names)

        self.check_gate_amounts(56, 17, 25, 19, 0, 8)

    def test_simulation_bench(self):
        pyrtl.input_from_iscas_bench(iscas89_bench_s27)
        trace = pyrtl.SimulationTrace({pyrtl.working_block().get_wirevector_by_name('G17')})
        sim = pyrtl.Simulation(trace)
        sim.step_multiple({
            'G0': '01101001',
            'G1': '11010100',
            'G2': '00011001',
            'G3': '11001100',
        })
        correct_output = ('G17 11110001\n')
        output = six.StringIO()
        trace.print_trace(output, compact=True)
        self.assertEqual(output.getvalue(), correct_output)

    def test_bench_with_same_io_name(self):
        output = six.StringIO()
        sys.stdout = output
        pyrtl.input_from_iscas_bench(example_bench_with_io_same_name)
        sys.stdout = sys.__stdout__

        self.assertEqual(
            output.getvalue(),
            "Found input and output wires with the same name. "
            "Output 'G3' has now been renamed to 'tmp3'.\n"
        )
        pyrtl.working_block().sanity_check()

        self.check_io(pyrtl.Input, ['G1', 'G2', 'G3'])
        self.check_io(pyrtl.Output, ['tmp3', 'G4'])


if __name__ == "__main__":
    unittest.main()
