1ca18e7dabc4bbad03674d6104a5621d16199994
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(Elaboratable
):
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
= gramNativePort(
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
= gramNativePort(
97 address_width
= port
.address_width
,
98 data_width
= port
.data_width
,
99 clock_domain
= clock_domain
,
101 self
.submodules
+= gramNativePortCDC(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
= gramNativePort(
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 elaborate(self
, platform
):
125 controller
= self
.controller
126 nmasters
= len(self
.masters
)
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
]
134 master_readys
= [0]*nmasters
135 master_wdata_readys
= [0]*nmasters
136 master_rdata_valids
= [0]*nmasters
138 arbiters
= [RoundRobin(nmasters
) for n
in range(self
.nbanks
)]
139 m
.submodules
+= arbiters
141 for nb
, arbiter
in enumerate(arbiters
):
142 bank
= getattr(controller
, "bank"+str(nb
))
144 # For each master, determine if another bank locks it ----------------------------------
146 for nm
, master
in enumerate(self
.masters
):
148 for other_nb
, other_arbiter
in enumerate(arbiters
):
150 other_bank
= getattr(controller
, "bank"+str(other_nb
))
151 locked
= locked |
(other_bank
.lock
& (other_arbiter
.grant
== nm
))
152 master_locked
.append(locked
)
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
)]
158 arbiter
.request
.eq(Cat(*bank_requested
)),
159 arbiter
.stb
.eq(~bank
.valid
& ~bank
.lock
)
162 # Route requests -----------------------------------------------------------------------
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
])
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
)]
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
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
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
)
197 # Route data writes ------------------------------------------------------------------------
198 with m
.Switch(Cat(*master_wdata_readys
)):
201 controller
.wdata
.eq(0),
202 controller
.wdata_we
.eq(0),
204 for nm
, master
in enumerate(self
.masters
):
207 controller
.wdata
.eq(master
.wdata
.data
),
208 controller
.wdata_we
.eq(master
.wdata
.we
),
211 # Route data reads -------------------------------------------------------------------------
212 for master
in self
.masters
:
213 m
.d
.comb
+= master
.rdata
.data
.eq(controller
.rdata
)