diff options
Diffstat (limited to 'passes/memory/memory_bram.cc')
-rw-r--r-- | passes/memory/memory_bram.cc | 126 |
1 files changed, 95 insertions, 31 deletions
diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc index 8f4214027..7b5dd08ab 100644 --- a/passes/memory/memory_bram.cc +++ b/passes/memory/memory_bram.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -32,6 +32,7 @@ struct rules_t SigSpec sig_addr, sig_data, sig_en; bool effective_clkpol; bool make_transp; + bool make_outreg; int mapped_port; }; @@ -85,6 +86,7 @@ struct rules_t pi.clkpol = clkpol[i]; pi.mapped_port = -1; pi.make_transp = false; + pi.make_outreg = false; pi.effective_clkpol = false; portinfos.push_back(pi); } @@ -110,15 +112,15 @@ struct rules_t if (ports[i] != other.ports[i]) log_error("Bram %s variants %d and %d have different number of %c-ports.\n", log_id(name), variant, other.variant, 'A'+i); if (wrmode[i] != other.wrmode[i]) - variant_params[stringf("\\CFG_WRMODE_%c", 'A' + i)] = wrmode[1]; + variant_params[stringf("\\CFG_WRMODE_%c", 'A' + i)] = wrmode[i]; if (enable[i] != other.enable[i]) - variant_params[stringf("\\CFG_ENABLE_%c", 'A' + i)] = enable[1]; + variant_params[stringf("\\CFG_ENABLE_%c", 'A' + i)] = enable[i]; if (transp[i] != other.transp[i]) - variant_params[stringf("\\CFG_TRANSP_%c", 'A' + i)] = transp[1]; + variant_params[stringf("\\CFG_TRANSP_%c", 'A' + i)] = transp[i]; if (clocks[i] != other.clocks[i]) - variant_params[stringf("\\CFG_CLOCKS_%c", 'A' + i)] = clocks[1]; + variant_params[stringf("\\CFG_CLOCKS_%c", 'A' + i)] = clocks[i]; if (clkpol[i] != other.clkpol[i]) - variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[1]; + variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[i]; } } }; @@ -126,7 +128,7 @@ struct rules_t struct match_t { IdString name; dict<string, int> min_limits, max_limits; - bool or_next_if_better, make_transp; + bool or_next_if_better, make_transp, make_outreg; char shuffle_enable; }; @@ -277,6 +279,7 @@ struct rules_t data.name = RTLIL::escape_id(tokens[1]); data.or_next_if_better = false; data.make_transp = false; + data.make_outreg = false; data.shuffle_enable = 0; while (next_line()) @@ -309,6 +312,12 @@ struct rules_t continue; } + if (GetSize(tokens) == 1 && tokens[0] == "make_outreg") { + data.make_transp = true; + data.make_outreg = true; + continue; + } + if (GetSize(tokens) == 1 && tokens[0] == "or_next_if_better") { data.or_next_if_better = true; continue; @@ -320,9 +329,7 @@ struct rules_t void parse(string filename) { - if (filename.substr(0, 2) == "+/") - filename = proc_share_dirname() + filename.substr(1); - + rewrite_filename(filename); infile.open(filename); linecount = 0; @@ -380,9 +387,9 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, if (pi.clkpol > 1) clkpol_wr_ports.insert(pi.clkpol); } - clocks_max = std::max(clocks_max, pi.clocks); - clkpol_max = std::max(clkpol_max, pi.clkpol); - transp_max = std::max(transp_max, pi.transp); + clocks_max = max(clocks_max, pi.clocks); + clkpol_max = max(clkpol_max, pi.clkpol); + transp_max = max(transp_max, pi.transp); } log(" Mapping to bram type %s (variant %d):\n", log_id(bram.name), bram.variant); @@ -393,6 +400,16 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, int mem_width = cell->getParam("\\WIDTH").as_int(); // int mem_offset = cell->getParam("\\OFFSET").as_int(); + bool cell_init = !SigSpec(cell->getParam("\\INIT")).is_fully_undef(); + vector<Const> initdata; + + if (cell_init) { + Const initparam = cell->getParam("\\INIT"); + initdata.reserve(mem_size); + for (int i=0; i < mem_size; i++) + initdata.push_back(initparam.extract(mem_width*i, mem_width, State::Sx)); + } + int wr_ports = cell->getParam("\\WR_PORTS").as_int(); auto wr_clken = SigSpec(cell->getParam("\\WR_CLK_ENABLE")); auto wr_clkpol = SigSpec(cell->getParam("\\WR_CLK_POLARITY")); @@ -412,11 +429,12 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, rd_clkpol.extend_u0(rd_ports); rd_transp.extend_u0(rd_ports); + SigSpec rd_en = cell->getPort("\\RD_EN"); SigSpec rd_clk = cell->getPort("\\RD_CLK"); SigSpec rd_data = cell->getPort("\\RD_DATA"); SigSpec rd_addr = cell->getPort("\\RD_ADDR"); - if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0) + if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0 && wr_ports > 0) { int bucket_size = bram.dbits / portinfos.at(match.shuffle_enable - 'A').enable; log(" Shuffle bit order to accommodate enable buckets of size %d..\n", bucket_size); @@ -588,7 +606,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, mapped_wr_port:; } - // houskeeping stuff for growing more read ports and restarting read port assignments + // housekeeping stuff for growing more read ports and restarting read port assignments int grow_read_ports_cursor = -1; bool try_growing_more_read_ports = false; @@ -656,6 +674,10 @@ grow_read_ports:; if (clken) { if (pi.clocks == 0) { + if (match.make_outreg) { + pi.make_outreg = true; + goto skip_bram_rport_clkcheck; + } log(" Bram port %c%d.%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); goto skip_bram_rport; } @@ -667,12 +689,17 @@ grow_read_ports:; log(" Bram port %c%d.%d has incompatible clock polarity.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); goto skip_bram_rport; } + if (rd_en[cell_port_i] != State::S1 && pi.enable == 0) { + log(" Bram port %c%d.%d has no read enable input.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); + goto skip_bram_rport; + } + skip_bram_rport_clkcheck: if (read_transp.count(pi.transp) && read_transp.at(pi.transp) != transp) { if (match.make_transp && wr_ports <= 1) { pi.make_transp = true; enable_make_transp = true; } else { - log(" Bram port %c%d.%d has incompatible read transparancy.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); + log(" Bram port %c%d.%d has incompatible read transparency.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); goto skip_bram_rport; } } @@ -691,6 +718,7 @@ grow_read_ports:; clock_polarities[pi.clkpol] = clkdom.second; read_transp[pi.transp] = transp; pi.sig_clock = clkdom.first; + pi.sig_en = rd_en[cell_port_i]; pi.effective_clkpol = clkdom.second; } @@ -789,6 +817,22 @@ grow_read_ports:; for (auto &vp : variant_params) c->setParam(vp.first, vp.second); + if (cell_init) { + int init_offset = grid_a*(1 << bram.abits); + int init_shift = grid_d*bram.dbits; + int init_size = (1 << bram.abits); + Const initparam(State::Sx, init_size*bram.dbits); + for (int i = 0; i < init_size; i++) { + State padding = State::Sx; + for (int j = 0; j < bram.dbits; j++) + if (init_offset+i < GetSize(initdata) && init_shift+j < GetSize(initdata[init_offset+i])) + initparam[i*bram.dbits+j] = initdata[init_offset+i][init_shift+j]; + else + initparam[i*bram.dbits+j] = padding; + } + c->setParam("\\INIT", initparam); + } + for (auto &pi : portinfos) { if (pi.dupidx != dupidx) @@ -815,8 +859,11 @@ grow_read_ports:; if (pi.enable) { SigSpec sig_en = pi.sig_en; - sig_en.extend_u0((grid_d+1) * pi.enable); - sig_en = sig_en.extract(grid_d * pi.enable, pi.enable); + + if (pi.wrmode == 1) { + sig_en.extend_u0((grid_d+1) * pi.enable); + sig_en = sig_en.extract(grid_d * pi.enable, pi.enable); + } if (!addr_ok.empty()) sig_en = module->Mux(NEW_ID, SigSpec(0, GetSize(sig_en)), sig_en, addr_ok); @@ -846,6 +893,14 @@ grow_read_ports:; SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits); c->setPort(stringf("\\%sDATA", pf), bram_dout); + if (pi.make_outreg) { + SigSpec bram_dout_q = module->addWire(NEW_ID, bram.dbits); + if (!pi.sig_en.empty()) + bram_dout = module->Mux(NEW_ID, bram_dout_q, bram_dout, pi.sig_en); + module->addDff(NEW_ID, pi.sig_clock, bram_dout, bram_dout_q, pi.effective_clkpol); + bram_dout = bram_dout_q; + } + if (pi.make_transp) { log(" Adding extra logic for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); @@ -871,7 +926,7 @@ grow_read_ports:; } SigSpec addr_ok_q = addr_ok; - if (pi.clocks && !addr_ok.empty()) { + if ((pi.clocks || pi.make_outreg) && !addr_ok.empty()) { addr_ok_q = module->addWire(NEW_ID); module->addDff(NEW_ID, pi.sig_clock, addr_ok, addr_ok_q, pi.effective_clkpol); } @@ -905,6 +960,8 @@ void handle_cell(Cell *cell, const rules_t &rules) { log("Processing %s.%s:\n", log_id(cell->module), log_id(cell)); + bool cell_init = !SigSpec(cell->getParam("\\INIT")).is_fully_undef(); + dict<string, int> match_properties; match_properties["words"] = cell->getParam("\\SIZE").as_int(); match_properties["abits"] = cell->getParam("\\ABITS").as_int(); @@ -920,7 +977,7 @@ void handle_cell(Cell *cell, const rules_t &rules) log("\n"); pool<pair<IdString, int>> failed_brams; - dict<pair<int, int>, std::tuple<int, int, int>> best_rule_cache; + dict<pair<int, int>, tuple<int, int, int>> best_rule_cache; for (int i = 0; i < GetSize(rules.matches); i++) { @@ -975,6 +1032,12 @@ void handle_cell(Cell *cell, const rules_t &rules) log(" Metrics for %s: awaste=%d dwaste=%d bwaste=%d waste=%d efficiency=%d\n", log_id(match.name), awaste, dwaste, bwaste, waste, efficiency); + if (cell_init && bram.init == 0) { + log(" Rule #%d for bram type %s (variant %d) rejected: cannot be initialized.\n", + i+1, log_id(bram.name), bram.variant); + goto next_match_rule; + } + for (auto it : match.min_limits) { if (it.first == "waste" || it.first == "dups" || it.first == "acells" || it.first == "dcells" || it.first == "cells") continue; @@ -987,6 +1050,7 @@ void handle_cell(Cell *cell, const rules_t &rules) i+1, log_id(bram.name), bram.variant, it.first.c_str(), it.second); goto next_match_rule; } + for (auto it : match.max_limits) { if (it.first == "acells" || it.first == "dcells" || it.first == "cells") continue; @@ -1014,10 +1078,7 @@ void handle_cell(Cell *cell, const rules_t &rules) } log(" Storing for later selection.\n"); - best_rule_cache[pair<int, int>(i, vi)] = std::tuple<int, int, int>(match_properties["efficiency"], -match_properties["cells"], -match_properties["acells"]); - - if (or_next_if_better) - goto next_match_rule; + best_rule_cache[pair<int, int>(i, vi)] = tuple<int, int, int>(match_properties["efficiency"], -match_properties["cells"], -match_properties["acells"]); next_match_rule: if (or_next_if_better || best_rule_cache.empty()) @@ -1070,14 +1131,14 @@ struct MemoryBramPass : public Pass { log("rules. A block ram description looks like this:\n"); log("\n"); log(" bram RAMB1024X32 # name of BRAM cell\n"); - // log(" init 1 # set to '1' if BRAM can be initialized\n"); + log(" init 1 # set to '1' if BRAM can be initialized\n"); log(" abits 10 # number of address bits\n"); log(" dbits 32 # number of data bits\n"); log(" groups 2 # number of port groups\n"); log(" ports 1 1 # number of ports in each group\n"); log(" wrmode 1 0 # set to '1' if this groups is write ports\n"); - log(" enable 4 0 # number of enable bits (for write ports)\n"); - log(" transp 0 2 # transparatent (for read ports)\n"); + log(" enable 4 1 # number of enable bits\n"); + log(" transp 0 2 # transparent (for read ports)\n"); log(" clocks 1 2 # clock configuration\n"); log(" clkpol 2 2 # clock polarity configuration\n"); log(" endbram\n"); @@ -1095,7 +1156,7 @@ struct MemoryBramPass : public Pass { log("greater than 1 share the same configuration bit.\n"); log("\n"); log("Using the same bram name in different bram blocks will create different variants\n"); - log("of the bram. Verilog configration parameters for the bram are created as needed.\n"); + log("of the bram. Verilog configuration parameters for the bram are created as needed.\n"); log("\n"); log("It is also possible to create variants by repeating statements in the bram block\n"); log("and appending '@<label>' to the individual statements.\n"); @@ -1128,7 +1189,7 @@ struct MemoryBramPass : public Pass { log(" dcells ....... number of cells in 'data-direction'\n"); log(" cells ........ total number of cells (acells*dcells*dups)\n"); log("\n"); - log("The interface for the created bram instances is dervived from the bram\n"); + log("The interface for the created bram instances is derived from the bram\n"); log("description. Use 'techmap' to convert the created bram instances into\n"); log("instances of the actual bram cells of your target architecture.\n"); log("\n"); @@ -1139,6 +1200,9 @@ struct MemoryBramPass : public Pass { log("A match containing the command 'make_transp' will add external circuitry\n"); log("to simulate 'transparent read', if necessary.\n"); log("\n"); + log("A match containing the command 'make_outreg' will add external flip-flops\n"); + log("to implement synchronous read ports, if necessary.\n"); + log("\n"); log("A match containing the command 'shuffle_enable A' will re-organize\n"); log("the data bits to accommodate the enable pattern of port A.\n"); log("\n"); @@ -1147,7 +1211,7 @@ struct MemoryBramPass : public Pass { { rules_t rules; - log_header("Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n"); + log_header(design, "Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { |