1 from functools
import reduce
2 from operator
import or_
, and_
4 from litex
.gen
import *
5 from litex
.gen
.genlib
.roundrobin
import *
6 from litex
.gen
.genlib
.fsm
import FSM
, NextState
8 from litex
.soc
.cores
.sdram
.lasmicon
.perf
import Bandwidth
9 from litex
.soc
.interconnect
.csr
import AutoCSR
13 def __init__(self
, a
, ba
):
16 self
.cas_n
= Signal(reset
=1)
17 self
.ras_n
= Signal(reset
=1)
18 self
.we_n
= Signal(reset
=1)
21 class CommandRequestRW(CommandRequest
):
22 def __init__(self
, a
, ba
):
23 CommandRequest
.__init
__(self
, a
, ba
)
26 self
.is_cmd
= Signal()
27 self
.is_read
= Signal()
28 self
.is_write
= Signal()
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
))
41 rr
= RoundRobin(len(requests
), SP_CE
)
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
)]
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
))))
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
)
65 class _Steerer(Module
):
66 def __init__(self
, commands
, dfi
):
69 self
.sel
= [Signal(max=ncmd
) for i
in range(nph
)]
73 def stb_and(cmd
, attr
):
74 if not hasattr(cmd
, "stb"):
77 return cmd
.stb
& getattr(cmd
, attr
)
78 for phase
, sel
in zip(dfi
.phases
, self
.sel
):
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)
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
])
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
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
)
109 choose_cmd
.want_reads
.eq(0),
110 choose_cmd
.want_writes
.eq(0)
112 if phy_settings
.nphases
== 1:
114 choose_cmd
.want_cmds
.eq(1),
115 choose_req
.want_cmds
.eq(1)
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
125 # Read/write turnaround
126 read_available
= Signal()
127 write_available
= Signal()
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
]))
133 def anti_starvation(timeout
):
138 time
= Signal(max=t
+1)
139 self
.comb
+= max_time
.eq(time
== 0)
146 self
.comb
+= max_time
.eq(0)
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
)
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
]))
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
]
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
)
168 self
.submodules
+= fsm
170 def steerer_sel(steerer
, phy_settings
, r_w_n
):
172 for i
in range(phy_settings
.nphases
):
173 s
= steerer
.sel
[i
].eq(STEER_NOP
)
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
)
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"),
196 # TODO: switch only after several cycles of ~read_available?
197 If(~read_available | max_read_time
, NextState("RTW"))
199 If(go_to_refresh
, NextState("REFRESH"))
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"),
208 If(~write_available | max_write_time
, NextState("WTR"))
210 If(go_to_refresh
, NextState("REFRESH"))
213 steerer
.sel
[0].eq(STEER_REFRESH
),
215 If(~refresher
.req
, NextState("READ"))
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)
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
)