sdram/phy: add simphy (software memtest OK in simulation with MT48LC4M16)
This commit is contained in:
parent
e79a716425
commit
e6de4b1bf9
|
@ -0,0 +1,199 @@
|
||||||
|
# This file is Copyright (c) 2015 Florent Kermarrec <florent@enjoy-digital.fr>
|
||||||
|
# License: BSD
|
||||||
|
|
||||||
|
# SDRAM simulation PHY at DFI level
|
||||||
|
# Status:
|
||||||
|
# - tested against software memtest with SDR with Verilator.
|
||||||
|
# TODO:
|
||||||
|
# - expose phy_settings to user
|
||||||
|
# - test with DDR, LPDDR, DDR2 and DDR3
|
||||||
|
# - add $display support to Migen and manage timing violations?
|
||||||
|
|
||||||
|
from migen.fhdl.std import *
|
||||||
|
from migen.fhdl.specials import *
|
||||||
|
from misoclib.mem.sdram.phy.dfi import *
|
||||||
|
from misoclib.mem import sdram
|
||||||
|
|
||||||
|
class Bank(Module):
|
||||||
|
def __init__(self, data_width, nrows, ncols):
|
||||||
|
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)
|
||||||
|
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 SDRAMPHYSim(Module):
|
||||||
|
def __init__(self, module, nmodules=1):
|
||||||
|
addressbits = module.geom_settings.addressbits
|
||||||
|
databits = module.geom_settings.databits
|
||||||
|
bankbits = module.geom_settings.bankbits
|
||||||
|
rowbits = module.geom_settings.rowbits
|
||||||
|
colbits = module.geom_settings.colbits
|
||||||
|
|
||||||
|
# XXX expose this to user
|
||||||
|
self.settings = sdram.PhySettings(
|
||||||
|
memtype=module.memtype,
|
||||||
|
dfi_databits=databits,
|
||||||
|
nphases=1,
|
||||||
|
rdphase=0,
|
||||||
|
wrphase=0,
|
||||||
|
rdcmdphase=0,
|
||||||
|
wrcmdphase=0,
|
||||||
|
cl=2,
|
||||||
|
read_latency=4,
|
||||||
|
write_latency=0
|
||||||
|
)
|
||||||
|
self.module = module
|
||||||
|
|
||||||
|
self.dfi = Interface(addressbits, bankbits, databits)
|
||||||
|
|
||||||
|
###
|
||||||
|
nbanks = 2**bankbits
|
||||||
|
nrows = 2**rowbits
|
||||||
|
ncols = 2**colbits
|
||||||
|
data_width = databits*nmodules
|
||||||
|
|
||||||
|
# DFI phases
|
||||||
|
phases = [DFIPhase(self.dfi, n) for n in range(self.settings.nphases)]
|
||||||
|
self.submodules += phases
|
||||||
|
|
||||||
|
# banks
|
||||||
|
banks = [Bank(data_width, nrows, ncols) 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))
|
||||||
|
read_data = Signal(data_width)
|
||||||
|
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(optree("|", [bank.read for bank in banks])),
|
||||||
|
banks_read_data.eq(optree("|", [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(iter([phase.rddata_valid for phase in phases])).eq(banks_read),
|
||||||
|
Cat(iter([phase.rddata for phase in phases])).eq(banks_read_data)
|
||||||
|
]
|
Loading…
Reference in New Issue