Rename VCD file output
[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 from functools import reduce
8 from operator import or_
9
10 from nmigen import *
11
12 from gram.common import *
13 from gram.core.controller import *
14 from gram.frontend.adaptation import *
15 from gram.compat import RoundRobin
16 import gram.stream as stream
17
18 __ALL__ = ["gramCrossbar"]
19
20 class gramCrossbar(Elaboratable):
21 """Multiplexes LiteDRAMController (slave) between ports (masters)
22
23 To get a port to LiteDRAM, use the `get_port` method. It handles data width
24 conversion and clock domain crossing, returning LiteDRAMNativePort.
25
26 The crossbar routes requests from masters to the BankMachines
27 (bankN.cmd_layout) and connects data path directly to the Multiplexer
28 (data_layout). It performs address translation based on chosen
29 `controller.settings.address_mapping`.
30 Internally, all masters are multiplexed between controller banks based on
31 the bank address (extracted from the presented address). Each bank has
32 a RoundRobin arbiter, that selects from masters that want to access this
33 bank and are not already locked.
34
35 Locks (cmd_layout.lock) make sure that, when a master starts a transaction
36 with given bank (which may include multiple reads/writes), no other bank
37 will be assigned to it during this time.
38 Arbiter (of a bank) considers given master as a candidate for selection if:
39 - given master's command is valid
40 - given master addresses the arbiter's bank
41 - given master is not locked
42 * i.e. it is not during transaction with another bank
43 * i.e. no other bank's arbiter granted permission for this master (with
44 bank.lock being active)
45
46 Data ready/valid signals for banks are routed from bankmachines with
47 a latency that synchronizes them with the data coming over datapath.
48
49 Parameters
50 ----------
51 controller : LiteDRAMInterface
52 Interface to LiteDRAMController
53
54 Attributes
55 ----------
56 masters : [LiteDRAMNativePort, ...]
57 LiteDRAM memory ports
58 """
59
60 def __init__(self, controller):
61 self.controller = controller
62
63 self.rca_bits = controller.address_width
64 self.nbanks = controller.nbanks
65 self.nranks = controller.nranks
66 self.cmd_buffer_depth = controller.settings.cmd_buffer_depth
67 self.read_latency = controller.settings.phy.read_latency + 1
68 self.write_latency = controller.settings.phy.write_latency + 1
69
70 self.bank_bits = log2_int(self.nbanks, False)
71 self.rank_bits = log2_int(self.nranks, False)
72
73 self.masters = []
74 self._pending_submodules = []
75
76 def get_native_port(self):
77 port = gramNativePort(
78 mode="both",
79 address_width=self.rca_bits + self.bank_bits - self.rank_bits,
80 data_width=self.controller.data_width,
81 clock_domain="sync",
82 id=len(self.masters))
83 self.masters.append(port)
84 return port
85
86 def get_port(self, mode="both", data_width=None, clock_domain="sync", reverse=False):
87 if data_width is None:
88 # use internal data_width when no width adaptation is requested
89 data_width = self.controller.data_width
90
91 # Crossbar port ----------------------------------------------------------------------------
92 port = gramNativePort(
93 mode=mode,
94 address_width=self.rca_bits + self.bank_bits - self.rank_bits,
95 data_width=self.controller.data_width,
96 clock_domain="sync",
97 id=len(self.masters))
98 self.masters.append(port)
99
100 # Clock domain crossing --------------------------------------------------------------------
101 if clock_domain != "sync":
102 new_port = gramNativePort(
103 mode=mode,
104 address_width=port.address_width,
105 data_width=port.data_width,
106 clock_domain=clock_domain,
107 id=port.id)
108 self._pending_submodules.append(gramNativePortCDC(new_port, port))
109 port = new_port
110
111 # Data width convertion --------------------------------------------------------------------
112 if data_width != self.controller.data_width:
113 if data_width > self.controller.data_width:
114 addr_shift = -log2_int(data_width//self.controller.data_width)
115 else:
116 addr_shift = log2_int(self.controller.data_width//data_width)
117 new_port = gramNativePort(
118 mode=mode,
119 address_width=port.address_width + addr_shift,
120 data_width=data_width,
121 clock_domain=clock_domain,
122 id=port.id)
123 self._pending_submodules.append(DomainRenamer(clock_domain)(
124 gramNativePortConverter(new_port, port, reverse)))
125 port = new_port
126
127 return port
128
129 def elaborate(self, platform):
130 m = Module()
131
132 m.submodules += self._pending_submodules
133
134 controller = self.controller
135 nmasters = len(self.masters)
136 assert nmasters > 0
137
138 # Address mapping --------------------------------------------------------------------------
139 cba_shifts = {"ROW_BANK_COL": controller.settings.geom.colbits - controller.address_align}
140 cba_shift = cba_shifts[controller.settings.address_mapping]
141 m_ba = [master.get_bank_address(self.bank_bits, cba_shift) for master in self.masters]
142 m_rca = [master.get_row_column_address(self.bank_bits, self.rca_bits, cba_shift) for master in self.masters]
143
144 master_readys = [0]*nmasters
145 master_wdata_readys = [0]*nmasters
146 master_rdata_valids = [0]*nmasters
147
148 arbiters = [RoundRobin(nmasters) for n in range(self.nbanks)]
149 m.submodules += arbiters
150
151 for nb, arbiter in enumerate(arbiters):
152 bank = getattr(controller, "bank"+str(nb))
153
154 # For each master, determine if another bank locks it ----------------------------------
155 master_locked = []
156 for nm, master in enumerate(self.masters):
157 locked = Signal()
158 for other_nb, other_arbiter in enumerate(arbiters):
159 if other_nb != nb:
160 other_bank = getattr(controller, "bank"+str(other_nb))
161 locked = locked | (other_bank.lock & (
162 other_arbiter.grant == nm))
163 master_locked.append(locked)
164
165 # Arbitrate ----------------------------------------------------------------------------
166 bank_selected = [(ba == nb) & ~locked for ba,
167 locked in zip(m_ba, master_locked)]
168 bank_requested = [bs & master.cmd.valid for bs,
169 master in zip(bank_selected, self.masters)]
170 m.d.comb += [
171 arbiter.request.eq(Cat(*bank_requested)),
172 arbiter.stb.eq(~bank.valid & ~bank.lock)
173 ]
174
175 # Route requests -----------------------------------------------------------------------
176 m.d.comb += [
177 bank.addr.eq(Array(m_rca)[arbiter.grant]),
178 bank.we.eq(Array(self.masters)[arbiter.grant].cmd.we),
179 bank.valid.eq(Array(bank_requested)[arbiter.grant])
180 ]
181 master_readys = [master_ready | ((arbiter.grant == nm) & bank_selected[nm] & bank.ready)
182 for nm, master_ready in enumerate(master_readys)]
183 master_wdata_readys = [master_wdata_ready | ((arbiter.grant == nm) & bank.wdata_ready)
184 for nm, master_wdata_ready in enumerate(master_wdata_readys)]
185 master_rdata_valids = [master_rdata_valid | ((arbiter.grant == nm) & bank.rdata_valid)
186 for nm, master_rdata_valid in enumerate(master_rdata_valids)]
187
188 # Delay write/read signals based on their latency
189 for nm, master_wdata_ready in enumerate(master_wdata_readys):
190 for i in range(self.write_latency):
191 new_master_wdata_ready = Signal()
192 m.d.sync += new_master_wdata_ready.eq(master_wdata_ready)
193 master_wdata_ready = new_master_wdata_ready
194 master_wdata_readys[nm] = master_wdata_ready
195
196 for nm, master_rdata_valid in enumerate(master_rdata_valids):
197 for i in range(self.read_latency):
198 new_master_rdata_valid = Signal()
199 m.d.sync += new_master_rdata_valid.eq(master_rdata_valid)
200 master_rdata_valid = new_master_rdata_valid
201 master_rdata_valids[nm] = master_rdata_valid
202
203 for master, master_ready in zip(self.masters, master_readys):
204 m.d.comb += master.cmd.ready.eq(master_ready)
205 for master, master_wdata_ready in zip(self.masters, master_wdata_readys):
206 m.d.comb += master.wdata.ready.eq(master_wdata_ready)
207 for master, master_rdata_valid in zip(self.masters, master_rdata_valids):
208 m.d.comb += master.rdata.valid.eq(master_rdata_valid)
209
210 # Route data writes ------------------------------------------------------------------------
211 with m.Switch(Cat(*master_wdata_readys)):
212 for nm, master in enumerate(self.masters):
213 with m.Case(2**nm):
214 m.d.comb += [
215 controller.wdata.eq(master.wdata.data),
216 controller.wdata_we.eq(master.wdata.we),
217 ]
218 with m.Case():
219 m.d.comb += [
220 controller.wdata.eq(0),
221 controller.wdata_we.eq(0),
222 ]
223
224 # Route data reads -------------------------------------------------------------------------
225 for master in self.masters:
226 m.d.comb += master.rdata.data.eq(controller.rdata)
227
228 return m