import io
import random
import subprocess
import sys
import unittest

import pyrtl
from pyrtl.corecircuits import _basic_add
from pyrtl.importexport import _VerilogSanitizer
from pyrtl.rtllib import testingutils as utils

full_adder_blif = r"""
# 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 = r"""
# 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
"""  # noqa: E501

# 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 = r"""
# 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 = r"""
# 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
"""

simple_unmerged_io_blif = r"""
# 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
"""

four_bit_adder_multi_module = r"""
# 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 = r"""
# 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 = pyrtl.Simulation(tracer=pyrtl.SimulationTrace(wires_to_track=io_vectors))
        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 = io.StringIO()
        sim.tracer.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 = pyrtl.Simulation(tracer=pyrtl.SimulationTrace(wires_to_track=io_vectors))
        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 = io.StringIO()
        sim.tracer.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)

        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 self.assertRaisesRegex(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 self.assertRaisesRegex(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;

    // Constants
    wire[5:0] k = 6'd38;

    // Combinational logic
    assign o = {4'd12, 3'd3, 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;

    // Memories
    reg[3:0] z[3:0];  // MemBlock
    reg[3:0] tmp0[3:0];  // MemBlock
    reg[3:0] tmp1[3:0];  // MemBlock
    reg[3:0] tmp2[3:0];  // MemBlock
    reg[3:0] tmp3[3:0];  // MemBlock
    reg[3:0] tmp4[3:0];  // MemBlock
    reg[3:0] tmp5[3:0];  // MemBlock
    reg[3:0] tmp6[3:0];  // MemBlock
    reg[3:0] tmp7[3:0];  // MemBlock
    reg[3:0] tmp8[3:0];  // MemBlock
    reg[3:0] tmp9[3:0];  // MemBlock
    reg[3:0] tmp10[3:0];  // MemBlock
    reg[3:0] tmp11[3:0];  // MemBlock

    // Registers
    reg[3:0] r;
    reg[3:0] s;

    // Temporaries
    wire[4:0] tmp14;
    wire[4:0] tmp18;
    wire[4:0] tmp22;
    wire[4:0] tmp26;
    wire[4:0] tmp30;
    wire[4:0] tmp34;
    wire[4:0] tmp38;
    wire[4:0] tmp42;
    wire[4:0] tmp46;
    wire[4:0] tmp50;
    wire[4:0] tmp54;
    wire[4:0] tmp58;
    wire[4:0] tmp60;
    wire[6:0] tmp66;
    wire[4:0] tmp70;
    wire[3:0] tmp72;
    wire[3:0] tmp76;
    wire[6:0] tmp79;

    // Combinational logic
    assign o = (tmp79[5:0]);
    assign tmp14 = (r + {{3 {1'd0}}, 1'd1});
    assign tmp18 = (r + {{3 {1'd0}}, 1'd1});
    assign tmp22 = (r + {{3 {1'd0}}, 1'd1});
    assign tmp26 = (r + {{3 {1'd0}}, 1'd1});
    assign tmp30 = (r + {{3 {1'd0}}, 1'd1});
    assign tmp34 = (r + {{3 {1'd0}}, 1'd1});
    assign tmp38 = (r + {{3 {1'd0}}, 1'd1});
    assign tmp42 = (r + {{3 {1'd0}}, 1'd1});
    assign tmp46 = (r + {{3 {1'd0}}, 1'd1});
    assign tmp50 = (r + {{3 {1'd0}}, 1'd1});
    assign tmp54 = (r + {{3 {1'd0}}, 1'd1});
    assign tmp58 = (r + {{3 {1'd0}}, 1'd1});
    assign tmp60 = (a + r);
    assign tmp66 = ((tmp60 + {{4 {1'd0}}, 1'd1}) - {{2 {1'd0}}, s});
    assign tmp70 = (a - {{3 {1'd0}}, 1'd1});
    assign tmp79 = ((tmp60 + {(1'd0), tmp72}) + {{2 {1'd0}}, tmp76});

    // Register logic
    always @(posedge clk) begin
        if (rst) begin
            r <= 4'd0;
            s <= 4'd13;
        end else begin
            r <= (tmp66[3:0]);
            s <= (tmp70[3:0]);
        end
    end

    // MemBlock z logic
    always @(posedge clk) begin
        z[2'd0] <= 4'd9;
    end

    // MemBlock tmp0 logic
    always @(posedge clk) begin
        tmp0[2'd0] <= a;
        tmp0[2'd1] <= (tmp14[3:0]);
    end
    assign tmp72 = tmp0[2'd0];

    // MemBlock tmp1 logic
    always @(posedge clk) begin
        tmp1[2'd0] <= a;
        tmp1[2'd1] <= (tmp18[3:0]);
    end
    assign tmp76 = tmp1[2'd0];

    // MemBlock tmp2 logic
    always @(posedge clk) begin
        tmp2[2'd0] <= a;
        tmp2[2'd1] <= (tmp22[3:0]);
    end

    // MemBlock tmp3 logic
    always @(posedge clk) begin
        tmp3[2'd0] <= a;
        tmp3[2'd1] <= (tmp26[3:0]);
    end

    // MemBlock tmp4 logic
    always @(posedge clk) begin
        tmp4[2'd0] <= a;
        tmp4[2'd1] <= (tmp30[3:0]);
    end

    // MemBlock tmp5 logic
    always @(posedge clk) begin
        tmp5[2'd0] <= a;
        tmp5[2'd1] <= (tmp34[3:0]);
    end

    // MemBlock tmp6 logic
    always @(posedge clk) begin
        tmp6[2'd0] <= a;
        tmp6[2'd1] <= (tmp38[3:0]);
    end

    // MemBlock tmp7 logic
    always @(posedge clk) begin
        tmp7[2'd0] <= a;
        tmp7[2'd1] <= (tmp42[3:0]);
    end

    // MemBlock tmp8 logic
    always @(posedge clk) begin
        tmp8[2'd0] <= a;
        tmp8[2'd1] <= (tmp46[3:0]);
    end

    // MemBlock tmp9 logic
    always @(posedge clk) begin
        tmp9[2'd0] <= a;
        tmp9[2'd1] <= (tmp50[3:0]);
    end

    // MemBlock tmp10 logic
    always @(posedge clk) begin
        tmp10[2'd0] <= a;
        tmp10[2'd1] <= (tmp54[3:0]);
    end

    // MemBlock tmp11 logic
    always @(posedge clk) begin
        tmp11[2'd0] <= a;
        tmp11[2'd1] <= (tmp58[3:0]);
    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;

    // Memories
    reg[7:0] rom[7:0];  // RomBlock
    reg[7:0] mem[255:0];  // MemBlock

    // Temporaries
    wire[7:0] tmp0;

    // Read-only memory data
    initial begin
        rom[0] = 8'ha;
        rom[1] = 8'h14;
        rom[2] = 8'h1e;
        rom[3] = 8'h28;
        rom[4] = 8'h32;
        rom[5] = 8'h3c;
        rom[6] = 8'h0;
        rom[7] = 8'h0;
    end

    // Combinational logic
    assign out1 = tmp0;

    // RomBlock rom logic
    assign tmp0 = rom[in1];

    // MemBlock mem logic
    always @(posedge clk) begin
        mem[tmp0] <= 8'd42;
    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;

    // Registers
    reg[3:0] tmp0;

    // Temporaries
    wire[4:0] tmp3;

    // Combinational logic
    assign o = tmp0;
    assign tmp3 = (tmp0 + {{3 {1'd0}}, 1'd1});

    // Register logic
    always @(posedge clk) begin
        if (rst) begin
            tmp0 <= 4'd2;
        end else begin
            tmp0 <= (tmp3[3:0]);
        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;

    // Registers
    reg[3:0] tmp0;

    // Temporaries
    wire[4:0] tmp3;

    // Combinational logic
    assign o = tmp0;
    assign tmp3 = (tmp0 + {{3 {1'd0}}, 1'd1});

    // Register logic
    always @(posedge clk or posedge rst) begin
        if (rst) begin
            tmp0 <= 4'd2;
        end else begin
            tmp0 <= (tmp3[3:0]);
        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;

    // Registers
    reg[3:0] tmp0;

    // Temporaries
    wire[4:0] tmp3;

    // Combinational logic
    assign o = tmp0;
    assign tmp3 = (tmp0 + {{3 {1'd0}}, 1'd1});

    // Register logic
    always @(posedge clk) begin
        tmp0 <= (tmp3[3:0]);
    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;

    // Registers
    reg[3:0] r;

    // Temporaries
    wire[4:0] tmp5;

    // Combinational logic
    assign tmp5 = (rst ? {{4 {1'd0}}, 1'd0} : (r + {{3 {1'd0}}, 1'd1}));

    // Register logic
    always @(posedge clk) begin
        r <= (tmp5[3:0]);
    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()

        self.maxDiff = 30000

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

    def test_textual_consistency_small(self):
        i = pyrtl.Const(12)
        j = pyrtl.Const(3, bitwidth=3)
        k = pyrtl.Const(38, name="k")
        o = pyrtl.Output(bitwidth=13, name="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(bitwidth=4, name="a")
        r = pyrtl.Register(bitwidth=4, name="r")
        s = pyrtl.Register(bitwidth=4, name="s", reset_value=13)
        # This will have mem id 0, so prints first despite actual name
        mt = pyrtl.MemBlock(bitwidth=4, addrwidth=2, name="z")
        m = [
            pyrtl.MemBlock(bitwidth=4, addrwidth=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(
            name="rom", bitwidth=8, addrwidth=3, romdata=rdata, pad_with_zeros=True
        )
        mem = pyrtl.MemBlock(name="mem", bitwidth=8, addrwidth=8)
        in1 = pyrtl.Input(bitwidth=3, name="in1")
        out1 = pyrtl.Output(bitwidth=8, name="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(bitwidth=4, reset_value=2)
        r.next <<= r + 1
        o = pyrtl.Output(bitwidth=4, name="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 self.assertRaisesRegex(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(bitwidth=1, name="rst")
        with self.assertRaisesRegex(
            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(bitwidth=1, name="rst")
        r = pyrtl.Register(bitwidth=4, name="r")
        r.next <<= pyrtl.select(rst, 0, r + 1)
        pyrtl.output_to_verilog(buffer, add_reset=False)
        self.assertEqual(buffer.getvalue(), verilog_custom_reset)

    def test_register_reset_value(self):
        register0 = pyrtl.Register(name="register0", bitwidth=8, reset_value=0)
        register1 = pyrtl.Register(name="register1", bitwidth=4, reset_value=1)
        register0.next <<= 0
        register1.next <<= 1

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

        self.assertTrue("reg[7:0] register0 = 8'd0" in buffer.getvalue())
        self.assertTrue("reg[3:0] register1 = 4'd1" in buffer.getvalue())

    def test_bit_slice_inputs(self):
        """Verify that wires are always declared for bit-slice inputs, even Consts."""
        a = pyrtl.Input(name="a", bitwidth=1)
        b = pyrtl.Input(name="b", bitwidth=1)
        c = pyrtl.Input(name="c", bitwidth=2)

        x = pyrtl.Output(name="x", bitwidth=1)
        y = pyrtl.Output(name="y", bitwidth=1)
        z = pyrtl.Output(name="z", bitwidth=1)

        x <<= pyrtl.Const(42)[1]

        y <<= (a + b)[1]

        t = pyrtl.WireVector()
        t <<= c
        z <<= t[1]

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

        # A constant should be declared for ``42``, even though it has no user-specified
        # name and only has one user, because that user is a bit-slice.
        self.assertTrue("wire[5:0] const_0_42 = 6'd42" in buffer.getvalue())
        # A temporary wire should be declared for ``a + b``, even though it has no name
        # and only has one user, because that user is a bit-slice.
        self.assertTrue("assign tmp1 = (a + b)" in buffer.getvalue())
        # A temporary wire should be declared for ``t``, even though it has no name and
        # only has one user and just passes through ``c``, because that user is a
        # bit-slice.
        self.assertTrue("assign tmp3 = c" in buffer.getvalue())


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):
        try:
            _ = subprocess.check_output(["yosys", "-V"])
        except OSError as exc:
            msg = "Testing Verilog input requires yosys"
            raise unittest.SkipTest(msg) from exc
        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 self.assertRaisesRegex(
            pyrtl.PyrtlError, "input_from_verilog expecting either open file or string"
        ):
            pyrtl.input_from_verilog(3)


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

    // block Inputs
    reg[1:0] a100;
    reg[3:0] w1;
    reg[2:0] w12;

    // block Outputs
    wire[1:0] out1;
    wire[8:0] out10;

    integer tb_addr;
    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 = 1'd0;
        rst = 1'd0;

        // Initialize Registers
        block.r1 = 3'd2;
        block.r2 = 4'd3;
        block.tmp0 = 8'd0;

        // Initialize MemBlocks
        for (tb_addr = 0; tb_addr < 32; tb_addr++) begin block.mem[tb_addr] = 4'd0; end
        block.mem[2] = 4'd9;
        block.mem[9] = 4'd12;
        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;

    // block Inputs
    reg[1:0] a100;
    reg[3:0] w1;
    reg[2:0] w12;

    // block Outputs
    wire[1:0] out1;
    wire[8:0] out10;

    integer tb_addr;
    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 = 1'd0;

        // Initialize Registers
        block.r1 = 3'd2;
        block.r2 = 4'd3;
        block.tmp0 = 8'd0;

        // Initialize MemBlocks
        for (tb_addr = 0; tb_addr < 32; tb_addr++) begin block.mem[tb_addr] = 4'd0; end
        block.mem[2] = 4'd9;
        block.mem[9] = 4'd12;
        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: E501

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

    // block Inputs
    reg rst;

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

    always
        #5 clk = ~clk;

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

        clk = 1'd0;

        // Initialize Registers
        block.r = 4'd0;
        $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()
        self.maxDiff = 10000

    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 = pyrtl.Simulation(tracer=pyrtl.SimulationTrace([counter_output, zero]))
        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.tracer)

    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(bitwidth=8)
        mem = pyrtl.MemBlock(name="mem", bitwidth=4, addrwidth=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 = pyrtl.Simulation(
            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.tracer

    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 self.assertRaisesRegex(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 self.assertRaisesRegex(
            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):
        rst = pyrtl.Input(1, "rst")
        r = pyrtl.Register(4, "r")
        r.next <<= pyrtl.select(rst, 0, r + 1)

        buffer = io.StringIO()
        pyrtl.output_verilog_testbench(buffer, add_reset=False)
        self.assertEqual(buffer.getvalue(), verilog_testbench_custom_reset)

    def test_only_initialize_memblocks(self):
        """Test that RomBlocks are not re-initialized by the testbench."""
        romblock = pyrtl.RomBlock(
            name="my_rom", bitwidth=3, addrwidth=2, romdata=[1, 2, 3, 4]
        )
        addr = pyrtl.Input(name="addr", bitwidth=2)
        data = pyrtl.Output(name="data", bitwidth=3)
        data <<= romblock[addr]

        buffer = io.StringIO()
        pyrtl.output_to_verilog(buffer)
        # The Verilog code should declare the RomBlock and define its values.
        self.assertTrue("reg[2:0] my_rom[3:0];  // RomBlock" in buffer.getvalue())
        self.assertTrue("my_rom[0] = 3'h1;" in buffer.getvalue())
        self.assertTrue("my_rom[1] = 3'h2;" in buffer.getvalue())
        self.assertTrue("my_rom[2] = 3'h3;" in buffer.getvalue())
        self.assertTrue("my_rom[3] = 3'h4;" in buffer.getvalue())

        buffer = io.StringIO()
        pyrtl.output_verilog_testbench(buffer, add_reset=False)
        # The testbench should not touch the RomBlock.
        self.assertTrue("my_rom" not in buffer.getvalue())


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 = io.StringIO()
        trace.print_trace(output, compact=True)
        self.assertEqual(output.getvalue(), correct_output)

    def test_bench_with_same_io_name(self):
        output = io.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()
