33
44from .. import tracer
55from .ast import *
6- from .ir import Elaboratable , Instance
6+ from .ir import Elaboratable , Instance , Fragment
77
88
99__all__ = ["Memory" , "ReadPort" , "WritePort" , "DummyPort" ]
1010
1111
12- class Memory :
12+ class Memory ( Elaboratable ) :
1313 """A word addressable storage.
1414
1515 Parameters
@@ -58,6 +58,8 @@ def __init__(self, *, width, depth, init=None, name=None, attrs=None, simulate=T
5858 .format (name or "memory" , addr )))
5959
6060 self .init = init
61+ self ._read_ports = []
62+ self ._write_ports = []
6163
6264 @property
6365 def init (self ):
@@ -116,6 +118,96 @@ def __getitem__(self, index):
116118 """Simulation only."""
117119 return self ._array [index ]
118120
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
119211
120212class ReadPort (Elaboratable ):
121213 """A memory read port.
@@ -142,9 +234,7 @@ class ReadPort(Elaboratable):
142234 data : Signal(memory.width), out
143235 Read data.
144236 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``.
148238
149239 Exceptions
150240 ----------
@@ -162,59 +252,19 @@ def __init__(self, memory, *, domain="sync", transparent=True, src_loc_at=0):
162252 name = "{}_r_addr" .format (memory .name ), src_loc_at = 1 + src_loc_at )
163253 self .data = Signal (memory .width ,
164254 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" :
166256 self .en = Signal (name = "{}_r_en" .format (memory .name ), reset = 1 ,
167257 src_loc_at = 1 + src_loc_at )
168258 else :
169259 self .en = Const (1 )
170260
261+ memory ._read_ports .append (self )
262+
171263 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
196266 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 ()
218268
219269
220270class WritePort (Elaboratable ):
@@ -272,31 +322,13 @@ def __init__(self, memory, *, domain="sync", granularity=None, src_loc_at=0):
272322 self .en = Signal (memory .width // granularity ,
273323 name = "{}_w_en" .format (memory .name ), src_loc_at = 1 + src_loc_at )
274324
325+ memory ._write_ports .append (self )
326+
275327 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
294330 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 ()
300332
301333
302334class DummyPort :
0 commit comments