33from nmigen import tracer
44
55
6- __all__ = ["CSRElement" , "CSRMultiplexer " ]
6+ __all__ = ["CSRElement" , "CSRInterface" , "CSRDecoder " ]
77
88
99class CSRElement (Record ):
@@ -40,8 +40,7 @@ def __init__(self, width, access, *, name=None, src_loc_at=0):
4040 if access not in ("r" , "w" , "rw" ):
4141 raise ValueError ("Access mode must be one of \" r\" , \" w\" , or \" rw\" , not {!r}"
4242 .format (access ))
43-
44- self .width = int (width )
43+ self .width = width
4544 self .access = access
4645
4746 layout = []
@@ -58,31 +57,84 @@ def __init__(self, width, access, *, name=None, src_loc_at=0):
5857 super ().__init__ (layout , name = name , src_loc_at = 1 )
5958
6059
61- class CSRMultiplexer ( Elaboratable ):
60+ class CSRInterface ( Record ):
6261 """CPU-side CSR interface.
6362
64- A low-level interface to a set of peripheral CSR registers that implements address-based
65- multiplexing and atomic updates of wide registers.
63+ A low-level interface to a set of atomically readable and writable peripheral CSR registers.
6664
6765 Operation
6866 ---------
6967
70- The CSR multiplexer splits each CSR register into chunks according to its data width. Each
71- chunk is assigned an address, and the first chunk of each register always has the provided
72- minimum alignment. This allows accessing CSRs of any size using any datapath width.
68+ CSR registers mapped to the CSR bus are split into chunks according to the bus data width.
69+ Each chunk is assigned a consecutive address on the bus. This allows accessing CSRs of any
70+ size using any datapath width.
7371
7472 When the first chunk of a register is read, the value of a register is captured, and reads
7573 from subsequent chunks of the same register return the captured values. When any chunk except
7674 the last chunk of a register is written, the written value is captured; a write to the last
7775 chunk writes the captured value to the register. This allows atomically accessing CSRs larger
7876 than datapath width.
7977
80- Reads to padding bytes return zeroes, and writes to padding bytes are ignored.
78+ Parameters
79+ ----------
80+ addr_width : int
81+ Address width. At most ``(2 ** addr_width) * data_width`` register bits will be available.
82+ data_width : int
83+ Data width. Registers are accessed in ``data_width`` sized chunks.
84+ name : str
85+ Name of the underlying record.
8186
82- Writes are registered, and add 1 cycle of latency.
87+ Attributes
88+ ----------
89+ addr : Signal(addr_width)
90+ Address for reads and writes.
91+ r_data : Signal(data_width)
92+ Read data. Valid on the next cycle after ``r_stb`` is asserted.
93+ r_stb : Signal()
94+ Read strobe. If ``addr`` points to the first chunk of a register, captures register value
95+ and causes read side effects to be performed (if any). If ``addr`` points to any chunk
96+ of a register, latches the captured value to ``r_data``. Otherwise, latches zero
97+ to ``r_data``.
98+ w_data : Signal(data_width)
99+ Write data. Must be valid when ``w_stb`` is asserted.
100+ w_stb : Signal()
101+ Write strobe. If ``addr`` points to the last chunk of a register, writes captured value
102+ to the register and causes write side effects to be performed (if any). If ``addr`` points
103+ to any chunk of a register, latches ``w_data`` to the captured value. Otherwise, does
104+ nothing.
105+ """
106+
107+ def __init__ (self , * , addr_width , data_width , name = None ):
108+ if not isinstance (addr_width , int ) or addr_width <= 0 :
109+ raise ValueError ("Address width must be a positive integer, not {!r}"
110+ .format (addr_width ))
111+ if not isinstance (data_width , int ) or data_width <= 0 :
112+ raise ValueError ("Data width must be a positive integer, not {!r}"
113+ .format (data_width ))
114+ self .addr_width = addr_width
115+ self .data_width = data_width
83116
84- Wide registers
85- --------------
117+ super ().__init__ ([
118+ ("addr" , addr_width ),
119+ ("r_data" , data_width ),
120+ ("r_stb" , 1 ),
121+ ("w_data" , data_width ),
122+ ("w_stb" , 1 ),
123+ ], name = name , src_loc_at = 1 )
124+
125+
126+ class CSRDecoder (Elaboratable ):
127+ """CSR bus decoder.
128+
129+ An address-based multiplexer for CSR registers implementing atomic updates.
130+
131+ Latency
132+ -------
133+
134+ Writes are registered, and are performed 1 cycle after ``w_stb`` is asserted.
135+
136+ Alignment
137+ ---------
86138
87139 Because the CSR bus conserves logic and routing resources, it is common to e.g. access
88140 a CSR bus with an *n*-bit data path from a CPU with a *k*-bit datapath (*k>n*) in cases
@@ -107,56 +159,29 @@ class CSRMultiplexer(Elaboratable):
107159 Parameters
108160 ----------
109161 addr_width : int
110- Address width. At most ``(2 ** addr_width) * data_width`` register bits will be available .
162+ Address width. See :class:`CSRInterface` .
111163 data_width : int
112- Data width. Registers are accessed in ``data_width`` sized chunks .
164+ Data width. See :class:`CSRInterface` .
113165 alignment : int
114166 Register alignment. The address assigned to each register will be a multiple of
115167 ``2 ** alignment``.
116168
117169 Attributes
118170 ----------
119- addr : Signal(addr_width)
120- Address for reads and writes.
121- r_data : Signal(data_width)
122- Read data. Valid on the next cycle after ``r_stb`` is asserted.
123- r_stb : Signal()
124- Read strobe. If ``addr`` points to the first chunk of a register, captures register value
125- and causes read side effects to be performed (if any). If ``addr`` points to any chunk
126- of a register, latches the captured value to ``r_data``. Otherwise, latches zero
127- to ``r_data``.
128- w_data : Signal(data_width)
129- Write data. Must be valid when ``w_stb`` is asserted.
130- w_stb : Signal()
131- Write strobe. If ``addr`` points to the last chunk of a register, writes captured value
132- to the register and causes write side effects to be performed (if any). If ``addr`` points
133- to any chunk of a register, latches ``w_data`` to the captured value. Otherwise, does
134- nothing.
171+ bus : :class:`CSRInterface`
172+ CSR bus providing access to registers.
135173 """
136174 def __init__ (self , * , addr_width , data_width , alignment = 0 ):
137- if not isinstance (addr_width , int ) or addr_width <= 0 :
138- raise ValueError ("Address width must be a positive integer, not {!r}"
139- .format (addr_width ))
140- if not isinstance (data_width , int ) or data_width <= 0 :
141- raise ValueError ("Data width must be a positive integer, not {!r}"
142- .format (data_width ))
175+ self .bus = CSRInterface (addr_width = addr_width , data_width = data_width )
176+
143177 if not isinstance (alignment , int ) or alignment < 0 :
144178 raise ValueError ("Alignment must be a non-negative integer, not {!r}"
145179 .format (alignment ))
146-
147- self .addr_width = int (addr_width )
148- self .data_width = int (data_width )
149- self .alignment = alignment
180+ self .alignment = alignment
150181
151182 self ._next_addr = 0
152183 self ._elements = dict ()
153184
154- self .addr = Signal (addr_width )
155- self .r_data = Signal (data_width )
156- self .r_stb = Signal ()
157- self .w_data = Signal (data_width )
158- self .w_stb = Signal ()
159-
160185 def add (self , element ):
161186 """Add a register.
162187
@@ -176,7 +201,7 @@ def add(self, element):
176201 .format (element ))
177202
178203 addr = self .align_to (self .alignment )
179- self ._next_addr += (element .width + self .data_width - 1 ) // self .data_width
204+ self ._next_addr += (element .width + self .bus . data_width - 1 ) // self . bus .data_width
180205 size = self .align_to (self .alignment ) - addr
181206 self ._elements [addr ] = element , size
182207 return addr , size
@@ -222,33 +247,33 @@ def elaborate(self, platform):
222247 # arithmetic comparisons, since some toolchains (e.g. Yosys) are too eager to infer
223248 # carry chains for comparisons, even with a constant. (Register sizes don't have
224249 # to be powers of 2.)
225- with m .Switch (self .addr ):
250+ with m .Switch (self .bus . addr ):
226251 for chunk_offset in range (elem_size ):
227- chunk_slice = slice (chunk_offset * self .data_width ,
228- (chunk_offset + 1 ) * self .data_width )
252+ chunk_slice = slice (chunk_offset * self .bus . data_width ,
253+ (chunk_offset + 1 ) * self .bus . data_width )
229254 with m .Case (elem_addr + chunk_offset ):
230255 if "r" in elem .access :
231- chunk_r_stb = Signal (self .data_width ,
256+ chunk_r_stb = Signal (self .bus . data_width ,
232257 name = "{}__r_stb_{}" .format (elem .name , chunk_offset ))
233258 r_data_fanin |= Mux (chunk_r_stb , shadow [chunk_slice ], 0 )
234259 if chunk_offset == 0 :
235- m .d .comb += elem .r_stb .eq (self .r_stb )
236- with m .If (self .r_stb ):
260+ m .d .comb += elem .r_stb .eq (self .bus . r_stb )
261+ with m .If (self .bus . r_stb ):
237262 m .d .sync += shadow .eq (elem .r_data )
238263 # Delay by 1 cycle, allowing reads to be pipelined.
239- m .d .sync += chunk_r_stb .eq (self .r_stb )
264+ m .d .sync += chunk_r_stb .eq (self .bus . r_stb )
240265
241266 if "w" in elem .access :
242267 if chunk_offset == elem_size - 1 :
243268 # Delay by 1 cycle, avoiding combinatorial paths through
244269 # the CSR bus and into CSR registers.
245- m .d .sync += elem .w_stb .eq (self .w_stb )
246- with m .If (self .w_stb ):
247- m .d .sync += shadow [chunk_slice ].eq (self .w_data )
270+ m .d .sync += elem .w_stb .eq (self .bus . w_stb )
271+ with m .If (self .bus . w_stb ):
272+ m .d .sync += shadow [chunk_slice ].eq (self .bus . w_data )
248273
249274 with m .Default ():
250275 m .d .sync += shadow .eq (0 )
251276
252- m .d .comb += self .r_data .eq (r_data_fanin )
277+ m .d .comb += self .bus . r_data .eq (r_data_fanin )
253278
254279 return m
0 commit comments