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)
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)
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)
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)
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)
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()
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))
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)
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)
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)
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()
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]
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()
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)
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)
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()
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()
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()
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')
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
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()
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})
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()
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)
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)
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)
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)
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)
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)
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')