Merge branch 'master' of https://github.com/enjoy-digital/litedram into AutoPrecharge
This commit is contained in:
commit
771ccfdc41
|
@ -31,8 +31,8 @@ class BankMachine(Module):
|
||||||
self.req = req = Record(cmd_layout(aw))
|
self.req = req = Record(cmd_layout(aw))
|
||||||
self.refresh_req = Signal()
|
self.refresh_req = Signal()
|
||||||
self.refresh_gnt = Signal()
|
self.refresh_gnt = Signal()
|
||||||
|
self.ras_allowed = ras_allowed = Signal()
|
||||||
self.cas_allowed = cas_allowed = Signal()
|
self.cas_allowed = cas_allowed = Signal()
|
||||||
self.activate_allowed = activate_allowed = Signal()
|
|
||||||
a = settings.geom.addressbits
|
a = settings.geom.addressbits
|
||||||
ba = settings.geom.bankbits
|
ba = settings.geom.bankbits
|
||||||
self.cmd = cmd = stream.Endpoint(cmd_request_rw_layout(a, ba))
|
self.cmd = cmd = stream.Endpoint(cmd_request_rw_layout(a, ba))
|
||||||
|
@ -99,6 +99,7 @@ class BankMachine(Module):
|
||||||
]
|
]
|
||||||
|
|
||||||
# Control and command generation FSM
|
# Control and command generation FSM
|
||||||
|
# Note: tRRD, tFAW, tCCD, tWTR timings are enforced by the multiplexer
|
||||||
self.submodules.fsm = fsm = FSM()
|
self.submodules.fsm = fsm = FSM()
|
||||||
fsm.act("REGULAR",
|
fsm.act("REGULAR",
|
||||||
If(self.refresh_req,
|
If(self.refresh_req,
|
||||||
|
@ -107,8 +108,6 @@ class BankMachine(Module):
|
||||||
If(has_openrow,
|
If(has_openrow,
|
||||||
If(hit,
|
If(hit,
|
||||||
If(cas_allowed,
|
If(cas_allowed,
|
||||||
# Note: write-to-read specification is enforced by
|
|
||||||
# multiplexer
|
|
||||||
cmd.valid.eq(1),
|
cmd.valid.eq(1),
|
||||||
If(cmd_buffer.source.we,
|
If(cmd_buffer.source.we,
|
||||||
req.wdata_ready.eq(cmd.ready),
|
req.wdata_ready.eq(cmd.ready),
|
||||||
|
@ -153,9 +152,9 @@ class BankMachine(Module):
|
||||||
fsm.act("ACTIVATE",
|
fsm.act("ACTIVATE",
|
||||||
sel_row_adr.eq(1),
|
sel_row_adr.eq(1),
|
||||||
track_open.eq(1),
|
track_open.eq(1),
|
||||||
cmd.valid.eq(activate_allowed),
|
cmd.valid.eq(ras_allowed),
|
||||||
cmd.is_cmd.eq(1),
|
cmd.is_cmd.eq(1),
|
||||||
If(cmd.ready & activate_allowed,
|
If(cmd.ready & ras_allowed,
|
||||||
NextState("TRCD")
|
NextState("TRCD")
|
||||||
),
|
),
|
||||||
cmd.ras.eq(1)
|
cmd.ras.eq(1)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from operator import or_, and_
|
from operator import add, or_, and_
|
||||||
|
|
||||||
from migen import *
|
from migen import *
|
||||||
from migen.genlib.roundrobin import *
|
from migen.genlib.roundrobin import *
|
||||||
|
@ -20,6 +20,7 @@ class _CommandChooser(Module):
|
||||||
|
|
||||||
a = len(requests[0].a)
|
a = len(requests[0].a)
|
||||||
ba = len(requests[0].ba)
|
ba = len(requests[0].ba)
|
||||||
|
|
||||||
# cas/ras/we are 0 when valid is inactive
|
# cas/ras/we are 0 when valid is inactive
|
||||||
self.cmd = cmd = stream.Endpoint(cmd_request_rw_layout(a, ba))
|
self.cmd = cmd = stream.Endpoint(cmd_request_rw_layout(a, ba))
|
||||||
|
|
||||||
|
@ -63,6 +64,19 @@ class _CommandChooser(Module):
|
||||||
)
|
)
|
||||||
self.comb += arbiter.ce.eq(cmd.ready)
|
self.comb += arbiter.ce.eq(cmd.ready)
|
||||||
|
|
||||||
|
# helpers
|
||||||
|
def accept(self):
|
||||||
|
return self.cmd.valid & self.cmd.ready
|
||||||
|
|
||||||
|
def activate(self):
|
||||||
|
return self.cmd.ras & ~self.cmd.cas & ~self.cmd.we
|
||||||
|
|
||||||
|
def write(self):
|
||||||
|
return self.cmd.is_write
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
return self.cmd.is_read
|
||||||
|
|
||||||
|
|
||||||
class _Steerer(Module):
|
class _Steerer(Module):
|
||||||
def __init__(self, commands, dfi):
|
def __init__(self, commands, dfi):
|
||||||
|
@ -102,6 +116,53 @@ class _Steerer(Module):
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class tXXDController(Module):
|
||||||
|
def __init__(self, txxd):
|
||||||
|
self.valid = valid = Signal()
|
||||||
|
self.ready = ready = Signal(reset=1)
|
||||||
|
ready.attr.add("no_retiming")
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
if txxd is not None:
|
||||||
|
count = Signal(max=txxd + 1)
|
||||||
|
self.sync += \
|
||||||
|
If(valid,
|
||||||
|
count.eq(txxd - 1),
|
||||||
|
If((txxd - 1) == 0,
|
||||||
|
ready.eq(1)
|
||||||
|
).Else(
|
||||||
|
ready.eq(0)
|
||||||
|
)
|
||||||
|
).Elif(~ready,
|
||||||
|
count.eq(count - 1),
|
||||||
|
If(count == 1, ready.eq(1))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class tFAWController(Module):
|
||||||
|
def __init__(self, tfaw):
|
||||||
|
self.valid = valid = Signal()
|
||||||
|
self.ready = ready = Signal(reset=1)
|
||||||
|
ready.attr.add("no_retiming")
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
if tfaw is not None:
|
||||||
|
count = Signal(max=tfaw)
|
||||||
|
window = Signal(tfaw)
|
||||||
|
self.sync += window.eq(Cat(valid, window))
|
||||||
|
self.comb += reduce(add, [window[i] for i in range(tfaw)])
|
||||||
|
self.sync += \
|
||||||
|
If(count < 4,
|
||||||
|
If(count == 3,
|
||||||
|
ready.eq(~valid)
|
||||||
|
).Else(
|
||||||
|
ready.eq(1)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Multiplexer(Module, AutoCSR):
|
class Multiplexer(Module, AutoCSR):
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
settings,
|
settings,
|
||||||
|
@ -112,8 +173,8 @@ class Multiplexer(Module, AutoCSR):
|
||||||
with_bandwidth=False):
|
with_bandwidth=False):
|
||||||
assert(settings.phy.nphases == len(dfi.phases))
|
assert(settings.phy.nphases == len(dfi.phases))
|
||||||
|
|
||||||
# Forward Declares
|
ras_allowed = Signal(reset=1)
|
||||||
activate_allowed = Signal(reset=1)
|
cas_allowed = Signal(reset=1)
|
||||||
|
|
||||||
# Command choosing
|
# Command choosing
|
||||||
requests = [bm.cmd for bm in bank_machines]
|
requests = [bm.cmd for bm in bank_machines]
|
||||||
|
@ -122,7 +183,7 @@ class Multiplexer(Module, AutoCSR):
|
||||||
if settings.phy.nphases == 1:
|
if settings.phy.nphases == 1:
|
||||||
self.comb += [
|
self.comb += [
|
||||||
choose_cmd.want_cmds.eq(1),
|
choose_cmd.want_cmds.eq(1),
|
||||||
choose_cmd.want_activates.eq(activate_allowed),
|
choose_cmd.want_activates.eq(ras_allowed),
|
||||||
choose_req.want_cmds.eq(1)
|
choose_req.want_cmds.eq(1)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -135,66 +196,32 @@ class Multiplexer(Module, AutoCSR):
|
||||||
steerer = _Steerer(commands, dfi)
|
steerer = _Steerer(commands, dfi)
|
||||||
self.submodules += steerer
|
self.submodules += steerer
|
||||||
|
|
||||||
# tRRD Command Timing
|
# tRRD timing (Row to Row delay)
|
||||||
trrd = settings.timing.tRRD
|
self.trrdcon = trrdcon = tXXDController(settings.timing.tRRD)
|
||||||
trrd_allowed = Signal(reset=1)
|
self.comb += trrdcon.valid.eq(choose_cmd.accept() & choose_cmd.activate())
|
||||||
is_act_cmd = Signal()
|
|
||||||
self.comb += is_act_cmd.eq(choose_cmd.cmd.ras & ~choose_cmd.cmd.cas & ~choose_cmd.cmd.we)
|
|
||||||
if trrd is not None:
|
|
||||||
trrd_count = Signal(max=trrd+1)
|
|
||||||
self.sync += \
|
|
||||||
If(choose_cmd.cmd.ready & choose_cmd.cmd.valid & is_act_cmd,
|
|
||||||
trrd_count.eq(trrd-1)
|
|
||||||
).Elif(~activate_allowed,
|
|
||||||
trrd_count.eq(trrd_count-1)
|
|
||||||
)
|
|
||||||
self.comb += trrd_allowed.eq(trrd_count == 0)
|
|
||||||
|
|
||||||
# tFAW Command Timing
|
# tFAW timing (Four Activate Window)
|
||||||
tfaw = settings.timing.tFAW
|
self.tfawcon = tfawcon = tFAWController(settings.timing.tFAW)
|
||||||
tfaw_allowed = Signal(reset=1)
|
self.comb += tfawcon.valid.eq(choose_cmd.accept() & choose_cmd.activate())
|
||||||
if tfaw is not None:
|
|
||||||
tfaw_count = Signal(max=tfaw)
|
|
||||||
tfaw_window = Signal(tfaw)
|
|
||||||
self.sync += tfaw_window.eq(Cat((is_act_cmd & choose_cmd.cmd.ready & choose_cmd.cmd.valid), tfaw_window))
|
|
||||||
for i in range(tfaw):
|
|
||||||
next_tfaw_count = Signal(max=tfaw)
|
|
||||||
self.comb += next_tfaw_count.eq(tfaw_count + tfaw_window[i])
|
|
||||||
tfaw_count = next_tfaw_count
|
|
||||||
self.comb += If(tfaw_count >=4, tfaw_allowed.eq(0))
|
|
||||||
|
|
||||||
self.comb += activate_allowed.eq(trrd_allowed & tfaw_allowed)
|
# RAS control
|
||||||
self.comb += [bm.activate_allowed.eq(activate_allowed) for bm in bank_machines]
|
self.comb += ras_allowed.eq(trrdcon.ready & tfawcon.ready)
|
||||||
|
self.comb += [bm.ras_allowed.eq(ras_allowed) for bm in bank_machines]
|
||||||
|
|
||||||
# CAS to CAS
|
# tCCD timing (Column to Column delay)
|
||||||
cas = choose_req.cmd.valid & choose_req.cmd.ready & (choose_req.cmd.is_read | choose_req.cmd.is_write)
|
self.tccdcon = tccdcon = tXXDController(settings.timing.tCCD)
|
||||||
cas_allowed = Signal(reset=1)
|
self.comb += tccdcon.valid.eq(choose_cmd.accept() & (choose_cmd.write() | choose_cmd.read()))
|
||||||
tccd = settings.timing.tCCD
|
|
||||||
if tccd is not None:
|
# CAS control
|
||||||
cas_count = Signal(max=tccd+1)
|
self.comb += cas_allowed.eq(tccdcon.ready)
|
||||||
self.sync += \
|
|
||||||
If(cas,
|
|
||||||
cas_count.eq(tccd-1)
|
|
||||||
).Elif(~cas_allowed,
|
|
||||||
cas_count.eq(cas_count-1)
|
|
||||||
)
|
|
||||||
self.comb += cas_allowed.eq(cas_count == 0)
|
|
||||||
self.comb += [bm.cas_allowed.eq(cas_allowed) for bm in bank_machines]
|
self.comb += [bm.cas_allowed.eq(cas_allowed) for bm in bank_machines]
|
||||||
|
|
||||||
# Write to Read
|
# tWTR timing (Write to Read delay)
|
||||||
wtr_allowed = Signal(reset=1)
|
self.twtrcon = twtrcon = tXXDController(
|
||||||
twtr = settings.timing.tWTR
|
settings.timing.tWTR +
|
||||||
if tccd is not None:
|
# tCCD must be added since tWTR begins after the transfer is complete
|
||||||
twtr += settings.timing.tCCD # tWTR begins after the transfer is complete, tCCD accounts for this
|
settings.timing.tCCD if settings.timing.tCCD is not None else 0)
|
||||||
wtr_count = Signal(max=twtr+1)
|
self.comb += twtrcon.valid.eq(choose_req.accept() & choose_req.write())
|
||||||
self.sync += [
|
|
||||||
If(choose_req.cmd.ready & choose_req.cmd.valid & choose_req.cmd.is_write,
|
|
||||||
wtr_count.eq(twtr-1)
|
|
||||||
).Elif(wtr_count != 0,
|
|
||||||
wtr_count.eq(wtr_count-1)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
self.comb += wtr_allowed.eq(wtr_count == 0)
|
|
||||||
|
|
||||||
# Read/write turnaround
|
# Read/write turnaround
|
||||||
read_available = Signal()
|
read_available = Signal()
|
||||||
|
@ -265,8 +292,8 @@ class Multiplexer(Module, AutoCSR):
|
||||||
fsm.act("READ",
|
fsm.act("READ",
|
||||||
read_time_en.eq(1),
|
read_time_en.eq(1),
|
||||||
choose_req.want_reads.eq(1),
|
choose_req.want_reads.eq(1),
|
||||||
choose_cmd.want_activates.eq(activate_allowed),
|
choose_cmd.want_activates.eq(ras_allowed),
|
||||||
choose_cmd.cmd.ready.eq(~is_act_cmd | activate_allowed),
|
choose_cmd.cmd.ready.eq(~choose_cmd.activate() | ras_allowed),
|
||||||
choose_req.cmd.ready.eq(1),
|
choose_req.cmd.ready.eq(1),
|
||||||
steerer_sel(steerer, "read"),
|
steerer_sel(steerer, "read"),
|
||||||
If(write_available,
|
If(write_available,
|
||||||
|
@ -282,8 +309,8 @@ class Multiplexer(Module, AutoCSR):
|
||||||
fsm.act("WRITE",
|
fsm.act("WRITE",
|
||||||
write_time_en.eq(1),
|
write_time_en.eq(1),
|
||||||
choose_req.want_writes.eq(1),
|
choose_req.want_writes.eq(1),
|
||||||
choose_cmd.want_activates.eq(activate_allowed),
|
choose_cmd.want_activates.eq(ras_allowed),
|
||||||
choose_cmd.cmd.ready.eq(~is_act_cmd | activate_allowed),
|
choose_cmd.cmd.ready.eq(~choose_cmd.activate() | ras_allowed),
|
||||||
choose_req.cmd.ready.eq(1),
|
choose_req.cmd.ready.eq(1),
|
||||||
steerer_sel(steerer, "write"),
|
steerer_sel(steerer, "write"),
|
||||||
If(read_available,
|
If(read_available,
|
||||||
|
@ -298,12 +325,12 @@ class Multiplexer(Module, AutoCSR):
|
||||||
fsm.act("REFRESH",
|
fsm.act("REFRESH",
|
||||||
steerer.sel[0].eq(STEER_REFRESH),
|
steerer.sel[0].eq(STEER_REFRESH),
|
||||||
refresher.cmd.ready.eq(1),
|
refresher.cmd.ready.eq(1),
|
||||||
If(refresher.cmd.last,
|
If(~refresher.cmd.valid,
|
||||||
NextState("READ")
|
NextState("READ")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
fsm.act("WTR",
|
fsm.act("WTR",
|
||||||
If(wtr_allowed,
|
If(twtrcon.ready,
|
||||||
NextState("READ")
|
NextState("READ")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -45,14 +45,16 @@ class Refresher(Module):
|
||||||
self.comb += self.timer.wait.eq(settings.with_refresh & ~self.timer.done)
|
self.comb += self.timer.wait.eq(settings.with_refresh & ~self.timer.done)
|
||||||
|
|
||||||
# Control FSM
|
# Control FSM
|
||||||
|
cmd_valid = Signal()
|
||||||
self.submodules.fsm = fsm = FSM()
|
self.submodules.fsm = fsm = FSM()
|
||||||
fsm.act("IDLE",
|
fsm.act("IDLE",
|
||||||
If(self.timer.done,
|
If(self.timer.done,
|
||||||
|
cmd_valid.eq(1),
|
||||||
NextState("WAIT_GRANT")
|
NextState("WAIT_GRANT")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
fsm.act("WAIT_GRANT",
|
fsm.act("WAIT_GRANT",
|
||||||
cmd.valid.eq(1),
|
cmd_valid.eq(1),
|
||||||
If(cmd.ready,
|
If(cmd.ready,
|
||||||
seq_start.eq(1),
|
seq_start.eq(1),
|
||||||
NextState("WAIT_SEQ")
|
NextState("WAIT_SEQ")
|
||||||
|
@ -60,9 +62,8 @@ class Refresher(Module):
|
||||||
)
|
)
|
||||||
fsm.act("WAIT_SEQ",
|
fsm.act("WAIT_SEQ",
|
||||||
If(seq_done,
|
If(seq_done,
|
||||||
cmd.last.eq(1),
|
cmd_valid.eq(0),
|
||||||
NextState("IDLE")
|
NextState("IDLE")
|
||||||
).Else(
|
|
||||||
cmd.valid.eq(1)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
self.sync += cmd.valid.eq(cmd_valid)
|
||||||
|
|
Loading…
Reference in New Issue