1 # This file is Copyright (c) 2015 Sebastien Bourdeauducq <sb@m-labs.hk>
2 # This file is Copyright (c) 2016-2019 Florent Kermarrec <florent@enjoy-digital.fr>
3 # This file is Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
6 """LiteDRAM BankMachine (Rows/Columns management)."""
12 from gram
.common
import *
13 from gram
.core
.multiplexer
import *
14 from gram
.compat
import delayed_enter
15 import gram
.stream
as stream
17 __ALL__
= ["BankMachine"]
19 # AddressSlicer ------------------------------------------------------------------------------------
23 """Helper for extracting row/col from address
25 Column occupies lower bits of the address, row - higher bits. Address has
26 a forced alignment, so column does not contain alignment bits.
29 def __init__(self
, colbits
, address_align
):
30 self
.colbits
= colbits
31 self
.address_align
= address_align
33 def row(self
, address
):
34 split
= self
.colbits
- self
.address_align
35 return address
[split
:]
37 def col(self
, address
):
38 split
= self
.colbits
- self
.address_align
39 return Cat(Repl(0, self
.address_align
), address
[:split
])
41 # BankMachine --------------------------------------------------------------------------------------
44 class BankMachine(Elaboratable
):
45 """Converts requests from ports into DRAM commands
47 BankMachine abstracts single DRAM bank by keeping track of the currently
48 selected row. It converts requests from LiteDRAMCrossbar to targetted
49 to that bank into DRAM commands that go to the Multiplexer, inserting any
50 needed activate/precharge commands (with optional auto-precharge). It also
51 keeps track and enforces some DRAM timings (other timings are enforced in
54 BankMachines work independently from the data path (which connects
55 LiteDRAMCrossbar with the Multiplexer directly).
57 Stream of requests from LiteDRAMCrossbar is being queued, so that reqeust
58 can be "looked ahead", and auto-precharge can be performed (if enabled in
61 Lock (cmd_layout.lock) is used to synchronise with LiteDRAMCrossbar. It is
63 - there is a valid command awaiting in `cmd_buffer_lookahead` - this buffer
64 becomes ready simply when the next data gets fetched to the `cmd_buffer`
65 - there is a valid command in `cmd_buffer` - `cmd_buffer` becomes ready
66 when the BankMachine sends wdata_ready/rdata_valid back to the crossbar
73 LiteDRAMInterface address width
75 Address alignment depending on burst length
77 Number of separate DRAM chips (width of chip select)
78 settings : ControllerSettings
79 LiteDRAMController settings
83 req : Record(cmd_layout)
84 Stream of requests from LiteDRAMCrossbar
85 refresh_req : Signal(), in
86 Indicates that refresh needs to be done, connects to Refresher.cmd.valid
87 refresh_gnt : Signal(), out
88 Indicates that refresh permission has been granted, satisfying timings
89 cmd : Endpoint(cmd_request_rw_layout)
90 Stream of commands to the Multiplexer
93 def __init__(self
, n
, address_width
, address_align
, nranks
, settings
):
94 self
.settings
= settings
95 self
.req
= req
= Record(cmd_layout(address_width
))
96 self
.refresh_req
= refresh_req
= Signal()
97 self
.refresh_gnt
= refresh_gnt
= Signal()
99 a
= settings
.geom
.addressbits
100 ba
= settings
.geom
.bankbits
+ log2_int(nranks
)
101 self
.cmd
= stream
.Endpoint(cmd_request_rw_layout(a
, ba
))
103 self
._address
_align
= address_align
106 def elaborate(self
, platform
):
109 auto_precharge
= Signal()
111 # Command buffer ---------------------------------------------------------------------------
112 cmd_buffer_layout
= [("we", 1), ("addr", len(self
.req
.addr
))]
113 cmd_buffer_lookahead
= stream
.SyncFIFO(
114 cmd_buffer_layout
, self
.settings
.cmd_buffer_depth
,
115 buffered
=self
.settings
.cmd_buffer_buffered
)
116 # 1 depth buffer to detect row change
117 cmd_buffer
= stream
.Buffer(cmd_buffer_layout
)
118 m
.submodules
+= cmd_buffer_lookahead
, cmd_buffer
120 cmd_buffer_lookahead
.sink
.valid
.eq(self
.req
.valid
),
121 self
.req
.ready
.eq(cmd_buffer_lookahead
.sink
.ready
),
122 cmd_buffer_lookahead
.sink
.payload
.we
.eq(self
.req
.we
),
123 cmd_buffer_lookahead
.sink
.payload
.addr
.eq(self
.req
.addr
),
125 cmd_buffer_lookahead
.source
.connect(cmd_buffer
.sink
),
126 cmd_buffer
.source
.ready
.eq(
127 self
.req
.wdata_ready | self
.req
.rdata_valid
),
128 self
.req
.lock
.eq(cmd_buffer_lookahead
.source
.valid |
129 cmd_buffer
.source
.valid
),
132 slicer
= _AddressSlicer(self
.settings
.geom
.colbits
, self
._address
_align
)
134 # Row tracking -----------------------------------------------------------------------------
135 row
= Signal(self
.settings
.geom
.rowbits
)
136 row_opened
= Signal()
140 m
.d
.comb
+= row_hit
.eq(row
== slicer
.row(cmd_buffer
.source
.addr
))
141 with m
.If(row_close
):
142 m
.d
.sync
+= row_opened
.eq(0)
143 with m
.Elif(row_open
):
146 row
.eq(slicer
.row(cmd_buffer
.source
.addr
)),
149 # Address generation -----------------------------------------------------------------------
150 row_col_n_addr_sel
= Signal()
151 m
.d
.comb
+= self
.cmd
.ba
.eq(self
._n
)
152 with m
.If(row_col_n_addr_sel
):
153 m
.d
.comb
+= self
.cmd
.a
.eq(slicer
.row(cmd_buffer
.source
.addr
))
155 m
.d
.comb
+= self
.cmd
.a
.eq((auto_precharge
<< 10)
156 | slicer
.col(cmd_buffer
.source
.addr
))
158 # tWTP (write-to-precharge) controller -----------------------------------------------------
159 write_latency
= math
.ceil(
160 self
.settings
.phy
.cwl
/ self
.settings
.phy
.nphases
)
161 precharge_time
= write_latency
+ self
.settings
.timing
.tWR
+ \
162 self
.settings
.timing
.tCCD
# AL=0
163 m
.submodules
.twtpcon
= twtpcon
= tXXDController(precharge_time
)
164 m
.d
.comb
+= twtpcon
.valid
.eq(self
.cmd
.valid
&
165 self
.cmd
.ready
& self
.cmd
.is_write
)
167 # tRC (activate-activate) controller -------------------------------------------------------
168 m
.submodules
.trccon
= trccon
= tXXDController(self
.settings
.timing
.tRC
)
169 m
.d
.comb
+= trccon
.valid
.eq(self
.cmd
.valid
& self
.cmd
.ready
& row_open
)
171 # tRAS (activate-precharge) controller -----------------------------------------------------
172 m
.submodules
.trascon
= trascon
= tXXDController(self
.settings
.timing
.tRAS
)
173 m
.d
.comb
+= trascon
.valid
.eq(self
.cmd
.valid
& self
.cmd
.ready
& row_open
)
175 # Auto Precharge generation ----------------------------------------------------------------
176 # generate auto precharge when current and next cmds are to different rows
177 if self
.settings
.with_auto_precharge
:
178 with m
.If(cmd_buffer_lookahead
.source
.valid
& cmd_buffer
.source
.valid
):
179 with m
.If(slicer
.row(cmd_buffer_lookahead
.source
.addr
) != slicer
.row(cmd_buffer
.source
.addr
)):
180 m
.d
.comb
+= auto_precharge
.eq(row_close
== 0)
182 # Control and command generation FSM -------------------------------------------------------
183 # Note: tRRD, tFAW, tCCD, tWTR timings are enforced by the multiplexer
185 with m
.State("Regular"):
186 with m
.If(self
.refresh_req
):
188 with m
.Elif(cmd_buffer
.source
.valid
):
189 with m
.If(row_opened
):
192 self
.cmd
.valid
.eq(1),
195 with m
.If(cmd_buffer
.source
.we
):
197 self
.req
.wdata_ready
.eq(self
.cmd
.ready
),
198 self
.cmd
.is_write
.eq(1),
203 self
.req
.rdata_valid
.eq(self
.cmd
.ready
),
204 self
.cmd
.is_read
.eq(1),
206 with m
.If(self
.cmd
.ready
& auto_precharge
):
207 m
.next
= "Autoprecharge"
213 with m
.State("Precharge"):
214 m
.d
.comb
+= row_close
.eq(1)
216 with m
.If(twtpcon
.ready
& trascon
.ready
):
218 self
.cmd
.valid
.eq(1),
221 self
.cmd
.is_cmd
.eq(1),
224 with m
.If(self
.cmd
.ready
):
227 with m
.State("Autoprecharge"):
228 m
.d
.comb
+= row_close
.eq(1)
230 with m
.If(twtpcon
.ready
& trascon
.ready
):
233 with m
.State("Activate"):
234 with m
.If(trccon
.ready
):
236 row_col_n_addr_sel
.eq(1),
238 self
.cmd
.valid
.eq(1),
239 self
.cmd
.is_cmd
.eq(1),
242 with m
.If(self
.cmd
.ready
):
245 with m
.State("Refresh"):
248 self
.cmd
.is_cmd
.eq(1),
251 with m
.If(twtpcon
.ready
):
252 m
.d
.comb
+= self
.refresh_gnt
.eq(1)
253 with m
.If(~self
.refresh_req
):
256 delayed_enter(m
, "tRP", "Activate", self
.settings
.timing
.tRP
- 1)
257 delayed_enter(m
, "tRCD", "Regular", self
.settings
.timing
.tRCD
- 1)