From: Florent Kermarrec Date: Fri, 29 May 2015 10:26:34 +0000 (+0200) Subject: sdram: refactor minicon and fix issues with DDRx memories X-Git-Tag: 24jan2021_ls180~2238 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=f40140dba5b90738be97e11e25093f6de867e055;p=litex.git sdram: refactor minicon and fix issues with DDRx memories - simplify code - fix AddressSlicer - manage write latency and write to precharge timings - add odt/reset_n signals --- diff --git a/misoclib/mem/sdram/core/minicon/__init__.py b/misoclib/mem/sdram/core/minicon/__init__.py index c3078cd2..71be33fc 100644 --- a/misoclib/mem/sdram/core/minicon/__init__.py +++ b/misoclib/mem/sdram/core/minicon/__init__.py @@ -1,6 +1,7 @@ from migen.fhdl.std import * from migen.bus import wishbone from migen.genlib.fsm import FSM, NextState +from migen.genlib.misc import optree, Counter, WaitTimer from misoclib.mem.sdram.phy import dfi as dfibus @@ -10,37 +11,56 @@ class _AddressSlicer: self.colbits = colbits self.bankbits = bankbits self.rowbits = rowbits - self.max_a = colbits + rowbits + bankbits self.address_align = address_align + self.addressbits = colbits - address_align + bankbits + rowbits def row(self, address): - split = self.bankbits + self.colbits + split = self.bankbits + self.colbits - self.address_align if isinstance(address, int): return address >> split else: - return address[split:self.max_a] + return address[split:self.addressbits] def bank(self, address): - mask = 2**(self.bankbits + self.colbits) - 1 - shift = self.colbits + split = self.colbits - self.address_align if isinstance(address, int): - return (address & mask) >> shift + return (address & (2**(split + self.bankbits) - 1)) >> split else: - return address[self.colbits:self.colbits+self.bankbits] + return address[split:split+self.bankbits] def col(self, address): - split = self.colbits + split = self.colbits - self.address_align if isinstance(address, int): return (address & (2**split - 1)) << self.address_align else: return Cat(Replicate(0, self.address_align), address[:split]) +@DecorateModule(InsertReset) +@DecorateModule(InsertCE) +class _Bank(Module): + def __init__(self, geom_settings): + self.open = Signal() + self.row = Signal(geom_settings.rowbits) + + self.idle = Signal(reset=1) + self.hit = Signal() + + # # # + + row = Signal(geom_settings.rowbits) + self.sync += \ + If(self.open, + self.idle.eq(0), + row.eq(self.row) + ) + self.comb += self.hit.eq(~self.idle & (self.row == row)) + + class MiniconSettings: def __init__(self): pass - class Minicon(Module): def __init__(self, phy_settings, geom_settings, timing_settings): if phy_settings.memtype in ["SDR"]: @@ -49,146 +69,135 @@ class Minicon(Module): burst_length = phy_settings.nphases*2 # command multiplication*DDR address_align = log2_int(burst_length) - nbanks = range(2**geom_settings.bankbits) - A10_ENABLED = 0 - COLUMN = 1 - ROW = 2 - rdphase = phy_settings.rdphase - wrphase = phy_settings.wrphase + # # # self.dfi = dfi = dfibus.Interface(geom_settings.addressbits, geom_settings.bankbits, phy_settings.dfi_databits, phy_settings.nphases) - self.bus = bus = wishbone.Interface(data_width=phy_settings.nphases*flen(dfi.phases[rdphase].rddata)) - slicer = _AddressSlicer(geom_settings.colbits, geom_settings.bankbits, geom_settings.rowbits, address_align) - refresh_req = Signal() - refresh_ack = Signal() - refresh_counter = Signal(max=timing_settings.tREFI+1) - hit = Signal() - row_open = Signal() - row_closeall = Signal() - addr_sel = Signal(max=3, reset=A10_ENABLED) - has_curbank_openrow = Signal() + self.bus = bus = wishbone.Interface() - # Extra bit means row is active when asserted - self.openrow = openrow = Array(Signal(geom_settings.rowbits + 1) for b in nbanks) - - self.comb += [ - hit.eq(openrow[slicer.bank(bus.adr)] == Cat(slicer.row(bus.adr), 1)), - has_curbank_openrow.eq(openrow[slicer.bank(bus.adr)][-1]), - bus.dat_r.eq(Cat(phase.rddata for phase in dfi.phases)), - Cat(phase.wrdata for phase in dfi.phases).eq(bus.dat_w), - Cat(phase.wrdata_mask for phase in dfi.phases).eq(~bus.sel), - ] + rdphase = phy_settings.rdphase + wrphase = phy_settings.wrphase - for phase in dfi.phases: + precharge_all = Signal() + activate = Signal() + refresh = Signal() + write = Signal() + read = Signal() + + # Compute current column, bank and row from wishbone address + slicer = _AddressSlicer(geom_settings.colbits, + geom_settings.bankbits, + geom_settings.rowbits, + address_align) + + # Manage banks + bank_open = Signal() + bank_idle = Signal() + bank_hit = Signal() + + banks = [] + for i in range(2**geom_settings.bankbits): + bank = _Bank(geom_settings) self.comb += [ - phase.cke.eq(1), - phase.cs_n.eq(0), - phase.address.eq(Array([2**10, slicer.col(bus.adr), slicer.row(bus.adr)])[addr_sel]), - phase.bank.eq(slicer.bank(bus.adr)) + bank.open.eq(activate), + bank.reset.eq(precharge_all), + bank.row.eq(slicer.row(bus.adr)) ] + banks.append(bank) + self.submodules += banks - for b in nbanks: - self.sync += [ - If(row_open & (b == slicer.bank(bus.adr)), - openrow[b].eq(Cat(slicer.row(bus.adr), 1)), - ), - If(row_closeall, - openrow[b][-1].eq(0) - ) - ] + cases = {} + for i, bank in enumerate(banks): + cases[i] = [bank.ce.eq(1)] + self.comb += Case(slicer.bank(bus.adr), cases) - self.sync += [ - If(refresh_ack, - refresh_req.eq(0) - ), - If(refresh_counter == 0, - refresh_counter.eq(timing_settings.tREFI), - refresh_req.eq(1) - ).Else( - refresh_counter.eq(refresh_counter - 1) - ) + self.comb += [ + bank_hit.eq(optree("|", [bank.hit & bank.ce for bank in banks])), + bank_idle.eq(optree("|", [bank.idle & bank.ce for bank in banks])), ] - fsm = FSM() - self.submodules += fsm + # Timings + write2precharge_timer = WaitTimer(2 + timing_settings.tWR - 1) + self.submodules += write2precharge_timer + self.comb += write2precharge_timer.wait.eq(~write) + + refresh_timer = WaitTimer(timing_settings.tREFI) + self.submodules += refresh_timer + self.comb += refresh_timer.wait.eq(~refresh) + + # Main FSM + self.submodules.fsm = fsm = FSM() fsm.act("IDLE", - If(refresh_req, - NextState("PRECHARGEALL") + If(refresh_timer.done, + NextState("PRECHARGE-ALL") ).Elif(bus.stb & bus.cyc, - If(hit & bus.we, - NextState("WRITE") - ), - If(hit & ~bus.we, - NextState("READ") - ), - If(has_curbank_openrow & ~hit, - NextState("PRECHARGE") - ), - If(~has_curbank_openrow, + If(bank_hit, + If(bus.we, + NextState("WRITE") + ).Else( + NextState("READ") + ) + ).Elif(~bank_idle, + If(write2precharge_timer.done, + NextState("PRECHARGE") + ) + ).Else( NextState("ACTIVATE") - ), + ) ) ) fsm.act("READ", - # We output Column bits at address pins so A10 is 0 - # to disable row Auto-Precharge + read.eq(1), dfi.phases[rdphase].ras_n.eq(1), dfi.phases[rdphase].cas_n.eq(0), dfi.phases[rdphase].we_n.eq(1), dfi.phases[rdphase].rddata_en.eq(1), - addr_sel.eq(COLUMN), - NextState("READ-WAIT-ACK"), + NextState("WAIT-READ-DONE"), ) - fsm.act("READ-WAIT-ACK", + fsm.act("WAIT-READ-DONE", If(dfi.phases[rdphase].rddata_valid, - NextState("IDLE"), - bus.ack.eq(1) - ).Else( - NextState("READ-WAIT-ACK") + bus.ack.eq(1), + NextState("IDLE") ) ) fsm.act("WRITE", + write.eq(1), dfi.phases[wrphase].ras_n.eq(1), dfi.phases[wrphase].cas_n.eq(0), dfi.phases[wrphase].we_n.eq(0), dfi.phases[wrphase].wrdata_en.eq(1), - addr_sel.eq(COLUMN), + NextState("WRITE-LATENCY") + ) + fsm.act("WRITE-ACK", bus.ack.eq(1), NextState("IDLE") ) - fsm.act("PRECHARGEALL", - row_closeall.eq(1), + fsm.act("PRECHARGE-ALL", + precharge_all.eq(1), dfi.phases[rdphase].ras_n.eq(0), dfi.phases[rdphase].cas_n.eq(1), dfi.phases[rdphase].we_n.eq(0), - addr_sel.eq(A10_ENABLED), NextState("PRE-REFRESH") ) fsm.act("PRECHARGE", - # Notes: - # 1. we are presenting the column address so that A10 is low - # 2. since we always go to the ACTIVATE state, we do not need - # to assert row_close because it will be reopen right after. - NextState("TRP"), - addr_sel.eq(COLUMN), - dfi.phases[rdphase].ras_n.eq(0), - dfi.phases[rdphase].cas_n.eq(1), - dfi.phases[rdphase].we_n.eq(0) + # do no reset bank since we are going to re-open it + dfi.phases[0].ras_n.eq(0), + dfi.phases[0].cas_n.eq(1), + dfi.phases[0].we_n.eq(0), + NextState("TRP") ) fsm.act("ACTIVATE", - row_open.eq(1), + activate.eq(1), + dfi.phases[0].ras_n.eq(0), + dfi.phases[0].cas_n.eq(1), + dfi.phases[0].we_n.eq(1), NextState("TRCD"), - dfi.phases[rdphase].ras_n.eq(0), - dfi.phases[rdphase].cas_n.eq(1), - dfi.phases[rdphase].we_n.eq(1), - addr_sel.eq(ROW) ) fsm.act("REFRESH", - refresh_ack.eq(1), + refresh.eq(1), dfi.phases[rdphase].ras_n.eq(0), dfi.phases[rdphase].cas_n.eq(0), dfi.phases[rdphase].we_n.eq(1), @@ -198,3 +207,30 @@ class Minicon(Module): fsm.delayed_enter("PRE-REFRESH", "REFRESH", timing_settings.tRP-1) fsm.delayed_enter("TRCD", "IDLE", timing_settings.tRCD-1) fsm.delayed_enter("POST-REFRESH", "IDLE", timing_settings.tRFC-1) + fsm.delayed_enter("WRITE-LATENCY", "WRITE-ACK", phy_settings.write_latency-1) + + # DFI commands + for phase in dfi.phases: + if hasattr(phase, "reset_n"): + self.comb += phase.reset_n.eq(1) + if hasattr(phase, "odt"): + self.comb += phase.odt.eq(1) + self.comb += [ + phase.cke.eq(1), + phase.cs_n.eq(0), + phase.bank.eq(slicer.bank(bus.adr)), + If(precharge_all, + phase.address.eq(2**10) + ).Elif(activate, + phase.address.eq(slicer.row(bus.adr)) + ).Elif(write | read, + phase.address.eq(slicer.col(bus.adr)) + ) + ] + + # DFI datapath + self.comb += [ + bus.dat_r.eq(Cat(phase.rddata for phase in dfi.phases)), + Cat(phase.wrdata for phase in dfi.phases).eq(bus.dat_w), + Cat(phase.wrdata_mask for phase in dfi.phases).eq(~bus.sel), + ] diff --git a/misoclib/soc/sdram.py b/misoclib/soc/sdram.py index 44ad29bc..a6a8175c 100644 --- a/misoclib/soc/sdram.py +++ b/misoclib/soc/sdram.py @@ -31,8 +31,6 @@ class SDRAMSoC(SoC): if self._sdram_phy_registered: raise FinalizeError self._sdram_phy_registered = True - if isinstance(self.sdram_controller_settings, MiniconSettings) and phy.settings.memtype != "SDR": - raise NotImplementedError("Minicon only supports SDR memtype for now (" + phy.settings.memtype + ")") # Core self.submodules.sdram = SDRAMCore(phy, @@ -72,14 +70,15 @@ class SDRAMSoC(SoC): # MINICON frontend elif isinstance(self.sdram_controller_settings, MiniconSettings): - if sdram_width == 32: + burst_width = phy.settings.dfi_databits*phy.settings.nphases + if burst_width == 32: self.register_mem("main_ram", self.mem_map["main_ram"], self.sdram.controller.bus, main_ram_size) - elif sdram_width < 32: - self.submodules.downconverter = downconverter = wishbone.DownConverter(32, sdram_width) + elif burst_width < 32: + self.submodules.downconverter = downconverter = wishbone.DownConverter(32, burst_width) self.comb += Record.connect(downconverter.wishbone_o, self.sdram.controller.bus) self.register_mem("main_ram", self.mem_map["main_ram"], downconverter.wishbone_i, main_ram_size) else: - raise NotImplementedError("Unsupported SDRAM width of {} > 32".format(sdram_width)) + raise NotImplementedError("Unsupported burst width of {} > 32".format(burst_width)) def do_finalize(self): if not self.integrated_main_ram_size: