1ca18e7dabc4bbad03674d6104a5621d16199994
[gram.git] / gram / core / crossbar.py
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>
5 # License: BSD
6
7 """LiteDRAM Crossbar."""
8
9 from functools import reduce
10 from operator import or_
11
12 from nmigen import *
13
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
19
20 # LiteDRAMCrossbar ---------------------------------------------------------------------------------
21
22 class gramCrossbar(Elaboratable):
23 """Multiplexes LiteDRAMController (slave) between ports (masters)
24
25 To get a port to LiteDRAM, use the `get_port` method. It handles data width
26 conversion and clock domain crossing, returning LiteDRAMNativePort.
27
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.
36
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)
47
48 Data ready/valid signals for banks are routed from bankmachines with
49 a latency that synchronizes them with the data coming over datapath.
50
51 Parameters
52 ----------
53 controller : LiteDRAMInterface
54 Interface to LiteDRAMController
55
56 Attributes
57 ----------
58 masters : [LiteDRAMNativePort, ...]
59 LiteDRAM memory ports
60 """
61 def __init__(self, controller):
62 self.controller = controller
63
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
70
71 self.bank_bits = log2_int(self.nbanks, False)
72 self.rank_bits = log2_int(self.nranks, False)
73
74 self.masters = []
75
76 def get_port(self, mode="both", data_width=None, clock_domain="sys", reverse=False):
77 if self.finalized:
78 raise FinalizeError
79
80 if data_width is None:
81 # use internal data_width when no width adaptation is requested
82 data_width = self.controller.data_width
83
84 # Crossbar port ----------------------------------------------------------------------------
85 port = gramNativePort(
86 mode = mode,
87 address_width = self.rca_bits + self.bank_bits - self.rank_bits,
88 data_width = self.controller.data_width,
89 clock_domain = "sys",
90 id = len(self.masters))
91 self.masters.append(port)
92
93 # Clock domain crossing --------------------------------------------------------------------
94 if clock_domain != "sys":
95 new_port = gramNativePort(
96 mode = mode,
97 address_width = port.address_width,
98 data_width = port.data_width,
99 clock_domain = clock_domain,
100 id = port.id)
101 self.submodules += gramNativePortCDC(new_port, port)
102 port = new_port
103
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)
108 else:
109 addr_shift = log2_int(self.controller.data_width//data_width)
110 new_port = gramNativePort(
111 mode = mode,
112 address_width = port.address_width + addr_shift,
113 data_width = data_width,
114 clock_domain = clock_domain,
115 id = port.id)
116 self.submodules += ClockDomainsRenamer(clock_domain)(
117 LiteDRAMNativePortConverter(new_port, port, reverse))
118 port = new_port
119
120 return port
121
122 def elaborate(self, platform):
123 m = Module()
124
125 controller = self.controller
126 nmasters = len(self.masters)
127
128 # Address mapping --------------------------------------------------------------------------
129 cba_shifts = {"ROW_BANK_COL": controller.settings.geom.colbits - controller.address_align}
130 cba_shift = cba_shifts[controller.settings.address_mapping]
131 m_ba = [master.get_bank_address(self.bank_bits, cba_shift) for master in self.masters]
132 m_rca = [master.get_row_column_address(self.bank_bits, self.rca_bits, cba_shift) for master in self.masters]
133
134 master_readys = [0]*nmasters
135 master_wdata_readys = [0]*nmasters
136 master_rdata_valids = [0]*nmasters
137
138 arbiters = [RoundRobin(nmasters) for n in range(self.nbanks)]
139 m.submodules += arbiters
140
141 for nb, arbiter in enumerate(arbiters):
142 bank = getattr(controller, "bank"+str(nb))
143
144 # For each master, determine if another bank locks it ----------------------------------
145 master_locked = []
146 for nm, master in enumerate(self.masters):
147 locked = Signal()
148 for other_nb, other_arbiter in enumerate(arbiters):
149 if other_nb != nb:
150 other_bank = getattr(controller, "bank"+str(other_nb))
151 locked = locked | (other_bank.lock & (other_arbiter.grant == nm))
152 master_locked.append(locked)
153
154 # Arbitrate ----------------------------------------------------------------------------
155 bank_selected = [(ba == nb) & ~locked for ba, locked in zip(m_ba, master_locked)]
156 bank_requested = [bs & master.cmd.valid for bs, master in zip(bank_selected, self.masters)]
157 m.d.comb += [
158 arbiter.request.eq(Cat(*bank_requested)),
159 arbiter.stb.eq(~bank.valid & ~bank.lock)
160 ]
161
162 # Route requests -----------------------------------------------------------------------
163 m.d.comb += [
164 bank.addr.eq(Array(m_rca)[arbiter.grant]),
165 bank.we.eq(Array(self.masters)[arbiter.grant].cmd.we),
166 bank.valid.eq(Array(bank_requested)[arbiter.grant])
167 ]
168 master_readys = [master_ready | ((arbiter.grant == nm) & bank_selected[nm] & bank.ready)
169 for nm, master_ready in enumerate(master_readys)]
170 master_wdata_readys = [master_wdata_ready | ((arbiter.grant == nm) & bank.wdata_ready)
171 for nm, master_wdata_ready in enumerate(master_wdata_readys)]
172 master_rdata_valids = [master_rdata_valid | ((arbiter.grant == nm) & bank.rdata_valid)
173 for nm, master_rdata_valid in enumerate(master_rdata_valids)]
174
175 # Delay write/read signals based on their latency
176 for nm, master_wdata_ready in enumerate(master_wdata_readys):
177 for i in range(self.write_latency):
178 new_master_wdata_ready = Signal()
179 m.d.sync += new_master_wdata_ready.eq(master_wdata_ready)
180 master_wdata_ready = new_master_wdata_ready
181 master_wdata_readys[nm] = master_wdata_ready
182
183 for nm, master_rdata_valid in enumerate(master_rdata_valids):
184 for i in range(self.read_latency):
185 new_master_rdata_valid = Signal()
186 m.d.sync += new_master_rdata_valid.eq(master_rdata_valid)
187 master_rdata_valid = new_master_rdata_valid
188 master_rdata_valids[nm] = master_rdata_valid
189
190 for master, master_ready in zip(self.masters, master_readys):
191 m.d.comb += master.cmd.ready.eq(master_ready)
192 for master, master_wdata_ready in zip(self.masters, master_wdata_readys):
193 m.d.comb += master.wdata.ready.eq(master_wdata_ready)
194 for master, master_rdata_valid in zip(self.masters, master_rdata_valids):
195 m.d.comb += master.rdata.valid.eq(master_rdata_valid)
196
197 # Route data writes ------------------------------------------------------------------------
198 with m.Switch(Cat(*master_wdata_readys)):
199 with m.Case():
200 m.d.comb += [
201 controller.wdata.eq(0),
202 controller.wdata_we.eq(0),
203 ]
204 for nm, master in enumerate(self.masters):
205 with m.Case(2**nm):
206 m.d.comb = [
207 controller.wdata.eq(master.wdata.data),
208 controller.wdata_we.eq(master.wdata.we),
209 ]
210
211 # Route data reads -------------------------------------------------------------------------
212 for master in self.masters:
213 m.d.comb += master.rdata.data.eq(controller.rdata)
214
215 return m