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) 2018 John Sully <john@csquare.ca>
4 # This file is Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
7 """LiteDRAM Crossbar."""
9 from functools
import reduce
10 from operator
import or_
14 from gram
.common
import *
15 from gram
.core
.controller
import *
16 from gram
.frontend
.adaptation
import *
17 from gram
.compat
import RoundRobin
18 import gram
.stream
as stream
20 # LiteDRAMCrossbar ---------------------------------------------------------------------------------
22 class gramCrossbar(Module
):
23 """Multiplexes LiteDRAMController (slave) between ports (masters)
25 To get a port to LiteDRAM, use the `get_port` method. It handles data width
26 conversion and clock domain crossing, returning LiteDRAMNativePort.
28 The crossbar routes requests from masters to the BankMachines
29 (bankN.cmd_layout) and connects data path directly to the Multiplexer
30 (data_layout). It performs address translation based on chosen
31 `controller.settings.address_mapping`.
32 Internally, all masters are multiplexed between controller banks based on
33 the bank address (extracted from the presented address). Each bank has
34 a RoundRobin arbiter, that selects from masters that want to access this
35 bank and are not already locked.
37 Locks (cmd_layout.lock) make sure that, when a master starts a transaction
38 with given bank (which may include multiple reads/writes), no other bank
39 will be assigned to it during this time.
40 Arbiter (of a bank) considers given master as a candidate for selection if:
41 - given master's command is valid
42 - given master addresses the arbiter's bank
43 - given master is not locked
44 * i.e. it is not during transaction with another bank
45 * i.e. no other bank's arbiter granted permission for this master (with
46 bank.lock being active)
48 Data ready/valid signals for banks are routed from bankmachines with
49 a latency that synchronizes them with the data coming over datapath.
53 controller : LiteDRAMInterface
54 Interface to LiteDRAMController
58 masters : [LiteDRAMNativePort, ...]
61 def __init__(self
, controller
):
62 self
.controller
= controller
64 self
.rca_bits
= controller
.address_width
65 self
.nbanks
= controller
.nbanks
66 self
.nranks
= controller
.nranks
67 self
.cmd_buffer_depth
= controller
.settings
.cmd_buffer_depth
68 self
.read_latency
= controller
.settings
.phy
.read_latency
+ 1
69 self
.write_latency
= controller
.settings
.phy
.write_latency
+ 1
71 self
.bank_bits
= log2_int(self
.nbanks
, False)
72 self
.rank_bits
= log2_int(self
.nranks
, False)
76 def get_port(self
, mode
="both", data_width
=None, clock_domain
="sys", reverse
=False):
80 if data_width
is None:
81 # use internal data_width when no width adaptation is requested
82 data_width
= self
.controller
.data_width
84 # Crossbar port ----------------------------------------------------------------------------
85 port
= LiteDRAMNativePort(
87 address_width
= self
.rca_bits
+ self
.bank_bits
- self
.rank_bits
,
88 data_width
= self
.controller
.data_width
,
90 id = len(self
.masters
))
91 self
.masters
.append(port
)
93 # Clock domain crossing --------------------------------------------------------------------
94 if clock_domain
!= "sys":
95 new_port
= LiteDRAMNativePort(
97 address_width
= port
.address_width
,
98 data_width
= port
.data_width
,
99 clock_domain
= clock_domain
,
101 self
.submodules
+= LiteDRAMNativePortCDC(new_port
, port
)
104 # Data width convertion --------------------------------------------------------------------
105 if data_width
!= self
.controller
.data_width
:
106 if data_width
> self
.controller
.data_width
:
107 addr_shift
= -log2_int(data_width
//self
.controller
.data_width
)
109 addr_shift
= log2_int(self
.controller
.data_width
//data_width
)
110 new_port
= LiteDRAMNativePort(
112 address_width
= port
.address_width
+ addr_shift
,
113 data_width
= data_width
,
114 clock_domain
= clock_domain
,
116 self
.submodules
+= ClockDomainsRenamer(clock_domain
)(
117 LiteDRAMNativePortConverter(new_port
, port
, reverse
))
122 def do_finalize(self
):
123 controller
= self
.controller
124 nmasters
= len(self
.masters
)
126 # Address mapping --------------------------------------------------------------------------
127 cba_shifts
= {"ROW_BANK_COL": controller
.settings
.geom
.colbits
- controller
.address_align
}
128 cba_shift
= cba_shifts
[controller
.settings
.address_mapping
]
129 m_ba
= [m
.get_bank_address(self
.bank_bits
, cba_shift
)for m
in self
.masters
]
130 m_rca
= [m
.get_row_column_address(self
.bank_bits
, self
.rca_bits
, cba_shift
) for m
in self
.masters
]
132 master_readys
= [0]*nmasters
133 master_wdata_readys
= [0]*nmasters
134 master_rdata_valids
= [0]*nmasters
136 arbiters
= [roundrobin
.RoundRobin(nmasters
, roundrobin
.SP_CE
) for n
in range(self
.nbanks
)]
137 self
.submodules
+= arbiters
139 for nb
, arbiter
in enumerate(arbiters
):
140 bank
= getattr(controller
, "bank"+str(nb
))
142 # For each master, determine if another bank locks it ----------------------------------
144 for nm
, master
in enumerate(self
.masters
):
146 for other_nb
, other_arbiter
in enumerate(arbiters
):
148 other_bank
= getattr(controller
, "bank"+str(other_nb
))
149 locked
= locked |
(other_bank
.lock
& (other_arbiter
.grant
== nm
))
150 master_locked
.append(locked
)
152 # Arbitrate ----------------------------------------------------------------------------
153 bank_selected
= [(ba
== nb
) & ~locked
for ba
, locked
in zip(m_ba
, master_locked
)]
154 bank_requested
= [bs
& master
.cmd
.valid
for bs
, master
in zip(bank_selected
, self
.masters
)]
156 arbiter
.request
.eq(Cat(*bank_requested
)),
157 arbiter
.ce
.eq(~bank
.valid
& ~bank
.lock
)
160 # Route requests -----------------------------------------------------------------------
162 bank
.addr
.eq(Array(m_rca
)[arbiter
.grant
]),
163 bank
.we
.eq(Array(self
.masters
)[arbiter
.grant
].cmd
.we
),
164 bank
.valid
.eq(Array(bank_requested
)[arbiter
.grant
])
166 master_readys
= [master_ready |
((arbiter
.grant
== nm
) & bank_selected
[nm
] & bank
.ready
)
167 for nm
, master_ready
in enumerate(master_readys
)]
168 master_wdata_readys
= [master_wdata_ready |
((arbiter
.grant
== nm
) & bank
.wdata_ready
)
169 for nm
, master_wdata_ready
in enumerate(master_wdata_readys
)]
170 master_rdata_valids
= [master_rdata_valid |
((arbiter
.grant
== nm
) & bank
.rdata_valid
)
171 for nm
, master_rdata_valid
in enumerate(master_rdata_valids
)]
173 # Delay write/read signals based on their latency
174 for nm
, master_wdata_ready
in enumerate(master_wdata_readys
):
175 for i
in range(self
.write_latency
):
176 new_master_wdata_ready
= Signal()
177 self
.sync
+= new_master_wdata_ready
.eq(master_wdata_ready
)
178 master_wdata_ready
= new_master_wdata_ready
179 master_wdata_readys
[nm
] = master_wdata_ready
181 for nm
, master_rdata_valid
in enumerate(master_rdata_valids
):
182 for i
in range(self
.read_latency
):
183 new_master_rdata_valid
= Signal()
184 self
.sync
+= new_master_rdata_valid
.eq(master_rdata_valid
)
185 master_rdata_valid
= new_master_rdata_valid
186 master_rdata_valids
[nm
] = master_rdata_valid
188 for master
, master_ready
in zip(self
.masters
, master_readys
):
189 self
.comb
+= master
.cmd
.ready
.eq(master_ready
)
190 for master
, master_wdata_ready
in zip(self
.masters
, master_wdata_readys
):
191 self
.comb
+= master
.wdata
.ready
.eq(master_wdata_ready
)
192 for master
, master_rdata_valid
in zip(self
.masters
, master_rdata_valids
):
193 self
.comb
+= master
.rdata
.valid
.eq(master_rdata_valid
)
195 # Route data writes ------------------------------------------------------------------------
197 for nm
, master
in enumerate(self
.masters
):
198 wdata_cases
[2**nm
] = [
199 controller
.wdata
.eq(master
.wdata
.data
),
200 controller
.wdata_we
.eq(master
.wdata
.we
)
202 wdata_cases
["default"] = [
203 controller
.wdata
.eq(0),
204 controller
.wdata_we
.eq(0)
206 self
.comb
+= Case(Cat(*master_wdata_readys
), wdata_cases
)
208 # Route data reads -------------------------------------------------------------------------
209 for master
in self
.masters
:
210 self
.comb
+= master
.rdata
.data
.eq(controller
.rdata
)