lpddr5: sim: fix non-syncronized pipe in simulation
Signed-off-by: Alessandro Comodi <acomodi@antmicro.com>
This commit is contained in:
parent
50ba27eb4c
commit
d7e2c82795
|
@ -18,6 +18,8 @@ from litedram.common import TappedDelayLine
|
|||
from litedram.phy.utils import edge, delayed
|
||||
from litedram.phy.sim_utils import SimLogger, Timing, PulseTiming, log_level_getter
|
||||
|
||||
from litedram.phy.lpddr5.basephy import get_frange
|
||||
|
||||
|
||||
CMD_INFO_LAYOUT = [
|
||||
("we", 1),
|
||||
|
@ -33,7 +35,7 @@ gtkw_dbg = {}
|
|||
class LPDDR5Sim(Module, AutoCSR):
|
||||
"""LPDDR5 DRAM simulation
|
||||
"""
|
||||
def __init__(self, pads, *, ck_freq, log_level, logger_kwargs, check_timings=True):
|
||||
def __init__(self, pads, *, ck_freq, wck_ck_ratio, log_level, logger_kwargs, check_timings=True):
|
||||
log_level = log_level_getter(log_level)
|
||||
|
||||
self.clock_domains.cd_ck = ClockDomain(reset_less=True)
|
||||
|
@ -61,6 +63,8 @@ class LPDDR5Sim(Module, AutoCSR):
|
|||
data = DataSim(pads, cmd_info,
|
||||
latency_ready = cmd.data_timer.ready_p,
|
||||
mode_regs = cmd.mode_regs,
|
||||
ck_freq = ck_freq,
|
||||
wck_ck_ratio = wck_ck_ratio,
|
||||
logger_kwargs = logger_kwargs,
|
||||
log_level = log_level
|
||||
)
|
||||
|
@ -222,6 +226,7 @@ class ModeRegisters(Module, AutoCSR):
|
|||
],
|
||||
)
|
||||
|
||||
|
||||
class Sync(list):
|
||||
# Helper for combining comb and sync
|
||||
def __init__(self, arg):
|
||||
|
@ -701,7 +706,7 @@ class CommandsSim(Module, AutoCSR):
|
|||
|
||||
|
||||
class DataSim(Module, AutoCSR):
|
||||
def __init__(self, pads, cmd_info, latency_ready, mode_regs, *, log_level, logger_kwargs, nrows=32768, ncols=1024, nbanks=16):
|
||||
def __init__(self, pads, cmd_info, latency_ready, mode_regs, ck_freq, wck_ck_ratio, *, log_level, logger_kwargs, nrows=32768, ncols=1024, nbanks=16):
|
||||
self.submodules.log = SimLogger(log_level=log_level("data"), **logger_kwargs)
|
||||
|
||||
# CommandsSim produces the data required for handling a data command via cmd_info endpoint.
|
||||
|
@ -709,17 +714,48 @@ class DataSim(Module, AutoCSR):
|
|||
# and store the information in a FIFO, so that it is possible to pipeline data commands.
|
||||
self.submodules.cmd_buf = stream.PipeValid(CMD_INFO_LAYOUT)
|
||||
gtkw_dbg["cmd_buf"] = self.cmd_buf
|
||||
|
||||
# The CommandsSim data handling generated command is clocked under the system clock domain.
|
||||
# Instead, the DataSim is clocked in the wck clock domain.
|
||||
# Given that, with a dynamic wck regime, wck needs to get through a preamble phase where it
|
||||
# is not active for a certain amount of clock cycles, the command buffer pipeline may not
|
||||
# be able to see changes in the data commands generated by CommandSim.
|
||||
#
|
||||
# The following logic verifies whether a delay needs to be added and adds it accordingly to
|
||||
# the data command, so that the pipe stream can intercept the various commands from the ck
|
||||
# domain correctly.
|
||||
twck = 1 / (ck_freq * wck_ck_ratio)
|
||||
frange = get_frange(twck, wck_ck_ratio).for_set(wl_set="A", rl_set=0)
|
||||
taps = max(1, max(frange.t_wckenl_wr, frange.t_wckenl_rd) + frange.t_wckpre_static) - 1
|
||||
|
||||
def delay_cmd_info(signals):
|
||||
for signal in signals:
|
||||
tapped_delay = ClockDomainsRenamer("ck")(TappedDelayLine(getattr(cmd_info, signal), ntaps=taps))
|
||||
setattr(self.submodules, f"{signal}_tap", tapped_delay)
|
||||
|
||||
self.comb += [
|
||||
getattr(self.cmd_buf.sink, signal).eq(reduce(or_, getattr(self, f"{signal}_tap").taps[0:taps])),
|
||||
]
|
||||
|
||||
if taps > 0:
|
||||
delay_cmd_info([tup[0] for tup in CMD_INFO_LAYOUT] + ["valid", "ready", "first", "last"])
|
||||
|
||||
else:
|
||||
self.comb += [
|
||||
cmd_info.connect(self.cmd_buf.sink, omit={"ready", "valid"}),
|
||||
# to latch a command only once we use an edge here, which we can do as there is no way
|
||||
# for 2 valid commands cycle-by-cycle
|
||||
self.cmd_buf.sink.valid.eq(edge(self, cmd_info.valid)),
|
||||
# if for some reason buffer hasn't been cleared, then we have an error
|
||||
If(self.cmd_buf.sink.valid & ~self.cmd_buf.sink.ready,
|
||||
self.log.error("Simulator internal error: CMD-to-DATA overflow")
|
||||
),
|
||||
]
|
||||
|
||||
self.comb += [
|
||||
cmd_info.connect(self.cmd_buf.sink, omit={"ready", "valid"}),
|
||||
cmd_info.ready.eq(1),
|
||||
# to latch a command only once we use an edge here, which we can do as there is no way
|
||||
# for 2 valid commands cycle-by-cycle
|
||||
self.cmd_buf.sink.valid.eq(edge(self, cmd_info.valid)),
|
||||
# if for some reason buffer hasn't been cleared, then we have an error
|
||||
If(self.cmd_buf.sink.valid & ~self.cmd_buf.sink.ready,
|
||||
self.log.error("Simulator internal error: CMD-to-DATA overflow")
|
||||
),
|
||||
]
|
||||
|
||||
cmd = self.cmd_buf.source
|
||||
|
||||
# DRAM Memory storage
|
||||
|
|
|
@ -168,6 +168,7 @@ class SimSoC(SoCCore):
|
|||
self.submodules.lpddr5sim = LPDDR5Sim(
|
||||
pads = self.ddrphy.pads,
|
||||
ck_freq = dfi_converter_ratio*sys_clk_freq,
|
||||
wck_ck_ratio = wck_ck_ratio,
|
||||
check_timings = not disable_delay,
|
||||
log_level = log_level,
|
||||
logger_kwargs = dict(
|
||||
|
|
Loading…
Reference in New Issue