mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
move sdram code to litedram (https://github.com/enjoy-digital/litedram)
This commit is contained in:
parent
42767286ca
commit
66362b1280
27 changed files with 23 additions and 2666 deletions
|
@ -5,11 +5,12 @@ import argparse
|
||||||
from litex.gen import *
|
from litex.gen import *
|
||||||
from litex.boards.platforms import de0nano
|
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.soc_sdram import *
|
||||||
from litex.soc.integration.builder import *
|
from litex.soc.integration.builder import *
|
||||||
|
|
||||||
|
from litedram.settings import IS42S16160
|
||||||
|
from litedram.phy import GENSDRPHY
|
||||||
|
|
||||||
|
|
||||||
class _PLL(Module):
|
class _PLL(Module):
|
||||||
def __init__(self, period_in, name, phase_shift, operation_mode):
|
def __init__(self, period_in, name, phase_shift, operation_mode):
|
||||||
|
|
|
@ -6,13 +6,14 @@ from litex.gen import *
|
||||||
from litex.gen.genlib.resetsync import AsyncResetSynchronizer
|
from litex.gen.genlib.resetsync import AsyncResetSynchronizer
|
||||||
from litex.boards.platforms import kc705
|
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.cores.flash import spi_flash
|
||||||
from litex.soc.integration.soc_core import mem_decoder
|
from litex.soc.integration.soc_core import mem_decoder
|
||||||
from litex.soc.integration.soc_sdram import *
|
from litex.soc.integration.soc_sdram import *
|
||||||
from litex.soc.integration.builder import *
|
from litex.soc.integration.builder import *
|
||||||
|
|
||||||
|
from litedram.settings import MT8JTF12864
|
||||||
|
from litedram.phy import k7ddrphy
|
||||||
|
|
||||||
from liteeth.phy import LiteEthPHY
|
from liteeth.phy import LiteEthPHY
|
||||||
from liteeth.core.mac import LiteEthMAC
|
from liteeth.core.mac import LiteEthMAC
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,12 @@ from litex.gen import *
|
||||||
from litex.gen.genlib.resetsync import AsyncResetSynchronizer
|
from litex.gen.genlib.resetsync import AsyncResetSynchronizer
|
||||||
from litex.boards.platforms import minispartan6
|
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.soc_sdram import *
|
||||||
from litex.soc.integration.builder import *
|
from litex.soc.integration.builder import *
|
||||||
|
|
||||||
|
from litedram.settings import AS4C16M16
|
||||||
|
from litedram.phy import GENSDRPHY
|
||||||
|
|
||||||
|
|
||||||
class _CRG(Module):
|
class _CRG(Module):
|
||||||
def __init__(self, platform, clk_freq):
|
def __init__(self, platform, clk_freq):
|
||||||
|
|
|
@ -10,10 +10,11 @@ from litex.gen.genlib.io import CRG
|
||||||
from litex.soc.integration.soc_sdram import *
|
from litex.soc.integration.soc_sdram import *
|
||||||
from litex.soc.integration.builder import *
|
from litex.soc.integration.builder import *
|
||||||
from litex.soc.cores import uart
|
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 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.phy.model import LiteEthPHYModel
|
||||||
from liteeth.core.mac import LiteEthMAC
|
from liteeth.core.mac import LiteEthMAC
|
||||||
|
|
||||||
|
|
|
@ -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")]
|
|
|
@ -1 +0,0 @@
|
||||||
from litex.soc.cores.sdram.lasmicon.core import ControllerSettings, LASMIcon
|
|
|
@ -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)
|
|
|
@ -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()
|
|
|
@ -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)
|
|
|
@ -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)
|
|
||||||
)
|
|
||||||
]
|
|
|
@ -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"))
|
|
||||||
)
|
|
|
@ -1 +0,0 @@
|
||||||
from litex.soc.cores.sdram.minicon.core import Minicon
|
|
|
@ -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),
|
|
||||||
]
|
|
|
@ -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)
|
|
||||||
]
|
|
|
@ -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
|
|
|
@ -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]))
|
|
|
@ -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)
|
|
||||||
)
|
|
|
@ -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)
|
|
||||||
]
|
|
|
@ -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
|
|
|
@ -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)
|
|
|
@ -1,11 +1,16 @@
|
||||||
from litex.gen import *
|
from litex.gen import *
|
||||||
from litex.gen.genlib.record 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.interconnect.csr import AutoCSR
|
||||||
from litex.soc.cores.sdram import dfii, minicon, lasmicon
|
|
||||||
from litex.soc.integration.soc_core import *
|
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"]
|
__all__ = ["SoCSDRAM", "soc_sdram_args", "soc_sdram_argdict"]
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
|
@ -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)
|
|
||||||
]
|
|
|
@ -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
|
|
|
@ -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)
|
|
||||||
]
|
|
|
@ -556,6 +556,7 @@ int memtest(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CSR_DDRPHY_BASE
|
||||||
int sdrlevel_generic(void)
|
int sdrlevel_generic(void)
|
||||||
{
|
{
|
||||||
int delay[DFII_PIX_DATA_SIZE/2];
|
int delay[DFII_PIX_DATA_SIZE/2];
|
||||||
|
@ -569,6 +570,7 @@ int sdrlevel_generic(void)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SDRAM_ISERDESE2_BITSLIP
|
||||||
static int sdrlevel_artix7(void)
|
static int sdrlevel_artix7(void)
|
||||||
{
|
{
|
||||||
int bitslip, delay, module;
|
int bitslip, delay, module;
|
||||||
|
@ -587,6 +589,7 @@ static int sdrlevel_artix7(void)
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int sdrlevel(void)
|
int sdrlevel(void)
|
||||||
{
|
{
|
||||||
|
@ -598,6 +601,7 @@ int sdrlevel(void)
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int sdrinit(void)
|
int sdrinit(void)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue