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