json: Add support for memories.
authorMarcelina Kościelnicka <mwk@0x04.net>
Tue, 9 Mar 2021 19:42:14 +0000 (20:42 +0100)
committerMarcelina Kościelnicka <mwk@0x04.net>
Mon, 15 Mar 2021 16:19:19 +0000 (17:19 +0100)
Previously, memories were silently discarded by the JSON backend, making
round-tripping modules with them crash.

Since there are already some users using JSON to implement custom
external passes that use memories (and infer width/size from memory
ports), let's fix this by just making JSON backend and frontend support
memories as first-class objects.

Processes are still not supported, and will now cause a hard error.

Fixes #1908.

backends/json/json.cc
frontends/json/jsonparse.cc

index eeadc1b8903f94950a14d1feddfc211889b26fd5..b13105f64a75230ad0bf7cc26d8fbdca75e65852 100644 (file)
@@ -135,6 +135,10 @@ struct JsonWriter
                // reserve 0 and 1 to avoid confusion with "0" and "1"
                sigidcounter = 2;
 
+               if (module->has_processes()) {
+                       log_error("Module %s contains processes, which are not supported by JSON backend.\n", log_id(module));
+               }
+
                f << stringf("    %s: {\n", get_name(module->name).c_str());
 
                f << stringf("      \"attributes\": {");
@@ -216,6 +220,27 @@ struct JsonWriter
                }
                f << stringf("\n      },\n");
 
+               if (!module->memories.empty()) {
+                       f << stringf("      \"memories\": {");
+                       first = true;
+                       for (auto &it : module->memories) {
+                               if (use_selection && !module->selected(it.second))
+                                       continue;
+                               f << stringf("%s\n", first ? "" : ",");
+                               f << stringf("        %s: {\n", get_name(it.second->name).c_str());
+                               f << stringf("          \"hide_name\": %s,\n", it.second->name[0] == '$' ? "1" : "0");
+                               f << stringf("          \"attributes\": {");
+                               write_parameters(it.second->attributes);
+                               f << stringf("\n          },\n");
+                               f << stringf("          \"width\": %d,\n", it.second->width);
+                               f << stringf("          \"start_offset\": %d,\n", it.second->start_offset);
+                               f << stringf("          \"size\": %d\n", it.second->size);
+                               f << stringf("        }");
+                               first = false;
+                       }
+                       f << stringf("\n      },\n");
+               }
+
                f << stringf("      \"netnames\": {");
                first = true;
                for (auto w : module->wires()) {
@@ -332,6 +357,10 @@ struct JsonBackend : public Backend {
                log("            <cell_name>: <cell_details>,\n");
                log("            ...\n");
                log("          },\n");
+               log("          \"memories\": {\n");
+               log("            <memory_name>: <memory_details>,\n");
+               log("            ...\n");
+               log("          },\n");
                log("          \"netnames\": {\n");
                log("            <net_name>: <net_details>,\n");
                log("            ...\n");
@@ -379,6 +408,19 @@ struct JsonBackend : public Backend {
                log("      },\n");
                log("    }\n");
                log("\n");
+               log("And <memory_details> is:\n");
+               log("\n");
+               log("    {\n");
+               log("      \"hide_name\": <1 | 0>,\n");
+               log("      \"attributes\": {\n");
+               log("        <attribute_name>: <attribute_value>,\n");
+               log("        ...\n");
+               log("      },\n");
+               log("      \"width\": <memory width>\n");
+               log("      \"start_offset\": <the lowest valid memory address>\n");
+               log("      \"size\": <memory size>\n");
+               log("    }\n");
+               log("\n");
                log("And <net_details> is:\n");
                log("\n");
                log("    {\n");
index 312f6d3be505069021b7039258746b5077b2d4dc..d897ac20b22b2ddbeca0db5f621b457041a24bd5 100644 (file)
@@ -539,6 +539,52 @@ void json_import(Design *design, string &modname, JsonNode *node)
                                json_parse_attr_param(cell->parameters, cell_node->data_dict.at("parameters"));
                }
        }
+
+       if (node->data_dict.count("memories"))
+       {
+               JsonNode *memories_node = node->data_dict.at("memories");
+
+               if (memories_node->type != 'D')
+                       log_error("JSON memories node is not a dictionary.\n");
+
+               for (auto &memory_node_it : memories_node->data_dict)
+               {
+                       IdString memory_name = RTLIL::escape_id(memory_node_it.first.c_str());
+                       JsonNode *memory_node = memory_node_it.second;
+
+                       RTLIL::Memory *mem = new RTLIL::Memory;
+                       mem->name = memory_name;
+
+                       if (memory_node->type != 'D')
+                               log_error("JSON memory node '%s' is not a dictionary.\n", log_id(memory_name));
+
+                       if (memory_node->data_dict.count("width") == 0)
+                               log_error("JSON memory node '%s' has no width attribute.\n", log_id(memory_name));
+                       JsonNode *width_node = memory_node->data_dict.at("width");
+                       if (width_node->type != 'N')
+                               log_error("JSON memory node '%s' has a non-number width.\n", log_id(memory_name));
+                       mem->width = width_node->data_number;
+
+                       if (memory_node->data_dict.count("size") == 0)
+                               log_error("JSON memory node '%s' has no size attribute.\n", log_id(memory_name));
+                       JsonNode *size_node = memory_node->data_dict.at("size");
+                       if (size_node->type != 'N')
+                               log_error("JSON memory node '%s' has a non-number size.\n", log_id(memory_name));
+                       mem->size = size_node->data_number;
+
+                       mem->start_offset = 0;
+                       if (memory_node->data_dict.count("start_offset") != 0) {
+                               JsonNode *val = memory_node->data_dict.at("start_offset");
+                               if (val->type == 'N')
+                                       mem->start_offset = val->data_number;
+                       }
+
+                       if (memory_node->data_dict.count("attributes"))
+                               json_parse_attr_param(mem->attributes, memory_node->data_dict.at("attributes"));
+
+                       module->memories[mem->name] = mem;
+               }
+       }
 }
 
 struct JsonFrontend : public Frontend {