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
+++ /dev/null
-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")]
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
+++ /dev/null
-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()
+++ /dev/null
-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)
+++ /dev/null
-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)
+++ /dev/null
-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)
+++ /dev/null
-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)
+++ /dev/null
-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)
- )
- ]
+++ /dev/null
-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"))
- )
+++ /dev/null
-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")
+++ /dev/null
-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
+++ /dev/null
-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")
+++ /dev/null
-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")
+++ /dev/null
-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")
+++ /dev/null
-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)
--- /dev/null
+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")
--- /dev/null
+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")]
--- /dev/null
+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()
--- /dev/null
+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)
--- /dev/null
+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)
--- /dev/null
+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)
+ )
+ ]
--- /dev/null
+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"))
+ )
--- /dev/null
+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")
--- /dev/null
+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
--- /dev/null
+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")
--- /dev/null
+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")
--- /dev/null
+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")
--- /dev/null
+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)
--- /dev/null
+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)
--- /dev/null
+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)
--- /dev/null
+#
+# 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]))
--- /dev/null
+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 <hw/common.h>\n#include <generated/csr.h>\n#include <hw/flags.h>\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
--- /dev/null
+# 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)
+ )
--- /dev/null
+# 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]),
+ ]
+++ /dev/null
-#
-# 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]))
+++ /dev/null
-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 <hw/common.h>\n#include <generated/csr.h>\n#include <hw/flags.h>\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
+++ /dev/null
-# 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)
- )
+++ /dev/null
-# 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]),
- ]
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
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),
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
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),
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):
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),