Merge pull request #1085 from YosysHQ/eddie/shregmap_improve
[yosys.git] / passes / memory / memory_map.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
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/register.h"
21 #include "kernel/log.h"
22 #include <sstream>
23 #include <set>
24 #include <stdlib.h>
25
26 USING_YOSYS_NAMESPACE
27 PRIVATE_NAMESPACE_BEGIN
28
29 struct MemoryMapWorker
30 {
31 RTLIL::Design *design;
32 RTLIL::Module *module;
33
34 std::map<std::pair<RTLIL::SigSpec, RTLIL::SigSpec>, RTLIL::SigBit> decoder_cache;
35
36 std::string genid(RTLIL::IdString name, std::string token1 = "", int i = -1, std::string token2 = "", int j = -1, std::string token3 = "", int k = -1, std::string token4 = "")
37 {
38 std::stringstream sstr;
39 sstr << "$memory" << name.str() << token1;
40
41 if (i >= 0)
42 sstr << "[" << i << "]";
43
44 sstr << token2;
45
46 if (j >= 0)
47 sstr << "[" << j << "]";
48
49 sstr << token3;
50
51 if (k >= 0)
52 sstr << "[" << k << "]";
53
54 sstr << token4 << "$" << (autoidx++);
55 return sstr.str();
56 }
57
58 RTLIL::Wire *addr_decode(RTLIL::SigSpec addr_sig, RTLIL::SigSpec addr_val)
59 {
60 std::pair<RTLIL::SigSpec, RTLIL::SigSpec> key(addr_sig, addr_val);
61 log_assert(GetSize(addr_sig) == GetSize(addr_val));
62
63 if (decoder_cache.count(key) == 0) {
64 if (GetSize(addr_sig) < 2) {
65 decoder_cache[key] = module->Eq(NEW_ID, addr_sig, addr_val);
66 } else {
67 int split_at = GetSize(addr_sig) / 2;
68 RTLIL::SigBit left_eq = addr_decode(addr_sig.extract(0, split_at), addr_val.extract(0, split_at));
69 RTLIL::SigBit right_eq = addr_decode(addr_sig.extract(split_at, GetSize(addr_sig) - split_at), addr_val.extract(split_at, GetSize(addr_val) - split_at));
70 decoder_cache[key] = module->And(NEW_ID, left_eq, right_eq);
71 }
72 }
73
74 RTLIL::SigBit bit = decoder_cache.at(key);
75 log_assert(bit.wire != nullptr && GetSize(bit.wire) == 1);
76 return bit.wire;
77 }
78
79 void handle_cell(RTLIL::Cell *cell)
80 {
81 std::set<int> static_ports;
82 std::map<int, RTLIL::SigSpec> static_cells_map;
83
84 int wr_ports = cell->parameters["\\WR_PORTS"].as_int();
85 int rd_ports = cell->parameters["\\RD_PORTS"].as_int();
86
87 int mem_size = cell->parameters["\\SIZE"].as_int();
88 int mem_width = cell->parameters["\\WIDTH"].as_int();
89 int mem_offset = cell->parameters["\\OFFSET"].as_int();
90 int mem_abits = cell->parameters["\\ABITS"].as_int();
91
92 SigSpec init_data = cell->getParam("\\INIT");
93 init_data.extend_u0(mem_size*mem_width, true);
94
95 // delete unused memory cell
96 if (wr_ports == 0 && rd_ports == 0) {
97 module->remove(cell);
98 return;
99 }
100
101 // all write ports must share the same clock
102 RTLIL::SigSpec clocks = cell->getPort("\\WR_CLK");
103 RTLIL::Const clocks_pol = cell->parameters["\\WR_CLK_POLARITY"];
104 RTLIL::Const clocks_en = cell->parameters["\\WR_CLK_ENABLE"];
105 clocks_pol.bits.resize(wr_ports);
106 clocks_en.bits.resize(wr_ports);
107 RTLIL::SigSpec refclock;
108 RTLIL::State refclock_pol = RTLIL::State::Sx;
109 for (int i = 0; i < clocks.size(); i++) {
110 RTLIL::SigSpec wr_en = cell->getPort("\\WR_EN").extract(i * mem_width, mem_width);
111 if (wr_en.is_fully_const() && !wr_en.as_bool()) {
112 static_ports.insert(i);
113 continue;
114 }
115 if (clocks_en.bits[i] != RTLIL::State::S1) {
116 RTLIL::SigSpec wr_addr = cell->getPort("\\WR_ADDR").extract(i*mem_abits, mem_abits);
117 RTLIL::SigSpec wr_data = cell->getPort("\\WR_DATA").extract(i*mem_width, mem_width);
118 if (wr_addr.is_fully_const()) {
119 // FIXME: Actually we should check for wr_en.is_fully_const() also and
120 // create a $adff cell with this ports wr_en input as reset pin when wr_en
121 // is not a simple static 1.
122 static_cells_map[wr_addr.as_int() - mem_offset] = wr_data;
123 static_ports.insert(i);
124 continue;
125 }
126 log("Not mapping memory cell %s in module %s (write port %d has no clock).\n",
127 cell->name.c_str(), module->name.c_str(), i);
128 return;
129 }
130 if (refclock.size() == 0) {
131 refclock = clocks.extract(i, 1);
132 refclock_pol = clocks_pol.bits[i];
133 }
134 if (clocks.extract(i, 1) != refclock || clocks_pol.bits[i] != refclock_pol) {
135 log("Not mapping memory cell %s in module %s (write clock %d is incompatible with other clocks).\n",
136 cell->name.c_str(), module->name.c_str(), i);
137 return;
138 }
139 }
140
141 log("Mapping memory cell %s in module %s:\n", cell->name.c_str(), module->name.c_str());
142
143 std::vector<RTLIL::SigSpec> data_reg_in;
144 std::vector<RTLIL::SigSpec> data_reg_out;
145
146 int count_static = 0;
147
148 for (int i = 0; i < mem_size; i++)
149 {
150 if (static_cells_map.count(i) > 0)
151 {
152 data_reg_in.push_back(RTLIL::SigSpec(RTLIL::State::Sz, mem_width));
153 data_reg_out.push_back(static_cells_map[i]);
154 count_static++;
155 }
156 else
157 {
158 RTLIL::Cell *c = module->addCell(genid(cell->name, "", i), "$dff");
159 c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"];
160 if (clocks_pol.bits.size() > 0) {
161 c->parameters["\\CLK_POLARITY"] = RTLIL::Const(clocks_pol.bits[0]);
162 c->setPort("\\CLK", clocks.extract(0, 1));
163 } else {
164 c->parameters["\\CLK_POLARITY"] = RTLIL::Const(RTLIL::State::S1);
165 c->setPort("\\CLK", RTLIL::SigSpec(RTLIL::State::S0));
166 }
167
168 RTLIL::Wire *w_in = module->addWire(genid(cell->name, "", i, "$d"), mem_width);
169 data_reg_in.push_back(RTLIL::SigSpec(w_in));
170 c->setPort("\\D", data_reg_in.back());
171
172 std::string w_out_name = stringf("%s[%d]", cell->parameters["\\MEMID"].decode_string().c_str(), i);
173 if (module->wires_.count(w_out_name) > 0)
174 w_out_name = genid(cell->name, "", i, "$q");
175
176 RTLIL::Wire *w_out = module->addWire(w_out_name, mem_width);
177 SigSpec w_init = init_data.extract(i*mem_width, mem_width);
178
179 if (!w_init.is_fully_undef())
180 w_out->attributes["\\init"] = w_init.as_const();
181
182 data_reg_out.push_back(RTLIL::SigSpec(w_out));
183 c->setPort("\\Q", data_reg_out.back());
184 }
185 }
186
187 log(" created %d $dff cells and %d static cells of width %d.\n", mem_size-count_static, count_static, mem_width);
188
189 int count_dff = 0, count_mux = 0, count_wrmux = 0;
190
191 for (int i = 0; i < cell->parameters["\\RD_PORTS"].as_int(); i++)
192 {
193 RTLIL::SigSpec rd_addr = cell->getPort("\\RD_ADDR").extract(i*mem_abits, mem_abits);
194
195 if (mem_offset)
196 rd_addr = module->Sub(NEW_ID, rd_addr, SigSpec(mem_offset, GetSize(rd_addr)));
197
198 std::vector<RTLIL::SigSpec> rd_signals;
199 rd_signals.push_back(cell->getPort("\\RD_DATA").extract(i*mem_width, mem_width));
200
201 if (cell->parameters["\\RD_CLK_ENABLE"].bits[i] == RTLIL::State::S1)
202 {
203 RTLIL::Cell *dff_cell = nullptr;
204
205 if (cell->parameters["\\RD_TRANSPARENT"].bits[i] == RTLIL::State::S1)
206 {
207 dff_cell = module->addCell(genid(cell->name, "$rdreg", i), "$dff");
208 dff_cell->parameters["\\WIDTH"] = RTLIL::Const(mem_abits);
209 dff_cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]);
210 dff_cell->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1));
211 dff_cell->setPort("\\D", rd_addr);
212 count_dff++;
213
214 RTLIL::Wire *w = module->addWire(genid(cell->name, "$rdreg", i, "$q"), mem_abits);
215
216 dff_cell->setPort("\\Q", RTLIL::SigSpec(w));
217 rd_addr = RTLIL::SigSpec(w);
218 }
219 else
220 {
221 dff_cell = module->addCell(genid(cell->name, "$rdreg", i), "$dff");
222 dff_cell->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"];
223 dff_cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]);
224 dff_cell->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1));
225 dff_cell->setPort("\\Q", rd_signals.back());
226 count_dff++;
227
228 RTLIL::Wire *w = module->addWire(genid(cell->name, "$rdreg", i, "$d"), mem_width);
229
230 rd_signals.clear();
231 rd_signals.push_back(RTLIL::SigSpec(w));
232 dff_cell->setPort("\\D", rd_signals.back());
233 }
234
235 SigBit en_bit = cell->getPort("\\RD_EN").extract(i);
236 if (en_bit != State::S1) {
237 SigSpec new_d = module->Mux(genid(cell->name, "$rdenmux", i),
238 dff_cell->getPort("\\Q"), dff_cell->getPort("\\D"), en_bit);
239 dff_cell->setPort("\\D", new_d);
240 }
241 }
242
243 for (int j = 0; j < mem_abits; j++)
244 {
245 std::vector<RTLIL::SigSpec> next_rd_signals;
246
247 for (size_t k = 0; k < rd_signals.size(); k++)
248 {
249 RTLIL::Cell *c = module->addCell(genid(cell->name, "$rdmux", i, "", j, "", k), "$mux");
250 c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"];
251 c->setPort("\\Y", rd_signals[k]);
252 c->setPort("\\S", rd_addr.extract(mem_abits-j-1, 1));
253 count_mux++;
254
255 c->setPort("\\A", module->addWire(genid(cell->name, "$rdmux", i, "", j, "", k, "$a"), mem_width));
256 c->setPort("\\B", module->addWire(genid(cell->name, "$rdmux", i, "", j, "", k, "$b"), mem_width));
257
258 next_rd_signals.push_back(c->getPort("\\A"));
259 next_rd_signals.push_back(c->getPort("\\B"));
260 }
261
262 next_rd_signals.swap(rd_signals);
263 }
264
265 for (int j = 0; j < mem_size; j++)
266 module->connect(RTLIL::SigSig(rd_signals[j], data_reg_out[j]));
267 }
268
269 log(" read interface: %d $dff and %d $mux cells.\n", count_dff, count_mux);
270
271 for (int i = 0; i < mem_size; i++)
272 {
273 if (static_cells_map.count(i) > 0)
274 continue;
275
276 RTLIL::SigSpec sig = data_reg_out[i];
277
278 for (int j = 0; j < cell->parameters["\\WR_PORTS"].as_int(); j++)
279 {
280 RTLIL::SigSpec wr_addr = cell->getPort("\\WR_ADDR").extract(j*mem_abits, mem_abits);
281 RTLIL::SigSpec wr_data = cell->getPort("\\WR_DATA").extract(j*mem_width, mem_width);
282 RTLIL::SigSpec wr_en = cell->getPort("\\WR_EN").extract(j*mem_width, mem_width);
283
284 if (mem_offset)
285 wr_addr = module->Sub(NEW_ID, wr_addr, SigSpec(mem_offset, GetSize(wr_addr)));
286
287 RTLIL::Wire *w_seladdr = addr_decode(wr_addr, RTLIL::SigSpec(i, mem_abits));
288
289 int wr_offset = 0;
290 while (wr_offset < wr_en.size())
291 {
292 int wr_width = 1;
293 RTLIL::SigSpec wr_bit = wr_en.extract(wr_offset, 1);
294
295 while (wr_offset + wr_width < wr_en.size()) {
296 RTLIL::SigSpec next_wr_bit = wr_en.extract(wr_offset + wr_width, 1);
297 if (next_wr_bit != wr_bit)
298 break;
299 wr_width++;
300 }
301
302 RTLIL::Wire *w = w_seladdr;
303
304 if (wr_bit != RTLIL::SigSpec(1, 1))
305 {
306 RTLIL::Cell *c = module->addCell(genid(cell->name, "$wren", i, "", j, "", wr_offset), "$and");
307 c->parameters["\\A_SIGNED"] = RTLIL::Const(0);
308 c->parameters["\\B_SIGNED"] = RTLIL::Const(0);
309 c->parameters["\\A_WIDTH"] = RTLIL::Const(1);
310 c->parameters["\\B_WIDTH"] = RTLIL::Const(1);
311 c->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
312 c->setPort("\\A", w);
313 c->setPort("\\B", wr_bit);
314
315 w = module->addWire(genid(cell->name, "$wren", i, "", j, "", wr_offset, "$y"));
316 c->setPort("\\Y", RTLIL::SigSpec(w));
317 }
318
319 RTLIL::Cell *c = module->addCell(genid(cell->name, "$wrmux", i, "", j, "", wr_offset), "$mux");
320 c->parameters["\\WIDTH"] = wr_width;
321 c->setPort("\\A", sig.extract(wr_offset, wr_width));
322 c->setPort("\\B", wr_data.extract(wr_offset, wr_width));
323 c->setPort("\\S", RTLIL::SigSpec(w));
324
325 w = module->addWire(genid(cell->name, "$wrmux", i, "", j, "", wr_offset, "$y"), wr_width);
326 c->setPort("\\Y", w);
327
328 sig.replace(wr_offset, w);
329 wr_offset += wr_width;
330 count_wrmux++;
331 }
332 }
333
334 module->connect(RTLIL::SigSig(data_reg_in[i], sig));
335 }
336
337 log(" write interface: %d write mux blocks.\n", count_wrmux);
338
339 module->remove(cell);
340 }
341
342 MemoryMapWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module)
343 {
344 std::vector<RTLIL::Cell*> cells;
345 for (auto cell : module->selected_cells())
346 if (cell->type == "$mem" && design->selected(module, cell))
347 cells.push_back(cell);
348 for (auto cell : cells)
349 handle_cell(cell);
350 }
351 };
352
353 struct MemoryMapPass : public Pass {
354 MemoryMapPass() : Pass("memory_map", "translate multiport memories to basic cells") { }
355 void help() YS_OVERRIDE
356 {
357 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
358 log("\n");
359 log(" memory_map [selection]\n");
360 log("\n");
361 log("This pass converts multiport memory cells as generated by the memory_collect\n");
362 log("pass to word-wide DFFs and address decoders.\n");
363 log("\n");
364 }
365 void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE {
366 log_header(design, "Executing MEMORY_MAP pass (converting $mem cells to logic and flip-flops).\n");
367 extra_args(args, 1, design);
368 for (auto mod : design->selected_modules())
369 MemoryMapWorker(design, mod);
370 }
371 } MemoryMapPass;
372
373 PRIVATE_NAMESPACE_END