diff --git a/litedram/common.py b/litedram/common.py index b128c8a..89a59a0 100644 --- a/litedram/common.py +++ b/litedram/common.py @@ -89,7 +89,8 @@ def rdata_description(dw): class LiteDRAMPort: - def __init__(self, aw, dw, cd="sys"): + def __init__(self, mode, aw, dw, cd="sys"): + self.mode = mode self.aw = aw self.dw = dw self.cd = cd @@ -101,6 +102,16 @@ class LiteDRAMPort: self.rdata = stream.Endpoint(rdata_description(dw)) +class LiteDRAMWritePort(LiteDRAMPort): + def __init__(self, *args, **kwargs): + LiteDRAMPort.__init__(self, "write", *args, **kwargs) + + +class LiteDRAMReadPort(LiteDRAMPort): + def __init__(self, *args, **kwargs): + LiteDRAMPort.__init__(self, "read", *args, **kwargs) + + def cmd_request_layout(a, ba): return [ ("a", a), diff --git a/litedram/frontend/adaptation.py b/litedram/frontend/adaptation.py index fbe20a9..2845902 100644 --- a/litedram/frontend/adaptation.py +++ b/litedram/frontend/adaptation.py @@ -6,44 +6,47 @@ from litedram.common import * class LiteDRAMPortCDC(Module): - # TODO: check cmd/wdata/rdata fifo depths - def __init__(self, port_from, port_to): + def __init__(self, port_from, port_to, + cmd_depth=4, + wdata_depth=16, + rdata_depth=16): assert port_from.aw == port_to.aw assert port_from.dw == port_to.dw + assert port_from.mode == port_to.mode aw = port_from.aw dw = port_from.dw + mode = port_from.mode cd_from = port_from.cd cd_to = port_to.cd # # # - cmd_fifo = stream.AsyncFIFO([("we", 1), ("adr", aw)], 4) + cmd_fifo = stream.AsyncFIFO([("we", 1), ("adr", aw)], cmd_depth) cmd_fifo = ClockDomainsRenamer({"write": cd_from, - "read": cd_to})(cmd_fifo) + "read": cd_to})(cmd_fifo) self.submodules += cmd_fifo - self.comb += [ - port_from.cmd.connect(cmd_fifo.sink), - cmd_fifo.source.connect(port_to.cmd) - ] + self.submodules += stream.Pipeline(port_from.cmd, + cmd_fifo, + port_to.cmd) - wdata_fifo = stream.AsyncFIFO([("data", dw), ("we", dw//8)], 16) - wdata_fifo = ClockDomainsRenamer({"write": cd_from, - "read": cd_to})(wdata_fifo) - self.submodules += wdata_fifo - self.comb += [ - port_from.wdata.connect(wdata_fifo.sink), - wdata_fifo.source.connect(port_to.wdata) - ] + if mode == "write" or mode == "both": + wdata_fifo = stream.AsyncFIFO([("data", dw), ("we", dw//8)], wdata_depth) + wdata_fifo = ClockDomainsRenamer({"write": cd_from, + "read": cd_to})(wdata_fifo) + self.submodules += wdata_fifo + self.submodules += stream.Pipeline(port_from.wdata, + wdata_fifo, + port_to.wdata) - rdata_fifo = stream.AsyncFIFO([("data", dw)], 16) - rdata_fifo = ClockDomainsRenamer({"write": cd_to, - "read": cd_from})(rdata_fifo) - self.submodules += rdata_fifo - self.comb += [ - port_to.rdata.connect(rdata_fifo.sink), - rdata_fifo.source.connect(port_from.rdata) - ] + if mode == "read" or mode == "both": + rdata_fifo = stream.AsyncFIFO([("data", dw)], rdata_depth) + rdata_fifo = ClockDomainsRenamer({"write": cd_to, + "read": cd_from})(rdata_fifo) + self.submodules += rdata_fifo + self.submodules += stream.Pipeline(port_to.rdata, + rdata_fifo, + port_from.rdata) class LiteDRAMPortDownConverter(Module): @@ -60,12 +63,14 @@ class LiteDRAMPortDownConverter(Module): def __init__(self, port_from, port_to): assert port_from.cd == port_to.cd assert port_from.dw > port_to.dw + assert port_from.mode == port_to.mode if port_from.dw % port_to.dw: raise ValueError("Ratio must be an int") # # # ratio = port_from.dw//port_to.dw + mode = port_from.mode counter = Signal(max=ratio) counter_reset = Signal() @@ -97,38 +102,38 @@ class LiteDRAMPortDownConverter(Module): ) ) - wdata_converter = stream.StrideConverter(port_from.wdata.description, - port_to.wdata.description) - self.submodules += wdata_converter - self.comb += [ - port_from.wdata.connect(wdata_converter.sink), - wdata_converter.source.connect(port_to.wdata) - ] + if mode == "write" or mode == "both": + wdata_converter = stream.StrideConverter(port_from.wdata.description, + port_to.wdata.description) + self.submodules += wdata_converter + self.submodules += stream.Pipeline(port_from.wdata, + wdata_converter, + port_to.wdata) - rdata_converter = stream.StrideConverter(port_to.rdata.description, - port_from.rdata.description) - self.submodules += rdata_converter - self.comb += [ - port_to.rdata.connect(rdata_converter.sink), - rdata_converter.source.connect(port_from.rdata) - ] + if mode == "read" or mode == "both": + rdata_converter = stream.StrideConverter(port_to.rdata.description, + port_from.rdata.description) + self.submodules += rdata_converter + self.submodules += stream.Pipeline(port_to.rdata, + rdata_converter, + port_from.rdata) -class LiteDRAMPortUpConverter(Module): - # TODO: - # - handle all specials cases (incomplete / non aligned bursts) - # - add exceptions on datapath for such cases - """LiteDRAM port UpConverter +class LiteDRAMWritePortUpConverter(Module): + # TODO: finish and remove hack + """LiteDRAM write port UpConverter This module increase user port data width to fit controller data width. With N = port_to.dw/port_from.dw: - Address is adapted (divided by N) - - N writes and read from user are regrouped in a single one to the controller + - N writes from user are regrouped in a single one to the controller (when possible, ie when consecutive and bursting) """ def __init__(self, port_from, port_to): assert port_from.cd == port_to.cd assert port_from.dw < port_to.dw + assert port_from.mode == port_to.mode + assert port_from.mode == "write" if port_to.dw % port_from.dw: raise ValueError("Ratio must be an int") @@ -156,11 +161,7 @@ class LiteDRAMPortUpConverter(Module): counter_ce.eq(1), NextValue(we, port_from.cmd.we), NextValue(address, port_from.cmd.adr), - If(we, - NextState("RECEIVE") - ).Else( - NextState("GENERATE") # FIXME - ) + NextState("RECEIVE") ) ) fsm.act("RECEIVE", @@ -168,12 +169,7 @@ class LiteDRAMPortUpConverter(Module): If(port_from.cmd.valid, counter_ce.eq(1), If(counter == ratio-1, - If(we, - NextState("GENERATE") - ).Else( - NextState("IDLE"), # FIXME - port_from.cmd.ready.eq(1), - ) + NextState("GENERATE") ) ) ) @@ -182,43 +178,110 @@ class LiteDRAMPortUpConverter(Module): port_to.cmd.we.eq(we), port_to.cmd.adr.eq(address[log2_int(ratio):]), If(port_to.cmd.ready, - If(we, - NextState("IDLE"), - port_from.cmd.ready.eq(1) - ).Else( - NextState("RECEIVE") # FIXME - ) + NextState("IDLE") ) ) wdata_converter = stream.StrideConverter(port_from.wdata.description, port_to.wdata.description) self.submodules += wdata_converter - self.comb += [ - port_from.wdata.connect(wdata_converter.sink), - wdata_converter.source.connect(port_to.wdata) - ] + self.submodules += stream.Pipeline(port_from.wdata, + wdata_converter, + port_to.wdata) + + +class LiteDRAMReadPortUpConverter(Module): + # TODO: finish and remove hack + """LiteDRAM port UpConverter + + This module increase user port data width to fit controller data width. + With N = port_to.dw/port_from.dw: + - Address is adapted (divided by N) + - N read from user are regrouped in a single one to the controller + (when possible, ie when consecutive and bursting) + """ + def __init__(self, port_from, port_to): + assert port_from.cd == port_to.cd + assert port_from.dw < port_to.dw + assert port_from.mode == port_to.mode + assert port_from.mode == "read" + if port_to.dw % port_from.dw: + raise ValueError("Ratio must be an int") + + # # # + + ratio = port_to.dw//port_from.dw + + we = Signal() + address = Signal(port_to.aw) + + counter = Signal(max=ratio) + counter_reset = Signal() + counter_ce = Signal() + self.sync += \ + If(counter_reset, + counter.eq(0) + ).Elif(counter_ce, + counter.eq(counter + 1) + ) + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + port_from.cmd.ready.eq(1), + If(port_from.cmd.valid, + counter_ce.eq(1), + NextValue(we, port_from.cmd.we), + NextValue(address, port_from.cmd.adr), + NextState("GENERATE") + ) + ) + fsm.act("RECEIVE", + port_from.cmd.ready.eq(1), + If(port_from.cmd.valid, + counter_ce.eq(1), + If(counter == ratio-1, + NextState("IDLE") + ) + ) + ) + fsm.act("GENERATE", + port_to.cmd.valid.eq(1), + port_to.cmd.we.eq(we), + port_to.cmd.adr.eq(address[log2_int(ratio):]), + If(port_to.cmd.ready, + NextState("RECEIVE") + ) + ) rdata_converter = stream.StrideConverter(port_to.rdata.description, port_from.rdata.description) self.submodules += rdata_converter - self.comb += [ - port_to.rdata.connect(rdata_converter.sink), - rdata_converter.source.connect(port_from.rdata) - ] + self.submodules += stream.Pipeline(port_to.rdata, + rdata_converter, + port_from.rdata) class LiteDRAMPortConverter(Module): def __init__(self, port_from, port_to): assert port_from.cd == port_to.cd + assert port_from.mode == port_to.mode # # # + mode = port_from.mode + if port_from.dw > port_to.dw: converter = LiteDRAMPortDownConverter(port_from, port_to) self.submodules += converter elif port_from.dw < port_to.dw: - converter = LiteDRAMPortUpConverter(port_from, port_to) + if mode == "write": + converter = LiteDRAMWritePortUpConverter(port_from, port_to) + elif mode == "read": + converter = LiteDRAMReadPortUpConverter(port_from, port_to) + else: + raise NotImplementedError + + converter self.submodules += converter else: self.comb += [ diff --git a/litedram/frontend/crossbar.py b/litedram/frontend/crossbar.py index 5412b7d..b6a88e7 100644 --- a/litedram/frontend/crossbar.py +++ b/litedram/frontend/crossbar.py @@ -26,19 +26,19 @@ class LiteDRAMCrossbar(Module): self.masters = [] - def get_port(self, dw=None, cd="sys"): + def get_port(self, mode="both", dw=None, cd="sys"): if self.finalized: raise FinalizeError if dw is None: dw = self.dw # crossbar port - port = LiteDRAMPort(self.rca_bits + self.bank_bits, self.dw, "sys") + port = LiteDRAMPort(mode, self.rca_bits + self.bank_bits, self.dw, "sys") self.masters.append(port) # clock domain crossing if cd != "sys": - new_port = LiteDRAMPort(port.aw, port.dw, cd) + new_port = LiteDRAMPort(mode, port.aw, port.dw, cd) self.submodules += LiteDRAMPortCDC(new_port, port) port = new_port @@ -48,7 +48,7 @@ class LiteDRAMCrossbar(Module): adr_shift = log2_int(dw//self.dw) else: adr_shift = -log2_int(self.dw//dw) - new_port = LiteDRAMPort(port.aw + adr_shift, dw, cd=cd) + new_port = LiteDRAMPort(mode, port.aw + adr_shift, dw, cd=cd) self.submodules += ClockDomainsRenamer(cd)(LiteDRAMPortConverter(new_port, port)) port = new_port diff --git a/test/bist_async_tb.py b/test/bist_async_tb.py index 0710f61..f721ed6 100755 --- a/test/bist_async_tb.py +++ b/test/bist_async_tb.py @@ -58,22 +58,8 @@ class TB(Module): self.controller.nrowbits) # write port - write_crossbar_port = self.crossbar.get_port() - write_user_port = LiteDRAMPort(write_crossbar_port.aw, - write_crossbar_port.dw, - cd="write") - self.submodules += LiteDRAMPortCDC(write_user_port, - write_crossbar_port) - - - read_crossbar_port = self.crossbar.get_port() - read_user_port = LiteDRAMPort(read_crossbar_port.aw, - read_crossbar_port.dw, - cd="read") - - # read port - self.submodules += LiteDRAMPortCDC(read_user_port, - read_crossbar_port) + write_user_port = self.crossbar.get_port("write", cd="write") + read_user_port = self.crossbar.get_port("read", cd="read") # generator / checker self.submodules.generator = LiteDRAMBISTGenerator(write_user_port) diff --git a/test/bist_tb.py b/test/bist_tb.py index 7613001..6580b4c 100755 --- a/test/bist_tb.py +++ b/test/bist_tb.py @@ -4,7 +4,7 @@ from litex.gen import * from litex.soc.interconnect.stream import * -from litedram.common import LiteDRAMPort +from litedram.common import LiteDRAMWritePort, LiteDRAMReadPort from litedram.frontend.bist import LiteDRAMBISTGenerator from litedram.frontend.bist import LiteDRAMBISTChecker @@ -12,8 +12,8 @@ from test.common import DRAMMemory class TB(Module): def __init__(self): - self.write_port = LiteDRAMPort(aw=32, dw=32) - self.read_port = LiteDRAMPort(aw=32, dw=32) + self.write_port = LiteDRAMWritePort(aw=32, dw=32) + self.read_port = LiteDRAMReadPort(aw=32, dw=32) self.submodules.generator = LiteDRAMBISTGenerator(self.write_port) self.submodules.checker = LiteDRAMBISTChecker(self.read_port) diff --git a/test/downconverter_tb.py b/test/downconverter_tb.py index f3dbace..7b4eb57 100755 --- a/test/downconverter_tb.py +++ b/test/downconverter_tb.py @@ -5,56 +5,64 @@ from litex.gen import * from litex.soc.interconnect.stream import * from litex.soc.interconnect.stream_sim import check -from litedram.common import LiteDRAMPort +from litedram.common import LiteDRAMWritePort, LiteDRAMReadPort from litedram.frontend.adaptation import LiteDRAMPortConverter from test.common import * class TB(Module): def __init__(self): - self.user_port = LiteDRAMPort(aw=32, dw=64) - self.crossbar_port = LiteDRAMPort(aw=32, dw=32) - self.submodules.converter = LiteDRAMPortConverter(self.user_port, - self.crossbar_port) + self.write_user_port = LiteDRAMWritePort(aw=32, dw=64) + self.write_crossbar_port = LiteDRAMWritePort(aw=32, dw=32) + self.submodules.write_converter = LiteDRAMPortConverter(self.write_user_port, + self.write_crossbar_port) + + self.read_user_port = LiteDRAMReadPort(aw=32, dw=64) + self.read_crossbar_port = LiteDRAMReadPort(aw=32, dw=32) + self.submodules.read_converter = LiteDRAMPortConverter(self.read_user_port, + self.read_crossbar_port) + self.memory = DRAMMemory(32, 128) write_data = [seed_to_data(i, nbits=64) for i in range(8)] read_data = [] + @passive def read_generator(dut): - yield dut.user_port.rdata.ready.eq(1) + yield dut.read_user_port.rdata.ready.eq(1) while True: - if (yield dut.user_port.rdata.valid): - read_data.append((yield dut.user_port.rdata.data)) + if (yield dut.read_user_port.rdata.valid): + read_data.append((yield dut.read_user_port.rdata.data)) yield + def main_generator(dut): # write for i in range(8): - yield dut.user_port.cmd.valid.eq(1) - yield dut.user_port.cmd.we.eq(1) - yield dut.user_port.cmd.adr.eq(i) - yield dut.user_port.wdata.valid.eq(1) - yield dut.user_port.wdata.data.eq(write_data[i]) + yield dut.write_user_port.cmd.valid.eq(1) + yield dut.write_user_port.cmd.we.eq(1) + yield dut.write_user_port.cmd.adr.eq(i) + yield dut.write_user_port.wdata.valid.eq(1) + yield dut.write_user_port.wdata.data.eq(write_data[i]) yield - while (yield dut.user_port.cmd.ready) == 0: + while (yield dut.write_user_port.cmd.ready) == 0: yield - while (yield dut.user_port.wdata.ready) == 0: + while (yield dut.write_user_port.wdata.ready) == 0: yield yield # read - yield dut.user_port.rdata.ready.eq(1) + yield dut.read_user_port.rdata.ready.eq(1) for i in range(8): - yield dut.user_port.cmd.valid.eq(1) - yield dut.user_port.cmd.we.eq(0) - yield dut.user_port.cmd.adr.eq(i) + yield dut.read_user_port.cmd.valid.eq(1) + yield dut.read_user_port.cmd.we.eq(0) + yield dut.read_user_port.cmd.adr.eq(i) yield - while (yield dut.user_port.cmd.ready) == 0: + while (yield dut.read_user_port.cmd.ready) == 0: yield - yield dut.user_port.cmd.valid.eq(0) + yield dut.read_user_port.cmd.valid.eq(0) yield # delay @@ -70,8 +78,8 @@ if __name__ == "__main__": generators = { "sys" : [main_generator(tb), read_generator(tb), - tb.memory.write_generator(tb.crossbar_port), - tb.memory.read_generator(tb.crossbar_port)] + tb.memory.write_generator(tb.write_crossbar_port), + tb.memory.read_generator(tb.read_crossbar_port)] } clocks = {"sys": 10} - run_simulation(tb, generators, clocks, vcd_name="sim.vcd") \ No newline at end of file + run_simulation(tb, generators, clocks, vcd_name="sim.vcd") diff --git a/test/upconverter_tb.py b/test/upconverter_tb.py index bf6c6e9..61ea88e 100755 --- a/test/upconverter_tb.py +++ b/test/upconverter_tb.py @@ -5,59 +5,68 @@ from litex.gen import * from litex.soc.interconnect.stream import * from litex.soc.interconnect.stream_sim import check -from litedram.common import LiteDRAMPort +from litedram.common import LiteDRAMWritePort, LiteDRAMReadPort from litedram.frontend.adaptation import LiteDRAMPortConverter from test.common import * class TB(Module): def __init__(self): - self.user_port = LiteDRAMPort(aw=32, dw=32) - self.crossbar_port = LiteDRAMPort(aw=32, dw=64) - self.submodules.converter = LiteDRAMPortConverter(self.user_port, - self.crossbar_port) + self.write_user_port = LiteDRAMWritePort(aw=32, dw=32) + self.write_crossbar_port = LiteDRAMWritePort(aw=32, dw=64) + self.submodules.write_converter = LiteDRAMPortConverter(self.write_user_port, + self.write_crossbar_port) + + self.read_user_port = LiteDRAMReadPort(aw=32, dw=32) + self.read_crossbar_port = LiteDRAMReadPort(aw=32, dw=64) + self.submodules.read_converter = LiteDRAMPortConverter(self.read_user_port, + self.read_crossbar_port) + self.memory = DRAMMemory(64, 128) + write_data = [seed_to_data(i, nbits=32) for i in range(8)] read_data = [] + @passive def read_generator(dut): - yield dut.user_port.rdata.ready.eq(1) + yield dut.read_user_port.rdata.ready.eq(1) while True: - if (yield dut.user_port.rdata.valid): - read_data.append((yield dut.user_port.rdata.data)) + if (yield dut.read_user_port.rdata.valid): + read_data.append((yield dut.read_user_port.rdata.data)) yield + def main_generator(dut): # write for i in range(8): - yield dut.user_port.cmd.valid.eq(1) - yield dut.user_port.cmd.we.eq(1) - yield dut.user_port.cmd.adr.eq(i) + yield dut.write_user_port.cmd.valid.eq(1) + yield dut.write_user_port.cmd.we.eq(1) + yield dut.write_user_port.cmd.adr.eq(i) yield - while (yield dut.user_port.cmd.ready) == 0: + while (yield dut.write_user_port.cmd.ready) == 0: yield - yield dut.user_port.cmd.valid.eq(0) + yield dut.write_user_port.cmd.valid.eq(0) yield - yield dut.user_port.wdata.valid.eq(1) - yield dut.user_port.wdata.data.eq(write_data[i]) + yield dut.write_user_port.wdata.valid.eq(1) + yield dut.write_user_port.wdata.data.eq(write_data[i]) yield - while (yield dut.user_port.wdata.ready) == 0: + while (yield dut.write_user_port.wdata.ready) == 0: yield - yield dut.user_port.wdata.valid.eq(0) + yield dut.write_user_port.wdata.valid.eq(0) yield # read for i in range(8): for j in range(2): - yield dut.user_port.cmd.valid.eq(1) - yield dut.user_port.cmd.we.eq(0) - yield dut.user_port.cmd.adr.eq(i) + yield dut.read_user_port.cmd.valid.eq(1) + yield dut.read_user_port.cmd.we.eq(0) + yield dut.read_user_port.cmd.adr.eq(i) yield - while (yield dut.user_port.cmd.ready) == 0: + while (yield dut.read_user_port.cmd.ready) == 0: yield - yield dut.user_port.cmd.valid.eq(0) + yield dut.read_user_port.cmd.valid.eq(0) yield # delay @@ -74,8 +83,8 @@ if __name__ == "__main__": generators = { "sys" : [main_generator(tb), read_generator(tb), - tb.memory.write_generator(tb.crossbar_port), - tb.memory.read_generator(tb.crossbar_port)] + tb.memory.write_generator(tb.write_crossbar_port), + tb.memory.read_generator(tb.read_crossbar_port)] } clocks = {"sys": 10} run_simulation(tb, generators, clocks, vcd_name="sim.vcd")