11import enum
22from nmigen import *
33
4+ from ..memory import MemoryMap
5+
46
57__all__ = ["Element" , "Interface" , "Decoder" ]
68
@@ -73,6 +75,9 @@ def __init__(self, width, access, *, name=None, src_loc_at=0):
7375 ]
7476 super ().__init__ (layout , name = name , src_loc_at = 1 )
7577
78+ # FIXME: get rid of this
79+ __hash__ = object .__hash__
80+
7681
7782class Interface (Record ):
7883 """CPU-side CSR interface.
@@ -189,61 +194,27 @@ class Decoder(Elaboratable):
189194 CSR bus providing access to registers.
190195 """
191196 def __init__ (self , * , addr_width , data_width , alignment = 0 ):
192- self .bus = Interface (addr_width = addr_width , data_width = data_width )
197+ self .bus = Interface (addr_width = addr_width , data_width = data_width )
198+ self ._map = MemoryMap (addr_width = addr_width , data_width = data_width , alignment = alignment )
193199
194- if not isinstance (alignment , int ) or alignment < 0 :
195- raise ValueError ("Alignment must be a non-negative integer, not {!r}"
196- .format (alignment ))
197- self .alignment = alignment
200+ def align_to (self , alignment ):
201+ """Align the implicit address of the next register.
198202
199- self ._next_addr = 0
200- self ._elements = dict ()
203+ See :meth:`MemoryMap.align_to` for details.
204+ """
205+ return self ._map .align_to (alignment )
201206
202- def add (self , element ):
207+ def add (self , element , * , addr = None , alignment = None ):
203208 """Add a register.
204209
205- Arguments
206- ---------
207- element : :class:`Element`
208- Interface of the register.
209-
210- Return value
211- ------------
212- An ``(addr, size)`` tuple, where ``addr`` is the address assigned to the first chunk of
213- the register, and ``size`` is the amount of chunks it takes, which may be greater than
214- ``element.size // self.data_width`` due to alignment.
210+ See :meth:`MemoryMap.add_resource` for details.
215211 """
216212 if not isinstance (element , Element ):
217213 raise TypeError ("Element must be an instance of csr.Element, not {!r}"
218214 .format (element ))
219215
220- addr = self .align_to (self .alignment )
221- self ._next_addr += (element .width + self .bus .data_width - 1 ) // self .bus .data_width
222- size = self .align_to (self .alignment ) - addr
223- self ._elements [addr ] = element , size
224- return addr , size
225-
226- def align_to (self , alignment ):
227- """Align the next register explicitly.
228-
229- Arguments
230- ---------
231- alignment : int
232- Register alignment. The address assigned to the next register will be a multiple of
233- ``2 ** alignment`` or ``2 ** self.alignment``, whichever is greater.
234-
235- Return value
236- ------------
237- Address of the next register.
238- """
239- if not isinstance (alignment , int ) or alignment < 0 :
240- raise ValueError ("Alignment must be a non-negative integer, not {!r}"
241- .format (alignment ))
242-
243- align_chunks = 1 << alignment
244- if self ._next_addr % align_chunks != 0 :
245- self ._next_addr += align_chunks - (self ._next_addr % align_chunks )
246- return self ._next_addr
216+ size = (element .width + self .bus .data_width - 1 ) // self .bus .data_width
217+ return self ._map .add_resource (element , size = size , addr = addr , alignment = alignment )
247218
248219 def elaborate (self , platform ):
249220 m = Module ()
@@ -255,7 +226,7 @@ def elaborate(self, platform):
255226 # 2-AND or 2-OR gates.
256227 r_data_fanin = 0
257228
258- for elem_addr , (elem , elem_size ) in self ._elements . items ():
229+ for elem , (elem_start , elem_end ) in self ._map . resources ():
259230 shadow = Signal (elem .width , name = "{}__shadow" .format (elem .name ))
260231 if elem .access .writable ():
261232 m .d .comb += elem .w_data .eq (shadow )
@@ -265,28 +236,29 @@ def elaborate(self, platform):
265236 # carry chains for comparisons, even with a constant. (Register sizes don't have
266237 # to be powers of 2.)
267238 with m .Switch (self .bus .addr ):
268- for chunk_offset in range (elem_size ):
269- chunk_slice = slice (chunk_offset * self .bus .data_width ,
270- (chunk_offset + 1 ) * self .bus .data_width )
271- with m .Case (elem_addr + chunk_offset ):
239+ for chunk_offset , chunk_addr in enumerate (range (elem_start , elem_end )):
240+ with m .Case (chunk_addr ):
241+ shadow_slice = shadow [chunk_offset * self .bus .data_width :
242+ (chunk_offset + 1 ) * self .bus .data_width ]
243+
272244 if elem .access .readable ():
273245 chunk_r_stb = Signal (self .bus .data_width ,
274246 name = "{}__r_stb_{}" .format (elem .name , chunk_offset ))
275- r_data_fanin |= Mux (chunk_r_stb , shadow [ chunk_slice ] , 0 )
276- if chunk_offset == 0 :
247+ r_data_fanin |= Mux (chunk_r_stb , shadow_slice , 0 )
248+ if chunk_addr == elem_start :
277249 m .d .comb += elem .r_stb .eq (self .bus .r_stb )
278250 with m .If (self .bus .r_stb ):
279251 m .d .sync += shadow .eq (elem .r_data )
280252 # Delay by 1 cycle, allowing reads to be pipelined.
281253 m .d .sync += chunk_r_stb .eq (self .bus .r_stb )
282254
283255 if elem .access .writable ():
284- if chunk_offset == elem_size - 1 :
256+ if chunk_addr == elem_end - 1 :
285257 # Delay by 1 cycle, avoiding combinatorial paths through
286258 # the CSR bus and into CSR registers.
287259 m .d .sync += elem .w_stb .eq (self .bus .w_stb )
288260 with m .If (self .bus .w_stb ):
289- m .d .sync += shadow [ chunk_slice ] .eq (self .bus .w_data )
261+ m .d .sync += shadow_slice .eq (self .bus .w_data )
290262
291263 with m .Default ():
292264 m .d .sync += shadow .eq (0 )
0 commit comments