Refactor directory hierarchy of sdram phys and controllers
[litex.git] / misoclib / sdram / lasmicon / multiplexer.py
1 from migen.fhdl.std import *
2 from migen.genlib.roundrobin import *
3 from migen.genlib.misc import optree
4 from migen.genlib.fsm import FSM, NextState
5 from migen.bank.description import AutoCSR
6
7 from misoclib.sdram.lasmicon.perf import Bandwidth
8
9 class CommandRequest:
10 def __init__(self, a, ba):
11 self.a = Signal(a)
12 self.ba = Signal(ba)
13 self.cas_n = Signal(reset=1)
14 self.ras_n = Signal(reset=1)
15 self.we_n = Signal(reset=1)
16
17 class CommandRequestRW(CommandRequest):
18 def __init__(self, a, ba):
19 CommandRequest.__init__(self, a, ba)
20 self.stb = Signal()
21 self.ack = Signal()
22 self.is_cmd = Signal()
23 self.is_read = Signal()
24 self.is_write = Signal()
25
26 class _CommandChooser(Module):
27 def __init__(self, requests):
28 self.want_reads = Signal()
29 self.want_writes = Signal()
30 self.want_cmds = Signal()
31 # NB: cas_n/ras_n/we_n are 1 when stb is inactive
32 self.cmd = CommandRequestRW(flen(requests[0].a), flen(requests[0].ba))
33
34 ###
35
36 rr = RoundRobin(len(requests), SP_CE)
37 self.submodules += rr
38
39 self.comb += [rr.request[i].eq(req.stb & ((req.is_cmd & self.want_cmds) | ((req.is_read == self.want_reads) | (req.is_write == self.want_writes))))
40 for i, req in enumerate(requests)]
41
42 stb = Signal()
43 self.comb += stb.eq(Array(req.stb for req in requests)[rr.grant])
44 for name in ["a", "ba", "is_read", "is_write", "is_cmd"]:
45 choices = Array(getattr(req, name) for req in requests)
46 self.comb += getattr(self.cmd, name).eq(choices[rr.grant])
47 for name in ["cas_n", "ras_n", "we_n"]:
48 # we should only assert those signals when stb is 1
49 choices = Array(getattr(req, name) for req in requests)
50 self.comb += If(self.cmd.stb, getattr(self.cmd, name).eq(choices[rr.grant]))
51 self.comb += self.cmd.stb.eq(stb \
52 & ((self.cmd.is_cmd & self.want_cmds) | ((self.cmd.is_read == self.want_reads) \
53 & (self.cmd.is_write == self.want_writes))))
54
55 self.comb += [If(self.cmd.stb & self.cmd.ack & (rr.grant == i), req.ack.eq(1))
56 for i, req in enumerate(requests)]
57 self.comb += rr.ce.eq(self.cmd.ack)
58
59 class _Steerer(Module):
60 def __init__(self, commands, dfi):
61 ncmd = len(commands)
62 nph = len(dfi.phases)
63 self.sel = [Signal(max=ncmd) for i in range(nph)]
64
65 ###
66
67 def stb_and(cmd, attr):
68 if not hasattr(cmd, "stb"):
69 return 0
70 else:
71 return cmd.stb & getattr(cmd, attr)
72 for phase, sel in zip(dfi.phases, self.sel):
73 self.comb += [
74 phase.cke.eq(1),
75 phase.cs_n.eq(0)
76 ]
77 if hasattr(phase, "odt"):
78 self.comb += phase.odt.eq(1)
79 if hasattr(phase, "reset_n"):
80 self.comb += phase.reset_n.eq(1)
81 self.sync += [
82 phase.address.eq(Array(cmd.a for cmd in commands)[sel]),
83 phase.bank.eq(Array(cmd.ba for cmd in commands)[sel]),
84 phase.cas_n.eq(Array(cmd.cas_n for cmd in commands)[sel]),
85 phase.ras_n.eq(Array(cmd.ras_n for cmd in commands)[sel]),
86 phase.we_n.eq(Array(cmd.we_n for cmd in commands)[sel]),
87 phase.rddata_en.eq(Array(stb_and(cmd, "is_read") for cmd in commands)[sel]),
88 phase.wrdata_en.eq(Array(stb_and(cmd, "is_write") for cmd in commands)[sel])
89 ]
90
91 class Multiplexer(Module, AutoCSR):
92 def __init__(self, phy_settings, geom_settings, timing_settings, bank_machines, refresher, dfi, lasmic):
93 assert(phy_settings.nphases == len(dfi.phases))
94
95 # Command choosing
96 requests = [bm.cmd for bm in bank_machines]
97 choose_cmd = _CommandChooser(requests)
98 choose_req = _CommandChooser(requests)
99 self.comb += [
100 choose_cmd.want_reads.eq(0),
101 choose_cmd.want_writes.eq(0)
102 ]
103 if phy_settings.nphases == 1:
104 self.comb += [
105 choose_cmd.want_cmds.eq(1),
106 choose_req.want_cmds.eq(1)
107 ]
108 self.submodules += choose_cmd, choose_req
109
110 # Command steering
111 nop = CommandRequest(geom_settings.mux_a, geom_settings.bank_a)
112 commands = [nop, choose_cmd.cmd, choose_req.cmd, refresher.cmd] # nop must be 1st
113 (STEER_NOP, STEER_CMD, STEER_REQ, STEER_REFRESH) = range(4)
114 steerer = _Steerer(commands, dfi)
115 self.submodules += steerer
116
117 # Read/write turnaround
118 read_available = Signal()
119 write_available = Signal()
120 self.comb += [
121 read_available.eq(optree("|", [req.stb & req.is_read for req in requests])),
122 write_available.eq(optree("|", [req.stb & req.is_write for req in requests]))
123 ]
124
125 def anti_starvation(timeout):
126 en = Signal()
127 max_time = Signal()
128 if timeout:
129 t = timeout - 1
130 time = Signal(max=t+1)
131 self.comb += max_time.eq(time == 0)
132 self.sync += If(~en,
133 time.eq(t)
134 ).Elif(~max_time,
135 time.eq(time - 1)
136 )
137 else:
138 self.comb += max_time.eq(0)
139 return en, max_time
140 read_time_en, max_read_time = anti_starvation(timing_settings.read_time)
141 write_time_en, max_write_time = anti_starvation(timing_settings.write_time)
142
143 # Refresh
144 self.comb += [bm.refresh_req.eq(refresher.req) for bm in bank_machines]
145 go_to_refresh = Signal()
146 self.comb += go_to_refresh.eq(optree("&", [bm.refresh_gnt for bm in bank_machines]))
147
148 # Datapath
149 all_rddata = [p.rddata for p in dfi.phases]
150 all_wrdata = [p.wrdata for p in dfi.phases]
151 all_wrdata_mask = [p.wrdata_mask for p in dfi.phases]
152 self.comb += [
153 lasmic.dat_r.eq(Cat(*all_rddata)),
154 Cat(*all_wrdata).eq(lasmic.dat_w),
155 Cat(*all_wrdata_mask).eq(~lasmic.dat_we)
156 ]
157
158 # Control FSM
159 fsm = FSM()
160 self.submodules += fsm
161
162 def steerer_sel(steerer, phy_settings, r_w_n):
163 r = []
164 for i in range(phy_settings.nphases):
165 s = steerer.sel[i].eq(STEER_NOP)
166 if r_w_n == "read":
167 if i == phy_settings.rdphase:
168 s = steerer.sel[i].eq(STEER_REQ)
169 elif i == phy_settings.rdcmdphase:
170 s = steerer.sel[i].eq(STEER_CMD)
171 elif r_w_n == "write":
172 if i == phy_settings.wrphase:
173 s = steerer.sel[i].eq(STEER_REQ)
174 elif i == phy_settings.wrcmdphase:
175 s = steerer.sel[i].eq(STEER_CMD)
176 else:
177 raise ValueError
178 r.append(s)
179 return r
180
181 fsm.act("READ",
182 read_time_en.eq(1),
183 choose_req.want_reads.eq(1),
184 choose_cmd.cmd.ack.eq(1),
185 choose_req.cmd.ack.eq(1),
186 steerer_sel(steerer, phy_settings, "read"),
187 If(write_available,
188 # TODO: switch only after several cycles of ~read_available?
189 If(~read_available | max_read_time, NextState("RTW"))
190 ),
191 If(go_to_refresh, NextState("REFRESH"))
192 )
193 fsm.act("WRITE",
194 write_time_en.eq(1),
195 choose_req.want_writes.eq(1),
196 choose_cmd.cmd.ack.eq(1),
197 choose_req.cmd.ack.eq(1),
198 steerer_sel(steerer, phy_settings, "write"),
199 If(read_available,
200 If(~write_available | max_write_time, NextState("WTR"))
201 ),
202 If(go_to_refresh, NextState("REFRESH"))
203 )
204 fsm.act("REFRESH",
205 steerer.sel[0].eq(STEER_REFRESH),
206 If(~refresher.req, NextState("READ"))
207 )
208 fsm.delayed_enter("RTW", "WRITE", phy_settings.read_latency-1) # FIXME: reduce this, actual limit is around (cl+1)/nphases
209 fsm.delayed_enter("WTR", "READ", timing_settings.tWTR-1)
210 # FIXME: workaround for zero-delay loop simulation problem with Icarus Verilog
211 fsm.finalize()
212 self.comb += refresher.ack.eq(fsm.state == fsm.encoding["REFRESH"])
213
214 self.submodules.bandwidth = Bandwidth(choose_req.cmd)