2 * yosys -- Yosys Open SYnthesis Suite
4 * Copyright (C) 2020 Marcelina Kościelnicka <mwk@0x04.net>
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include "kernel/mem.h"
21 #include "kernel/ff.h"
31 module
->memories
.erase(mem
->name
);
35 for (auto &port
: rd_ports
) {
37 module
->remove(port
.cell
);
41 for (auto &port
: wr_ports
) {
43 module
->remove(port
.cell
);
47 for (auto &init
: inits
) {
49 module
->remove(init
.cell
);
57 std::vector
<int> rd_left
;
58 for (int i
= 0; i
< GetSize(rd_ports
); i
++) {
59 auto &port
= rd_ports
[i
];
62 module
->remove(port
.cell
);
68 std::vector
<int> wr_left
;
69 for (int i
= 0; i
< GetSize(wr_ports
); i
++) {
70 auto &port
= wr_ports
[i
];
73 module
->remove(port
.cell
);
79 std::vector
<int> init_left
;
80 for (int i
= 0; i
< GetSize(inits
); i
++) {
81 auto &init
= inits
[i
];
84 module
->remove(init
.cell
);
87 init_left
.push_back(i
);
90 for (int i
= 0; i
< GetSize(rd_left
); i
++)
92 std::swap(rd_ports
[i
], rd_ports
[rd_left
[i
]]);
93 rd_ports
.resize(GetSize(rd_left
));
94 for (int i
= 0; i
< GetSize(wr_left
); i
++)
96 std::swap(wr_ports
[i
], wr_ports
[wr_left
[i
]]);
97 wr_ports
.resize(GetSize(wr_left
));
98 for (int i
= 0; i
< GetSize(init_left
); i
++)
99 if (i
!= init_left
[i
])
100 std::swap(inits
[i
], inits
[init_left
[i
]]);
101 inits
.resize(GetSize(init_left
));
103 for (auto &port
: rd_ports
) {
104 for (int i
= 0; i
< GetSize(wr_left
); i
++) {
105 port
.transparency_mask
[i
] = port
.transparency_mask
[wr_left
[i
]];
106 port
.collision_x_mask
[i
] = port
.collision_x_mask
[wr_left
[i
]];
108 port
.transparency_mask
.resize(GetSize(wr_left
));
109 port
.collision_x_mask
.resize(GetSize(wr_left
));
111 for (auto &port
: wr_ports
) {
112 for (int i
= 0; i
< GetSize(wr_left
); i
++)
113 port
.priority_mask
[i
] = port
.priority_mask
[wr_left
[i
]];
114 port
.priority_mask
.resize(GetSize(wr_left
));
119 module
->memories
.erase(mem
->name
);
126 cell
= module
->addCell(memid
, ID($mem_v2
));
128 cell
->type
= ID($mem_v2
);
129 cell
->attributes
= attributes
;
130 cell
->parameters
[ID::MEMID
] = Const(memid
.str());
131 cell
->parameters
[ID::WIDTH
] = Const(width
);
132 cell
->parameters
[ID::OFFSET
] = Const(start_offset
);
133 cell
->parameters
[ID::SIZE
] = Const(size
);
134 Const rd_wide_continuation
, rd_clk_enable
, rd_clk_polarity
, rd_transparency_mask
, rd_collision_x_mask
;
135 Const wr_wide_continuation
, wr_clk_enable
, wr_clk_polarity
, wr_priority_mask
;
136 Const rd_ce_over_srst
, rd_arst_value
, rd_srst_value
, rd_init_value
;
137 SigSpec rd_clk
, rd_en
, rd_addr
, rd_data
;
138 SigSpec wr_clk
, wr_en
, wr_addr
, wr_data
;
139 SigSpec rd_arst
, rd_srst
;
141 for (auto &port
: rd_ports
)
142 abits
= std::max(abits
, GetSize(port
.addr
));
143 for (auto &port
: wr_ports
)
144 abits
= std::max(abits
, GetSize(port
.addr
));
145 cell
->parameters
[ID::ABITS
] = Const(abits
);
146 std::vector
<int> wr_port_xlat
;
147 for (int i
= 0; i
< GetSize(wr_ports
); i
++)
148 for (int j
= 0; j
< (1 << wr_ports
[i
].wide_log2
); j
++)
149 wr_port_xlat
.push_back(i
);
150 for (auto &port
: rd_ports
) {
152 module
->remove(port
.cell
);
155 for (int sub
= 0; sub
< (1 << port
.wide_log2
); sub
++)
157 rd_wide_continuation
.bits
.push_back(State(sub
!= 0));
158 rd_clk_enable
.bits
.push_back(State(port
.clk_enable
));
159 rd_clk_polarity
.bits
.push_back(State(port
.clk_polarity
));
160 rd_ce_over_srst
.bits
.push_back(State(port
.ce_over_srst
));
161 rd_clk
.append(port
.clk
);
162 rd_arst
.append(port
.arst
);
163 rd_srst
.append(port
.srst
);
164 rd_en
.append(port
.en
);
165 SigSpec addr
= port
.sub_addr(sub
);
166 addr
.extend_u0(abits
, false);
167 rd_addr
.append(addr
);
168 log_assert(GetSize(addr
) == abits
);
169 for (auto idx
: wr_port_xlat
) {
170 rd_transparency_mask
.bits
.push_back(State(bool(port
.transparency_mask
[idx
])));
171 rd_collision_x_mask
.bits
.push_back(State(bool(port
.collision_x_mask
[idx
])));
174 rd_data
.append(port
.data
);
175 for (auto &bit
: port
.arst_value
)
176 rd_arst_value
.bits
.push_back(bit
);
177 for (auto &bit
: port
.srst_value
)
178 rd_srst_value
.bits
.push_back(bit
);
179 for (auto &bit
: port
.init_value
)
180 rd_init_value
.bits
.push_back(bit
);
182 if (rd_ports
.empty()) {
183 rd_wide_continuation
= State::S0
;
184 rd_clk_enable
= State::S0
;
185 rd_clk_polarity
= State::S0
;
186 rd_ce_over_srst
= State::S0
;
187 rd_arst_value
= State::S0
;
188 rd_srst_value
= State::S0
;
189 rd_init_value
= State::S0
;
191 if (rd_ports
.empty() || wr_ports
.empty()) {
192 rd_transparency_mask
= State::S0
;
193 rd_collision_x_mask
= State::S0
;
195 cell
->parameters
[ID::RD_PORTS
] = Const(GetSize(rd_clk
));
196 cell
->parameters
[ID::RD_CLK_ENABLE
] = rd_clk_enable
;
197 cell
->parameters
[ID::RD_CLK_POLARITY
] = rd_clk_polarity
;
198 cell
->parameters
[ID::RD_TRANSPARENCY_MASK
] = rd_transparency_mask
;
199 cell
->parameters
[ID::RD_COLLISION_X_MASK
] = rd_collision_x_mask
;
200 cell
->parameters
[ID::RD_WIDE_CONTINUATION
] = rd_wide_continuation
;
201 cell
->parameters
[ID::RD_CE_OVER_SRST
] = rd_ce_over_srst
;
202 cell
->parameters
[ID::RD_ARST_VALUE
] = rd_arst_value
;
203 cell
->parameters
[ID::RD_SRST_VALUE
] = rd_srst_value
;
204 cell
->parameters
[ID::RD_INIT_VALUE
] = rd_init_value
;
205 cell
->parameters
.erase(ID::RD_TRANSPARENT
);
206 cell
->setPort(ID::RD_CLK
, rd_clk
);
207 cell
->setPort(ID::RD_EN
, rd_en
);
208 cell
->setPort(ID::RD_ARST
, rd_arst
);
209 cell
->setPort(ID::RD_SRST
, rd_srst
);
210 cell
->setPort(ID::RD_ADDR
, rd_addr
);
211 cell
->setPort(ID::RD_DATA
, rd_data
);
212 for (auto &port
: wr_ports
) {
214 module
->remove(port
.cell
);
217 for (int sub
= 0; sub
< (1 << port
.wide_log2
); sub
++)
219 wr_wide_continuation
.bits
.push_back(State(sub
!= 0));
220 wr_clk_enable
.bits
.push_back(State(port
.clk_enable
));
221 wr_clk_polarity
.bits
.push_back(State(port
.clk_polarity
));
222 wr_clk
.append(port
.clk
);
223 for (auto idx
: wr_port_xlat
)
224 wr_priority_mask
.bits
.push_back(State(bool(port
.priority_mask
[idx
])));
225 SigSpec addr
= port
.sub_addr(sub
);
226 addr
.extend_u0(abits
, false);
227 wr_addr
.append(addr
);
228 log_assert(GetSize(addr
) == abits
);
230 wr_en
.append(port
.en
);
231 wr_data
.append(port
.data
);
233 if (wr_ports
.empty()) {
234 wr_wide_continuation
= State::S0
;
235 wr_clk_enable
= State::S0
;
236 wr_clk_polarity
= State::S0
;
237 wr_priority_mask
= State::S0
;
239 cell
->parameters
[ID::WR_PORTS
] = Const(GetSize(wr_clk
));
240 cell
->parameters
[ID::WR_CLK_ENABLE
] = wr_clk_enable
;
241 cell
->parameters
[ID::WR_CLK_POLARITY
] = wr_clk_polarity
;
242 cell
->parameters
[ID::WR_PRIORITY_MASK
] = wr_priority_mask
;
243 cell
->parameters
[ID::WR_WIDE_CONTINUATION
] = wr_wide_continuation
;
244 cell
->setPort(ID::WR_CLK
, wr_clk
);
245 cell
->setPort(ID::WR_EN
, wr_en
);
246 cell
->setPort(ID::WR_ADDR
, wr_addr
);
247 cell
->setPort(ID::WR_DATA
, wr_data
);
248 for (auto &init
: inits
) {
250 module
->remove(init
.cell
);
254 cell
->parameters
[ID::INIT
] = get_init_data();
257 module
->remove(cell
);
263 mem
= new RTLIL::Memory
;
265 module
->memories
[memid
] = mem
;
268 mem
->start_offset
= start_offset
;
270 mem
->attributes
= attributes
;
271 for (auto &port
: rd_ports
) {
273 port
.cell
= module
->addCell(NEW_ID
, ID($memrd_v2
));
274 port
.cell
->type
= ID($memrd_v2
);
275 port
.cell
->attributes
= port
.attributes
;
276 port
.cell
->parameters
[ID::MEMID
] = memid
.str();
277 port
.cell
->parameters
[ID::ABITS
] = GetSize(port
.addr
);
278 port
.cell
->parameters
[ID::WIDTH
] = width
<< port
.wide_log2
;
279 port
.cell
->parameters
[ID::CLK_ENABLE
] = port
.clk_enable
;
280 port
.cell
->parameters
[ID::CLK_POLARITY
] = port
.clk_polarity
;
281 port
.cell
->parameters
[ID::CE_OVER_SRST
] = port
.ce_over_srst
;
282 port
.cell
->parameters
[ID::ARST_VALUE
] = port
.arst_value
;
283 port
.cell
->parameters
[ID::SRST_VALUE
] = port
.srst_value
;
284 port
.cell
->parameters
[ID::INIT_VALUE
] = port
.init_value
;
285 port
.cell
->parameters
[ID::TRANSPARENCY_MASK
] = port
.transparency_mask
;
286 port
.cell
->parameters
[ID::COLLISION_X_MASK
] = port
.collision_x_mask
;
287 port
.cell
->parameters
.erase(ID::TRANSPARENT
);
288 port
.cell
->setPort(ID::CLK
, port
.clk
);
289 port
.cell
->setPort(ID::EN
, port
.en
);
290 port
.cell
->setPort(ID::ARST
, port
.arst
);
291 port
.cell
->setPort(ID::SRST
, port
.srst
);
292 port
.cell
->setPort(ID::ADDR
, port
.addr
);
293 port
.cell
->setPort(ID::DATA
, port
.data
);
296 for (auto &port
: wr_ports
) {
298 port
.cell
= module
->addCell(NEW_ID
, ID($memwr_v2
));
299 port
.cell
->type
= ID($memwr_v2
);
300 port
.cell
->attributes
= port
.attributes
;
301 if (port
.cell
->parameters
.count(ID::PRIORITY
))
302 port
.cell
->parameters
.erase(ID::PRIORITY
);
303 port
.cell
->parameters
[ID::MEMID
] = memid
.str();
304 port
.cell
->parameters
[ID::ABITS
] = GetSize(port
.addr
);
305 port
.cell
->parameters
[ID::WIDTH
] = width
<< port
.wide_log2
;
306 port
.cell
->parameters
[ID::CLK_ENABLE
] = port
.clk_enable
;
307 port
.cell
->parameters
[ID::CLK_POLARITY
] = port
.clk_polarity
;
308 port
.cell
->parameters
[ID::PORTID
] = idx
++;
309 port
.cell
->parameters
[ID::PRIORITY_MASK
] = port
.priority_mask
;
310 port
.cell
->setPort(ID::CLK
, port
.clk
);
311 port
.cell
->setPort(ID::EN
, port
.en
);
312 port
.cell
->setPort(ID::ADDR
, port
.addr
);
313 port
.cell
->setPort(ID::DATA
, port
.data
);
316 for (auto &init
: inits
) {
317 bool v2
= !init
.en
.is_fully_ones();
319 init
.cell
= module
->addCell(NEW_ID
, v2
? ID($meminit_v2
) : ID($meminit
));
321 init
.cell
->type
= v2
? ID($meminit_v2
) : ID($meminit
);
322 init
.cell
->attributes
= init
.attributes
;
323 init
.cell
->parameters
[ID::MEMID
] = memid
.str();
324 init
.cell
->parameters
[ID::ABITS
] = GetSize(init
.addr
);
325 init
.cell
->parameters
[ID::WIDTH
] = width
;
326 init
.cell
->parameters
[ID::WORDS
] = GetSize(init
.data
) / width
;
327 init
.cell
->parameters
[ID::PRIORITY
] = idx
++;
328 init
.cell
->setPort(ID::ADDR
, init
.addr
);
329 init
.cell
->setPort(ID::DATA
, init
.data
);
331 init
.cell
->setPort(ID::EN
, init
.en
);
333 init
.cell
->unsetPort(ID::EN
);
338 void Mem::clear_inits() {
339 for (auto &init
: inits
)
343 void Mem::coalesce_inits() {
344 // start address -> end address
345 std::map
<int, int> chunks
;
346 // Figure out chunk boundaries.
347 for (auto &init
: inits
) {
351 for (auto bit
: init
.en
)
352 if (bit
== State::S1
)
358 int addr
= init
.addr
.as_int();
359 int addr_e
= addr
+ GetSize(init
.data
) / width
;
360 auto it_e
= chunks
.upper_bound(addr_e
);
362 while (it
!= chunks
.begin()) {
364 if (it
->second
< addr
) {
370 // No overlapping inits — add this one to index.
371 chunks
[addr
] = addr_e
;
373 // We have an overlap — all chunks in the [it, it_e)
374 // range will be merged with this init.
375 if (it
->first
< addr
)
379 if (it_last
->second
> addr_e
)
380 addr_e
= it_last
->second
;
381 chunks
.erase(it
, it_e
);
382 chunks
[addr
] = addr_e
;
385 // Group inits by the chunk they belong to.
386 dict
<int, std::vector
<int>> inits_by_chunk
;
387 for (int i
= 0; i
< GetSize(inits
); i
++) {
388 auto &init
= inits
[i
];
391 auto it
= chunks
.upper_bound(init
.addr
.as_int());
393 inits_by_chunk
[it
->first
].push_back(i
);
394 int addr
= init
.addr
.as_int();
395 int addr_e
= addr
+ GetSize(init
.data
) / width
;
396 log_assert(addr
>= it
->first
&& addr_e
<= it
->second
);
398 // Process each chunk.
399 for (auto &it
: inits_by_chunk
) {
400 int caddr
= it
.first
;
401 int caddr_e
= chunks
[caddr
];
402 auto &chunk_inits
= it
.second
;
403 if (GetSize(chunk_inits
) == 1) {
404 auto &init
= inits
[chunk_inits
[0]];
405 if (!init
.en
.is_fully_ones()) {
406 for (int i
= 0; i
< GetSize(init
.data
); i
++)
407 if (init
.en
[i
% width
] != State::S1
)
408 init
.data
[i
] = State::Sx
;
409 init
.en
= Const(State::S1
, width
);
413 Const
cdata(State::Sx
, (caddr_e
- caddr
) * width
);
414 for (int idx
: chunk_inits
) {
415 auto &init
= inits
[idx
];
416 int offset
= (init
.addr
.as_int() - caddr
) * width
;
417 log_assert(offset
>= 0);
418 log_assert(offset
+ GetSize(init
.data
) <= GetSize(cdata
));
419 for (int i
= 0; i
< GetSize(init
.data
); i
++)
420 if (init
.en
[i
% width
] == State::S1
)
421 cdata
.bits
[i
+offset
] = init
.data
.bits
[i
];
425 new_init
.addr
= caddr
;
426 new_init
.data
= cdata
;
427 new_init
.en
= Const(State::S1
, width
);
428 inits
.push_back(new_init
);
432 Const
Mem::get_init_data() const {
433 Const
init_data(State::Sx
, width
* size
);
434 for (auto &init
: inits
) {
437 int offset
= (init
.addr
.as_int() - start_offset
) * width
;
438 for (int i
= 0; i
< GetSize(init
.data
); i
++)
439 if (0 <= i
+offset
&& i
+offset
< GetSize(init_data
) && init
.en
[i
% width
] == State::S1
)
440 init_data
.bits
[i
+offset
] = init
.data
.bits
[i
];
446 int max_wide_log2
= 0;
447 for (auto &port
: rd_ports
) {
450 log_assert(GetSize(port
.clk
) == 1);
451 log_assert(GetSize(port
.en
) == 1);
452 log_assert(GetSize(port
.arst
) == 1);
453 log_assert(GetSize(port
.srst
) == 1);
454 log_assert(GetSize(port
.data
) == (width
<< port
.wide_log2
));
455 log_assert(GetSize(port
.init_value
) == (width
<< port
.wide_log2
));
456 log_assert(GetSize(port
.arst_value
) == (width
<< port
.wide_log2
));
457 log_assert(GetSize(port
.srst_value
) == (width
<< port
.wide_log2
));
458 if (!port
.clk_enable
) {
459 log_assert(port
.en
== State::S1
);
460 log_assert(port
.arst
== State::S0
);
461 log_assert(port
.srst
== State::S0
);
463 for (int j
= 0; j
< port
.wide_log2
; j
++) {
464 log_assert(port
.addr
[j
] == State::S0
);
466 max_wide_log2
= std::max(max_wide_log2
, port
.wide_log2
);
467 log_assert(GetSize(port
.transparency_mask
) == GetSize(wr_ports
));
468 log_assert(GetSize(port
.collision_x_mask
) == GetSize(wr_ports
));
469 for (int j
= 0; j
< GetSize(wr_ports
); j
++) {
470 auto &wport
= wr_ports
[j
];
471 if ((port
.transparency_mask
[j
] || port
.collision_x_mask
[j
]) && !wport
.removed
) {
472 log_assert(port
.clk_enable
);
473 log_assert(wport
.clk_enable
);
474 log_assert(port
.clk
== wport
.clk
);
475 log_assert(port
.clk_polarity
== wport
.clk_polarity
);
477 log_assert(!port
.transparency_mask
[j
] || !port
.collision_x_mask
[j
]);
480 for (int i
= 0; i
< GetSize(wr_ports
); i
++) {
481 auto &port
= wr_ports
[i
];
484 log_assert(GetSize(port
.clk
) == 1);
485 log_assert(GetSize(port
.en
) == (width
<< port
.wide_log2
));
486 log_assert(GetSize(port
.data
) == (width
<< port
.wide_log2
));
487 for (int j
= 0; j
< port
.wide_log2
; j
++) {
488 log_assert(port
.addr
[j
] == State::S0
);
490 max_wide_log2
= std::max(max_wide_log2
, port
.wide_log2
);
491 log_assert(GetSize(port
.priority_mask
) == GetSize(wr_ports
));
492 for (int j
= 0; j
< GetSize(wr_ports
); j
++) {
493 auto &wport
= wr_ports
[j
];
494 if (port
.priority_mask
[j
] && !wport
.removed
) {
496 log_assert(port
.clk_enable
== wport
.clk_enable
);
497 if (port
.clk_enable
) {
498 log_assert(port
.clk
== wport
.clk
);
499 log_assert(port
.clk_polarity
== wport
.clk_polarity
);
504 int mask
= (1 << max_wide_log2
) - 1;
505 log_assert(!(start_offset
& mask
));
506 log_assert(!(size
& mask
));
512 dict
<IdString
, pool
<Cell
*>> rd_ports
;
513 dict
<IdString
, pool
<Cell
*>> wr_ports
;
514 dict
<IdString
, pool
<Cell
*>> inits
;
515 MemIndex (Module
*module
) {
516 for (auto cell
: module
->cells()) {
517 if (cell
->type
.in(ID($memwr
), ID($memwr_v2
)))
518 wr_ports
[cell
->parameters
.at(ID::MEMID
).decode_string()].insert(cell
);
519 else if (cell
->type
.in(ID($memrd
), ID($memrd_v2
)))
520 rd_ports
[cell
->parameters
.at(ID::MEMID
).decode_string()].insert(cell
);
521 else if (cell
->type
.in(ID($meminit
), ID($meminit_v2
)))
522 inits
[cell
->parameters
.at(ID::MEMID
).decode_string()].insert(cell
);
527 Mem
mem_from_memory(Module
*module
, RTLIL::Memory
*mem
, const MemIndex
&index
) {
528 Mem
res(module
, mem
->name
, mem
->width
, mem
->start_offset
, mem
->size
);
531 res
.attributes
= mem
->attributes
;
532 std::vector
<bool> rd_transparent
;
533 std::vector
<int> wr_portid
;
534 if (index
.rd_ports
.count(mem
->name
)) {
535 for (auto cell
: index
.rd_ports
.at(mem
->name
)) {
537 bool is_compat
= cell
->type
== ID($memrd
);
539 mrd
.attributes
= cell
->attributes
;
540 mrd
.clk_enable
= cell
->parameters
.at(ID::CLK_ENABLE
).as_bool();
541 mrd
.clk_polarity
= cell
->parameters
.at(ID::CLK_POLARITY
).as_bool();
542 mrd
.clk
= cell
->getPort(ID::CLK
);
543 mrd
.en
= cell
->getPort(ID::EN
);
544 mrd
.addr
= cell
->getPort(ID::ADDR
);
545 mrd
.data
= cell
->getPort(ID::DATA
);
546 mrd
.wide_log2
= ceil_log2(GetSize(mrd
.data
) / mem
->width
);
547 bool transparent
= false;
549 transparent
= cell
->parameters
.at(ID::TRANSPARENT
).as_bool();
550 mrd
.ce_over_srst
= false;
551 mrd
.arst_value
= Const(State::Sx
, mem
->width
<< mrd
.wide_log2
);
552 mrd
.srst_value
= Const(State::Sx
, mem
->width
<< mrd
.wide_log2
);
553 mrd
.init_value
= Const(State::Sx
, mem
->width
<< mrd
.wide_log2
);
554 mrd
.srst
= State::S0
;
555 mrd
.arst
= State::S0
;
556 if (!mrd
.clk_enable
) {
557 // Fix some patterns that we'll allow for backwards compatibility,
558 // but don't want to see moving forwards: async transparent
559 // ports (inherently meaningless) and async ports without
560 // const 1 tied to EN bit (which may mean a latch in the future).
562 if (mrd
.en
== State::Sx
)
566 mrd
.ce_over_srst
= cell
->parameters
.at(ID::CE_OVER_SRST
).as_bool();
567 mrd
.arst_value
= cell
->parameters
.at(ID::ARST_VALUE
);
568 mrd
.srst_value
= cell
->parameters
.at(ID::SRST_VALUE
);
569 mrd
.init_value
= cell
->parameters
.at(ID::INIT_VALUE
);
570 mrd
.arst
= cell
->getPort(ID::ARST
);
571 mrd
.srst
= cell
->getPort(ID::SRST
);
573 res
.rd_ports
.push_back(mrd
);
574 rd_transparent
.push_back(transparent
);
577 if (index
.wr_ports
.count(mem
->name
)) {
578 std::vector
<std::pair
<int, MemWr
>> ports
;
579 for (auto cell
: index
.wr_ports
.at(mem
->name
)) {
581 bool is_compat
= cell
->type
== ID($memwr
);
583 mwr
.attributes
= cell
->attributes
;
584 mwr
.clk_enable
= cell
->parameters
.at(ID::CLK_ENABLE
).as_bool();
585 mwr
.clk_polarity
= cell
->parameters
.at(ID::CLK_POLARITY
).as_bool();
586 mwr
.clk
= cell
->getPort(ID::CLK
);
587 mwr
.en
= cell
->getPort(ID::EN
);
588 mwr
.addr
= cell
->getPort(ID::ADDR
);
589 mwr
.data
= cell
->getPort(ID::DATA
);
590 mwr
.wide_log2
= ceil_log2(GetSize(mwr
.data
) / mem
->width
);
591 ports
.push_back(std::make_pair(cell
->parameters
.at(is_compat
? ID::PRIORITY
: ID::PORTID
).as_int(), mwr
));
593 std::sort(ports
.begin(), ports
.end(), [](const std::pair
<int, MemWr
> &a
, const std::pair
<int, MemWr
> &b
) { return a
.first
< b
.first
; });
594 for (auto &it
: ports
) {
595 res
.wr_ports
.push_back(it
.second
);
596 wr_portid
.push_back(it
.first
);
598 for (int i
= 0; i
< GetSize(res
.wr_ports
); i
++) {
599 auto &port
= res
.wr_ports
[i
];
600 bool is_compat
= port
.cell
->type
== ID($memwr
);
602 port
.priority_mask
.resize(GetSize(res
.wr_ports
));
603 for (int j
= 0; j
< i
; j
++) {
604 auto &oport
= res
.wr_ports
[j
];
605 if (port
.clk_enable
!= oport
.clk_enable
)
607 if (port
.clk_enable
&& port
.clk
!= oport
.clk
)
609 if (port
.clk_enable
&& port
.clk_polarity
!= oport
.clk_polarity
)
611 port
.priority_mask
[j
] = true;
614 Const orig_prio_mask
= port
.cell
->parameters
.at(ID::PRIORITY_MASK
);
615 for (int orig_portid
: wr_portid
) {
616 bool has_prio
= orig_portid
< GetSize(orig_prio_mask
) && orig_prio_mask
[orig_portid
] == State::S1
;
617 port
.priority_mask
.push_back(has_prio
);
622 if (index
.inits
.count(mem
->name
)) {
623 std::vector
<std::pair
<int, MemInit
>> inits
;
624 for (auto cell
: index
.inits
.at(mem
->name
)) {
627 init
.attributes
= cell
->attributes
;
628 auto addr
= cell
->getPort(ID::ADDR
);
629 auto data
= cell
->getPort(ID::DATA
);
630 if (!addr
.is_fully_const())
631 log_error("Non-constant address %s in memory initialization %s.\n", log_signal(addr
), log_id(cell
));
632 if (!data
.is_fully_const())
633 log_error("Non-constant data %s in memory initialization %s.\n", log_signal(data
), log_id(cell
));
634 init
.addr
= addr
.as_const();
635 init
.data
= data
.as_const();
636 if (cell
->type
== ID($meminit_v2
)) {
637 auto en
= cell
->getPort(ID::EN
);
638 if (!en
.is_fully_const())
639 log_error("Non-constant enable %s in memory initialization %s.\n", log_signal(en
), log_id(cell
));
640 init
.en
= en
.as_const();
642 init
.en
= RTLIL::Const(State::S1
, mem
->width
);
644 inits
.push_back(std::make_pair(cell
->parameters
.at(ID::PRIORITY
).as_int(), init
));
646 std::sort(inits
.begin(), inits
.end(), [](const std::pair
<int, MemInit
> &a
, const std::pair
<int, MemInit
> &b
) { return a
.first
< b
.first
; });
647 for (auto &it
: inits
)
648 res
.inits
.push_back(it
.second
);
650 for (int i
= 0; i
< GetSize(res
.rd_ports
); i
++) {
651 auto &port
= res
.rd_ports
[i
];
652 bool is_compat
= port
.cell
->type
== ID($memrd
);
654 port
.transparency_mask
.resize(GetSize(res
.wr_ports
));
655 port
.collision_x_mask
.resize(GetSize(res
.wr_ports
));
656 if (!rd_transparent
[i
])
658 if (!port
.clk_enable
)
660 for (int j
= 0; j
< GetSize(res
.wr_ports
); j
++) {
661 auto &wport
= res
.wr_ports
[j
];
662 if (!wport
.clk_enable
)
664 if (port
.clk
!= wport
.clk
)
666 if (port
.clk_polarity
!= wport
.clk_polarity
)
668 port
.transparency_mask
[j
] = true;
671 Const orig_trans_mask
= port
.cell
->parameters
.at(ID::TRANSPARENCY_MASK
);
672 Const orig_cx_mask
= port
.cell
->parameters
.at(ID::COLLISION_X_MASK
);
673 for (int orig_portid
: wr_portid
) {
674 port
.transparency_mask
.push_back(orig_portid
< GetSize(orig_trans_mask
) && orig_trans_mask
[orig_portid
] == State::S1
);
675 port
.collision_x_mask
.push_back(orig_portid
< GetSize(orig_cx_mask
) && orig_cx_mask
[orig_portid
] == State::S1
);
683 Mem
mem_from_cell(Cell
*cell
) {
684 Mem
res(cell
->module
, cell
->parameters
.at(ID::MEMID
).decode_string(),
685 cell
->parameters
.at(ID::WIDTH
).as_int(),
686 cell
->parameters
.at(ID::OFFSET
).as_int(),
687 cell
->parameters
.at(ID::SIZE
).as_int()
689 bool is_compat
= cell
->type
== ID($mem
);
690 int abits
= cell
->parameters
.at(ID::ABITS
).as_int();
693 res
.attributes
= cell
->attributes
;
694 Const
&init
= cell
->parameters
.at(ID::INIT
);
695 if (!init
.is_fully_undef()) {
697 while (pos
< res
.size
) {
698 Const word
= init
.extract(pos
* res
.width
, res
.width
, State::Sx
);
699 if (word
.is_fully_undef()) {
703 for (epos
= pos
; epos
< res
.size
; epos
++) {
704 Const eword
= init
.extract(epos
* res
.width
, res
.width
, State::Sx
);
705 if (eword
.is_fully_undef())
709 minit
.addr
= res
.start_offset
+ pos
;
710 minit
.data
= init
.extract(pos
* res
.width
, (epos
- pos
) * res
.width
, State::Sx
);
711 minit
.en
= RTLIL::Const(State::S1
, res
.width
);
712 res
.inits
.push_back(minit
);
717 int n_rd_ports
= cell
->parameters
.at(ID::RD_PORTS
).as_int();
718 int n_wr_ports
= cell
->parameters
.at(ID::WR_PORTS
).as_int();
719 Const rd_wide_continuation
= is_compat
? Const(State::S0
, n_rd_ports
) : cell
->parameters
.at(ID::RD_WIDE_CONTINUATION
);
720 Const wr_wide_continuation
= is_compat
? Const(State::S0
, n_wr_ports
) : cell
->parameters
.at(ID::WR_WIDE_CONTINUATION
);
721 for (int i
= 0, ni
; i
< n_rd_ports
; i
= ni
) {
723 while (ni
< n_rd_ports
&& rd_wide_continuation
[ni
] == State::S1
)
726 mrd
.wide_log2
= ceil_log2(ni
- i
);
727 log_assert(ni
- i
== (1 << mrd
.wide_log2
));
728 mrd
.clk_enable
= cell
->parameters
.at(ID::RD_CLK_ENABLE
).extract(i
, 1).as_bool();
729 mrd
.clk_polarity
= cell
->parameters
.at(ID::RD_CLK_POLARITY
).extract(i
, 1).as_bool();
730 mrd
.clk
= cell
->getPort(ID::RD_CLK
).extract(i
, 1);
731 mrd
.en
= cell
->getPort(ID::RD_EN
).extract(i
, 1);
732 mrd
.addr
= cell
->getPort(ID::RD_ADDR
).extract(i
* abits
, abits
);
733 mrd
.data
= cell
->getPort(ID::RD_DATA
).extract(i
* res
.width
, (ni
- i
) * res
.width
);
735 mrd
.ce_over_srst
= false;
736 mrd
.arst_value
= Const(State::Sx
, res
.width
<< mrd
.wide_log2
);
737 mrd
.srst_value
= Const(State::Sx
, res
.width
<< mrd
.wide_log2
);
738 mrd
.init_value
= Const(State::Sx
, res
.width
<< mrd
.wide_log2
);
739 mrd
.arst
= State::S0
;
740 mrd
.srst
= State::S0
;
742 mrd
.ce_over_srst
= cell
->parameters
.at(ID::RD_CE_OVER_SRST
).extract(i
, 1).as_bool();
743 mrd
.arst_value
= cell
->parameters
.at(ID::RD_ARST_VALUE
).extract(i
* res
.width
, (ni
- i
) * res
.width
);
744 mrd
.srst_value
= cell
->parameters
.at(ID::RD_SRST_VALUE
).extract(i
* res
.width
, (ni
- i
) * res
.width
);
745 mrd
.init_value
= cell
->parameters
.at(ID::RD_INIT_VALUE
).extract(i
* res
.width
, (ni
- i
) * res
.width
);
746 mrd
.arst
= cell
->getPort(ID::RD_ARST
).extract(i
, 1);
747 mrd
.srst
= cell
->getPort(ID::RD_SRST
).extract(i
, 1);
750 Const transparency_mask
= cell
->parameters
.at(ID::RD_TRANSPARENCY_MASK
).extract(i
* n_wr_ports
, n_wr_ports
);
751 Const collision_x_mask
= cell
->parameters
.at(ID::RD_COLLISION_X_MASK
).extract(i
* n_wr_ports
, n_wr_ports
);
752 for (int j
= 0; j
< n_wr_ports
; j
++)
753 if (wr_wide_continuation
[j
] != State::S1
) {
754 mrd
.transparency_mask
.push_back(transparency_mask
[j
] == State::S1
);
755 mrd
.collision_x_mask
.push_back(collision_x_mask
[j
] == State::S1
);
758 res
.rd_ports
.push_back(mrd
);
760 for (int i
= 0, ni
; i
< n_wr_ports
; i
= ni
) {
762 while (ni
< n_wr_ports
&& wr_wide_continuation
[ni
] == State::S1
)
765 mwr
.wide_log2
= ceil_log2(ni
- i
);
766 log_assert(ni
- i
== (1 << mwr
.wide_log2
));
767 mwr
.clk_enable
= cell
->parameters
.at(ID::WR_CLK_ENABLE
).extract(i
, 1).as_bool();
768 mwr
.clk_polarity
= cell
->parameters
.at(ID::WR_CLK_POLARITY
).extract(i
, 1).as_bool();
769 mwr
.clk
= cell
->getPort(ID::WR_CLK
).extract(i
, 1);
770 mwr
.en
= cell
->getPort(ID::WR_EN
).extract(i
* res
.width
, (ni
- i
) * res
.width
);
771 mwr
.addr
= cell
->getPort(ID::WR_ADDR
).extract(i
* abits
, abits
);
772 mwr
.data
= cell
->getPort(ID::WR_DATA
).extract(i
* res
.width
, (ni
- i
) * res
.width
);
774 Const priority_mask
= cell
->parameters
.at(ID::WR_PRIORITY_MASK
).extract(i
* n_wr_ports
, n_wr_ports
);
775 for (int j
= 0; j
< n_wr_ports
; j
++)
776 if (wr_wide_continuation
[j
] != State::S1
)
777 mwr
.priority_mask
.push_back(priority_mask
[j
] == State::S1
);
779 res
.wr_ports
.push_back(mwr
);
782 for (int i
= 0; i
< GetSize(res
.wr_ports
); i
++) {
783 auto &port
= res
.wr_ports
[i
];
784 port
.priority_mask
.resize(GetSize(res
.wr_ports
));
785 for (int j
= 0; j
< i
; j
++) {
786 auto &oport
= res
.wr_ports
[j
];
787 if (port
.clk_enable
!= oport
.clk_enable
)
789 if (port
.clk_enable
&& port
.clk
!= oport
.clk
)
791 if (port
.clk_enable
&& port
.clk_polarity
!= oport
.clk_polarity
)
793 port
.priority_mask
[j
] = true;
796 for (int i
= 0; i
< GetSize(res
.rd_ports
); i
++) {
797 auto &port
= res
.rd_ports
[i
];
798 port
.transparency_mask
.resize(GetSize(res
.wr_ports
));
799 port
.collision_x_mask
.resize(GetSize(res
.wr_ports
));
800 if (!cell
->parameters
.at(ID::RD_TRANSPARENT
).extract(i
, 1).as_bool())
802 if (!port
.clk_enable
)
804 for (int j
= 0; j
< GetSize(res
.wr_ports
); j
++) {
805 auto &wport
= res
.wr_ports
[j
];
806 if (!wport
.clk_enable
)
808 if (port
.clk
!= wport
.clk
)
810 if (port
.clk_polarity
!= wport
.clk_polarity
)
812 port
.transparency_mask
[j
] = true;
822 std::vector
<Mem
> Mem::get_all_memories(Module
*module
) {
823 std::vector
<Mem
> res
;
824 MemIndex
index(module
);
825 for (auto it
: module
->memories
) {
826 res
.push_back(mem_from_memory(module
, it
.second
, index
));
828 for (auto cell
: module
->cells()) {
829 if (cell
->type
.in(ID($mem
), ID($mem_v2
)))
830 res
.push_back(mem_from_cell(cell
));
835 std::vector
<Mem
> Mem::get_selected_memories(Module
*module
) {
836 std::vector
<Mem
> res
;
837 MemIndex
index(module
);
838 for (auto it
: module
->memories
) {
839 if (module
->design
->selected(module
, it
.second
))
840 res
.push_back(mem_from_memory(module
, it
.second
, index
));
842 for (auto cell
: module
->selected_cells()) {
843 if (cell
->type
.in(ID($mem
), ID($mem_v2
)))
844 res
.push_back(mem_from_cell(cell
));
849 Cell
*Mem::extract_rdff(int idx
, FfInitVals
*initvals
) {
850 MemRd
&port
= rd_ports
[idx
];
852 if (!port
.clk_enable
)
857 // There are two ways to handle rdff extraction when transparency is involved:
859 // - if all of the following conditions are true, put the FF on address input:
861 // - the port has no clock enable, no reset, and no initial value
862 // - the port is transparent wrt all write ports (implying they also share
865 // - otherwise, put the FF on the data output, and make bypass paths for
866 // all write ports wrt which this port is transparent
867 bool trans_use_addr
= true;
868 for (int i
= 0; i
< GetSize(wr_ports
); i
++)
869 if (!port
.transparency_mask
[i
] && !wr_ports
[i
].removed
)
870 trans_use_addr
= false;
872 // If there are no write ports at all, we could possibly use either way; do data
874 if (GetSize(wr_ports
) == 0)
875 trans_use_addr
= false;
877 if (port
.en
!= State::S1
|| port
.srst
!= State::S0
|| port
.arst
!= State::S0
|| !port
.init_value
.is_fully_undef())
878 trans_use_addr
= false;
882 // Do not put a register in front of constant address bits — this is both
883 // unnecessary and will break wide ports.
885 for (int i
= 0; i
< GetSize(port
.addr
); i
++)
886 if (port
.addr
[i
].wire
)
891 SigSpec sig_q
= module
->addWire(stringf("$%s$rdreg[%d]$q", memid
.c_str(), idx
), width
);
895 for (int i
= 0; i
< GetSize(port
.addr
); i
++)
896 if (port
.addr
[i
].wire
) {
897 sig_d
.append(port
.addr
[i
]);
898 port
.addr
[i
] = sig_q
[pos
++];
901 c
= module
->addDff(stringf("$%s$rdreg[%d]", memid
.c_str(), idx
), port
.clk
, sig_d
, sig_q
, port
.clk_polarity
);
908 log_assert(port
.arst
== State::S0
|| port
.srst
== State::S0
);
910 SigSpec async_d
= module
->addWire(stringf("$%s$rdreg[%d]$d", memid
.c_str(), idx
), GetSize(port
.data
));
911 SigSpec sig_d
= async_d
;
913 for (int i
= 0; i
< GetSize(wr_ports
); i
++) {
914 auto &wport
= wr_ports
[i
];
917 if (port
.transparency_mask
[i
] || port
.collision_x_mask
[i
]) {
918 log_assert(wport
.clk_enable
);
919 log_assert(wport
.clk
== port
.clk
);
920 log_assert(wport
.clk_enable
== port
.clk_enable
);
921 int min_wide_log2
= std::min(port
.wide_log2
, wport
.wide_log2
);
922 int max_wide_log2
= std::max(port
.wide_log2
, wport
.wide_log2
);
923 bool wide_write
= wport
.wide_log2
> port
.wide_log2
;
924 for (int sub
= 0; sub
< (1 << max_wide_log2
); sub
+= (1 << min_wide_log2
)) {
925 SigSpec raddr
= port
.addr
;
926 SigSpec waddr
= wport
.addr
;
928 waddr
= wport
.sub_addr(sub
);
930 raddr
= port
.sub_addr(sub
);
933 addr_eq
= module
->Eq(stringf("$%s$rdtransen[%d][%d][%d]$d", memid
.c_str(), idx
, i
, sub
), raddr
, waddr
);
935 int ewidth
= width
<< min_wide_log2
;
936 int wsub
= wide_write
? sub
: 0;
937 int rsub
= wide_write
? 0 : sub
;
938 while (pos
< ewidth
) {
940 while (epos
< ewidth
&& wport
.en
[epos
+ wsub
* width
] == wport
.en
[pos
+ wsub
* width
])
942 SigSpec cur
= sig_d
.extract(pos
+ rsub
* width
, epos
-pos
);
943 SigSpec other
= port
.transparency_mask
[i
] ? wport
.data
.extract(pos
+ wsub
* width
, epos
-pos
) : Const(State::Sx
, epos
-pos
);
946 cond
= module
->And(stringf("$%s$rdtransgate[%d][%d][%d][%d]$d", memid
.c_str(), idx
, i
, sub
, pos
), wport
.en
[pos
+ wsub
* width
], addr_eq
);
948 cond
= wport
.en
[pos
+ wsub
* width
];
949 SigSpec merged
= module
->Mux(stringf("$%s$rdtransmux[%d][%d][%d][%d]$d", memid
.c_str(), idx
, i
, sub
, pos
), cur
, other
, cond
);
950 sig_d
.replace(pos
+ rsub
* width
, merged
);
957 IdString name
= stringf("$%s$rdreg[%d]", memid
.c_str(), idx
);
958 FfData
ff(module
, initvals
, name
);
959 ff
.width
= GetSize(port
.data
);
961 ff
.sig_clk
= port
.clk
;
962 ff
.pol_clk
= port
.clk_polarity
;
963 if (port
.en
!= State::S1
) {
968 if (port
.arst
!= State::S0
) {
971 ff
.sig_arst
= port
.arst
;
972 ff
.val_arst
= port
.arst_value
;
974 if (port
.srst
!= State::S0
) {
977 ff
.sig_srst
= port
.srst
;
978 ff
.val_srst
= port
.srst_value
;
979 ff
.ce_over_srst
= ff
.has_ce
&& port
.ce_over_srst
;
982 ff
.sig_q
= port
.data
;
983 ff
.val_init
= port
.init_value
;
988 log("Extracted %s FF from read port %d of %s.%s: %s\n", trans_use_addr
? "addr" : "data",
989 idx
, log_id(module
), log_id(memid
), log_id(c
));
992 port
.clk
= State::S0
;
993 port
.arst
= State::S0
;
994 port
.srst
= State::S0
;
995 port
.clk_enable
= false;
996 port
.clk_polarity
= true;
997 port
.ce_over_srst
= false;
998 port
.arst_value
= Const(State::Sx
, GetSize(port
.data
));
999 port
.srst_value
= Const(State::Sx
, GetSize(port
.data
));
1000 port
.init_value
= Const(State::Sx
, GetSize(port
.data
));
1002 for (int i
= 0; i
< GetSize(wr_ports
); i
++) {
1003 port
.transparency_mask
[i
] = false;
1004 port
.collision_x_mask
[i
] = false;
1010 void Mem::narrow() {
1011 // NOTE: several passes depend on this function not modifying
1012 // the design at all until (and unless) emit() is called.
1013 // Be careful to preserve this.
1014 std::vector
<MemRd
> new_rd_ports
;
1015 std::vector
<MemWr
> new_wr_ports
;
1016 std::vector
<std::pair
<int, int>> new_rd_map
;
1017 std::vector
<std::pair
<int, int>> new_wr_map
;
1018 for (int i
= 0; i
< GetSize(rd_ports
); i
++) {
1019 auto &port
= rd_ports
[i
];
1020 for (int sub
= 0; sub
< (1 << port
.wide_log2
); sub
++) {
1021 new_rd_map
.push_back(std::make_pair(i
, sub
));
1024 for (int i
= 0; i
< GetSize(wr_ports
); i
++) {
1025 auto &port
= wr_ports
[i
];
1026 for (int sub
= 0; sub
< (1 << port
.wide_log2
); sub
++) {
1027 new_wr_map
.push_back(std::make_pair(i
, sub
));
1030 for (auto &it
: new_rd_map
) {
1031 MemRd
&orig
= rd_ports
[it
.first
];
1034 port
.cell
= nullptr;
1035 if (port
.wide_log2
) {
1036 port
.data
= port
.data
.extract(it
.second
* width
, width
);
1037 port
.init_value
= port
.init_value
.extract(it
.second
* width
, width
);
1038 port
.arst_value
= port
.arst_value
.extract(it
.second
* width
, width
);
1039 port
.srst_value
= port
.srst_value
.extract(it
.second
* width
, width
);
1040 port
.addr
= port
.sub_addr(it
.second
);
1043 port
.transparency_mask
.clear();
1044 port
.collision_x_mask
.clear();
1045 for (auto &it2
: new_wr_map
)
1046 port
.transparency_mask
.push_back(orig
.transparency_mask
[it2
.first
]);
1047 for (auto &it2
: new_wr_map
)
1048 port
.collision_x_mask
.push_back(orig
.collision_x_mask
[it2
.first
]);
1049 new_rd_ports
.push_back(port
);
1051 for (auto &it
: new_wr_map
) {
1052 MemWr
&orig
= wr_ports
[it
.first
];
1055 port
.cell
= nullptr;
1056 if (port
.wide_log2
) {
1057 port
.data
= port
.data
.extract(it
.second
* width
, width
);
1058 port
.en
= port
.en
.extract(it
.second
* width
, width
);
1059 port
.addr
= port
.sub_addr(it
.second
);
1062 port
.priority_mask
.clear();
1063 for (auto &it2
: new_wr_map
)
1064 port
.priority_mask
.push_back(orig
.priority_mask
[it2
.first
]);
1065 new_wr_ports
.push_back(port
);
1067 std::swap(rd_ports
, new_rd_ports
);
1068 std::swap(wr_ports
, new_wr_ports
);
1071 void Mem::emulate_priority(int idx1
, int idx2
, FfInitVals
*initvals
)
1073 auto &port1
= wr_ports
[idx1
];
1074 auto &port2
= wr_ports
[idx2
];
1075 if (!port2
.priority_mask
[idx1
])
1077 for (int i
= 0; i
< GetSize(rd_ports
); i
++) {
1078 auto &rport
= rd_ports
[i
];
1081 if (rport
.transparency_mask
[idx1
] && !(rport
.transparency_mask
[idx2
] || rport
.collision_x_mask
[idx2
]))
1082 emulate_transparency(idx1
, i
, initvals
);
1084 int min_wide_log2
= std::min(port1
.wide_log2
, port2
.wide_log2
);
1085 int max_wide_log2
= std::max(port1
.wide_log2
, port2
.wide_log2
);
1086 bool wide1
= port1
.wide_log2
> port2
.wide_log2
;
1087 for (int sub
= 0; sub
< (1 << max_wide_log2
); sub
+= (1 << min_wide_log2
)) {
1088 SigSpec addr1
= port1
.addr
;
1089 SigSpec addr2
= port2
.addr
;
1091 addr1
= port1
.sub_addr(sub
);
1093 addr2
= port2
.sub_addr(sub
);
1094 SigSpec addr_eq
= module
->Eq(NEW_ID
, addr1
, addr2
);
1095 int ewidth
= width
<< min_wide_log2
;
1096 int sub1
= wide1
? sub
: 0;
1097 int sub2
= wide1
? 0 : sub
;
1098 dict
<std::pair
<SigBit
, SigBit
>, SigBit
> cache
;
1099 for (int pos
= 0; pos
< ewidth
; pos
++) {
1100 SigBit
&en1
= port1
.en
[pos
+ sub1
* width
];
1101 SigBit
&en2
= port2
.en
[pos
+ sub2
* width
];
1102 std::pair
<SigBit
, SigBit
> key(en1
, en2
);
1103 if (cache
.count(key
)) {
1106 SigBit active2
= module
->And(NEW_ID
, addr_eq
, en2
);
1107 SigBit nactive2
= module
->Not(NEW_ID
, active2
);
1108 en1
= cache
[key
] = module
->And(NEW_ID
, en1
, nactive2
);
1112 port2
.priority_mask
[idx1
] = false;
1115 void Mem::emulate_transparency(int widx
, int ridx
, FfInitVals
*initvals
) {
1116 auto &wport
= wr_ports
[widx
];
1117 auto &rport
= rd_ports
[ridx
];
1118 log_assert(rport
.transparency_mask
[widx
]);
1119 // If other write ports have priority over this one, emulate their transparency too.
1120 for (int i
= GetSize(wr_ports
) - 1; i
> widx
; i
--) {
1121 if (wr_ports
[i
].removed
)
1123 if (rport
.transparency_mask
[i
] && wr_ports
[i
].priority_mask
[widx
])
1124 emulate_transparency(i
, ridx
, initvals
);
1126 int min_wide_log2
= std::min(rport
.wide_log2
, wport
.wide_log2
);
1127 int max_wide_log2
= std::max(rport
.wide_log2
, wport
.wide_log2
);
1128 bool wide_write
= wport
.wide_log2
> rport
.wide_log2
;
1129 // The write data FF doesn't need full reset/init behavior, as it'll be masked by
1130 // the mux whenever this would be relevant. It does, however, need to have the same
1131 // clock enable signal as the read port.
1132 SigSpec wdata_q
= module
->addWire(NEW_ID
, GetSize(wport
.data
));
1133 module
->addDffe(NEW_ID
, rport
.clk
, rport
.en
, wport
.data
, wdata_q
, rport
.clk_polarity
, true);
1134 for (int sub
= 0; sub
< (1 << max_wide_log2
); sub
+= (1 << min_wide_log2
)) {
1135 SigSpec raddr
= rport
.addr
;
1136 SigSpec waddr
= wport
.addr
;
1137 for (int j
= min_wide_log2
; j
< max_wide_log2
; j
++)
1139 waddr
= wport
.sub_addr(sub
);
1141 raddr
= rport
.sub_addr(sub
);
1144 addr_eq
= module
->Eq(NEW_ID
, raddr
, waddr
);
1146 int ewidth
= width
<< min_wide_log2
;
1147 int wsub
= wide_write
? sub
: 0;
1148 int rsub
= wide_write
? 0 : sub
;
1149 SigSpec rdata_a
= module
->addWire(NEW_ID
, ewidth
);
1150 while (pos
< ewidth
) {
1152 while (epos
< ewidth
&& wport
.en
[epos
+ wsub
* width
] == wport
.en
[pos
+ wsub
* width
])
1156 cond
= module
->And(NEW_ID
, wport
.en
[pos
+ wsub
* width
], addr_eq
);
1158 cond
= wport
.en
[pos
+ wsub
* width
];
1159 SigSpec cond_q
= module
->addWire(NEW_ID
);
1160 // The FF for storing the bypass enable signal must be carefully
1161 // constructed to preserve the overall init/reset/enable behavior
1162 // of the whole port.
1163 FfData
ff(module
, initvals
, NEW_ID
);
1168 ff
.sig_clk
= rport
.clk
;
1169 ff
.pol_clk
= rport
.clk_polarity
;
1170 if (rport
.en
!= State::S1
) {
1172 ff
.sig_ce
= rport
.en
;
1175 if (rport
.arst
!= State::S0
) {
1177 ff
.sig_arst
= rport
.arst
;
1179 ff
.val_arst
= State::S0
;
1181 if (rport
.srst
!= State::S0
) {
1183 ff
.sig_srst
= rport
.srst
;
1185 ff
.val_srst
= State::S0
;
1186 ff
.ce_over_srst
= rport
.ce_over_srst
;
1188 if (!rport
.init_value
.is_fully_undef())
1189 ff
.val_init
= State::S0
;
1191 ff
.val_init
= State::Sx
;
1193 // And the final bypass mux.
1194 SigSpec cur
= rdata_a
.extract(pos
, epos
-pos
);
1195 SigSpec other
= wdata_q
.extract(pos
+ wsub
* width
, epos
-pos
);
1196 SigSpec dest
= rport
.data
.extract(pos
+ rsub
* width
, epos
-pos
);
1197 module
->addMux(NEW_ID
, cur
, other
, cond_q
, dest
);
1200 rport
.data
.replace(rsub
* width
, rdata_a
);
1202 rport
.transparency_mask
[widx
] = false;
1203 rport
.collision_x_mask
[widx
] = true;
1206 void Mem::prepare_wr_merge(int idx1
, int idx2
, FfInitVals
*initvals
) {
1207 log_assert(idx1
< idx2
);
1208 auto &port1
= wr_ports
[idx1
];
1209 auto &port2
= wr_ports
[idx2
];
1210 // If port 2 has priority over a port before port 1, make port 1 have priority too.
1211 for (int i
= 0; i
< idx1
; i
++)
1212 if (port2
.priority_mask
[i
])
1213 port1
.priority_mask
[i
] = true;
1214 // If port 2 has priority over a port after port 1, emulate it.
1215 for (int i
= idx1
+ 1; i
< idx2
; i
++)
1216 if (port2
.priority_mask
[i
] && !wr_ports
[i
].removed
)
1217 emulate_priority(i
, idx2
, initvals
);
1218 // If some port had priority over port 2, make it have priority over the merged port too.
1219 for (int i
= idx2
+ 1; i
< GetSize(wr_ports
); i
++) {
1220 auto &oport
= wr_ports
[i
];
1221 if (oport
.priority_mask
[idx2
])
1222 oport
.priority_mask
[idx1
] = true;
1224 // Make sure all read ports have identical collision/transparency behavior wrt both
1226 for (int i
= 0; i
< GetSize(rd_ports
); i
++) {
1227 auto &rport
= rd_ports
[i
];
1230 // If collision already undefined with both ports, it's fine.
1231 if (rport
.collision_x_mask
[idx1
] && rport
.collision_x_mask
[idx2
])
1233 // If one port has undefined collision, change it to the behavior
1234 // of the other port.
1235 if (rport
.collision_x_mask
[idx1
]) {
1236 rport
.collision_x_mask
[idx1
] = false;
1237 rport
.transparency_mask
[idx1
] = rport
.transparency_mask
[idx2
];
1240 if (rport
.collision_x_mask
[idx2
]) {
1241 rport
.collision_x_mask
[idx2
] = false;
1242 rport
.transparency_mask
[idx2
] = rport
.transparency_mask
[idx1
];
1245 // If transparent with both ports, also fine.
1246 if (rport
.transparency_mask
[idx1
] && rport
.transparency_mask
[idx2
])
1248 // If transparent with only one, emulate it, and remove the collision-X
1249 // flag that emulate_transparency will set (to align with the other port).
1250 if (rport
.transparency_mask
[idx1
]) {
1251 emulate_transparency(i
, idx1
, initvals
);
1252 rport
.collision_x_mask
[idx1
] = false;
1255 if (rport
.transparency_mask
[idx2
]) {
1256 emulate_transparency(i
, idx2
, initvals
);
1257 rport
.collision_x_mask
[idx2
] = false;
1260 // If we got here, it's transparent with neither port, which is fine.
1264 void Mem::prepare_rd_merge(int idx1
, int idx2
, FfInitVals
*initvals
) {
1265 auto &port1
= rd_ports
[idx1
];
1266 auto &port2
= rd_ports
[idx2
];
1267 // Note that going through write ports in order is important, since
1268 // emulating transparency of a write port can change transparency
1269 // mask for higher-numbered ports (due to transitive transparency
1270 // emulation needed because of write port priority).
1271 for (int i
= 0; i
< GetSize(wr_ports
); i
++) {
1272 if (wr_ports
[i
].removed
)
1274 // Both ports undefined, OK.
1275 if (port1
.collision_x_mask
[i
] && port2
.collision_x_mask
[i
])
1277 // Only one port undefined — change its behavior
1278 // to align with the other port.
1279 if (port1
.collision_x_mask
[i
]) {
1280 port1
.collision_x_mask
[i
] = false;
1281 port1
.transparency_mask
[i
] = port2
.transparency_mask
[i
];
1284 if (port2
.collision_x_mask
[i
]) {
1285 port2
.collision_x_mask
[i
] = false;
1286 port2
.transparency_mask
[i
] = port1
.transparency_mask
[i
];
1289 // Both ports transparent, OK.
1290 if (port1
.transparency_mask
[i
] && port2
.transparency_mask
[i
])
1292 // Only one port transparent — emulate transparency
1294 if (port1
.transparency_mask
[i
]) {
1295 emulate_transparency(i
, idx1
, initvals
);
1296 port1
.collision_x_mask
[i
] = false;
1299 if (port2
.transparency_mask
[i
]) {
1300 emulate_transparency(i
, idx2
, initvals
);
1301 port2
.collision_x_mask
[i
] = false;
1304 // No ports transparent, OK.
1309 void Mem::widen_prep(int wide_log2
) {
1310 // Make sure start_offset and size are aligned to the port width,
1311 // adjust if necessary.
1312 int mask
= ((1 << wide_log2
) - 1);
1313 int delta
= start_offset
& mask
;
1314 start_offset
-= delta
;
1322 void Mem::widen_wr_port(int idx
, int wide_log2
) {
1323 widen_prep(wide_log2
);
1324 auto &port
= wr_ports
[idx
];
1325 log_assert(port
.wide_log2
<= wide_log2
);
1326 if (port
.wide_log2
< wide_log2
) {
1327 SigSpec new_data
, new_en
;
1328 SigSpec addr_lo
= port
.addr
.extract(0, wide_log2
);
1329 for (int sub
= 0; sub
< (1 << wide_log2
); sub
+= (1 << port
.wide_log2
))
1331 Const
cur_addr_lo(sub
, wide_log2
);
1332 if (addr_lo
== cur_addr_lo
) {
1333 // Always writes to this subword.
1334 new_data
.append(port
.data
);
1335 new_en
.append(port
.en
);
1336 } else if (addr_lo
.is_fully_const()) {
1337 // Never writes to this subword.
1338 new_data
.append(Const(State::Sx
, GetSize(port
.data
)));
1339 new_en
.append(Const(State::S0
, GetSize(port
.data
)));
1341 // May or may not write to this subword.
1342 new_data
.append(port
.data
);
1343 SigSpec addr_eq
= module
->Eq(NEW_ID
, addr_lo
, cur_addr_lo
);
1344 SigSpec en
= module
->Mux(NEW_ID
, Const(State::S0
, GetSize(port
.data
)), port
.en
, addr_eq
);
1348 port
.addr
.replace(port
.wide_log2
, Const(State::S0
, wide_log2
- port
.wide_log2
));
1349 port
.data
= new_data
;
1351 port
.wide_log2
= wide_log2
;