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 *
# RefreshSequencer ---------------------------------------------------------------------------------
# RefreshExecuter ----------------------------------------------------------------------------------
class RefreshSequencer(Module):
"""Refresh Sequencer
class RefreshExecuter(Module):
"""Refresh Executer
Execute the refresh sequence to the DRAM:
- 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 -------------------------------------------------------------------------------------
class RefreshTimer(Module):
@ -87,6 +116,31 @@ class RefreshTimer(Module):
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 ----------------------------------------------------------------------------------------
class Refresher(Module):
@ -103,7 +157,7 @@ class Refresher(Module):
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
babits = settings.geom.bankbits + log2_int(settings.phy.nranks)
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.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 ------------------------------------------------------------------------
sequencer = RefreshSequencer(cmd, settings.timing.tRP, settings.timing.tRFC)
sequencer = RefreshSequencer(cmd, settings.timing.tRP, settings.timing.tRFC, n=n)
self.submodules.sequencer = sequencer
# Refresh FSM ------------------------------------------------------------------------------
self.submodules.fsm = fsm = FSM()
fsm.act("IDLE",
If(settings.with_refresh,
# Wait periodic Timer pulse
If(timer.done,
# Wait refresh accumulator
If(accum.req_o,
NextState("WAIT-GRANT")
)
)

View file

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