520cb2122114be3d3f515f8a894414270285b619
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
),
121 m
.submodules
.lookahead_slicer
= lookahead_slicer
= _AddressSlicer(len(cmd_buffer_lookahead
.source
.addr
),
122 self
.settings
.geom
.colbits
, self
._address
_align
)
123 m
.submodules
.current_slicer
= current_slicer
= _AddressSlicer(len(cmd_buffer
.source
.addr
),
124 self
.settings
.geom
.colbits
, self
._address
_align
)
126 current_slicer
.address
.eq(cmd_buffer
.source
.addr
),
127 lookahead_slicer
.address
.eq(cmd_buffer_lookahead
.source
.addr
),
130 # Row tracking -----------------------------------------------------------------------------
131 row
= Signal(self
.settings
.geom
.rowbits
)
132 row_opened
= Signal()
136 m
.d
.comb
+= row_hit
.eq(row
== current_slicer
.row
)
137 with m
.If(row_close
):
138 m
.d
.sync
+= row_opened
.eq(0)
139 with m
.Elif(row_open
):
142 row
.eq(current_slicer
.row
),
145 # Address generation -----------------------------------------------------------------------
146 row_col_n_addr_sel
= Signal()
147 m
.d
.comb
+= self
.cmd
.ba
.eq(self
._n
)
148 with m
.If(row_col_n_addr_sel
):
149 m
.d
.comb
+= self
.cmd
.a
.eq(current_slicer
.row
)
151 m
.d
.comb
+= self
.cmd
.a
.eq((auto_precharge
<< 10) | current_slicer
.col
)
153 # tWTP / tRC / tRAS controllers
154 write_latency
= math
.ceil(self
.settings
.phy
.cwl
/ self
.settings
.phy
.nphases
)
155 precharge_time
= write_latency
+ self
.settings
.timing
.tWR
+ self
.settings
.timing
.tCCD
# AL=0
156 m
.submodules
.twtpcon
= twtpcon
= tXXDController(precharge_time
)
157 m
.d
.comb
+= twtpcon
.valid
.eq(self
.cmd
.valid
& self
.cmd
.ready
& self
.cmd
.is_write
)
159 m
.submodules
.trccon
= trccon
= tXXDController(self
.settings
.timing
.tRC
)
160 m
.submodules
.trascon
= trascon
= tXXDController(self
.settings
.timing
.tRAS
)
161 valid_ready_row_open
= Signal()
163 valid_ready_row_open
.eq(self
.cmd
.valid
& self
.cmd
.ready
& row_open
),
164 trccon
.valid
.eq(valid_ready_row_open
),
165 trascon
.valid
.eq(valid_ready_row_open
),
168 # Auto Precharge generation ----------------------------------------------------------------
169 # generate auto precharge when current and next cmds are to different rows
170 if self
.settings
.with_auto_precharge
:
171 with m
.If(cmd_buffer_lookahead
.source
.valid
& cmd_buffer
.source
.valid
):
172 with m
.If(lookahead_slicer
.row
!= current_slicer
.row
):
173 m
.d
.comb
+= auto_precharge
.eq(row_close
== 0)
175 # Control and command generation FSM -------------------------------------------------------
176 # Note: tRRD, tFAW, tCCD, tWTR timings are enforced by the multiplexer
178 with m
.State("Regular"):
179 with m
.If(self
.refresh_req
):
180 with m
.If(row_opened
):
181 m
.next
= "Precharge-For-Refresh"
184 with m
.Elif(cmd_buffer
.source
.valid
):
185 with m
.If(row_opened
):
188 self
.cmd
.valid
.eq(1),
191 with m
.If(cmd_buffer
.source
.we
):
193 self
.req
.wdata_ready
.eq(self
.cmd
.ready
),
194 self
.cmd
.is_write
.eq(1),
199 self
.req
.rdata_valid
.eq(self
.cmd
.ready
),
200 self
.cmd
.is_read
.eq(1),
202 with m
.If(self
.cmd
.ready
& auto_precharge
):
203 m
.next
= "Autoprecharge"
209 with m
.State("Precharge"):
210 m
.d
.comb
+= row_close
.eq(1)
212 with m
.If(twtpcon
.ready
& trascon
.ready
):
214 self
.cmd
.valid
.eq(1),
217 self
.cmd
.is_cmd
.eq(1),
220 with m
.If(self
.cmd
.ready
):
223 with m
.State("Precharge-For-Refresh"):
224 m
.d
.comb
+= row_close
.eq(1)
226 with m
.If(twtpcon
.ready
& trascon
.ready
):
228 self
.cmd
.valid
.eq(1),
231 self
.cmd
.is_cmd
.eq(1),
234 with m
.If(self
.cmd
.ready
):
237 with m
.State("Autoprecharge"):
238 m
.d
.comb
+= row_close
.eq(1)
240 with m
.If(twtpcon
.ready
& trascon
.ready
):
243 with m
.State("Activate"):
244 with m
.If(trccon
.ready
):
246 row_col_n_addr_sel
.eq(1),
248 self
.cmd
.valid
.eq(1),
249 self
.cmd
.is_cmd
.eq(1),
252 with m
.If(self
.cmd
.ready
):
255 with m
.State("Refresh"):
258 self
.cmd
.is_cmd
.eq(1),
261 with m
.If(twtpcon
.ready
):
262 m
.d
.comb
+= self
.refresh_gnt
.eq(1)
263 with m
.If(~self
.refresh_req
):
266 delayed_enter(m
, "tRP", "Activate", self
.settings
.timing
.tRP
- 1)
267 delayed_enter(m
, "tRCD", "Regular", self
.settings
.timing
.tRCD
- 1)