:param addr_width: width of the address bus
:param data_width: width of the data bus
:param we_width: number of write enable lines
+ :param transparent: whether a simultaneous read and write returns the
+ new value (True) or the old value (False) on the full
+ read port
"""
- def __init__(self, addr_width, data_width, we_width):
+ def __init__(self, addr_width, data_width, we_width, transparent):
self.addr_width = addr_width
self.data_width = data_width
self.we_width = we_width
+ self.transparent = transparent
# interface signals
self.wr_addr_i = Signal(addr_width); """write port address"""
self.wr_data_i = Signal(data_width); """write port data"""
mem0.phase.eq(phase),
mem1.phase.eq(phase),
]
+ # store the write information for the next cycle
+ last_addr = Signal(self.addr_width)
+ last_we = Signal(self.we_width)
+ last_data = Signal(self.data_width)
+ m.d.sync += [
+ last_addr.eq(self.wr_addr_i),
+ last_we.eq(self.wr_we_i),
+ last_data.eq(self.wr_data_i),
+ ]
+ # read path
# wire read address to memories, and XOR their output
+ xor_data = Signal(self.data_width)
m.d.comb += [
mem0.rd_addr_i.eq(self.rd_addr_i),
mem1.rd_addr_i.eq(self.rd_addr_i),
- self.rd_data_o.eq(mem0.rd_data_o ^ mem1.rd_data_o),
+ xor_data.eq(mem0.rd_data_o ^ mem1.rd_data_o),
]
+ if self.transparent:
+ # do the read and write addresses coincide?
+ same_read_write = Signal()
+ m.d.sync += same_read_write.eq(self.rd_addr_i == self.wr_addr_i)
+ gran = self.data_width // self.we_width
+ for i in range(self.we_width):
+ # when simultaneously reading and writing to the same location
+ # and write lane, bypass the memory, and output the write
+ # holding register instead
+ with m.If(same_read_write & last_we[i]):
+ m.d.comb += self.rd_data_o.word_select(i, gran).eq(
+ last_data.word_select(i, gran))
+ # otherwise, output the xor data
+ with m.Else():
+ m.d.comb += self.rd_data_o.word_select(i, gran).eq(
+ xor_data.word_select(i, gran))
+ # when not transparent, just output the memory contents (xor data)
+ else:
+ m.d.comb += self.rd_data_o.eq(xor_data)
# write path
# 1) read the memory location which is about to be written
m.d.comb += [
mem0.rdp_addr_i.eq(self.wr_addr_i),
mem1.rdp_addr_i.eq(self.wr_addr_i),
]
- # store the write information for the next cycle
- last_addr = Signal(self.addr_width)
- last_we = Signal(self.we_width)
- last_data = Signal(self.data_width)
- m.d.sync += [
- last_addr.eq(self.wr_addr_i),
- last_we.eq(self.wr_we_i),
- last_data.eq(self.wr_data_i),
- ]
# 2) write the XOR of the other memory data, and the desired value
m.d.comb += [
mem0.wr_addr_i.eq(last_addr),
class DualPortXorRegfileTestCase(FHDLTestCase):
- def test_case(self):
+ def do_test_case(self, transparent):
"""
Simulate some read/write/modify operations on the dual port register
file
"""
- dut = DualPortXorRegfile(7, 32, 4)
+ dut = DualPortXorRegfile(7, 32, 4, transparent)
sim = Simulator(dut)
sim.add_clock(1e-6)
yield from write(0, 0, 0)
yield
# test simultaneous read and write
- # non-transparent read: returns the old value
- yield from read(0x42, 0x78345612)
+ if transparent:
+ # transparent reads, returns the new value
+ yield from read(0x42, 0x78AA5666)
+ else:
+ # non-transparent read: returns the old value
+ yield from read(0x42, 0x78345612)
yield from write(0x42, 0b0101, 0x55AA9966)
yield
# after a cycle, returns the new value
sim.add_sync_process(process)
debug_file = 'test_dual_port_xor_regfile'
+ if transparent:
+ debug_file += '_transparent'
traces = ['clk', 'phase',
{'comment': 'write port'},
'wr_addr_i[6:0]', 'wr_we_i[3:0]', 'wr_data_i[31:0]',
with sim_writer:
sim.run()
+ def test_case(self):
+ with self.subTest("non-transparent reads"):
+ self.do_test_case(False)
+ with self.subTest("transparent reads"):
+ self.do_test_case(True)
+
if __name__ == "__main__":
unittest.main()