From: Yann Sionneau Date: Wed, 26 Nov 2014 09:37:50 +0000 (+0100) Subject: Refactor directory hierarchy of sdram phys and controllers X-Git-Tag: 24jan2021_ls180~2622 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=cf92821;p=litex.git Refactor directory hierarchy of sdram phys and controllers --- diff --git a/make.py b/make.py index 8de6e09e..99a12bb6 100755 --- a/make.py +++ b/make.py @@ -7,7 +7,7 @@ from migen.util.misc import autotype from migen.fhdl import simplify from misoclib.gensoc import cpuif -from misoclib.sdramphy import initsequence +from misoclib.sdram.phy import initsequence from misoc_import import misoc_import diff --git a/misoclib/dfii/__init__.py b/misoclib/dfii/__init__.py deleted file mode 100644 index ba53d376..00000000 --- a/misoclib/dfii/__init__.py +++ /dev/null @@ -1,57 +0,0 @@ -from migen.fhdl.std import * -from migen.bus import dfi -from migen.bank.description import * - -class PhaseInjector(Module, AutoCSR): - def __init__(self, phase): - self._command = CSRStorage(6) # cs, we, cas, ras, wren, rden - self._command_issue = CSR() - self._address = CSRStorage(flen(phase.address)) - self._baddress = CSRStorage(flen(phase.bank)) - self._wrdata = CSRStorage(flen(phase.wrdata)) - self._rddata = CSRStatus(flen(phase.rddata)) - - ### - - self.comb += [ - If(self._command_issue.re, - phase.cs_n.eq(~self._command.storage[0]), - phase.we_n.eq(~self._command.storage[1]), - phase.cas_n.eq(~self._command.storage[2]), - phase.ras_n.eq(~self._command.storage[3]) - ).Else( - phase.cs_n.eq(1), - phase.we_n.eq(1), - phase.cas_n.eq(1), - phase.ras_n.eq(1) - ), - phase.address.eq(self._address.storage), - phase.bank.eq(self._baddress.storage), - phase.wrdata_en.eq(self._command_issue.re & self._command.storage[4]), - phase.rddata_en.eq(self._command_issue.re & self._command.storage[5]), - phase.wrdata.eq(self._wrdata.storage), - phase.wrdata_mask.eq(0) - ] - self.sync += If(phase.rddata_valid, self._rddata.status.eq(phase.rddata)) - -class DFIInjector(Module, AutoCSR): - def __init__(self, a, ba, d, nphases=1): - inti = dfi.Interface(a, ba, d, nphases) - self.slave = dfi.Interface(a, ba, d, nphases) - self.master = dfi.Interface(a, ba, d, nphases) - - self._control = CSRStorage(4) # sel, cke, odt, reset_n - - for n, phase in enumerate(inti.phases): - setattr(self.submodules, "pi" + str(n), PhaseInjector(phase)) - - ### - - self.comb += If(self._control.storage[0], - self.slave.connect(self.master) - ).Else( - inti.connect(self.master) - ) - self.comb += [phase.cke.eq(self._control.storage[1]) for phase in inti.phases] - self.comb += [phase.odt.eq(self._control.storage[2]) for phase in inti.phases if hasattr(phase, "odt")] - self.comb += [phase.reset_n.eq(self._control.storage[3]) for phase in inti.phases if hasattr(phase, "reset_n")] diff --git a/misoclib/gensoc/__init__.py b/misoclib/gensoc/__init__.py index 661b1d10..d605a1a6 100644 --- a/misoclib/gensoc/__init__.py +++ b/misoclib/gensoc/__init__.py @@ -8,8 +8,10 @@ from migen.bank import csrgen from migen.bus import wishbone, csr, lasmibus, dfi from migen.bus import wishbone2lasmi, wishbone2csr -from misoclib import lm32, mor1kx, uart, dfii, lasmicon, identifier, timer, memtest -from misoclib.lasmicon.minicon import Minicon +from misoclib import lm32, mor1kx, uart, identifier, timer, memtest +from misoclib.sdram import lasmicon +from misoclib.sdram import dfii +from misoclib.sdram.minicon import Minicon class GenSoC(Module): csr_base = 0xe0000000 diff --git a/misoclib/lasmicon/__init__.py b/misoclib/lasmicon/__init__.py deleted file mode 100644 index 7dbf313f..00000000 --- a/misoclib/lasmicon/__init__.py +++ /dev/null @@ -1,54 +0,0 @@ -from collections import namedtuple - -from migen.fhdl.std import * -from migen.bus import dfi, lasmibus - -from misoclib.lasmicon.refresher import * -from misoclib.lasmicon.bankmachine import * -from misoclib.lasmicon.multiplexer import * - -PhySettingsT = namedtuple("PhySettings", "memtype dfi_d nphases rdphase wrphase rdcmdphase wrcmdphase cl cwl read_latency write_latency") -def PhySettings(memtype, dfi_d, nphases, rdphase, wrphase, rdcmdphase, wrcmdphase, cl, read_latency, write_latency, cwl=0): - return PhySettingsT(memtype, dfi_d, nphases, rdphase, wrphase, rdcmdphase, wrcmdphase, cl, cwl, read_latency, write_latency) - -GeomSettingsT = namedtuple("_GeomSettings", "bank_a row_a col_a mux_a") -def GeomSettings(bank_a, row_a, col_a): - return GeomSettingsT(bank_a, row_a, col_a, max(row_a, col_a)) - -TimingSettings = namedtuple("TimingSettings", "tRP tRCD tWR tWTR tREFI tRFC" \ - " req_queue_size read_time write_time") - -class LASMIcon(Module): - def __init__(self, phy_settings, geom_settings, timing_settings): - if phy_settings.memtype in ["SDR"]: - burst_length = phy_settings.nphases*1 # command multiplication*SDR - elif phy_settings.memtype in ["DDR", "LPDDR", "DDR2", "DDR3"]: - burst_length = phy_settings.nphases*2 # command multiplication*DDR - address_align = log2_int(burst_length) - - self.dfi = dfi.Interface(geom_settings.mux_a, - geom_settings.bank_a, - phy_settings.dfi_d, - phy_settings.nphases) - self.lasmic = lasmibus.Interface( - aw=geom_settings.row_a + geom_settings.col_a - address_align, - dw=phy_settings.dfi_d*phy_settings.nphases, - nbanks=2**geom_settings.bank_a, - req_queue_size=timing_settings.req_queue_size, - read_latency=phy_settings.read_latency+1, - write_latency=phy_settings.write_latency+1) - self.nrowbits = geom_settings.col_a - address_align - - ### - - self.submodules.refresher = Refresher(geom_settings.mux_a, geom_settings.bank_a, - timing_settings.tRP, timing_settings.tREFI, timing_settings.tRFC) - self.submodules.bank_machines = [BankMachine(geom_settings, timing_settings, address_align, i, - getattr(self.lasmic, "bank"+str(i))) - for i in range(2**geom_settings.bank_a)] - self.submodules.multiplexer = Multiplexer(phy_settings, geom_settings, timing_settings, - self.bank_machines, self.refresher, - self.dfi, self.lasmic) - - def get_csrs(self): - return self.multiplexer.get_csrs() diff --git a/misoclib/lasmicon/bankmachine.py b/misoclib/lasmicon/bankmachine.py deleted file mode 100644 index 9b08d28f..00000000 --- a/misoclib/lasmicon/bankmachine.py +++ /dev/null @@ -1,144 +0,0 @@ -from migen.fhdl.std import * -from migen.genlib.roundrobin import * -from migen.genlib.fsm import FSM, NextState -from migen.genlib.misc import optree -from migen.genlib.fifo import SyncFIFO - -from misoclib.lasmicon.multiplexer import * - -class _AddressSlicer: - def __init__(self, col_a, address_align): - self.col_a = col_a - self.address_align = address_align - - def row(self, address): - split = self.col_a - self.address_align - if isinstance(address, int): - return address >> split - else: - return address[split:] - - def col(self, address): - split = self.col_a - 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]) - -class BankMachine(Module): - def __init__(self, geom_settings, timing_settings, address_align, bankn, req): - self.refresh_req = Signal() - self.refresh_gnt = Signal() - self.cmd = CommandRequestRW(geom_settings.mux_a, geom_settings.bank_a) - - ### - - # Request FIFO - self.submodules.req_fifo = SyncFIFO([("we", 1), ("adr", flen(req.adr))], timing_settings.req_queue_size) - self.comb += [ - self.req_fifo.din.we.eq(req.we), - self.req_fifo.din.adr.eq(req.adr), - self.req_fifo.we.eq(req.stb), - req.req_ack.eq(self.req_fifo.writable), - - self.req_fifo.re.eq(req.dat_ack), - req.lock.eq(self.req_fifo.readable) - ] - reqf = self.req_fifo.dout - - slicer = _AddressSlicer(geom_settings.col_a, address_align) - - # Row tracking - has_openrow = Signal() - openrow = Signal(geom_settings.row_a) - hit = Signal() - self.comb += hit.eq(openrow == slicer.row(reqf.adr)) - track_open = Signal() - track_close = Signal() - self.sync += [ - If(track_open, - has_openrow.eq(1), - openrow.eq(slicer.row(reqf.adr)) - ), - If(track_close, - has_openrow.eq(0) - ) - ] - - # Address generation - s_row_adr = Signal() - self.comb += [ - self.cmd.ba.eq(bankn), - If(s_row_adr, - self.cmd.a.eq(slicer.row(reqf.adr)) - ).Else( - self.cmd.a.eq(slicer.col(reqf.adr)) - ) - ] - - # Respect write-to-precharge specification - precharge_ok = Signal() - t_unsafe_precharge = 2 + timing_settings.tWR - 1 - unsafe_precharge_count = Signal(max=t_unsafe_precharge+1) - self.comb += precharge_ok.eq(unsafe_precharge_count == 0) - self.sync += [ - If(self.cmd.stb & self.cmd.ack & self.cmd.is_write, - unsafe_precharge_count.eq(t_unsafe_precharge) - ).Elif(~precharge_ok, - unsafe_precharge_count.eq(unsafe_precharge_count-1) - ) - ] - - # Control and command generation FSM - fsm = FSM() - self.submodules += fsm - fsm.act("REGULAR", - If(self.refresh_req, - NextState("REFRESH") - ).Elif(self.req_fifo.readable, - If(has_openrow, - If(hit, - # NB: write-to-read specification is enforced by multiplexer - self.cmd.stb.eq(1), - req.dat_ack.eq(self.cmd.ack), - self.cmd.is_read.eq(~reqf.we), - self.cmd.is_write.eq(reqf.we), - self.cmd.cas_n.eq(0), - self.cmd.we_n.eq(~reqf.we) - ).Else( - NextState("PRECHARGE") - ) - ).Else( - NextState("ACTIVATE") - ) - ) - ) - fsm.act("PRECHARGE", - # Notes: - # 1. we are presenting the column address, A10 is always low - # 2. since we always go to the ACTIVATE state, we do not need - # to assert track_close. - If(precharge_ok, - self.cmd.stb.eq(1), - If(self.cmd.ack, NextState("TRP")), - self.cmd.ras_n.eq(0), - self.cmd.we_n.eq(0), - self.cmd.is_cmd.eq(1) - ) - ) - fsm.act("ACTIVATE", - s_row_adr.eq(1), - track_open.eq(1), - self.cmd.stb.eq(1), - self.cmd.is_cmd.eq(1), - If(self.cmd.ack, NextState("TRCD")), - self.cmd.ras_n.eq(0) - ) - fsm.act("REFRESH", - self.refresh_gnt.eq(precharge_ok), - track_close.eq(1), - self.cmd.is_cmd.eq(1), - If(~self.refresh_req, NextState("REGULAR")) - ) - fsm.delayed_enter("TRP", "ACTIVATE", timing_settings.tRP-1) - fsm.delayed_enter("TRCD", "REGULAR", timing_settings.tRCD-1) diff --git a/misoclib/lasmicon/minicon.py b/misoclib/lasmicon/minicon.py deleted file mode 100755 index 6cd36744..00000000 --- a/misoclib/lasmicon/minicon.py +++ /dev/null @@ -1,203 +0,0 @@ -from migen.fhdl.std import * -from migen.bus import wishbone -from migen.bus import dfi as dfibus -from migen.genlib.fsm import FSM, NextState - -class _AddressSlicer: - def __init__(self, col_a, bank_a, row_a, address_align): - self.col_a = col_a - self.bank_a = bank_a - self.row_a = row_a - self.max_a = col_a + row_a + bank_a - self.address_align = address_align - - def row(self, address): - split = self.bank_a + self.col_a - if isinstance(address, int): - return address >> split - else: - return address[split:self.max_a] - - def bank(self, address): - mask = 2**(self.bank_a + self.col_a) - 1 - shift = self.col_a - if isinstance(address, int): - return (address & mask) >> shift - else: - return address[self.col_a:self.col_a+self.bank_a] - - def col(self, address): - split = self.col_a - if isinstance(address, int): - return (address & (2**split - 1)) << self.address_align - else: - return Cat(Replicate(0, self.address_align), address[:split]) - -class Minicon(Module): - def __init__(self, phy_settings, geom_settings, timing_settings): - if phy_settings.memtype in ["SDR"]: - burst_length = phy_settings.nphases*1 # command multiplication*SDR - elif phy_settings.memtype in ["DDR", "LPDDR", "DDR2", "DDR3"]: - burst_length = phy_settings.nphases*2 # command multiplication*DDR - address_align = log2_int(burst_length) - - nbanks = range(2**geom_settings.bank_a) - A10_ENABLED = 0 - COLUMN = 1 - ROW = 2 - rdphase = phy_settings.rdphase - wrphase = phy_settings.wrphase - rdcmdphase = phy_settings.rdcmdphase - wrcmdphase = phy_settings.wrcmdphase - - self.dfi = dfi = dfibus.Interface(geom_settings.mux_a, - geom_settings.bank_a, - phy_settings.dfi_d, - phy_settings.nphases) - - self.bus = bus = wishbone.Interface(data_width=phy_settings.nphases*flen(dfi.phases[rdphase].rddata)) - slicer = _AddressSlicer(geom_settings.col_a, geom_settings.bank_a, geom_settings.row_a, address_align) - req_addr = Signal(geom_settings.col_a + geom_settings.bank_a + geom_settings.row_a) - refresh_req = Signal() - refresh_ack = Signal() - wb_access = 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() - cl_counter = Signal(max=phy_settings.cl+1) - - # Extra bit means row is active when asserted - self.openrow = openrow = Array(Signal(geom_settings.row_a + 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]), - wb_access.eq(bus.stb & bus.cyc), - 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), - ] - - for phase in dfi.phases: - self.comb += [ - phase.cke.eq(1), - phase.address.eq(Array([2**10, slicer.col(bus.adr), slicer.row(bus.adr)])[addr_sel]), - If(wb_access, - phase.bank.eq(slicer.bank(bus.adr)) - ) - ] - phase.cs_n.reset = 0 - phase.ras_n.reset = 1 - phase.cas_n.reset = 1 - phase.we_n.reset = 1 - - 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) - ) - ] - - 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) - ) - ] - - fsm = FSM() - self.submodules += fsm - fsm.act("IDLE", - If(refresh_req, - NextState("PRECHARGEALL") - ).Elif(wb_access, - If(hit & bus.we, - NextState("WRITE"), - ), - If(hit & ~bus.we, - NextState("READ"), - ), - If(has_curbank_openrow & ~hit, - NextState("PRECHARGE") - ), - If(~has_curbank_openrow, - NextState("ACTIVATE") - ), - ) - ) - fsm.act("READ", - # We output Column bits at address pins so that A10 is 0 - # to disable row Auto-Precharge - dfi.phases[rdcmdphase].ras_n.eq(1), - dfi.phases[rdcmdphase].cas_n.eq(0), - dfi.phases[rdcmdphase].we_n.eq(1), - dfi.phases[rdphase].rddata_en.eq(1), - addr_sel.eq(COLUMN), - NextState("READ-WAIT-ACK"), - ) - fsm.act("READ-WAIT-ACK", - If(dfi.phases[rdphase].rddata_valid, - NextState("IDLE"), - bus.ack.eq(1) - ).Else( - NextState("READ-WAIT-ACK") - ) - ) - fsm.act("WRITE", - dfi.phases[wrcmdphase].ras_n.eq(1), - dfi.phases[wrcmdphase].cas_n.eq(0), - dfi.phases[wrcmdphase].we_n.eq(0), - dfi.phases[wrphase].wrdata_en.eq(1), - addr_sel.eq(COLUMN), - bus.ack.eq(1), - NextState("IDLE") - ) - fsm.act("PRECHARGEALL", - row_closeall.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) - ) - fsm.act("ACTIVATE", - row_open.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), - dfi.phases[rdphase].ras_n.eq(0), - dfi.phases[rdphase].cas_n.eq(0), - dfi.phases[rdphase].we_n.eq(1), - NextState("POST-REFRESH") - ) - fsm.delayed_enter("TRP", "ACTIVATE", timing_settings.tRP-1) - 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) diff --git a/misoclib/lasmicon/minicontb.py b/misoclib/lasmicon/minicontb.py deleted file mode 100755 index 8fb982b4..00000000 --- a/misoclib/lasmicon/minicontb.py +++ /dev/null @@ -1,192 +0,0 @@ -from migen.fhdl.std import * -from migen.bus.transactions import TRead, TWrite -from migen.bus import wishbone -from migen.sim.generic import Simulator -from migen.sim import icarus -from mibuild.platforms import papilio_pro as board -from misoclib import lasmicon -from misoclib.lasmicon.minicon import Minicon -from misoclib.sdramphy import gensdrphy -from itertools import chain -from os.path import isfile -import sys - -clk_freq = 80000000 - -from math import ceil - -def ns(t, margin=True): - clk_period_ns = 1000000000/clk_freq - if margin: - t += clk_period_ns/2 - return ceil(t/clk_period_ns) - -class MiniconTB(Module): - def __init__(self, sdrphy, dfi, sdram_geom, sdram_timing, pads, sdram_clk): - - self.clk_freq = 80000000 - phy_settings = sdrphy.phy_settings - rdphase = phy_settings.rdphase - self.submodules.slave = Minicon(phy_settings, sdram_geom, sdram_timing) - - self.submodules.tap = wishbone.Tap(self.slave.bus) - self.submodules.dc = dc = wishbone.DownConverter(32, phy_settings.nphases*flen(dfi.phases[rdphase].rddata)) - self.submodules.master = wishbone.Initiator(self.genxfers(), bus=dc.wishbone_i) - self.submodules.intercon = wishbone.InterconnectPointToPoint(dc.wishbone_o, self.slave.bus) - - self.submodules.sdrphy = self.sdrphy = sdrphy - self.dfi = dfi - self.pads = pads - - self.specials += Instance("mt48lc4m16a2", - io_Dq=pads.dq, - i_Addr=pads.a, - i_Ba=pads.ba, - i_Clk=ClockSignal(), - i_Cke=pads.cke, - i_Cs_n=pads.cs_n, - i_Ras_n=pads.ras_n, - i_Cas_n=pads.cas_n, - i_We_n=pads.we_n, - i_Dqm=pads.dm - ) - - def genxfers(self): - cycle = 0 - for a in chain(range(4),range(256,260),range(1024,1028)): - t = TRead(a) - yield t - print("read {} in {} cycles".format(t.data, t.latency)) - for a in chain(range(4),range(256,260),range(1024,1028),range(4096,4100)): - t = TWrite(a, 0xaa55aa55+cycle) - cycle += 1 - yield t - print("read {} in {} cycles".format(t.data, t.latency)) - for a in chain(range(4),range(256,260),range(1024,1028),range(4096,4100)): - t = TRead(a) - yield t - print("read {} in {} cycles".format(t.data, t.latency)) - - def gen_simulation(self, selfp): - dfi = selfp.dfi - phy = self.sdrphy - rdphase = phy.phy_settings.rdphase - cycle = 0 - - while True: - yield - -class MyTopLevel: - def __init__(self, vcd_name=None, vcd_level=1, - top_name="top", dut_type="dut", dut_name="dut", - cd_name="sys", clk_period=10): - self.vcd_name = vcd_name - self.vcd_level = vcd_level - self.top_name = top_name - self.dut_type = dut_type - self.dut_name = dut_name - - self._cd_name = cd_name - self._clk_period = clk_period - - cd = ClockDomain(self._cd_name) - cd_ps = ClockDomain("sys_ps") - self.clock_domains = [cd, cd_ps] - self.ios = {cd.clk, cd.rst, cd_ps.clk} - - def get(self, sockaddr): - template1 = """`timescale 1ns / 1ps - -module {top_name}(); - -reg {clk_name}; -reg {rst_name}; -reg sys_ps_clk; - -initial begin - {rst_name} <= 1'b1; - @(posedge {clk_name}); - {rst_name} <= 1'b0; -end - -always begin - {clk_name} <= 1'b0; - #{hclk_period}; - {clk_name} <= 1'b1; - #{hclk_period}; -end - -always @(posedge {clk_name} or negedge {clk_name}) - sys_ps_clk <= #({hclk_period}*2-3) {clk_name}; - -{dut_type} {dut_name}( - .{rst_name}({rst_name}), - .{clk_name}({clk_name}), - .sys_ps_clk(sys_ps_clk) -); - -initial $migensim_connect("{sockaddr}"); -always @(posedge {clk_name}) $migensim_tick; -""" - template2 = """ -initial begin - $dumpfile("{vcd_name}"); - $dumpvars({vcd_level}, {dut_name}); -end -""" - r = template1.format(top_name=self.top_name, - dut_type=self.dut_type, - dut_name=self.dut_name, - clk_name=self._cd_name + "_clk", - rst_name=self._cd_name + "_rst", - hclk_period=str(self._clk_period/2), - sockaddr=sockaddr) - if self.vcd_name is not None: - r += template2.format(vcd_name=self.vcd_name, - vcd_level=str(self.vcd_level), - dut_name=self.dut_name) - r += "\nendmodule" - return r - - -if __name__ == "__main__": - - plat = board.Platform() - - sdram_geom = lasmicon.GeomSettings( - bank_a=2, - row_a=12, - col_a=8 - ) - - sdram_timing = lasmicon.TimingSettings( - tRP=ns(15), - tRCD=ns(15), - tWR=ns(14), - tWTR=2, - tREFI=ns(64*1000*1000/4096, False), - tRFC=ns(66), - req_queue_size=8, - read_time=32, - write_time=16 - ) - - sdram_pads = plat.request("sdram") - sdram_clk = plat.request("sdram_clock") - - sdrphy = gensdrphy.GENSDRPHY(sdram_pads) - -# This sets CL to 2 during LMR done on 1st cycle - sdram_pads.a.reset = 1<<5 - - s = MiniconTB(sdrphy, sdrphy.dfi, sdram_geom, sdram_timing, pads=sdram_pads, sdram_clk=sdram_clk) - - extra_files = [ "sdram_model/mt48lc4m16a2.v" ] - - if not isfile(extra_files[0]): - print("ERROR: You need to download Micron Verilog simulation model for MT48LC4M16A2 and put it in sdram_model/mt48lc4m16a2.v") - print("File can be downloaded from this URL: http://www.micron.com/-/media/documents/products/sim%20model/dram/dram/4054mt48lc4m16a2.zip") - sys.exit(1) - - with Simulator(s, MyTopLevel("top.vcd", clk_period=int(1/0.08)), icarus.Runner(extra_files=extra_files, keep_files=True)) as sim: - sim.run(5000) diff --git a/misoclib/lasmicon/multiplexer.py b/misoclib/lasmicon/multiplexer.py deleted file mode 100644 index f72bf86c..00000000 --- a/misoclib/lasmicon/multiplexer.py +++ /dev/null @@ -1,214 +0,0 @@ -from migen.fhdl.std import * -from migen.genlib.roundrobin import * -from migen.genlib.misc import optree -from migen.genlib.fsm import FSM, NextState -from migen.bank.description import AutoCSR - -from misoclib.lasmicon.perf import Bandwidth - -class CommandRequest: - def __init__(self, a, ba): - self.a = Signal(a) - self.ba = Signal(ba) - self.cas_n = Signal(reset=1) - self.ras_n = Signal(reset=1) - self.we_n = Signal(reset=1) - -class CommandRequestRW(CommandRequest): - def __init__(self, a, ba): - CommandRequest.__init__(self, a, ba) - self.stb = Signal() - self.ack = Signal() - self.is_cmd = Signal() - self.is_read = Signal() - self.is_write = Signal() - -class _CommandChooser(Module): - def __init__(self, requests): - self.want_reads = Signal() - self.want_writes = Signal() - self.want_cmds = Signal() - # NB: cas_n/ras_n/we_n are 1 when stb is inactive - self.cmd = CommandRequestRW(flen(requests[0].a), flen(requests[0].ba)) - - ### - - rr = RoundRobin(len(requests), SP_CE) - self.submodules += rr - - self.comb += [rr.request[i].eq(req.stb & ((req.is_cmd & self.want_cmds) | ((req.is_read == self.want_reads) | (req.is_write == self.want_writes)))) - for i, req in enumerate(requests)] - - stb = Signal() - self.comb += stb.eq(Array(req.stb for req in requests)[rr.grant]) - for name in ["a", "ba", "is_read", "is_write", "is_cmd"]: - choices = Array(getattr(req, name) for req in requests) - self.comb += getattr(self.cmd, name).eq(choices[rr.grant]) - for name in ["cas_n", "ras_n", "we_n"]: - # we should only assert those signals when stb is 1 - choices = Array(getattr(req, name) for req in requests) - self.comb += If(self.cmd.stb, getattr(self.cmd, name).eq(choices[rr.grant])) - self.comb += self.cmd.stb.eq(stb \ - & ((self.cmd.is_cmd & self.want_cmds) | ((self.cmd.is_read == self.want_reads) \ - & (self.cmd.is_write == self.want_writes)))) - - self.comb += [If(self.cmd.stb & self.cmd.ack & (rr.grant == i), req.ack.eq(1)) - for i, req in enumerate(requests)] - self.comb += rr.ce.eq(self.cmd.ack) - -class _Steerer(Module): - def __init__(self, commands, dfi): - ncmd = len(commands) - nph = len(dfi.phases) - self.sel = [Signal(max=ncmd) for i in range(nph)] - - ### - - def stb_and(cmd, attr): - if not hasattr(cmd, "stb"): - return 0 - else: - return cmd.stb & getattr(cmd, attr) - for phase, sel in zip(dfi.phases, self.sel): - self.comb += [ - phase.cke.eq(1), - phase.cs_n.eq(0) - ] - if hasattr(phase, "odt"): - self.comb += phase.odt.eq(1) - if hasattr(phase, "reset_n"): - self.comb += phase.reset_n.eq(1) - self.sync += [ - phase.address.eq(Array(cmd.a for cmd in commands)[sel]), - phase.bank.eq(Array(cmd.ba for cmd in commands)[sel]), - phase.cas_n.eq(Array(cmd.cas_n for cmd in commands)[sel]), - phase.ras_n.eq(Array(cmd.ras_n for cmd in commands)[sel]), - phase.we_n.eq(Array(cmd.we_n for cmd in commands)[sel]), - phase.rddata_en.eq(Array(stb_and(cmd, "is_read") for cmd in commands)[sel]), - phase.wrdata_en.eq(Array(stb_and(cmd, "is_write") for cmd in commands)[sel]) - ] - -class Multiplexer(Module, AutoCSR): - def __init__(self, phy_settings, geom_settings, timing_settings, bank_machines, refresher, dfi, lasmic): - assert(phy_settings.nphases == len(dfi.phases)) - - # Command choosing - requests = [bm.cmd for bm in bank_machines] - choose_cmd = _CommandChooser(requests) - choose_req = _CommandChooser(requests) - self.comb += [ - choose_cmd.want_reads.eq(0), - choose_cmd.want_writes.eq(0) - ] - if phy_settings.nphases == 1: - self.comb += [ - choose_cmd.want_cmds.eq(1), - choose_req.want_cmds.eq(1) - ] - self.submodules += choose_cmd, choose_req - - # Command steering - nop = CommandRequest(geom_settings.mux_a, geom_settings.bank_a) - commands = [nop, choose_cmd.cmd, choose_req.cmd, refresher.cmd] # nop must be 1st - (STEER_NOP, STEER_CMD, STEER_REQ, STEER_REFRESH) = range(4) - steerer = _Steerer(commands, dfi) - self.submodules += steerer - - # Read/write turnaround - read_available = Signal() - write_available = Signal() - self.comb += [ - read_available.eq(optree("|", [req.stb & req.is_read for req in requests])), - write_available.eq(optree("|", [req.stb & req.is_write for req in requests])) - ] - - def anti_starvation(timeout): - en = Signal() - max_time = Signal() - if timeout: - t = timeout - 1 - time = Signal(max=t+1) - self.comb += max_time.eq(time == 0) - self.sync += If(~en, - time.eq(t) - ).Elif(~max_time, - time.eq(time - 1) - ) - else: - self.comb += max_time.eq(0) - return en, max_time - read_time_en, max_read_time = anti_starvation(timing_settings.read_time) - write_time_en, max_write_time = anti_starvation(timing_settings.write_time) - - # Refresh - self.comb += [bm.refresh_req.eq(refresher.req) for bm in bank_machines] - go_to_refresh = Signal() - self.comb += go_to_refresh.eq(optree("&", [bm.refresh_gnt for bm in bank_machines])) - - # Datapath - all_rddata = [p.rddata for p in dfi.phases] - all_wrdata = [p.wrdata for p in dfi.phases] - all_wrdata_mask = [p.wrdata_mask for p in dfi.phases] - self.comb += [ - lasmic.dat_r.eq(Cat(*all_rddata)), - Cat(*all_wrdata).eq(lasmic.dat_w), - Cat(*all_wrdata_mask).eq(~lasmic.dat_we) - ] - - # Control FSM - fsm = FSM() - self.submodules += fsm - - def steerer_sel(steerer, phy_settings, r_w_n): - r = [] - for i in range(phy_settings.nphases): - s = steerer.sel[i].eq(STEER_NOP) - if r_w_n == "read": - if i == phy_settings.rdphase: - s = steerer.sel[i].eq(STEER_REQ) - elif i == phy_settings.rdcmdphase: - s = steerer.sel[i].eq(STEER_CMD) - elif r_w_n == "write": - if i == phy_settings.wrphase: - s = steerer.sel[i].eq(STEER_REQ) - elif i == phy_settings.wrcmdphase: - s = steerer.sel[i].eq(STEER_CMD) - else: - raise ValueError - r.append(s) - return r - - fsm.act("READ", - read_time_en.eq(1), - choose_req.want_reads.eq(1), - choose_cmd.cmd.ack.eq(1), - choose_req.cmd.ack.eq(1), - steerer_sel(steerer, phy_settings, "read"), - If(write_available, - # TODO: switch only after several cycles of ~read_available? - If(~read_available | max_read_time, NextState("RTW")) - ), - If(go_to_refresh, NextState("REFRESH")) - ) - fsm.act("WRITE", - write_time_en.eq(1), - choose_req.want_writes.eq(1), - choose_cmd.cmd.ack.eq(1), - choose_req.cmd.ack.eq(1), - steerer_sel(steerer, phy_settings, "write"), - If(read_available, - If(~write_available | max_write_time, NextState("WTR")) - ), - If(go_to_refresh, NextState("REFRESH")) - ) - fsm.act("REFRESH", - steerer.sel[0].eq(STEER_REFRESH), - If(~refresher.req, NextState("READ")) - ) - fsm.delayed_enter("RTW", "WRITE", phy_settings.read_latency-1) # FIXME: reduce this, actual limit is around (cl+1)/nphases - fsm.delayed_enter("WTR", "READ", timing_settings.tWTR-1) - # FIXME: workaround for zero-delay loop simulation problem with Icarus Verilog - fsm.finalize() - self.comb += refresher.ack.eq(fsm.state == fsm.encoding["REFRESH"]) - - self.submodules.bandwidth = Bandwidth(choose_req.cmd) diff --git a/misoclib/lasmicon/perf.py b/misoclib/lasmicon/perf.py deleted file mode 100644 index ec09c79c..00000000 --- a/misoclib/lasmicon/perf.py +++ /dev/null @@ -1,44 +0,0 @@ -from migen.fhdl.std import * -from migen.bank.description import * - -class Bandwidth(Module, AutoCSR): - def __init__(self, cmd, period_bits=24): - self._r_update = CSR() - self._r_nreads = CSRStatus(period_bits) - self._r_nwrites = CSRStatus(period_bits) - - ### - - cmd_stb = Signal() - cmd_ack = Signal() - cmd_is_read = Signal() - cmd_is_write = Signal() - self.sync += [ - cmd_stb.eq(cmd.stb), - cmd_ack.eq(cmd.ack), - cmd_is_read.eq(cmd.is_read), - cmd_is_write.eq(cmd.is_write) - ] - - counter = Signal(period_bits) - period = Signal() - nreads = Signal(period_bits) - nwrites = Signal(period_bits) - nreads_r = Signal(period_bits) - nwrites_r = Signal(period_bits) - self.sync += [ - Cat(counter, period).eq(counter + 1), - If(period, - nreads_r.eq(nreads), - nwrites_r.eq(nwrites), - nreads.eq(0), - nwrites.eq(0) - ).Elif(cmd_stb & cmd_ack, - If(cmd_is_read, nreads.eq(nreads + 1)), - If(cmd_is_write, nwrites.eq(nwrites + 1)), - ), - If(self._r_update.re, - self._r_nreads.status.eq(nreads_r), - self._r_nwrites.status.eq(nwrites_r) - ) - ] diff --git a/misoclib/lasmicon/refresher.py b/misoclib/lasmicon/refresher.py deleted file mode 100644 index 76482bd4..00000000 --- a/misoclib/lasmicon/refresher.py +++ /dev/null @@ -1,68 +0,0 @@ -from migen.fhdl.std import * -from migen.genlib.misc import timeline -from migen.genlib.fsm import FSM - -from misoclib.lasmicon.multiplexer import * - -class Refresher(Module): - def __init__(self, a, ba, tRP, tREFI, tRFC): - self.req = Signal() - self.ack = Signal() # 1st command 1 cycle after assertion of ack - self.cmd = CommandRequest(a, ba) - - ### - - # Refresh sequence generator: - # PRECHARGE ALL --(tRP)--> AUTO REFRESH --(tRFC)--> done - seq_start = Signal() - seq_done = Signal() - self.sync += [ - self.cmd.a.eq(2**10), - self.cmd.ba.eq(0), - self.cmd.cas_n.eq(1), - self.cmd.ras_n.eq(1), - self.cmd.we_n.eq(1), - seq_done.eq(0) - ] - self.sync += timeline(seq_start, [ - (1, [ - self.cmd.ras_n.eq(0), - self.cmd.we_n.eq(0) - ]), - (1+tRP, [ - self.cmd.cas_n.eq(0), - self.cmd.ras_n.eq(0) - ]), - (1+tRP+tRFC, [ - seq_done.eq(1) - ]) - ]) - - # Periodic refresh counter - counter = Signal(max=tREFI) - start = Signal() - self.sync += [ - start.eq(0), - If(counter == 0, - start.eq(1), - counter.eq(tREFI - 1) - ).Else( - counter.eq(counter - 1) - ) - ] - - # Control FSM - fsm = FSM() - self.submodules += fsm - fsm.act("IDLE", If(start, NextState("WAIT_GRANT"))) - fsm.act("WAIT_GRANT", - self.req.eq(1), - If(self.ack, - seq_start.eq(1), - NextState("WAIT_SEQ") - ) - ) - fsm.act("WAIT_SEQ", - self.req.eq(1), - If(seq_done, NextState("IDLE")) - ) diff --git a/misoclib/lasmicon/test/bankmachine.py b/misoclib/lasmicon/test/bankmachine.py deleted file mode 100644 index d446fb0f..00000000 --- a/misoclib/lasmicon/test/bankmachine.py +++ /dev/null @@ -1,41 +0,0 @@ -from migen.fhdl.std import * -from migen.bus.lasmibus import * -from migen.sim.generic import run_simulation - -from misoclib.lasmicon.bankmachine import * - -from common import sdram_phy, sdram_geom, sdram_timing, CommandLogger - -def my_generator(): - for x in range(10): - yield True, x - for x in range(10): - yield False, 128*x - -class TB(Module): - def __init__(self): - self.req = Interface(32, 32, 1, - sdram_timing.req_queue_size, sdram_phy.read_latency, sdram_phy.write_latency) - self.submodules.dut = BankMachine(sdram_geom, sdram_timing, 2, 0, self.req) - self.submodules.logger = CommandLogger(self.dut.cmd, True) - self.generator = my_generator() - self.dat_ack_cnt = 0 - - def do_simulation(self, selfp): - if selfp.req.dat_ack: - self.dat_ack_cnt += 1 - if selfp.req.req_ack: - try: - we, adr = next(self.generator) - except StopIteration: - selfp.req.stb = 0 - if not selfp.req.lock: - print("data ack count: {0}".format(self.dat_ack_cnt)) - raise StopSimulation - return - selfp.req.adr = adr - selfp.req.we = we - selfp.req.stb = 1 - -if __name__ == "__main__": - run_simulation(TB(), vcd_name="my.vcd") diff --git a/misoclib/lasmicon/test/common.py b/misoclib/lasmicon/test/common.py deleted file mode 100644 index c463297b..00000000 --- a/misoclib/lasmicon/test/common.py +++ /dev/null @@ -1,101 +0,0 @@ -from fractions import Fraction -from math import ceil - -from migen.fhdl.std import * - -from misoclib import lasmicon - -MHz = 1000000 -clk_freq = (83 + Fraction(1, 3))*MHz - -clk_period_ns = 1000000000/clk_freq -def ns(t, margin=True): - if margin: - t += clk_period_ns/2 - return ceil(t/clk_period_ns) - -sdram_phy = lasmicon.PhySettings( - memtype="DDR", - dfi_d=64, - nphases=2, - rdphase=0, - wrphase=1, - rdcmdphase=1, - wrcmdphase=0, - cl=3, - read_latency=5, - write_latency=0 -) - -sdram_geom = lasmicon.GeomSettings( - bank_a=2, - row_a=13, - col_a=10 -) -sdram_timing = lasmicon.TimingSettings( - tRP=ns(15), - tRCD=ns(15), - tWR=ns(15), - tWTR=2, - tREFI=ns(7800, False), - tRFC=ns(70), - - req_queue_size=8, - read_time=32, - write_time=16 -) - -def decode_sdram(ras_n, cas_n, we_n, bank, address): - elts = [] - if not ras_n and cas_n and we_n: - elts.append("ACTIVATE") - elts.append("BANK " + str(bank)) - elts.append("ROW " + str(address)) - elif ras_n and not cas_n and we_n: - elts.append("READ\t") - elts.append("BANK " + str(bank)) - elts.append("COL " + str(address)) - elif ras_n and not cas_n and not we_n: - elts.append("WRITE\t") - elts.append("BANK " + str(bank)) - elts.append("COL " + str(address)) - elif ras_n and cas_n and not we_n: - elts.append("BST") - elif not ras_n and not cas_n and we_n: - elts.append("AUTO REFRESH") - elif not ras_n and cas_n and not we_n: - elts.append("PRECHARGE") - if address & 2**10: - elts.append("ALL") - else: - elts.append("BANK " + str(bank)) - elif not ras_n and not cas_n and not we_n: - elts.append("LMR") - return elts - -class CommandLogger(Module): - def __init__(self, cmd, rw=False): - self.cmd = cmd - if rw: - self.comb += self.cmd.ack.eq(1) - - def do_simulation(self, selfp): - elts = ["@" + str(selfp.simulator.cycle_counter)] - cmdp = selfp.cmd - elts += decode_sdram(cmdp.ras_n, cmdp.cas_n, cmdp.we_n, cmdp.ba, cmdp.a) - if len(elts) > 1: - print("\t".join(elts)) - do_simulation.passive = True - -class DFILogger(Module): - def __init__(self, dfi): - self.dfi = dfi - - def do_simulation(self, selfp): - dfip = selfp.dfi - for i, p in enumerate(dfip.phases): - elts = ["@" + str(selfp.simulator.cycle_counter) + ":" + str(i)] - elts += decode_sdram(p.ras_n, p.cas_n, p.we_n, p.bank, p.address) - if len(elts) > 1: - print("\t".join(elts)) - do_simulation.passive = True diff --git a/misoclib/lasmicon/test/lasmicon.py b/misoclib/lasmicon/test/lasmicon.py deleted file mode 100644 index 05466d44..00000000 --- a/misoclib/lasmicon/test/lasmicon.py +++ /dev/null @@ -1,39 +0,0 @@ -from migen.fhdl.std import * -from migen.bus.lasmibus import * -from migen.sim.generic import run_simulation - -from misoclib.lasmicon import * - -from common import sdram_phy, sdram_geom, sdram_timing, DFILogger - -def my_generator_r(n): - for x in range(10): - t = TRead(128*n + 48*n*x) - yield t - print("{0:3}: reads done".format(n)) - -def my_generator_w(n): - for x in range(10): - t = TWrite(128*n + 48*n*x, x) - yield t - print("{0:3}: writes done".format(n)) - -def my_generator(n): - if n % 2: - return my_generator_w(n // 2) - else: - return my_generator_r(n // 2) - -class TB(Module): - def __init__(self): - self.submodules.dut = LASMIcon(sdram_phy, sdram_geom, sdram_timing) - self.submodules.xbar = lasmibus.Crossbar([self.dut.lasmic], self.dut.nrowbits) - self.submodules.logger = DFILogger(self.dut.dfi) - - masters = [self.xbar.get_master() for i in range(6)] - self.initiators = [Initiator(my_generator(n), master) - for n, master in enumerate(masters)] - self.submodules += self.initiators - -if __name__ == "__main__": - run_simulation(TB(), vcd_name="my.vcd") diff --git a/misoclib/lasmicon/test/lasmicon_df.py b/misoclib/lasmicon/test/lasmicon_df.py deleted file mode 100644 index b5380df5..00000000 --- a/misoclib/lasmicon/test/lasmicon_df.py +++ /dev/null @@ -1,39 +0,0 @@ -from migen.fhdl.std import * -from migen.bus import lasmibus -from migen.actorlib import dma_lasmi -from migen.sim.generic import run_simulation - -from misoclib.lasmicon import * - -from common import sdram_phy, sdram_geom, sdram_timing, DFILogger - -class TB(Module): - def __init__(self): - self.submodules.ctler = LASMIcon(sdram_phy, sdram_geom, sdram_timing) - self.submodules.xbar = lasmibus.Crossbar([self.ctler.lasmic], self.ctler.nrowbits) - self.submodules.logger = DFILogger(self.ctler.dfi) - self.submodules.writer = dma_lasmi.Writer(self.xbar.get_master()) - - self.comb += self.writer.address_data.stb.eq(1) - pl = self.writer.address_data.payload - pl.a.reset = 255 - pl.d.reset = pl.a.reset*2 - self.sync += If(self.writer.address_data.ack, - pl.a.eq(pl.a + 1), - pl.d.eq(pl.d + 2) - ) - self.open_row = None - - def do_simulation(self, selfp): - dfip = selfp.ctler.dfi - for p in dfip.phases: - if p.ras_n and not p.cas_n and not p.we_n: # write - d = dfip.phases[0].wrdata | (dfip.phases[1].wrdata << 64) - print(d) - if d != p.address//2 + p.bank*512 + self.open_row*2048: - print("**** ERROR ****") - elif not p.ras_n and p.cas_n and p.we_n: # activate - self.open_row = p.address - -if __name__ == "__main__": - run_simulation(TB(), ncycles=3500, vcd_name="my.vcd") diff --git a/misoclib/lasmicon/test/lasmicon_wb.py b/misoclib/lasmicon/test/lasmicon_wb.py deleted file mode 100644 index 1a385ed9..00000000 --- a/misoclib/lasmicon/test/lasmicon_wb.py +++ /dev/null @@ -1,36 +0,0 @@ -from migen.fhdl.std import * -from migen.bus import wishbone, wishbone2lasmi, lasmibus -from migen.bus.transactions import * -from migen.sim.generic import run_simulation - -from misoclib.lasmicon import * - -from common import sdram_phy, sdram_geom, sdram_timing, DFILogger - -l2_size = 8192 # in bytes - -def my_generator(): - for x in range(20): - t = TWrite(x, x) - yield t - print(str(t) + " delay=" + str(t.latency)) - for x in range(20): - t = TRead(x) - yield t - print(str(t) + " delay=" + str(t.latency)) - for x in range(20): - t = TRead(x+l2_size//4) - yield t - print(str(t) + " delay=" + str(t.latency)) - -class TB(Module): - def __init__(self): - self.submodules.ctler = LASMIcon(sdram_phy, sdram_geom, sdram_timing) - self.submodules.xbar = lasmibus.Crossbar([self.ctler.lasmic], self.ctler.nrowbits) - self.submodules.logger = DFILogger(self.ctler.dfi) - self.submodules.bridge = wishbone2lasmi.WB2LASMI(l2_size//4, self.xbar.get_master()) - self.submodules.initiator = wishbone.Initiator(my_generator()) - self.submodules.conn = wishbone.InterconnectPointToPoint(self.initiator.bus, self.bridge.wishbone) - -if __name__ == "__main__": - run_simulation(TB(), vcd_name="my.vcd") diff --git a/misoclib/lasmicon/test/refresher.py b/misoclib/lasmicon/test/refresher.py deleted file mode 100644 index 040b42db..00000000 --- a/misoclib/lasmicon/test/refresher.py +++ /dev/null @@ -1,45 +0,0 @@ -from random import Random - -from migen.fhdl.std import * -from migen.sim.generic import run_simulation - -from misoclib.lasmicon.refresher import * - -from common import CommandLogger - -class Granter(Module): - def __init__(self, req, ack): - self.req = req - self.ack = ack - self.state = 0 - self.prng = Random(92837) - - def do_simulation(self, selfp): - elts = ["@" + str(selfp.simulator.cycle_counter)] - - if self.state == 0: - if selfp.req: - elts.append("Refresher requested access") - self.state = 1 - elif self.state == 1: - if self.prng.randrange(0, 5) == 0: - elts.append("Granted access to refresher") - selfp.ack = 1 - self.state = 2 - elif self.state == 2: - if not selfp.req: - elts.append("Refresher released access") - selfp.ack = 0 - self.state = 0 - - if len(elts) > 1: - print("\t".join(elts)) - -class TB(Module): - def __init__(self): - self.submodules.dut = Refresher(13, 2, tRP=3, tREFI=100, tRFC=5) - self.submodules.logger = CommandLogger(self.dut.cmd) - self.submodules.granter = Granter(self.dut.req, self.dut.ack) - -if __name__ == "__main__": - run_simulation(TB(), ncycles=400) diff --git a/misoclib/sdram/__init__.py b/misoclib/sdram/__init__.py new file mode 100644 index 00000000..f861d63c --- /dev/null +++ b/misoclib/sdram/__init__.py @@ -0,0 +1,12 @@ +from collections import namedtuple + +PhySettingsT = namedtuple("PhySettings", "memtype dfi_d nphases rdphase wrphase rdcmdphase wrcmdphase cl cwl read_latency write_latency") +def PhySettings(memtype, dfi_d, nphases, rdphase, wrphase, rdcmdphase, wrcmdphase, cl, read_latency, write_latency, cwl=0): + return PhySettingsT(memtype, dfi_d, nphases, rdphase, wrphase, rdcmdphase, wrcmdphase, cl, cwl, read_latency, write_latency) + +GeomSettingsT = namedtuple("_GeomSettings", "bank_a row_a col_a mux_a") +def GeomSettings(bank_a, row_a, col_a): + return GeomSettingsT(bank_a, row_a, col_a, max(row_a, col_a)) + +TimingSettings = namedtuple("TimingSettings", "tRP tRCD tWR tWTR tREFI tRFC" \ + " req_queue_size read_time write_time") diff --git a/misoclib/sdram/dfii/__init__.py b/misoclib/sdram/dfii/__init__.py new file mode 100644 index 00000000..ba53d376 --- /dev/null +++ b/misoclib/sdram/dfii/__init__.py @@ -0,0 +1,57 @@ +from migen.fhdl.std import * +from migen.bus import dfi +from migen.bank.description import * + +class PhaseInjector(Module, AutoCSR): + def __init__(self, phase): + self._command = CSRStorage(6) # cs, we, cas, ras, wren, rden + self._command_issue = CSR() + self._address = CSRStorage(flen(phase.address)) + self._baddress = CSRStorage(flen(phase.bank)) + self._wrdata = CSRStorage(flen(phase.wrdata)) + self._rddata = CSRStatus(flen(phase.rddata)) + + ### + + self.comb += [ + If(self._command_issue.re, + phase.cs_n.eq(~self._command.storage[0]), + phase.we_n.eq(~self._command.storage[1]), + phase.cas_n.eq(~self._command.storage[2]), + phase.ras_n.eq(~self._command.storage[3]) + ).Else( + phase.cs_n.eq(1), + phase.we_n.eq(1), + phase.cas_n.eq(1), + phase.ras_n.eq(1) + ), + phase.address.eq(self._address.storage), + phase.bank.eq(self._baddress.storage), + phase.wrdata_en.eq(self._command_issue.re & self._command.storage[4]), + phase.rddata_en.eq(self._command_issue.re & self._command.storage[5]), + phase.wrdata.eq(self._wrdata.storage), + phase.wrdata_mask.eq(0) + ] + self.sync += If(phase.rddata_valid, self._rddata.status.eq(phase.rddata)) + +class DFIInjector(Module, AutoCSR): + def __init__(self, a, ba, d, nphases=1): + inti = dfi.Interface(a, ba, d, nphases) + self.slave = dfi.Interface(a, ba, d, nphases) + self.master = dfi.Interface(a, ba, d, nphases) + + self._control = CSRStorage(4) # sel, cke, odt, reset_n + + for n, phase in enumerate(inti.phases): + setattr(self.submodules, "pi" + str(n), PhaseInjector(phase)) + + ### + + self.comb += If(self._control.storage[0], + self.slave.connect(self.master) + ).Else( + inti.connect(self.master) + ) + self.comb += [phase.cke.eq(self._control.storage[1]) for phase in inti.phases] + self.comb += [phase.odt.eq(self._control.storage[2]) for phase in inti.phases if hasattr(phase, "odt")] + self.comb += [phase.reset_n.eq(self._control.storage[3]) for phase in inti.phases if hasattr(phase, "reset_n")] diff --git a/misoclib/sdram/lasmicon/__init__.py b/misoclib/sdram/lasmicon/__init__.py new file mode 100644 index 00000000..1136bcfe --- /dev/null +++ b/misoclib/sdram/lasmicon/__init__.py @@ -0,0 +1,41 @@ +from migen.fhdl.std import * +from migen.bus import dfi, lasmibus + +from misoclib.sdram.lasmicon.refresher import * +from misoclib.sdram.lasmicon.bankmachine import * +from misoclib.sdram.lasmicon.multiplexer import * + +class LASMIcon(Module): + def __init__(self, phy_settings, geom_settings, timing_settings): + if phy_settings.memtype in ["SDR"]: + burst_length = phy_settings.nphases*1 # command multiplication*SDR + elif phy_settings.memtype in ["DDR", "LPDDR", "DDR2", "DDR3"]: + burst_length = phy_settings.nphases*2 # command multiplication*DDR + address_align = log2_int(burst_length) + + self.dfi = dfi.Interface(geom_settings.mux_a, + geom_settings.bank_a, + phy_settings.dfi_d, + phy_settings.nphases) + self.lasmic = lasmibus.Interface( + aw=geom_settings.row_a + geom_settings.col_a - address_align, + dw=phy_settings.dfi_d*phy_settings.nphases, + nbanks=2**geom_settings.bank_a, + req_queue_size=timing_settings.req_queue_size, + read_latency=phy_settings.read_latency+1, + write_latency=phy_settings.write_latency+1) + self.nrowbits = geom_settings.col_a - address_align + + ### + + self.submodules.refresher = Refresher(geom_settings.mux_a, geom_settings.bank_a, + timing_settings.tRP, timing_settings.tREFI, timing_settings.tRFC) + self.submodules.bank_machines = [BankMachine(geom_settings, timing_settings, address_align, i, + getattr(self.lasmic, "bank"+str(i))) + for i in range(2**geom_settings.bank_a)] + self.submodules.multiplexer = Multiplexer(phy_settings, geom_settings, timing_settings, + self.bank_machines, self.refresher, + self.dfi, self.lasmic) + + def get_csrs(self): + return self.multiplexer.get_csrs() diff --git a/misoclib/sdram/lasmicon/bankmachine.py b/misoclib/sdram/lasmicon/bankmachine.py new file mode 100644 index 00000000..53c43f0a --- /dev/null +++ b/misoclib/sdram/lasmicon/bankmachine.py @@ -0,0 +1,144 @@ +from migen.fhdl.std import * +from migen.genlib.roundrobin import * +from migen.genlib.fsm import FSM, NextState +from migen.genlib.misc import optree +from migen.genlib.fifo import SyncFIFO + +from misoclib.sdram.lasmicon.multiplexer import * + +class _AddressSlicer: + def __init__(self, col_a, address_align): + self.col_a = col_a + self.address_align = address_align + + def row(self, address): + split = self.col_a - self.address_align + if isinstance(address, int): + return address >> split + else: + return address[split:] + + def col(self, address): + split = self.col_a - 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]) + +class BankMachine(Module): + def __init__(self, geom_settings, timing_settings, address_align, bankn, req): + self.refresh_req = Signal() + self.refresh_gnt = Signal() + self.cmd = CommandRequestRW(geom_settings.mux_a, geom_settings.bank_a) + + ### + + # Request FIFO + self.submodules.req_fifo = SyncFIFO([("we", 1), ("adr", flen(req.adr))], timing_settings.req_queue_size) + self.comb += [ + self.req_fifo.din.we.eq(req.we), + self.req_fifo.din.adr.eq(req.adr), + self.req_fifo.we.eq(req.stb), + req.req_ack.eq(self.req_fifo.writable), + + self.req_fifo.re.eq(req.dat_ack), + req.lock.eq(self.req_fifo.readable) + ] + reqf = self.req_fifo.dout + + slicer = _AddressSlicer(geom_settings.col_a, address_align) + + # Row tracking + has_openrow = Signal() + openrow = Signal(geom_settings.row_a) + hit = Signal() + self.comb += hit.eq(openrow == slicer.row(reqf.adr)) + track_open = Signal() + track_close = Signal() + self.sync += [ + If(track_open, + has_openrow.eq(1), + openrow.eq(slicer.row(reqf.adr)) + ), + If(track_close, + has_openrow.eq(0) + ) + ] + + # Address generation + s_row_adr = Signal() + self.comb += [ + self.cmd.ba.eq(bankn), + If(s_row_adr, + self.cmd.a.eq(slicer.row(reqf.adr)) + ).Else( + self.cmd.a.eq(slicer.col(reqf.adr)) + ) + ] + + # Respect write-to-precharge specification + precharge_ok = Signal() + t_unsafe_precharge = 2 + timing_settings.tWR - 1 + unsafe_precharge_count = Signal(max=t_unsafe_precharge+1) + self.comb += precharge_ok.eq(unsafe_precharge_count == 0) + self.sync += [ + If(self.cmd.stb & self.cmd.ack & self.cmd.is_write, + unsafe_precharge_count.eq(t_unsafe_precharge) + ).Elif(~precharge_ok, + unsafe_precharge_count.eq(unsafe_precharge_count-1) + ) + ] + + # Control and command generation FSM + fsm = FSM() + self.submodules += fsm + fsm.act("REGULAR", + If(self.refresh_req, + NextState("REFRESH") + ).Elif(self.req_fifo.readable, + If(has_openrow, + If(hit, + # NB: write-to-read specification is enforced by multiplexer + self.cmd.stb.eq(1), + req.dat_ack.eq(self.cmd.ack), + self.cmd.is_read.eq(~reqf.we), + self.cmd.is_write.eq(reqf.we), + self.cmd.cas_n.eq(0), + self.cmd.we_n.eq(~reqf.we) + ).Else( + NextState("PRECHARGE") + ) + ).Else( + NextState("ACTIVATE") + ) + ) + ) + fsm.act("PRECHARGE", + # Notes: + # 1. we are presenting the column address, A10 is always low + # 2. since we always go to the ACTIVATE state, we do not need + # to assert track_close. + If(precharge_ok, + self.cmd.stb.eq(1), + If(self.cmd.ack, NextState("TRP")), + self.cmd.ras_n.eq(0), + self.cmd.we_n.eq(0), + self.cmd.is_cmd.eq(1) + ) + ) + fsm.act("ACTIVATE", + s_row_adr.eq(1), + track_open.eq(1), + self.cmd.stb.eq(1), + self.cmd.is_cmd.eq(1), + If(self.cmd.ack, NextState("TRCD")), + self.cmd.ras_n.eq(0) + ) + fsm.act("REFRESH", + self.refresh_gnt.eq(precharge_ok), + track_close.eq(1), + self.cmd.is_cmd.eq(1), + If(~self.refresh_req, NextState("REGULAR")) + ) + fsm.delayed_enter("TRP", "ACTIVATE", timing_settings.tRP-1) + fsm.delayed_enter("TRCD", "REGULAR", timing_settings.tRCD-1) diff --git a/misoclib/sdram/lasmicon/multiplexer.py b/misoclib/sdram/lasmicon/multiplexer.py new file mode 100644 index 00000000..cee6f1fd --- /dev/null +++ b/misoclib/sdram/lasmicon/multiplexer.py @@ -0,0 +1,214 @@ +from migen.fhdl.std import * +from migen.genlib.roundrobin import * +from migen.genlib.misc import optree +from migen.genlib.fsm import FSM, NextState +from migen.bank.description import AutoCSR + +from misoclib.sdram.lasmicon.perf import Bandwidth + +class CommandRequest: + def __init__(self, a, ba): + self.a = Signal(a) + self.ba = Signal(ba) + self.cas_n = Signal(reset=1) + self.ras_n = Signal(reset=1) + self.we_n = Signal(reset=1) + +class CommandRequestRW(CommandRequest): + def __init__(self, a, ba): + CommandRequest.__init__(self, a, ba) + self.stb = Signal() + self.ack = Signal() + self.is_cmd = Signal() + self.is_read = Signal() + self.is_write = Signal() + +class _CommandChooser(Module): + def __init__(self, requests): + self.want_reads = Signal() + self.want_writes = Signal() + self.want_cmds = Signal() + # NB: cas_n/ras_n/we_n are 1 when stb is inactive + self.cmd = CommandRequestRW(flen(requests[0].a), flen(requests[0].ba)) + + ### + + rr = RoundRobin(len(requests), SP_CE) + self.submodules += rr + + self.comb += [rr.request[i].eq(req.stb & ((req.is_cmd & self.want_cmds) | ((req.is_read == self.want_reads) | (req.is_write == self.want_writes)))) + for i, req in enumerate(requests)] + + stb = Signal() + self.comb += stb.eq(Array(req.stb for req in requests)[rr.grant]) + for name in ["a", "ba", "is_read", "is_write", "is_cmd"]: + choices = Array(getattr(req, name) for req in requests) + self.comb += getattr(self.cmd, name).eq(choices[rr.grant]) + for name in ["cas_n", "ras_n", "we_n"]: + # we should only assert those signals when stb is 1 + choices = Array(getattr(req, name) for req in requests) + self.comb += If(self.cmd.stb, getattr(self.cmd, name).eq(choices[rr.grant])) + self.comb += self.cmd.stb.eq(stb \ + & ((self.cmd.is_cmd & self.want_cmds) | ((self.cmd.is_read == self.want_reads) \ + & (self.cmd.is_write == self.want_writes)))) + + self.comb += [If(self.cmd.stb & self.cmd.ack & (rr.grant == i), req.ack.eq(1)) + for i, req in enumerate(requests)] + self.comb += rr.ce.eq(self.cmd.ack) + +class _Steerer(Module): + def __init__(self, commands, dfi): + ncmd = len(commands) + nph = len(dfi.phases) + self.sel = [Signal(max=ncmd) for i in range(nph)] + + ### + + def stb_and(cmd, attr): + if not hasattr(cmd, "stb"): + return 0 + else: + return cmd.stb & getattr(cmd, attr) + for phase, sel in zip(dfi.phases, self.sel): + self.comb += [ + phase.cke.eq(1), + phase.cs_n.eq(0) + ] + if hasattr(phase, "odt"): + self.comb += phase.odt.eq(1) + if hasattr(phase, "reset_n"): + self.comb += phase.reset_n.eq(1) + self.sync += [ + phase.address.eq(Array(cmd.a for cmd in commands)[sel]), + phase.bank.eq(Array(cmd.ba for cmd in commands)[sel]), + phase.cas_n.eq(Array(cmd.cas_n for cmd in commands)[sel]), + phase.ras_n.eq(Array(cmd.ras_n for cmd in commands)[sel]), + phase.we_n.eq(Array(cmd.we_n for cmd in commands)[sel]), + phase.rddata_en.eq(Array(stb_and(cmd, "is_read") for cmd in commands)[sel]), + phase.wrdata_en.eq(Array(stb_and(cmd, "is_write") for cmd in commands)[sel]) + ] + +class Multiplexer(Module, AutoCSR): + def __init__(self, phy_settings, geom_settings, timing_settings, bank_machines, refresher, dfi, lasmic): + assert(phy_settings.nphases == len(dfi.phases)) + + # Command choosing + requests = [bm.cmd for bm in bank_machines] + choose_cmd = _CommandChooser(requests) + choose_req = _CommandChooser(requests) + self.comb += [ + choose_cmd.want_reads.eq(0), + choose_cmd.want_writes.eq(0) + ] + if phy_settings.nphases == 1: + self.comb += [ + choose_cmd.want_cmds.eq(1), + choose_req.want_cmds.eq(1) + ] + self.submodules += choose_cmd, choose_req + + # Command steering + nop = CommandRequest(geom_settings.mux_a, geom_settings.bank_a) + commands = [nop, choose_cmd.cmd, choose_req.cmd, refresher.cmd] # nop must be 1st + (STEER_NOP, STEER_CMD, STEER_REQ, STEER_REFRESH) = range(4) + steerer = _Steerer(commands, dfi) + self.submodules += steerer + + # Read/write turnaround + read_available = Signal() + write_available = Signal() + self.comb += [ + read_available.eq(optree("|", [req.stb & req.is_read for req in requests])), + write_available.eq(optree("|", [req.stb & req.is_write for req in requests])) + ] + + def anti_starvation(timeout): + en = Signal() + max_time = Signal() + if timeout: + t = timeout - 1 + time = Signal(max=t+1) + self.comb += max_time.eq(time == 0) + self.sync += If(~en, + time.eq(t) + ).Elif(~max_time, + time.eq(time - 1) + ) + else: + self.comb += max_time.eq(0) + return en, max_time + read_time_en, max_read_time = anti_starvation(timing_settings.read_time) + write_time_en, max_write_time = anti_starvation(timing_settings.write_time) + + # Refresh + self.comb += [bm.refresh_req.eq(refresher.req) for bm in bank_machines] + go_to_refresh = Signal() + self.comb += go_to_refresh.eq(optree("&", [bm.refresh_gnt for bm in bank_machines])) + + # Datapath + all_rddata = [p.rddata for p in dfi.phases] + all_wrdata = [p.wrdata for p in dfi.phases] + all_wrdata_mask = [p.wrdata_mask for p in dfi.phases] + self.comb += [ + lasmic.dat_r.eq(Cat(*all_rddata)), + Cat(*all_wrdata).eq(lasmic.dat_w), + Cat(*all_wrdata_mask).eq(~lasmic.dat_we) + ] + + # Control FSM + fsm = FSM() + self.submodules += fsm + + def steerer_sel(steerer, phy_settings, r_w_n): + r = [] + for i in range(phy_settings.nphases): + s = steerer.sel[i].eq(STEER_NOP) + if r_w_n == "read": + if i == phy_settings.rdphase: + s = steerer.sel[i].eq(STEER_REQ) + elif i == phy_settings.rdcmdphase: + s = steerer.sel[i].eq(STEER_CMD) + elif r_w_n == "write": + if i == phy_settings.wrphase: + s = steerer.sel[i].eq(STEER_REQ) + elif i == phy_settings.wrcmdphase: + s = steerer.sel[i].eq(STEER_CMD) + else: + raise ValueError + r.append(s) + return r + + fsm.act("READ", + read_time_en.eq(1), + choose_req.want_reads.eq(1), + choose_cmd.cmd.ack.eq(1), + choose_req.cmd.ack.eq(1), + steerer_sel(steerer, phy_settings, "read"), + If(write_available, + # TODO: switch only after several cycles of ~read_available? + If(~read_available | max_read_time, NextState("RTW")) + ), + If(go_to_refresh, NextState("REFRESH")) + ) + fsm.act("WRITE", + write_time_en.eq(1), + choose_req.want_writes.eq(1), + choose_cmd.cmd.ack.eq(1), + choose_req.cmd.ack.eq(1), + steerer_sel(steerer, phy_settings, "write"), + If(read_available, + If(~write_available | max_write_time, NextState("WTR")) + ), + If(go_to_refresh, NextState("REFRESH")) + ) + fsm.act("REFRESH", + steerer.sel[0].eq(STEER_REFRESH), + If(~refresher.req, NextState("READ")) + ) + fsm.delayed_enter("RTW", "WRITE", phy_settings.read_latency-1) # FIXME: reduce this, actual limit is around (cl+1)/nphases + fsm.delayed_enter("WTR", "READ", timing_settings.tWTR-1) + # FIXME: workaround for zero-delay loop simulation problem with Icarus Verilog + fsm.finalize() + self.comb += refresher.ack.eq(fsm.state == fsm.encoding["REFRESH"]) + + self.submodules.bandwidth = Bandwidth(choose_req.cmd) diff --git a/misoclib/sdram/lasmicon/perf.py b/misoclib/sdram/lasmicon/perf.py new file mode 100644 index 00000000..ec09c79c --- /dev/null +++ b/misoclib/sdram/lasmicon/perf.py @@ -0,0 +1,44 @@ +from migen.fhdl.std import * +from migen.bank.description import * + +class Bandwidth(Module, AutoCSR): + def __init__(self, cmd, period_bits=24): + self._r_update = CSR() + self._r_nreads = CSRStatus(period_bits) + self._r_nwrites = CSRStatus(period_bits) + + ### + + cmd_stb = Signal() + cmd_ack = Signal() + cmd_is_read = Signal() + cmd_is_write = Signal() + self.sync += [ + cmd_stb.eq(cmd.stb), + cmd_ack.eq(cmd.ack), + cmd_is_read.eq(cmd.is_read), + cmd_is_write.eq(cmd.is_write) + ] + + counter = Signal(period_bits) + period = Signal() + nreads = Signal(period_bits) + nwrites = Signal(period_bits) + nreads_r = Signal(period_bits) + nwrites_r = Signal(period_bits) + self.sync += [ + Cat(counter, period).eq(counter + 1), + If(period, + nreads_r.eq(nreads), + nwrites_r.eq(nwrites), + nreads.eq(0), + nwrites.eq(0) + ).Elif(cmd_stb & cmd_ack, + If(cmd_is_read, nreads.eq(nreads + 1)), + If(cmd_is_write, nwrites.eq(nwrites + 1)), + ), + If(self._r_update.re, + self._r_nreads.status.eq(nreads_r), + self._r_nwrites.status.eq(nwrites_r) + ) + ] diff --git a/misoclib/sdram/lasmicon/refresher.py b/misoclib/sdram/lasmicon/refresher.py new file mode 100644 index 00000000..cb59fb86 --- /dev/null +++ b/misoclib/sdram/lasmicon/refresher.py @@ -0,0 +1,68 @@ +from migen.fhdl.std import * +from migen.genlib.misc import timeline +from migen.genlib.fsm import FSM + +from misoclib.sdram.lasmicon.multiplexer import * + +class Refresher(Module): + def __init__(self, a, ba, tRP, tREFI, tRFC): + self.req = Signal() + self.ack = Signal() # 1st command 1 cycle after assertion of ack + self.cmd = CommandRequest(a, ba) + + ### + + # Refresh sequence generator: + # PRECHARGE ALL --(tRP)--> AUTO REFRESH --(tRFC)--> done + seq_start = Signal() + seq_done = Signal() + self.sync += [ + self.cmd.a.eq(2**10), + self.cmd.ba.eq(0), + self.cmd.cas_n.eq(1), + self.cmd.ras_n.eq(1), + self.cmd.we_n.eq(1), + seq_done.eq(0) + ] + self.sync += timeline(seq_start, [ + (1, [ + self.cmd.ras_n.eq(0), + self.cmd.we_n.eq(0) + ]), + (1+tRP, [ + self.cmd.cas_n.eq(0), + self.cmd.ras_n.eq(0) + ]), + (1+tRP+tRFC, [ + seq_done.eq(1) + ]) + ]) + + # Periodic refresh counter + counter = Signal(max=tREFI) + start = Signal() + self.sync += [ + start.eq(0), + If(counter == 0, + start.eq(1), + counter.eq(tREFI - 1) + ).Else( + counter.eq(counter - 1) + ) + ] + + # Control FSM + fsm = FSM() + self.submodules += fsm + fsm.act("IDLE", If(start, NextState("WAIT_GRANT"))) + fsm.act("WAIT_GRANT", + self.req.eq(1), + If(self.ack, + seq_start.eq(1), + NextState("WAIT_SEQ") + ) + ) + fsm.act("WAIT_SEQ", + self.req.eq(1), + If(seq_done, NextState("IDLE")) + ) diff --git a/misoclib/sdram/lasmicon/test/bankmachine.py b/misoclib/sdram/lasmicon/test/bankmachine.py new file mode 100644 index 00000000..a6719cf5 --- /dev/null +++ b/misoclib/sdram/lasmicon/test/bankmachine.py @@ -0,0 +1,41 @@ +from migen.fhdl.std import * +from migen.bus.lasmibus import * +from migen.sim.generic import run_simulation + +from misoclib.sdram.lasmicon.bankmachine import * + +from common import sdram_phy, sdram_geom, sdram_timing, CommandLogger + +def my_generator(): + for x in range(10): + yield True, x + for x in range(10): + yield False, 128*x + +class TB(Module): + def __init__(self): + self.req = Interface(32, 32, 1, + sdram_timing.req_queue_size, sdram_phy.read_latency, sdram_phy.write_latency) + self.submodules.dut = BankMachine(sdram_geom, sdram_timing, 2, 0, self.req) + self.submodules.logger = CommandLogger(self.dut.cmd, True) + self.generator = my_generator() + self.dat_ack_cnt = 0 + + def do_simulation(self, selfp): + if selfp.req.dat_ack: + self.dat_ack_cnt += 1 + if selfp.req.req_ack: + try: + we, adr = next(self.generator) + except StopIteration: + selfp.req.stb = 0 + if not selfp.req.lock: + print("data ack count: {0}".format(self.dat_ack_cnt)) + raise StopSimulation + return + selfp.req.adr = adr + selfp.req.we = we + selfp.req.stb = 1 + +if __name__ == "__main__": + run_simulation(TB(), vcd_name="my.vcd") diff --git a/misoclib/sdram/lasmicon/test/common.py b/misoclib/sdram/lasmicon/test/common.py new file mode 100644 index 00000000..3d60d704 --- /dev/null +++ b/misoclib/sdram/lasmicon/test/common.py @@ -0,0 +1,101 @@ +from fractions import Fraction +from math import ceil + +from migen.fhdl.std import * + +from misoclib import sdram + +MHz = 1000000 +clk_freq = (83 + Fraction(1, 3))*MHz + +clk_period_ns = 1000000000/clk_freq +def ns(t, margin=True): + if margin: + t += clk_period_ns/2 + return ceil(t/clk_period_ns) + +sdram_phy = sdram.PhySettings( + memtype="DDR", + dfi_d=64, + nphases=2, + rdphase=0, + wrphase=1, + rdcmdphase=1, + wrcmdphase=0, + cl=3, + read_latency=5, + write_latency=0 +) + +sdram_geom = sdram.GeomSettings( + bank_a=2, + row_a=13, + col_a=10 +) +sdram_timing = sdram.TimingSettings( + tRP=ns(15), + tRCD=ns(15), + tWR=ns(15), + tWTR=2, + tREFI=ns(7800, False), + tRFC=ns(70), + + req_queue_size=8, + read_time=32, + write_time=16 +) + +def decode_sdram(ras_n, cas_n, we_n, bank, address): + elts = [] + if not ras_n and cas_n and we_n: + elts.append("ACTIVATE") + elts.append("BANK " + str(bank)) + elts.append("ROW " + str(address)) + elif ras_n and not cas_n and we_n: + elts.append("READ\t") + elts.append("BANK " + str(bank)) + elts.append("COL " + str(address)) + elif ras_n and not cas_n and not we_n: + elts.append("WRITE\t") + elts.append("BANK " + str(bank)) + elts.append("COL " + str(address)) + elif ras_n and cas_n and not we_n: + elts.append("BST") + elif not ras_n and not cas_n and we_n: + elts.append("AUTO REFRESH") + elif not ras_n and cas_n and not we_n: + elts.append("PRECHARGE") + if address & 2**10: + elts.append("ALL") + else: + elts.append("BANK " + str(bank)) + elif not ras_n and not cas_n and not we_n: + elts.append("LMR") + return elts + +class CommandLogger(Module): + def __init__(self, cmd, rw=False): + self.cmd = cmd + if rw: + self.comb += self.cmd.ack.eq(1) + + def do_simulation(self, selfp): + elts = ["@" + str(selfp.simulator.cycle_counter)] + cmdp = selfp.cmd + elts += decode_sdram(cmdp.ras_n, cmdp.cas_n, cmdp.we_n, cmdp.ba, cmdp.a) + if len(elts) > 1: + print("\t".join(elts)) + do_simulation.passive = True + +class DFILogger(Module): + def __init__(self, dfi): + self.dfi = dfi + + def do_simulation(self, selfp): + dfip = selfp.dfi + for i, p in enumerate(dfip.phases): + elts = ["@" + str(selfp.simulator.cycle_counter) + ":" + str(i)] + elts += decode_sdram(p.ras_n, p.cas_n, p.we_n, p.bank, p.address) + if len(elts) > 1: + print("\t".join(elts)) + do_simulation.passive = True diff --git a/misoclib/sdram/lasmicon/test/lasmicon.py b/misoclib/sdram/lasmicon/test/lasmicon.py new file mode 100644 index 00000000..e4cd0458 --- /dev/null +++ b/misoclib/sdram/lasmicon/test/lasmicon.py @@ -0,0 +1,39 @@ +from migen.fhdl.std import * +from migen.bus.lasmibus import * +from migen.sim.generic import run_simulation + +from misoclib.sdram.lasmicon import * + +from common import sdram_phy, sdram_geom, sdram_timing, DFILogger + +def my_generator_r(n): + for x in range(10): + t = TRead(128*n + 48*n*x) + yield t + print("{0:3}: reads done".format(n)) + +def my_generator_w(n): + for x in range(10): + t = TWrite(128*n + 48*n*x, x) + yield t + print("{0:3}: writes done".format(n)) + +def my_generator(n): + if n % 2: + return my_generator_w(n // 2) + else: + return my_generator_r(n // 2) + +class TB(Module): + def __init__(self): + self.submodules.dut = LASMIcon(sdram_phy, sdram_geom, sdram_timing) + self.submodules.xbar = lasmibus.Crossbar([self.dut.lasmic], self.dut.nrowbits) + self.submodules.logger = DFILogger(self.dut.dfi) + + masters = [self.xbar.get_master() for i in range(6)] + self.initiators = [Initiator(my_generator(n), master) + for n, master in enumerate(masters)] + self.submodules += self.initiators + +if __name__ == "__main__": + run_simulation(TB(), vcd_name="my.vcd") diff --git a/misoclib/sdram/lasmicon/test/lasmicon_df.py b/misoclib/sdram/lasmicon/test/lasmicon_df.py new file mode 100644 index 00000000..d2a4add1 --- /dev/null +++ b/misoclib/sdram/lasmicon/test/lasmicon_df.py @@ -0,0 +1,39 @@ +from migen.fhdl.std import * +from migen.bus import lasmibus +from migen.actorlib import dma_lasmi +from migen.sim.generic import run_simulation + +from misoclib.sdram.lasmicon import * + +from common import sdram_phy, sdram_geom, sdram_timing, DFILogger + +class TB(Module): + def __init__(self): + self.submodules.ctler = LASMIcon(sdram_phy, sdram_geom, sdram_timing) + self.submodules.xbar = lasmibus.Crossbar([self.ctler.lasmic], self.ctler.nrowbits) + self.submodules.logger = DFILogger(self.ctler.dfi) + self.submodules.writer = dma_lasmi.Writer(self.xbar.get_master()) + + self.comb += self.writer.address_data.stb.eq(1) + pl = self.writer.address_data.payload + pl.a.reset = 255 + pl.d.reset = pl.a.reset*2 + self.sync += If(self.writer.address_data.ack, + pl.a.eq(pl.a + 1), + pl.d.eq(pl.d + 2) + ) + self.open_row = None + + def do_simulation(self, selfp): + dfip = selfp.ctler.dfi + for p in dfip.phases: + if p.ras_n and not p.cas_n and not p.we_n: # write + d = dfip.phases[0].wrdata | (dfip.phases[1].wrdata << 64) + print(d) + if d != p.address//2 + p.bank*512 + self.open_row*2048: + print("**** ERROR ****") + elif not p.ras_n and p.cas_n and p.we_n: # activate + self.open_row = p.address + +if __name__ == "__main__": + run_simulation(TB(), ncycles=3500, vcd_name="my.vcd") diff --git a/misoclib/sdram/lasmicon/test/lasmicon_wb.py b/misoclib/sdram/lasmicon/test/lasmicon_wb.py new file mode 100644 index 00000000..35d18d9b --- /dev/null +++ b/misoclib/sdram/lasmicon/test/lasmicon_wb.py @@ -0,0 +1,36 @@ +from migen.fhdl.std import * +from migen.bus import wishbone, wishbone2lasmi, lasmibus +from migen.bus.transactions import * +from migen.sim.generic import run_simulation + +from misoclib.sdram.lasmicon import * + +from common import sdram_phy, sdram_geom, sdram_timing, DFILogger + +l2_size = 8192 # in bytes + +def my_generator(): + for x in range(20): + t = TWrite(x, x) + yield t + print(str(t) + " delay=" + str(t.latency)) + for x in range(20): + t = TRead(x) + yield t + print(str(t) + " delay=" + str(t.latency)) + for x in range(20): + t = TRead(x+l2_size//4) + yield t + print(str(t) + " delay=" + str(t.latency)) + +class TB(Module): + def __init__(self): + self.submodules.ctler = LASMIcon(sdram_phy, sdram_geom, sdram_timing) + self.submodules.xbar = lasmibus.Crossbar([self.ctler.lasmic], self.ctler.nrowbits) + self.submodules.logger = DFILogger(self.ctler.dfi) + self.submodules.bridge = wishbone2lasmi.WB2LASMI(l2_size//4, self.xbar.get_master()) + self.submodules.initiator = wishbone.Initiator(my_generator()) + self.submodules.conn = wishbone.InterconnectPointToPoint(self.initiator.bus, self.bridge.wishbone) + +if __name__ == "__main__": + run_simulation(TB(), vcd_name="my.vcd") diff --git a/misoclib/sdram/lasmicon/test/refresher.py b/misoclib/sdram/lasmicon/test/refresher.py new file mode 100644 index 00000000..6437a781 --- /dev/null +++ b/misoclib/sdram/lasmicon/test/refresher.py @@ -0,0 +1,45 @@ +from random import Random + +from migen.fhdl.std import * +from migen.sim.generic import run_simulation + +from misoclib.sdram.lasmicon.refresher import * + +from common import CommandLogger + +class Granter(Module): + def __init__(self, req, ack): + self.req = req + self.ack = ack + self.state = 0 + self.prng = Random(92837) + + def do_simulation(self, selfp): + elts = ["@" + str(selfp.simulator.cycle_counter)] + + if self.state == 0: + if selfp.req: + elts.append("Refresher requested access") + self.state = 1 + elif self.state == 1: + if self.prng.randrange(0, 5) == 0: + elts.append("Granted access to refresher") + selfp.ack = 1 + self.state = 2 + elif self.state == 2: + if not selfp.req: + elts.append("Refresher released access") + selfp.ack = 0 + self.state = 0 + + if len(elts) > 1: + print("\t".join(elts)) + +class TB(Module): + def __init__(self): + self.submodules.dut = Refresher(13, 2, tRP=3, tREFI=100, tRFC=5) + self.submodules.logger = CommandLogger(self.dut.cmd) + self.submodules.granter = Granter(self.dut.req, self.dut.ack) + +if __name__ == "__main__": + run_simulation(TB(), ncycles=400) diff --git a/misoclib/sdram/minicon/__init__.py b/misoclib/sdram/minicon/__init__.py new file mode 100755 index 00000000..6cd36744 --- /dev/null +++ b/misoclib/sdram/minicon/__init__.py @@ -0,0 +1,203 @@ +from migen.fhdl.std import * +from migen.bus import wishbone +from migen.bus import dfi as dfibus +from migen.genlib.fsm import FSM, NextState + +class _AddressSlicer: + def __init__(self, col_a, bank_a, row_a, address_align): + self.col_a = col_a + self.bank_a = bank_a + self.row_a = row_a + self.max_a = col_a + row_a + bank_a + self.address_align = address_align + + def row(self, address): + split = self.bank_a + self.col_a + if isinstance(address, int): + return address >> split + else: + return address[split:self.max_a] + + def bank(self, address): + mask = 2**(self.bank_a + self.col_a) - 1 + shift = self.col_a + if isinstance(address, int): + return (address & mask) >> shift + else: + return address[self.col_a:self.col_a+self.bank_a] + + def col(self, address): + split = self.col_a + if isinstance(address, int): + return (address & (2**split - 1)) << self.address_align + else: + return Cat(Replicate(0, self.address_align), address[:split]) + +class Minicon(Module): + def __init__(self, phy_settings, geom_settings, timing_settings): + if phy_settings.memtype in ["SDR"]: + burst_length = phy_settings.nphases*1 # command multiplication*SDR + elif phy_settings.memtype in ["DDR", "LPDDR", "DDR2", "DDR3"]: + burst_length = phy_settings.nphases*2 # command multiplication*DDR + address_align = log2_int(burst_length) + + nbanks = range(2**geom_settings.bank_a) + A10_ENABLED = 0 + COLUMN = 1 + ROW = 2 + rdphase = phy_settings.rdphase + wrphase = phy_settings.wrphase + rdcmdphase = phy_settings.rdcmdphase + wrcmdphase = phy_settings.wrcmdphase + + self.dfi = dfi = dfibus.Interface(geom_settings.mux_a, + geom_settings.bank_a, + phy_settings.dfi_d, + phy_settings.nphases) + + self.bus = bus = wishbone.Interface(data_width=phy_settings.nphases*flen(dfi.phases[rdphase].rddata)) + slicer = _AddressSlicer(geom_settings.col_a, geom_settings.bank_a, geom_settings.row_a, address_align) + req_addr = Signal(geom_settings.col_a + geom_settings.bank_a + geom_settings.row_a) + refresh_req = Signal() + refresh_ack = Signal() + wb_access = 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() + cl_counter = Signal(max=phy_settings.cl+1) + + # Extra bit means row is active when asserted + self.openrow = openrow = Array(Signal(geom_settings.row_a + 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]), + wb_access.eq(bus.stb & bus.cyc), + 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), + ] + + for phase in dfi.phases: + self.comb += [ + phase.cke.eq(1), + phase.address.eq(Array([2**10, slicer.col(bus.adr), slicer.row(bus.adr)])[addr_sel]), + If(wb_access, + phase.bank.eq(slicer.bank(bus.adr)) + ) + ] + phase.cs_n.reset = 0 + phase.ras_n.reset = 1 + phase.cas_n.reset = 1 + phase.we_n.reset = 1 + + 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) + ) + ] + + 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) + ) + ] + + fsm = FSM() + self.submodules += fsm + fsm.act("IDLE", + If(refresh_req, + NextState("PRECHARGEALL") + ).Elif(wb_access, + If(hit & bus.we, + NextState("WRITE"), + ), + If(hit & ~bus.we, + NextState("READ"), + ), + If(has_curbank_openrow & ~hit, + NextState("PRECHARGE") + ), + If(~has_curbank_openrow, + NextState("ACTIVATE") + ), + ) + ) + fsm.act("READ", + # We output Column bits at address pins so that A10 is 0 + # to disable row Auto-Precharge + dfi.phases[rdcmdphase].ras_n.eq(1), + dfi.phases[rdcmdphase].cas_n.eq(0), + dfi.phases[rdcmdphase].we_n.eq(1), + dfi.phases[rdphase].rddata_en.eq(1), + addr_sel.eq(COLUMN), + NextState("READ-WAIT-ACK"), + ) + fsm.act("READ-WAIT-ACK", + If(dfi.phases[rdphase].rddata_valid, + NextState("IDLE"), + bus.ack.eq(1) + ).Else( + NextState("READ-WAIT-ACK") + ) + ) + fsm.act("WRITE", + dfi.phases[wrcmdphase].ras_n.eq(1), + dfi.phases[wrcmdphase].cas_n.eq(0), + dfi.phases[wrcmdphase].we_n.eq(0), + dfi.phases[wrphase].wrdata_en.eq(1), + addr_sel.eq(COLUMN), + bus.ack.eq(1), + NextState("IDLE") + ) + fsm.act("PRECHARGEALL", + row_closeall.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) + ) + fsm.act("ACTIVATE", + row_open.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), + dfi.phases[rdphase].ras_n.eq(0), + dfi.phases[rdphase].cas_n.eq(0), + dfi.phases[rdphase].we_n.eq(1), + NextState("POST-REFRESH") + ) + fsm.delayed_enter("TRP", "ACTIVATE", timing_settings.tRP-1) + 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) diff --git a/misoclib/sdram/minicon/tb/minicontb.py b/misoclib/sdram/minicon/tb/minicontb.py new file mode 100755 index 00000000..325da01c --- /dev/null +++ b/misoclib/sdram/minicon/tb/minicontb.py @@ -0,0 +1,192 @@ +from migen.fhdl.std import * +from migen.bus.transactions import TRead, TWrite +from migen.bus import wishbone +from migen.sim.generic import Simulator +from migen.sim import icarus +from mibuild.platforms import papilio_pro as board +from misoclib import sdram +from misoclib.sdram.minicon import Minicon +from misoclib.sdram.phy import gensdrphy +from itertools import chain +from os.path import isfile +import sys + +clk_freq = 80000000 + +from math import ceil + +def ns(t, margin=True): + clk_period_ns = 1000000000/clk_freq + if margin: + t += clk_period_ns/2 + return ceil(t/clk_period_ns) + +class MiniconTB(Module): + def __init__(self, sdrphy, dfi, sdram_geom, sdram_timing, pads, sdram_clk): + + self.clk_freq = 80000000 + phy_settings = sdrphy.phy_settings + rdphase = phy_settings.rdphase + self.submodules.slave = Minicon(phy_settings, sdram_geom, sdram_timing) + + self.submodules.tap = wishbone.Tap(self.slave.bus) + self.submodules.dc = dc = wishbone.DownConverter(32, phy_settings.nphases*flen(dfi.phases[rdphase].rddata)) + self.submodules.master = wishbone.Initiator(self.genxfers(), bus=dc.wishbone_i) + self.submodules.intercon = wishbone.InterconnectPointToPoint(dc.wishbone_o, self.slave.bus) + + self.submodules.sdrphy = self.sdrphy = sdrphy + self.dfi = dfi + self.pads = pads + + self.specials += Instance("mt48lc4m16a2", + io_Dq=pads.dq, + i_Addr=pads.a, + i_Ba=pads.ba, + i_Clk=ClockSignal(), + i_Cke=pads.cke, + i_Cs_n=pads.cs_n, + i_Ras_n=pads.ras_n, + i_Cas_n=pads.cas_n, + i_We_n=pads.we_n, + i_Dqm=pads.dm + ) + + def genxfers(self): + cycle = 0 + for a in chain(range(4),range(256,260),range(1024,1028)): + t = TRead(a) + yield t + print("read {} in {} cycles".format(t.data, t.latency)) + for a in chain(range(4),range(256,260),range(1024,1028),range(4096,4100)): + t = TWrite(a, 0xaa55aa55+cycle) + cycle += 1 + yield t + print("read {} in {} cycles".format(t.data, t.latency)) + for a in chain(range(4),range(256,260),range(1024,1028),range(4096,4100)): + t = TRead(a) + yield t + print("read {} in {} cycles".format(t.data, t.latency)) + + def gen_simulation(self, selfp): + dfi = selfp.dfi + phy = self.sdrphy + rdphase = phy.phy_settings.rdphase + cycle = 0 + + while True: + yield + +class MyTopLevel: + def __init__(self, vcd_name=None, vcd_level=1, + top_name="top", dut_type="dut", dut_name="dut", + cd_name="sys", clk_period=10): + self.vcd_name = vcd_name + self.vcd_level = vcd_level + self.top_name = top_name + self.dut_type = dut_type + self.dut_name = dut_name + + self._cd_name = cd_name + self._clk_period = clk_period + + cd = ClockDomain(self._cd_name) + cd_ps = ClockDomain("sys_ps") + self.clock_domains = [cd, cd_ps] + self.ios = {cd.clk, cd.rst, cd_ps.clk} + + def get(self, sockaddr): + template1 = """`timescale 1ns / 1ps + +module {top_name}(); + +reg {clk_name}; +reg {rst_name}; +reg sys_ps_clk; + +initial begin + {rst_name} <= 1'b1; + @(posedge {clk_name}); + {rst_name} <= 1'b0; +end + +always begin + {clk_name} <= 1'b0; + #{hclk_period}; + {clk_name} <= 1'b1; + #{hclk_period}; +end + +always @(posedge {clk_name} or negedge {clk_name}) + sys_ps_clk <= #({hclk_period}*2-3) {clk_name}; + +{dut_type} {dut_name}( + .{rst_name}({rst_name}), + .{clk_name}({clk_name}), + .sys_ps_clk(sys_ps_clk) +); + +initial $migensim_connect("{sockaddr}"); +always @(posedge {clk_name}) $migensim_tick; +""" + template2 = """ +initial begin + $dumpfile("{vcd_name}"); + $dumpvars({vcd_level}, {dut_name}); +end +""" + r = template1.format(top_name=self.top_name, + dut_type=self.dut_type, + dut_name=self.dut_name, + clk_name=self._cd_name + "_clk", + rst_name=self._cd_name + "_rst", + hclk_period=str(self._clk_period/2), + sockaddr=sockaddr) + if self.vcd_name is not None: + r += template2.format(vcd_name=self.vcd_name, + vcd_level=str(self.vcd_level), + dut_name=self.dut_name) + r += "\nendmodule" + return r + + +if __name__ == "__main__": + + plat = board.Platform() + + sdram_geom = sdram.GeomSettings( + bank_a=2, + row_a=12, + col_a=8 + ) + + sdram_timing = sdram.TimingSettings( + tRP=ns(15), + tRCD=ns(15), + tWR=ns(14), + tWTR=2, + tREFI=ns(64*1000*1000/4096, False), + tRFC=ns(66), + req_queue_size=8, + read_time=32, + write_time=16 + ) + + sdram_pads = plat.request("sdram") + sdram_clk = plat.request("sdram_clock") + + sdrphy = gensdrphy.GENSDRPHY(sdram_pads) + +# This sets CL to 2 during LMR done on 1st cycle + sdram_pads.a.reset = 1<<5 + + s = MiniconTB(sdrphy, sdrphy.dfi, sdram_geom, sdram_timing, pads=sdram_pads, sdram_clk=sdram_clk) + + extra_files = [ "sdram_model/mt48lc4m16a2.v" ] + + if not isfile(extra_files[0]): + print("ERROR: You need to download Micron Verilog simulation model for MT48LC4M16A2 and put it in sdram_model/mt48lc4m16a2.v") + print("File can be downloaded from this URL: http://www.micron.com/-/media/documents/products/sim%20model/dram/dram/4054mt48lc4m16a2.zip") + sys.exit(1) + + with Simulator(s, MyTopLevel("top.vcd", clk_period=int(1/0.08)), icarus.Runner(extra_files=extra_files, keep_files=True)) as sim: + sim.run(5000) diff --git a/misoclib/sdram/phy/gensdrphy.py b/misoclib/sdram/phy/gensdrphy.py new file mode 100644 index 00000000..9abd1d00 --- /dev/null +++ b/misoclib/sdram/phy/gensdrphy.py @@ -0,0 +1,94 @@ +# +# 1:1 frequency-ratio Generic SDR PHY +# +# The GENSDRPHY is validated on CycloneIV (Altera) but since it does +# not use vendor-dependent code, it can also be used on other architectures. +# +# The PHY needs 2 Clock domains: +# - sys_clk : The System Clock domain +# - sys_clk_ps : The System Clock domain with its phase shifted +# (-3ns on C4@100MHz) +# +# Assert dfi_wrdata_en and present the data +# on dfi_wrdata_mask/dfi_wrdata in the same +# cycle as the write command. +# +# Assert dfi_rddata_en in the same cycle as the read +# command. The data will come back on dfi_rddata +# 4 cycles later, along with the assertion of +# dfi_rddata_valid. +# +# This PHY only supports CAS Latency 2. +# + +from migen.fhdl.std import * +from migen.bus.dfi import * +from migen.genlib.record import * +from migen.fhdl.specials import * + +from misoclib import sdram + +class GENSDRPHY(Module): + def __init__(self, pads): + a = flen(pads.a) + ba = flen(pads.ba) + d = flen(pads.dq) + + self.phy_settings = sdram.PhySettings( + memtype="SDR", + dfi_d=d, + nphases=1, + rdphase=0, + wrphase=0, + rdcmdphase=0, + wrcmdphase=0, + cl=2, + read_latency=4, + write_latency=0 + ) + + self.dfi = Interface(a, ba, d) + + ### + + # + # Command/address + # + self.sync += [ + pads.a.eq(self.dfi.p0.address), + pads.ba.eq(self.dfi.p0.bank), + pads.cke.eq(self.dfi.p0.cke), + pads.cas_n.eq(self.dfi.p0.cas_n), + pads.ras_n.eq(self.dfi.p0.ras_n), + pads.we_n.eq(self.dfi.p0.we_n) + ] + if hasattr(pads, "cs_n"): + self.sync += pads.cs_n.eq(self.dfi.p0.cs_n), + + # + # DQ/DQS/DM data + # + sd_dq_out = Signal(d) + drive_dq = Signal() + self.sync += sd_dq_out.eq(self.dfi.p0.wrdata), + self.specials += Tristate(pads.dq, sd_dq_out, drive_dq) + self.sync += \ + If(self.dfi.p0.wrdata_en, + pads.dm.eq(self.dfi.p0.wrdata_mask) + ).Else( + pads.dm.eq(0) + ) + sd_dq_in_ps = Signal(d) + self.sync.sys_ps += sd_dq_in_ps.eq(pads.dq) + self.sync += self.dfi.p0.rddata.eq(sd_dq_in_ps) + + # + # DQ/DM control + # + d_dfi_wrdata_en = Signal() + self.sync += d_dfi_wrdata_en.eq(self.dfi.p0.wrdata_en) + self.comb += drive_dq.eq(d_dfi_wrdata_en) + + rddata_sr = Signal(4) + self.comb += self.dfi.p0.rddata_valid.eq(rddata_sr[3]) + self.sync += rddata_sr.eq(Cat(self.dfi.p0.rddata_en, rddata_sr[:3])) diff --git a/misoclib/sdram/phy/initsequence.py b/misoclib/sdram/phy/initsequence.py new file mode 100644 index 00000000..32ccd77f --- /dev/null +++ b/misoclib/sdram/phy/initsequence.py @@ -0,0 +1,225 @@ +from migen.fhdl.std import log2_int + +def get_sdram_phy_header(sdram_phy): + r = "#ifndef __GENERATED_SDRAM_PHY_H\n#define __GENERATED_SDRAM_PHY_H\n" + r += "#include \n#include \n#include \n\n" + + nphases = sdram_phy.phy_settings.nphases + r += "#define DFII_NPHASES "+str(nphases)+"\n\n" + + r += "static void cdelay(int i);\n" + + # commands_px functions + for n in range(nphases): + r += """ +static void command_p{n}(int cmd) +{{ + dfii_pi{n}_command_write(cmd); + dfii_pi{n}_command_issue_write(1); +}}""".format(n=str(n)) + r += "\n\n" + + # rd/wr access macros + r += """ +#define dfii_pird_address_write(X) dfii_pi{rdphase}_address_write(X) +#define dfii_piwr_address_write(X) dfii_pi{wrphase}_address_write(X) + +#define dfii_pird_baddress_write(X) dfii_pi{rdphase}_baddress_write(X) +#define dfii_piwr_baddress_write(X) dfii_pi{wrphase}_baddress_write(X) + +#define command_prd(X) command_p{rdphase}(X) +#define command_pwr(X) command_p{wrphase}(X) +""".format(rdphase=str(sdram_phy.phy_settings.rdphase), wrphase=str(sdram_phy.phy_settings.wrphase)) + r +="\n" + + # + # sdrrd/sdrwr functions utilities + # + r += "#define DFII_PIX_DATA_SIZE CSR_DFII_PI0_WRDATA_SIZE\n" + dfii_pix_wrdata_addr = [] + for n in range(nphases): + dfii_pix_wrdata_addr.append("CSR_DFII_PI{n}_WRDATA_ADDR".format(n=n)) + r += """ +const unsigned int dfii_pix_wrdata_addr[{n}] = {{ + {dfii_pix_wrdata_addr} +}}; +""".format(n=nphases, dfii_pix_wrdata_addr=",\n\t".join(dfii_pix_wrdata_addr)) + + dfii_pix_rddata_addr = [] + for n in range(nphases): + dfii_pix_rddata_addr.append("CSR_DFII_PI{n}_RDDATA_ADDR".format(n=n)) + r += """ +const unsigned int dfii_pix_rddata_addr[{n}] = {{ + {dfii_pix_rddata_addr} +}}; +""".format(n=nphases, dfii_pix_rddata_addr=",\n\t".join(dfii_pix_rddata_addr)) + r +="\n" + + # init sequence + cmds = { + "PRECHARGE_ALL" : "DFII_COMMAND_RAS|DFII_COMMAND_WE|DFII_COMMAND_CS", + "MODE_REGISTER" : "DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS", + "AUTO_REFRESH" : "DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_CS", + "UNRESET" : "DFII_CONTROL_ODT|DFII_CONTROL_RESET_N", + "CKE" : "DFII_CONTROL_CKE|DFII_CONTROL_ODT|DFII_CONTROL_RESET_N" + } + + cl = sdram_phy.phy_settings.cl + + if sdram_phy.phy_settings.memtype == "SDR": + bl = sdram_phy.phy_settings.nphases + mr = log2_int(bl) + (cl << 4) + reset_dll = 1 << 8 + + init_sequence = [ + ("Bring CKE high", 0x0000, 0, cmds["CKE"], 20000), + ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), + ("Load Mode Register / Reset DLL, CL={0:d}, BL={1:d}".format(cl, bl), mr + reset_dll, 0, cmds["MODE_REGISTER"], 200), + ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), + ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), + ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), + ("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200) + ] + + elif sdram_phy.phy_settings.memtype == "DDR": + bl = 2*sdram_phy.phy_settings.nphases + mr = log2_int(bl) + (cl << 4) + emr = 0 + reset_dll = 1 << 8 + + init_sequence = [ + ("Bring CKE high", 0x0000, 0, cmds["CKE"], 20000), + ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), + ("Load Extended Mode Register", emr, 1, cmds["MODE_REGISTER"], 0), + ("Load Mode Register / Reset DLL, CL={0:d}, BL={1:d}".format(cl, bl), mr + reset_dll, 0, cmds["MODE_REGISTER"], 200), + ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), + ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), + ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), + ("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200) + ] + + elif sdram_phy.phy_settings.memtype == "LPDDR": + bl = 2*sdram_phy.phy_settings.nphases + mr = log2_int(bl) + (cl << 4) + emr = 0 + reset_dll = 1 << 8 + + init_sequence = [ + ("Bring CKE high", 0x0000, 0, cmds["CKE"], 20000), + ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), + ("Load Extended Mode Register", emr, 2, cmds["MODE_REGISTER"], 0), + ("Load Mode Register / Reset DLL, CL={0:d}, BL={1:d}".format(cl, bl), mr + reset_dll, 0, cmds["MODE_REGISTER"], 200), + ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), + ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), + ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), + ("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200) + ] + + elif sdram_phy.phy_settings.memtype == "DDR2": + bl = 2*sdram_phy.phy_settings.nphases + wr = 2 + mr = log2_int(bl) + (cl << 4) + (wr << 9) + emr = 0 + emr2 = 0 + emr3 = 0 + reset_dll = 1 << 8 + ocd = 7 << 7 + + init_sequence = [ + ("Bring CKE high", 0x0000, 0, cmds["CKE"], 20000), + ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), + ("Load Extended Mode Register 3", emr3, 3, cmds["MODE_REGISTER"], 0), + ("Load Extended Mode Register 2", emr2, 2, cmds["MODE_REGISTER"], 0), + ("Load Extended Mode Register", emr, 1, cmds["MODE_REGISTER"], 0), + ("Load Mode Register / Reset DLL, CL={0:d}, BL={1:d}".format(cl, bl), mr + reset_dll, 0, cmds["MODE_REGISTER"], 200), + ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), + ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), + ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), + ("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200), + ("Load Extended Mode Register / OCD Default", emr+ocd, 1, cmds["MODE_REGISTER"], 0), + ("Load Extended Mode Register / OCD Exit", emr, 1, cmds["MODE_REGISTER"], 0), + ] + elif sdram_phy.phy_settings.memtype == "DDR3": + bl = 2*sdram_phy.phy_settings.nphases + if bl != 8: + raise NotImplementedError("DDR3 PHY header generator only supports BL of 8") + + def format_mr0(cl, wr, dll_reset): + cl_to_mr0 = { + 5 : 0b0010, + 6 : 0b0100, + 7 : 0b0110, + 8 : 0b1000, + 9 : 0b1010, + 10: 0b1100, + 11: 0b1110, + 12: 0b0001, + 13: 0b0011, + 14: 0b0101 + } + wr_to_mr0 = { + 16: 0b000, + 5 : 0b001, + 6 : 0b010, + 7 : 0b011, + 8 : 0b100, + 10: 0b101, + 12: 0b110, + 14: 0b111 + } + mr0 = (cl_to_mr0[cl] & 1) << 2 + mr0 |= ((cl_to_mr0[cl] >> 1) & 0b111) << 4 + mr0 |= dll_reset << 8 + mr0 |= wr_to_mr0[wr] << 9 + return mr0 + + def format_mr1(output_drive_strength, rtt_nom): + mr1 = ((output_drive_strength >> 0) & 1) << 1 + mr1 |= ((output_drive_strength >> 1) & 1) << 5 + mr1 |= ((rtt_nom >> 0) & 1) << 2 + mr1 |= ((rtt_nom >> 1) & 1) << 6 + mr1 |= ((rtt_nom >> 2) & 1) << 9 + return mr1 + + def format_mr2(cwl, rtt_wr): + mr2 = (cwl-5) << 3 + mr2 |= rtt_wr << 9 + return mr2 + + mr0 = format_mr0(cl, 8, 1) # wr=8 FIXME: this should be ceiling(tWR/tCK) + mr1 = format_mr1(1, 1) # Output Drive Strength RZQ/7 (34 ohm) / Rtt RZQ/4 (60 ohm) + mr2 = format_mr2(sdram_phy.phy_settings.cwl, 2) # Rtt(WR) RZQ/4 + mr3 = 0 + + init_sequence = [ + ("Release reset", 0x0000, 0, cmds["UNRESET"], 50000), + ("Bring CKE high", 0x0000, 0, cmds["CKE"], 10000), + ("Load Mode Register 2", mr2, 2, cmds["MODE_REGISTER"], 0), + ("Load Mode Register 3", mr3, 3, cmds["MODE_REGISTER"], 0), + ("Load Mode Register 1", mr1, 1, cmds["MODE_REGISTER"], 0), + ("Load Mode Register 0, CL={0:d}, BL={1:d}".format(cl, bl), mr0, 0, cmds["MODE_REGISTER"], 200), + ("ZQ Calibration", 0x0400, 0, "DFII_COMMAND_WE|DFII_COMMAND_CS", 200), + ] + + # the value of MR1 needs to be modified during write leveling + r += "#define DDR3_MR1 {}\n\n".format(mr1) + else: + raise NotImplementedError("Unsupported memory type: "+sdram_phy.phy_settings.memtype) + + r += "static void init_sequence(void)\n{\n" + for comment, a, ba, cmd, delay in init_sequence: + r += "\t/* {0} */\n".format(comment) + r += "\tdfii_pi0_address_write({0:#x});\n".format(a) + r += "\tdfii_pi0_baddress_write({0:d});\n".format(ba) + if cmd[:12] == "DFII_CONTROL": + r += "\tdfii_control_write({0});\n".format(cmd) + else: + r += "\tcommand_p0({0});\n".format(cmd) + if delay: + r += "\tcdelay({0:d});\n".format(delay) + r += "\n" + r += "}\n" + + r += "#endif\n" + + return r diff --git a/misoclib/sdram/phy/k7ddrphy.py b/misoclib/sdram/phy/k7ddrphy.py new file mode 100644 index 00000000..5e83f161 --- /dev/null +++ b/misoclib/sdram/phy/k7ddrphy.py @@ -0,0 +1,291 @@ +# tCK=5ns CL=7 CWL=6 + +from migen.fhdl.std import * +from migen.bus.dfi import * +from migen.bank.description import * + +from misoclib import sdram + +class K7DDRPHY(Module, AutoCSR): + def __init__(self, pads, memtype): + a = flen(pads.a) + ba = flen(pads.ba) + d = flen(pads.dq) + nphases = 4 + + self._r_wlevel_en = CSRStorage() + self._r_wlevel_strobe = CSR() + self._r_dly_sel = CSRStorage(d//8) + self._r_rdly_dq_rst = CSR() + self._r_rdly_dq_inc = CSR() + self._r_rdly_dq_bitslip = CSR() + self._r_wdly_dq_rst = CSR() + self._r_wdly_dq_inc = CSR() + self._r_wdly_dqs_rst = CSR() + self._r_wdly_dqs_inc = CSR() + + self.phy_settings = sdram.PhySettings( + memtype=memtype, + dfi_d=2*d, + nphases=nphases, + rdphase=0, + wrphase=2, + rdcmdphase=1, + wrcmdphase=0, + cl=7, + cwl=6, + read_latency=6, + write_latency=2 + ) + + self.dfi = Interface(a, ba, self.phy_settings.dfi_d, nphases) + + ### + + # Clock + sd_clk_se = Signal() + self.specials += [ + Instance("OSERDESE2", + p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1, + p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", + p_SERDES_MODE="MASTER", + + o_OQ=sd_clk_se, + i_OCE=1, + i_RST=ResetSignal(), + i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal(), + i_D1=0, i_D2=1, i_D3=0, i_D4=1, + i_D5=0, i_D6=1, i_D7=0, i_D8=1 + ), + Instance("OBUFDS", + i_I=sd_clk_se, + o_O=pads.clk_p, + o_OB=pads.clk_n + ) + ] + + # Addresses and commands + for i in range(a): + self.specials += \ + Instance("OSERDESE2", + p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1, + p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", + p_SERDES_MODE="MASTER", + + o_OQ=pads.a[i], + i_OCE=1, + i_RST=ResetSignal(), + i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal(), + i_D1=self.dfi.phases[0].address[i], i_D2=self.dfi.phases[0].address[i], + i_D3=self.dfi.phases[1].address[i], i_D4=self.dfi.phases[1].address[i], + i_D5=self.dfi.phases[2].address[i], i_D6=self.dfi.phases[2].address[i], + i_D7=self.dfi.phases[3].address[i], i_D8=self.dfi.phases[3].address[i] + ) + for i in range(ba): + self.specials += \ + Instance("OSERDESE2", + p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1, + p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", + p_SERDES_MODE="MASTER", + + o_OQ=pads.ba[i], + i_OCE=1, + i_RST=ResetSignal(), + i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal(), + i_D1=self.dfi.phases[0].bank[i], i_D2=self.dfi.phases[0].bank[i], + i_D3=self.dfi.phases[1].bank[i], i_D4=self.dfi.phases[1].bank[i], + i_D5=self.dfi.phases[2].bank[i], i_D6=self.dfi.phases[2].bank[i], + i_D7=self.dfi.phases[3].bank[i], i_D8=self.dfi.phases[3].bank[i] + ) + for name in "ras_n", "cas_n", "we_n", "cs_n", "cke", "odt", "reset_n": + self.specials += \ + Instance("OSERDESE2", + p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1, + p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", + p_SERDES_MODE="MASTER", + + o_OQ=getattr(pads, name), + i_OCE=1, + i_RST=ResetSignal(), + i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal(), + i_D1=getattr(self.dfi.phases[0], name), i_D2=getattr(self.dfi.phases[0], name), + i_D3=getattr(self.dfi.phases[1], name), i_D4=getattr(self.dfi.phases[1], name), + i_D5=getattr(self.dfi.phases[2], name), i_D6=getattr(self.dfi.phases[2], name), + i_D7=getattr(self.dfi.phases[3], name), i_D8=getattr(self.dfi.phases[3], name) + ) + + # DQS and DM + oe_dqs = Signal() + dqs_serdes_pattern = Signal(8) + self.comb += \ + If(self._r_wlevel_en.storage, + If(self._r_wlevel_strobe.re, + dqs_serdes_pattern.eq(0b00000001) + ).Else( + dqs_serdes_pattern.eq(0b00000000) + ) + ).Else( + dqs_serdes_pattern.eq(0b01010101) + ) + for i in range(d//8): + dm_o_nodelay = Signal() + self.specials += \ + Instance("OSERDESE2", + p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1, + p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", + p_SERDES_MODE="MASTER", + + o_OQ=dm_o_nodelay, + i_OCE=1, + i_RST=ResetSignal(), + i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal(), + i_D1=self.dfi.phases[0].wrdata_mask[i], i_D2=self.dfi.phases[0].wrdata_mask[d//8+i], + i_D3=self.dfi.phases[1].wrdata_mask[i], i_D4=self.dfi.phases[1].wrdata_mask[d//8+i], + i_D5=self.dfi.phases[2].wrdata_mask[i], i_D6=self.dfi.phases[2].wrdata_mask[d//8+i], + i_D7=self.dfi.phases[3].wrdata_mask[i], i_D8=self.dfi.phases[3].wrdata_mask[d//8+i] + ) + self.specials += \ + Instance("ODELAYE2", + p_DELAY_SRC="ODATAIN", p_SIGNAL_PATTERN="DATA", + p_CINVCTRL_SEL="FALSE", p_HIGH_PERFORMANCE_MODE="TRUE", p_REFCLK_FREQUENCY=200.0, + p_PIPE_SEL="FALSE", p_ODELAY_TYPE="VARIABLE", p_ODELAY_VALUE=0, + + i_C=ClockSignal(), + i_LD=self._r_dly_sel.storage[i] & self._r_wdly_dq_rst.re, + i_CE=self._r_dly_sel.storage[i] & self._r_wdly_dq_inc.re, + i_LDPIPEEN=0, i_INC=1, + + o_ODATAIN=dm_o_nodelay, o_DATAOUT=pads.dm[i] + ) + + dqs_nodelay = Signal() + dqs_delayed = Signal() + dqs_t = Signal() + self.specials += [ + Instance("OSERDESE2", + p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1, + p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", + p_SERDES_MODE="MASTER", + + o_OFB=dqs_nodelay, o_TQ=dqs_t, + i_OCE=1, i_TCE=1, + i_RST=ResetSignal(), + i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal(), + i_D1=dqs_serdes_pattern[0], i_D2=dqs_serdes_pattern[1], + i_D3=dqs_serdes_pattern[2], i_D4=dqs_serdes_pattern[3], + i_D5=dqs_serdes_pattern[4], i_D6=dqs_serdes_pattern[5], + i_D7=dqs_serdes_pattern[6], i_D8=dqs_serdes_pattern[7], + i_T1=~oe_dqs + ), + Instance("ODELAYE2", + p_DELAY_SRC="ODATAIN", p_SIGNAL_PATTERN="DATA", + p_CINVCTRL_SEL="FALSE", p_HIGH_PERFORMANCE_MODE="TRUE", p_REFCLK_FREQUENCY=200.0, + p_PIPE_SEL="FALSE", p_ODELAY_TYPE="VARIABLE", p_ODELAY_VALUE=6, + + i_C=ClockSignal(), + i_LD=self._r_dly_sel.storage[i] & self._r_wdly_dqs_rst.re, + i_CE=self._r_dly_sel.storage[i] & self._r_wdly_dqs_inc.re, + i_LDPIPEEN=0, i_INC=1, + + o_ODATAIN=dqs_nodelay, o_DATAOUT=dqs_delayed + ), + Instance("OBUFTDS", + i_I=dqs_delayed, i_T=dqs_t, + o_O=pads.dqs_p[i], o_OB=pads.dqs_n[i] + ) + ] + + # DQ + oe_dq = Signal() + for i in range(d): + dq_o_nodelay = Signal() + dq_o_delayed = Signal() + dq_i_nodelay = Signal() + dq_i_delayed = Signal() + dq_t = Signal() + self.specials += [ + Instance("OSERDESE2", + p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1, + p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", + p_SERDES_MODE="MASTER", + + o_OQ=dq_o_nodelay, o_TQ=dq_t, + i_OCE=1, i_TCE=1, + i_RST=ResetSignal(), + i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal(), + i_D1=self.dfi.phases[0].wrdata[i], i_D2=self.dfi.phases[0].wrdata[d+i], + i_D3=self.dfi.phases[1].wrdata[i], i_D4=self.dfi.phases[1].wrdata[d+i], + i_D5=self.dfi.phases[2].wrdata[i], i_D6=self.dfi.phases[2].wrdata[d+i], + i_D7=self.dfi.phases[3].wrdata[i], i_D8=self.dfi.phases[3].wrdata[d+i], + i_T1=~oe_dq + ), + Instance("ISERDESE2", + p_DATA_WIDTH=8, p_DATA_RATE="DDR", + p_SERDES_MODE="MASTER", p_INTERFACE_TYPE="NETWORKING", + p_NUM_CE=1, p_IOBDELAY="IFD", + + i_DDLY=dq_i_delayed, + i_CE1=1, + i_RST=ResetSignal() | (self._r_dly_sel.storage[i//8] & self._r_wdly_dq_rst.re), + i_CLK=ClockSignal("sys4x"), i_CLKB=~ClockSignal("sys4x"), i_CLKDIV=ClockSignal(), + i_BITSLIP=self._r_dly_sel.storage[i//8] & self._r_rdly_dq_bitslip.re, + o_Q8=self.dfi.phases[0].rddata[i], o_Q7=self.dfi.phases[0].rddata[d+i], + o_Q6=self.dfi.phases[1].rddata[i], o_Q5=self.dfi.phases[1].rddata[d+i], + o_Q4=self.dfi.phases[2].rddata[i], o_Q3=self.dfi.phases[2].rddata[d+i], + o_Q2=self.dfi.phases[3].rddata[i], o_Q1=self.dfi.phases[3].rddata[d+i] + ), + Instance("ODELAYE2", + p_DELAY_SRC="ODATAIN", p_SIGNAL_PATTERN="DATA", + p_CINVCTRL_SEL="FALSE", p_HIGH_PERFORMANCE_MODE="TRUE", p_REFCLK_FREQUENCY=200.0, + p_PIPE_SEL="FALSE", p_ODELAY_TYPE="VARIABLE", p_ODELAY_VALUE=0, + + i_C=ClockSignal(), + i_LD=self._r_dly_sel.storage[i//8] & self._r_wdly_dq_rst.re, + i_CE=self._r_dly_sel.storage[i//8] & self._r_wdly_dq_inc.re, + i_LDPIPEEN=0, i_INC=1, + + o_ODATAIN=dq_o_nodelay, o_DATAOUT=dq_o_delayed + ), + Instance("IDELAYE2", + p_DELAY_SRC="IDATAIN", p_SIGNAL_PATTERN="DATA", + p_CINVCTRL_SEL="FALSE", p_HIGH_PERFORMANCE_MODE="TRUE", p_REFCLK_FREQUENCY=200.0, + p_PIPE_SEL="FALSE", p_IDELAY_TYPE="VARIABLE", p_IDELAY_VALUE=6, + + i_C=ClockSignal(), + i_LD=self._r_dly_sel.storage[i//8] & self._r_rdly_dq_rst.re, + i_CE=self._r_dly_sel.storage[i//8] & self._r_rdly_dq_inc.re, + i_LDPIPEEN=0, i_INC=1, + + i_IDATAIN=dq_i_nodelay, o_DATAOUT=dq_i_delayed + ), + Instance("IOBUF", + i_I=dq_o_delayed, o_O=dq_i_nodelay, i_T=dq_t, + io_IO=pads.dq[i] + ) + ] + + # Flow control + # + # total read latency = 6: + # 2 cycles through OSERDESE2 + # 2 cycles CAS + # 2 cycles through ISERDESE2 + rddata_en = self.dfi.phases[self.phy_settings.rdphase].rddata_en + for i in range(5): + n_rddata_en = Signal() + self.sync += n_rddata_en.eq(rddata_en) + rddata_en = n_rddata_en + self.sync += [phase.rddata_valid.eq(rddata_en | self._r_wlevel_en.storage) + for phase in self.dfi.phases] + + oe = Signal() + last_wrdata_en = Signal(4) + wrphase = self.dfi.phases[self.phy_settings.wrphase] + self.sync += last_wrdata_en.eq(Cat(wrphase.wrdata_en, last_wrdata_en[:3])) + self.comb += oe.eq(last_wrdata_en[1] | last_wrdata_en[2] | last_wrdata_en[3]) + self.sync += \ + If(self._r_wlevel_en.storage, + oe_dqs.eq(1), oe_dq.eq(0) + ).Else( + oe_dqs.eq(oe), oe_dq.eq(oe) + ) diff --git a/misoclib/sdram/phy/s6ddrphy.py b/misoclib/sdram/phy/s6ddrphy.py new file mode 100644 index 00000000..0dd38372 --- /dev/null +++ b/misoclib/sdram/phy/s6ddrphy.py @@ -0,0 +1,358 @@ +# 1:2 frequency-ratio DDR / LPDDR / DDR2 PHY for Spartan-6 +# +# Assert dfi_wrdata_en and present the data +# on dfi_wrdata_mask/dfi_wrdata in the same +# cycle as the write command. +# +# Assert dfi_rddata_en in the same cycle as the read +# command. The data will come back on dfi_rddata +# 5 cycles later, along with the assertion +# of dfi_rddata_valid. +# +# This PHY only supports CAS Latency 3. +# Read commands must be sent on phase 0. +# Write commands must be sent on phase 1. +# + +from migen.fhdl.std import * +from migen.bus.dfi import * +from migen.genlib.record import * + +from misoclib import sdram + +class S6DDRPHY(Module): + def __init__(self, pads, memtype, rd_bitslip, wr_bitslip, dqs_ddr_alignment): + if memtype not in ["DDR", "LPDDR", "DDR2"]: + raise NotImplementedError("S6DDRPHY only supports DDR, LPDDR and DDR2") + a = flen(pads.a) + ba = flen(pads.ba) + d = flen(pads.dq) + nphases = 2 + + self.phy_settings = sdram.PhySettings( + memtype=memtype, + dfi_d=2*d, + nphases=nphases, + rdphase=0, + wrphase=1, + rdcmdphase=1, + wrcmdphase=0, + cl=3, + read_latency=5, + write_latency=0 + ) + + self.dfi = Interface(a, ba, self.phy_settings.dfi_d, nphases) + self.clk4x_wr_strb = Signal() + self.clk4x_rd_strb = Signal() + + ### + + # sys_clk : system clk, used for dfi interface + # sdram_half_clk : half rate sdram clk + # sdram_full_wr_clk : full rate sdram write clk + # sdram_full_rd_clk : full rate sdram read clk + sd_sys = getattr(self.sync, "sys") + sd_sdram_half = getattr(self.sync, "sdram_half") + + sys_clk = ClockSignal("sys") + sdram_half_clk = ClockSignal("sdram_half") + sdram_full_wr_clk = ClockSignal("sdram_full_wr") + sdram_full_rd_clk = ClockSignal("sdram_full_rd") + + # + # Command/address + # + + # select active phase + # sys_clk ----____----____ + # phase_sel(nphases=2) 0 1 0 1 Half Rate + phase_sel = Signal(log2_int(nphases)) + sys_clk_d = Signal() + + sd_sdram_half += [ + If(sys_clk & ~sys_clk_d, + phase_sel.eq(0) + ).Else( + phase_sel.eq(phase_sel+1) + ), + sys_clk_d.eq(sys_clk) + ] + + # register dfi cmds on half_rate clk + r_dfi = Array(Record(phase_cmd_description(a, ba)) for i in range(nphases)) + for n, phase in enumerate(self.dfi.phases): + sd_sdram_half +=[ + r_dfi[n].address.eq(phase.address), + r_dfi[n].bank.eq(phase.bank), + r_dfi[n].cs_n.eq(phase.cs_n), + r_dfi[n].cke.eq(phase.cke), + r_dfi[n].cas_n.eq(phase.cas_n), + r_dfi[n].ras_n.eq(phase.ras_n), + r_dfi[n].we_n.eq(phase.we_n) + ] + + # output cmds + sd_sdram_half += [ + pads.a.eq(r_dfi[phase_sel].address), + pads.ba.eq(r_dfi[phase_sel].bank), + pads.cke.eq(r_dfi[phase_sel].cke), + pads.ras_n.eq(r_dfi[phase_sel].ras_n), + pads.cas_n.eq(r_dfi[phase_sel].cas_n), + pads.we_n.eq(r_dfi[phase_sel].we_n) + ] + if hasattr(pads, "cs_n"): + sd_sdram_half += pads.cs_n.eq(r_dfi[phase_sel].cs_n) + + # + # Bitslip + # + bitslip_cnt = Signal(4) + bitslip_inc = Signal() + + sd_sys += [ + If(bitslip_cnt == rd_bitslip, + bitslip_inc.eq(0) + ).Else( + bitslip_cnt.eq(bitslip_cnt+1), + bitslip_inc.eq(1) + ) + ] + + # + # DQ/DQS/DM data + # + sdram_half_clk_n = Signal() + self.comb += sdram_half_clk_n.eq(~sdram_half_clk) + + postamble = Signal() + drive_dqs = Signal() + dqs_t_d0 = Signal() + dqs_t_d1 = Signal() + + dqs_o = Signal(d//8) + dqs_t = Signal(d//8) + + self.comb += [ + dqs_t_d0.eq(~(drive_dqs | postamble)), + dqs_t_d1.eq(~drive_dqs), + ] + + for i in range(d//8): + # DQS output + self.specials += Instance("ODDR2", + p_DDR_ALIGNMENT=dqs_ddr_alignment, + p_INIT=0, + p_SRTYPE="ASYNC", + + i_C0=sdram_half_clk, + i_C1=sdram_half_clk_n, + + i_CE=1, + i_D0=0, + i_D1=1, + i_R=0, + i_S=0, + + o_Q=dqs_o[i] + ) + + # DQS tristate cmd + self.specials += Instance("ODDR2", + p_DDR_ALIGNMENT=dqs_ddr_alignment, + p_INIT=0, + p_SRTYPE="ASYNC", + + i_C0=sdram_half_clk, + i_C1=sdram_half_clk_n, + + i_CE=1, + i_D0=dqs_t_d0, + i_D1=dqs_t_d1, + i_R=0, + i_S=0, + + o_Q=dqs_t[i] + ) + + # DQS tristate buffer + if hasattr(pads, "dqs_n"): + self.specials += Instance("OBUFTDS", + i_I=dqs_o[i], + i_T=dqs_t[i], + + o_O=pads.dqs[i], + o_OB=pads.dqs_n[i], + ) + else: + self.specials += Instance("OBUFT", + i_I=dqs_o[i], + i_T=dqs_t[i], + + o_O=pads.dqs[i] + ) + + sd_sdram_half += postamble.eq(drive_dqs) + + d_dfi = [Record(phase_wrdata_description(nphases*d)+phase_rddata_description(nphases*d)) + for i in range(2*nphases)] + + for n, phase in enumerate(self.dfi.phases): + self.comb += [ + d_dfi[n].wrdata.eq(phase.wrdata), + d_dfi[n].wrdata_mask.eq(phase.wrdata_mask), + d_dfi[n].wrdata_en.eq(phase.wrdata_en), + d_dfi[n].rddata_en.eq(phase.rddata_en), + ] + sd_sys += [ + d_dfi[nphases+n].wrdata.eq(phase.wrdata), + d_dfi[nphases+n].wrdata_mask.eq(phase.wrdata_mask) + ] + + + drive_dq = Signal() + drive_dq_n = [Signal() for i in range(2)] + self.comb += drive_dq_n[0].eq(~drive_dq) + sd_sys += drive_dq_n[1].eq(drive_dq_n[0]) + + dq_t = Signal(d) + dq_o = Signal(d) + dq_i = Signal(d) + + dq_wrdata = [] + for i in range(2): + for j in reversed(range(nphases)): + dq_wrdata.append(d_dfi[i*nphases+j].wrdata[:d]) + dq_wrdata.append(d_dfi[i*nphases+j].wrdata[d:]) + + for i in range(d): + # Data serializer + self.specials += Instance("OSERDES2", + p_DATA_WIDTH=4, + p_DATA_RATE_OQ="SDR", + p_DATA_RATE_OT="SDR", + p_SERDES_MODE="NONE", + p_OUTPUT_MODE="SINGLE_ENDED", + + o_OQ=dq_o[i], + i_OCE=1, + i_CLK0=sdram_full_wr_clk, + i_CLK1=0, + i_IOCE=self.clk4x_wr_strb, + i_RST=0, + i_CLKDIV=sys_clk, + + i_D1=dq_wrdata[wr_bitslip+3][i], + i_D2=dq_wrdata[wr_bitslip+2][i], + i_D3=dq_wrdata[wr_bitslip+1][i], + i_D4=dq_wrdata[wr_bitslip+0][i], + + o_TQ=dq_t[i], + i_T1=drive_dq_n[(wr_bitslip+3)//4], + i_T2=drive_dq_n[(wr_bitslip+2)//4], + i_T3=drive_dq_n[(wr_bitslip+1)//4], + i_T4=drive_dq_n[(wr_bitslip+0)//4], + i_TRAIN=0, + i_TCE=1, + i_SHIFTIN1=0, + i_SHIFTIN2=0, + i_SHIFTIN3=0, + i_SHIFTIN4=0, + ) + + # Data deserializer + self.specials += Instance("ISERDES2", + p_DATA_WIDTH=4, + p_DATA_RATE="SDR", + p_BITSLIP_ENABLE="TRUE", + p_SERDES_MODE="NONE", + p_INTERFACE_TYPE="RETIMED", + + i_D=dq_i[i], + i_CE0=1, + i_CLK0=sdram_full_rd_clk, + i_CLK1=0, + i_IOCE=self.clk4x_rd_strb, + i_RST=ResetSignal(), + i_CLKDIV=sys_clk, + i_BITSLIP=bitslip_inc, + + o_Q1=d_dfi[0*nphases+0].rddata[i+d], + o_Q2=d_dfi[0*nphases+0].rddata[i], + o_Q3=d_dfi[0*nphases+1].rddata[i+d], + o_Q4=d_dfi[0*nphases+1].rddata[i], + ) + + # Data buffer + self.specials += Instance("IOBUF", + i_I=dq_o[i], + o_O=dq_i[i], + i_T=dq_t[i], + io_IO=pads.dq[i] + ) + + dq_wrdata_mask = [] + for i in range(2): + for j in reversed(range(nphases)): + dq_wrdata_mask.append(d_dfi[i*nphases+j].wrdata_mask[:d//8]) + dq_wrdata_mask.append(d_dfi[i*nphases+j].wrdata_mask[d//8:]) + + for i in range(d//8): + # Mask serializer + self.specials += Instance("OSERDES2", + p_DATA_WIDTH=4, + p_DATA_RATE_OQ="SDR", + p_DATA_RATE_OT="SDR", + p_SERDES_MODE="NONE", + p_OUTPUT_MODE="SINGLE_ENDED", + + o_OQ=pads.dm[i], + i_OCE=1, + i_CLK0=sdram_full_wr_clk, + i_CLK1=0, + i_IOCE=self.clk4x_wr_strb, + i_RST=0, + i_CLKDIV=sys_clk, + + i_D1=dq_wrdata_mask[wr_bitslip+3][i], + i_D2=dq_wrdata_mask[wr_bitslip+2][i], + i_D3=dq_wrdata_mask[wr_bitslip+1][i], + i_D4=dq_wrdata_mask[wr_bitslip+0][i], + + i_TRAIN=0, + i_TCE=0, + i_SHIFTIN1=0, + i_SHIFTIN2=0, + i_SHIFTIN3=0, + i_SHIFTIN4=0, + ) + + # + # ODT + # + # ODT not yet supported + if hasattr(pads, "odt"): + self.comb += pads.odt.eq(0) + + # + # DQ/DQS/DM control + # + self.comb += drive_dq.eq(d_dfi[self.phy_settings.wrphase].wrdata_en) + + d_dfi_wrdata_en = Signal() + sd_sys += d_dfi_wrdata_en.eq(d_dfi[self.phy_settings.wrphase].wrdata_en) + + r_dfi_wrdata_en = Signal(2) + sd_sdram_half += r_dfi_wrdata_en.eq(Cat(d_dfi_wrdata_en, r_dfi_wrdata_en[0])) + + self.comb += drive_dqs.eq(r_dfi_wrdata_en[1]) + + rddata_sr = Signal(self.phy_settings.read_latency) + sd_sys += rddata_sr.eq(Cat(rddata_sr[1:self.phy_settings.read_latency], + d_dfi[self.phy_settings.rdphase].rddata_en)) + + for n, phase in enumerate(self.dfi.phases): + self.comb += [ + phase.rddata.eq(d_dfi[n].rddata), + phase.rddata_valid.eq(rddata_sr[0]), + ] diff --git a/misoclib/sdramphy/gensdrphy.py b/misoclib/sdramphy/gensdrphy.py deleted file mode 100644 index a065a939..00000000 --- a/misoclib/sdramphy/gensdrphy.py +++ /dev/null @@ -1,94 +0,0 @@ -# -# 1:1 frequency-ratio Generic SDR PHY -# -# The GENSDRPHY is validated on CycloneIV (Altera) but since it does -# not use vendor-dependent code, it can also be used on other architectures. -# -# The PHY needs 2 Clock domains: -# - sys_clk : The System Clock domain -# - sys_clk_ps : The System Clock domain with its phase shifted -# (-3ns on C4@100MHz) -# -# Assert dfi_wrdata_en and present the data -# on dfi_wrdata_mask/dfi_wrdata in the same -# cycle as the write command. -# -# Assert dfi_rddata_en in the same cycle as the read -# command. The data will come back on dfi_rddata -# 4 cycles later, along with the assertion of -# dfi_rddata_valid. -# -# This PHY only supports CAS Latency 2. -# - -from migen.fhdl.std import * -from migen.bus.dfi import * -from migen.genlib.record import * -from migen.fhdl.specials import * - -from misoclib import lasmicon - -class GENSDRPHY(Module): - def __init__(self, pads): - a = flen(pads.a) - ba = flen(pads.ba) - d = flen(pads.dq) - - self.phy_settings = lasmicon.PhySettings( - memtype="SDR", - dfi_d=d, - nphases=1, - rdphase=0, - wrphase=0, - rdcmdphase=0, - wrcmdphase=0, - cl=2, - read_latency=4, - write_latency=0 - ) - - self.dfi = Interface(a, ba, d) - - ### - - # - # Command/address - # - self.sync += [ - pads.a.eq(self.dfi.p0.address), - pads.ba.eq(self.dfi.p0.bank), - pads.cke.eq(self.dfi.p0.cke), - pads.cas_n.eq(self.dfi.p0.cas_n), - pads.ras_n.eq(self.dfi.p0.ras_n), - pads.we_n.eq(self.dfi.p0.we_n) - ] - if hasattr(pads, "cs_n"): - self.sync += pads.cs_n.eq(self.dfi.p0.cs_n), - - # - # DQ/DQS/DM data - # - sd_dq_out = Signal(d) - drive_dq = Signal() - self.sync += sd_dq_out.eq(self.dfi.p0.wrdata), - self.specials += Tristate(pads.dq, sd_dq_out, drive_dq) - self.sync += \ - If(self.dfi.p0.wrdata_en, - pads.dm.eq(self.dfi.p0.wrdata_mask) - ).Else( - pads.dm.eq(0) - ) - sd_dq_in_ps = Signal(d) - self.sync.sys_ps += sd_dq_in_ps.eq(pads.dq) - self.sync += self.dfi.p0.rddata.eq(sd_dq_in_ps) - - # - # DQ/DM control - # - d_dfi_wrdata_en = Signal() - self.sync += d_dfi_wrdata_en.eq(self.dfi.p0.wrdata_en) - self.comb += drive_dq.eq(d_dfi_wrdata_en) - - rddata_sr = Signal(4) - self.comb += self.dfi.p0.rddata_valid.eq(rddata_sr[3]) - self.sync += rddata_sr.eq(Cat(self.dfi.p0.rddata_en, rddata_sr[:3])) diff --git a/misoclib/sdramphy/initsequence.py b/misoclib/sdramphy/initsequence.py deleted file mode 100644 index 32ccd77f..00000000 --- a/misoclib/sdramphy/initsequence.py +++ /dev/null @@ -1,225 +0,0 @@ -from migen.fhdl.std import log2_int - -def get_sdram_phy_header(sdram_phy): - r = "#ifndef __GENERATED_SDRAM_PHY_H\n#define __GENERATED_SDRAM_PHY_H\n" - r += "#include \n#include \n#include \n\n" - - nphases = sdram_phy.phy_settings.nphases - r += "#define DFII_NPHASES "+str(nphases)+"\n\n" - - r += "static void cdelay(int i);\n" - - # commands_px functions - for n in range(nphases): - r += """ -static void command_p{n}(int cmd) -{{ - dfii_pi{n}_command_write(cmd); - dfii_pi{n}_command_issue_write(1); -}}""".format(n=str(n)) - r += "\n\n" - - # rd/wr access macros - r += """ -#define dfii_pird_address_write(X) dfii_pi{rdphase}_address_write(X) -#define dfii_piwr_address_write(X) dfii_pi{wrphase}_address_write(X) - -#define dfii_pird_baddress_write(X) dfii_pi{rdphase}_baddress_write(X) -#define dfii_piwr_baddress_write(X) dfii_pi{wrphase}_baddress_write(X) - -#define command_prd(X) command_p{rdphase}(X) -#define command_pwr(X) command_p{wrphase}(X) -""".format(rdphase=str(sdram_phy.phy_settings.rdphase), wrphase=str(sdram_phy.phy_settings.wrphase)) - r +="\n" - - # - # sdrrd/sdrwr functions utilities - # - r += "#define DFII_PIX_DATA_SIZE CSR_DFII_PI0_WRDATA_SIZE\n" - dfii_pix_wrdata_addr = [] - for n in range(nphases): - dfii_pix_wrdata_addr.append("CSR_DFII_PI{n}_WRDATA_ADDR".format(n=n)) - r += """ -const unsigned int dfii_pix_wrdata_addr[{n}] = {{ - {dfii_pix_wrdata_addr} -}}; -""".format(n=nphases, dfii_pix_wrdata_addr=",\n\t".join(dfii_pix_wrdata_addr)) - - dfii_pix_rddata_addr = [] - for n in range(nphases): - dfii_pix_rddata_addr.append("CSR_DFII_PI{n}_RDDATA_ADDR".format(n=n)) - r += """ -const unsigned int dfii_pix_rddata_addr[{n}] = {{ - {dfii_pix_rddata_addr} -}}; -""".format(n=nphases, dfii_pix_rddata_addr=",\n\t".join(dfii_pix_rddata_addr)) - r +="\n" - - # init sequence - cmds = { - "PRECHARGE_ALL" : "DFII_COMMAND_RAS|DFII_COMMAND_WE|DFII_COMMAND_CS", - "MODE_REGISTER" : "DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS", - "AUTO_REFRESH" : "DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_CS", - "UNRESET" : "DFII_CONTROL_ODT|DFII_CONTROL_RESET_N", - "CKE" : "DFII_CONTROL_CKE|DFII_CONTROL_ODT|DFII_CONTROL_RESET_N" - } - - cl = sdram_phy.phy_settings.cl - - if sdram_phy.phy_settings.memtype == "SDR": - bl = sdram_phy.phy_settings.nphases - mr = log2_int(bl) + (cl << 4) - reset_dll = 1 << 8 - - init_sequence = [ - ("Bring CKE high", 0x0000, 0, cmds["CKE"], 20000), - ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), - ("Load Mode Register / Reset DLL, CL={0:d}, BL={1:d}".format(cl, bl), mr + reset_dll, 0, cmds["MODE_REGISTER"], 200), - ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), - ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), - ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), - ("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200) - ] - - elif sdram_phy.phy_settings.memtype == "DDR": - bl = 2*sdram_phy.phy_settings.nphases - mr = log2_int(bl) + (cl << 4) - emr = 0 - reset_dll = 1 << 8 - - init_sequence = [ - ("Bring CKE high", 0x0000, 0, cmds["CKE"], 20000), - ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), - ("Load Extended Mode Register", emr, 1, cmds["MODE_REGISTER"], 0), - ("Load Mode Register / Reset DLL, CL={0:d}, BL={1:d}".format(cl, bl), mr + reset_dll, 0, cmds["MODE_REGISTER"], 200), - ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), - ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), - ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), - ("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200) - ] - - elif sdram_phy.phy_settings.memtype == "LPDDR": - bl = 2*sdram_phy.phy_settings.nphases - mr = log2_int(bl) + (cl << 4) - emr = 0 - reset_dll = 1 << 8 - - init_sequence = [ - ("Bring CKE high", 0x0000, 0, cmds["CKE"], 20000), - ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), - ("Load Extended Mode Register", emr, 2, cmds["MODE_REGISTER"], 0), - ("Load Mode Register / Reset DLL, CL={0:d}, BL={1:d}".format(cl, bl), mr + reset_dll, 0, cmds["MODE_REGISTER"], 200), - ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), - ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), - ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), - ("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200) - ] - - elif sdram_phy.phy_settings.memtype == "DDR2": - bl = 2*sdram_phy.phy_settings.nphases - wr = 2 - mr = log2_int(bl) + (cl << 4) + (wr << 9) - emr = 0 - emr2 = 0 - emr3 = 0 - reset_dll = 1 << 8 - ocd = 7 << 7 - - init_sequence = [ - ("Bring CKE high", 0x0000, 0, cmds["CKE"], 20000), - ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), - ("Load Extended Mode Register 3", emr3, 3, cmds["MODE_REGISTER"], 0), - ("Load Extended Mode Register 2", emr2, 2, cmds["MODE_REGISTER"], 0), - ("Load Extended Mode Register", emr, 1, cmds["MODE_REGISTER"], 0), - ("Load Mode Register / Reset DLL, CL={0:d}, BL={1:d}".format(cl, bl), mr + reset_dll, 0, cmds["MODE_REGISTER"], 200), - ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), - ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), - ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), - ("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200), - ("Load Extended Mode Register / OCD Default", emr+ocd, 1, cmds["MODE_REGISTER"], 0), - ("Load Extended Mode Register / OCD Exit", emr, 1, cmds["MODE_REGISTER"], 0), - ] - elif sdram_phy.phy_settings.memtype == "DDR3": - bl = 2*sdram_phy.phy_settings.nphases - if bl != 8: - raise NotImplementedError("DDR3 PHY header generator only supports BL of 8") - - def format_mr0(cl, wr, dll_reset): - cl_to_mr0 = { - 5 : 0b0010, - 6 : 0b0100, - 7 : 0b0110, - 8 : 0b1000, - 9 : 0b1010, - 10: 0b1100, - 11: 0b1110, - 12: 0b0001, - 13: 0b0011, - 14: 0b0101 - } - wr_to_mr0 = { - 16: 0b000, - 5 : 0b001, - 6 : 0b010, - 7 : 0b011, - 8 : 0b100, - 10: 0b101, - 12: 0b110, - 14: 0b111 - } - mr0 = (cl_to_mr0[cl] & 1) << 2 - mr0 |= ((cl_to_mr0[cl] >> 1) & 0b111) << 4 - mr0 |= dll_reset << 8 - mr0 |= wr_to_mr0[wr] << 9 - return mr0 - - def format_mr1(output_drive_strength, rtt_nom): - mr1 = ((output_drive_strength >> 0) & 1) << 1 - mr1 |= ((output_drive_strength >> 1) & 1) << 5 - mr1 |= ((rtt_nom >> 0) & 1) << 2 - mr1 |= ((rtt_nom >> 1) & 1) << 6 - mr1 |= ((rtt_nom >> 2) & 1) << 9 - return mr1 - - def format_mr2(cwl, rtt_wr): - mr2 = (cwl-5) << 3 - mr2 |= rtt_wr << 9 - return mr2 - - mr0 = format_mr0(cl, 8, 1) # wr=8 FIXME: this should be ceiling(tWR/tCK) - mr1 = format_mr1(1, 1) # Output Drive Strength RZQ/7 (34 ohm) / Rtt RZQ/4 (60 ohm) - mr2 = format_mr2(sdram_phy.phy_settings.cwl, 2) # Rtt(WR) RZQ/4 - mr3 = 0 - - init_sequence = [ - ("Release reset", 0x0000, 0, cmds["UNRESET"], 50000), - ("Bring CKE high", 0x0000, 0, cmds["CKE"], 10000), - ("Load Mode Register 2", mr2, 2, cmds["MODE_REGISTER"], 0), - ("Load Mode Register 3", mr3, 3, cmds["MODE_REGISTER"], 0), - ("Load Mode Register 1", mr1, 1, cmds["MODE_REGISTER"], 0), - ("Load Mode Register 0, CL={0:d}, BL={1:d}".format(cl, bl), mr0, 0, cmds["MODE_REGISTER"], 200), - ("ZQ Calibration", 0x0400, 0, "DFII_COMMAND_WE|DFII_COMMAND_CS", 200), - ] - - # the value of MR1 needs to be modified during write leveling - r += "#define DDR3_MR1 {}\n\n".format(mr1) - else: - raise NotImplementedError("Unsupported memory type: "+sdram_phy.phy_settings.memtype) - - r += "static void init_sequence(void)\n{\n" - for comment, a, ba, cmd, delay in init_sequence: - r += "\t/* {0} */\n".format(comment) - r += "\tdfii_pi0_address_write({0:#x});\n".format(a) - r += "\tdfii_pi0_baddress_write({0:d});\n".format(ba) - if cmd[:12] == "DFII_CONTROL": - r += "\tdfii_control_write({0});\n".format(cmd) - else: - r += "\tcommand_p0({0});\n".format(cmd) - if delay: - r += "\tcdelay({0:d});\n".format(delay) - r += "\n" - r += "}\n" - - r += "#endif\n" - - return r diff --git a/misoclib/sdramphy/k7ddrphy.py b/misoclib/sdramphy/k7ddrphy.py deleted file mode 100644 index f6012940..00000000 --- a/misoclib/sdramphy/k7ddrphy.py +++ /dev/null @@ -1,291 +0,0 @@ -# tCK=5ns CL=7 CWL=6 - -from migen.fhdl.std import * -from migen.bus.dfi import * -from migen.bank.description import * - -from misoclib import lasmicon - -class K7DDRPHY(Module, AutoCSR): - def __init__(self, pads, memtype): - a = flen(pads.a) - ba = flen(pads.ba) - d = flen(pads.dq) - nphases = 4 - - self._r_wlevel_en = CSRStorage() - self._r_wlevel_strobe = CSR() - self._r_dly_sel = CSRStorage(d//8) - self._r_rdly_dq_rst = CSR() - self._r_rdly_dq_inc = CSR() - self._r_rdly_dq_bitslip = CSR() - self._r_wdly_dq_rst = CSR() - self._r_wdly_dq_inc = CSR() - self._r_wdly_dqs_rst = CSR() - self._r_wdly_dqs_inc = CSR() - - self.phy_settings = lasmicon.PhySettings( - memtype=memtype, - dfi_d=2*d, - nphases=nphases, - rdphase=0, - wrphase=2, - rdcmdphase=1, - wrcmdphase=0, - cl=7, - cwl=6, - read_latency=6, - write_latency=2 - ) - - self.dfi = Interface(a, ba, self.phy_settings.dfi_d, nphases) - - ### - - # Clock - sd_clk_se = Signal() - self.specials += [ - Instance("OSERDESE2", - p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1, - p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", - p_SERDES_MODE="MASTER", - - o_OQ=sd_clk_se, - i_OCE=1, - i_RST=ResetSignal(), - i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal(), - i_D1=0, i_D2=1, i_D3=0, i_D4=1, - i_D5=0, i_D6=1, i_D7=0, i_D8=1 - ), - Instance("OBUFDS", - i_I=sd_clk_se, - o_O=pads.clk_p, - o_OB=pads.clk_n - ) - ] - - # Addresses and commands - for i in range(a): - self.specials += \ - Instance("OSERDESE2", - p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1, - p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", - p_SERDES_MODE="MASTER", - - o_OQ=pads.a[i], - i_OCE=1, - i_RST=ResetSignal(), - i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal(), - i_D1=self.dfi.phases[0].address[i], i_D2=self.dfi.phases[0].address[i], - i_D3=self.dfi.phases[1].address[i], i_D4=self.dfi.phases[1].address[i], - i_D5=self.dfi.phases[2].address[i], i_D6=self.dfi.phases[2].address[i], - i_D7=self.dfi.phases[3].address[i], i_D8=self.dfi.phases[3].address[i] - ) - for i in range(ba): - self.specials += \ - Instance("OSERDESE2", - p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1, - p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", - p_SERDES_MODE="MASTER", - - o_OQ=pads.ba[i], - i_OCE=1, - i_RST=ResetSignal(), - i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal(), - i_D1=self.dfi.phases[0].bank[i], i_D2=self.dfi.phases[0].bank[i], - i_D3=self.dfi.phases[1].bank[i], i_D4=self.dfi.phases[1].bank[i], - i_D5=self.dfi.phases[2].bank[i], i_D6=self.dfi.phases[2].bank[i], - i_D7=self.dfi.phases[3].bank[i], i_D8=self.dfi.phases[3].bank[i] - ) - for name in "ras_n", "cas_n", "we_n", "cs_n", "cke", "odt", "reset_n": - self.specials += \ - Instance("OSERDESE2", - p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1, - p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", - p_SERDES_MODE="MASTER", - - o_OQ=getattr(pads, name), - i_OCE=1, - i_RST=ResetSignal(), - i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal(), - i_D1=getattr(self.dfi.phases[0], name), i_D2=getattr(self.dfi.phases[0], name), - i_D3=getattr(self.dfi.phases[1], name), i_D4=getattr(self.dfi.phases[1], name), - i_D5=getattr(self.dfi.phases[2], name), i_D6=getattr(self.dfi.phases[2], name), - i_D7=getattr(self.dfi.phases[3], name), i_D8=getattr(self.dfi.phases[3], name) - ) - - # DQS and DM - oe_dqs = Signal() - dqs_serdes_pattern = Signal(8) - self.comb += \ - If(self._r_wlevel_en.storage, - If(self._r_wlevel_strobe.re, - dqs_serdes_pattern.eq(0b00000001) - ).Else( - dqs_serdes_pattern.eq(0b00000000) - ) - ).Else( - dqs_serdes_pattern.eq(0b01010101) - ) - for i in range(d//8): - dm_o_nodelay = Signal() - self.specials += \ - Instance("OSERDESE2", - p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1, - p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", - p_SERDES_MODE="MASTER", - - o_OQ=dm_o_nodelay, - i_OCE=1, - i_RST=ResetSignal(), - i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal(), - i_D1=self.dfi.phases[0].wrdata_mask[i], i_D2=self.dfi.phases[0].wrdata_mask[d//8+i], - i_D3=self.dfi.phases[1].wrdata_mask[i], i_D4=self.dfi.phases[1].wrdata_mask[d//8+i], - i_D5=self.dfi.phases[2].wrdata_mask[i], i_D6=self.dfi.phases[2].wrdata_mask[d//8+i], - i_D7=self.dfi.phases[3].wrdata_mask[i], i_D8=self.dfi.phases[3].wrdata_mask[d//8+i] - ) - self.specials += \ - Instance("ODELAYE2", - p_DELAY_SRC="ODATAIN", p_SIGNAL_PATTERN="DATA", - p_CINVCTRL_SEL="FALSE", p_HIGH_PERFORMANCE_MODE="TRUE", p_REFCLK_FREQUENCY=200.0, - p_PIPE_SEL="FALSE", p_ODELAY_TYPE="VARIABLE", p_ODELAY_VALUE=0, - - i_C=ClockSignal(), - i_LD=self._r_dly_sel.storage[i] & self._r_wdly_dq_rst.re, - i_CE=self._r_dly_sel.storage[i] & self._r_wdly_dq_inc.re, - i_LDPIPEEN=0, i_INC=1, - - o_ODATAIN=dm_o_nodelay, o_DATAOUT=pads.dm[i] - ) - - dqs_nodelay = Signal() - dqs_delayed = Signal() - dqs_t = Signal() - self.specials += [ - Instance("OSERDESE2", - p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1, - p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", - p_SERDES_MODE="MASTER", - - o_OFB=dqs_nodelay, o_TQ=dqs_t, - i_OCE=1, i_TCE=1, - i_RST=ResetSignal(), - i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal(), - i_D1=dqs_serdes_pattern[0], i_D2=dqs_serdes_pattern[1], - i_D3=dqs_serdes_pattern[2], i_D4=dqs_serdes_pattern[3], - i_D5=dqs_serdes_pattern[4], i_D6=dqs_serdes_pattern[5], - i_D7=dqs_serdes_pattern[6], i_D8=dqs_serdes_pattern[7], - i_T1=~oe_dqs - ), - Instance("ODELAYE2", - p_DELAY_SRC="ODATAIN", p_SIGNAL_PATTERN="DATA", - p_CINVCTRL_SEL="FALSE", p_HIGH_PERFORMANCE_MODE="TRUE", p_REFCLK_FREQUENCY=200.0, - p_PIPE_SEL="FALSE", p_ODELAY_TYPE="VARIABLE", p_ODELAY_VALUE=6, - - i_C=ClockSignal(), - i_LD=self._r_dly_sel.storage[i] & self._r_wdly_dqs_rst.re, - i_CE=self._r_dly_sel.storage[i] & self._r_wdly_dqs_inc.re, - i_LDPIPEEN=0, i_INC=1, - - o_ODATAIN=dqs_nodelay, o_DATAOUT=dqs_delayed - ), - Instance("OBUFTDS", - i_I=dqs_delayed, i_T=dqs_t, - o_O=pads.dqs_p[i], o_OB=pads.dqs_n[i] - ) - ] - - # DQ - oe_dq = Signal() - for i in range(d): - dq_o_nodelay = Signal() - dq_o_delayed = Signal() - dq_i_nodelay = Signal() - dq_i_delayed = Signal() - dq_t = Signal() - self.specials += [ - Instance("OSERDESE2", - p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1, - p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", - p_SERDES_MODE="MASTER", - - o_OQ=dq_o_nodelay, o_TQ=dq_t, - i_OCE=1, i_TCE=1, - i_RST=ResetSignal(), - i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal(), - i_D1=self.dfi.phases[0].wrdata[i], i_D2=self.dfi.phases[0].wrdata[d+i], - i_D3=self.dfi.phases[1].wrdata[i], i_D4=self.dfi.phases[1].wrdata[d+i], - i_D5=self.dfi.phases[2].wrdata[i], i_D6=self.dfi.phases[2].wrdata[d+i], - i_D7=self.dfi.phases[3].wrdata[i], i_D8=self.dfi.phases[3].wrdata[d+i], - i_T1=~oe_dq - ), - Instance("ISERDESE2", - p_DATA_WIDTH=8, p_DATA_RATE="DDR", - p_SERDES_MODE="MASTER", p_INTERFACE_TYPE="NETWORKING", - p_NUM_CE=1, p_IOBDELAY="IFD", - - i_DDLY=dq_i_delayed, - i_CE1=1, - i_RST=ResetSignal() | (self._r_dly_sel.storage[i//8] & self._r_wdly_dq_rst.re), - i_CLK=ClockSignal("sys4x"), i_CLKB=~ClockSignal("sys4x"), i_CLKDIV=ClockSignal(), - i_BITSLIP=self._r_dly_sel.storage[i//8] & self._r_rdly_dq_bitslip.re, - o_Q8=self.dfi.phases[0].rddata[i], o_Q7=self.dfi.phases[0].rddata[d+i], - o_Q6=self.dfi.phases[1].rddata[i], o_Q5=self.dfi.phases[1].rddata[d+i], - o_Q4=self.dfi.phases[2].rddata[i], o_Q3=self.dfi.phases[2].rddata[d+i], - o_Q2=self.dfi.phases[3].rddata[i], o_Q1=self.dfi.phases[3].rddata[d+i] - ), - Instance("ODELAYE2", - p_DELAY_SRC="ODATAIN", p_SIGNAL_PATTERN="DATA", - p_CINVCTRL_SEL="FALSE", p_HIGH_PERFORMANCE_MODE="TRUE", p_REFCLK_FREQUENCY=200.0, - p_PIPE_SEL="FALSE", p_ODELAY_TYPE="VARIABLE", p_ODELAY_VALUE=0, - - i_C=ClockSignal(), - i_LD=self._r_dly_sel.storage[i//8] & self._r_wdly_dq_rst.re, - i_CE=self._r_dly_sel.storage[i//8] & self._r_wdly_dq_inc.re, - i_LDPIPEEN=0, i_INC=1, - - o_ODATAIN=dq_o_nodelay, o_DATAOUT=dq_o_delayed - ), - Instance("IDELAYE2", - p_DELAY_SRC="IDATAIN", p_SIGNAL_PATTERN="DATA", - p_CINVCTRL_SEL="FALSE", p_HIGH_PERFORMANCE_MODE="TRUE", p_REFCLK_FREQUENCY=200.0, - p_PIPE_SEL="FALSE", p_IDELAY_TYPE="VARIABLE", p_IDELAY_VALUE=6, - - i_C=ClockSignal(), - i_LD=self._r_dly_sel.storage[i//8] & self._r_rdly_dq_rst.re, - i_CE=self._r_dly_sel.storage[i//8] & self._r_rdly_dq_inc.re, - i_LDPIPEEN=0, i_INC=1, - - i_IDATAIN=dq_i_nodelay, o_DATAOUT=dq_i_delayed - ), - Instance("IOBUF", - i_I=dq_o_delayed, o_O=dq_i_nodelay, i_T=dq_t, - io_IO=pads.dq[i] - ) - ] - - # Flow control - # - # total read latency = 6: - # 2 cycles through OSERDESE2 - # 2 cycles CAS - # 2 cycles through ISERDESE2 - rddata_en = self.dfi.phases[self.phy_settings.rdphase].rddata_en - for i in range(5): - n_rddata_en = Signal() - self.sync += n_rddata_en.eq(rddata_en) - rddata_en = n_rddata_en - self.sync += [phase.rddata_valid.eq(rddata_en | self._r_wlevel_en.storage) - for phase in self.dfi.phases] - - oe = Signal() - last_wrdata_en = Signal(4) - wrphase = self.dfi.phases[self.phy_settings.wrphase] - self.sync += last_wrdata_en.eq(Cat(wrphase.wrdata_en, last_wrdata_en[:3])) - self.comb += oe.eq(last_wrdata_en[1] | last_wrdata_en[2] | last_wrdata_en[3]) - self.sync += \ - If(self._r_wlevel_en.storage, - oe_dqs.eq(1), oe_dq.eq(0) - ).Else( - oe_dqs.eq(oe), oe_dq.eq(oe) - ) diff --git a/misoclib/sdramphy/s6ddrphy.py b/misoclib/sdramphy/s6ddrphy.py deleted file mode 100644 index 9c5c9258..00000000 --- a/misoclib/sdramphy/s6ddrphy.py +++ /dev/null @@ -1,358 +0,0 @@ -# 1:2 frequency-ratio DDR / LPDDR / DDR2 PHY for Spartan-6 -# -# Assert dfi_wrdata_en and present the data -# on dfi_wrdata_mask/dfi_wrdata in the same -# cycle as the write command. -# -# Assert dfi_rddata_en in the same cycle as the read -# command. The data will come back on dfi_rddata -# 5 cycles later, along with the assertion -# of dfi_rddata_valid. -# -# This PHY only supports CAS Latency 3. -# Read commands must be sent on phase 0. -# Write commands must be sent on phase 1. -# - -from migen.fhdl.std import * -from migen.bus.dfi import * -from migen.genlib.record import * - -from misoclib import lasmicon - -class S6DDRPHY(Module): - def __init__(self, pads, memtype, rd_bitslip, wr_bitslip, dqs_ddr_alignment): - if memtype not in ["DDR", "LPDDR", "DDR2"]: - raise NotImplementedError("S6DDRPHY only supports DDR, LPDDR and DDR2") - a = flen(pads.a) - ba = flen(pads.ba) - d = flen(pads.dq) - nphases = 2 - - self.phy_settings = lasmicon.PhySettings( - memtype=memtype, - dfi_d=2*d, - nphases=nphases, - rdphase=0, - wrphase=1, - rdcmdphase=1, - wrcmdphase=0, - cl=3, - read_latency=5, - write_latency=0 - ) - - self.dfi = Interface(a, ba, self.phy_settings.dfi_d, nphases) - self.clk4x_wr_strb = Signal() - self.clk4x_rd_strb = Signal() - - ### - - # sys_clk : system clk, used for dfi interface - # sdram_half_clk : half rate sdram clk - # sdram_full_wr_clk : full rate sdram write clk - # sdram_full_rd_clk : full rate sdram read clk - sd_sys = getattr(self.sync, "sys") - sd_sdram_half = getattr(self.sync, "sdram_half") - - sys_clk = ClockSignal("sys") - sdram_half_clk = ClockSignal("sdram_half") - sdram_full_wr_clk = ClockSignal("sdram_full_wr") - sdram_full_rd_clk = ClockSignal("sdram_full_rd") - - # - # Command/address - # - - # select active phase - # sys_clk ----____----____ - # phase_sel(nphases=2) 0 1 0 1 Half Rate - phase_sel = Signal(log2_int(nphases)) - sys_clk_d = Signal() - - sd_sdram_half += [ - If(sys_clk & ~sys_clk_d, - phase_sel.eq(0) - ).Else( - phase_sel.eq(phase_sel+1) - ), - sys_clk_d.eq(sys_clk) - ] - - # register dfi cmds on half_rate clk - r_dfi = Array(Record(phase_cmd_description(a, ba)) for i in range(nphases)) - for n, phase in enumerate(self.dfi.phases): - sd_sdram_half +=[ - r_dfi[n].address.eq(phase.address), - r_dfi[n].bank.eq(phase.bank), - r_dfi[n].cs_n.eq(phase.cs_n), - r_dfi[n].cke.eq(phase.cke), - r_dfi[n].cas_n.eq(phase.cas_n), - r_dfi[n].ras_n.eq(phase.ras_n), - r_dfi[n].we_n.eq(phase.we_n) - ] - - # output cmds - sd_sdram_half += [ - pads.a.eq(r_dfi[phase_sel].address), - pads.ba.eq(r_dfi[phase_sel].bank), - pads.cke.eq(r_dfi[phase_sel].cke), - pads.ras_n.eq(r_dfi[phase_sel].ras_n), - pads.cas_n.eq(r_dfi[phase_sel].cas_n), - pads.we_n.eq(r_dfi[phase_sel].we_n) - ] - if hasattr(pads, "cs_n"): - sd_sdram_half += pads.cs_n.eq(r_dfi[phase_sel].cs_n) - - # - # Bitslip - # - bitslip_cnt = Signal(4) - bitslip_inc = Signal() - - sd_sys += [ - If(bitslip_cnt == rd_bitslip, - bitslip_inc.eq(0) - ).Else( - bitslip_cnt.eq(bitslip_cnt+1), - bitslip_inc.eq(1) - ) - ] - - # - # DQ/DQS/DM data - # - sdram_half_clk_n = Signal() - self.comb += sdram_half_clk_n.eq(~sdram_half_clk) - - postamble = Signal() - drive_dqs = Signal() - dqs_t_d0 = Signal() - dqs_t_d1 = Signal() - - dqs_o = Signal(d//8) - dqs_t = Signal(d//8) - - self.comb += [ - dqs_t_d0.eq(~(drive_dqs | postamble)), - dqs_t_d1.eq(~drive_dqs), - ] - - for i in range(d//8): - # DQS output - self.specials += Instance("ODDR2", - p_DDR_ALIGNMENT=dqs_ddr_alignment, - p_INIT=0, - p_SRTYPE="ASYNC", - - i_C0=sdram_half_clk, - i_C1=sdram_half_clk_n, - - i_CE=1, - i_D0=0, - i_D1=1, - i_R=0, - i_S=0, - - o_Q=dqs_o[i] - ) - - # DQS tristate cmd - self.specials += Instance("ODDR2", - p_DDR_ALIGNMENT=dqs_ddr_alignment, - p_INIT=0, - p_SRTYPE="ASYNC", - - i_C0=sdram_half_clk, - i_C1=sdram_half_clk_n, - - i_CE=1, - i_D0=dqs_t_d0, - i_D1=dqs_t_d1, - i_R=0, - i_S=0, - - o_Q=dqs_t[i] - ) - - # DQS tristate buffer - if hasattr(pads, "dqs_n"): - self.specials += Instance("OBUFTDS", - i_I=dqs_o[i], - i_T=dqs_t[i], - - o_O=pads.dqs[i], - o_OB=pads.dqs_n[i], - ) - else: - self.specials += Instance("OBUFT", - i_I=dqs_o[i], - i_T=dqs_t[i], - - o_O=pads.dqs[i] - ) - - sd_sdram_half += postamble.eq(drive_dqs) - - d_dfi = [Record(phase_wrdata_description(nphases*d)+phase_rddata_description(nphases*d)) - for i in range(2*nphases)] - - for n, phase in enumerate(self.dfi.phases): - self.comb += [ - d_dfi[n].wrdata.eq(phase.wrdata), - d_dfi[n].wrdata_mask.eq(phase.wrdata_mask), - d_dfi[n].wrdata_en.eq(phase.wrdata_en), - d_dfi[n].rddata_en.eq(phase.rddata_en), - ] - sd_sys += [ - d_dfi[nphases+n].wrdata.eq(phase.wrdata), - d_dfi[nphases+n].wrdata_mask.eq(phase.wrdata_mask) - ] - - - drive_dq = Signal() - drive_dq_n = [Signal() for i in range(2)] - self.comb += drive_dq_n[0].eq(~drive_dq) - sd_sys += drive_dq_n[1].eq(drive_dq_n[0]) - - dq_t = Signal(d) - dq_o = Signal(d) - dq_i = Signal(d) - - dq_wrdata = [] - for i in range(2): - for j in reversed(range(nphases)): - dq_wrdata.append(d_dfi[i*nphases+j].wrdata[:d]) - dq_wrdata.append(d_dfi[i*nphases+j].wrdata[d:]) - - for i in range(d): - # Data serializer - self.specials += Instance("OSERDES2", - p_DATA_WIDTH=4, - p_DATA_RATE_OQ="SDR", - p_DATA_RATE_OT="SDR", - p_SERDES_MODE="NONE", - p_OUTPUT_MODE="SINGLE_ENDED", - - o_OQ=dq_o[i], - i_OCE=1, - i_CLK0=sdram_full_wr_clk, - i_CLK1=0, - i_IOCE=self.clk4x_wr_strb, - i_RST=0, - i_CLKDIV=sys_clk, - - i_D1=dq_wrdata[wr_bitslip+3][i], - i_D2=dq_wrdata[wr_bitslip+2][i], - i_D3=dq_wrdata[wr_bitslip+1][i], - i_D4=dq_wrdata[wr_bitslip+0][i], - - o_TQ=dq_t[i], - i_T1=drive_dq_n[(wr_bitslip+3)//4], - i_T2=drive_dq_n[(wr_bitslip+2)//4], - i_T3=drive_dq_n[(wr_bitslip+1)//4], - i_T4=drive_dq_n[(wr_bitslip+0)//4], - i_TRAIN=0, - i_TCE=1, - i_SHIFTIN1=0, - i_SHIFTIN2=0, - i_SHIFTIN3=0, - i_SHIFTIN4=0, - ) - - # Data deserializer - self.specials += Instance("ISERDES2", - p_DATA_WIDTH=4, - p_DATA_RATE="SDR", - p_BITSLIP_ENABLE="TRUE", - p_SERDES_MODE="NONE", - p_INTERFACE_TYPE="RETIMED", - - i_D=dq_i[i], - i_CE0=1, - i_CLK0=sdram_full_rd_clk, - i_CLK1=0, - i_IOCE=self.clk4x_rd_strb, - i_RST=ResetSignal(), - i_CLKDIV=sys_clk, - i_BITSLIP=bitslip_inc, - - o_Q1=d_dfi[0*nphases+0].rddata[i+d], - o_Q2=d_dfi[0*nphases+0].rddata[i], - o_Q3=d_dfi[0*nphases+1].rddata[i+d], - o_Q4=d_dfi[0*nphases+1].rddata[i], - ) - - # Data buffer - self.specials += Instance("IOBUF", - i_I=dq_o[i], - o_O=dq_i[i], - i_T=dq_t[i], - io_IO=pads.dq[i] - ) - - dq_wrdata_mask = [] - for i in range(2): - for j in reversed(range(nphases)): - dq_wrdata_mask.append(d_dfi[i*nphases+j].wrdata_mask[:d//8]) - dq_wrdata_mask.append(d_dfi[i*nphases+j].wrdata_mask[d//8:]) - - for i in range(d//8): - # Mask serializer - self.specials += Instance("OSERDES2", - p_DATA_WIDTH=4, - p_DATA_RATE_OQ="SDR", - p_DATA_RATE_OT="SDR", - p_SERDES_MODE="NONE", - p_OUTPUT_MODE="SINGLE_ENDED", - - o_OQ=pads.dm[i], - i_OCE=1, - i_CLK0=sdram_full_wr_clk, - i_CLK1=0, - i_IOCE=self.clk4x_wr_strb, - i_RST=0, - i_CLKDIV=sys_clk, - - i_D1=dq_wrdata_mask[wr_bitslip+3][i], - i_D2=dq_wrdata_mask[wr_bitslip+2][i], - i_D3=dq_wrdata_mask[wr_bitslip+1][i], - i_D4=dq_wrdata_mask[wr_bitslip+0][i], - - i_TRAIN=0, - i_TCE=0, - i_SHIFTIN1=0, - i_SHIFTIN2=0, - i_SHIFTIN3=0, - i_SHIFTIN4=0, - ) - - # - # ODT - # - # ODT not yet supported - if hasattr(pads, "odt"): - self.comb += pads.odt.eq(0) - - # - # DQ/DQS/DM control - # - self.comb += drive_dq.eq(d_dfi[self.phy_settings.wrphase].wrdata_en) - - d_dfi_wrdata_en = Signal() - sd_sys += d_dfi_wrdata_en.eq(d_dfi[self.phy_settings.wrphase].wrdata_en) - - r_dfi_wrdata_en = Signal(2) - sd_sdram_half += r_dfi_wrdata_en.eq(Cat(d_dfi_wrdata_en, r_dfi_wrdata_en[0])) - - self.comb += drive_dqs.eq(r_dfi_wrdata_en[1]) - - rddata_sr = Signal(self.phy_settings.read_latency) - sd_sys += rddata_sr.eq(Cat(rddata_sr[1:self.phy_settings.read_latency], - d_dfi[self.phy_settings.rdphase].rddata_en)) - - for n, phase in enumerate(self.dfi.phases): - self.comb += [ - phase.rddata.eq(d_dfi[n].rddata), - phase.rddata_valid.eq(rddata_sr[0]), - ] diff --git a/targets/kc705.py b/targets/kc705.py index cd0df769..2bab721e 100644 --- a/targets/kc705.py +++ b/targets/kc705.py @@ -1,8 +1,8 @@ from migen.fhdl.std import * from migen.genlib.resetsync import AsyncResetSynchronizer -from misoclib import lasmicon, spiflash, ethmac -from misoclib.sdramphy import k7ddrphy +from misoclib import sdram, spiflash, ethmac +from misoclib.sdram.phy import k7ddrphy from misoclib.gensoc import SDRAMSoC from misoclib.ethmac.phy import gmii @@ -75,12 +75,12 @@ class BaseSoC(SDRAMSoC): self.submodules.crg = _CRG(platform) - sdram_geom = lasmicon.GeomSettings( + sdram_geom = sdram.GeomSettings( bank_a=3, row_a=16, col_a=10 ) - sdram_timing = lasmicon.TimingSettings( + sdram_timing = sdram.TimingSettings( tRP=self.ns(15), tRCD=self.ns(15), tWR=self.ns(15), diff --git a/targets/mlabs_video.py b/targets/mlabs_video.py index 869f232c..fcc219a2 100644 --- a/targets/mlabs_video.py +++ b/targets/mlabs_video.py @@ -4,8 +4,8 @@ from fractions import Fraction from migen.fhdl.std import * from mibuild.generic_platform import ConstraintError -from misoclib import lasmicon, mxcrg, norflash16, ethmac, framebuffer, gpio -from misoclib.sdramphy import s6ddrphy +from misoclib import sdram, mxcrg, norflash16, minimac3, framebuffer, gpio +from misoclib.sdram.phy import s6ddrphy from misoclib.gensoc import SDRAMSoC from misoclib.ethmac.phy import mii @@ -31,12 +31,12 @@ class BaseSoC(SDRAMSoC): cpu_reset_address=0x00180000, **kwargs) - sdram_geom = lasmicon.GeomSettings( + sdram_geom = sdram.GeomSettings( bank_a=2, row_a=13, col_a=10 ) - sdram_timing = lasmicon.TimingSettings( + sdram_timing = sdram.TimingSettings( tRP=self.ns(15), tRCD=self.ns(15), tWR=self.ns(15), diff --git a/targets/ppro.py b/targets/ppro.py index 05bfea3b..34b1a638 100644 --- a/targets/ppro.py +++ b/targets/ppro.py @@ -3,8 +3,8 @@ from fractions import Fraction from migen.fhdl.std import * from migen.genlib.resetsync import AsyncResetSynchronizer -from misoclib import lasmicon, spiflash -from misoclib.sdramphy import gensdrphy +from misoclib import spiflash, sdram +from misoclib.sdram.phy import gensdrphy from misoclib.gensoc import SDRAMSoC class _CRG(Module): @@ -67,12 +67,12 @@ class BaseSoC(SDRAMSoC): self.submodules.crg = _CRG(platform, clk_freq) - sdram_geom = lasmicon.GeomSettings( + sdram_geom = sdram.GeomSettings( bank_a=2, row_a=12, col_a=8 ) - sdram_timing = lasmicon.TimingSettings( + sdram_timing = sdram.TimingSettings( tRP=self.ns(15), tRCD=self.ns(15), tWR=self.ns(14),