kernel/mem: Add model for wide ports.
[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
22 USING_YOSYS_NAMESPACE
23
24 void Mem::remove() {
25 if (cell) {
26 module->remove(cell);
27 cell = nullptr;
28 }
29 if (mem) {
30 module->memories.erase(mem->name);
31 delete mem;
32 mem = nullptr;
33 }
34 for (auto &port : rd_ports) {
35 if (port.cell) {
36 module->remove(port.cell);
37 port.cell = nullptr;
38 }
39 }
40 for (auto &port : wr_ports) {
41 if (port.cell) {
42 module->remove(port.cell);
43 port.cell = nullptr;
44 }
45 }
46 for (auto &init : inits) {
47 if (init.cell) {
48 module->remove(init.cell);
49 init.cell = nullptr;
50 }
51 }
52 }
53
54 void Mem::emit() {
55 check();
56 std::vector<int> rd_left;
57 for (int i = 0; i < GetSize(rd_ports); i++) {
58 auto &port = rd_ports[i];
59 if (port.removed) {
60 if (port.cell) {
61 module->remove(port.cell);
62 }
63 } else {
64 rd_left.push_back(i);
65 }
66 }
67 std::vector<int> wr_left;
68 for (int i = 0; i < GetSize(wr_ports); i++) {
69 auto &port = wr_ports[i];
70 if (port.removed) {
71 if (port.cell) {
72 module->remove(port.cell);
73 }
74 } else {
75 wr_left.push_back(i);
76 }
77 }
78 for (int i = 0; i < GetSize(rd_left); i++)
79 if (i != rd_left[i])
80 std::swap(rd_ports[i], rd_ports[rd_left[i]]);
81 rd_ports.resize(GetSize(rd_left));
82 for (int i = 0; i < GetSize(wr_left); i++)
83 if (i != wr_left[i])
84 std::swap(wr_ports[i], wr_ports[wr_left[i]]);
85 wr_ports.resize(GetSize(wr_left));
86
87 // for future: handle transparency mask here
88
89 for (auto &port : wr_ports) {
90 for (int i = 0; i < GetSize(wr_left); i++)
91 port.priority_mask[i] = port.priority_mask[wr_left[i]];
92 port.priority_mask.resize(GetSize(wr_left));
93 }
94
95 if (packed) {
96 if (mem) {
97 module->memories.erase(mem->name);
98 delete mem;
99 mem = nullptr;
100 }
101 if (!cell) {
102 if (memid.empty())
103 memid = NEW_ID;
104 cell = module->addCell(memid, ID($mem));
105 }
106 cell->attributes = attributes;
107 cell->parameters[ID::MEMID] = Const(memid.str());
108 cell->parameters[ID::WIDTH] = Const(width);
109 cell->parameters[ID::OFFSET] = Const(start_offset);
110 cell->parameters[ID::SIZE] = Const(size);
111 cell->parameters[ID::RD_PORTS] = Const(GetSize(rd_ports));
112 cell->parameters[ID::WR_PORTS] = Const(GetSize(wr_ports));
113 Const rd_clk_enable, rd_clk_polarity, rd_transparent;
114 Const wr_clk_enable, wr_clk_polarity;
115 SigSpec rd_clk, rd_en, rd_addr, rd_data;
116 SigSpec wr_clk, wr_en, wr_addr, wr_data;
117 int abits = 0;
118 for (auto &port : rd_ports)
119 abits = std::max(abits, GetSize(port.addr));
120 for (auto &port : wr_ports)
121 abits = std::max(abits, GetSize(port.addr));
122 cell->parameters[ID::ABITS] = Const(abits);
123 for (auto &port : rd_ports) {
124 // TODO: remove
125 log_assert(port.wide_log2 == 0);
126 if (port.cell) {
127 module->remove(port.cell);
128 port.cell = nullptr;
129 }
130 rd_clk_enable.bits.push_back(State(port.clk_enable));
131 rd_clk_polarity.bits.push_back(State(port.clk_polarity));
132 rd_transparent.bits.push_back(State(port.transparent));
133 rd_clk.append(port.clk);
134 log_assert(GetSize(port.clk) == 1);
135 rd_en.append(port.en);
136 log_assert(GetSize(port.en) == 1);
137 SigSpec addr = port.addr;
138 addr.extend_u0(abits, false);
139 rd_addr.append(addr);
140 log_assert(GetSize(addr) == abits);
141 rd_data.append(port.data);
142 log_assert(GetSize(port.data) == width);
143 }
144 if (rd_ports.empty()) {
145 rd_clk_enable = State::S0;
146 rd_clk_polarity = State::S0;
147 rd_transparent = State::S0;
148 }
149 cell->parameters[ID::RD_CLK_ENABLE] = rd_clk_enable;
150 cell->parameters[ID::RD_CLK_POLARITY] = rd_clk_polarity;
151 cell->parameters[ID::RD_TRANSPARENT] = rd_transparent;
152 cell->setPort(ID::RD_CLK, rd_clk);
153 cell->setPort(ID::RD_EN, rd_en);
154 cell->setPort(ID::RD_ADDR, rd_addr);
155 cell->setPort(ID::RD_DATA, rd_data);
156 for (auto &port : wr_ports) {
157 // TODO: remove
158 log_assert(port.wide_log2 == 0);
159 if (port.cell) {
160 module->remove(port.cell);
161 port.cell = nullptr;
162 }
163 wr_clk_enable.bits.push_back(State(port.clk_enable));
164 wr_clk_polarity.bits.push_back(State(port.clk_polarity));
165 wr_clk.append(port.clk);
166 log_assert(GetSize(port.clk) == 1);
167 wr_en.append(port.en);
168 log_assert(GetSize(port.en) == width);
169 SigSpec addr = port.addr;
170 addr.extend_u0(abits, false);
171 wr_addr.append(addr);
172 log_assert(GetSize(addr) == abits);
173 wr_data.append(port.data);
174 log_assert(GetSize(port.data) == width);
175 }
176 if (wr_ports.empty()) {
177 wr_clk_enable = State::S0;
178 wr_clk_polarity = State::S0;
179 }
180 cell->parameters[ID::WR_CLK_ENABLE] = wr_clk_enable;
181 cell->parameters[ID::WR_CLK_POLARITY] = wr_clk_polarity;
182 cell->setPort(ID::WR_CLK, wr_clk);
183 cell->setPort(ID::WR_EN, wr_en);
184 cell->setPort(ID::WR_ADDR, wr_addr);
185 cell->setPort(ID::WR_DATA, wr_data);
186 for (auto &init : inits) {
187 if (init.cell) {
188 module->remove(init.cell);
189 init.cell = nullptr;
190 }
191 }
192 cell->parameters[ID::INIT] = get_init_data();
193 } else {
194 if (cell) {
195 module->remove(cell);
196 cell = nullptr;
197 }
198 if (!mem) {
199 if (memid.empty())
200 memid = NEW_ID;
201 mem = new RTLIL::Memory;
202 mem->name = memid;
203 module->memories[memid] = mem;
204 }
205 mem->width = width;
206 mem->start_offset = start_offset;
207 mem->size = size;
208 for (auto &port : rd_ports) {
209 if (!port.cell)
210 port.cell = module->addCell(NEW_ID, ID($memrd));
211 port.cell->parameters[ID::MEMID] = memid.str();
212 port.cell->parameters[ID::ABITS] = GetSize(port.addr);
213 port.cell->parameters[ID::WIDTH] = width << port.wide_log2;
214 port.cell->parameters[ID::CLK_ENABLE] = port.clk_enable;
215 port.cell->parameters[ID::CLK_POLARITY] = port.clk_polarity;
216 port.cell->parameters[ID::TRANSPARENT] = port.transparent;
217 port.cell->setPort(ID::CLK, port.clk);
218 port.cell->setPort(ID::EN, port.en);
219 port.cell->setPort(ID::ADDR, port.addr);
220 port.cell->setPort(ID::DATA, port.data);
221 }
222 int idx = 0;
223 for (auto &port : wr_ports) {
224 if (!port.cell)
225 port.cell = module->addCell(NEW_ID, ID($memwr));
226 port.cell->parameters[ID::MEMID] = memid.str();
227 port.cell->parameters[ID::ABITS] = GetSize(port.addr);
228 port.cell->parameters[ID::WIDTH] = width << port.wide_log2;
229 port.cell->parameters[ID::CLK_ENABLE] = port.clk_enable;
230 port.cell->parameters[ID::CLK_POLARITY] = port.clk_polarity;
231 port.cell->parameters[ID::PRIORITY] = idx++;
232 port.cell->setPort(ID::CLK, port.clk);
233 port.cell->setPort(ID::EN, port.en);
234 port.cell->setPort(ID::ADDR, port.addr);
235 port.cell->setPort(ID::DATA, port.data);
236 }
237 idx = 0;
238 for (auto &init : inits) {
239 if (!init.cell)
240 init.cell = module->addCell(NEW_ID, ID($meminit));
241 init.cell->parameters[ID::MEMID] = memid.str();
242 init.cell->parameters[ID::ABITS] = GetSize(init.addr);
243 init.cell->parameters[ID::WIDTH] = width;
244 init.cell->parameters[ID::WORDS] = GetSize(init.data) / width;
245 init.cell->parameters[ID::PRIORITY] = idx++;
246 init.cell->setPort(ID::ADDR, init.addr);
247 init.cell->setPort(ID::DATA, init.data);
248 }
249 }
250 }
251
252 void Mem::clear_inits() {
253 for (auto &init : inits)
254 if (init.cell)
255 module->remove(init.cell);
256 inits.clear();
257 }
258
259 Const Mem::get_init_data() const {
260 Const init_data(State::Sx, width * size);
261 for (auto &init : inits) {
262 int offset = (init.addr.as_int() - start_offset) * width;
263 for (int i = 0; i < GetSize(init.data); i++)
264 if (0 <= i+offset && i+offset < GetSize(init_data))
265 init_data.bits[i+offset] = init.data.bits[i];
266 }
267 return init_data;
268 }
269
270 void Mem::check() {
271 int max_wide_log2 = 0;
272 for (auto &port : rd_ports) {
273 if (port.removed)
274 continue;
275 log_assert(GetSize(port.clk) == 1);
276 log_assert(GetSize(port.en) == 1);
277 log_assert(GetSize(port.data) == (width << port.wide_log2));
278 if (!port.clk_enable) {
279 log_assert(!port.transparent);
280 }
281 for (int j = 0; j < port.wide_log2; j++) {
282 log_assert(port.addr[j] == State::S0);
283 }
284 max_wide_log2 = std::max(max_wide_log2, port.wide_log2);
285 }
286 for (int i = 0; i < GetSize(wr_ports); i++) {
287 auto &port = wr_ports[i];
288 if (port.removed)
289 continue;
290 log_assert(GetSize(port.clk) == 1);
291 log_assert(GetSize(port.en) == (width << port.wide_log2));
292 log_assert(GetSize(port.data) == (width << port.wide_log2));
293 for (int j = 0; j < port.wide_log2; j++) {
294 log_assert(port.addr[j] == State::S0);
295 }
296 max_wide_log2 = std::max(max_wide_log2, port.wide_log2);
297 log_assert(GetSize(port.priority_mask) == GetSize(wr_ports));
298 for (int j = 0; j < GetSize(wr_ports); j++) {
299 auto &wport = wr_ports[j];
300 if (port.priority_mask[j] && !wport.removed) {
301 log_assert(j < i);
302 log_assert(port.clk_enable == wport.clk_enable);
303 if (port.clk_enable) {
304 log_assert(port.clk == wport.clk);
305 log_assert(port.clk_polarity == wport.clk_polarity);
306 }
307 }
308 }
309 }
310 int mask = (1 << max_wide_log2) - 1;
311 log_assert(!(start_offset & mask));
312 log_assert(!(size & mask));
313 }
314
315 namespace {
316
317 struct MemIndex {
318 dict<IdString, pool<Cell *>> rd_ports;
319 dict<IdString, pool<Cell *>> wr_ports;
320 dict<IdString, pool<Cell *>> inits;
321 MemIndex (Module *module) {
322 for (auto cell: module->cells()) {
323 if (cell->type == ID($memwr))
324 wr_ports[cell->parameters.at(ID::MEMID).decode_string()].insert(cell);
325 else if (cell->type == ID($memrd))
326 rd_ports[cell->parameters.at(ID::MEMID).decode_string()].insert(cell);
327 else if (cell->type == ID($meminit))
328 inits[cell->parameters.at(ID::MEMID).decode_string()].insert(cell);
329 }
330 }
331 };
332
333 Mem mem_from_memory(Module *module, RTLIL::Memory *mem, const MemIndex &index) {
334 Mem res(module, mem->name, mem->width, mem->start_offset, mem->size);
335 res.packed = false;
336 res.mem = mem;
337 res.attributes = mem->attributes;
338 if (index.rd_ports.count(mem->name)) {
339 for (auto cell : index.rd_ports.at(mem->name)) {
340 MemRd mrd;
341 mrd.cell = cell;
342 mrd.attributes = cell->attributes;
343 mrd.clk_enable = cell->parameters.at(ID::CLK_ENABLE).as_bool();
344 mrd.clk_polarity = cell->parameters.at(ID::CLK_POLARITY).as_bool();
345 mrd.transparent = cell->parameters.at(ID::TRANSPARENT).as_bool();
346 mrd.clk = cell->getPort(ID::CLK);
347 mrd.en = cell->getPort(ID::EN);
348 mrd.addr = cell->getPort(ID::ADDR);
349 mrd.data = cell->getPort(ID::DATA);
350 mrd.wide_log2 = ceil_log2(GetSize(mrd.data) / mem->width);
351 res.rd_ports.push_back(mrd);
352 }
353 }
354 if (index.wr_ports.count(mem->name)) {
355 std::vector<std::pair<int, MemWr>> ports;
356 for (auto cell : index.wr_ports.at(mem->name)) {
357 MemWr mwr;
358 mwr.cell = cell;
359 mwr.attributes = cell->attributes;
360 mwr.clk_enable = cell->parameters.at(ID::CLK_ENABLE).as_bool();
361 mwr.clk_polarity = cell->parameters.at(ID::CLK_POLARITY).as_bool();
362 mwr.clk = cell->getPort(ID::CLK);
363 mwr.en = cell->getPort(ID::EN);
364 mwr.addr = cell->getPort(ID::ADDR);
365 mwr.data = cell->getPort(ID::DATA);
366 mwr.wide_log2 = ceil_log2(GetSize(mwr.data) / mem->width);
367 ports.push_back(std::make_pair(cell->parameters.at(ID::PRIORITY).as_int(), mwr));
368 }
369 std::sort(ports.begin(), ports.end(), [](const std::pair<int, MemWr> &a, const std::pair<int, MemWr> &b) { return a.first < b.first; });
370 for (auto &it : ports)
371 res.wr_ports.push_back(it.second);
372 }
373 if (index.inits.count(mem->name)) {
374 std::vector<std::pair<int, MemInit>> inits;
375 for (auto cell : index.inits.at(mem->name)) {
376 MemInit init;
377 init.cell = cell;
378 init.attributes = cell->attributes;
379 auto addr = cell->getPort(ID::ADDR);
380 auto data = cell->getPort(ID::DATA);
381 if (!addr.is_fully_const())
382 log_error("Non-constant address %s in memory initialization %s.\n", log_signal(addr), log_id(cell));
383 if (!data.is_fully_const())
384 log_error("Non-constant data %s in memory initialization %s.\n", log_signal(data), log_id(cell));
385 init.addr = addr.as_const();
386 init.data = data.as_const();
387 inits.push_back(std::make_pair(cell->parameters.at(ID::PRIORITY).as_int(), init));
388 }
389 std::sort(inits.begin(), inits.end(), [](const std::pair<int, MemInit> &a, const std::pair<int, MemInit> &b) { return a.first < b.first; });
390 for (auto &it : inits)
391 res.inits.push_back(it.second);
392 }
393 for (int i = 0; i < GetSize(res.wr_ports); i++) {
394 auto &port = res.wr_ports[i];
395 port.priority_mask.resize(GetSize(res.wr_ports));
396 for (int j = 0; j < i; j++) {
397 auto &oport = res.wr_ports[j];
398 if (port.clk_enable != oport.clk_enable)
399 continue;
400 if (port.clk_enable && port.clk != oport.clk)
401 continue;
402 if (port.clk_enable && port.clk_polarity != oport.clk_polarity)
403 continue;
404 port.priority_mask[j] = true;
405 }
406 }
407 res.check();
408 return res;
409 }
410
411 Mem mem_from_cell(Cell *cell) {
412 Mem res(cell->module, cell->parameters.at(ID::MEMID).decode_string(),
413 cell->parameters.at(ID::WIDTH).as_int(),
414 cell->parameters.at(ID::OFFSET).as_int(),
415 cell->parameters.at(ID::SIZE).as_int()
416 );
417 int abits = cell->parameters.at(ID::ABITS).as_int();
418 res.packed = true;
419 res.cell = cell;
420 res.attributes = cell->attributes;
421 Const &init = cell->parameters.at(ID::INIT);
422 if (!init.is_fully_undef()) {
423 int pos = 0;
424 while (pos < res.size) {
425 Const word = init.extract(pos * res.width, res.width, State::Sx);
426 if (word.is_fully_undef()) {
427 pos++;
428 } else {
429 int epos;
430 for (epos = pos; epos < res.size; epos++) {
431 Const eword = init.extract(epos * res.width, res.width, State::Sx);
432 if (eword.is_fully_undef())
433 break;
434 }
435 MemInit minit;
436 minit.addr = res.start_offset + pos;
437 minit.data = init.extract(pos * res.width, (epos - pos) * res.width, State::Sx);
438 res.inits.push_back(minit);
439 pos = epos;
440 }
441 }
442 }
443 for (int i = 0; i < cell->parameters.at(ID::RD_PORTS).as_int(); i++) {
444 MemRd mrd;
445 mrd.wide_log2 = 0;
446 mrd.clk_enable = cell->parameters.at(ID::RD_CLK_ENABLE).extract(i, 1).as_bool();
447 mrd.clk_polarity = cell->parameters.at(ID::RD_CLK_POLARITY).extract(i, 1).as_bool();
448 mrd.transparent = cell->parameters.at(ID::RD_TRANSPARENT).extract(i, 1).as_bool();
449 mrd.clk = cell->getPort(ID::RD_CLK).extract(i, 1);
450 mrd.en = cell->getPort(ID::RD_EN).extract(i, 1);
451 mrd.addr = cell->getPort(ID::RD_ADDR).extract(i * abits, abits);
452 mrd.data = cell->getPort(ID::RD_DATA).extract(i * res.width, res.width);
453 res.rd_ports.push_back(mrd);
454 }
455 for (int i = 0; i < cell->parameters.at(ID::WR_PORTS).as_int(); i++) {
456 MemWr mwr;
457 mwr.wide_log2 = 0;
458 mwr.clk_enable = cell->parameters.at(ID::WR_CLK_ENABLE).extract(i, 1).as_bool();
459 mwr.clk_polarity = cell->parameters.at(ID::WR_CLK_POLARITY).extract(i, 1).as_bool();
460 mwr.clk = cell->getPort(ID::WR_CLK).extract(i, 1);
461 mwr.en = cell->getPort(ID::WR_EN).extract(i * res.width, res.width);
462 mwr.addr = cell->getPort(ID::WR_ADDR).extract(i * abits, abits);
463 mwr.data = cell->getPort(ID::WR_DATA).extract(i * res.width, res.width);
464 res.wr_ports.push_back(mwr);
465 }
466 for (int i = 0; i < GetSize(res.wr_ports); i++) {
467 auto &port = res.wr_ports[i];
468 port.priority_mask.resize(GetSize(res.wr_ports));
469 for (int j = 0; j < i; j++) {
470 auto &oport = res.wr_ports[j];
471 if (port.clk_enable != oport.clk_enable)
472 continue;
473 if (port.clk_enable && port.clk != oport.clk)
474 continue;
475 if (port.clk_enable && port.clk_polarity != oport.clk_polarity)
476 continue;
477 port.priority_mask[j] = true;
478 }
479 }
480 res.check();
481 return res;
482 }
483
484 }
485
486 std::vector<Mem> Mem::get_all_memories(Module *module) {
487 std::vector<Mem> res;
488 MemIndex index(module);
489 for (auto it: module->memories) {
490 res.push_back(mem_from_memory(module, it.second, index));
491 }
492 for (auto cell: module->cells()) {
493 if (cell->type == ID($mem))
494 res.push_back(mem_from_cell(cell));
495 }
496 return res;
497 }
498
499 std::vector<Mem> Mem::get_selected_memories(Module *module) {
500 std::vector<Mem> res;
501 MemIndex index(module);
502 for (auto it: module->memories) {
503 if (module->design->selected(module, it.second))
504 res.push_back(mem_from_memory(module, it.second, index));
505 }
506 for (auto cell: module->selected_cells()) {
507 if (cell->type == ID($mem))
508 res.push_back(mem_from_cell(cell));
509 }
510 return res;
511 }
512
513 Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) {
514 MemRd &port = rd_ports[idx];
515
516 if (!port.clk_enable)
517 return nullptr;
518
519 Cell *c;
520
521 if (port.transparent)
522 {
523 SigSpec sig_q = module->addWire(stringf("%s$rdreg[%d]$q", memid.c_str(), idx), GetSize(port.addr));
524 SigSpec sig_d = port.addr;
525 port.addr = sig_q;
526 c = module->addDffe(stringf("%s$rdreg[%d]", memid.c_str(), idx), port.clk, port.en, sig_d, sig_q, port.clk_polarity, true);
527 }
528 else
529 {
530 SigSpec sig_d = module->addWire(stringf("%s$rdreg[%d]$d", memid.c_str(), idx), GetSize(port.data));
531 SigSpec sig_q = port.data;
532 port.data = sig_d;
533 c = module->addDffe(stringf("%s$rdreg[%d]", memid.c_str(), idx), port.clk, port.en, sig_d, sig_q, port.clk_polarity, true);
534 }
535
536 log("Extracted %s FF from read port %d of %s.%s: %s\n", port.transparent ? "addr" : "data",
537 idx, log_id(module), log_id(memid), log_id(c));
538
539 port.en = State::S1;
540 port.clk = State::S0;
541 port.clk_enable = false;
542 port.clk_polarity = true;
543 port.transparent = false;
544
545 return c;
546 }