From 60f047f13606554cf800603580fd464d5764a174 Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 1 Jan 2020 07:04:37 +0000 Subject: memory_bram: add `attr_icase` option. Some vendor toolchains use case insensitive matching for values of attributes that control BRAM inference. --- passes/memory/memory_bram.cc | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) (limited to 'passes') diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc index 24478f2ee..e7394ac29 100644 --- a/passes/memory/memory_bram.cc +++ b/passes/memory/memory_bram.cc @@ -137,9 +137,26 @@ struct rules_t vector>> attributes; }; + bool attr_icase; dict> brams; vector matches; + std::string map_case(std::string value) const + { + if (attr_icase) { + for (char &c : value) + c = tolower(c); + } + return value; + } + + RTLIL::Const map_case(RTLIL::Const value) const + { + if (value.flags & RTLIL::CONST_FLAG_STRING) + return map_case(value.decode_string()); + return value; + } + std::ifstream infile; vector tokens; vector labels; @@ -337,7 +354,7 @@ struct rules_t IdString key = RTLIL::escape_id(tokens[idx].substr(c1, c2)); Const val = c2 != std::string::npos ? tokens[idx].substr(c2+1) : RTLIL::Const(1); - data.attributes.back().emplace_back(exists, key, val); + data.attributes.back().emplace_back(exists, key, map_case(val)); } continue; } @@ -351,6 +368,7 @@ struct rules_t rewrite_filename(filename); infile.open(filename); linecount = 0; + attr_icase = false; if (infile.fail()) log_error("Can't open rules file `%s'.\n", filename.c_str()); @@ -360,6 +378,11 @@ struct rules_t if (!labels.empty()) syntax_error(); + if (GetSize(tokens) == 2 && tokens[0] == "attr_icase") { + attr_icase = atoi(tokens[1].c_str()); + continue; + } + if (tokens[0] == "bram") { parse_bram(); continue; @@ -843,7 +866,7 @@ grow_read_ports:; } else if (!exists) continue; - if (it->second != value) + if (rules.map_case(it->second) != value) continue; found = true; break; @@ -855,7 +878,7 @@ grow_read_ports:; ss << "!"; IdString key = std::get<1>(sums.front()); ss << log_id(key); - const Const &value = std::get<2>(sums.front()); + const Const &value = rules.map_case(std::get<2>(sums.front())); if (exists && value != Const(1)) ss << "=\"" << value.decode_string() << "\""; @@ -1167,7 +1190,7 @@ void handle_cell(Cell *cell, const rules_t &rules) } else if (!exists) continue; - if (it->second != value) + if (rules.map_case(it->second) != value) continue; found = true; break; @@ -1179,7 +1202,7 @@ void handle_cell(Cell *cell, const rules_t &rules) ss << "!"; IdString key = std::get<1>(sums.front()); ss << log_id(key); - const Const &value = std::get<2>(sums.front()); + const Const &value = rules.map_case(std::get<2>(sums.front())); if (exists && value != Const(1)) ss << "=\"" << value.decode_string() << "\""; @@ -1252,8 +1275,13 @@ struct MemoryBramPass : public Pass { log("The given rules file describes the available resources and how they should be\n"); log("used.\n"); log("\n"); - log("The rules file contains a set of block ram description and a sequence of match\n"); - log("rules. A block ram description looks like this:\n"); + log("The rules file contains configuration options, a set of block ram description\n"); + log("and a sequence of match rules.\n"); + log("\n"); + log("The option 'attr_icase' configures how attribute values are matched. The value 0\n"); + log("means case-sensitive, 1 means case-insensitive.\n"); + log("\n"); + log("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"); -- cgit v1.2.3 From e0def9e4d93bc99a65c3c918b53fee17fbe3a2ba Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 1 Jan 2020 09:42:33 +0000 Subject: memory_map: add -attr option, to respect inference attributes. Before this commit, memory_map (which is always a part of a synth script) would always pick up any $mem cell that was not processed by a preceding pass and lower it down to $dff/$mux cells. This is undesirable for two reasons: * If there is an explicit inference attribute set on a $mem cell, e.g. (* ram_block *), then it is arguably incorrect to map such a memory to $dff/$mux cells. * If memory_map tries to lower a memory that was intended to be mapped to a large BRAM, it often takes extraordinarily long time to finish, produces an extremely large log file, and outputs an unusable design. After this commit, properly invoked memory_map will not map any memory that has an explicit inference attribute specified, solving the first issue, and alleviating the second. The default behavior is not changed. --- passes/memory/memory_map.cc | 119 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 113 insertions(+), 6 deletions(-) (limited to 'passes') diff --git a/passes/memory/memory_map.cc b/passes/memory/memory_map.cc index 65bccb5ef..8820d6d72 100644 --- a/passes/memory/memory_map.cc +++ b/passes/memory/memory_map.cc @@ -28,11 +28,32 @@ PRIVATE_NAMESPACE_BEGIN struct MemoryMapWorker { + bool attr_icase = false; + dict> attributes; + RTLIL::Design *design; RTLIL::Module *module; std::map, RTLIL::SigBit> decoder_cache; + MemoryMapWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module) {} + + std::string map_case(std::string value) const + { + if (attr_icase) { + for (char &c : value) + c = tolower(c); + } + return value; + } + + RTLIL::Const map_case(RTLIL::Const value) const + { + if (value.flags & RTLIL::CONST_FLAG_STRING) + return map_case(value.decode_string()); + return value; + } + 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 = "") { std::stringstream sstr; @@ -98,6 +119,36 @@ struct MemoryMapWorker return; } + // check if attributes allow us to infer FFRAM for this cell + for (const auto &attr : attributes) { + if (cell->attributes.count(attr.first)) { + const auto &cell_attr = cell->attributes[attr.first]; + if (attr.second.empty()) { + log("Not mapping memory cell %s in module %s (attribute %s is set).\n", + cell->name.c_str(), module->name.c_str(), attr.first.c_str()); + return; + } + + bool found = false; + for (auto &value : attr.second) { + if (map_case(cell_attr) == map_case(value)) { + found = true; + break; + } + } + if (!found) { + if (cell_attr.flags & RTLIL::CONST_FLAG_STRING) { + log("Not mapping memory cell %s in module %s (attribute %s is set to \"%s\").\n", + cell->name.c_str(), module->name.c_str(), attr.first.c_str(), cell_attr.decode_string().c_str()); + } else { + log("Not mapping memory cell %s in module %s (attribute %s is set to %d).\n", + cell->name.c_str(), module->name.c_str(), attr.first.c_str(), cell_attr.as_int()); + } + return; + } + } + } + // all write ports must share the same clock RTLIL::SigSpec clocks = cell->getPort("\\WR_CLK"); RTLIL::Const clocks_pol = cell->parameters["\\WR_CLK_POLARITY"]; @@ -339,7 +390,7 @@ struct MemoryMapWorker module->remove(cell); } - MemoryMapWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module) + void run() { std::vector cells; for (auto cell : module->selected_cells()) @@ -356,17 +407,73 @@ struct MemoryMapPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" memory_map [selection]\n"); + log(" memory_map [options] [selection]\n"); log("\n"); log("This pass converts multiport memory cells as generated by the memory_collect\n"); log("pass to word-wide DFFs and address decoders.\n"); log("\n"); + log(" -attr !\n"); + log(" do not map memories that have attribute set.\n"); + log("\n"); + log(" -attr [=]\n"); + log(" for memories that have attribute set, only map them if its value\n"); + log(" is a string (if specified), or an integer 1 (otherwise). if this\n"); + log(" option is specified multiple times, map the memory if the attribute is\n"); + log(" to any of the values.\n"); + log("\n"); + log(" -iattr\n"); + log(" for -attr, ignore case of .\n"); + log("\n"); } - void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + bool attr_icase = false; + dict> attributes; + log_header(design, "Executing MEMORY_MAP pass (converting $mem cells to logic and flip-flops).\n"); - extra_args(args, 1, design); - for (auto mod : design->selected_modules()) - MemoryMapWorker(design, mod); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-attr" && argidx + 1 < args.size()) + { + std::string attr_arg = args[++argidx]; + std::string name; + RTLIL::Const value; + size_t eq_at = attr_arg.find('='); + if (eq_at != std::string::npos) { + name = attr_arg.substr(0, eq_at); + value = attr_arg.substr(eq_at + 1); + } else { + name = attr_arg; + value = RTLIL::Const(1); + } + if (attr_arg.size() > 1 && attr_arg[0] == '!') { + if (value != RTLIL::Const(1)) { + --argidx; + break; // we don't support -attr != + } + attributes[RTLIL::escape_id(name.substr(1))].clear(); + } else { + attributes[RTLIL::escape_id(name)].push_back(value); + } + continue; + } + if (args[argidx] == "-iattr") + { + attr_icase = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto mod : design->selected_modules()) { + MemoryMapWorker worker(design, mod); + worker.attr_icase = attr_icase; + worker.attributes = attributes; + worker.run(); + } } } MemoryMapPass; -- cgit v1.2.3