move sdram code to litedram (https://github.com/enjoy-digital/litedram)
This commit is contained in:
parent
42767286ca
commit
66362b1280
|
@ -5,11 +5,12 @@ import argparse
|
|||
from litex.gen import *
|
||||
from litex.boards.platforms import de0nano
|
||||
|
||||
from litex.soc.cores.sdram.settings import IS42S16160
|
||||
from litex.soc.cores.sdram.phy import GENSDRPHY
|
||||
from litex.soc.integration.soc_sdram import *
|
||||
from litex.soc.integration.builder import *
|
||||
|
||||
from litedram.settings import IS42S16160
|
||||
from litedram.phy import GENSDRPHY
|
||||
|
||||
|
||||
class _PLL(Module):
|
||||
def __init__(self, period_in, name, phase_shift, operation_mode):
|
||||
|
|
|
@ -6,13 +6,14 @@ from litex.gen import *
|
|||
from litex.gen.genlib.resetsync import AsyncResetSynchronizer
|
||||
from litex.boards.platforms import kc705
|
||||
|
||||
from litex.soc.cores.sdram.settings import MT8JTF12864
|
||||
from litex.soc.cores.sdram.phy import k7ddrphy
|
||||
from litex.soc.cores.flash import spi_flash
|
||||
from litex.soc.integration.soc_core import mem_decoder
|
||||
from litex.soc.integration.soc_sdram import *
|
||||
from litex.soc.integration.builder import *
|
||||
|
||||
from litedram.settings import MT8JTF12864
|
||||
from litedram.phy import k7ddrphy
|
||||
|
||||
from liteeth.phy import LiteEthPHY
|
||||
from liteeth.core.mac import LiteEthMAC
|
||||
|
||||
|
|
|
@ -7,11 +7,12 @@ from litex.gen import *
|
|||
from litex.gen.genlib.resetsync import AsyncResetSynchronizer
|
||||
from litex.boards.platforms import minispartan6
|
||||
|
||||
from litex.soc.cores.sdram.settings import AS4C16M16
|
||||
from litex.soc.cores.sdram.phy import GENSDRPHY
|
||||
from litex.soc.integration.soc_sdram import *
|
||||
from litex.soc.integration.builder import *
|
||||
|
||||
from litedram.settings import AS4C16M16
|
||||
from litedram.phy import GENSDRPHY
|
||||
|
||||
|
||||
class _CRG(Module):
|
||||
def __init__(self, platform, clk_freq):
|
||||
|
|
|
@ -10,10 +10,11 @@ from litex.gen.genlib.io import CRG
|
|||
from litex.soc.integration.soc_sdram import *
|
||||
from litex.soc.integration.builder import *
|
||||
from litex.soc.cores import uart
|
||||
from litex.soc.cores.sdram.settings import PhySettings, IS42S16160
|
||||
from litex.soc.cores.sdram.model import SDRAMPHYModel
|
||||
from litex.soc.integration.soc_core import mem_decoder
|
||||
|
||||
from litedram.settings import PhySettings, IS42S16160
|
||||
from litedram.model import SDRAMPHYModel
|
||||
|
||||
from liteeth.phy.model import LiteEthPHYModel
|
||||
from liteeth.core.mac import LiteEthMAC
|
||||
|
||||
|
|
|
@ -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.genlib.record import *
|
||||
|
||||
from litex.soc.interconnect import wishbone, wishbone2lasmi, lasmi_bus
|
||||
from litex.soc.interconnect import wishbone
|
||||
|
||||
from litex.soc.interconnect import wishbone
|
||||
from litex.soc.interconnect.csr import AutoCSR
|
||||
from litex.soc.cores.sdram import dfii, minicon, lasmicon
|
||||
from litex.soc.integration.soc_core import *
|
||||
|
||||
from litedram import lasmi_bus
|
||||
from litedram.frontend import wishbone2lasmi
|
||||
from litedram import dfii, minicon, lasmicon
|
||||
|
||||
|
||||
__all__ = ["SoCSDRAM", "soc_sdram_args", "soc_sdram_argdict"]
|
||||
|
||||
|
|
|
@ -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 delay[DFII_PIX_DATA_SIZE/2];
|
||||
|
@ -569,6 +570,7 @@ int sdrlevel_generic(void)
|
|||
return 1;
|
||||
}
|
||||
|
||||
#ifdef SDRAM_ISERDESE2_BITSLIP
|
||||
static int sdrlevel_artix7(void)
|
||||
{
|
||||
int bitslip, delay, module;
|
||||
|
@ -587,6 +589,7 @@ static int sdrlevel_artix7(void)
|
|||
}
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int sdrlevel(void)
|
||||
{
|
||||
|
@ -598,6 +601,7 @@ int sdrlevel(void)
|
|||
return 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
int sdrinit(void)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue