3
3
4
4
from .. import tracer
5
5
from .ast import *
6
- from .ir import Elaboratable , Instance
6
+ from .ir import Elaboratable , Instance , Fragment
7
7
8
8
9
9
__all__ = ["Memory" , "ReadPort" , "WritePort" , "DummyPort" ]
10
10
11
11
12
- class Memory :
12
+ class Memory ( Elaboratable ) :
13
13
"""A word addressable storage.
14
14
15
15
Parameters
@@ -58,6 +58,8 @@ def __init__(self, *, width, depth, init=None, name=None, attrs=None, simulate=T
58
58
.format (name or "memory" , addr )))
59
59
60
60
self .init = init
61
+ self ._read_ports = []
62
+ self ._write_ports = []
61
63
62
64
@property
63
65
def init (self ):
@@ -116,6 +118,96 @@ def __getitem__(self, index):
116
118
"""Simulation only."""
117
119
return self ._array [index ]
118
120
121
+ def elaborate (self , platform ):
122
+ init = "" .join (format (Const (elem , unsigned (self .width )).value , f"0{ self .width } b" ) for elem in reversed (self .init ))
123
+ init = Const (int (init or "0" , 2 ), len (self .init ) * self .width )
124
+ rd_clk = []
125
+ rd_clk_enable = 0
126
+ rd_transparency_mask = 0
127
+ for index , port in enumerate (self ._read_ports ):
128
+ if port .domain != "comb" :
129
+ rd_clk .append (ClockSignal (port .domain ))
130
+ rd_clk_enable |= 1 << index
131
+ if port .transparent :
132
+ for write_index , write_port in enumerate (self ._write_ports ):
133
+ if port .domain == write_port .domain :
134
+ rd_transparency_mask |= 1 << (index * len (self ._write_ports ) + write_index )
135
+ else :
136
+ rd_clk .append (Const (0 , 1 ))
137
+ f = Instance ("$mem_v2" ,
138
+ * (("a" , attr , value ) for attr , value in self .attrs .items ()),
139
+ p_SIZE = self .depth ,
140
+ p_OFFSET = 0 ,
141
+ p_ABITS = Shape .cast (range (self .depth )).width ,
142
+ p_WIDTH = self .width ,
143
+ p_INIT = init ,
144
+ p_RD_PORTS = len (self ._read_ports ),
145
+ p_RD_CLK_ENABLE = Const (rd_clk_enable , len (self ._read_ports )) if self ._read_ports else Const (0 , 1 ),
146
+ p_RD_CLK_POLARITY = Const (- 1 , unsigned (len (self ._read_ports ))) if self ._read_ports else Const (0 , 1 ),
147
+ p_RD_TRANSPARENCY_MASK = Const (rd_transparency_mask , max (1 , len (self ._read_ports ) * len (self ._write_ports ))),
148
+ p_RD_COLLISION_X_MASK = Const (0 , max (1 , len (self ._read_ports ) * len (self ._write_ports ))),
149
+ p_RD_WIDE_CONTINUATION = Const (0 , len (self ._read_ports )) if self ._read_ports else Const (0 , 1 ),
150
+ p_RD_CE_OVER_SRST = Const (0 , len (self ._read_ports )) if self ._read_ports else Const (0 , 1 ),
151
+ p_RD_ARST_VALUE = Const (0 , len (self ._read_ports ) * self .width ),
152
+ p_RD_SRST_VALUE = Const (0 , len (self ._read_ports ) * self .width ),
153
+ p_RD_INIT_VALUE = Const (0 , len (self ._read_ports ) * self .width ),
154
+ p_WR_PORTS = len (self ._write_ports ),
155
+ p_WR_CLK_ENABLE = Const (- 1 , unsigned (len (self ._write_ports ))) if self ._write_ports else Const (0 , 1 ),
156
+ p_WR_CLK_POLARITY = Const (- 1 , unsigned (len (self ._write_ports ))) if self ._write_ports else Const (0 , 1 ),
157
+ p_WR_PRIORITY_MASK = Const (0 , len (self ._write_ports ) * len (self ._write_ports )) if self ._write_ports else Const (0 , 1 ),
158
+ p_WR_WIDE_CONTINUATION = Const (0 , len (self ._write_ports )) if self ._write_ports else Const (0 , 1 ),
159
+ i_RD_CLK = Cat (rd_clk ),
160
+ i_RD_EN = Cat (port .en for port in self ._read_ports ),
161
+ i_RD_ARST = Const (0 , len (self ._read_ports )),
162
+ i_RD_SRST = Const (0 , len (self ._read_ports )),
163
+ i_RD_ADDR = Cat (port .addr for port in self ._read_ports ),
164
+ o_RD_DATA = Cat (port .data for port in self ._read_ports ),
165
+ i_WR_CLK = Cat (ClockSignal (port .domain ) for port in self ._write_ports ),
166
+ i_WR_EN = Cat (Cat (en_bit .replicate (port .granularity ) for en_bit in port .en ) for port in self ._write_ports ),
167
+ i_WR_ADDR = Cat (port .addr for port in self ._write_ports ),
168
+ i_WR_DATA = Cat (port .data for port in self ._write_ports ),
169
+ )
170
+ for port in self ._read_ports :
171
+ port ._MustUse__used = True
172
+ if port .domain == "comb" :
173
+ # Asynchronous port
174
+ f .add_statements (port .data .eq (self ._array [port .addr ]))
175
+ f .add_driver (port .data )
176
+ else :
177
+ # Synchronous port
178
+ data = self ._array [port .addr ]
179
+ for write_port in self ._write_ports :
180
+ if port .domain == write_port .domain and port .transparent :
181
+ if len (write_port .en ) > 1 :
182
+ parts = []
183
+ for index , en_bit in enumerate (write_port .en ):
184
+ offset = index * write_port .granularity
185
+ bits = slice (offset , offset + write_port .granularity )
186
+ cond = en_bit & (port .addr == write_port .addr )
187
+ parts .append (Mux (cond , write_port .data [bits ], data [bits ]))
188
+ data = Cat (parts )
189
+ else :
190
+ data = Mux (write_port .en , write_port .data , data )
191
+ f .add_statements (
192
+ Switch (port .en , {
193
+ 1 : port .data .eq (data )
194
+ })
195
+ )
196
+ f .add_driver (port .data , port .domain )
197
+ for port in self ._write_ports :
198
+ port ._MustUse__used = True
199
+ if len (port .en ) > 1 :
200
+ for index , en_bit in enumerate (port .en ):
201
+ offset = index * port .granularity
202
+ bits = slice (offset , offset + port .granularity )
203
+ write_data = self ._array [port .addr ][bits ].eq (port .data [bits ])
204
+ f .add_statements (Switch (en_bit , { 1 : write_data }))
205
+ else :
206
+ write_data = self ._array [port .addr ].eq (port .data )
207
+ f .add_statements (Switch (port .en , { 1 : write_data }))
208
+ for signal in self ._array :
209
+ f .add_driver (signal , port .domain )
210
+ return f
119
211
120
212
class ReadPort (Elaboratable ):
121
213
"""A memory read port.
@@ -142,9 +234,7 @@ class ReadPort(Elaboratable):
142
234
data : Signal(memory.width), out
143
235
Read data.
144
236
en : Signal or Const, in
145
- Read enable. If asserted, ``data`` is updated with the word stored at ``addr``. Note that
146
- transparent ports cannot assign ``en`` (which is hardwired to 1 instead), as doing so is
147
- currently not supported by Yosys.
237
+ Read enable. If asserted, ``data`` is updated with the word stored at ``addr``.
148
238
149
239
Exceptions
150
240
----------
@@ -162,59 +252,19 @@ def __init__(self, memory, *, domain="sync", transparent=True, src_loc_at=0):
162
252
name = "{}_r_addr" .format (memory .name ), src_loc_at = 1 + src_loc_at )
163
253
self .data = Signal (memory .width ,
164
254
name = "{}_r_data" .format (memory .name ), src_loc_at = 1 + src_loc_at )
165
- if self .domain != "comb" and not transparent :
255
+ if self .domain != "comb" :
166
256
self .en = Signal (name = "{}_r_en" .format (memory .name ), reset = 1 ,
167
257
src_loc_at = 1 + src_loc_at )
168
258
else :
169
259
self .en = Const (1 )
170
260
261
+ memory ._read_ports .append (self )
262
+
171
263
def elaborate (self , platform ):
172
- f = Instance ("$memrd" ,
173
- p_MEMID = self .memory ,
174
- p_ABITS = self .addr .width ,
175
- p_WIDTH = self .data .width ,
176
- p_CLK_ENABLE = self .domain != "comb" ,
177
- p_CLK_POLARITY = 1 ,
178
- p_TRANSPARENT = self .transparent ,
179
- i_CLK = ClockSignal (self .domain ) if self .domain != "comb" else Const (0 ),
180
- i_EN = self .en ,
181
- i_ADDR = self .addr ,
182
- o_DATA = self .data ,
183
- )
184
- if self .domain == "comb" :
185
- # Asynchronous port
186
- f .add_statements (self .data .eq (self .memory ._array [self .addr ]))
187
- f .add_driver (self .data )
188
- elif not self .transparent :
189
- # Synchronous, read-before-write port
190
- f .add_statements (
191
- Switch (self .en , {
192
- 1 : self .data .eq (self .memory ._array [self .addr ])
193
- })
194
- )
195
- f .add_driver (self .data , self .domain )
264
+ if self is self .memory ._read_ports [0 ]:
265
+ return self .memory
196
266
else :
197
- # Synchronous, write-through port
198
- # This model is a bit unconventional. We model transparent ports as asynchronous ports
199
- # that are latched when the clock is high. This isn't exactly correct, but it is very
200
- # close to the correct behavior of a transparent port, and the difference should only
201
- # be observable in pathological cases of clock gating. A register is injected to
202
- # the address input to achieve the correct address-to-data latency. Also, the reset
203
- # value of the data output is forcibly set to the 0th initial value, if any--note that
204
- # many FPGAs do not guarantee this behavior!
205
- if len (self .memory .init ) > 0 :
206
- self .data .reset = operator .index (self .memory .init [0 ])
207
- latch_addr = Signal .like (self .addr )
208
- f .add_statements (
209
- latch_addr .eq (self .addr ),
210
- Switch (ClockSignal (self .domain ), {
211
- 0 : self .data .eq (self .data ),
212
- 1 : self .data .eq (self .memory ._array [latch_addr ]),
213
- }),
214
- )
215
- f .add_driver (latch_addr , self .domain )
216
- f .add_driver (self .data )
217
- return f
267
+ return Fragment ()
218
268
219
269
220
270
class WritePort (Elaboratable ):
@@ -272,31 +322,13 @@ def __init__(self, memory, *, domain="sync", granularity=None, src_loc_at=0):
272
322
self .en = Signal (memory .width // granularity ,
273
323
name = "{}_w_en" .format (memory .name ), src_loc_at = 1 + src_loc_at )
274
324
325
+ memory ._write_ports .append (self )
326
+
275
327
def elaborate (self , platform ):
276
- f = Instance ("$memwr" ,
277
- p_MEMID = self .memory ,
278
- p_ABITS = self .addr .width ,
279
- p_WIDTH = self .data .width ,
280
- p_CLK_ENABLE = 1 ,
281
- p_CLK_POLARITY = 1 ,
282
- p_PRIORITY = 0 ,
283
- i_CLK = ClockSignal (self .domain ),
284
- i_EN = Cat (en_bit .replicate (self .granularity ) for en_bit in self .en ),
285
- i_ADDR = self .addr ,
286
- i_DATA = self .data ,
287
- )
288
- if len (self .en ) > 1 :
289
- for index , en_bit in enumerate (self .en ):
290
- offset = index * self .granularity
291
- bits = slice (offset , offset + self .granularity )
292
- write_data = self .memory ._array [self .addr ][bits ].eq (self .data [bits ])
293
- f .add_statements (Switch (en_bit , { 1 : write_data }))
328
+ if not self .memory ._read_ports and self is self .memory ._write_ports [0 ]:
329
+ return self .memory
294
330
else :
295
- write_data = self .memory ._array [self .addr ].eq (self .data )
296
- f .add_statements (Switch (self .en , { 1 : write_data }))
297
- for signal in self .memory ._array :
298
- f .add_driver (signal , self .domain )
299
- return f
331
+ return Fragment ()
300
332
301
333
302
334
class DummyPort :
0 commit comments