phy/model: small cleanup and add TODOs
This commit is contained in:
parent
c07f4a1f1b
commit
7d13136cdb
|
@ -1,10 +1,13 @@
|
||||||
# This file is Copyright (c) 2015 Florent Kermarrec <florent@enjoy-digital.fr>
|
# This file is Copyright (c) 2015-2020 Florent Kermarrec <florent@enjoy-digital.fr>
|
||||||
# License: BSD
|
# License: BSD
|
||||||
|
|
||||||
# SDRAM simulation PHY at DFI level
|
# SDRAM simulation PHY at DFI level tested with SDR/DDR/DDR2/LPDDR/DDR3
|
||||||
# tested with SDR/DDR/DDR2/LPDDR/DDR3
|
|
||||||
# TODO:
|
# TODO:
|
||||||
# - add $display support to LiteX gen and manage timing violations?
|
# - test/add DDR4 support.
|
||||||
|
# - add init/dump capabilities.
|
||||||
|
# - add multirank support.
|
||||||
|
# - add bandwidth/efficiency measurements.
|
||||||
|
# - add timings checks.
|
||||||
|
|
||||||
from migen import *
|
from migen import *
|
||||||
|
|
||||||
|
@ -13,8 +16,9 @@ from litedram.phy.dfi import *
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from operator import or_
|
from operator import or_
|
||||||
|
|
||||||
|
# Bank Model ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class Bank(Module):
|
class BankModel(Module):
|
||||||
def __init__(self, data_width, nrows, ncols, burst_length, we_granularity):
|
def __init__(self, data_width, nrows, ncols, burst_length, we_granularity):
|
||||||
self.activate = Signal()
|
self.activate = Signal()
|
||||||
self.activate_row = Signal(max=nrows)
|
self.activate_row = Signal(max=nrows)
|
||||||
|
@ -42,10 +46,11 @@ class Bank(Module):
|
||||||
row.eq(self.activate_row)
|
row.eq(self.activate_row)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.specials.mem = mem = Memory(data_width, nrows*ncols//burst_length)
|
mem = Memory(data_width, nrows*ncols//burst_length)
|
||||||
self.specials.write_port = write_port = mem.get_port(write_capable=True,
|
write_port = mem.get_port(write_capable=True, we_granularity=we_granularity)
|
||||||
we_granularity=we_granularity)
|
read_port = mem.get_port(async_read=True)
|
||||||
self.specials.read_port = read_port = mem.get_port(async_read=True)
|
self.specials += mem, read_port, write_port
|
||||||
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
If(active,
|
If(active,
|
||||||
write_port.adr.eq(row*ncols | self.write_col),
|
write_port.adr.eq(row*ncols | self.write_col),
|
||||||
|
@ -62,8 +67,9 @@ class Bank(Module):
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# DFI Phase Model ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
class DFIPhase(Module):
|
class DFIPhaseModel(Module):
|
||||||
def __init__(self, dfi, n):
|
def __init__(self, dfi, n):
|
||||||
phase = getattr(dfi, "p"+str(n))
|
phase = getattr(dfi, "p"+str(n))
|
||||||
|
|
||||||
|
@ -94,44 +100,56 @@ class DFIPhase(Module):
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# SDRAM PHY Model ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
class SDRAMPHYModel(Module):
|
class SDRAMPHYModel(Module):
|
||||||
def __init__(self, module, settings, we_granularity=8):
|
def __init__(self, module, settings, we_granularity=8):
|
||||||
if settings.memtype in ["SDR"]:
|
# Parameters
|
||||||
burst_length = settings.nphases*1 # command multiplication*SDR
|
burst_length = {
|
||||||
elif settings.memtype in ["DDR", "LPDDR", "DDR2", "DDR3"]:
|
"SDR": 1,
|
||||||
burst_length = settings.nphases*2 # command multiplication*DDR
|
"DDR": 2,
|
||||||
|
"LPDDR": 2,
|
||||||
|
"DDR2": 2,
|
||||||
|
"DDR3": 2,
|
||||||
|
}[settings.memtype]
|
||||||
|
|
||||||
addressbits = module.geom_settings.addressbits
|
addressbits = module.geom_settings.addressbits
|
||||||
bankbits = module.geom_settings.bankbits
|
bankbits = module.geom_settings.bankbits
|
||||||
rowbits = module.geom_settings.rowbits
|
rowbits = module.geom_settings.rowbits
|
||||||
colbits = module.geom_settings.colbits
|
colbits = module.geom_settings.colbits
|
||||||
|
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
self.module = module
|
self.module = module
|
||||||
|
|
||||||
self.dfi = Interface(addressbits, bankbits, self.settings.nranks, self.settings.dfi_databits, self.settings.nphases)
|
# DFI Interface
|
||||||
|
self.dfi = Interface(
|
||||||
|
addressbits = addressbits,
|
||||||
|
bankbits = bankbits,
|
||||||
|
nranks = self.settings.nranks,
|
||||||
|
databits = self.settings.dfi_databits,
|
||||||
|
nphases = self.settings.nphases
|
||||||
|
)
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
nbanks = 2**bankbits
|
nbanks = 2**bankbits
|
||||||
nrows = 2**rowbits
|
nrows = 2**rowbits
|
||||||
ncols = 2**colbits
|
ncols = 2**colbits
|
||||||
data_width = self.settings.dfi_databits*self.settings.nphases
|
data_width = self.settings.dfi_databits*self.settings.nphases
|
||||||
|
|
||||||
# DFI phases
|
# DFI phases -------------------------------------------------------------------------------
|
||||||
phases = [DFIPhase(self.dfi, n) for n in range(self.settings.nphases)]
|
phases = [DFIPhaseModel(self.dfi, n) for n in range(self.settings.nphases)]
|
||||||
self.submodules += phases
|
self.submodules += phases
|
||||||
|
|
||||||
# banks
|
# Banks ------------------------------------------------------------------------------------
|
||||||
banks = [Bank(data_width, nrows, ncols, burst_length, we_granularity) for i in range(nbanks)]
|
banks = [BankModel(data_width, nrows, ncols, burst_length, we_granularity) for i in range(nbanks)]
|
||||||
self.submodules += banks
|
self.submodules += banks
|
||||||
|
|
||||||
# connect DFI phases to banks (cmds, write datapath)
|
# Connect DFI phases to Banks (CMDs, Write datapath) ---------------------------------------
|
||||||
for nb, bank in enumerate(banks):
|
for nb, bank in enumerate(banks):
|
||||||
# bank activate
|
# Bank activate
|
||||||
activates = Signal(len(phases))
|
activates = Signal(len(phases))
|
||||||
cases = {}
|
cases = {}
|
||||||
for np, phase in enumerate(phases):
|
for np, phase in enumerate(phases):
|
||||||
self.comb += activates[np].eq(phase.activate)
|
self.comb += activates[np].eq(phase.activate)
|
||||||
cases[2**np] = [
|
cases[2**np] = [
|
||||||
|
@ -140,9 +158,9 @@ class SDRAMPHYModel(Module):
|
||||||
]
|
]
|
||||||
self.comb += Case(activates, cases)
|
self.comb += Case(activates, cases)
|
||||||
|
|
||||||
# bank precharge
|
# Bank precharge
|
||||||
precharges = Signal(len(phases))
|
precharges = Signal(len(phases))
|
||||||
cases = {}
|
cases = {}
|
||||||
for np, phase in enumerate(phases):
|
for np, phase in enumerate(phases):
|
||||||
self.comb += precharges[np].eq(phase.precharge)
|
self.comb += precharges[np].eq(phase.precharge)
|
||||||
cases[2**np] = [
|
cases[2**np] = [
|
||||||
|
@ -150,9 +168,9 @@ class SDRAMPHYModel(Module):
|
||||||
]
|
]
|
||||||
self.comb += Case(precharges, cases)
|
self.comb += Case(precharges, cases)
|
||||||
|
|
||||||
# bank writes
|
# Bank writes
|
||||||
writes = Signal(len(phases))
|
writes = Signal(len(phases))
|
||||||
cases = {}
|
cases = {}
|
||||||
for np, phase in enumerate(phases):
|
for np, phase in enumerate(phases):
|
||||||
self.comb += writes[np].eq(phase.write)
|
self.comb += writes[np].eq(phase.write)
|
||||||
cases[2**np] = [
|
cases[2**np] = [
|
||||||
|
@ -165,7 +183,7 @@ class SDRAMPHYModel(Module):
|
||||||
bank.write_mask.eq(Cat(*[phase.wrdata_mask for phase in phases]))
|
bank.write_mask.eq(Cat(*[phase.wrdata_mask for phase in phases]))
|
||||||
]
|
]
|
||||||
|
|
||||||
# bank reads
|
# Bank reads
|
||||||
reads = Signal(len(phases))
|
reads = Signal(len(phases))
|
||||||
cases = {}
|
cases = {}
|
||||||
for np, phase in enumerate(phases):
|
for np, phase in enumerate(phases):
|
||||||
|
@ -176,23 +194,23 @@ class SDRAMPHYModel(Module):
|
||||||
]
|
]
|
||||||
self.comb += Case(reads, cases)
|
self.comb += Case(reads, cases)
|
||||||
|
|
||||||
# connect banks to DFI phases (cmds, read datapath)
|
# Connect Banks to DFI phases (CMDs, Read datapath) ----------------------------------------
|
||||||
banks_read = Signal()
|
banks_read = Signal()
|
||||||
banks_read_data = Signal(data_width)
|
banks_read_data = Signal(data_width)
|
||||||
self.comb += [
|
self.comb += [
|
||||||
banks_read.eq(reduce(or_, [bank.read for bank in banks])),
|
banks_read.eq(reduce(or_, [bank.read for bank in banks])),
|
||||||
banks_read_data.eq(reduce(or_, [bank.read_data for bank in banks]))
|
banks_read_data.eq(reduce(or_, [bank.read_data for bank in banks]))
|
||||||
]
|
]
|
||||||
|
|
||||||
# simulate read latency
|
# Simulate read latency --------------------------------------------------------------------
|
||||||
for i in range(self.settings.read_latency):
|
for i in range(self.settings.read_latency):
|
||||||
new_banks_read = Signal()
|
new_banks_read = Signal()
|
||||||
new_banks_read_data = Signal(data_width)
|
new_banks_read_data = Signal(data_width)
|
||||||
self.sync += [
|
self.sync += [
|
||||||
new_banks_read.eq(banks_read),
|
new_banks_read.eq(banks_read),
|
||||||
new_banks_read_data.eq(banks_read_data)
|
new_banks_read_data.eq(banks_read_data)
|
||||||
]
|
]
|
||||||
banks_read = new_banks_read
|
banks_read = new_banks_read
|
||||||
banks_read_data = new_banks_read_data
|
banks_read_data = new_banks_read_data
|
||||||
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
|
|
Loading…
Reference in New Issue