cded5ae29f23c6e6784b198d525e2474115d9c11
[litex.git] / litex / soc / interconnect / csr_bus.py
1 # This file is Copyright (c) 2015 Sebastien Bourdeauducq <sb@m-labs.hk>
2 # This file is Copyright (c) 2015-2018 Florent Kermarrec <florent@enjoy-digital.fr>
3 # This file is Copyright (c) 2016-2019 Tim 'mithro' Ansell <me@mith.ro>
4 # License: BSD
5
6 """
7 CSR-2 bus
8 =========
9
10 The CSR-2 bus is a low-bandwidth, resource-sensitive bus designed for accessing
11 the configuration and status registers of cores from software.
12 """
13
14 from functools import reduce
15 from operator import or_
16
17 from migen import *
18 from migen.genlib.record import *
19 from migen.genlib.misc import chooser
20 from migen.util.misc import xdir
21
22 from litex.soc.interconnect import csr
23 from litex.soc.interconnect.csr import CSRStorage
24
25 # CSR Definition -----------------------------------------------------------------------------------
26
27 _layout = [
28 ("adr", "address_width", DIR_M_TO_S),
29 ("we", 1, DIR_M_TO_S),
30 ("dat_w", "data_width", DIR_M_TO_S),
31 ("dat_r", "data_width", DIR_S_TO_M)
32 ]
33
34
35 class Interface(Record):
36 def __init__(self, data_width=8, address_width=14, alignment=32):
37 self.data_width = data_width
38 self.address_width = address_width
39 self.alignment = alignment
40 Record.__init__(self, set_layout_parameters(_layout,
41 data_width = data_width,
42 address_width = address_width))
43 self.adr.reset_less = True
44 self.dat_w.reset_less = True
45 self.dat_r.reset_less = True
46
47 @classmethod
48 def like(self, other):
49 return Interface(len(other.dat_w),
50 len(other.adr))
51
52 def write(self, adr, dat):
53 yield self.adr.eq(adr)
54 yield self.dat_w.eq(dat)
55 yield self.we.eq(1)
56 yield
57 yield self.we.eq(0)
58
59 def read(self, adr):
60 yield self.adr.eq(adr)
61 yield
62 yield
63 return (yield self.dat_r)
64
65 # CSR Interconnect ---------------------------------------------------------------------------------
66
67 class Interconnect(Module):
68 def __init__(self, master, slaves):
69 self.comb += master.connect(*slaves)
70
71
72 class InterconnectShared(Module):
73 def __init__(self, masters, slaves):
74 intermediate = Interface.like(masters[0])
75 self.comb += [
76 intermediate.adr.eq(reduce(or_, [masters[i].adr for i in range(len(masters))])),
77 intermediate.we.eq(reduce(or_, [masters[i].we for i in range(len(masters))])),
78 intermediate.dat_w.eq(reduce(or_, [masters[i].dat_w for i in range(len(masters))]))
79 ]
80 for i in range(len(masters)):
81 self.comb += masters[i].dat_r.eq(intermediate.dat_r)
82 self.comb += intermediate.connect(*slaves)
83
84 # CSR SRAM -----------------------------------------------------------------------------------------
85
86 class SRAM(Module):
87 def __init__(self, mem_or_size, address, read_only=None, init=None, bus=None, paging=0x800, soc_bus_data_width=32):
88 if bus is None:
89 bus = Interface()
90 self.bus = bus
91 aligned_paging = paging//(soc_bus_data_width//8)
92 data_width = len(self.bus.dat_w)
93 if isinstance(mem_or_size, Memory):
94 mem = mem_or_size
95 else:
96 mem = Memory(data_width, mem_or_size//(data_width//8), init=init)
97 mem_size = int(mem.width*mem.depth/8)
98 csrw_per_memw = (mem.width + data_width - 1)//data_width
99 word_bits = log2_int(csrw_per_memw)
100 page_bits = log2_int((mem.depth*csrw_per_memw + aligned_paging - 1)//aligned_paging, False)
101 if page_bits:
102 self._page = CSRStorage(page_bits, name=mem.name_override + "_page")
103 print("WARNING: SRAM CSR memory will require paged access.")
104 else:
105 self._page = None
106 if read_only is None:
107 if hasattr(mem, "bus_read_only"):
108 read_only = mem.bus_read_only
109 else:
110 read_only = False
111
112 # # #
113
114 port = mem.get_port(write_capable=not read_only)
115 self.specials += mem, port
116
117 sel = Signal()
118 sel_r = Signal()
119 self.sync += sel_r.eq(sel)
120 self.comb += sel.eq(self.bus.adr[log2_int(aligned_paging):] == address)
121
122 if word_bits:
123 word_index = Signal(word_bits, reset_less=True)
124 word_expanded = Signal(csrw_per_memw*data_width)
125 self.sync += word_index.eq(self.bus.adr[:word_bits])
126 self.comb += [
127 word_expanded.eq(port.dat_r),
128 If(sel_r,
129 chooser(word_expanded, word_index, self.bus.dat_r, n=csrw_per_memw, reverse=True)
130 )
131 ]
132 if not read_only:
133 wregs = []
134 for i in range(csrw_per_memw-1):
135 wreg = Signal(data_width, reset_less=True)
136 self.sync += If(sel & self.bus.we & (self.bus.adr[:word_bits] == i), wreg.eq(self.bus.dat_w))
137 wregs.append(wreg)
138 memword_chunks = [self.bus.dat_w] + list(reversed(wregs))
139 self.comb += [
140 port.we.eq(sel & self.bus.we & (self.bus.adr[:word_bits] == csrw_per_memw - 1)),
141 port.dat_w.eq(Cat(*memword_chunks))
142 ]
143 else:
144 self.comb += If(sel_r, self.bus.dat_r.eq(port.dat_r))
145 if not read_only:
146 self.comb += [
147 port.we.eq(sel & self.bus.we),
148 port.dat_w.eq(self.bus.dat_w)
149 ]
150
151 if self._page is None:
152 self.comb += port.adr.eq(self.bus.adr[word_bits:word_bits+len(port.adr)])
153 else:
154 pv = self._page.storage
155 self.comb += port.adr.eq(Cat(self.bus.adr[word_bits:word_bits+len(port.adr)-len(pv)], pv))
156
157 def get_csrs(self):
158 if self._page is None:
159 return []
160 else:
161 return [self._page]
162
163 # CSR Bank -----------------------------------------------------------------------------------------
164
165 class CSRBank(csr.GenericBank):
166 def __init__(self, description, address=0, bus=None, paging=0x800, soc_bus_data_width=32):
167 if bus is None:
168 bus = Interface()
169 self.bus = bus
170 aligned_paging = paging//(soc_bus_data_width//8)
171
172 # # #
173
174 csr.GenericBank.__init__(self, description, len(self.bus.dat_w))
175
176 sel = Signal()
177 self.comb += sel.eq(self.bus.adr[log2_int(aligned_paging):] == address)
178
179 for i, c in enumerate(self.simple_csrs):
180 self.comb += [
181 c.r.eq(self.bus.dat_w[:c.size]),
182 c.re.eq(sel & \
183 self.bus.we & \
184 (self.bus.adr[:self.decode_bits] == i)),
185 c.we.eq(sel & \
186 ~self.bus.we & \
187 (self.bus.adr[:self.decode_bits] == i)),
188 ]
189
190 brcases = dict((i, self.bus.dat_r.eq(c.w)) for i, c in enumerate(self.simple_csrs))
191 self.sync += [
192 self.bus.dat_r.eq(0),
193 If(sel, Case(self.bus.adr[:self.decode_bits], brcases))
194 ]
195
196
197 # address_map(name, memory) returns the CSR offset at which to map
198 # the CSR object (register bank or memory).
199 # If memory=None, the object is the register bank of object source.name.
200 # Otherwise, it is a memory object belonging to source.name.
201 # address_map is called exactly once for each object at each call to
202 # scan(), so it can have side effects.
203 class CSRBankArray(Module):
204 def __init__(self, source, address_map, *ifargs, paging=0x800, soc_bus_data_width=32, **ifkwargs):
205 self.source = source
206 self.address_map = address_map
207 self.paging = paging
208 self.soc_bus_data_width = soc_bus_data_width
209 self.scan(ifargs, ifkwargs)
210
211 def scan(self, ifargs, ifkwargs):
212 self.banks = []
213 self.srams = []
214 self.constants = []
215 for name, obj in xdir(self.source, True):
216 if hasattr(obj, "get_csrs"):
217 csrs = obj.get_csrs()
218 else:
219 csrs = []
220 if hasattr(obj, "get_memories"):
221 memories = obj.get_memories()
222 for memory in memories:
223 if isinstance(memory, tuple):
224 read_only, memory = memory
225 else:
226 read_only = False
227 mapaddr = self.address_map(name, memory)
228 if mapaddr is None:
229 continue
230 sram_bus = Interface(*ifargs, **ifkwargs)
231 mmap = SRAM(memory, mapaddr,
232 read_only = read_only,
233 bus = sram_bus,
234 paging = self.paging)
235 self.submodules += mmap
236 csrs += mmap.get_csrs()
237 self.srams.append((name, memory, mapaddr, mmap))
238 if hasattr(obj, "get_constants"):
239 for constant in obj.get_constants():
240 self.constants.append((name, constant))
241 if csrs:
242 mapaddr = self.address_map(name, None)
243 if mapaddr is None:
244 continue
245 bank_bus = Interface(*ifargs, **ifkwargs)
246 rmap = CSRBank(csrs, mapaddr,
247 bus = bank_bus,
248 paging = self.paging,
249 soc_bus_data_width = self.soc_bus_data_width)
250 self.submodules += rmap
251 self.banks.append((name, csrs, mapaddr, rmap))
252
253 def get_rmaps(self):
254 return [rmap for name, csrs, mapaddr, rmap in self.banks]
255
256 def get_mmaps(self):
257 return [mmap for name, memory, mapaddr, mmap in self.srams]
258
259 def get_buses(self):
260 return [i.bus for i in self.get_rmaps() + self.get_mmaps()]