memory_map: add -attr option, to respect inference attributes.
authorwhitequark <whitequark@whitequark.org>
Wed, 1 Jan 2020 09:42:33 +0000 (09:42 +0000)
committerwhitequark <whitequark@whitequark.org>
Fri, 3 Apr 2020 05:51:40 +0000 (05:51 +0000)
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

index 65bccb5ef9907965c51f4a7a6e4851b7d7c1251b..8820d6d72dfbd38ca7d6b17f6af169815fd7f53c 100644 (file)
@@ -28,11 +28,32 @@ PRIVATE_NAMESPACE_BEGIN
 
 struct MemoryMapWorker
 {
+       bool attr_icase = false;
+       dict<RTLIL::IdString, std::vector<RTLIL::Const>> attributes;
+
        RTLIL::Design *design;
        RTLIL::Module *module;
 
        std::map<std::pair<RTLIL::SigSpec, RTLIL::SigSpec>, 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<RTLIL::Cell*> 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 !<name>\n");
+               log("        do not map memories that have attribute <name> set.\n");
+               log("\n");
+               log("    -attr <name>[=<value>]\n");
+               log("        for memories that have attribute <name> set, only map them if its value\n");
+               log("        is a string <value> (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 <value>.\n");
+               log("\n");
        }
-       void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE {
+       void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+       {
+               bool attr_icase = false;
+               dict<RTLIL::IdString, std::vector<RTLIL::Const>> 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 !<name>=<value>
+                                       }
+                                       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;