FfData: some refactoring.
[yosys.git] / kernel / mem.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2020 Marcelina Kościelnicka <mwk@0x04.net>
5 *
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.
9 *
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.
17 *
18 */
19
20 #include "kernel/mem.h"
21 #include "kernel/ff.h"
22
23 USING_YOSYS_NAMESPACE
24
25 void Mem::remove() {
26 if (cell) {
27 module->remove(cell);
28 cell = nullptr;
29 }
30 if (mem) {
31 module->memories.erase(mem->name);
32 delete mem;
33 mem = nullptr;
34 }
35 for (auto &port : rd_ports) {
36 if (port.cell) {
37 module->remove(port.cell);
38 port.cell = nullptr;
39 }
40 }
41 for (auto &port : wr_ports) {
42 if (port.cell) {
43 module->remove(port.cell);
44 port.cell = nullptr;
45 }
46 }
47 for (auto &init : inits) {
48 if (init.cell) {
49 module->remove(init.cell);
50 init.cell = nullptr;
51 }
52 }
53 }
54
55 void Mem::emit() {
56 check();
57 std::vector<int> rd_left;
58 for (int i = 0; i < GetSize(rd_ports); i++) {
59 auto &port = rd_ports[i];
60 if (port.removed) {
61 if (port.cell) {
62 module->remove(port.cell);
63 }
64 } else {
65 rd_left.push_back(i);
66 }
67 }
68 std::vector<int> wr_left;
69 for (int i = 0; i < GetSize(wr_ports); i++) {
70 auto &port = wr_ports[i];
71 if (port.removed) {
72 if (port.cell) {
73 module->remove(port.cell);
74 }
75 } else {
76 wr_left.push_back(i);
77 }
78 }
79 std::vector<int> init_left;
80 for (int i = 0; i < GetSize(inits); i++) {
81 auto &init = inits[i];
82 if (init.removed) {
83 if (init.cell) {
84 module->remove(init.cell);
85 }
86 } else {
87 init_left.push_back(i);
88 }
89 }
90 for (int i = 0; i < GetSize(rd_left); i++)
91 if (i != 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++)
95 if (i != 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));
102
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]];
107 }
108 port.transparency_mask.resize(GetSize(wr_left));
109 port.collision_x_mask.resize(GetSize(wr_left));
110 }
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));
115 }
116
117 if (packed) {
118 if (mem) {
119 module->memories.erase(mem->name);
120 delete mem;
121 mem = nullptr;
122 }
123 if (!cell) {
124 if (memid.empty())
125 memid = NEW_ID;
126 cell = module->addCell(memid, ID($mem_v2));
127 }
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;
140 int abits = 0;
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) {
151 if (port.cell) {
152 module->remove(port.cell);
153 port.cell = nullptr;
154 }
155 for (int sub = 0; sub < (1 << port.wide_log2); sub++)
156 {
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])));
172 }
173 }
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);
181 }
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;
190 }
191 if (rd_ports.empty() || wr_ports.empty()) {
192 rd_transparency_mask = State::S0;
193 rd_collision_x_mask = State::S0;
194 }
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) {
213 if (port.cell) {
214 module->remove(port.cell);
215 port.cell = nullptr;
216 }
217 for (int sub = 0; sub < (1 << port.wide_log2); sub++)
218 {
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);
229 }
230 wr_en.append(port.en);
231 wr_data.append(port.data);
232 }
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;
238 }
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) {
249 if (init.cell) {
250 module->remove(init.cell);
251 init.cell = nullptr;
252 }
253 }
254 cell->parameters[ID::INIT] = get_init_data();
255 } else {
256 if (cell) {
257 module->remove(cell);
258 cell = nullptr;
259 }
260 if (!mem) {
261 if (memid.empty())
262 memid = NEW_ID;
263 mem = new RTLIL::Memory;
264 mem->name = memid;
265 module->memories[memid] = mem;
266 }
267 mem->width = width;
268 mem->start_offset = start_offset;
269 mem->size = size;
270 mem->attributes = attributes;
271 for (auto &port : rd_ports) {
272 if (!port.cell)
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);
294 }
295 int idx = 0;
296 for (auto &port : wr_ports) {
297 if (!port.cell)
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);
314 }
315 idx = 0;
316 for (auto &init : inits) {
317 bool v2 = !init.en.is_fully_ones();
318 if (!init.cell)
319 init.cell = module->addCell(NEW_ID, v2 ? ID($meminit_v2) : ID($meminit));
320 else
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);
330 if (v2)
331 init.cell->setPort(ID::EN, init.en);
332 else
333 init.cell->unsetPort(ID::EN);
334 }
335 }
336 }
337
338 void Mem::clear_inits() {
339 for (auto &init : inits)
340 init.removed = true;
341 }
342
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) {
348 if (init.removed)
349 continue;
350 bool valid = false;
351 for (auto bit : init.en)
352 if (bit == State::S1)
353 valid = true;
354 if (!valid) {
355 init.removed = true;
356 continue;
357 }
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);
361 auto it = it_e;
362 while (it != chunks.begin()) {
363 --it;
364 if (it->second < addr) {
365 ++it;
366 break;
367 }
368 }
369 if (it == it_e) {
370 // No overlapping inits — add this one to index.
371 chunks[addr] = addr_e;
372 } else {
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)
376 addr = it->first;
377 auto it_last = it_e;
378 it_last--;
379 if (it_last->second > addr_e)
380 addr_e = it_last->second;
381 chunks.erase(it, it_e);
382 chunks[addr] = addr_e;
383 }
384 }
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];
389 if (init.removed)
390 continue;
391 auto it = chunks.upper_bound(init.addr.as_int());
392 --it;
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);
397 }
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);
410 }
411 continue;
412 }
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];
422 init.removed = true;
423 }
424 MemInit new_init;
425 new_init.addr = caddr;
426 new_init.data = cdata;
427 new_init.en = Const(State::S1, width);
428 inits.push_back(new_init);
429 }
430 }
431
432 Const Mem::get_init_data() const {
433 Const init_data(State::Sx, width * size);
434 for (auto &init : inits) {
435 if (init.removed)
436 continue;
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];
441 }
442 return init_data;
443 }
444
445 void Mem::check() {
446 int max_wide_log2 = 0;
447 for (auto &port : rd_ports) {
448 if (port.removed)
449 continue;
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);
462 }
463 for (int j = 0; j < port.wide_log2; j++) {
464 log_assert(port.addr[j] == State::S0);
465 }
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);
476 }
477 log_assert(!port.transparency_mask[j] || !port.collision_x_mask[j]);
478 }
479 }
480 for (int i = 0; i < GetSize(wr_ports); i++) {
481 auto &port = wr_ports[i];
482 if (port.removed)
483 continue;
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);
489 }
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) {
495 log_assert(j < i);
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);
500 }
501 }
502 }
503 }
504 int mask = (1 << max_wide_log2) - 1;
505 log_assert(!(start_offset & mask));
506 log_assert(!(size & mask));
507 }
508
509 namespace {
510
511 struct MemIndex {
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);
523 }
524 }
525 };
526
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);
529 res.packed = false;
530 res.mem = mem;
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)) {
536 MemRd mrd;
537 bool is_compat = cell->type == ID($memrd);
538 mrd.cell = cell;
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;
548 if (is_compat) {
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).
561 transparent = false;
562 if (mrd.en == State::Sx)
563 mrd.en = State::S1;
564 }
565 } else {
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);
572 }
573 res.rd_ports.push_back(mrd);
574 rd_transparent.push_back(transparent);
575 }
576 }
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)) {
580 MemWr mwr;
581 bool is_compat = cell->type == ID($memwr);
582 mwr.cell = cell;
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));
592 }
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);
597 }
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);
601 if (is_compat) {
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)
606 continue;
607 if (port.clk_enable && port.clk != oport.clk)
608 continue;
609 if (port.clk_enable && port.clk_polarity != oport.clk_polarity)
610 continue;
611 port.priority_mask[j] = true;
612 }
613 } else {
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);
618 }
619 }
620 }
621 }
622 if (index.inits.count(mem->name)) {
623 std::vector<std::pair<int, MemInit>> inits;
624 for (auto cell : index.inits.at(mem->name)) {
625 MemInit init;
626 init.cell = cell;
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();
641 } else {
642 init.en = RTLIL::Const(State::S1, mem->width);
643 }
644 inits.push_back(std::make_pair(cell->parameters.at(ID::PRIORITY).as_int(), init));
645 }
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);
649 }
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);
653 if (is_compat) {
654 port.transparency_mask.resize(GetSize(res.wr_ports));
655 port.collision_x_mask.resize(GetSize(res.wr_ports));
656 if (!rd_transparent[i])
657 continue;
658 if (!port.clk_enable)
659 continue;
660 for (int j = 0; j < GetSize(res.wr_ports); j++) {
661 auto &wport = res.wr_ports[j];
662 if (!wport.clk_enable)
663 continue;
664 if (port.clk != wport.clk)
665 continue;
666 if (port.clk_polarity != wport.clk_polarity)
667 continue;
668 port.transparency_mask[j] = true;
669 }
670 } else {
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);
676 }
677 }
678 }
679 res.check();
680 return res;
681 }
682
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()
688 );
689 bool is_compat = cell->type == ID($mem);
690 int abits = cell->parameters.at(ID::ABITS).as_int();
691 res.packed = true;
692 res.cell = cell;
693 res.attributes = cell->attributes;
694 Const &init = cell->parameters.at(ID::INIT);
695 if (!init.is_fully_undef()) {
696 int pos = 0;
697 while (pos < res.size) {
698 Const word = init.extract(pos * res.width, res.width, State::Sx);
699 if (word.is_fully_undef()) {
700 pos++;
701 } else {
702 int epos;
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())
706 break;
707 }
708 MemInit minit;
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);
713 pos = epos;
714 }
715 }
716 }
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) {
722 ni = i + 1;
723 while (ni < n_rd_ports && rd_wide_continuation[ni] == State::S1)
724 ni++;
725 MemRd mrd;
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);
734 if (is_compat) {
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;
741 } else {
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);
748 }
749 if (!is_compat) {
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);
756 }
757 }
758 res.rd_ports.push_back(mrd);
759 }
760 for (int i = 0, ni; i < n_wr_ports; i = ni) {
761 ni = i + 1;
762 while (ni < n_wr_ports && wr_wide_continuation[ni] == State::S1)
763 ni++;
764 MemWr mwr;
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);
773 if (!is_compat) {
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);
778 }
779 res.wr_ports.push_back(mwr);
780 }
781 if (is_compat) {
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)
788 continue;
789 if (port.clk_enable && port.clk != oport.clk)
790 continue;
791 if (port.clk_enable && port.clk_polarity != oport.clk_polarity)
792 continue;
793 port.priority_mask[j] = true;
794 }
795 }
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())
801 continue;
802 if (!port.clk_enable)
803 continue;
804 for (int j = 0; j < GetSize(res.wr_ports); j++) {
805 auto &wport = res.wr_ports[j];
806 if (!wport.clk_enable)
807 continue;
808 if (port.clk != wport.clk)
809 continue;
810 if (port.clk_polarity != wport.clk_polarity)
811 continue;
812 port.transparency_mask[j] = true;
813 }
814 }
815 }
816 res.check();
817 return res;
818 }
819
820 }
821
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));
827 }
828 for (auto cell: module->cells()) {
829 if (cell->type.in(ID($mem), ID($mem_v2)))
830 res.push_back(mem_from_cell(cell));
831 }
832 return res;
833 }
834
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));
841 }
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));
845 }
846 return res;
847 }
848
849 Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) {
850 MemRd &port = rd_ports[idx];
851
852 if (!port.clk_enable)
853 return nullptr;
854
855 Cell *c;
856
857 // There are two ways to handle rdff extraction when transparency is involved:
858 //
859 // - if all of the following conditions are true, put the FF on address input:
860 //
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
863 // the clock domain)
864 //
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;
871
872 // If there are no write ports at all, we could possibly use either way; do data
873 // FF in this case.
874 if (GetSize(wr_ports) == 0)
875 trans_use_addr = false;
876
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;
879
880 if (trans_use_addr)
881 {
882 // Do not put a register in front of constant address bits — this is both
883 // unnecessary and will break wide ports.
884 int width = 0;
885 for (int i = 0; i < GetSize(port.addr); i++)
886 if (port.addr[i].wire)
887 width++;
888
889 if (width)
890 {
891 SigSpec sig_q = module->addWire(stringf("$%s$rdreg[%d]$q", memid.c_str(), idx), width);
892 SigSpec sig_d;
893
894 int pos = 0;
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++];
899 }
900
901 c = module->addDff(stringf("$%s$rdreg[%d]", memid.c_str(), idx), port.clk, sig_d, sig_q, port.clk_polarity);
902 } else {
903 c = nullptr;
904 }
905 }
906 else
907 {
908 log_assert(port.arst == State::S0 || port.srst == State::S0);
909
910 SigSpec async_d = module->addWire(stringf("$%s$rdreg[%d]$d", memid.c_str(), idx), GetSize(port.data));
911 SigSpec sig_d = async_d;
912
913 for (int i = 0; i < GetSize(wr_ports); i++) {
914 auto &wport = wr_ports[i];
915 if (wport.removed)
916 continue;
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;
927 if (wide_write)
928 waddr = wport.sub_addr(sub);
929 else
930 raddr = port.sub_addr(sub);
931 SigSpec addr_eq;
932 if (raddr != waddr)
933 addr_eq = module->Eq(stringf("$%s$rdtransen[%d][%d][%d]$d", memid.c_str(), idx, i, sub), raddr, waddr);
934 int pos = 0;
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) {
939 int epos = pos;
940 while (epos < ewidth && wport.en[epos + wsub * width] == wport.en[pos + wsub * width])
941 epos++;
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);
944 SigSpec cond;
945 if (raddr != waddr)
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);
947 else
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);
951 pos = epos;
952 }
953 }
954 }
955 }
956
957 IdString name = stringf("$%s$rdreg[%d]", memid.c_str(), idx);
958 FfData ff(module, initvals, name);
959 ff.width = GetSize(port.data);
960 ff.has_clk = true;
961 ff.sig_clk = port.clk;
962 ff.pol_clk = port.clk_polarity;
963 if (port.en != State::S1) {
964 ff.has_ce = true;
965 ff.pol_ce = true;
966 ff.sig_ce = port.en;
967 }
968 if (port.arst != State::S0) {
969 ff.has_arst = true;
970 ff.pol_arst = true;
971 ff.sig_arst = port.arst;
972 ff.val_arst = port.arst_value;
973 }
974 if (port.srst != State::S0) {
975 ff.has_srst = true;
976 ff.pol_srst = true;
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;
980 }
981 ff.sig_d = sig_d;
982 ff.sig_q = port.data;
983 ff.val_init = port.init_value;
984 port.data = async_d;
985 c = ff.emit();
986 }
987
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));
990
991 port.en = State::S1;
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));
1001
1002 for (int i = 0; i < GetSize(wr_ports); i++) {
1003 port.transparency_mask[i] = false;
1004 port.collision_x_mask[i] = false;
1005 }
1006
1007 return c;
1008 }
1009
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));
1022 }
1023 }
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));
1028 }
1029 }
1030 for (auto &it : new_rd_map) {
1031 MemRd &orig = rd_ports[it.first];
1032 MemRd port = orig;
1033 if (it.second != 0)
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);
1041 port.wide_log2 = 0;
1042 }
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);
1050 }
1051 for (auto &it : new_wr_map) {
1052 MemWr &orig = wr_ports[it.first];
1053 MemWr port = orig;
1054 if (it.second != 0)
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);
1060 port.wide_log2 = 0;
1061 }
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);
1066 }
1067 std::swap(rd_ports, new_rd_ports);
1068 std::swap(wr_ports, new_wr_ports);
1069 }
1070
1071 void Mem::emulate_priority(int idx1, int idx2, FfInitVals *initvals)
1072 {
1073 auto &port1 = wr_ports[idx1];
1074 auto &port2 = wr_ports[idx2];
1075 if (!port2.priority_mask[idx1])
1076 return;
1077 for (int i = 0; i < GetSize(rd_ports); i++) {
1078 auto &rport = rd_ports[i];
1079 if (rport.removed)
1080 continue;
1081 if (rport.transparency_mask[idx1] && !(rport.transparency_mask[idx2] || rport.collision_x_mask[idx2]))
1082 emulate_transparency(idx1, i, initvals);
1083 }
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;
1090 if (wide1)
1091 addr1 = port1.sub_addr(sub);
1092 else
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)) {
1104 en1 = cache[key];
1105 } else {
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);
1109 }
1110 }
1111 }
1112 port2.priority_mask[idx1] = false;
1113 }
1114
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)
1122 continue;
1123 if (rport.transparency_mask[i] && wr_ports[i].priority_mask[widx])
1124 emulate_transparency(i, ridx, initvals);
1125 }
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++)
1138 if (wide_write)
1139 waddr = wport.sub_addr(sub);
1140 else
1141 raddr = rport.sub_addr(sub);
1142 SigSpec addr_eq;
1143 if (raddr != waddr)
1144 addr_eq = module->Eq(NEW_ID, raddr, waddr);
1145 int pos = 0;
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) {
1151 int epos = pos;
1152 while (epos < ewidth && wport.en[epos + wsub * width] == wport.en[pos + wsub * width])
1153 epos++;
1154 SigSpec cond;
1155 if (raddr != waddr)
1156 cond = module->And(NEW_ID, wport.en[pos + wsub * width], addr_eq);
1157 else
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);
1164 ff.width = 1;
1165 ff.sig_q = cond_q;
1166 ff.sig_d = cond;
1167 ff.has_clk = true;
1168 ff.sig_clk = rport.clk;
1169 ff.pol_clk = rport.clk_polarity;
1170 if (rport.en != State::S1) {
1171 ff.has_ce = true;
1172 ff.sig_ce = rport.en;
1173 ff.pol_ce = true;
1174 }
1175 if (rport.arst != State::S0) {
1176 ff.has_arst = true;
1177 ff.sig_arst = rport.arst;
1178 ff.pol_arst = true;
1179 ff.val_arst = State::S0;
1180 }
1181 if (rport.srst != State::S0) {
1182 ff.has_srst = true;
1183 ff.sig_srst = rport.srst;
1184 ff.pol_srst = true;
1185 ff.val_srst = State::S0;
1186 ff.ce_over_srst = rport.ce_over_srst;
1187 }
1188 if (!rport.init_value.is_fully_undef())
1189 ff.val_init = State::S0;
1190 else
1191 ff.val_init = State::Sx;
1192 ff.emit();
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);
1198 pos = epos;
1199 }
1200 rport.data.replace(rsub * width, rdata_a);
1201 }
1202 rport.transparency_mask[widx] = false;
1203 rport.collision_x_mask[widx] = true;
1204 }
1205
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;
1223 }
1224 // Make sure all read ports have identical collision/transparency behavior wrt both
1225 // ports.
1226 for (int i = 0; i < GetSize(rd_ports); i++) {
1227 auto &rport = rd_ports[i];
1228 if (rport.removed)
1229 continue;
1230 // If collision already undefined with both ports, it's fine.
1231 if (rport.collision_x_mask[idx1] && rport.collision_x_mask[idx2])
1232 continue;
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];
1238 continue;
1239 }
1240 if (rport.collision_x_mask[idx2]) {
1241 rport.collision_x_mask[idx2] = false;
1242 rport.transparency_mask[idx2] = rport.transparency_mask[idx1];
1243 continue;
1244 }
1245 // If transparent with both ports, also fine.
1246 if (rport.transparency_mask[idx1] && rport.transparency_mask[idx2])
1247 continue;
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;
1253 continue;
1254 }
1255 if (rport.transparency_mask[idx2]) {
1256 emulate_transparency(i, idx2, initvals);
1257 rport.collision_x_mask[idx2] = false;
1258 continue;
1259 }
1260 // If we got here, it's transparent with neither port, which is fine.
1261 }
1262 }
1263
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)
1273 continue;
1274 // Both ports undefined, OK.
1275 if (port1.collision_x_mask[i] && port2.collision_x_mask[i])
1276 continue;
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];
1282 continue;
1283 }
1284 if (port2.collision_x_mask[i]) {
1285 port2.collision_x_mask[i] = false;
1286 port2.transparency_mask[i] = port1.transparency_mask[i];
1287 continue;
1288 }
1289 // Both ports transparent, OK.
1290 if (port1.transparency_mask[i] && port2.transparency_mask[i])
1291 continue;
1292 // Only one port transparent — emulate transparency
1293 // on the other.
1294 if (port1.transparency_mask[i]) {
1295 emulate_transparency(i, idx1, initvals);
1296 port1.collision_x_mask[i] = false;
1297 continue;
1298 }
1299 if (port2.transparency_mask[i]) {
1300 emulate_transparency(i, idx2, initvals);
1301 port2.collision_x_mask[i] = false;
1302 continue;
1303 }
1304 // No ports transparent, OK.
1305 }
1306
1307 }
1308
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;
1315 size += delta;
1316 if (size & mask) {
1317 size |= mask;
1318 size++;
1319 }
1320 }
1321
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))
1330 {
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)));
1340 } else {
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);
1345 new_en.append(en);
1346 }
1347 }
1348 port.addr.replace(port.wide_log2, Const(State::S0, wide_log2 - port.wide_log2));
1349 port.data = new_data;
1350 port.en = new_en;
1351 port.wide_log2 = wide_log2;
1352 }
1353 }