2012-03-14 13:26:05 -04:00
|
|
|
from migen.fhdl.structure import *
|
2013-03-10 14:32:38 -04:00
|
|
|
from migen.fhdl.module import Module
|
2013-02-24 06:31:00 -05:00
|
|
|
from migen.genlib.roundrobin import *
|
|
|
|
from migen.genlib.misc import optree
|
|
|
|
from migen.genlib.fsm import FSM
|
2012-03-14 13:26:05 -04:00
|
|
|
|
2012-03-15 15:29:26 -04:00
|
|
|
class CommandRequest:
|
2012-03-17 19:12:03 -04:00
|
|
|
def __init__(self, a, ba):
|
2012-11-29 17:38:04 -05:00
|
|
|
self.a = Signal(a)
|
|
|
|
self.ba = Signal(ba)
|
2012-03-15 15:29:26 -04:00
|
|
|
self.cas_n = Signal(reset=1)
|
|
|
|
self.ras_n = Signal(reset=1)
|
|
|
|
self.we_n = Signal(reset=1)
|
|
|
|
|
|
|
|
class CommandRequestRW(CommandRequest):
|
2012-03-17 19:12:03 -04:00
|
|
|
def __init__(self, a, ba, tagbits):
|
|
|
|
CommandRequest.__init__(self, a, ba)
|
2012-03-15 15:29:26 -04:00
|
|
|
self.stb = Signal()
|
|
|
|
self.ack = Signal()
|
|
|
|
self.is_read = Signal()
|
|
|
|
self.is_write = Signal()
|
2012-11-29 17:38:04 -05:00
|
|
|
self.tag = Signal(tagbits)
|
2012-03-15 15:29:26 -04:00
|
|
|
|
2013-03-10 14:32:38 -04:00
|
|
|
class _CommandChooser(Module):
|
2012-03-18 17:11:01 -04:00
|
|
|
def __init__(self, requests, tagbits):
|
|
|
|
self.want_reads = Signal()
|
|
|
|
self.want_writes = Signal()
|
|
|
|
# NB: cas_n/ras_n/we_n are 1 when stb is inactive
|
2013-03-10 14:32:38 -04:00
|
|
|
self.cmd = CommandRequestRW(len(requests[0].a), len(requests[0].ba), tagbits)
|
2012-03-18 17:11:01 -04:00
|
|
|
|
2013-03-10 14:32:38 -04:00
|
|
|
###
|
|
|
|
|
|
|
|
rr = RoundRobin(len(requests), SP_CE)
|
|
|
|
self.submodules += rr
|
2012-03-18 17:11:01 -04:00
|
|
|
|
2013-03-10 14:32:38 -04:00
|
|
|
self.comb += [rr.request[i].eq(req.stb & ((req.is_read == self.want_reads) | (req.is_write == self.want_writes)))
|
|
|
|
for i, req in enumerate(requests)]
|
2012-03-18 17:11:01 -04:00
|
|
|
|
|
|
|
stb = Signal()
|
2013-03-10 14:32:38 -04:00
|
|
|
self.comb += stb.eq(Array(req.stb for req in requests)[rr.grant])
|
2012-07-13 12:05:26 -04:00
|
|
|
for name in ["a", "ba", "is_read", "is_write", "tag"]:
|
2013-03-10 14:32:38 -04:00
|
|
|
choices = Array(getattr(req, name) for req in requests)
|
|
|
|
self.comb += getattr(self.cmd, name).eq(choices[rr.grant])
|
2012-07-13 12:05:26 -04:00
|
|
|
for name in ["cas_n", "ras_n", "we_n"]:
|
|
|
|
# we should only assert those signals when stb is 1
|
2013-03-10 14:32:38 -04:00
|
|
|
choices = Array(getattr(req, name) for req in requests)
|
|
|
|
self.comb += If(self.cmd.stb, getattr(self.cmd, name).eq(choices[rr.grant]))
|
|
|
|
self.comb += self.cmd.stb.eq(stb \
|
2012-07-13 12:05:26 -04:00
|
|
|
& (self.cmd.is_read == self.want_reads) \
|
2013-03-10 14:32:38 -04:00
|
|
|
& (self.cmd.is_write == self.want_writes))
|
2012-03-18 17:11:01 -04:00
|
|
|
|
2013-03-10 14:32:38 -04:00
|
|
|
self.comb += [If(self.cmd.stb & self.cmd.ack & (rr.grant == i), req.ack.eq(1))
|
|
|
|
for i, req in enumerate(requests)]
|
|
|
|
self.comb += rr.ce.eq(self.cmd.ack)
|
2012-03-18 17:11:01 -04:00
|
|
|
|
2013-03-10 14:32:38 -04:00
|
|
|
class _Steerer(Module):
|
2012-03-18 17:11:01 -04:00
|
|
|
def __init__(self, commands, dfi):
|
2013-03-10 14:32:38 -04:00
|
|
|
ncmd = len(commands)
|
|
|
|
nph = len(dfi.phases)
|
2012-11-29 17:41:51 -05:00
|
|
|
self.sel = [Signal(max=ncmd) for i in range(nph)]
|
2012-03-18 17:11:01 -04:00
|
|
|
|
2013-03-10 14:32:38 -04:00
|
|
|
###
|
|
|
|
|
2012-03-18 17:11:01 -04:00
|
|
|
def stb_and(cmd, attr):
|
|
|
|
if not hasattr(cmd, "stb"):
|
2012-11-28 17:18:53 -05:00
|
|
|
return 0
|
2012-03-18 17:11:01 -04:00
|
|
|
else:
|
|
|
|
return cmd.stb & getattr(cmd, attr)
|
2013-03-10 14:32:38 -04:00
|
|
|
for phase, sel in zip(dfi.phases, self.sel):
|
|
|
|
self.comb += [
|
2012-03-18 17:11:01 -04:00
|
|
|
phase.cke.eq(1),
|
|
|
|
phase.cs_n.eq(0)
|
|
|
|
]
|
2013-03-10 14:32:38 -04:00
|
|
|
self.sync += [
|
|
|
|
phase.address.eq(Array(cmd.a for cmd in commands)[sel]),
|
|
|
|
phase.bank.eq(Array(cmd.ba for cmd in commands)[sel]),
|
|
|
|
phase.cas_n.eq(Array(cmd.cas_n for cmd in commands)[sel]),
|
|
|
|
phase.ras_n.eq(Array(cmd.ras_n for cmd in commands)[sel]),
|
|
|
|
phase.we_n.eq(Array(cmd.we_n for cmd in commands)[sel]),
|
|
|
|
phase.rddata_en.eq(Array(stb_and(cmd, "is_read") for cmd in commands)[sel]),
|
|
|
|
phase.wrdata_en.eq(Array(stb_and(cmd, "is_write") for cmd in commands)[sel])
|
2012-07-13 12:05:26 -04:00
|
|
|
]
|
2012-03-18 17:11:01 -04:00
|
|
|
|
2013-03-10 14:32:38 -04:00
|
|
|
class _Datapath(Module):
|
2012-03-18 17:11:01 -04:00
|
|
|
def __init__(self, timing_settings, command, dfi, hub):
|
2013-03-10 14:32:38 -04:00
|
|
|
tagbits = len(hub.tag_call)
|
2012-03-18 17:11:01 -04:00
|
|
|
|
|
|
|
rd_valid = Signal()
|
2012-11-29 17:38:04 -05:00
|
|
|
rd_tag = Signal(tagbits)
|
2012-03-18 17:11:01 -04:00
|
|
|
wr_valid = Signal()
|
2012-11-29 17:38:04 -05:00
|
|
|
wr_tag = Signal(tagbits)
|
2013-03-10 14:32:38 -04:00
|
|
|
self.comb += [
|
|
|
|
hub.call.eq(rd_valid | wr_valid),
|
2012-03-18 17:11:01 -04:00
|
|
|
If(wr_valid,
|
2013-03-10 14:32:38 -04:00
|
|
|
hub.tag_call.eq(wr_tag)
|
2012-03-18 17:11:01 -04:00
|
|
|
).Else(
|
2013-03-10 14:32:38 -04:00
|
|
|
hub.tag_call.eq(rd_tag)
|
2012-03-18 17:11:01 -04:00
|
|
|
)
|
|
|
|
]
|
|
|
|
|
2013-03-10 14:32:38 -04:00
|
|
|
rd_delay = timing_settings.rd_delay + 1
|
2012-05-15 07:13:40 -04:00
|
|
|
rd_valid_d = [Signal() for i in range(rd_delay)]
|
2012-11-29 17:38:04 -05:00
|
|
|
rd_tag_d = [Signal(tagbits) for i in range(rd_delay)]
|
2012-05-15 07:13:40 -04:00
|
|
|
for i in range(rd_delay):
|
2012-03-18 17:11:01 -04:00
|
|
|
if i:
|
2013-03-10 14:32:38 -04:00
|
|
|
self.sync += [
|
2012-03-18 17:11:01 -04:00
|
|
|
rd_valid_d[i].eq(rd_valid_d[i-1]),
|
|
|
|
rd_tag_d[i].eq(rd_tag_d[i-1])
|
|
|
|
]
|
|
|
|
else:
|
2013-03-10 14:32:38 -04:00
|
|
|
self.sync += [
|
|
|
|
rd_valid_d[i].eq(command.stb & command.ack & command.is_read),
|
|
|
|
rd_tag_d[i].eq(command.tag)
|
2012-03-18 17:11:01 -04:00
|
|
|
]
|
2013-03-10 14:32:38 -04:00
|
|
|
self.comb += [
|
2012-03-18 17:11:01 -04:00
|
|
|
rd_valid.eq(rd_valid_d[-1]),
|
|
|
|
rd_tag.eq(rd_tag_d[-1]),
|
2013-03-10 14:32:38 -04:00
|
|
|
wr_valid.eq(command.stb & command.ack & command.is_write),
|
|
|
|
wr_tag.eq(command.tag),
|
2012-03-18 17:11:01 -04:00
|
|
|
]
|
|
|
|
|
2013-03-10 14:32:38 -04:00
|
|
|
all_rddata = [p.rddata for p in dfi.phases]
|
|
|
|
all_wrdata = [p.wrdata for p in dfi.phases]
|
|
|
|
all_wrdata_mask = [p.wrdata_mask for p in dfi.phases]
|
|
|
|
self.comb += [
|
|
|
|
hub.dat_r.eq(Cat(*all_rddata)),
|
|
|
|
Cat(*all_wrdata).eq(hub.dat_w),
|
|
|
|
Cat(*all_wrdata_mask).eq(hub.dat_wm)
|
2012-03-18 17:11:01 -04:00
|
|
|
]
|
|
|
|
|
2013-03-10 14:32:38 -04:00
|
|
|
class Multiplexer(Module):
|
2012-03-14 13:26:05 -04:00
|
|
|
def __init__(self, phy_settings, geom_settings, timing_settings, bank_machines, refresher, dfi, hub):
|
2013-03-10 14:32:38 -04:00
|
|
|
assert(phy_settings.nphases == len(dfi.phases))
|
|
|
|
if phy_settings.nphases != 2:
|
2012-03-18 17:11:01 -04:00
|
|
|
raise NotImplementedError("TODO: multiplexer only supports 2 phases")
|
2012-03-14 13:26:05 -04:00
|
|
|
|
2012-03-18 17:11:01 -04:00
|
|
|
# Command choosing
|
2013-03-10 14:32:38 -04:00
|
|
|
requests = [bm.cmd for bm in bank_machines]
|
|
|
|
tagbits = len(hub.tag_call)
|
2012-03-18 17:11:01 -04:00
|
|
|
choose_cmd = _CommandChooser(requests, tagbits)
|
|
|
|
choose_req = _CommandChooser(requests, tagbits)
|
2013-03-10 14:32:38 -04:00
|
|
|
self.comb += [
|
2012-03-18 17:11:01 -04:00
|
|
|
choose_cmd.want_reads.eq(0),
|
|
|
|
choose_cmd.want_writes.eq(0)
|
|
|
|
]
|
2013-03-10 14:32:38 -04:00
|
|
|
self.submodules += choose_cmd, choose_req
|
2012-03-18 17:11:01 -04:00
|
|
|
|
|
|
|
# Command steering
|
2013-03-10 14:32:38 -04:00
|
|
|
nop = CommandRequest(geom_settings.mux_a, geom_settings.bank_a)
|
|
|
|
commands = [nop, choose_cmd.cmd, choose_req.cmd, refresher.cmd] # nop must be 1st
|
2012-03-18 17:11:01 -04:00
|
|
|
(STEER_NOP, STEER_CMD, STEER_REQ, STEER_REFRESH) = range(4)
|
2013-03-10 14:32:38 -04:00
|
|
|
steerer = _Steerer(commands, dfi)
|
|
|
|
self.submodules += steerer
|
2012-03-18 17:11:01 -04:00
|
|
|
|
|
|
|
# Read/write turnaround
|
|
|
|
read_available = Signal()
|
|
|
|
write_available = Signal()
|
2013-03-10 14:32:38 -04:00
|
|
|
self.comb += [
|
2012-03-18 17:11:01 -04:00
|
|
|
read_available.eq(optree("|", [req.stb & req.is_read for req in requests])),
|
|
|
|
write_available.eq(optree("|", [req.stb & req.is_write for req in requests]))
|
|
|
|
]
|
|
|
|
|
|
|
|
def anti_starvation(timeout):
|
|
|
|
en = Signal()
|
|
|
|
max_time = Signal()
|
|
|
|
if timeout:
|
|
|
|
t = timeout - 1
|
2012-11-29 17:41:51 -05:00
|
|
|
time = Signal(max=t+1)
|
2013-03-10 14:32:38 -04:00
|
|
|
self.comb += max_time.eq(time == 0)
|
|
|
|
self.sync += If(~en,
|
2012-03-18 17:11:01 -04:00
|
|
|
time.eq(t)
|
|
|
|
).Elif(~max_time,
|
|
|
|
time.eq(time - 1)
|
|
|
|
)
|
|
|
|
else:
|
2013-03-10 14:32:38 -04:00
|
|
|
self.comb += max_time.eq(0)
|
2012-03-18 17:11:01 -04:00
|
|
|
return en, max_time
|
2013-03-10 14:32:38 -04:00
|
|
|
read_time_en, max_read_time = anti_starvation(timing_settings.read_time)
|
|
|
|
write_time_en, max_write_time = anti_starvation(timing_settings.write_time)
|
2012-03-18 17:11:01 -04:00
|
|
|
|
|
|
|
# Refresh
|
2013-03-10 14:32:38 -04:00
|
|
|
self.comb += [bm.refresh_req.eq(refresher.req) for bm in bank_machines]
|
2012-08-04 16:59:21 -04:00
|
|
|
go_to_refresh = Signal()
|
2013-03-10 14:32:38 -04:00
|
|
|
self.comb += go_to_refresh.eq(optree("&", [bm.refresh_gnt for bm in bank_machines]))
|
2012-03-18 17:11:01 -04:00
|
|
|
|
|
|
|
# Datapath
|
2013-03-10 14:32:38 -04:00
|
|
|
datapath = _Datapath(timing_settings, choose_req.cmd, dfi, hub)
|
|
|
|
self.submodules += datapath
|
2012-03-18 17:11:01 -04:00
|
|
|
|
|
|
|
# Control FSM
|
|
|
|
fsm = FSM("READ", "WRITE", "REFRESH", delayed_enters=[
|
2013-03-10 14:32:38 -04:00
|
|
|
("RTW", "WRITE", timing_settings.rd_delay),
|
|
|
|
("WTR", "READ", timing_settings.tWR)
|
2012-03-18 17:11:01 -04:00
|
|
|
])
|
2013-03-10 14:32:38 -04:00
|
|
|
self.submodules += fsm
|
2012-03-18 17:11:01 -04:00
|
|
|
fsm.act(fsm.READ,
|
|
|
|
read_time_en.eq(1),
|
|
|
|
choose_req.want_reads.eq(1),
|
|
|
|
choose_cmd.cmd.ack.eq(1),
|
|
|
|
choose_req.cmd.ack.eq(1),
|
2013-03-10 14:32:38 -04:00
|
|
|
steerer.sel[1-phy_settings.rdphase].eq(STEER_CMD),
|
|
|
|
steerer.sel[phy_settings.rdphase].eq(STEER_REQ),
|
2012-03-18 17:11:01 -04:00
|
|
|
If(write_available,
|
|
|
|
# TODO: switch only after several cycles of ~read_available?
|
|
|
|
If(~read_available | max_read_time, fsm.next_state(fsm.RTW))
|
|
|
|
),
|
2012-08-04 16:59:21 -04:00
|
|
|
If(go_to_refresh, fsm.next_state(fsm.REFRESH))
|
2012-03-18 17:11:01 -04:00
|
|
|
)
|
|
|
|
fsm.act(fsm.WRITE,
|
|
|
|
write_time_en.eq(1),
|
|
|
|
choose_req.want_writes.eq(1),
|
|
|
|
choose_cmd.cmd.ack.eq(1),
|
|
|
|
choose_req.cmd.ack.eq(1),
|
2013-03-10 14:32:38 -04:00
|
|
|
steerer.sel[1-phy_settings.wrphase].eq(STEER_CMD),
|
|
|
|
steerer.sel[phy_settings.wrphase].eq(STEER_REQ),
|
2012-03-18 17:11:01 -04:00
|
|
|
If(read_available,
|
|
|
|
If(~write_available | max_write_time, fsm.next_state(fsm.WTR))
|
|
|
|
),
|
2012-08-04 16:59:21 -04:00
|
|
|
If(go_to_refresh, fsm.next_state(fsm.REFRESH))
|
2012-03-18 17:11:01 -04:00
|
|
|
)
|
|
|
|
fsm.act(fsm.REFRESH,
|
|
|
|
steerer.sel[0].eq(STEER_REFRESH),
|
2013-03-10 14:32:38 -04:00
|
|
|
If(~refresher.req, fsm.next_state(fsm.READ))
|
2012-03-18 17:11:01 -04:00
|
|
|
)
|
2012-11-28 16:49:22 -05:00
|
|
|
# FIXME: workaround for zero-delay loop simulation problem with Icarus Verilog
|
2013-03-10 14:32:38 -04:00
|
|
|
self.comb += refresher.ack.eq(fsm._state == fsm.REFRESH)
|