From 4e640592205086f2dfce158ec19296e4d6817513 Mon Sep 17 00:00:00 2001 From: Cesar Strauss Date: Sat, 2 Apr 2022 17:46:29 -0300 Subject: [PATCH] Implement transparent read ports on the phased write SRAM Add a multiplexer to select the write memory instead of the read memory, in case both port addresses coincide. --- src/soc/regfile/sram_wrapper.py | 70 ++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 10 deletions(-) diff --git a/src/soc/regfile/sram_wrapper.py b/src/soc/regfile/sram_wrapper.py index 22d43978..f80c4323 100644 --- a/src/soc/regfile/sram_wrapper.py +++ b/src/soc/regfile/sram_wrapper.py @@ -241,13 +241,17 @@ class PhasedDualPortRegfile(Elaboratable): :param we_width: number of write enable lines :param write_phase: indicates on which phase the write port will accept data + :param transparent: whether a simultaneous read and write returns the + new value (True) or the old value (False) """ - def __init__(self, addr_width, data_width, we_width, write_phase): + def __init__(self, addr_width, data_width, we_width, write_phase, + transparent=False): self.addr_width = addr_width self.data_width = data_width self.we_width = we_width self.write_phase = write_phase + self.transparent = transparent self.wr_addr_i = Signal(addr_width) """write port address""" self.wr_data_i = Signal(data_width) @@ -274,6 +278,8 @@ class PhasedDualPortRegfile(Elaboratable): # holding registers for the write port of the second memory last_wr_addr = Signal(self.addr_width) last_wr_we = Signal(self.we_width) + # do the read and write address coincide? + same_read_write = Signal() with m.If(self.phase == self.write_phase): # write phase, start a write on the first memory m.d.comb += mem1.a.eq(self.wr_addr_i) @@ -286,24 +292,40 @@ class PhasedDualPortRegfile(Elaboratable): m.d.comb += mem2.a.eq(self.rd_addr_i) # output previously read data from the first memory m.d.comb += self.rd_data_o.eq(mem1.q) + if self.transparent: + # remember whether we are reading from the same location we are + # writing + m.d.sync += same_read_write.eq(self.rd_addr_i == self.wr_addr_i) with m.Else(): # read phase, write last written data on second memory m.d.comb += mem2.a.eq(last_wr_addr) m.d.comb += mem2.we.eq(last_wr_we) # start a read on the first memory m.d.comb += mem1.a.eq(self.rd_addr_i) - # output previously read data from the second memory - m.d.comb += self.rd_data_o.eq(mem2.q) + if self.transparent: + with m.If(same_read_write): + # when transparent, and read and write addresses coincide, + # output the data just written + m.d.comb += self.rd_data_o.eq(mem1.q) + with m.Else(): + # otherwise, output previously read data + # from the second memory + m.d.comb += self.rd_data_o.eq(mem2.q) + else: + # always output the read data from the second memory, + # if not transparent + m.d.comb += self.rd_data_o.eq(mem2.q) + return m class PhasedDualPortRegfileTestCase(FHDLTestCase): - def do_test_phased_dual_port_regfile(self, write_phase): + def do_test_phased_dual_port_regfile(self, write_phase, transparent): """ Simulate some read/write/modify operations on the phased write memory """ - dut = PhasedDualPortRegfile(7, 32, 4, write_phase) + dut = PhasedDualPortRegfile(7, 32, 4, write_phase, transparent) sim = Simulator(dut) sim.add_clock(1e-6) @@ -364,24 +386,52 @@ class PhasedDualPortRegfileTestCase(FHDLTestCase): yield yield from read(0, 0x12563478) yield from skip_write() + yield + # try reading and writing to the same location, simultaneously + yield from read(0x42) + yield from write(0x42, 0b1111, 0x55AA9966) + yield + # ... and read again + yield from read(0x42) + yield from skip_write() + yield + if transparent: + # returns the value just written + yield from read(0, 0x55AA9966) + else: + # returns the old value + yield from read(0, 0x12563478) + yield from write(0, 0, 0) + yield + # after a cycle, always returns the new value + yield from read(0, 0x55AA9966) + yield from skip_write() sim.add_sync_process(process) + debug_file = f'test_phased_dual_port_{write_phase}' + if transparent: + debug_file += '_transparent' traces = ['clk', 'phase', 'wr_addr_i[6:0]', 'wr_we_i[3:0]', 'wr_data_i[31:0]', 'rd_addr_i[6:0]', 'rd_data_o[31:0]'] - write_gtkw(f'test_phased_dual_port_{write_phase}.gtkw', - f'test_phased_dual_port_{write_phase}.vcd', + write_gtkw(debug_file + '.gtkw', + debug_file + '.vcd', traces, module='top', zoom=-22) - sim_writer = sim.write_vcd(f'test_phased_dual_port_{write_phase}.vcd') + sim_writer = sim.write_vcd(debug_file + '.vcd') with sim_writer: sim.run() def test_phased_dual_port_regfile(self): """test both types (odd and even write ports) of phased write memory""" with self.subTest("writes happen on phase 0"): - self.do_test_phased_dual_port_regfile(0) + self.do_test_phased_dual_port_regfile(0, False) with self.subTest("writes happen on phase 1"): - self.do_test_phased_dual_port_regfile(1) + self.do_test_phased_dual_port_regfile(1, False) + """test again, with a transparent read port""" + with self.subTest("writes happen on phase 0 (transparent reads)"): + self.do_test_phased_dual_port_regfile(0, True) + with self.subTest("writes happen on phase 1 (transparent reads)"): + self.do_test_phased_dual_port_regfile(1, True) if __name__ == "__main__": -- 2.30.2