This commit is contained in:
Florent Kermarrec 2016-04-29 07:21:42 +02:00
parent 42767286ca
commit 66362b1280
27 changed files with 23 additions and 2666 deletions

View File

@ -5,11 +5,12 @@ import argparse
from litex.gen import *
from litex.boards.platforms import de0nano
from litex.soc.cores.sdram.settings import IS42S16160
from litex.soc.cores.sdram.phy import GENSDRPHY
from litex.soc.integration.soc_sdram import *
from litex.soc.integration.builder import *
from litedram.settings import IS42S16160
from litedram.phy import GENSDRPHY
class _PLL(Module):
def __init__(self, period_in, name, phase_shift, operation_mode):

View File

@ -6,13 +6,14 @@ from litex.gen import *
from litex.gen.genlib.resetsync import AsyncResetSynchronizer
from litex.boards.platforms import kc705
from litex.soc.cores.sdram.settings import MT8JTF12864
from litex.soc.cores.sdram.phy import k7ddrphy
from litex.soc.cores.flash import spi_flash
from litex.soc.integration.soc_core import mem_decoder
from litex.soc.integration.soc_sdram import *
from litex.soc.integration.builder import *
from litedram.settings import MT8JTF12864
from litedram.phy import k7ddrphy
from liteeth.phy import LiteEthPHY
from liteeth.core.mac import LiteEthMAC

View File

@ -7,11 +7,12 @@ from litex.gen import *
from litex.gen.genlib.resetsync import AsyncResetSynchronizer
from litex.boards.platforms import minispartan6
from litex.soc.cores.sdram.settings import AS4C16M16
from litex.soc.cores.sdram.phy import GENSDRPHY
from litex.soc.integration.soc_sdram import *
from litex.soc.integration.builder import *
from litedram.settings import AS4C16M16
from litedram.phy import GENSDRPHY
class _CRG(Module):
def __init__(self, platform, clk_freq):

View File

@ -10,10 +10,11 @@ from litex.gen.genlib.io import CRG
from litex.soc.integration.soc_sdram import *
from litex.soc.integration.builder import *
from litex.soc.cores import uart
from litex.soc.cores.sdram.settings import PhySettings, IS42S16160
from litex.soc.cores.sdram.model import SDRAMPHYModel
from litex.soc.integration.soc_core import mem_decoder
from litedram.settings import PhySettings, IS42S16160
from litedram.model import SDRAMPHYModel
from liteeth.phy.model import LiteEthPHYModel
from liteeth.core.mac import LiteEthMAC

View File

@ -1,60 +0,0 @@
from litex.gen import *
from litex.soc.interconnect import dfi
from litex.soc.interconnect.csr 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(len(phase.address))
self._baddress = CSRStorage(len(phase.bank))
self._wrdata = CSRStorage(len(phase.wrdata))
self._rddata = CSRStatus(len(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, addressbits, bankbits, databits, nphases=1):
inti = dfi.Interface(addressbits, bankbits, databits, nphases)
self.slave = dfi.Interface(addressbits, bankbits, databits, nphases)
self.master = dfi.Interface(addressbits, bankbits, databits, 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")]

View File

@ -1 +0,0 @@
from litex.soc.cores.sdram.lasmicon.core import ControllerSettings, LASMIcon

View File

@ -1,153 +0,0 @@
from litex.gen import *
from litex.gen.genlib.roundrobin import *
from litex.gen.genlib.fsm import FSM, NextState
from litex.gen.genlib.fifo import SyncFIFO
from litex.soc.cores.sdram.lasmicon.multiplexer import *
class _AddressSlicer:
def __init__(self, colbits, address_align):
self.colbits = colbits
self.address_align = address_align
def row(self, address):
split = self.colbits - self.address_align
if isinstance(address, int):
return address >> split
else:
return address[split:]
def col(self, address):
split = self.colbits - self.address_align
if isinstance(address, int):
return (address & (2**split - 1)) << self.address_align
else:
return Cat(Replicate(0, self.address_align), address[:split])
class BankMachine(Module):
def __init__(self, geom_settings, timing_settings, controller_settings, address_align, bankn, req):
self.refresh_req = Signal()
self.refresh_gnt = Signal()
self.cmd = CommandRequestRW(geom_settings.addressbits, geom_settings.bankbits)
###
# Request FIFO
layout = [("we", 1), ("adr", len(req.adr))]
req_in = Record(layout)
reqf = Record(layout)
self.submodules.req_fifo = SyncFIFO(layout_len(layout),
controller_settings.req_queue_size)
self.comb += [
self.req_fifo.din.eq(req_in.raw_bits()),
reqf.raw_bits().eq(self.req_fifo.dout)
]
self.comb += [
req_in.we.eq(req.we),
req_in.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_w_ack | req.dat_r_ack),
req.lock.eq(self.req_fifo.readable)
]
slicer = _AddressSlicer(geom_settings.colbits, address_align)
# Row tracking
has_openrow = Signal()
openrow = Signal(geom_settings.rowbits)
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_w_ack.eq(self.cmd.ack & reqf.we),
req.dat_r_ack.eq(self.cmd.ack & ~reqf.we),
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)

View File

@ -1,53 +0,0 @@
from litex.gen import *
from litex.soc.interconnect import dfi, lasmi_bus
from litex.soc.cores.sdram.lasmicon.refresher import *
from litex.soc.cores.sdram.lasmicon.bankmachine import *
from litex.soc.cores.sdram.lasmicon.multiplexer import *
class ControllerSettings:
def __init__(self, req_queue_size=8, read_time=32, write_time=16, with_bandwidth=False):
self.req_queue_size = req_queue_size
self.read_time = read_time
self.write_time = write_time
self.with_bandwidth = with_bandwidth
class LASMIcon(Module):
def __init__(self, phy_settings, geom_settings, timing_settings,
controller_settings=None):
if controller_settings is None:
controller_settings = ControllerSettings()
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.addressbits,
geom_settings.bankbits,
phy_settings.dfi_databits,
phy_settings.nphases)
self.lasmic = lasmi_bus.Interface(
aw=geom_settings.rowbits + geom_settings.colbits - address_align,
dw=phy_settings.dfi_databits*phy_settings.nphases,
nbanks=2**geom_settings.bankbits,
req_queue_size=controller_settings.req_queue_size,
read_latency=phy_settings.read_latency+1,
write_latency=phy_settings.write_latency+1)
self.nrowbits = geom_settings.colbits - address_align
###
self.submodules.refresher = Refresher(geom_settings.addressbits, geom_settings.bankbits,
timing_settings.tRP, timing_settings.tREFI, timing_settings.tRFC)
self.submodules.bank_machines = [BankMachine(geom_settings, timing_settings, controller_settings, address_align, i,
getattr(self.lasmic, "bank"+str(i)))
for i in range(2**geom_settings.bankbits)]
self.submodules.multiplexer = Multiplexer(phy_settings, geom_settings, timing_settings, controller_settings,
self.bank_machines, self.refresher,
self.dfi, self.lasmic)
def get_csrs(self):
return self.multiplexer.get_csrs()

View File

@ -1,222 +0,0 @@
from functools import reduce
from operator import or_, and_
from litex.gen import *
from litex.gen.genlib.roundrobin import *
from litex.gen.genlib.fsm import FSM, NextState
from litex.soc.cores.sdram.lasmicon.perf import Bandwidth
from litex.soc.interconnect.csr import AutoCSR
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(len(requests[0].a), len(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, controller_settings, bank_machines, refresher, dfi, lasmic,
with_bandwidth=False):
assert(phy_settings.nphases == len(dfi.phases))
self.phy_settings = phy_settings
# Command choosing
requests = [bm.cmd for bm in bank_machines]
self.submodules.choose_cmd = choose_cmd = _CommandChooser(requests)
self.submodules.choose_req = 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)
]
# Command steering
nop = CommandRequest(geom_settings.addressbits, geom_settings.bankbits)
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(reduce(or_, [req.stb & req.is_read for req in requests])),
write_available.eq(reduce(or_, [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(controller_settings.read_time)
write_time_en, max_write_time = anti_starvation(controller_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(reduce(and_, [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),
refresher.ack.eq(1),
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)
if controller_settings.with_bandwidth:
data_width = phy_settings.dfi_databits*phy_settings.nphases
self.submodules.bandwidth = Bandwidth(self.choose_req.cmd, data_width)

View File

@ -1,47 +0,0 @@
from litex.gen import *
from litex.soc.interconnect.csr import *
class Bandwidth(Module, AutoCSR):
def __init__(self, cmd, data_width, period_bits=24):
self._update = CSR()
self._nreads = CSRStatus(period_bits)
self._nwrites = CSRStatus(period_bits)
self._data_width = CSRStatus(bits_for(data_width), reset=data_width)
###
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._update.re,
self._nreads.status.eq(nreads_r),
self._nwrites.status.eq(nwrites_r)
)
]

View File

@ -1,69 +0,0 @@
from litex.gen import *
from litex.gen.genlib.misc import timeline
from litex.gen.genlib.fsm import FSM
from litex.soc.cores.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"))
)

View File

@ -1 +0,0 @@
from litex.soc.cores.sdram.minicon.core import Minicon

View File

@ -1,235 +0,0 @@
from functools import reduce
from operator import or_
from litex.gen import *
from litex.gen.genlib.fsm import FSM, NextState
from litex.gen.genlib.misc import WaitTimer
from litex.soc.interconnect import dfi as dfibus
from litex.soc.interconnect import wishbone
class _AddressSlicer:
def __init__(self, colbits, bankbits, rowbits, address_align):
self.colbits = colbits
self.bankbits = bankbits
self.rowbits = rowbits
self.address_align = address_align
self.addressbits = colbits - address_align + bankbits + rowbits
def row(self, address):
split = self.bankbits + self.colbits - self.address_align
if isinstance(address, int):
return address >> split
else:
return address[split:self.addressbits]
def bank(self, address):
split = self.colbits - self.address_align
if isinstance(address, int):
return (address & (2**(split + self.bankbits) - 1)) >> split
else:
return address[split:split+self.bankbits]
def col(self, address):
split = self.colbits - self.address_align
if isinstance(address, int):
return (address & (2**split - 1)) << self.address_align
else:
return Cat(Replicate(0, self.address_align), address[:split])
@ResetInserter()
@CEInserter()
class _Bank(Module):
def __init__(self, geom_settings):
self.open = Signal()
self.row = Signal(geom_settings.rowbits)
self.idle = Signal(reset=1)
self.hit = Signal()
# # #
row = Signal(geom_settings.rowbits)
self.sync += \
If(self.open,
self.idle.eq(0),
row.eq(self.row)
)
self.comb += self.hit.eq(~self.idle & (self.row == row))
class 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
burst_width = phy_settings.dfi_databits*phy_settings.nphases
address_align = log2_int(burst_length)
# # #
self.dfi = dfi = dfibus.Interface(geom_settings.addressbits,
geom_settings.bankbits,
phy_settings.dfi_databits,
phy_settings.nphases)
self.bus = bus = wishbone.Interface(burst_width)
rdphase = phy_settings.rdphase
wrphase = phy_settings.wrphase
precharge_all = Signal()
activate = Signal()
refresh = Signal()
write = Signal()
read = Signal()
# Compute current column, bank and row from wishbone address
slicer = _AddressSlicer(geom_settings.colbits,
geom_settings.bankbits,
geom_settings.rowbits,
address_align)
# Manage banks
bank_idle = Signal()
bank_hit = Signal()
banks = []
for i in range(2**geom_settings.bankbits):
bank = _Bank(geom_settings)
self.comb += [
bank.open.eq(activate),
bank.reset.eq(precharge_all),
bank.row.eq(slicer.row(bus.adr))
]
banks.append(bank)
self.submodules += banks
cases = {}
for i, bank in enumerate(banks):
cases[i] = [bank.ce.eq(1)]
self.comb += Case(slicer.bank(bus.adr), cases)
self.comb += [
bank_hit.eq(reduce(or_, [bank.hit & bank.ce for bank in banks])),
bank_idle.eq(reduce(or_, [bank.idle & bank.ce for bank in banks])),
]
# Timings
write2precharge_timer = WaitTimer(2 + timing_settings.tWR - 1)
self.submodules += write2precharge_timer
self.comb += write2precharge_timer.wait.eq(~write)
refresh_timer = WaitTimer(timing_settings.tREFI)
self.submodules += refresh_timer
self.comb += refresh_timer.wait.eq(~refresh)
# Main FSM
self.submodules.fsm = fsm = FSM()
fsm.act("IDLE",
If(refresh_timer.done,
NextState("PRECHARGE-ALL")
).Elif(bus.stb & bus.cyc,
If(bank_hit,
If(bus.we,
NextState("WRITE")
).Else(
NextState("READ")
)
).Elif(~bank_idle,
If(write2precharge_timer.done,
NextState("PRECHARGE")
)
).Else(
NextState("ACTIVATE")
)
)
)
fsm.act("READ",
read.eq(1),
dfi.phases[rdphase].ras_n.eq(1),
dfi.phases[rdphase].cas_n.eq(0),
dfi.phases[rdphase].we_n.eq(1),
dfi.phases[rdphase].rddata_en.eq(1),
NextState("WAIT-READ-DONE"),
)
fsm.act("WAIT-READ-DONE",
If(dfi.phases[rdphase].rddata_valid,
bus.ack.eq(1),
NextState("IDLE")
)
)
fsm.act("WRITE",
write.eq(1),
dfi.phases[wrphase].ras_n.eq(1),
dfi.phases[wrphase].cas_n.eq(0),
dfi.phases[wrphase].we_n.eq(0),
dfi.phases[wrphase].wrdata_en.eq(1),
NextState("WRITE-LATENCY")
)
fsm.act("WRITE-ACK",
bus.ack.eq(1),
NextState("IDLE")
)
fsm.act("PRECHARGE-ALL",
precharge_all.eq(1),
dfi.phases[rdphase].ras_n.eq(0),
dfi.phases[rdphase].cas_n.eq(1),
dfi.phases[rdphase].we_n.eq(0),
NextState("PRE-REFRESH")
)
fsm.act("PRECHARGE",
# do no reset bank since we are going to re-open it
dfi.phases[0].ras_n.eq(0),
dfi.phases[0].cas_n.eq(1),
dfi.phases[0].we_n.eq(0),
NextState("TRP")
)
fsm.act("ACTIVATE",
activate.eq(1),
dfi.phases[0].ras_n.eq(0),
dfi.phases[0].cas_n.eq(1),
dfi.phases[0].we_n.eq(1),
NextState("TRCD"),
)
fsm.act("REFRESH",
refresh.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("WRITE-LATENCY", "WRITE-ACK", phy_settings.write_latency-1)
fsm.delayed_enter("TRP", "ACTIVATE", timing_settings.tRP-1)
fsm.delayed_enter("TRCD", "IDLE", timing_settings.tRCD-1)
fsm.delayed_enter("PRE-REFRESH", "REFRESH", timing_settings.tRP-1)
fsm.delayed_enter("POST-REFRESH", "IDLE", timing_settings.tRFC-1)
# DFI commands
for phase in dfi.phases:
if hasattr(phase, "reset_n"):
self.comb += phase.reset_n.eq(1)
if hasattr(phase, "odt"):
self.comb += phase.odt.eq(1)
self.comb += [
phase.cke.eq(1),
phase.cs_n.eq(0),
phase.bank.eq(slicer.bank(bus.adr)),
If(precharge_all,
phase.address.eq(2**10)
).Elif(activate,
phase.address.eq(slicer.row(bus.adr))
).Elif(write | read,
phase.address.eq(slicer.col(bus.adr))
)
]
# DFI datapath
self.comb += [
bus.dat_r.eq(Cat(phase.rddata for phase in dfi.phases)),
Cat(phase.wrdata for phase in dfi.phases).eq(bus.dat_w),
Cat(phase.wrdata_mask for phase in dfi.phases).eq(~bus.sel),
]

View File

@ -1,197 +0,0 @@
# This file is Copyright (c) 2015 Florent Kermarrec <florent@enjoy-digital.fr>
# License: BSD
# SDRAM simulation PHY at DFI level
# tested with SDR/DDR/DDR2/LPDDR/DDR3
# TODO:
# - add $display support to LiteX gen and manage timing violations?
from litex.gen import *
from litex.gen.fhdl.specials import *
from litex.soc.interconnect.dfi import *
from functools import reduce
from operator import or_
class Bank(Module):
def __init__(self, data_width, nrows, ncols, burst_length):
self.activate = Signal()
self.activate_row = Signal(max=nrows)
self.precharge = Signal()
self.write = Signal()
self.write_col = Signal(max=ncols)
self.write_data = Signal(data_width)
self.write_mask = Signal(data_width//8)
self.read = Signal()
self.read_col = Signal(max=ncols)
self.read_data = Signal(data_width)
# # #
active = Signal()
row = Signal(max=nrows)
self.sync += \
If(self.precharge,
active.eq(0),
).Elif(self.activate,
active.eq(1),
row.eq(self.activate_row)
)
self.specials.mem = mem = Memory(data_width, nrows*ncols//burst_length)
self.specials.write_port = write_port = mem.get_port(write_capable=True,
we_granularity=8)
self.specials.read_port = read_port = mem.get_port(async_read=True)
self.comb += [
If(active,
write_port.adr.eq(row*ncols | self.write_col),
write_port.dat_w.eq(self.write_data),
write_port.we.eq(Replicate(self.write, data_width//8) & ~self.write_mask),
If(self.read,
read_port.adr.eq(row*ncols | self.read_col),
self.read_data.eq(read_port.dat_r)
)
)
]
class DFIPhase(Module):
def __init__(self, dfi, n):
phase = getattr(dfi, "p"+str(n))
self.bank = phase.bank
self.address = phase.address
self.wrdata = phase.wrdata
self.wrdata_mask = phase.wrdata_mask
self.rddata = phase.rddata
self.rddata_valid = phase.rddata_valid
self.activate = Signal()
self.precharge = Signal()
self.write = Signal()
self.read = Signal()
# # #
self.comb += [
If(~phase.cs_n & ~phase.ras_n & phase.cas_n,
self.activate.eq(phase.we_n),
self.precharge.eq(~phase.we_n)
),
If(~phase.cs_n & phase.ras_n & ~phase.cas_n,
self.write.eq(~phase.we_n),
self.read.eq(phase.we_n)
)
]
class SDRAMPHYModel(Module):
def __init__(self, module, settings):
if settings.memtype in ["SDR"]:
burst_length = settings.nphases*1 # command multiplication*SDR
elif settings.memtype in ["DDR", "LPDDR", "DDR2", "DDR3"]:
burst_length = settings.nphases*2 # command multiplication*DDR
addressbits = module.geom_settings.addressbits
bankbits = module.geom_settings.bankbits
rowbits = module.geom_settings.rowbits
colbits = module.geom_settings.colbits
self.settings = settings
self.module = module
self.dfi = Interface(addressbits, bankbits, self.settings.dfi_databits, self.settings.nphases)
# # #
nbanks = 2**bankbits
nrows = 2**rowbits
ncols = 2**colbits
data_width = self.settings.dfi_databits*self.settings.nphases
# DFI phases
phases = [DFIPhase(self.dfi, n) for n in range(self.settings.nphases)]
self.submodules += phases
# banks
banks = [Bank(data_width, nrows, ncols, burst_length) for i in range(nbanks)]
self.submodules += banks
# connect DFI phases to banks (cmds, write datapath)
for nb, bank in enumerate(banks):
# bank activate
activates = Signal(len(phases))
cases = {}
for np, phase in enumerate(phases):
self.comb += activates[np].eq(phase.activate)
cases[2**np] = [
bank.activate.eq(phase.bank == nb),
bank.activate_row.eq(phase.address)
]
self.comb += Case(activates, cases)
# bank precharge
precharges = Signal(len(phases))
cases = {}
for np, phase in enumerate(phases):
self.comb += precharges[np].eq(phase.precharge)
cases[2**np] = [
bank.precharge.eq((phase.bank == nb) | phase.address[10])
]
self.comb += Case(precharges, cases)
# bank writes
writes = Signal(len(phases))
cases = {}
for np, phase in enumerate(phases):
self.comb += writes[np].eq(phase.write)
cases[2**np] = [
bank.write.eq(phase.bank == nb),
bank.write_col.eq(phase.address)
]
self.comb += Case(writes, cases)
self.comb += [
bank.write_data.eq(Cat(*[phase.wrdata for phase in phases])),
bank.write_mask.eq(Cat(*[phase.wrdata_mask for phase in phases]))
]
# bank reads
reads = Signal(len(phases))
cases = {}
for np, phase in enumerate(phases):
self.comb += reads[np].eq(phase.read)
cases[2**np] = [
bank.read.eq(phase.bank == nb),
bank.read_col.eq(phase.address)
]
self.comb += Case(reads, cases)
# connect banks to DFI phases (cmds, read datapath)
banks_read = Signal()
banks_read_data = Signal(data_width)
self.comb += [
banks_read.eq(reduce(or_, [bank.read for bank in banks])),
banks_read_data.eq(reduce(or_, [bank.read_data for bank in banks]))
]
# simulate read latency
for i in range(self.settings.read_latency):
new_banks_read = Signal()
new_banks_read_data = Signal(data_width)
self.sync += [
new_banks_read.eq(banks_read),
new_banks_read_data.eq(banks_read_data)
]
banks_read = new_banks_read
banks_read_data = new_banks_read_data
self.comb += [
Cat(*[phase.rddata_valid for phase in phases]).eq(banks_read),
Cat(*[phase.rddata for phase in phases]).eq(banks_read_data)
]

View File

@ -1,3 +0,0 @@
from litex.soc.cores.sdram.phy.gensdrphy import GENSDRPHY
from litex.soc.cores.sdram.phy.s6ddrphy import S6HalfRateDDRPHY, S6QuarterRateDDRPHY
from litex.soc.cores.sdram.phy.k7ddrphy import K7DDRPHY

View File

@ -1,95 +0,0 @@
#
# 1:1 frequency-ratio Generic SDR PHY
#
# The GENSDRPHY is validated on CycloneIV (Altera) but since it does
# not use vendor-dependent code, it can also be used on other architectures.
#
# The PHY needs 2 Clock domains:
# - sys_clk : The System Clock domain
# - sys_clk_ps : The System Clock domain with its phase shifted
# (-3ns on C4@100MHz)
#
# Assert dfi_wrdata_en and present the data
# on dfi_wrdata_mask/dfi_wrdata in the same
# cycle as the write command.
#
# Assert dfi_rddata_en in the same cycle as the read
# command. The data will come back on dfi_rddata
# 4 cycles later, along with the assertion of
# dfi_rddata_valid.
#
# This PHY only supports CAS Latency 2.
#
from litex.gen import *
from litex.gen.genlib.record import *
from litex.gen.fhdl.specials import Tristate
from litex.soc.interconnect.dfi import *
from litex.soc.cores.sdram import settings as sdram_settings
class GENSDRPHY(Module):
def __init__(self, pads):
addressbits = len(pads.a)
bankbits = len(pads.ba)
databits = len(pads.dq)
self.settings = sdram_settings.PhySettings(
memtype="SDR",
dfi_databits=databits,
nphases=1,
rdphase=0,
wrphase=0,
rdcmdphase=0,
wrcmdphase=0,
cl=2,
read_latency=4,
write_latency=0
)
self.dfi = Interface(addressbits, bankbits, databits)
###
#
# 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(databits)
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(databits)
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]))

View File

@ -1,292 +0,0 @@
# tCK=5ns CL=7 CWL=6
from litex.gen import *
from litex.soc.interconnect.dfi import *
from litex.soc.interconnect.csr import *
from litex.soc.cores.sdram import settings as sdram_settings
class K7DDRPHY(Module, AutoCSR):
def __init__(self, pads):
addressbits = len(pads.a)
bankbits = len(pads.ba)
databits = len(pads.dq)
nphases = 4
self._wlevel_en = CSRStorage()
self._wlevel_strobe = CSR()
self._dly_sel = CSRStorage(databits//8)
self._rdly_dq_rst = CSR()
self._rdly_dq_inc = CSR()
self._rdly_dq_bitslip = CSR()
self._wdly_dq_rst = CSR()
self._wdly_dq_inc = CSR()
self._wdly_dqs_rst = CSR()
self._wdly_dqs_inc = CSR()
self.settings = sdram_settings.PhySettings(
memtype="DDR3",
dfi_databits=2*databits,
nphases=nphases,
rdphase=0,
wrphase=2,
rdcmdphase=1,
wrcmdphase=0,
cl=7,
cwl=6,
read_latency=6,
write_latency=2
)
self.dfi = Interface(addressbits, bankbits, 2*databits, 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(addressbits):
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(bankbits):
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._wlevel_en.storage,
If(self._wlevel_strobe.re,
dqs_serdes_pattern.eq(0b00000001)
).Else(
dqs_serdes_pattern.eq(0b00000000)
)
).Else(
dqs_serdes_pattern.eq(0b01010101)
)
for i in range(databits//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[databits//8+i],
i_D3=self.dfi.phases[1].wrdata_mask[i], i_D4=self.dfi.phases[1].wrdata_mask[databits//8+i],
i_D5=self.dfi.phases[2].wrdata_mask[i], i_D6=self.dfi.phases[2].wrdata_mask[databits//8+i],
i_D7=self.dfi.phases[3].wrdata_mask[i], i_D8=self.dfi.phases[3].wrdata_mask[databits//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._dly_sel.storage[i] & self._wdly_dq_rst.re,
i_CE=self._dly_sel.storage[i] & self._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._dly_sel.storage[i] & self._wdly_dqs_rst.re,
i_CE=self._dly_sel.storage[i] & self._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(databits):
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[databits+i],
i_D3=self.dfi.phases[1].wrdata[i], i_D4=self.dfi.phases[1].wrdata[databits+i],
i_D5=self.dfi.phases[2].wrdata[i], i_D6=self.dfi.phases[2].wrdata[databits+i],
i_D7=self.dfi.phases[3].wrdata[i], i_D8=self.dfi.phases[3].wrdata[databits+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._dly_sel.storage[i//8] & self._wdly_dq_rst.re),
i_CLK=ClockSignal("sys4x"), i_CLKB=~ClockSignal("sys4x"), i_CLKDIV=ClockSignal(),
i_BITSLIP=self._dly_sel.storage[i//8] & self._rdly_dq_bitslip.re,
o_Q8=self.dfi.phases[0].rddata[i], o_Q7=self.dfi.phases[0].rddata[databits+i],
o_Q6=self.dfi.phases[1].rddata[i], o_Q5=self.dfi.phases[1].rddata[databits+i],
o_Q4=self.dfi.phases[2].rddata[i], o_Q3=self.dfi.phases[2].rddata[databits+i],
o_Q2=self.dfi.phases[3].rddata[i], o_Q1=self.dfi.phases[3].rddata[databits+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._dly_sel.storage[i//8] & self._wdly_dq_rst.re,
i_CE=self._dly_sel.storage[i//8] & self._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._dly_sel.storage[i//8] & self._rdly_dq_rst.re,
i_CE=self._dly_sel.storage[i//8] & self._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.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._wlevel_en.storage)
for phase in self.dfi.phases]
oe = Signal()
last_wrdata_en = Signal(4)
wrphase = self.dfi.phases[self.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._wlevel_en.storage,
oe_dqs.eq(1), oe_dq.eq(0)
).Else(
oe_dqs.eq(oe), oe_dq.eq(oe)
)

View File

@ -1,488 +0,0 @@
# 1:2 and 1:4 frequency-ratio DDR / LPDDR / DDR2 / DDR3 PHYs 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 for DDR, LPDDR, DDR2
# and CAS latency 5/CAS write latency 6 for DDR3.
#
# Read commands must be sent on phase 0.
# Write commands must be sent on phase 1.
#
from functools import reduce
from operator import or_
from litex.gen import *
from litex.gen.genlib.record import *
from litex.gen.fhdl.decorators import ClockDomainsRenamer
from litex.soc.interconnect.dfi import *
from litex.soc.cores.sdram import settings as sdram_settings
class S6HalfRateDDRPHY(Module):
def __init__(self, pads, memtype, rd_bitslip, wr_bitslip, dqs_ddr_alignment):
if memtype not in ["DDR", "LPDDR", "DDR2", "DDR3"]:
raise NotImplementedError("S6HalfRateDDRPHY only supports DDR, LPDDR, DDR2 and DDR3")
addressbits = len(pads.a)
bankbits = len(pads.ba)
databits = len(pads.dq)
nphases = 2
if memtype == "DDR3":
self.settings = sdram_settings.PhySettings(
memtype="DDR3",
dfi_databits=2*databits,
nphases=nphases,
rdphase=0,
wrphase=1,
rdcmdphase=1,
wrcmdphase=0,
cl=5,
cwl=6,
read_latency=6,
write_latency=2
)
else:
self.settings = sdram_settings.PhySettings(
memtype=memtype,
dfi_databits=2*databits,
nphases=nphases,
rdphase=0,
wrphase=1,
rdcmdphase=1,
wrcmdphase=0,
cl=3,
read_latency=5,
write_latency=0
)
self.dfi = Interface(addressbits, bankbits, 2*databits, 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))
phase_half = Signal.like(phase_sel)
phase_sys = Signal.like(phase_half)
sd_sys += phase_sys.eq(phase_half)
sd_sdram_half += [
If(phase_half == phase_sys,
phase_sel.eq(0),
).Else(
phase_sel.eq(phase_sel+1)
),
phase_half.eq(phase_half+1),
]
# register dfi cmds on half_rate clk
r_dfi = Array(Record(phase_cmd_description(addressbits, bankbits)) for i in range(nphases))
for n, phase in enumerate(self.dfi.phases):
sd_sdram_half += [
r_dfi[n].reset_n.eq(phase.reset_n),
r_dfi[n].odt.eq(phase.odt),
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)
]
# optional pads
for name in "reset_n", "cs_n", "odt":
if hasattr(pads, name):
sd_sdram_half += getattr(pads, name).eq(getattr(r_dfi[phase_sel], name))
#
# 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(databits//8)
dqs_t = Signal(databits//8)
self.comb += [
dqs_t_d0.eq(~(drive_dqs | postamble)),
dqs_t_d1.eq(~drive_dqs),
]
for i in range(databits//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*databits)+phase_rddata_description(nphases*databits))
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(databits)
dq_o = Signal(databits)
dq_i = Signal(databits)
dq_wrdata = []
for i in range(2):
for j in reversed(range(nphases)):
dq_wrdata.append(d_dfi[i*nphases+j].wrdata[:databits])
dq_wrdata.append(d_dfi[i*nphases+j].wrdata[databits:])
for i in range(databits):
# 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+databits],
o_Q2=d_dfi[0*nphases+0].rddata[i],
o_Q3=d_dfi[0*nphases+1].rddata[i+databits],
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[:databits//8])
dq_wrdata_mask.append(d_dfi[i*nphases+j].wrdata_mask[databits//8:])
for i in range(databits//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,
)
#
# DQ/DQS/DM control
#
# write
wrdata_en = Signal()
self.comb += wrdata_en.eq(reduce(or_, [d_dfi[p].wrdata_en for p in range(nphases)]))
if memtype == "DDR3":
r_drive_dq = Signal(self.settings.cwl-1)
sd_sdram_half += r_drive_dq.eq(Cat(wrdata_en, r_drive_dq))
self.comb += drive_dq.eq(r_drive_dq[self.settings.cwl-2])
else:
self.comb += drive_dq.eq(wrdata_en)
wrdata_en_d = Signal()
sd_sys += wrdata_en_d.eq(wrdata_en)
r_dfi_wrdata_en = Signal(max(self.settings.cwl, self.settings.cl))
sd_sdram_half += r_dfi_wrdata_en.eq(Cat(wrdata_en_d, r_dfi_wrdata_en))
if memtype == "DDR3":
self.comb += drive_dqs.eq(r_dfi_wrdata_en[self.settings.cwl-1])
else:
self.comb += drive_dqs.eq(r_dfi_wrdata_en[1])
# read
rddata_en = Signal()
self.comb += rddata_en.eq(reduce(or_, [d_dfi[p].rddata_en for p in range(nphases)]))
rddata_sr = Signal(self.settings.read_latency)
sd_sys += rddata_sr.eq(Cat(rddata_sr[1:self.settings.read_latency], 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]),
]
class S6QuarterRateDDRPHY(Module):
def __init__(self, pads, rd_bitslip, wr_bitslip, dqs_ddr_alignment):
half_rate_phy = S6HalfRateDDRPHY(pads, "DDR3", rd_bitslip, wr_bitslip, dqs_ddr_alignment)
self.submodules += ClockDomainsRenamer("sys2x")(half_rate_phy)
addressbits = len(pads.a)
bankbits = len(pads.ba)
databits = len(pads.dq)
nphases = 4
self.settings = sdram_settings.PhySettings(
memtype="DDR3",
dfi_databits=2*databits,
nphases=nphases,
rdphase=0,
wrphase=1,
rdcmdphase=1,
wrcmdphase=0,
cl=5,
cwl=6,
read_latency=6//2+1,
write_latency=2//2
)
self.dfi = Interface(addressbits, bankbits, 2*databits, nphases)
self.clk8x_wr_strb = half_rate_phy.clk4x_wr_strb
self.clk8x_rd_strb = half_rate_phy.clk4x_rd_strb
# sys_clk : system clk, used for dfi interface
# sys2x_clk : 2x system clk
sd_sys = getattr(self.sync, "sys")
sd_sys2x = getattr(self.sync, "sys2x")
# select active sys2x phase
# sys_clk ----____----____
# phase_sel 0 1 0 1
phase_sel = Signal()
phase_sys2x = Signal.like(phase_sel)
phase_sys = Signal.like(phase_sys2x)
sd_sys += phase_sys.eq(phase_sys2x)
sd_sys2x += [
If(phase_sys2x == phase_sys,
phase_sel.eq(0),
).Else(
phase_sel.eq(~phase_sel)
),
phase_sys2x.eq(~phase_sel)
]
# DFI adaptation
# Commands and writes
dfi_omit = set(["rddata", "rddata_valid", "wrdata_en"])
self.comb += [
If(~phase_sel,
self.dfi.phases[0].connect(half_rate_phy.dfi.phases[0], omit=dfi_omit),
self.dfi.phases[1].connect(half_rate_phy.dfi.phases[1], omit=dfi_omit),
).Else(
self.dfi.phases[2].connect(half_rate_phy.dfi.phases[0], omit=dfi_omit),
self.dfi.phases[3].connect(half_rate_phy.dfi.phases[1], omit=dfi_omit),
),
]
wr_data_en = self.dfi.phases[self.settings.wrphase].wrdata_en & ~phase_sel
wr_data_en_d = Signal()
sd_sys2x += wr_data_en_d.eq(wr_data_en)
self.comb += half_rate_phy.dfi.phases[half_rate_phy.settings.wrphase].wrdata_en.eq(wr_data_en | wr_data_en_d)
# Reads
rddata = Array(Signal(2*databits) for i in range(2))
rddata_valid = Signal(2)
for i in range(2):
sd_sys2x += [
rddata_valid[i].eq(half_rate_phy.dfi.phases[i].rddata_valid),
rddata[i].eq(half_rate_phy.dfi.phases[i].rddata)
]
sd_sys += [
self.dfi.phases[0].rddata.eq(rddata[0]),
self.dfi.phases[0].rddata_valid.eq(rddata_valid[0]),
self.dfi.phases[1].rddata.eq(rddata[1]),
self.dfi.phases[1].rddata_valid.eq(rddata_valid[1]),
self.dfi.phases[2].rddata.eq(half_rate_phy.dfi.phases[0].rddata),
self.dfi.phases[2].rddata_valid.eq(half_rate_phy.dfi.phases[0].rddata_valid),
self.dfi.phases[3].rddata.eq(half_rate_phy.dfi.phases[1].rddata),
self.dfi.phases[3].rddata_valid.eq(half_rate_phy.dfi.phases[1].rddata_valid)
]

View File

@ -1,196 +0,0 @@
from math import ceil
from collections import namedtuple
from litex.gen import *
PhySettingsT = namedtuple("PhySettings", "memtype dfi_databits nphases rdphase wrphase rdcmdphase wrcmdphase cl cwl read_latency write_latency")
def PhySettings(memtype, dfi_databits, nphases, rdphase, wrphase, rdcmdphase, wrcmdphase, cl, read_latency, write_latency, cwl=0):
return PhySettingsT(memtype, dfi_databits, nphases, rdphase, wrphase, rdcmdphase, wrcmdphase, cl, cwl, read_latency, write_latency)
GeomSettingsT = namedtuple("_GeomSettings", "bankbits rowbits colbits addressbits")
def GeomSettings(bankbits, rowbits, colbits):
return GeomSettingsT(bankbits, rowbits, colbits, max(rowbits, colbits))
TimingSettings = namedtuple("TimingSettings", "tRP tRCD tWR tWTR tREFI tRFC")
# TODO:
# Try to share the maximum information we can between modules:
# - ex: MT46V32M16 and MT46H32M16 are almost identical (V=DDR, H=LPDDR)
# - Modules can have different configuration:
# MT8JTF12864 (1GB), MT8JTF25664 (2GB)
# but share all others informations, try to create an unique module for all
# configurations.
# - Modules can have different speedgrades, add support for it (and also add
# a check to verify clk_freq is in the supported range)
class SDRAMModule:
def __init__(self, clk_freq, rate):
self.clk_freq = clk_freq
self.rate = rate
self.geom_settings = GeomSettings(
bankbits=log2_int(self.nbanks),
rowbits=log2_int(self.nrows),
colbits=log2_int(self.ncols),
)
self.timing_settings = TimingSettings(
tRP=self.ns(self.tRP),
tRCD=self.ns(self.tRCD),
tWR=self.ns(self.tWR),
tWTR=self.tWTR,
tREFI=self.ns(self.tREFI, False),
tRFC=self.ns(self.tRFC)
)
def ns(self, t, margin=True):
clk_period_ns = 1000000000/self.clk_freq
if margin:
margins = {
"1:1" : 0,
"1:2" : clk_period_ns/2,
"1:4" : 3*clk_period_ns/4
}
t += margins[self.rate]
return ceil(t/clk_period_ns)
# SDR
class IS42S16160(SDRAMModule):
memtype = "SDR"
# geometry
nbanks = 4
nrows = 8192
ncols = 512
# timings (-7 speedgrade)
tRP = 20
tRCD = 20
tWR = 20
tWTR = 2
tREFI = 64*1000*1000/8192
tRFC = 70
class MT48LC4M16(SDRAMModule):
memtype = "SDR"
# geometry
nbanks = 4
nrows = 4096
ncols = 256
# timings (-7 speedgrade)
tRP = 15
tRCD = 15
tWR = 14
tWTR = 2
tREFI = 64*1000*1000/4096
tRFC = 66
class AS4C16M16(SDRAMModule):
memtype = "SDR"
# geometry
nbanks = 4
nrows = 8192
ncols = 512
# timings (-6 speedgrade)
tRP = 18
tRCD = 18
tWR = 12
tWTR = 2
tREFI = 64*1000*1000/8192
tRFC = 60
# DDR
class MT46V32M16(SDRAMModule):
memtype = "DDR"
# geometry
nbanks = 4
nrows = 8192
ncols = 1024
# timings (-6 speedgrade)
tRP = 15
tRCD = 15
tWR = 15
tWTR = 2
tREFI = 64*1000*1000/8192
tRFC = 70
# LPDDR
class MT46H32M16(SDRAMModule):
memtype = "LPDDR"
# geometry
nbanks = 4
nrows = 8192
ncols = 1024
# timings
tRP = 15
tRCD = 15
tWR = 15
tWTR = 2
tREFI = 64*1000*1000/8192
tRFC = 72
# DDR2
class MT47H128M8(SDRAMModule):
memtype = "DDR2"
# geometry
nbanks = 8
nrows = 16384
ncols = 1024
# timings
tRP = 15
tRCD = 15
tWR = 15
tWTR = 2
tREFI = 7800
tRFC = 127.5
class P3R1GE4JGF(SDRAMModule):
memtype = "DDR2"
# geometry
nbanks = 8
nrows = 8192
ncols = 1024
# timings
tRP = 12.5
tRCD = 12.5
tWR = 15
tWTR = 3
tREFI = 7800
tRFC = 127.5
# DDR3
class MT8JTF12864(SDRAMModule):
memtype = "DDR3"
# geometry
nbanks = 8
nrows = 16384
ncols = 1024
# timings
tRP = 15
tRCD = 15
tWR = 15
tWTR = 2
tREFI = 7800
tRFC = 70
class MT41J128M16(SDRAMModule):
memtype = "DDR3"
# geometry
nbanks = 8
nrows = 16384
ncols = 1024
# timings
tRP = 15
tRCD = 15
tWR = 15
tWTR = 3
tREFI = 64*1000*1000/16384
tRFC = 260

View File

@ -1,123 +0,0 @@
from functools import reduce
from operator import xor
from litex.gen import *
from litex.soc.interconnect.csr import *
from litex.soc.interconnect import dma_lasmi
# TODO: implement or replace DMAControllers in MiSoC
@ResetInserter()
@CEInserter()
class LFSR(Module):
def __init__(self, n_out, n_state=31, taps=[27, 30]):
self.o = Signal(n_out)
###
state = Signal(n_state)
curval = [state[i] for i in range(n_state)]
curval += [0]*(n_out - n_state)
for i in range(n_out):
nv = ~reduce(xor, [curval[tap] for tap in taps])
curval.insert(0, nv)
curval.pop()
self.sync += [
state.eq(Cat(*curval[:n_state])),
self.o.eq(Cat(*curval))
]
memtest_magic = 0x361f
class Writer(Module):
def __init__(self, lasmim):
self._magic = CSRStatus(16)
self._reset = CSR()
self._shoot = CSR()
self.submodules._dma = DMAWriteController(dma_lasmi.Writer(lasmim),
MODE_EXTERNAL)
###
self.comb += self._magic.status.eq(memtest_magic)
lfsr = LFSR(lasmim.dw)
self.submodules += lfsr
self.comb += lfsr.reset.eq(self._reset.re)
en = Signal()
en_counter = Signal(lasmim.aw)
self.comb += en.eq(en_counter != 0)
self.sync += [
If(self._shoot.re,
en_counter.eq(self._dma.length)
).Elif(lfsr.ce,
en_counter.eq(en_counter - 1)
)
]
self.comb += [
self._dma.trigger.eq(self._shoot.re),
self._dma.data.stb.eq(en),
lfsr.ce.eq(en & self._dma.data.ack),
self._dma.data.d.eq(lfsr.o)
]
def get_csrs(self):
return [self._magic, self._reset, self._shoot] + self._dma.get_csrs()
class Reader(Module):
def __init__(self, lasmim):
self._magic = CSRStatus(16)
self._reset = CSR()
self._error_count = CSRStatus(lasmim.aw)
self.submodules._dma = DMAReadController(dma_lasmi.Reader(lasmim),
MODE_SINGLE_SHOT)
###
self.comb += self._magic.status.eq(memtest_magic)
lfsr = LFSR(lasmim.dw)
self.submodules += lfsr
self.comb += lfsr.reset.eq(self._reset.re)
self.comb += [
lfsr.ce.eq(self._dma.data.stb),
self._dma.data.ack.eq(1)
]
err_cnt = self._error_count.status
self.sync += [
If(self._reset.re,
err_cnt.eq(0)
).Elif(self._dma.data.stb,
If(self._dma.data.d != lfsr.o, err_cnt.eq(err_cnt + 1))
)
]
def get_csrs(self):
return [self._magic, self._reset, self._error_count] + self._dma.get_csrs()
class _LFSRTB(Module):
def __init__(self, *args, **kwargs):
self.submodules.dut = LFSR(*args, **kwargs)
self.comb += self.dut.ce.eq(1)
def do_simulation(self, selfp):
print("{0:032x}".format(selfp.dut.o))
if __name__ == "__main__":
from litex.gen.fhdl import verilog
from litex.gen.sim.generic import run_simulation
lfsr = LFSR(3, 4, [3, 2])
print(verilog.convert(lfsr, ios={lfsr.ce, lfsr.reset, lfsr.o}))
run_simulation(_LFSRTB(128), ncycles=20)

View File

@ -1,11 +1,16 @@
from litex.gen import *
from litex.gen.genlib.record import *
from litex.soc.interconnect import wishbone, wishbone2lasmi, lasmi_bus
from litex.soc.interconnect import wishbone
from litex.soc.interconnect import wishbone
from litex.soc.interconnect.csr import AutoCSR
from litex.soc.cores.sdram import dfii, minicon, lasmicon
from litex.soc.integration.soc_core import *
from litedram import lasmi_bus
from litedram.frontend import wishbone2lasmi
from litedram import dfii, minicon, lasmicon
__all__ = ["SoCSDRAM", "soc_sdram_args", "soc_sdram_argdict"]

View File

@ -1,73 +0,0 @@
from litex.gen import *
from litex.gen.genlib.record import *
def phase_cmd_description(addressbits, bankbits):
return [
("address", addressbits, DIR_M_TO_S),
("bank", bankbits, DIR_M_TO_S),
("cas_n", 1, DIR_M_TO_S),
("cs_n", 1, DIR_M_TO_S),
("ras_n", 1, DIR_M_TO_S),
("we_n", 1, DIR_M_TO_S),
("cke", 1, DIR_M_TO_S),
("odt", 1, DIR_M_TO_S),
("reset_n", 1, DIR_M_TO_S)
]
def phase_wrdata_description(databits):
return [
("wrdata", databits, DIR_M_TO_S),
("wrdata_en", 1, DIR_M_TO_S),
("wrdata_mask", databits//8, DIR_M_TO_S)
]
def phase_rddata_description(databits):
return [
("rddata_en", 1, DIR_M_TO_S),
("rddata", databits, DIR_S_TO_M),
("rddata_valid", 1, DIR_S_TO_M)
]
def phase_description(addressbits, bankbits, databits):
r = phase_cmd_description(addressbits, bankbits)
r += phase_wrdata_description(databits)
r += phase_rddata_description(databits)
return r
class Interface(Record):
def __init__(self, addressbits, bankbits, databits, nphases=1):
layout = [("p"+str(i), phase_description(addressbits, bankbits, databits)) for i in range(nphases)]
Record.__init__(self, layout)
self.phases = [getattr(self, "p"+str(i)) for i in range(nphases)]
for p in self.phases:
p.cas_n.reset = 1
p.cs_n.reset = 1
p.ras_n.reset = 1
p.we_n.reset = 1
# Returns pairs (DFI-mandated signal name, Migen signal object)
def get_standard_names(self, m2s=True, s2m=True):
r = []
add_suffix = len(self.phases) > 1
for n, phase in enumerate(self.phases):
for field, size, direction in phase.layout:
if (m2s and direction == DIR_M_TO_S) or (s2m and direction == DIR_S_TO_M):
if add_suffix:
if direction == DIR_M_TO_S:
suffix = "_p" + str(n)
else:
suffix = "_w" + str(n)
else:
suffix = ""
r.append(("dfi_" + field + suffix, getattr(phase, field)))
return r
class Interconnect(Module):
def __init__(self, master, slave):
self.comb += master.connect(slave)

View File

@ -1,91 +0,0 @@
from litex.gen import *
from litex.gen.genlib.fifo import SyncFIFO
from litex.soc.interconnect import stream
class Reader(Module):
def __init__(self, lasmim, fifo_depth=None):
self.sink = sink = stream.Endpoint([("address", lasmim.aw)])
self.source = source = stream.Endpoint([("data", lasmim.dw)])
self.busy = Signal()
# # #
if fifo_depth is None:
fifo_depth = lasmim.req_queue_size + lasmim.read_latency + 2
# request issuance
request_enable = Signal()
request_issued = Signal()
self.comb += [
lasmim.we.eq(0),
lasmim.stb.eq(sink.valid & request_enable),
lasmim.adr.eq(sink.address),
sink.ready.eq(lasmim.req_ack & request_enable),
request_issued.eq(lasmim.stb & lasmim.req_ack)
]
# FIFO reservation level counter
# incremented when data is planned to be queued
# decremented when data is dequeued
data_dequeued = Signal()
rsv_level = Signal(max=fifo_depth+1)
self.sync += [
If(request_issued,
If(~data_dequeued, rsv_level.eq(rsv_level + 1))
).Elif(data_dequeued,
rsv_level.eq(rsv_level - 1)
)
]
self.comb += [
self.busy.eq(rsv_level != 0),
request_enable.eq(rsv_level != fifo_depth)
]
# FIFO
fifo = SyncFIFO(lasmim.dw, fifo_depth)
self.submodules += fifo
self.comb += [
fifo.din.eq(lasmim.dat_r),
fifo.we.eq(lasmim.dat_r_ack),
source.valid.eq(fifo.readable),
fifo.re.eq(source.ready),
source.data.eq(fifo.dout),
data_dequeued.eq(source.valid & source.ready)
]
class Writer(Module):
def __init__(self, lasmim, fifo_depth=None):
self.source = source = stream.Endpoint([("address", lasmim.aw),
("data", lasmim.dw)])
self.busy = Signal()
# # #
if fifo_depth is None:
fifo_depth = lasmim.req_queue_size + lasmim.write_latency + 2
fifo = SyncFIFO(lasmim.dw, fifo_depth)
self.submodules += fifo
self.comb += [
lasmim.we.eq(1),
lasmim.stb.eq(fifo.writable & source.valid),
lasmim.adr.eq(source.address),
source.ready.eq(fifo.writable & lasmim.req_ack),
fifo.we.eq(source.valid & lasmim.req_ack),
fifo.din.eq(source.data)
]
self.comb += [
If(lasmim.dat_w_ack,
fifo.re.eq(1),
lasmim.dat_we.eq(2**(lasmim.dw//8)-1),
lasmim.dat_w.eq(fifo.dout)
),
self.busy.eq(fifo.readable)
]

View File

@ -1,208 +0,0 @@
from functools import reduce
from operator import or_
from litex.gen import *
from litex.gen.genlib import roundrobin
from litex.gen.genlib.record import *
class Interface(Record):
def __init__(self, aw, dw, nbanks, req_queue_size, read_latency, write_latency):
self.aw = aw
self.dw = dw
self.nbanks = nbanks
self.req_queue_size = req_queue_size
self.read_latency = read_latency
self.write_latency = write_latency
bank_layout = [
("adr", aw, DIR_M_TO_S),
("we", 1, DIR_M_TO_S),
("stb", 1, DIR_M_TO_S),
("req_ack", 1, DIR_S_TO_M),
("dat_w_ack", 1, DIR_S_TO_M),
("dat_r_ack", 1, DIR_S_TO_M),
("lock", 1, DIR_S_TO_M)
]
if nbanks > 1:
layout = [("bank"+str(i), bank_layout) for i in range(nbanks)]
else:
layout = bank_layout
layout += [
("dat_w", dw, DIR_M_TO_S),
("dat_we", dw//8, DIR_M_TO_S),
("dat_r", dw, DIR_S_TO_M)
]
Record.__init__(self, layout)
def _getattr_all(l, attr):
it = iter(l)
r = getattr(next(it), attr)
for e in it:
if getattr(e, attr) != r:
raise ValueError
return r
class LASMIxbar(Module):
def __init__(self, controllers, cba_shift):
self._controllers = controllers
self._cba_shift = cba_shift
self._rca_bits = _getattr_all(controllers, "aw")
self._dw = _getattr_all(controllers, "dw")
self._nbanks = _getattr_all(controllers, "nbanks")
self._req_queue_size = _getattr_all(controllers, "req_queue_size")
self._read_latency = _getattr_all(controllers, "read_latency")
self._write_latency = _getattr_all(controllers, "write_latency")
self._bank_bits = log2_int(self._nbanks, False)
self._controller_bits = log2_int(len(self._controllers), False)
self._masters = []
def get_master(self):
if self.finalized:
raise FinalizeError
lasmi_master = Interface(self._rca_bits + self._bank_bits + self._controller_bits,
self._dw, 1, self._req_queue_size, self._read_latency, self._write_latency)
self._masters.append(lasmi_master)
return lasmi_master
def do_finalize(self):
nmasters = len(self._masters)
m_ca, m_ba, m_rca = self._split_master_addresses(self._controller_bits,
self._bank_bits, self._rca_bits, self._cba_shift)
for nc, controller in enumerate(self._controllers):
if self._controller_bits:
controller_selected = [ca == nc for ca in m_ca]
else:
controller_selected = [1]*nmasters
master_req_acks = [0]*nmasters
master_dat_w_acks = [0]*nmasters
master_dat_r_acks = [0]*nmasters
rrs = [roundrobin.RoundRobin(nmasters, roundrobin.SP_CE) for n in range(self._nbanks)]
self.submodules += rrs
for nb, rr in enumerate(rrs):
bank = getattr(controller, "bank"+str(nb))
# for each master, determine if another bank locks it
master_locked = []
for nm, master in enumerate(self._masters):
locked = 0
for other_nb, other_rr in enumerate(rrs):
if other_nb != nb:
other_bank = getattr(controller, "bank"+str(other_nb))
locked = locked | (other_bank.lock & (other_rr.grant == nm))
master_locked.append(locked)
# arbitrate
bank_selected = [cs & (ba == nb) & ~locked for cs, ba, locked in zip(controller_selected, m_ba, master_locked)]
bank_requested = [bs & master.stb for bs, master in zip(bank_selected, self._masters)]
self.comb += [
rr.request.eq(Cat(*bank_requested)),
rr.ce.eq(~bank.stb & ~bank.lock)
]
# route requests
self.comb += [
bank.adr.eq(Array(m_rca)[rr.grant]),
bank.we.eq(Array(self._masters)[rr.grant].we),
bank.stb.eq(Array(bank_requested)[rr.grant])
]
master_req_acks = [master_req_ack | ((rr.grant == nm) & bank_selected[nm] & bank.req_ack)
for nm, master_req_ack in enumerate(master_req_acks)]
master_dat_w_acks = [master_dat_w_ack | ((rr.grant == nm) & bank.dat_w_ack)
for nm, master_dat_w_ack in enumerate(master_dat_w_acks)]
master_dat_r_acks = [master_dat_r_ack | ((rr.grant == nm) & bank.dat_r_ack)
for nm, master_dat_r_ack in enumerate(master_dat_r_acks)]
for nm, master_dat_w_ack in enumerate(master_dat_w_acks):
for i in range(self._write_latency):
new_master_dat_w_ack = Signal()
self.sync += new_master_dat_w_ack.eq(master_dat_w_ack)
master_dat_w_ack = new_master_dat_w_ack
master_dat_w_acks[nm] = master_dat_w_ack
for nm, master_dat_r_ack in enumerate(master_dat_r_acks):
for i in range(self._read_latency):
new_master_dat_r_ack = Signal()
self.sync += new_master_dat_r_ack.eq(master_dat_r_ack)
master_dat_r_ack = new_master_dat_r_ack
master_dat_r_acks[nm] = master_dat_r_ack
self.comb += [master.req_ack.eq(master_req_ack) for master, master_req_ack in zip(self._masters, master_req_acks)]
self.comb += [master.dat_w_ack.eq(master_dat_w_ack) for master, master_dat_w_ack in zip(self._masters, master_dat_w_acks)]
self.comb += [master.dat_r_ack.eq(master_dat_r_ack) for master, master_dat_r_ack in zip(self._masters, master_dat_r_acks)]
# route data writes
controller_selected_wl = controller_selected
for i in range(self._write_latency):
n_controller_selected_wl = [Signal() for i in range(nmasters)]
self.sync += [n.eq(o) for n, o in zip(n_controller_selected_wl, controller_selected_wl)]
controller_selected_wl = n_controller_selected_wl
dat_w_maskselect = []
dat_we_maskselect = []
for master, selected in zip(self._masters, controller_selected_wl):
o_dat_w = Signal(self._dw)
o_dat_we = Signal(self._dw//8)
self.comb += If(selected,
o_dat_w.eq(master.dat_w),
o_dat_we.eq(master.dat_we)
)
dat_w_maskselect.append(o_dat_w)
dat_we_maskselect.append(o_dat_we)
self.comb += [
controller.dat_w.eq(reduce(or_, dat_w_maskselect)),
controller.dat_we.eq(reduce(or_, dat_we_maskselect))
]
# route data reads
if self._controller_bits:
for master in self._masters:
controller_sel = Signal(self._controller_bits)
for nc, controller in enumerate(self._controllers):
for nb in range(nbanks):
bank = getattr(controller, "bank"+str(nb))
self.comb += If(bank.stb & bank.ack, controller_sel.eq(nc))
for i in range(self._read_latency):
n_controller_sel = Signal(self._controller_bits)
self.sync += n_controller_sel.eq(controller_sel)
controller_sel = n_controller_sel
self.comb += master.dat_r.eq(Array(self._controllers)[controller_sel].dat_r)
else:
self.comb += [master.dat_r.eq(self._controllers[0].dat_r) for master in self._masters]
def _split_master_addresses(self, controller_bits, bank_bits, rca_bits, cba_shift):
m_ca = [] # controller address
m_ba = [] # bank address
m_rca = [] # row and column address
for master in self._masters:
cba = Signal(self._controller_bits + self._bank_bits)
rca = Signal(self._rca_bits)
cba_upper = cba_shift + controller_bits + bank_bits
self.comb += cba.eq(master.adr[cba_shift:cba_upper])
if cba_shift < self._rca_bits:
if cba_shift:
self.comb += rca.eq(Cat(master.adr[:cba_shift], master.adr[cba_upper:]))
else:
self.comb += rca.eq(master.adr[cba_upper:])
else:
self.comb += rca.eq(master.adr[:cba_shift])
if self._controller_bits:
ca = Signal(self._controller_bits)
ba = Signal(self._bank_bits)
self.comb += Cat(ba, ca).eq(cba)
else:
ca = None
ba = cba
m_ca.append(ca)
m_ba.append(ba)
m_rca.append(rca)
return m_ca, m_ba, m_rca

View File

@ -1,49 +0,0 @@
from litex.gen import *
from litex.gen.genlib.fsm import FSM, NextState
class WB2LASMI(Module):
def __init__(self, wishbone, lasmim):
###
# Control FSM
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
If(wishbone.cyc & wishbone.stb,
NextState("REQUEST")
)
)
fsm.act("REQUEST",
lasmim.stb.eq(1),
lasmim.we.eq(wishbone.we),
If(lasmim.req_ack,
If(wishbone.we,
NextState("WRITE_DATA")
).Else(
NextState("READ_DATA")
)
)
)
fsm.act("WRITE_DATA",
If(lasmim.dat_w_ack,
lasmim.dat_we.eq(wishbone.sel),
wishbone.ack.eq(1),
NextState("IDLE")
)
)
fsm.act("READ_DATA",
If(lasmim.dat_r_ack,
wishbone.ack.eq(1),
NextState("IDLE")
)
)
# Address / Datapath
self.comb += [
lasmim.adr.eq(wishbone.adr),
If(lasmim.dat_w_ack,
lasmim.dat_w.eq(wishbone.dat_w),
),
wishbone.dat_r.eq(lasmim.dat_r)
]

View File

@ -556,6 +556,7 @@ int memtest(void)
}
}
#ifdef CSR_DDRPHY_BASE
int sdrlevel_generic(void)
{
int delay[DFII_PIX_DATA_SIZE/2];
@ -569,6 +570,7 @@ int sdrlevel_generic(void)
return 1;
}
#ifdef SDRAM_ISERDESE2_BITSLIP
static int sdrlevel_artix7(void)
{
int bitslip, delay, module;
@ -587,6 +589,7 @@ static int sdrlevel_artix7(void)
}
return 1;
}
#endif
int sdrlevel(void)
{
@ -598,6 +601,7 @@ int sdrlevel(void)
return 0;
#endif
}
#endif
int sdrinit(void)
{