180 lines
6.2 KiB
Python
180 lines
6.2 KiB
Python
"""LiteDRAM BankMachine (Rows/Columns management)."""
|
|
|
|
import math
|
|
|
|
from migen import *
|
|
|
|
from litex.soc.interconnect import stream
|
|
|
|
from litedram.common import *
|
|
from litedram.core.multiplexer import *
|
|
|
|
|
|
class _AddressSlicer:
|
|
def __init__(self, colbits, address_align):
|
|
self.colbits = colbits
|
|
self.address_align = address_align
|
|
|
|
def row(self, address):
|
|
split = self.colbits - self.address_align
|
|
return address[split:]
|
|
|
|
def col(self, address):
|
|
split = self.colbits - self.address_align
|
|
return Cat(Replicate(0, self.address_align), address[:split])
|
|
|
|
|
|
class BankMachine(Module):
|
|
def __init__(self, n, address_width, address_align, nranks, settings):
|
|
self.req = req = Record(cmd_layout(address_width))
|
|
self.refresh_req = refresh_req = Signal()
|
|
self.refresh_gnt = refresh_gnt = Signal()
|
|
|
|
a = settings.geom.addressbits
|
|
ba = settings.geom.bankbits + log2_int(nranks)
|
|
self.cmd = cmd = stream.Endpoint(cmd_request_rw_layout(a, ba))
|
|
|
|
# # #
|
|
|
|
auto_precharge = Signal()
|
|
|
|
# Command buffer
|
|
cmd_buffer_layout = [("we", 1), ("addr", len(req.addr))]
|
|
cmd_buffer_lookahead = stream.SyncFIFO(
|
|
cmd_buffer_layout, settings.cmd_buffer_depth,
|
|
buffered=settings.cmd_buffer_buffered)
|
|
cmd_buffer = stream.Buffer(cmd_buffer_layout) # 1 depth buffer to detect row change
|
|
self.submodules += cmd_buffer_lookahead, cmd_buffer
|
|
self.comb += [
|
|
req.connect(cmd_buffer_lookahead.sink, keep={"valid", "ready", "we", "addr"}),
|
|
cmd_buffer_lookahead.source.connect(cmd_buffer.sink),
|
|
cmd_buffer.source.ready.eq(req.wdata_ready | req.rdata_valid),
|
|
req.lock.eq(cmd_buffer_lookahead.source.valid | cmd_buffer.source.valid),
|
|
]
|
|
|
|
slicer = _AddressSlicer(settings.geom.colbits, address_align)
|
|
|
|
# Row tracking
|
|
row = Signal(settings.geom.rowbits)
|
|
row_opened = Signal()
|
|
row_hit = Signal()
|
|
row_open = Signal()
|
|
row_close = Signal()
|
|
self.comb += row_hit.eq(row == slicer.row(cmd_buffer.source.addr))
|
|
self.sync += \
|
|
If(row_close,
|
|
row_opened.eq(0)
|
|
).Elif(row_open,
|
|
row_opened.eq(1),
|
|
row.eq(slicer.row(cmd_buffer.source.addr))
|
|
)
|
|
|
|
# Address generation
|
|
row_col_n_addr_sel = Signal()
|
|
self.comb += [
|
|
cmd.ba.eq(n),
|
|
If(row_col_n_addr_sel,
|
|
cmd.a.eq(slicer.row(cmd_buffer.source.addr))
|
|
).Else(
|
|
cmd.a.eq((auto_precharge << 10) | slicer.col(cmd_buffer.source.addr))
|
|
)
|
|
]
|
|
|
|
# tWTP (write-to-precharge) controller
|
|
write_latency = math.ceil(settings.phy.cwl / settings.phy.nphases)
|
|
precharge_time = write_latency + settings.timing.tWR + settings.timing.tCCD # AL=0
|
|
self.submodules.twtpcon = twtpcon = tXXDController(precharge_time)
|
|
self.comb += twtpcon.valid.eq(cmd.valid & cmd.ready & cmd.is_write)
|
|
|
|
# tRC (activate-activate) controller
|
|
self.submodules.trccon = trccon = tXXDController(settings.timing.tRC)
|
|
self.comb += trccon.valid.eq(cmd.valid & cmd.ready & row_open)
|
|
|
|
# tRAS (activate-precharge) controller
|
|
self.submodules.trascon = trascon = tXXDController(settings.timing.tRAS)
|
|
self.comb += trascon.valid.eq(cmd.valid & cmd.ready & row_open)
|
|
|
|
# Auto Precharge generation
|
|
if settings.with_auto_precharge:
|
|
self.comb += \
|
|
If(cmd_buffer_lookahead.source.valid & cmd_buffer.source.valid,
|
|
If(slicer.row(cmd_buffer_lookahead.source.addr) !=
|
|
slicer.row(cmd_buffer.source.addr),
|
|
auto_precharge.eq(row_close == 0)
|
|
)
|
|
)
|
|
|
|
# Control and command generation FSM
|
|
# Note: tRRD, tFAW, tCCD, tWTR timings are enforced by the multiplexer
|
|
self.submodules.fsm = fsm = FSM()
|
|
fsm.act("REGULAR",
|
|
If(refresh_req,
|
|
NextState("REFRESH")
|
|
).Elif(cmd_buffer.source.valid,
|
|
If(row_opened,
|
|
If(row_hit,
|
|
cmd.valid.eq(1),
|
|
If(cmd_buffer.source.we,
|
|
req.wdata_ready.eq(cmd.ready),
|
|
cmd.is_write.eq(1),
|
|
cmd.we.eq(1),
|
|
).Else(
|
|
req.rdata_valid.eq(cmd.ready),
|
|
cmd.is_read.eq(1)
|
|
),
|
|
cmd.cas.eq(1),
|
|
If(cmd.ready & auto_precharge,
|
|
NextState("AUTOPRECHARGE")
|
|
)
|
|
).Else(
|
|
NextState("PRECHARGE")
|
|
)
|
|
).Else(
|
|
NextState("ACTIVATE")
|
|
)
|
|
)
|
|
)
|
|
fsm.act("PRECHARGE",
|
|
# Note: we are presenting the column address, A10 is always low
|
|
If(twtpcon.ready & trascon.ready,
|
|
cmd.valid.eq(1),
|
|
If(cmd.ready,
|
|
NextState("TRP")
|
|
),
|
|
cmd.ras.eq(1),
|
|
cmd.we.eq(1),
|
|
cmd.is_cmd.eq(1)
|
|
),
|
|
row_close.eq(1)
|
|
)
|
|
fsm.act("AUTOPRECHARGE",
|
|
If(twtpcon.ready & trascon.ready,
|
|
NextState("TRP")
|
|
),
|
|
row_close.eq(1)
|
|
)
|
|
fsm.act("ACTIVATE",
|
|
If(trccon.ready,
|
|
row_col_n_addr_sel.eq(1),
|
|
row_open.eq(1),
|
|
cmd.valid.eq(1),
|
|
cmd.is_cmd.eq(1),
|
|
If(cmd.ready,
|
|
NextState("TRCD")
|
|
),
|
|
cmd.ras.eq(1)
|
|
)
|
|
)
|
|
fsm.act("REFRESH",
|
|
If(twtpcon.ready,
|
|
refresh_gnt.eq(1),
|
|
),
|
|
row_close.eq(1),
|
|
cmd.is_cmd.eq(1),
|
|
If(~refresh_req,
|
|
NextState("REGULAR")
|
|
)
|
|
)
|
|
fsm.delayed_enter("TRP", "ACTIVATE", settings.timing.tRP - 1)
|
|
fsm.delayed_enter("TRCD", "REGULAR", settings.timing.tRCD - 1)
|