core/refresher: add capability to accumulate N refreshs and execute the N refreshs together

Being able to accumulate refreshs allow reducing the number of interruptions to the Controller from 1 every tREFI cycles to 1 every N*tREFI cycles.
This commit is contained in:
Florent Kermarrec 2019-08-14 09:57:24 +02:00
parent 818c4ca9db
commit 338d18dba0
2 changed files with 74 additions and 10 deletions

View file

@ -11,10 +11,10 @@ from litex.soc.interconnect import stream
from litedram.core.multiplexer import * from litedram.core.multiplexer import *
# RefreshSequencer --------------------------------------------------------------------------------- # RefreshExecuter ----------------------------------------------------------------------------------
class RefreshSequencer(Module): class RefreshExecuter(Module):
"""Refresh Sequencer """Refresh Executer
Execute the refresh sequence to the DRAM: Execute the refresh sequence to the DRAM:
- Send a "Precharge All" command - Send a "Precharge All" command
@ -48,6 +48,35 @@ class RefreshSequencer(Module):
]) ])
] ]
# RefreshSequencer ---------------------------------------------------------------------------------
class RefreshSequencer(Module):
"""Refresh Sequencer
Sequence N refreshs to the DRAM.
"""
def __init__(self, cmd, trp, trfc, n=1):
self.start = Signal()
self.done = Signal()
# # #
executer = RefreshExecuter(cmd, trp, trfc)
self.submodules += executer
count = Signal(bits_for(n), reset=n-1)
self.sync += [
If(self.start,
count.eq(count.reset)
).Elif(executer.done,
If(count != 0,
count.eq(count - 1)
)
)
]
self.comb += executer.start.eq(self.start | (count != 0))
self.comb += self.done.eq(executer.done & (count == 0))
# RefreshTimer ------------------------------------------------------------------------------------- # RefreshTimer -------------------------------------------------------------------------------------
class RefreshTimer(Module): class RefreshTimer(Module):
@ -87,6 +116,31 @@ class RefreshTimer(Module):
self.count.eq(count) self.count.eq(count)
] ]
# RefreshAccumulator -------------------------------------------------------------------------------
class RefreshAccumulator(Module):
"""Refresh Accumulator
Accumulate N Refresh requests and generate a request when N is reached.
"""
def __init__(self, n=1):
self.req_i = Signal()
self.req_o = Signal()
# # #
count = Signal(bits_for(n), reset=n-1)
self.sync += [
self.req_o.eq(0),
If(self.req_i,
count.eq(count - 1),
If(count == 0,
count.eq(count.reset),
self.req_o.eq(1)
)
)
]
# Refresher ---------------------------------------------------------------------------------------- # Refresher ----------------------------------------------------------------------------------------
class Refresher(Module): class Refresher(Module):
@ -103,7 +157,7 @@ class Refresher(Module):
transactions are done, the Refresher can execute the refresh Sequence and release the Controller. transactions are done, the Refresher can execute the refresh Sequence and release the Controller.
""" """
def __init__(self, settings): def __init__(self, settings, n=1):
abits = settings.geom.addressbits abits = settings.geom.addressbits
babits = settings.geom.bankbits + log2_int(settings.phy.nranks) babits = settings.geom.bankbits + log2_int(settings.phy.nranks)
self.cmd = cmd = stream.Endpoint(cmd_request_rw_layout(a=abits, ba=babits)) self.cmd = cmd = stream.Endpoint(cmd_request_rw_layout(a=abits, ba=babits))
@ -115,16 +169,21 @@ class Refresher(Module):
self.submodules.timer = timer self.submodules.timer = timer
self.comb += self.timer.wait.eq(~self.timer.done) self.comb += self.timer.wait.eq(~self.timer.done)
# Refresh Accumulator ----------------------------------------------------------------------
accum = RefreshAccumulator(n=n)
self.submodules.accum = accum
self.comb += accum.req_i.eq(self.timer.done)
# Refresh Sequencer ------------------------------------------------------------------------ # Refresh Sequencer ------------------------------------------------------------------------
sequencer = RefreshSequencer(cmd, settings.timing.tRP, settings.timing.tRFC) sequencer = RefreshSequencer(cmd, settings.timing.tRP, settings.timing.tRFC, n=n)
self.submodules.sequencer = sequencer self.submodules.sequencer = sequencer
# Refresh FSM ------------------------------------------------------------------------------ # Refresh FSM ------------------------------------------------------------------------------
self.submodules.fsm = fsm = FSM() self.submodules.fsm = fsm = FSM()
fsm.act("IDLE", fsm.act("IDLE",
If(settings.with_refresh, If(settings.with_refresh,
# Wait periodic Timer pulse # Wait refresh accumulator
If(timer.done, If(accum.req_o,
NextState("WAIT-GRANT") NextState("WAIT-GRANT")
) )
) )

View file

@ -66,7 +66,7 @@ class TestRefresh(unittest.TestCase):
for i in range(1, 32): for i in range(1, 32):
self.refresh_timer_test(i) self.refresh_timer_test(i)
def test_refresher(self): def refresher_test(self, n):
class Obj: pass class Obj: pass
settings = Obj() settings = Obj()
settings.with_refresh = True settings.with_refresh = True
@ -93,9 +93,14 @@ class TestRefresh(unittest.TestCase):
while (yield dut.cmd.valid) == 0: while (yield dut.cmd.valid) == 0:
cmd_valid_gap += 1 cmd_valid_gap += 1
yield yield
if cmd_valid_gap != settings.timing.tREFI: if cmd_valid_gap != n*settings.timing.tREFI:
print(cmd_valid_gap)
dut.errors += 1 dut.errors += 1
dut = Refresher(settings) dut = Refresher(settings, n=n)
run_simulation(dut, [generator(dut)]) run_simulation(dut, [generator(dut)])
self.assertEqual(dut.errors, 0) self.assertEqual(dut.errors, 0)
def test_refresher(self):
for i in [1, 2, 4, 8]:
self.refresher_test(n=i)