Exemple #1
0
    def test_net_wrong_bitwidth(self):
        net = self.new_net(op='x',
                           args=tuple(pyrtl.Input(i) for i in range(1, 4)))
        self.invalid_net("args have mismatched bitwidths", net)
        net = self.new_net(op='x',
                           args=tuple(pyrtl.Input(2) for i in range(3)))
        self.invalid_net("mux select must be a single bit", net)

        for op in '&|^n+-*<>=':
            net = self.new_net(op=op,
                               args=(tuple(
                                   pyrtl.Input(i) for i in range(2, 4))))
            self.invalid_net("args have mismatched bitwidths", net)

        net = self.new_net(op='m',
                           op_param=(1234, pyrtl.MemBlock(2, 3)),
                           args=1)
        self.invalid_net("mem addrwidth mismatch", net)

        net = self.new_net(op='@',
                           op_param=(1234, pyrtl.MemBlock(2, 2)),
                           args=tuple(pyrtl.Input(i) for i in (4, 2, 1)))
        self.invalid_net("mem addrwidth mismatch", net)
        net = self.new_net(op='@',
                           op_param=(1234, pyrtl.MemBlock(2, 2)),
                           args=tuple(pyrtl.Input(i) for i in (2, 4, 1)))
        self.invalid_net("mem bitwidth mismatch", net)
        net = self.new_net(op='@',
                           op_param=(1234, pyrtl.MemBlock(2, 2)),
                           args=3)
        self.invalid_net("mem write enable must be 1 bit", net)
Exemple #2
0
 def test_net_dest_wrong_bitwidth(self):
     for op in 'w~&|^nr':
         net = self.new_net(op=op,
                            args=1 if op in 'w~r' else 2,
                            dests=(pyrtl.Output(3), ))
         self.invalid_net("upper bits of destination unassigned", net)
     for op in '<>=':
         net = self.new_net(op=op, dests=(pyrtl.Output(2), ))
         self.invalid_net("destination should be of bitwidth=1", net)
     for op in '+-':
         net = self.new_net(op=op, dests=(pyrtl.Output(4), ))
         self.invalid_net("upper bits of destination unassigned", net)
     net = self.new_net(op='*', dests=(pyrtl.Output(5), ))
     self.invalid_net("upper bits of destination unassigned", net)
     net = self.new_net(op='x',
                        args=tuple(pyrtl.Input(1) for i in range(3)))
     self.invalid_net("upper bits of mux output undefined", net)
     net = self.new_net(op='c', args=3, dests=(pyrtl.Output(7), ))
     self.invalid_net("upper bits of concat output undefined", net)
     net = self.new_net(op='s', args=1, op_param=(1, ))
     self.invalid_net("upper bits of select output undefined", net)
     net = self.new_net(op='m',
                        op_param=(1234, pyrtl.MemBlock(3, 2)),
                        args=1)
     self.invalid_net("mem read dest bitwidth mismatch", net)
     net = self.new_net(op='@',
                        op_param=(1234, pyrtl.MemBlock(2, 2)),
                        args=tuple(pyrtl.Input(i) for i in (2, 2, 1)))
     self.invalid_net("mem write dest should be empty tuple", net)
Exemple #3
0
    def test_copy_mem(self):
        ins = [pyrtl.Input(5) for i in range(4)]
        out = pyrtl.Output(5)

        mem1 = pyrtl.MemBlock(5, 5, name='mem1')
        mem2 = pyrtl.MemBlock(5, 5, name='mem2')

        mem1_o1 = mem1[ins[0]]
        mem1[ins[1]] <<= ins[2]
        mem2_o2 = mem2[ins[3]]
        out <<= mem1_o1 & mem2_o2

        old_block = pyrtl.working_block()
        old_block.sanity_check()
        self.num_net_of_type('m', 2, old_block)
        self.num_net_of_type('@', 1, old_block)
        self.num_net_of_type('&', 1, old_block)
        self.num_memories(2, old_block)

        self.name_memories('mem1 mem2', old_block)

        new_block = transform.copy_block()
        self.num_net_of_type('m', 2, new_block)
        self.num_net_of_type('@', 1, new_block)
        self.num_net_of_type('&', 1, new_block)
        self.num_memories(2, new_block)

        self.name_memories('mem1 mem2', new_block)
Exemple #4
0
    def test_net_wrong_op_param_mem(self):
        for op in 'm@':
            net = self.new_net(op=op,
                               op_param=[1234, pyrtl.MemBlock(1, 1)],
                               args=tuple(
                                   pyrtl.Input(1)
                                   for i in range(1 if op == 'm' else 3)))
            self.invalid_net("mem op requires tuple op_param", net)
        for op in 'm@':
            net = self.new_net(op=op,
                               op_param=(1234, pyrtl.MemBlock(1, 1), 'hi'),
                               args=tuple(
                                   pyrtl.Input(1)
                                   for i in range(1 if op == 'm' else 3)))
            self.invalid_net("mem op requires 2 op_params in tuple", net)
        for op in 'm@':
            net = self.new_net(op=op,
                               op_param=('hi', pyrtl.MemBlock(1, 1)),
                               args=tuple(
                                   pyrtl.Input(1)
                                   for i in range(1 if op == 'm' else 3)))
            self.invalid_net("mem op requires first operand as int", net)

        class NotMem:  # so that some earlier mem tests can work properly
            def __init__(self, bw=1, aw=1):
                self.bitwidth, self.addrwidth = bw, aw

        for op in 'm@':
            net = self.new_net(op=op,
                               op_param=(1234, NotMem()),
                               args=tuple(
                                   pyrtl.Input(1)
                                   for i in range(1 if op == 'm' else 3)))
            self.invalid_net("mem op requires second operand of a memory type",
                             net)
Exemple #5
0
 def test_memblock_with_write_enable_with_equalsign(self):
     memory = pyrtl.MemBlock(bitwidth=self.bitwidth, addrwidth=self.addrwidth, name='memory')
     we = pyrtl.Const(1, bitwidth=1)
     self.output1 <<= memory[self.mem_read_address1]
     self.output2 <<= memory[self.mem_read_address2]
     memory[self.mem_write_address] <<= \
         pyrtl.MemBlock.EnabledWrite(self.mem_write_data, enable=we)
Exemple #6
0
 def test_async_check_should_pass_with_select(self):
     memory = pyrtl.MemBlock(
                 bitwidth=self.bitwidth, 
                 addrwidth=self.addrwidth-1,
                 name='memory')
     self.output1 <<= memory[self.mem_read_address1[0:-1]]
     pyrtl.working_block().sanity_check()
Exemple #7
0
 def test_area_est_unchanged(self):
     a = pyrtl.Const(2, 8)
     b = pyrtl.Const(85, 8)
     zero = pyrtl.Const(0, 1)
     reg = pyrtl.Register(8)
     mem = pyrtl.MemBlock(8, 8)
     out = pyrtl.Output(8)
     nota, aLSB, athenb, aORb, aANDb, aNANDb, \
     aXORb, aequalsb, altb, agtb, aselectb, \
     aplusb, bminusa, atimesb, memread = [pyrtl.Output() for i in range(15)]
     out <<= zero
     nota <<= ~a
     aLSB <<= a[0]
     athenb <<= pyrtl.concat(a, b)
     aORb <<= a | b
     aANDb <<= a & b
     aNANDb <<= a.nand(b)
     aXORb <<= a ^ b
     aequalsb <<= a==b
     altb <<= a < b
     agtb <<= a > b
     aselectb <<= pyrtl.select(zero, a, b)
     reg.next <<= a
     aplusb <<= a + b
     bminusa <<= a - b
     atimesb <<= a*b
     memread <<= mem[0]
     mem[1] <<= a
     self.assertEquals(estimate.area_estimation(), (0.00734386752, 0.01879779717361501))
Exemple #8
0
 def test_read_memindexed_ior(self):
     self.mem = pyrtl.MemBlock(8, 8)
     self.mem_val_map = {self.mem: {0: 5, 1: 4, 2: 3, 3: 2, 4: 1, 5: 0}}
     decide = pyrtl.Input(1)
     ind = pyrtl.Input(3)
     x = self.mem[ind]
     y = pyrtl.Output(8, 'y')
     z = pyrtl.Output(8, 'z')
     w = pyrtl.Output(8, 'w')
     with pyrtl.conditional_assignment:
         with decide:
             y |= x
             z |= x
         with pyrtl.otherwise:
             w |= x
     sim_trace = pyrtl.SimulationTrace()
     sim = pyrtl.Simulation(tracer=sim_trace,
                            memory_value_map=self.mem_val_map)
     for i in range(5):
         sim.step({decide: i % 2, ind: i})
         if i == 0:
             y_exp, z_exp, w_exp = 0, 0, 5
         elif i == 1:
             y_exp, z_exp, w_exp = 4, 4, 0
         elif i == 2:
             y_exp, z_exp, w_exp = 0, 0, 3
         elif i == 3:
             y_exp, z_exp, w_exp = 2, 2, 0
         else:
             y_exp, z_exp, w_exp = 0, 0, 1
         self.assertEqual(sim.inspect(y), y_exp)
         self.assertEqual(sim.inspect(z), z_exp)
         self.assertEqual(sim.inspect(w), w_exp)
     self.assertEqual(self.mem.num_read_ports, 1)
Exemple #9
0
 def test_time_est_unchanged(self):
     a = pyrtl.Const(2, 8)
     b = pyrtl.Const(85, 8)
     zero = pyrtl.Const(0, 1)
     reg = pyrtl.Register(8)
     mem = pyrtl.MemBlock(8, 8)
     out = pyrtl.Output(8)
     nota, aLSB, athenb, aORb, aANDb, aNANDb, \
     aXORb, aequalsb, altb, agtb, aselectb, \
     aplusb, bminusa, atimesb, memread = [pyrtl.Output() for i in range(15)]
     out <<= zero
     nota <<= ~a
     aLSB <<= a[0]
     athenb <<= pyrtl.concat(a, b)
     aORb <<= a | b
     aANDb <<= a & b
     aNANDb <<= a.nand(b)
     aXORb <<= a ^ b
     aequalsb <<= a == b
     altb <<= a < b
     agtb <<= a > b
     aselectb <<= pyrtl.select(zero, a, b)
     reg.next <<= a
     aplusb <<= a + b
     bminusa <<= a - b
     atimesb <<= a * b
     memread <<= mem[0]
     mem[1] <<= a
     timing = estimate.TimingAnalysis()
     self.assertEqual(timing.max_freq(), 610.2770657878676)
     self.assertEquals(timing.max_length(), 1255.6000000000001)
Exemple #10
0
 def test_over_max_write_ports(self):
     lim_memory = pyrtl.MemBlock(bitwidth=self.bitwidth, addrwidth=self.addrwidth,
                                 name='lim_memory', max_write_ports=4)
     for i in range(lim_memory.max_write_ports):
         lim_memory[self.mem_write_address] <<= pyrtl.Const(6)
     with self.assertRaises(pyrtl.PyrtlError):
         lim_memory[self.mem_write_address] <<= pyrtl.Const(6)
Exemple #11
0
 def test_async_check_should_pass(self):
     memory = pyrtl.MemBlock(bitwidth=self.bitwidth,
                             addrwidth=self.addrwidth,
                             name='memory')
     self.output1 <<= memory[self.mem_read_address1]
     memory[self.mem_write_address] <<= self.mem_write_data
     pyrtl.working_block().sanity_check()
Exemple #12
0
 def test_over_max_read_ports(self):
     lim_memory = pyrtl.MemBlock(bitwidth=self.bitwidth, addrwidth=self.addrwidth,
                                 name='lim_memory', max_read_ports=8)
     for i in range(lim_memory.max_read_ports):
         self.output1 <<= lim_memory[self.mem_read_address1]
     with self.assertRaises(pyrtl.PyrtlError):
         self.output2 <<= lim_memory[self.mem_read_address2]
Exemple #13
0
 def test_memblock_assign_with_extention(self):
     memory = pyrtl.MemBlock(bitwidth=self.bitwidth, addrwidth=self.addrwidth, name='memory')
     big_output = pyrtl.Output(self.bitwidth+1, "big_output")
     big_output <<= memory[self.mem_read_address1]
     self.output1 <<= 1
     self.output2 <<= 2
     memory[self.mem_write_address] <<= self.mem_write_data
     pyrtl.working_block().sanity_check()
Exemple #14
0
 def test_memblock_added_user_named(self):
     mem_name = 'small_memory'
     small_memory = pyrtl.MemBlock(bitwidth=self.bitwidth,
                                   addrwidth=self.addrwidth,
                                   name=mem_name,
                                   max_read_ports=2,
                                   max_write_ports=1)
     self.assertIs(pyrtl.working_block().get_memblock_by_name(mem_name),
                   small_memory)
Exemple #15
0
    def test_as_graph_duplicate_args(self):
        a = pyrtl.Input(3)
        x = pyrtl.Input(1)
        d = pyrtl.Output()
        b = a & a
        c = pyrtl.concat(a, a)
        m = pyrtl.MemBlock(addrwidth=3, bitwidth=3, name='m')
        m2 = pyrtl.MemBlock(addrwidth=1, bitwidth=1, name='m')
        d <<= m[a]
        m[a] <<= a
        m2[x] <<= pyrtl.MemBlock.EnabledWrite(x, x)

        b = pyrtl.working_block()
        src_g, dst_g = b.net_connections(False)
        self.check_graph_correctness(src_g, dst_g)

        src_g, dst_g = b.net_connections(True)
        self.check_graph_correctness(src_g, dst_g, True)
Exemple #16
0
    def setUp(self):
        pyrtl.reset_working_block()
        self.bitwidth = 3
        self.addrwidth = 3
        self.output1 = pyrtl.Output(self.bitwidth, "o1")
        self.output2 = pyrtl.Output(self.bitwidth, "o2")
        self.read_addr1 = pyrtl.Input(self.addrwidth)
        self.read_addr2 = pyrtl.Input(self.addrwidth)
        self.write_addr = pyrtl.Input(self.addrwidth)
        self.write_data = pyrtl.Input(self.bitwidth)
        self.mem1 = pyrtl.MemBlock(bitwidth=self.bitwidth, addrwidth=self.addrwidth, name='mem1')
        self.mem2 = pyrtl.MemBlock(bitwidth=self.bitwidth, addrwidth=self.addrwidth, name='mem2')
        self.output1 <<= self.mem1[self.read_addr1]
        self.output2 <<= self.mem1[self.read_addr2]
        self.mem1[self.write_addr] <<= self.write_data

        # build the actual simulation environment
        self.sim_trace = pyrtl.SimulationTrace()
Exemple #17
0
 def test_async_check_should_notpass_with_add(self):
     memory = pyrtl.MemBlock(bitwidth=self.bitwidth,
                             addrwidth=self.addrwidth,
                             name='memory')
     addr = pyrtl.WireVector(self.bitwidth)
     addr <<= self.mem_read_address1 + self.mem_read_address2
     self.output1 <<= memory[addr]
     with self.assertRaises(pyrtl.PyrtlError):
         pyrtl.working_block().sanity_check()
Exemple #18
0
 def test_async_check_should_pass_with_cat(self):
     memory = pyrtl.MemBlock(
                 bitwidth=self.bitwidth, 
                 addrwidth=self.addrwidth,
                 name='memory')
     addr = pyrtl.concat(self.mem_read_address1[0], self.mem_read_address2[0:-1])
     self.output1 <<= memory[addr]
     memory[self.mem_write_address] <<= self.mem_write_data
     pyrtl.working_block().sanity_check()
Exemple #19
0
 def test_basic_true_condition_memwrite(self):
     m = pyrtl.MemBlock(addrwidth=2, bitwidth=2, name='m')
     i = pyrtl.Register(bitwidth=2, name='i')
     o = pyrtl.WireVector(bitwidth=2, name='o')
     i.next <<= i + 1
     with pyrtl.conditional_assignment:
         with m[i]:
             m[i] <<= i
     o <<= m[i]
     self.check_trace('i 01230123\no 00000123\n')
Exemple #20
0
 def test_memblock_to_memblock_direct_operation(self):
     memory = pyrtl.MemBlock(bitwidth=self.bitwidth, addrwidth=self.addrwidth, name='memory')
     temp = (memory[self.mem_read_address1] == memory[self.mem_read_address2])
     temp = (memory[self.mem_read_address1] != memory[self.mem_read_address2])
     temp = (memory[self.mem_read_address1] & memory[self.mem_read_address2])
     temp = (memory[self.mem_read_address1] | memory[self.mem_read_address2])
     temp = (memory[self.mem_read_address1] + memory[self.mem_read_address2])
     temp = (memory[self.mem_read_address1] - memory[self.mem_read_address2])
     temp = (memory[self.mem_read_address1] * memory[self.mem_read_address2])
     self.output1 <<= temp
Exemple #21
0
    def test_2read_1write(self):
        small_memory = pyrtl.MemBlock(bitwidth=self.bitwidth, addrwidth=self.addrwidth,
                                      name='small_memory', max_read_ports=2, max_write_ports=1)
        temp = small_memory[self.mem_read_address1]  # read
        temp2 = small_memory[self.mem_read_address2]  # read

        self.output1 <<= temp
        self.output2 <<= temp2
        small_memory[self.mem_write_address] <<= pyrtl.Const(6)  # write
        pyrtl.working_block().sanity_check()
Exemple #22
0
 def test_inspect_mem(self):
     a = pyrtl.Input(8, 'a')
     b = pyrtl.Input(8, 'b')
     mem = pyrtl.MemBlock(8, 8, 'mem')
     mem[b] <<= a
     sim_trace = pyrtl.SimulationTrace()
     sim = self.sim(tracer=sim_trace)
     self.assertEqual(sim.inspect_mem(mem), {})
     sim.step({a: 3, b: 23})
     self.assertEqual(sim.inspect_mem(mem), {23: 3})
Exemple #23
0
    def test_all_mem_1(self):
        readAdd1, readAdd2, writeAdd1, writeAdd2, readData1, readData2 = \
            (pyrtl.Input(bitwidth=3) for i in range(6))

        dataOut = pyrtl.Output(bitwidth=3)

        memory = pyrtl.MemBlock(3, 3, asynchronous=True)

        memory[readAdd1 & readAdd2] <<= readData1 ^ readData2
        dataOut <<= memory[writeAdd1 | writeAdd2]
        self.everything_t_procedure()
Exemple #24
0
 def test_same_memblock_referenced_across_multiple_operators(self):
     mem_name = 'mem'
     mem = pyrtl.MemBlock(32, 5, mem_name)
     x = mem[0]
     mem[1] <<= 42
     mem = pyrtl.working_block().get_memblock_by_name(mem_name)
     for net in pyrtl.working_block().logic:
         if net.op == 'm':
             self.assertIs(net.op_param[1], mem)
         if net.op == '@':
             self.assertIs(net.op_param[1], mem)
Exemple #25
0
 def setUp(self):
     pyrtl.reset_working_block()
     self.bitwidth = 3
     self.addrwidth = 5
     self.output1 = pyrtl.Output(self.bitwidth, "output1")
     self.output2 = pyrtl.Output(self.bitwidth, "output2")
     self.mem_read_address1 = pyrtl.Input(self.addrwidth, name='mem_read_address1')
     self.mem_read_address2 = pyrtl.Input(self.addrwidth, name='mem_read_address2')
     self.mem_write_address = pyrtl.Input(self.addrwidth, name='mem_write_address')
     self.mem_write_data = pyrtl.Input(self.bitwidth, name='mem_write_data')
     self.memory = pyrtl.MemBlock(bitwidth=self.bitwidth, addrwidth=self.addrwidth,
                                  name='self.memory', max_read_ports=None)
Exemple #26
0
 def test_write_memindexed_ilshift(self):
     self.mem1 = pyrtl.MemBlock(8, 8)
     self.mem2 = pyrtl.MemBlock(8, 8, asynchronous=True)
     self.mem_val_map = {self.mem1: {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5}}
     addr1 = pyrtl.Input(3)
     addr2 = pyrtl.Input(3)  # will be one behind addr1
     inp = pyrtl.Input(3)
     x = self.mem1[addr1]  # value follows addr1
     self.mem2[x] <<= inp
     out = pyrtl.Output(9, name='out')
     out <<= self.mem2[addr2]  # one behind addr1, so one behind x
     sim_trace = pyrtl.SimulationTrace()
     sim = pyrtl.Simulation(tracer=sim_trace, memory_value_map=self.mem_val_map)
     for i in range(5):
         sim.step({
             addr1: i,
             addr2: 0 if i == 0 else i-1,  # one behind addr1
             inp: 5-i
         })
         self.assertEqual(sim.inspect(out), 0 if i ==0 else 5-(i-1))
     self.assertEqual(self.mem1.read_ports, 1)  # 2 b/c of the output read
     self.assertEqual(self.mem2.write_ports, 1)
Exemple #27
0
    def test_textual_consistency(self):
        from pyrtl.wire import _reset_wire_indexers
        from pyrtl.memory import _reset_memory_indexer

        # To compare textual consistency, need to make
        # sure we're starting at the same index for all
        # automatically created names.
        _reset_wire_indexers()
        _reset_memory_indexer()

        # 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)
        s = pyrtl.Register(4)
        # 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)
Exemple #28
0
    def test_as_graph_memory(self):
        m = pyrtl.MemBlock(addrwidth=2, bitwidth=2, name='m', max_read_ports=None)
        i = pyrtl.Register(bitwidth=2, name='i')
        o = pyrtl.WireVector(bitwidth=2, name='o')
        i.next <<= i + 1
        m[i] <<= pyrtl.mux((m[i] != 0), 0, m[i])
        o <<= m[i]

        b = pyrtl.working_block()
        src_g, dst_g = b.net_connections(False)
        self.check_graph_correctness(src_g, dst_g)

        src_g, dst_g = b.net_connections(True)
        self.check_graph_correctness(src_g, dst_g, True)
Exemple #29
0
 def test_write_memindexed_ior(self):
     self.mem1 = pyrtl.MemBlock(8, 8)
     self.mem2 = pyrtl.MemBlock(8, 8, asynchronous=True)
     self.mem_val_map = {self.mem1: {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5}}
     decide = pyrtl.Input(1)
     inp = pyrtl.Input(3)
     addr1 = pyrtl.Input(3)
     addr2 = pyrtl.Input(3)  # will be one behind addr1
     zero = pyrtl.Const(0, 3)
     x = self.mem1[addr1]
     x.name = 'x'
     out = pyrtl.Output(8, name='out')
     with pyrtl.conditional_assignment:
         with decide:
             self.mem2[x] |= inp
         with pyrtl.otherwise:
             self.mem2[x] |= zero
     out <<= self.mem2[addr2]  # one behind addr1, so one behind x
     sim_trace = pyrtl.SimulationTrace()
     sim = pyrtl.Simulation(tracer=sim_trace,
                            memory_value_map=self.mem_val_map)
     for i in range(5):
         sim.step({
             decide: i % 2,
             addr1: i,
             addr2: 0 if i == 0 else i - 1,  # one behind addr1
             inp: 5 - i
         })
         if (i == 0) | (i == 1) | (i == 3):
             out_exp = 0
         elif i == 2:
             out_exp = 4
         else:
             out_exp = 2
         self.assertEqual(sim.inspect(out), out_exp)
     self.assertEqual(self.mem1.num_read_ports, 1)
     self.assertEqual(self.mem2.num_write_ports, 1)
Exemple #30
0
 def test_basic_true_condition_memread(self):
     m = pyrtl.MemBlock(addrwidth=2, bitwidth=3, name='m')
     i = pyrtl.Register(bitwidth=3, name='i')
     o = pyrtl.WireVector(bitwidth=2, name='o')
     i.next <<= i + 1
     addr = i[0:2]
     with pyrtl.conditional_assignment:
         with i < 4:
             m[addr] |= i
         with pyrtl.otherwise:
             with addr[0]:
                 # this should happen every time because no
                 # state is being updated!
                 o <<= m[addr]
     self.check_trace('i 01234567\no 00000123\n')