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>
10 from gram
.common
import *
11 from gram
.core
.multiplexer
import *
12 from gram
.compat
import delayed_enter
13 import gram
.stream
as stream
15 __ALL__
= ["BankMachine"]
17 class _AddressSlicer(Elaboratable
):
18 def __init__(self
, addrbits
, colbits
, address_align
):
19 self
._address
_align
= address_align
20 self
._split
= colbits
- address_align
22 self
.address
= Signal(addrbits
)
23 self
.row
= Signal(addrbits
-self
._split
)
24 self
.col
= Signal(address_align
+self
._split
)
26 def elaborate(self
, platform
):
30 self
.row
.eq(self
.address
[self
._split
:]),
31 self
.col
.eq(Cat(Repl(0, self
._address
_align
), self
.address
[:self
._split
]))
36 class BankMachine(Elaboratable
):
37 """Converts requests from ports into DRAM commands
39 BankMachine abstracts single DRAM bank by keeping track of the currently
40 selected row. It converts requests from gramCrossbar to targetted
41 to that bank into DRAM commands that go to the Multiplexer, inserting any
42 needed activate/precharge commands (with optional auto-precharge). It also
43 keeps track and enforces some DRAM timings (other timings are enforced in
46 BankMachines work independently from the data path (which connects
47 gramCrossbar with the Multiplexer directly).
49 Stream of requests from gramCrossbar is being queued, so that reqeust
50 can be "looked ahead", and auto-precharge can be performed (if enabled in
53 Lock (cmd_layout.lock) is used to synchronise with gramCrossbar. It is
55 - there is a valid command awaiting in `cmd_buffer_lookahead` - this buffer
56 becomes ready simply when the next data gets fetched to the `cmd_buffer`
57 - there is a valid command in `cmd_buffer` - `cmd_buffer` becomes ready
58 when the BankMachine sends wdata_ready/rdata_valid back to the crossbar
65 LiteDRAMInterface address width
67 Address alignment depending on burst length
69 Number of separate DRAM chips (width of chip select)
70 settings : ControllerSettings
71 LiteDRAMController settings
75 req : Record(cmd_layout)
76 Stream of requests from gramCrossbar
77 refresh_req : Signal(), in
78 Indicates that refresh needs to be done, connects to Refresher.cmd.valid
79 refresh_gnt : Signal(), out
80 Indicates that refresh permission has been granted, satisfying timings
81 cmd : Endpoint(cmd_request_rw_layout)
82 Stream of commands to the Multiplexer
85 def __init__(self
, n
, address_width
, address_align
, nranks
, settings
):
86 self
.settings
= settings
87 self
.req
= req
= Record(cmd_layout(address_width
))
88 self
.refresh_req
= Signal()
89 self
.refresh_gnt
= Signal()
91 a
= settings
.geom
.addressbits
92 ba
= settings
.geom
.bankbits
+ log2_int(nranks
)
93 self
.cmd
= stream
.Endpoint(cmd_request_rw_layout(a
, ba
))
95 self
._address
_align
= address_align
98 def elaborate(self
, platform
):
101 auto_precharge
= Signal()
103 # Command buffer ---------------------------------------------------------------------------
104 cmd_buffer_layout
= [("we", 1), ("addr", len(self
.req
.addr
))]
105 cmd_buffer_lookahead
= stream
.SyncFIFO(
106 cmd_buffer_layout
, self
.settings
.cmd_buffer_depth
,
107 buffered
=self
.settings
.cmd_buffer_buffered
)
108 # 1 depth buffer to detect row change
109 cmd_buffer
= stream
.Buffer(cmd_buffer_layout
)
110 m
.submodules
+= cmd_buffer_lookahead
, cmd_buffer
112 cmd_buffer_lookahead
.sink
.valid
.eq(self
.req
.valid
),
113 self
.req
.ready
.eq(cmd_buffer_lookahead
.sink
.ready
),
114 cmd_buffer_lookahead
.sink
.payload
.we
.eq(self
.req
.we
),
115 cmd_buffer_lookahead
.sink
.payload
.addr
.eq(self
.req
.addr
),
116 cmd_buffer_lookahead
.source
.connect(cmd_buffer
.sink
),
117 cmd_buffer
.source
.ready
.eq(self
.req
.wdata_ready | self
.req
.rdata_valid
),
118 self
.req
.lock
.eq(cmd_buffer_lookahead
.source
.valid | cmd_buffer
.source
.valid
),
122 m
.submodules
.lookahead_slicer
= lookahead_slicer
= _AddressSlicer(len(cmd_buffer_lookahead
.source
.addr
),
123 self
.settings
.geom
.colbits
, self
._address
_align
)
124 m
.submodules
.current_slicer
= current_slicer
= _AddressSlicer(len(cmd_buffer
.source
.addr
),
125 self
.settings
.geom
.colbits
, self
._address
_align
)
127 current_slicer
.address
.eq(cmd_buffer
.source
.addr
),
128 lookahead_slicer
.address
.eq(cmd_buffer_lookahead
.source
.addr
),
131 # Row tracking -----------------------------------------------------------------------------
132 row
= Signal(self
.settings
.geom
.rowbits
)
133 row_opened
= Signal()
137 m
.d
.comb
+= row_hit
.eq(row
== current_slicer
.row
)
138 with m
.If(row_close
):
139 m
.d
.sync
+= row_opened
.eq(0)
140 with m
.Elif(row_open
):
143 row
.eq(current_slicer
.row
),
146 # Address generation -----------------------------------------------------------------------
147 row_col_n_addr_sel
= Signal()
148 m
.d
.comb
+= self
.cmd
.ba
.eq(self
._n
)
149 with m
.If(row_col_n_addr_sel
):
150 m
.d
.comb
+= self
.cmd
.a
.eq(current_slicer
.row
)
152 m
.d
.comb
+= self
.cmd
.a
.eq((auto_precharge
<< 10) | current_slicer
.col
)
154 # tWTP / tRC / tRAS controllers
155 write_latency
= math
.ceil(self
.settings
.phy
.cwl
/ self
.settings
.phy
.nphases
)
156 precharge_time
= write_latency
+ self
.settings
.timing
.tWR
+ self
.settings
.timing
.tCCD
# AL=0
157 m
.submodules
.twtpcon
= twtpcon
= tXXDController(precharge_time
)
158 m
.d
.comb
+= twtpcon
.valid
.eq(self
.cmd
.valid
& self
.cmd
.ready
& self
.cmd
.is_write
)
160 m
.submodules
.trccon
= trccon
= tXXDController(self
.settings
.timing
.tRC
)
161 m
.submodules
.trascon
= trascon
= tXXDController(self
.settings
.timing
.tRAS
)
162 valid_ready_row_open
= Signal()
164 valid_ready_row_open
.eq(self
.cmd
.valid
& self
.cmd
.ready
& row_open
),
165 trccon
.valid
.eq(valid_ready_row_open
),
166 trascon
.valid
.eq(valid_ready_row_open
),
169 # Auto Precharge generation ----------------------------------------------------------------
170 # generate auto precharge when current and next cmds are to different rows
171 if self
.settings
.with_auto_precharge
:
172 with m
.If(cmd_buffer_lookahead
.source
.valid
& cmd_buffer
.source
.valid
):
173 with m
.If(lookahead_slicer
.row
!= current_slicer
.row
):
174 m
.d
.comb
+= auto_precharge
.eq(row_close
== 0)
176 # Control and command generation FSM -------------------------------------------------------
177 # Note: tRRD, tFAW, tCCD, tWTR timings are enforced by the multiplexer
179 with m
.State("Regular"):
180 with m
.If(self
.refresh_req
):
181 with m
.If(row_opened
):
182 m
.next
= "Precharge-For-Refresh"
185 with m
.Elif(cmd_buffer
.source
.valid
):
186 with m
.If(row_opened
):
189 self
.cmd
.valid
.eq(1),
192 with m
.If(cmd_buffer
.source
.we
):
194 self
.req
.wdata_ready
.eq(self
.cmd
.ready
),
195 self
.cmd
.is_write
.eq(1),
200 self
.req
.rdata_valid
.eq(self
.cmd
.ready
),
201 self
.cmd
.is_read
.eq(1),
203 with m
.If(self
.cmd
.ready
& auto_precharge
):
204 m
.next
= "Autoprecharge"
210 with m
.State("Precharge"):
211 m
.d
.comb
+= row_close
.eq(1)
213 with m
.If(twtpcon
.ready
& trascon
.ready
):
215 self
.cmd
.valid
.eq(1),
218 self
.cmd
.is_cmd
.eq(1),
221 with m
.If(self
.cmd
.ready
):
224 with m
.State("Precharge-For-Refresh"):
225 m
.d
.comb
+= row_close
.eq(1)
227 with m
.If(twtpcon
.ready
& trascon
.ready
):
229 self
.cmd
.valid
.eq(1),
232 self
.cmd
.is_cmd
.eq(1),
235 with m
.If(self
.cmd
.ready
):
238 with m
.State("Autoprecharge"):
239 m
.d
.comb
+= row_close
.eq(1)
241 with m
.If(twtpcon
.ready
& trascon
.ready
):
244 with m
.State("Activate"):
245 with m
.If(trccon
.ready
):
247 row_col_n_addr_sel
.eq(1),
249 self
.cmd
.valid
.eq(1),
250 self
.cmd
.is_cmd
.eq(1),
253 with m
.If(self
.cmd
.ready
):
256 with m
.State("Refresh"):
259 self
.cmd
.is_cmd
.eq(1),
262 with m
.If(twtpcon
.ready
):
263 m
.d
.comb
+= self
.refresh_gnt
.eq(1)
264 with m
.If(~self
.refresh_req
):
267 delayed_enter(m
, "tRP", "Activate", self
.settings
.timing
.tRP
- 1)
268 delayed_enter(m
, "tRCD", "Regular", self
.settings
.timing
.tRCD
- 1)