From fa7317f5940841e7ff56fed748c4f1f4117baae4 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 11 Nov 2019 16:55:52 +0000 Subject: frontend: Draft for a generic template-based frontend API Signed-off-by: David Shah --- frontend/generic.h | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 frontend/generic.h diff --git a/frontend/generic.h b/frontend/generic.h new file mode 100644 index 00000000..35428052 --- /dev/null +++ b/frontend/generic.h @@ -0,0 +1,102 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2019 David Shah + * + * 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 + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +/* + * Generic Frontend Framework + * + * This is designed to make it possible to build frontends for parsing any format isomorphic to Yosys JSON [1] + * with maximal inlining and minimal need for overhead such as runtime polymorphism or extra wrapper types. + * + * [1] http://www.clifford.at/yosys/cmd_write_json.html + * + * The frontend should implement a class referred to as FrontendType that defines the following type(def)s and + * functions: + * + * Types: + * ModuleDataType: corresponds to a single entry in "modules" + * ModulePortDataType: corresponds to a single entry in "ports" of a module + * CellDataType: corresponds to a single entry in "cells" + * NetnameDataType: corresponds to a single entry in "netnames" + * BitVectorDataType: corresponds to a signal/constant bit vector (e.g. a "connections" field) + * + * Functions: + * + * void foreach_module(Func); + * calls Func(const std::string &name, const ModuleDataType &mod); + * for each module in the netlist + * + * void foreach_port(const ModuleDataType &mod, Func); + * calls Func(const std::string &name, const ModulePortDataType &port); + * for each port of mod + * + * void foreach_cell(const ModuleDataType &mod, Func); + * calls Func(const std::string &name, const CellDataType &cell); + * for each cell of mod + * + * void foreach_netname(const ModuleDataType &mod, Func); + * calls Func(const std::string &name, const NetnameDataType &cell); + * for each netname entry of mod + * + * PortType get_port_dir(const ModulePortDataType &port); + * gets the PortType direction of a module port + * + * int get_port_offset(const ModulePortDataType &port); + * gets the start bit number of a port + * + * bool is_port_upto(const ModulePortDataType &port); + * returns true if a port is an "upto" type port + * + * const BitVectorDataType &get_port_bits(const ModulePortDataType &port); + * gets the bit vector of a module port + * + * const std::string& get_cell_type(const CellDataType &cell); + * gets the type of a cell + * + * void foreach_attr(const {ModuleDataType|CellDataType|ModulePortDataType|NetnameDataType} &obj, Func); + * calls Func(const std::string &name, const Property &value); + * for each attribute on a module, cell, module port or net + * + * void foreach_param(const CellDataType &obj, Func); + * calls Func(const std::string &name, const Property &value); + * for each parameter of a cell + * + * void foreach_port_dir(const CellDataType &cell, Func); + * calls Func(const std::string &name, PortType dir); + * for each port direction of a cell + * + * void foreach_port_conn(const CellDataType &cell, Func); + * calls Func(const std::string &name, const BitVectorDataType &conn); + * for each port connection of a cell + * + * const BitVectorDataType &get_net_bits(const NetnameDataType &net); + * gets the BitVector corresponding to the bits entry of a netname field + * + * int get_vector_length(const BitVectorDataType &bits); + * gets the length of a BitVector + * + * bool is_vector_bit_constant(const BitVectorDataType &bits, int i); + * returns true if bit of bits is constant + * + * char get_vector_bit_constval(const BitVectorDataType &bits, int i); + * returns a char [01xz] corresponding to the constant value of bit + * + * int get_vector_bit_signal(const BitVectorDataType &bits, int i); + * returns the signal number of vector bit + * + */ \ No newline at end of file -- cgit v1.2.3 From fffc3b844730d1241d0056f989b3a9492d62005c Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 12 Nov 2019 11:33:49 +0000 Subject: frontend/base: Top module handling Signed-off-by: David Shah --- CMakeLists.txt | 6 +- common/command.cc | 1 + frontend/frontend_base.h | 192 +++++++++++++++++++++++++++++++++++++++++++++++ frontend/generic.h | 102 ------------------------- 4 files changed, 197 insertions(+), 104 deletions(-) create mode 100644 frontend/frontend_base.h delete mode 100644 frontend/generic.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9af4bb6c..e8fa4ec0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,7 +191,7 @@ if (BUILD_PYTHON) endif () endif() -include_directories(common/ json/ ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}) +include_directories(common/ json/ frontend/ ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}) if(BUILD_HEAP) find_package (Eigen3 REQUIRED NO_MODULE) @@ -202,7 +202,9 @@ endif() aux_source_directory(common/ COMMON_SRC_FILES) aux_source_directory(json/ JSON_PARSER_FILES) -set(COMMON_FILES ${COMMON_SRC_FILES} ${JSON_PARSER_FILES}) +aux_source_directory(frontend/ FRONTEND_FILES) + +set(COMMON_FILES ${COMMON_SRC_FILES} ${JSON_PARSER_FILES} ${FRONTEND_FILES}) set(CMAKE_BUILD_TYPE Release) if(MINGW) diff --git a/common/command.cc b/common/command.cc index fd310789..13cd3498 100644 --- a/common/command.cc +++ b/common/command.cc @@ -35,6 +35,7 @@ #include #include "command.h" #include "design_utils.h" +#include "frontend_base.h" #include "jsonparse.h" #include "jsonwrite.h" #include "log.h" diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h new file mode 100644 index 00000000..8b76cc85 --- /dev/null +++ b/frontend/frontend_base.h @@ -0,0 +1,192 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2019 David Shah + * + * 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 + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +/* + * Generic Frontend Framework + * + * This is designed to make it possible to build frontends for parsing any format isomorphic to Yosys JSON [1] + * with maximal inlining and minimal need for overhead such as runtime polymorphism or extra wrapper types. + * + * [1] http://www.clifford.at/yosys/cmd_write_json.html + * + * The frontend should implement a class referred to as FrontendType that defines the following type(def)s and + * functions: + * + * Types: + * ModuleDataType: corresponds to a single entry in "modules" + * ModulePortDataType: corresponds to a single entry in "ports" of a module + * CellDataType: corresponds to a single entry in "cells" + * NetnameDataType: corresponds to a single entry in "netnames" + * BitVectorDataType: corresponds to a signal/constant bit vector (e.g. a "connections" field) + * + * Functions: + * + * void foreach_module(Func); + * calls Func(const std::string &name, const ModuleDataType &mod); + * for each module in the netlist + * + * void foreach_port(const ModuleDataType &mod, Func); + * calls Func(const std::string &name, const ModulePortDataType &port); + * for each port of mod + * + * void foreach_cell(const ModuleDataType &mod, Func); + * calls Func(const std::string &name, const CellDataType &cell); + * for each cell of mod + * + * void foreach_netname(const ModuleDataType &mod, Func); + * calls Func(const std::string &name, const NetnameDataType &cell); + * for each netname entry of mod + * + * PortType get_port_dir(const ModulePortDataType &port); + * gets the PortType direction of a module port + * + * int get_port_offset(const ModulePortDataType &port); + * gets the start bit number of a port + * + * bool is_port_upto(const ModulePortDataType &port); + * returns true if a port is an "upto" type port + * + * const BitVectorDataType &get_port_bits(const ModulePortDataType &port); + * gets the bit vector of a module port + * + * const std::string& get_cell_type(const CellDataType &cell); + * gets the type of a cell + * + * void foreach_attr(const {ModuleDataType|CellDataType|ModulePortDataType|NetnameDataType} &obj, Func); + * calls Func(const std::string &name, const Property &value); + * for each attribute on a module, cell, module port or net + * + * void foreach_param(const CellDataType &obj, Func); + * calls Func(const std::string &name, const Property &value); + * for each parameter of a cell + * + * void foreach_port_dir(const CellDataType &cell, Func); + * calls Func(const std::string &name, PortType dir); + * for each port direction of a cell + * + * void foreach_port_conn(const CellDataType &cell, Func); + * calls Func(const std::string &name, const BitVectorDataType &conn); + * for each port connection of a cell + * + * const BitVectorDataType &get_net_bits(const NetnameDataType &net); + * gets the BitVector corresponding to the bits entry of a netname field + * + * int get_vector_length(const BitVectorDataType &bits); + * gets the length of a BitVector + * + * bool is_vector_bit_constant(const BitVectorDataType &bits, int i); + * returns true if bit of bits is constant + * + * char get_vector_bit_constval(const BitVectorDataType &bits, int i); + * returns a char [01xz] corresponding to the constant value of bit + * + * int get_vector_bit_signal(const BitVectorDataType &bits, int i); + * returns the signal number of vector bit + * + */ + +#include "log.h" +#include "nextpnr.h" +NEXTPNR_NAMESPACE_BEGIN + +namespace { + +template struct GenericFrontend +{ + GenericFrontend(Context *ctx, const FrontendType &impl) : ctx(ctx), impl(impl) {} + Context *ctx; + const FrontendType &impl; + using mod_dat_t = typename FrontendType::ModuleDataType; + using mod_port_dat_t = typename FrontendType::ModulePortDataType; + using cell_dat_t = typename FrontendType::CellDataType; + using netname_dat_t = typename FrontendType::NetnameDataType; + using bitvector_t = typename FrontendType::BitVectorDataType; + + // Used for hierarchy resolution + struct ModuleInfo + { + mod_dat_t *mod_data; + bool is_top = false, is_blackbox = false, is_whitebox = false; + inline bool is_box() const { return is_blackbox || is_whitebox; } + std::unordered_set instantiated_celltypes; + }; + std::unordered_map mods; + IdString top; + + // Process the list of modules and determine + // the top module + void find_top_module() + { + impl.foreach_module([&](const std::string &name, const mod_dat_t &mod) { + IdString mod_id = ctx->id(name); + auto &mi = mods[mod_id]; + mi.mod_data = &mod; + impl.foreach_attr(mod, [&](const std::string &name, const Property &value) { + if (name == "top") + mi.is_top = (value.intval != 0); + else if (name == "blackbox") + mi.is_blackbox = (value.intval != 0); + else if (name == "whitebox") + mi.is_whitebox = (value.intval != 0); + }); + impl.foreach_cell(mod, [&](const std::string &name, const cell_dat_t &cell) { + mi.instantiated_cells.insert(ctx->id(impl.get_cell_type(cell))); + }); + }); + // First of all, see if a top module has been manually specified + if (ctx->settings.count(ctx->id("frontend/top"))) { + IdString user_top = ctx->id(ctx->settings.at(ctx->id("frontend/top")).as_string()); + if (!mods.count(user_top)) + log_error("Top module '%s' not found!\n", ctx->nameOf(user_top)); + top = user_top; + return; + } + // If not, look for a module with the top attribute set + IdString top_by_attr; + for (auto &mod : mods) { + if (mod.second.is_top && !mod.second.is_box()) { + if (top_by_attr != IdString()) + log_error("Found multiple modules with (* top *) set (including %s and %s).\n", + ctx->nameOf(top_by_attr), ctx->nameOf(mod.first)); + top_by_attr = mod.first; + } + } + if (top_by_attr != IdString()) { + top = top_by_attr; + return; + } + // Finally, attempt to autodetect the top module using hierarchy + // (a module that is not a box and is not used as a cell by any other module) + std::unordered_set candidate_top; + for (auto &mod : mods) + if (!mod.second.is_box()) + candidate_top.insert(mod.first); + for (auto &mod : mods) + for (auto &c : mod.second.instantiated_celltypes) + candidate_top.erase(c); + if (candidate_top.size() != 1) + log_error("Failed to autodetect top module, please specify using --top.\n"); + top = *(candidate_top.begin()); + } +}; +} // namespace + +template void run_frontend(Context *ctx, const FrontendType &impl) {} + +NEXTPNR_NAMESPACE_END \ No newline at end of file diff --git a/frontend/generic.h b/frontend/generic.h deleted file mode 100644 index 35428052..00000000 --- a/frontend/generic.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2019 David Shah - * - * 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 - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -/* - * Generic Frontend Framework - * - * This is designed to make it possible to build frontends for parsing any format isomorphic to Yosys JSON [1] - * with maximal inlining and minimal need for overhead such as runtime polymorphism or extra wrapper types. - * - * [1] http://www.clifford.at/yosys/cmd_write_json.html - * - * The frontend should implement a class referred to as FrontendType that defines the following type(def)s and - * functions: - * - * Types: - * ModuleDataType: corresponds to a single entry in "modules" - * ModulePortDataType: corresponds to a single entry in "ports" of a module - * CellDataType: corresponds to a single entry in "cells" - * NetnameDataType: corresponds to a single entry in "netnames" - * BitVectorDataType: corresponds to a signal/constant bit vector (e.g. a "connections" field) - * - * Functions: - * - * void foreach_module(Func); - * calls Func(const std::string &name, const ModuleDataType &mod); - * for each module in the netlist - * - * void foreach_port(const ModuleDataType &mod, Func); - * calls Func(const std::string &name, const ModulePortDataType &port); - * for each port of mod - * - * void foreach_cell(const ModuleDataType &mod, Func); - * calls Func(const std::string &name, const CellDataType &cell); - * for each cell of mod - * - * void foreach_netname(const ModuleDataType &mod, Func); - * calls Func(const std::string &name, const NetnameDataType &cell); - * for each netname entry of mod - * - * PortType get_port_dir(const ModulePortDataType &port); - * gets the PortType direction of a module port - * - * int get_port_offset(const ModulePortDataType &port); - * gets the start bit number of a port - * - * bool is_port_upto(const ModulePortDataType &port); - * returns true if a port is an "upto" type port - * - * const BitVectorDataType &get_port_bits(const ModulePortDataType &port); - * gets the bit vector of a module port - * - * const std::string& get_cell_type(const CellDataType &cell); - * gets the type of a cell - * - * void foreach_attr(const {ModuleDataType|CellDataType|ModulePortDataType|NetnameDataType} &obj, Func); - * calls Func(const std::string &name, const Property &value); - * for each attribute on a module, cell, module port or net - * - * void foreach_param(const CellDataType &obj, Func); - * calls Func(const std::string &name, const Property &value); - * for each parameter of a cell - * - * void foreach_port_dir(const CellDataType &cell, Func); - * calls Func(const std::string &name, PortType dir); - * for each port direction of a cell - * - * void foreach_port_conn(const CellDataType &cell, Func); - * calls Func(const std::string &name, const BitVectorDataType &conn); - * for each port connection of a cell - * - * const BitVectorDataType &get_net_bits(const NetnameDataType &net); - * gets the BitVector corresponding to the bits entry of a netname field - * - * int get_vector_length(const BitVectorDataType &bits); - * gets the length of a BitVector - * - * bool is_vector_bit_constant(const BitVectorDataType &bits, int i); - * returns true if bit of bits is constant - * - * char get_vector_bit_constval(const BitVectorDataType &bits, int i); - * returns a char [01xz] corresponding to the constant value of bit - * - * int get_vector_bit_signal(const BitVectorDataType &bits, int i); - * returns the signal number of vector bit - * - */ \ No newline at end of file -- cgit v1.2.3 From 6aaa9f5a3d02224f2760d993d114163ce7678e1f Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 13 Nov 2019 12:11:17 +0000 Subject: frontend/base: Functions for port import Signed-off-by: David Shah --- common/nextpnr.h | 2 + frontend/frontend_base.h | 145 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 146 insertions(+), 1 deletion(-) diff --git a/common/nextpnr.h b/common/nextpnr.h index 24f6948b..3fce97a2 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -397,6 +397,8 @@ struct NetInfo : ArchNetInfo // wire -> uphill_pip std::unordered_map wires; + std::vector aliases; // entries in net_aliases that point to this net + std::unique_ptr clkconstr; TimingConstrObjectId tmg_id; diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h index 8b76cc85..42be2bfd 100644 --- a/frontend/frontend_base.h +++ b/frontend/frontend_base.h @@ -101,6 +101,7 @@ * */ +#include "design_utils.h" #include "log.h" #include "nextpnr.h" NEXTPNR_NAMESPACE_BEGIN @@ -121,7 +122,7 @@ template struct GenericFrontend // Used for hierarchy resolution struct ModuleInfo { - mod_dat_t *mod_data; + const mod_dat_t *mod_data; bool is_top = false, is_blackbox = false, is_whitebox = false; inline bool is_box() const { return is_blackbox || is_whitebox; } std::unordered_set instantiated_celltypes; @@ -184,6 +185,148 @@ template struct GenericFrontend log_error("Failed to autodetect top module, please specify using --top.\n"); top = *(candidate_top.begin()); } + + // Create a unique name (guaranteed collision free) for a net or a cell; based on + // a base name and suffix. __unique__i will be be appended with increasing i + // if a collision is found until no collision + IdString unique_name(const std::string &base, const std::string &suffix, bool is_net) + { + IdString name; + int incr = 0; + do { + std::string comb = base + suffix; + if (incr > 0) { + comb += "__unique__"; + comb += std::to_string(incr); + } + name = ctx->id(comb); + incr++; + } while (is_net ? ctx->nets.count(name) : ctx->cells.count(name)); + return name; + } + + // A flat index of map; designed to cope with renaming + // A net's udata points into this index + std::vector net_flatindex; + + // This structure contains some structures specific to the import of a module at + // a certain point in the hierarchy + struct HierModuleState + { + bool is_toplevel; + std::string prefix; + // Map from index in module to "flat" index of nets + std::vector index_to_net_flatindex; + // Get a reference to index_to_net; resizing if + // appropriate + NetInfo *&net_by_idx(int idx) + { + NPNR_ASSERT(idx >= 0); + if (idx >= int(index_to_net_flatindex.size())) + index_to_net_flatindex.resize(idx + 1, nullptr); + return index_to_net_flatindex.at(idx); + } + std::unordered_map> port_to_bus; + }; + + void import_module(HierModuleState &m, mod_dat_t *data) + { + std::vector index_to_net; + // Import port connections; for submodules only + if (!m.is_toplevel) { + import_port_connections(m, data); + } + } + + // Add a constant-driving VCC or GND cell to make a net constant + // (constval can be [01xz], x and z or no-ops) + int const_autoidx = 0; + void add_constant_driver(HierModuleState &m, NetInfo *net, char constval) + { + + if (constval == 'x' || constval == 'z') + return; // 'x' or 'z' is the same as undriven + NPNR_ASSERT(constval == '0' || constval == '1'); + IdString cell_name = unique_name( + m.prefix, net->name.str(ctx) + (constval == '1' ? "$VCC$" : "$GND$") + std::to_string(const_autoidx++), + false); + CellInfo *cc = ctx->createCell(cell_name, ctx->id(constval == '1' ? "VCC" : "GND")); + cc->ports[ctx->id("Y")].name = ctx->id("Y"); + cc->ports[ctx->id("Y")].type = PORT_OUT; + if (net->driver.cell != nullptr) + log_error("Net '%s' is multiply driven by port %s.%s and constant '%c'\n", ctx->nameOf(net), + ctx->nameOf(net->driver.cell), ctx->nameOf(net->driver.port), constval); + connect_port(ctx, net, cc, ctx->id("Y")); + } + + // Merge two nets - e.g. if one net in a submodule bifurcates to two output bits and therefore two different + // parent nets + void merge_nets(NetInfo *base, NetInfo *mergee) + { + // Resolve drivers + if (mergee->driver.cell != nullptr) { + if (base->driver.cell != nullptr) + log_error("Attempting to merge nets '%s' and '%s' due to port connectivity; but this would result in a " + "multiply driven net\n", + ctx->nameOf(base), ctx->nameOf(mergee)); + else { + mergee->driver.cell->ports[mergee->driver.port].net = base; + base->driver = mergee->driver; + } + } + // Combine users + for (auto &usr : mergee->users) { + usr.cell->ports[usr.port].net = base; + base->users.push_back(usr); + } + // Point aliases to the new net + for (IdString alias : mergee->aliases) { + ctx->net_aliases[alias] = base->name; + base->aliases.push_back(alias); + } + // Create a new alias from mergee's name to new base name + ctx->net_aliases[mergee->name] = base->name; + // Update flat index of nets + net_flatindex.at(mergee->udata) = base; + // Remove merged net from context + ctx->nets.erase(mergee->name); + } + + // Import connections between a submodule and its parent + void import_port_connections(HierModuleState &m, const mod_dat_t &data) + { + impl.foreach_port(data, [&](const std::string &name, const mod_port_dat_t &port) { + // CHECK: should disconnected module inputs really just be skipped; or is it better + // to insert a ground driver? + if (!m.port_to_bus.count(ctx->id(name))) + return; + auto &p2b = m.port_to_bus.at(ctx->id(name)); + // Get direction and vector of port bits + PortType dir = impl.get_port_dir(port); + const auto &bv = impl.get_port_bits(port); + int bv_size = impl.get_vector_length(bv); + // Iterate over bits of port; making connections + for (int i = 0; i < std::min(bv_size, p2b.size()); i++) { + NetInfo *conn_net = p2b.at(i); + if (conn_net == nullptr) + continue; + if (impl.is_vector_bit_constant(bv, i)) { + // It is a constant, we might need to insert a constant driver here to drive the corresponding + // net in the parent + char constval = impl.get_vector_bit_constval(bv, i); + // Inputs cannot be driving a constant back to the parent + if (dir == PORT_IN) + log_error("Input port %s%s[%d] cannot be driving a constant '%c'.\n", m.prefix.c_str(), + port.c_str(), i, constval); + // Insert the constant driver + add_constant_driver(m, conn_net, constval); + } else { + // If not driving a constant; simply make the port bit net index in the submodule correspond + // to connected net in the parent module + } + } + }); + } }; } // namespace -- cgit v1.2.3 From 25867e3f231cc8ffe3ce87e6f1af1ca1d4c46146 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 13 Nov 2019 13:51:28 +0000 Subject: frontend/base: Improve net indexing Signed-off-by: David Shah --- frontend/frontend_base.h | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h index 42be2bfd..35d4409c 100644 --- a/frontend/frontend_base.h +++ b/frontend/frontend_base.h @@ -205,9 +205,10 @@ template struct GenericFrontend return name; } - // A flat index of map; designed to cope with renaming + // A flat index of map; designed to cope with merging nets where pointers to nets would go stale // A net's udata points into this index std::vector net_flatindex; + std::vector> net_old_indices; // the other indices of a net in net_flatindex for merging // This structure contains some structures specific to the import of a module at // a certain point in the hierarchy @@ -216,17 +217,17 @@ template struct GenericFrontend bool is_toplevel; std::string prefix; // Map from index in module to "flat" index of nets - std::vector index_to_net_flatindex; + std::vector index_to_net_flatindex; // Get a reference to index_to_net; resizing if // appropriate - NetInfo *&net_by_idx(int idx) + int &net_by_idx(int idx) { NPNR_ASSERT(idx >= 0); if (idx >= int(index_to_net_flatindex.size())) - index_to_net_flatindex.resize(idx + 1, nullptr); + index_to_net_flatindex.resize(idx + 1, -1); return index_to_net_flatindex.at(idx); } - std::unordered_map> port_to_bus; + std::unordered_map> port_to_bus; }; void import_module(HierModuleState &m, mod_dat_t *data) @@ -287,7 +288,13 @@ template struct GenericFrontend // Create a new alias from mergee's name to new base name ctx->net_aliases[mergee->name] = base->name; // Update flat index of nets + for (auto old_idx : net_old_indices.at(mergee->udata)) { + net_old_indices.at(base).push_back(old_idx); + net_flatindex.at(old_idx) = base; + } + net_old_indices.at(base).push_back(mergee->udata); net_flatindex.at(mergee->udata) = base; + net_old_indices.at(mergee->udata).clear(); // Remove merged net from context ctx->nets.erase(mergee->name); } @@ -307,9 +314,11 @@ template struct GenericFrontend int bv_size = impl.get_vector_length(bv); // Iterate over bits of port; making connections for (int i = 0; i < std::min(bv_size, p2b.size()); i++) { - NetInfo *conn_net = p2b.at(i); - if (conn_net == nullptr) + int conn_net = p2b.at(i); + if (conn_net == -1) continue; + NetInfo *conn_ni = net_flatindex.at(conn_net); + NPNR_ASSERT(conn_ni != nullptr); if (impl.is_vector_bit_constant(bv, i)) { // It is a constant, we might need to insert a constant driver here to drive the corresponding // net in the parent @@ -319,10 +328,20 @@ template struct GenericFrontend log_error("Input port %s%s[%d] cannot be driving a constant '%c'.\n", m.prefix.c_str(), port.c_str(), i, constval); // Insert the constant driver - add_constant_driver(m, conn_net, constval); + add_constant_driver(m, conn_ni, constval); } else { // If not driving a constant; simply make the port bit net index in the submodule correspond // to connected net in the parent module + int &submod_net = m.net_by_idx(impl.get_vector_bit_signal(bv, i)); + if (submod_net == -1) { + // A net at this index doesn't yet exist + // We can simply set this index to point to the net in the parent + submod_net = conn_net; + } else { + // A net at this index already exists (this would usually be a submodule net + // connected to more than one I/O port) + merge_nets(net_flatindex.at(submod_net), net_flatindex.at(conn_net)); + } } } }); -- cgit v1.2.3 From 240561c370c03ad33b2ac397369659a0b9a54c18 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 13 Nov 2019 16:17:43 +0000 Subject: 3rdparty: Add json11 Signed-off-by: David Shah --- 3rdparty/json11/LICENSE.txt | 19 ++ 3rdparty/json11/json11.cpp | 790 ++++++++++++++++++++++++++++++++++++++++++++ 3rdparty/json11/json11.hpp | 232 +++++++++++++ CMakeLists.txt | 5 +- 4 files changed, 1044 insertions(+), 2 deletions(-) create mode 100644 3rdparty/json11/LICENSE.txt create mode 100644 3rdparty/json11/json11.cpp create mode 100644 3rdparty/json11/json11.hpp diff --git a/3rdparty/json11/LICENSE.txt b/3rdparty/json11/LICENSE.txt new file mode 100644 index 00000000..691742e9 --- /dev/null +++ b/3rdparty/json11/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2013 Dropbox, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/3rdparty/json11/json11.cpp b/3rdparty/json11/json11.cpp new file mode 100644 index 00000000..88024e92 --- /dev/null +++ b/3rdparty/json11/json11.cpp @@ -0,0 +1,790 @@ +/* Copyright (c) 2013 Dropbox, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "json11.hpp" +#include +#include +#include +#include +#include + +namespace json11 { + +static const int max_depth = 200; + +using std::string; +using std::vector; +using std::map; +using std::make_shared; +using std::initializer_list; +using std::move; + +/* Helper for representing null - just a do-nothing struct, plus comparison + * operators so the helpers in JsonValue work. We can't use nullptr_t because + * it may not be orderable. + */ +struct NullStruct { + bool operator==(NullStruct) const { return true; } + bool operator<(NullStruct) const { return false; } +}; + +/* * * * * * * * * * * * * * * * * * * * + * Serialization + */ + +static void dump(NullStruct, string &out) { + out += "null"; +} + +static void dump(double value, string &out) { + if (std::isfinite(value)) { + char buf[32]; + snprintf(buf, sizeof buf, "%.17g", value); + out += buf; + } else { + out += "null"; + } +} + +static void dump(int value, string &out) { + char buf[32]; + snprintf(buf, sizeof buf, "%d", value); + out += buf; +} + +static void dump(bool value, string &out) { + out += value ? "true" : "false"; +} + +static void dump(const string &value, string &out) { + out += '"'; + for (size_t i = 0; i < value.length(); i++) { + const char ch = value[i]; + if (ch == '\\') { + out += "\\\\"; + } else if (ch == '"') { + out += "\\\""; + } else if (ch == '\b') { + out += "\\b"; + } else if (ch == '\f') { + out += "\\f"; + } else if (ch == '\n') { + out += "\\n"; + } else if (ch == '\r') { + out += "\\r"; + } else if (ch == '\t') { + out += "\\t"; + } else if (static_cast(ch) <= 0x1f) { + char buf[8]; + snprintf(buf, sizeof buf, "\\u%04x", ch); + out += buf; + } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 + && static_cast(value[i+2]) == 0xa8) { + out += "\\u2028"; + i += 2; + } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 + && static_cast(value[i+2]) == 0xa9) { + out += "\\u2029"; + i += 2; + } else { + out += ch; + } + } + out += '"'; +} + +static void dump(const Json::array &values, string &out) { + bool first = true; + out += "["; + for (const auto &value : values) { + if (!first) + out += ", "; + value.dump(out); + first = false; + } + out += "]"; +} + +static void dump(const Json::object &values, string &out) { + bool first = true; + out += "{"; + for (const auto &kv : values) { + if (!first) + out += ", "; + dump(kv.first, out); + out += ": "; + kv.second.dump(out); + first = false; + } + out += "}"; +} + +void Json::dump(string &out) const { + m_ptr->dump(out); +} + +/* * * * * * * * * * * * * * * * * * * * + * Value wrappers + */ + +template +class Value : public JsonValue { +protected: + + // Constructors + explicit Value(const T &value) : m_value(value) {} + explicit Value(T &&value) : m_value(move(value)) {} + + // Get type tag + Json::Type type() const override { + return tag; + } + + // Comparisons + bool equals(const JsonValue * other) const override { + return m_value == static_cast *>(other)->m_value; + } + bool less(const JsonValue * other) const override { + return m_value < static_cast *>(other)->m_value; + } + + const T m_value; + void dump(string &out) const override { json11::dump(m_value, out); } +}; + +class JsonDouble final : public Value { + double number_value() const override { return m_value; } + int int_value() const override { return static_cast(m_value); } + bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } + bool less(const JsonValue * other) const override { return m_value < other->number_value(); } +public: + explicit JsonDouble(double value) : Value(value) {} +}; + +class JsonInt final : public Value { + double number_value() const override { return m_value; } + int int_value() const override { return m_value; } + bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } + bool less(const JsonValue * other) const override { return m_value < other->number_value(); } +public: + explicit JsonInt(int value) : Value(value) {} +}; + +class JsonBoolean final : public Value { + bool bool_value() const override { return m_value; } +public: + explicit JsonBoolean(bool value) : Value(value) {} +}; + +class JsonString final : public Value { + const string &string_value() const override { return m_value; } +public: + explicit JsonString(const string &value) : Value(value) {} + explicit JsonString(string &&value) : Value(move(value)) {} +}; + +class JsonArray final : public Value { + const Json::array &array_items() const override { return m_value; } + const Json & operator[](size_t i) const override; +public: + explicit JsonArray(const Json::array &value) : Value(value) {} + explicit JsonArray(Json::array &&value) : Value(move(value)) {} +}; + +class JsonObject final : public Value { + const Json::object &object_items() const override { return m_value; } + const Json & operator[](const string &key) const override; +public: + explicit JsonObject(const Json::object &value) : Value(value) {} + explicit JsonObject(Json::object &&value) : Value(move(value)) {} +}; + +class JsonNull final : public Value { +public: + JsonNull() : Value({}) {} +}; + +/* * * * * * * * * * * * * * * * * * * * + * Static globals - static-init-safe + */ +struct Statics { + const std::shared_ptr null = make_shared(); + const std::shared_ptr t = make_shared(true); + const std::shared_ptr f = make_shared(false); + const string empty_string; + const vector empty_vector; + const map empty_map; + Statics() {} +}; + +static const Statics & statics() { + static const Statics s {}; + return s; +} + +static const Json & static_null() { + // This has to be separate, not in Statics, because Json() accesses statics().null. + static const Json json_null; + return json_null; +} + +/* * * * * * * * * * * * * * * * * * * * + * Constructors + */ + +Json::Json() noexcept : m_ptr(statics().null) {} +Json::Json(std::nullptr_t) noexcept : m_ptr(statics().null) {} +Json::Json(double value) : m_ptr(make_shared(value)) {} +Json::Json(int value) : m_ptr(make_shared(value)) {} +Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {} +Json::Json(const string &value) : m_ptr(make_shared(value)) {} +Json::Json(string &&value) : m_ptr(make_shared(move(value))) {} +Json::Json(const char * value) : m_ptr(make_shared(value)) {} +Json::Json(const Json::array &values) : m_ptr(make_shared(values)) {} +Json::Json(Json::array &&values) : m_ptr(make_shared(move(values))) {} +Json::Json(const Json::object &values) : m_ptr(make_shared(values)) {} +Json::Json(Json::object &&values) : m_ptr(make_shared(move(values))) {} + +/* * * * * * * * * * * * * * * * * * * * + * Accessors + */ + +Json::Type Json::type() const { return m_ptr->type(); } +double Json::number_value() const { return m_ptr->number_value(); } +int Json::int_value() const { return m_ptr->int_value(); } +bool Json::bool_value() const { return m_ptr->bool_value(); } +const string & Json::string_value() const { return m_ptr->string_value(); } +const vector & Json::array_items() const { return m_ptr->array_items(); } +const map & Json::object_items() const { return m_ptr->object_items(); } +const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; } +const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; } + +double JsonValue::number_value() const { return 0; } +int JsonValue::int_value() const { return 0; } +bool JsonValue::bool_value() const { return false; } +const string & JsonValue::string_value() const { return statics().empty_string; } +const vector & JsonValue::array_items() const { return statics().empty_vector; } +const map & JsonValue::object_items() const { return statics().empty_map; } +const Json & JsonValue::operator[] (size_t) const { return static_null(); } +const Json & JsonValue::operator[] (const string &) const { return static_null(); } + +const Json & JsonObject::operator[] (const string &key) const { + auto iter = m_value.find(key); + return (iter == m_value.end()) ? static_null() : iter->second; +} +const Json & JsonArray::operator[] (size_t i) const { + if (i >= m_value.size()) return static_null(); + else return m_value[i]; +} + +/* * * * * * * * * * * * * * * * * * * * + * Comparison + */ + +bool Json::operator== (const Json &other) const { + if (m_ptr == other.m_ptr) + return true; + if (m_ptr->type() != other.m_ptr->type()) + return false; + + return m_ptr->equals(other.m_ptr.get()); +} + +bool Json::operator< (const Json &other) const { + if (m_ptr == other.m_ptr) + return false; + if (m_ptr->type() != other.m_ptr->type()) + return m_ptr->type() < other.m_ptr->type(); + + return m_ptr->less(other.m_ptr.get()); +} + +/* * * * * * * * * * * * * * * * * * * * + * Parsing + */ + +/* esc(c) + * + * Format char c suitable for printing in an error message. + */ +static inline string esc(char c) { + char buf[12]; + if (static_cast(c) >= 0x20 && static_cast(c) <= 0x7f) { + snprintf(buf, sizeof buf, "'%c' (%d)", c, c); + } else { + snprintf(buf, sizeof buf, "(%d)", c); + } + return string(buf); +} + +static inline bool in_range(long x, long lower, long upper) { + return (x >= lower && x <= upper); +} + +namespace { +/* JsonParser + * + * Object that tracks all state of an in-progress parse. + */ +struct JsonParser final { + + /* State + */ + const string &str; + size_t i; + string &err; + bool failed; + const JsonParse strategy; + + /* fail(msg, err_ret = Json()) + * + * Mark this parse as failed. + */ + Json fail(string &&msg) { + return fail(move(msg), Json()); + } + + template + T fail(string &&msg, const T err_ret) { + if (!failed) + err = std::move(msg); + failed = true; + return err_ret; + } + + /* consume_whitespace() + * + * Advance until the current character is non-whitespace. + */ + void consume_whitespace() { + while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t') + i++; + } + + /* consume_comment() + * + * Advance comments (c-style inline and multiline). + */ + bool consume_comment() { + bool comment_found = false; + if (str[i] == '/') { + i++; + if (i == str.size()) + return fail("unexpected end of input after start of comment", false); + if (str[i] == '/') { // inline comment + i++; + // advance until next line, or end of input + while (i < str.size() && str[i] != '\n') { + i++; + } + comment_found = true; + } + else if (str[i] == '*') { // multiline comment + i++; + if (i > str.size()-2) + return fail("unexpected end of input inside multi-line comment", false); + // advance until closing tokens + while (!(str[i] == '*' && str[i+1] == '/')) { + i++; + if (i > str.size()-2) + return fail( + "unexpected end of input inside multi-line comment", false); + } + i += 2; + comment_found = true; + } + else + return fail("malformed comment", false); + } + return comment_found; + } + + /* consume_garbage() + * + * Advance until the current character is non-whitespace and non-comment. + */ + void consume_garbage() { + consume_whitespace(); + if(strategy == JsonParse::COMMENTS) { + bool comment_found = false; + do { + comment_found = consume_comment(); + if (failed) return; + consume_whitespace(); + } + while(comment_found); + } + } + + /* get_next_token() + * + * Return the next non-whitespace character. If the end of the input is reached, + * flag an error and return 0. + */ + char get_next_token() { + consume_garbage(); + if (failed) return static_cast(0); + if (i == str.size()) + return fail("unexpected end of input", static_cast(0)); + + return str[i++]; + } + + /* encode_utf8(pt, out) + * + * Encode pt as UTF-8 and add it to out. + */ + void encode_utf8(long pt, string & out) { + if (pt < 0) + return; + + if (pt < 0x80) { + out += static_cast(pt); + } else if (pt < 0x800) { + out += static_cast((pt >> 6) | 0xC0); + out += static_cast((pt & 0x3F) | 0x80); + } else if (pt < 0x10000) { + out += static_cast((pt >> 12) | 0xE0); + out += static_cast(((pt >> 6) & 0x3F) | 0x80); + out += static_cast((pt & 0x3F) | 0x80); + } else { + out += static_cast((pt >> 18) | 0xF0); + out += static_cast(((pt >> 12) & 0x3F) | 0x80); + out += static_cast(((pt >> 6) & 0x3F) | 0x80); + out += static_cast((pt & 0x3F) | 0x80); + } + } + + /* parse_string() + * + * Parse a string, starting at the current position. + */ + string parse_string() { + string out; + long last_escaped_codepoint = -1; + while (true) { + if (i == str.size()) + return fail("unexpected end of input in string", ""); + + char ch = str[i++]; + + if (ch == '"') { + encode_utf8(last_escaped_codepoint, out); + return out; + } + + if (in_range(ch, 0, 0x1f)) + return fail("unescaped " + esc(ch) + " in string", ""); + + // The usual case: non-escaped characters + if (ch != '\\') { + encode_utf8(last_escaped_codepoint, out); + last_escaped_codepoint = -1; + out += ch; + continue; + } + + // Handle escapes + if (i == str.size()) + return fail("unexpected end of input in string", ""); + + ch = str[i++]; + + if (ch == 'u') { + // Extract 4-byte escape sequence + string esc = str.substr(i, 4); + // Explicitly check length of the substring. The following loop + // relies on std::string returning the terminating NUL when + // accessing str[length]. Checking here reduces brittleness. + if (esc.length() < 4) { + return fail("bad \\u escape: " + esc, ""); + } + for (size_t j = 0; j < 4; j++) { + if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F') + && !in_range(esc[j], '0', '9')) + return fail("bad \\u escape: " + esc, ""); + } + + long codepoint = strtol(esc.data(), nullptr, 16); + + // JSON specifies that characters outside the BMP shall be encoded as a pair + // of 4-hex-digit \u escapes encoding their surrogate pair components. Check + // whether we're in the middle of such a beast: the previous codepoint was an + // escaped lead (high) surrogate, and this is a trail (low) surrogate. + if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF) + && in_range(codepoint, 0xDC00, 0xDFFF)) { + // Reassemble the two surrogate pairs into one astral-plane character, per + // the UTF-16 algorithm. + encode_utf8((((last_escaped_codepoint - 0xD800) << 10) + | (codepoint - 0xDC00)) + 0x10000, out); + last_escaped_codepoint = -1; + } else { + encode_utf8(last_escaped_codepoint, out); + last_escaped_codepoint = codepoint; + } + + i += 4; + continue; + } + + encode_utf8(last_escaped_codepoint, out); + last_escaped_codepoint = -1; + + if (ch == 'b') { + out += '\b'; + } else if (ch == 'f') { + out += '\f'; + } else if (ch == 'n') { + out += '\n'; + } else if (ch == 'r') { + out += '\r'; + } else if (ch == 't') { + out += '\t'; + } else if (ch == '"' || ch == '\\' || ch == '/') { + out += ch; + } else { + return fail("invalid escape character " + esc(ch), ""); + } + } + } + + /* parse_number() + * + * Parse a double. + */ + Json parse_number() { + size_t start_pos = i; + + if (str[i] == '-') + i++; + + // Integer part + if (str[i] == '0') { + i++; + if (in_range(str[i], '0', '9')) + return fail("leading 0s not permitted in numbers"); + } else if (in_range(str[i], '1', '9')) { + i++; + while (in_range(str[i], '0', '9')) + i++; + } else { + return fail("invalid " + esc(str[i]) + " in number"); + } + + if (str[i] != '.' && str[i] != 'e' && str[i] != 'E' + && (i - start_pos) <= static_cast(std::numeric_limits::digits10)) { + return std::atoi(str.c_str() + start_pos); + } + + // Decimal part + if (str[i] == '.') { + i++; + if (!in_range(str[i], '0', '9')) + return fail("at least one digit required in fractional part"); + + while (in_range(str[i], '0', '9')) + i++; + } + + // Exponent part + if (str[i] == 'e' || str[i] == 'E') { + i++; + + if (str[i] == '+' || str[i] == '-') + i++; + + if (!in_range(str[i], '0', '9')) + return fail("at least one digit required in exponent"); + + while (in_range(str[i], '0', '9')) + i++; + } + + return std::strtod(str.c_str() + start_pos, nullptr); + } + + /* expect(str, res) + * + * Expect that 'str' starts at the character that was just read. If it does, advance + * the input and return res. If not, flag an error. + */ + Json expect(const string &expected, Json res) { + assert(i != 0); + i--; + if (str.compare(i, expected.length(), expected) == 0) { + i += expected.length(); + return res; + } else { + return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length())); + } + } + + /* parse_json() + * + * Parse a JSON object. + */ + Json parse_json(int depth) { + if (depth > max_depth) { + return fail("exceeded maximum nesting depth"); + } + + char ch = get_next_token(); + if (failed) + return Json(); + + if (ch == '-' || (ch >= '0' && ch <= '9')) { + i--; + return parse_number(); + } + + if (ch == 't') + return expect("true", true); + + if (ch == 'f') + return expect("false", false); + + if (ch == 'n') + return expect("null", Json()); + + if (ch == '"') + return parse_string(); + + if (ch == '{') { + map data; + ch = get_next_token(); + if (ch == '}') + return data; + + while (1) { + if (ch != '"') + return fail("expected '\"' in object, got " + esc(ch)); + + string key = parse_string(); + if (failed) + return Json(); + + ch = get_next_token(); + if (ch != ':') + return fail("expected ':' in object, got " + esc(ch)); + + data[std::move(key)] = parse_json(depth + 1); + if (failed) + return Json(); + + ch = get_next_token(); + if (ch == '}') + break; + if (ch != ',') + return fail("expected ',' in object, got " + esc(ch)); + + ch = get_next_token(); + } + return data; + } + + if (ch == '[') { + vector data; + ch = get_next_token(); + if (ch == ']') + return data; + + while (1) { + i--; + data.push_back(parse_json(depth + 1)); + if (failed) + return Json(); + + ch = get_next_token(); + if (ch == ']') + break; + if (ch != ',') + return fail("expected ',' in list, got " + esc(ch)); + + ch = get_next_token(); + (void)ch; + } + return data; + } + + return fail("expected value, got " + esc(ch)); + } +}; +}//namespace { + +Json Json::parse(const string &in, string &err, JsonParse strategy) { + JsonParser parser { in, 0, err, false, strategy }; + Json result = parser.parse_json(0); + + // Check for any trailing garbage + parser.consume_garbage(); + if (parser.failed) + return Json(); + if (parser.i != in.size()) + return parser.fail("unexpected trailing " + esc(in[parser.i])); + + return result; +} + +// Documented in json11.hpp +vector Json::parse_multi(const string &in, + std::string::size_type &parser_stop_pos, + string &err, + JsonParse strategy) { + JsonParser parser { in, 0, err, false, strategy }; + parser_stop_pos = 0; + vector json_vec; + while (parser.i != in.size() && !parser.failed) { + json_vec.push_back(parser.parse_json(0)); + if (parser.failed) + break; + + // Check for another object + parser.consume_garbage(); + if (parser.failed) + break; + parser_stop_pos = parser.i; + } + return json_vec; +} + +/* * * * * * * * * * * * * * * * * * * * + * Shape-checking + */ + +bool Json::has_shape(const shape & types, string & err) const { + if (!is_object()) { + err = "expected JSON object, got " + dump(); + return false; + } + + const auto& obj_items = object_items(); + for (auto & item : types) { + const auto it = obj_items.find(item.first); + if (it == obj_items.cend() || it->second.type() != item.second) { + err = "bad type for " + item.first + " in " + dump(); + return false; + } + } + + return true; +} + +} // namespace json11 diff --git a/3rdparty/json11/json11.hpp b/3rdparty/json11/json11.hpp new file mode 100644 index 00000000..0c47d050 --- /dev/null +++ b/3rdparty/json11/json11.hpp @@ -0,0 +1,232 @@ +/* json11 + * + * json11 is a tiny JSON library for C++11, providing JSON parsing and serialization. + * + * The core object provided by the library is json11::Json. A Json object represents any JSON + * value: null, bool, number (int or double), string (std::string), array (std::vector), or + * object (std::map). + * + * Json objects act like values: they can be assigned, copied, moved, compared for equality or + * order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and + * Json::parse (static) to parse a std::string as a Json object. + * + * Internally, the various types of Json object are represented by the JsonValue class + * hierarchy. + * + * A note on numbers - JSON specifies the syntax of number formatting but not its semantics, + * so some JSON implementations distinguish between integers and floating-point numbers, while + * some don't. In json11, we choose the latter. Because some JSON implementations (namely + * Javascript itself) treat all numbers as the same type, distinguishing the two leads + * to JSON that will be *silently* changed by a round-trip through those implementations. + * Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also + * provides integer helpers. + * + * Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the + * range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64 + * or long long to avoid the Y2038K problem; a double storing microseconds since some epoch + * will be exact for +/- 275 years.) + */ + +/* Copyright (c) 2013 Dropbox, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER + #if _MSC_VER <= 1800 // VS 2013 + #ifndef noexcept + #define noexcept throw() + #endif + + #ifndef snprintf + #define snprintf _snprintf_s + #endif + #endif +#endif + +namespace json11 { + +enum JsonParse { + STANDARD, COMMENTS +}; + +class JsonValue; + +class Json final { +public: + // Types + enum Type { + NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT + }; + + // Array and object typedefs + typedef std::vector array; + typedef std::map object; + + // Constructors for the various types of JSON value. + Json() noexcept; // NUL + Json(std::nullptr_t) noexcept; // NUL + Json(double value); // NUMBER + Json(int value); // NUMBER + Json(bool value); // BOOL + Json(const std::string &value); // STRING + Json(std::string &&value); // STRING + Json(const char * value); // STRING + Json(const array &values); // ARRAY + Json(array &&values); // ARRAY + Json(const object &values); // OBJECT + Json(object &&values); // OBJECT + + // Implicit constructor: anything with a to_json() function. + template + Json(const T & t) : Json(t.to_json()) {} + + // Implicit constructor: map-like objects (std::map, std::unordered_map, etc) + template ().begin()->first)>::value + && std::is_constructible().begin()->second)>::value, + int>::type = 0> + Json(const M & m) : Json(object(m.begin(), m.end())) {} + + // Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc) + template ().begin())>::value, + int>::type = 0> + Json(const V & v) : Json(array(v.begin(), v.end())) {} + + // This prevents Json(some_pointer) from accidentally producing a bool. Use + // Json(bool(some_pointer)) if that behavior is desired. + Json(void *) = delete; + + // Accessors + Type type() const; + + bool is_null() const { return type() == NUL; } + bool is_number() const { return type() == NUMBER; } + bool is_bool() const { return type() == BOOL; } + bool is_string() const { return type() == STRING; } + bool is_array() const { return type() == ARRAY; } + bool is_object() const { return type() == OBJECT; } + + // Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not + // distinguish between integer and non-integer numbers - number_value() and int_value() + // can both be applied to a NUMBER-typed object. + double number_value() const; + int int_value() const; + + // Return the enclosed value if this is a boolean, false otherwise. + bool bool_value() const; + // Return the enclosed string if this is a string, "" otherwise. + const std::string &string_value() const; + // Return the enclosed std::vector if this is an array, or an empty vector otherwise. + const array &array_items() const; + // Return the enclosed std::map if this is an object, or an empty map otherwise. + const object &object_items() const; + + // Return a reference to arr[i] if this is an array, Json() otherwise. + const Json & operator[](size_t i) const; + // Return a reference to obj[key] if this is an object, Json() otherwise. + const Json & operator[](const std::string &key) const; + + // Serialize. + void dump(std::string &out) const; + std::string dump() const { + std::string out; + dump(out); + return out; + } + + // Parse. If parse fails, return Json() and assign an error message to err. + static Json parse(const std::string & in, + std::string & err, + JsonParse strategy = JsonParse::STANDARD); + static Json parse(const char * in, + std::string & err, + JsonParse strategy = JsonParse::STANDARD) { + if (in) { + return parse(std::string(in), err, strategy); + } else { + err = "null input"; + return nullptr; + } + } + // Parse multiple objects, concatenated or separated by whitespace + static std::vector parse_multi( + const std::string & in, + std::string::size_type & parser_stop_pos, + std::string & err, + JsonParse strategy = JsonParse::STANDARD); + + static inline std::vector parse_multi( + const std::string & in, + std::string & err, + JsonParse strategy = JsonParse::STANDARD) { + std::string::size_type parser_stop_pos; + return parse_multi(in, parser_stop_pos, err, strategy); + } + + bool operator== (const Json &rhs) const; + bool operator< (const Json &rhs) const; + bool operator!= (const Json &rhs) const { return !(*this == rhs); } + bool operator<= (const Json &rhs) const { return !(rhs < *this); } + bool operator> (const Json &rhs) const { return (rhs < *this); } + bool operator>= (const Json &rhs) const { return !(*this < rhs); } + + /* has_shape(types, err) + * + * Return true if this is a JSON object and, for each item in types, has a field of + * the given type. If not, return false and set err to a descriptive message. + */ + typedef std::initializer_list> shape; + bool has_shape(const shape & types, std::string & err) const; + +private: + std::shared_ptr m_ptr; +}; + +// Internal class hierarchy - JsonValue objects are not exposed to users of this API. +class JsonValue { +protected: + friend class Json; + friend class JsonInt; + friend class JsonDouble; + virtual Json::Type type() const = 0; + virtual bool equals(const JsonValue * other) const = 0; + virtual bool less(const JsonValue * other) const = 0; + virtual void dump(std::string &out) const = 0; + virtual double number_value() const; + virtual int int_value() const; + virtual bool bool_value() const; + virtual const std::string &string_value() const; + virtual const Json::array &array_items() const; + virtual const Json &operator[](size_t i) const; + virtual const Json::object &object_items() const; + virtual const Json &operator[](const std::string &key) const; + virtual ~JsonValue() {} +}; + +} // namespace json11 diff --git a/CMakeLists.txt b/CMakeLists.txt index e8fa4ec0..54da5bdd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,7 +191,7 @@ if (BUILD_PYTHON) endif () endif() -include_directories(common/ json/ frontend/ ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}) +include_directories(common/ json/ frontend/ 3rdparty/json11/ ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}) if(BUILD_HEAP) find_package (Eigen3 REQUIRED NO_MODULE) @@ -202,9 +202,10 @@ endif() aux_source_directory(common/ COMMON_SRC_FILES) aux_source_directory(json/ JSON_PARSER_FILES) +aux_source_directory(3rdparty/json11 EXT_JSON11_FILES) aux_source_directory(frontend/ FRONTEND_FILES) -set(COMMON_FILES ${COMMON_SRC_FILES} ${JSON_PARSER_FILES} ${FRONTEND_FILES}) +set(COMMON_FILES ${COMMON_SRC_FILES} ${EXT_JSON11_FILES} ${JSON_PARSER_FILES} ${FRONTEND_FILES}) set(CMAKE_BUILD_TYPE Release) if(MINGW) -- cgit v1.2.3 From 77cbd70a72997e659bcba70175761b9fb930c5c6 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 13 Nov 2019 18:52:13 +0000 Subject: frontend: JSON implementation of the generic framework Signed-off-by: David Shah --- frontend/frontend_base.h | 38 +++++----- frontend/json_frontend.cc | 189 ++++++++++++++++++++++++++++++++++++++++++++++ frontend/json_frontend.h | 26 +++++++ 3 files changed, 235 insertions(+), 18 deletions(-) create mode 100644 frontend/json_frontend.cc create mode 100644 frontend/json_frontend.h diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h index 35d4409c..426d9431 100644 --- a/frontend/frontend_base.h +++ b/frontend/frontend_base.h @@ -56,11 +56,11 @@ * PortType get_port_dir(const ModulePortDataType &port); * gets the PortType direction of a module port * - * int get_port_offset(const ModulePortDataType &port); - * gets the start bit number of a port + * int get_array_offset(const ModulePortDataType &port); + * gets the start bit number of a port or netname entry * - * bool is_port_upto(const ModulePortDataType &port); - * returns true if a port is an "upto" type port + * bool is_array_upto(const ModulePortDataType &port); + * returns true if a port/net is an "upto" type port or netname entry * * const BitVectorDataType &get_port_bits(const ModulePortDataType &port); * gets the bit vector of a module port @@ -108,6 +108,14 @@ NEXTPNR_NAMESPACE_BEGIN namespace { +// Used for hierarchy resolution +struct ModuleInfo +{ + bool is_top = false, is_blackbox = false, is_whitebox = false; + inline bool is_box() const { return is_blackbox || is_whitebox; } + std::unordered_set instantiated_celltypes; +}; + template struct GenericFrontend { GenericFrontend(Context *ctx, const FrontendType &impl) : ctx(ctx), impl(impl) {} @@ -119,14 +127,6 @@ template struct GenericFrontend using netname_dat_t = typename FrontendType::NetnameDataType; using bitvector_t = typename FrontendType::BitVectorDataType; - // Used for hierarchy resolution - struct ModuleInfo - { - const mod_dat_t *mod_data; - bool is_top = false, is_blackbox = false, is_whitebox = false; - inline bool is_box() const { return is_blackbox || is_whitebox; } - std::unordered_set instantiated_celltypes; - }; std::unordered_map mods; IdString top; @@ -137,7 +137,6 @@ template struct GenericFrontend impl.foreach_module([&](const std::string &name, const mod_dat_t &mod) { IdString mod_id = ctx->id(name); auto &mi = mods[mod_id]; - mi.mod_data = &mod; impl.foreach_attr(mod, [&](const std::string &name, const Property &value) { if (name == "top") mi.is_top = (value.intval != 0); @@ -147,7 +146,7 @@ template struct GenericFrontend mi.is_whitebox = (value.intval != 0); }); impl.foreach_cell(mod, [&](const std::string &name, const cell_dat_t &cell) { - mi.instantiated_cells.insert(ctx->id(impl.get_cell_type(cell))); + mi.instantiated_celltypes.insert(ctx->id(impl.get_cell_type(cell))); }); }); // First of all, see if a top module has been manually specified @@ -230,7 +229,7 @@ template struct GenericFrontend std::unordered_map> port_to_bus; }; - void import_module(HierModuleState &m, mod_dat_t *data) + void import_module(HierModuleState &m, const mod_dat_t &data) { std::vector index_to_net; // Import port connections; for submodules only @@ -289,10 +288,10 @@ template struct GenericFrontend ctx->net_aliases[mergee->name] = base->name; // Update flat index of nets for (auto old_idx : net_old_indices.at(mergee->udata)) { - net_old_indices.at(base).push_back(old_idx); + net_old_indices.at(base->udata).push_back(old_idx); net_flatindex.at(old_idx) = base; } - net_old_indices.at(base).push_back(mergee->udata); + net_old_indices.at(base->udata).push_back(mergee->udata); net_flatindex.at(mergee->udata) = base; net_old_indices.at(mergee->udata).clear(); // Remove merged net from context @@ -349,6 +348,9 @@ template struct GenericFrontend }; } // namespace -template void run_frontend(Context *ctx, const FrontendType &impl) {} +template void run_frontend(Context *ctx, const FrontendType &impl) +{ + GenericFrontend(ctx, impl); +} NEXTPNR_NAMESPACE_END \ No newline at end of file diff --git a/frontend/json_frontend.cc b/frontend/json_frontend.cc new file mode 100644 index 00000000..91c9f06a --- /dev/null +++ b/frontend/json_frontend.cc @@ -0,0 +1,189 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2019 David Shah + * + * 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 + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "json_frontend.h" +#include "frontend_base.h" +#include "json11.hpp" +#include "log.h" +#include "nextpnr.h" + +#include + +NEXTPNR_NAMESPACE_BEGIN + +using namespace json11; + +struct JsonFrontendImpl +{ + // See specification in frontend_base.h + JsonFrontendImpl(Json &root) : root(root){}; + Json &root; + typedef const Json &ModuleDataType; + typedef const Json &ModulePortDataType; + typedef const Json &CellDataType; + typedef const Json &NetnameDataType; + typedef const Json::array &BitVectorDataType; + + template void foreach_module(TFunc Func) + { + for (const auto &mod : root.object_items()) + Func(mod.first, mod.second); + } + + template void foreach_port(const ModuleDataType &mod, TFunc Func) + { + const auto &ports = mod["ports"]; + if (ports.is_null()) + return; + for (const auto &port : ports.object_items()) + Func(port.first, port.second); + } + + template void foreach_cell(const ModuleDataType &mod, TFunc Func) + { + const auto &cells = mod["cells"]; + if (cells.is_null()) + return; + for (const auto &cell : cells.object_items()) + Func(cell.first, cell.second); + } + + template void foreach_netname(const ModuleDataType &mod, TFunc Func) + { + const auto &netnames = mod["netnames"]; + if (netnames.is_null()) + return; + for (const auto &netname : netnames.object_items()) + Func(netname.first, netname.second); + } + + PortType lookup_portdir(const std::string &dir) + { + if (dir == "input") + return PORT_IN; + else if (dir == "inout") + return PORT_INOUT; + else if (dir == "output") + return PORT_OUT; + else + NPNR_ASSERT_FALSE("invalid json port direction"); + } + + PortType get_port_dir(const ModulePortDataType &port) { return lookup_portdir(port["direction"].string_value()); } + + int get_array_offset(const Json &obj) + { + auto offset = obj["offset"]; + return offset.is_null() ? 0 : offset.int_value(); + } + + bool is_array_upto(const Json &obj) + { + auto upto = obj["upto"]; + return upto.is_null() ? false : bool(upto.int_value()); + } + + const BitVectorDataType &get_port_bits(const ModulePortDataType &port) { return port["bits"].array_items(); } + + const std::string &get_cell_type(const CellDataType &cell) { return cell["type"].string_value(); } + + Property parse_property(const Json &val) + { + if (val.is_number()) + return Property(val.int_value(), 32); + else + return Property::from_string(val.string_value()); + } + + template void foreach_attr(const Json &obj, TFunc Func) + { + const auto &attrs = obj["attributes"]; + if (attrs.is_null()) + return; + for (const auto &attr : attrs.object_items()) { + Func(attr.first, parse_property(attr.second)); + } + } + + template void foreach_param(const Json &obj, TFunc Func) + { + const auto ¶ms = obj["parameters"]; + if (params.is_null()) + return; + for (const auto ¶m : params.object_items()) { + Func(param.first, parse_property(param.second)); + } + } + + template void foreach_port_dir(const CellDataType &cell, TFunc Func) + { + for (const auto &pdir : cell["port_directions"].object_items()) + Func(pdir.first, lookup_portdir(pdir.second.string_value())); + } + + template void foreach_port_conn(const CellDataType &cell, TFunc Func) + { + for (const auto &pconn : cell["connections"].object_items()) + Func(pconn.first, pconn.second); + } + + template const BitVectorDataType &get_net_bits(const NetnameDataType &net) + { + return net["bits"].array_items(); + } + + int get_vector_length(const BitVectorDataType &bits) { return int(bits.size()); } + + bool is_vector_bit_constant(const BitVectorDataType &bits, int i) + { + NPNR_ASSERT(i < int(bits.size())); + return bits[i].is_string(); + } + + char get_vector_bit_constval(const BitVectorDataType &bits, int i) + { + auto s = bits.at(i).string_value(); + NPNR_ASSERT(s.size() == 1); + return s.at(0); + } + + int get_vector_bit_signal(const BitVectorDataType &bits, int i) + { + NPNR_ASSERT(bits.at(i).is_number()); + return bits.at(i).int_value(); + } +}; + +bool parse_json(std::istream &in, const std::string &filename, Context *ctx) +{ + Json root; + { + if (!in) + log_error("Failed to open JSON file '%s'.\n", filename.c_str()); + std::string json_str((std::istreambuf_iterator(in)), std::istreambuf_iterator()); + std::string error; + root = Json::parse(json_str, error, JsonParse::COMMENTS); + if (root.is_null()) + log_error("Failed to parse JSON file '%s': %s.\n", filename.c_str(), error.c_str()); + } + run_frontend(ctx, JsonFrontendImpl(root)); + return true; +} + +NEXTPNR_NAMESPACE_END \ No newline at end of file diff --git a/frontend/json_frontend.h b/frontend/json_frontend.h new file mode 100644 index 00000000..1e3deb8d --- /dev/null +++ b/frontend/json_frontend.h @@ -0,0 +1,26 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2019 David Shah + * + * 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 + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +bool parse_json(std::istream &in, const std::string &filename, Context *ctx); + +NEXTPNR_NAMESPACE_END \ No newline at end of file -- cgit v1.2.3 From 21b4965966048402424e76027ee9807d071e2b1c Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 14 Nov 2019 19:07:38 +0000 Subject: frontend/base: Functions for net[name] import Signed-off-by: David Shah --- frontend/frontend_base.h | 116 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h index 426d9431..6d0e04c7 100644 --- a/frontend/frontend_base.h +++ b/frontend/frontend_base.h @@ -227,6 +227,8 @@ template struct GenericFrontend return index_to_net_flatindex.at(idx); } std::unordered_map> port_to_bus; + // All of the names given to a net + std::vector> net_names; }; void import_module(HierModuleState &m, const mod_dat_t &data) @@ -238,6 +240,120 @@ template struct GenericFrontend } } + // Multiple labels might refer to the same net. Resolve conflicts for the primary name thus: + // - (toplevel) ports are always preferred + // - names with fewer $ are always prefered + // - between equal $ counts, fewer .s are prefered + // - ties are resolved alphabetically + bool prefer_netlabel(HierModuleState &m, const std::string &a, const std::string &b) + { + if (m.port_to_bus.count(ctx->id(a))) + return true; + if (m.port_to_bus.count(ctx->id(b))) + return false; + + if (b.empty()) + return true; + long a_dollars = std::count(a.begin(), a.end(), '$'), b_dollars = std::count(b.begin(), b.end(), '$'); + if (a_dollars < b_dollars) + return true; + else if (a_dollars > b_dollars) + return false; + long a_dots = std::count(a.begin(), a.end(), '.'), b_dots = std::count(b.begin(), b.end(), '.'); + if (a_dots < b_dots) + return true; + else if (a_dots > b_dots) + return false; + return a < b; + }; + + // Get a net by index in modulestate (not flatindex); creating it if it doesn't already exist + NetInfo *create_or_get_net(HierModuleState &m, int idx) + { + std::string name; + if (idx < int(m.net_names.size()) && !m.net_names.at(idx).empty()) { + // Use the rule above to find the preferred name for a net + name = m.net_names.at(idx).at(0); + for (size_t j = 1; j < m.net_names.at(idx).size(); j++) + if (prefer_netlabel(m.net_names.at(idx).at(j), name)) + name = m.net_names.at(idx).at(j); + } else { + name = "$frontend$" + std::to_string(idx); + } + NetInfo *net = ctx->createNet(unique_name(m.prefix, name, true)); + // Add to the flat index of nets + net->udata = int(net_flatindex.size()); + net_flatindex.push_back(net); + // Add to the module-level index of nets + auto &midx = m.net_by_idx(idx); + // Check we don't try and create more than one net with the same index + NPNR_ASSERT(midx == -1); + midx = net->udata; + // Create aliases for all possible names + if (idx < int(m.net_names.size()) && !m.net_names.at(idx).empty()) { + for (const auto &name : m.net_names.at(idx)) { + IdString name_id = ctx->id(name); + net->aliases.push_back(name_id); + ctx->net_aliases[name_id] = net->name; + } + } else { + net->aliases.push_back(net->name); + ctx->net_aliases[net->name] = net->name; + } + } + + // Get the name of a vector bit given basename; settings and index + std::string get_bit_name(const std::string &base, int index, int length, int offset = 0, bool upto = false) + { + std::string port = base; + if (length == 1 && offset == 0) + return port; + int real_index; + if (upto) + real_index = offset + length - index - 1; // reversed ports like [0:7] + else + real_index = offset + index; // normal 'downto' ports like [7:0] + port += '['; + port += std::to_string(real_index); + port += ']'; + return port; + } + + // Import the netnames section of a module + void import_module_netnames(HierModuleState &m, const mod_dat_t &data) + { + impl.foreach_netname(data, [&](const std::string &basename, const netname_dat_t &nn) { + bool upto = impl.is_array_upto(nn); + int offset = impl.get_array_offset(nn); + const auto &bits = impl.get_net_bits(nn); + int width = impl.get_vector_length(bits); + for (int i = 0; i < width; i++) { + if (impl.is_vector_bit_constant(bits, i)) + continue; + + std::string bit_name = get_bit_name(basename, i, width, offset, upto); + + int net_bit = impl.get_vecotr_bit_signal(bits, i); + int mapped_bit = m.net_by_idx(net_bit); + if (mapped_bit == -1) { + // Net doesn't exist yet. Add the name here to the list of candidate names so we have that for when + // we create it later + if (net_bit >= int(m.net_names.size())) + m.net_names.resize(net_bit + 1); + m.net_names.at(net_bit).push_back(bit_name); + } else { + // Net already exists; add this name as an alias + NetInfo *ni = net_flatindex.at(mapped_bit); + IdString alias_name = ctx->id(m.prefix + bit_name); + if (ctx->net_aliases.count(alias_name)) + continue; // don't add duplicate aliases + ctx->net_aliases[alias_name] = ni->name; + ni->aliases.push_back(alias_name); + } + } + }); + } + // Add a constant-driving VCC or GND cell to make a net constant // (constval can be [01xz], x and z or no-ops) int const_autoidx = 0; -- cgit v1.2.3 From bed053251eece5a45e32eebc958fd5f3c683a9c1 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 14 Nov 2019 20:01:45 +0000 Subject: frontend/base: Functions for leaf cell import Signed-off-by: David Shah --- frontend/frontend_base.h | 72 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h index 6d0e04c7..beb0e31a 100644 --- a/frontend/frontend_base.h +++ b/frontend/frontend_base.h @@ -354,6 +354,78 @@ template struct GenericFrontend }); } + void create_constant_net(HierModuleState &m, const std::string name_hint, char constval) + { + IdString name = unique_name(m.base, name_hint); + NetInfo *ni = ctx->createNet(name); + add_constant_driver(m, ni, constval); + } + + // Import a leaf cell - (white|black)box + void import_leaf_cell(HierModuleState &m, const std::string &name, const cell_dat_t &cd) + { + IdString inst_name = unique_name(m.base, name, false); + CellInfo *ci = ctx->createCell(inst_name, ctx->id(get_cell_type(cd))); + // Import port directions + std::unordered_map port_dirs; + impl.foreach_port_dir(cd, [&](const std::string &port, PortType dir) { port_dirs[ctx->id(port)] = dir; }); + // Import port connectivity + impl.foreach_port_conn(cd, [&](const std::string &name, const bitvector_t &bits) { + if (!port_dirs.count(ctx->id(name))) + log_error("Failed to get direction for port '%s' of cell '%s'\n", name.c_str(), inst_name.c_str(ctx)); + PortType dir = port_dirs.at(ctx->id(name)); + int width = impl.get_vector_length(bits); + for (int i = 0; i < width; i++) { + std::string port_bit_name = get_bit_name(name, i, width); + IdString port_bit_ids = ctx->id(port_bit_name); + // Create cell port + ci->ports[port_bit_ids].name = port_bit_ids; + ci->ports[port_bit_ids].type = dir; + // Resolve connectivity + NetInfo *net; + if (impl.is_vector_bit_constant(bits, i)) { + // Create a constant driver if one is needed + net = create_constant_net(m, name + "." + port_bit_name + "$const", + impl.get_vector_bit_constval(bits, i)); + } else { + // Otherwise, lookup (creating if needed) the net with this index + net = create_or_get_net(m, impl.get_vector_bit_signal(bits, i)); + } + NPNR_ASSERT(net != nullptr); + + // Check for multiple drivers + if (dir == PORT_OUT && net->driver.cell != nullptr) + log_error("Net '%s' is multiply driven by cell ports %s.%s and %s.%s\n", ctx->nameOf(net), + ctx->nameOf(net->driver.cell), ctx->nameOf(net->driver.port), ctx->nameOf(inst_name), + port_bit_name.c_str()); + connect_port(ctx, net, ci, port_bit_ids); + } + }); + // Import attributes and parameters + impl.foreach_attr(cd, + [&](const std::string &name, const Property &value) { ci->attrs[ctx->id(name)] = value; }); + impl.foreach_param(cd, + [&](const std::string &name, const Property &value) { ci->params[ctx->id(name)] = value; }); + } + + // Import a submodule cell + void import_submodule_cell(HierModuleState &m, const std::string &name, const cell_dat_t &cd) {} + + // Import the cells section of a module + void import_module_cells(HierModuleState &m, const mod_dat_t &data) + { + m.foreach_cell(data, [&](const std::string &cellname, const cell_dat_t &cd) { + IdString type = ctx->id(get_cell_type(cd)); + if (mods.count(type) && !mods.at(type).is_box()) { + // Module type is known; and not boxed. Import as a submodule by flattening hierarchy + import_submodule_cell(m, cellname, cd); + } else { + // Module type is unknown or boxes. Import as a leaf cell (nextpnr CellInfo) + import_leaf_cell(m, cellname, cd); + } + }); + } + // Add a constant-driving VCC or GND cell to make a net constant // (constval can be [01xz], x and z or no-ops) int const_autoidx = 0; -- cgit v1.2.3 From 98f93f39be7385179036e565fdf9e51eafbce173 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 14 Nov 2019 20:13:57 +0000 Subject: frontend/base: Functions for hierarchical submodule cell import Signed-off-by: David Shah --- frontend/frontend_base.h | 121 +++++++++++++++++++++++++++++------------------ 1 file changed, 76 insertions(+), 45 deletions(-) diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h index beb0e31a..dbf4b4bc 100644 --- a/frontend/frontend_base.h +++ b/frontend/frontend_base.h @@ -128,6 +128,7 @@ template struct GenericFrontend using bitvector_t = typename FrontendType::BitVectorDataType; std::unordered_map mods; + std::unordered_map mod_refs; IdString top; // Process the list of modules and determine @@ -137,6 +138,7 @@ template struct GenericFrontend impl.foreach_module([&](const std::string &name, const mod_dat_t &mod) { IdString mod_id = ctx->id(name); auto &mi = mods[mod_id]; + mod_refs[mod_id] = mod; impl.foreach_attr(mod, [&](const std::string &name, const Property &value) { if (name == "top") mi.is_top = (value.intval != 0); @@ -354,75 +356,104 @@ template struct GenericFrontend }); } - void create_constant_net(HierModuleState &m, const std::string name_hint, char constval) - { + void create_constant_net(HierModuleState &m, const std::string name_hint, char constval) { IdString name = unique_name(m.base, name_hint); NetInfo *ni = ctx->createNet(name); add_constant_driver(m, ni, constval); } // Import a leaf cell - (white|black)box - void import_leaf_cell(HierModuleState &m, const std::string &name, const cell_dat_t &cd) - { + void import_leaf_cell(HierModuleState &m, const std::string &name, const cell_dat_t &cd) { IdString inst_name = unique_name(m.base, name, false); CellInfo *ci = ctx->createCell(inst_name, ctx->id(get_cell_type(cd))); // Import port directions std::unordered_map port_dirs; - impl.foreach_port_dir(cd, [&](const std::string &port, PortType dir) { port_dirs[ctx->id(port)] = dir; }); + impl.foreach_port_dir(cd, [&](const std::string &port, PortType dir) { + port_dirs[ctx->id(port)] = dir; + }); // Import port connectivity impl.foreach_port_conn(cd, [&](const std::string &name, const bitvector_t &bits) { - if (!port_dirs.count(ctx->id(name))) - log_error("Failed to get direction for port '%s' of cell '%s'\n", name.c_str(), inst_name.c_str(ctx)); - PortType dir = port_dirs.at(ctx->id(name)); + if (!port_dirs.count(ctx->id(name))) + log_error("Failed to get direction for port '%s' of cell '%s'\n", name.c_str(), inst_name.c_str(ctx)); + PortType dir = port_dirs.at(ctx->id(name)); + int width = impl.get_vector_length(bits); + for (int i = 0; i < width; i++) { + std::string port_bit_name = get_bit_name(name, i, width); + IdString port_bit_ids = ctx->id(port_bit_name); + // Create cell port + ci->ports[port_bit_ids].name = port_bit_ids; + ci->ports[port_bit_ids].type = dir; + // Resolve connectivity + NetInfo *net; + if (impl.is_vector_bit_constant(bits, i)) { + // Create a constant driver if one is needed + net = create_constant_net(m, name + "." + port_bit_name + "$const", impl.get_vector_bit_constval(bits, i)); + } else { + // Otherwise, lookup (creating if needed) the net with this index + net = create_or_get_net(m, impl.get_vector_bit_signal(bits, i)); + } + NPNR_ASSERT(net != nullptr); + + // Check for multiple drivers + if (dir == PORT_OUT && net->driver.cell != nullptr) + log_error("Net '%s' is multiply driven by cell ports %s.%s and %s.%s\n", ctx->nameOf(net), + ctx->nameOf(net->driver.cell), ctx->nameOf(net->driver.port), ctx->nameOf(inst_name), port_bit_name.c_str()); + connect_port(ctx, net, ci, port_bit_ids); + } + }); + // Import attributes and parameters + impl.foreach_attr(cd, [&](const std::string &name, const Property &value) { + ci->attrs[ctx->id(name)] = value; + }); + impl.foreach_param(cd, [&](const std::string &name, const Property &value) { + ci->params[ctx->id(name)] = value; + }); + } + + // Import a submodule cell + void import_submodule_cell(HierModuleState &m, const std::string &name, const cell_dat_t &cd) { + HierModuleState submod; + submod.is_toplevel = false; + // Create mapping from submodule port to nets (referenced by index in flatindex) + impl.foreach_port_conn(cd, [&](const std::string &name, const bitvector_t &bits) { int width = impl.get_vector_length(bits); for (int i = 0; i < width; i++) { - std::string port_bit_name = get_bit_name(name, i, width); - IdString port_bit_ids = ctx->id(port_bit_name); - // Create cell port - ci->ports[port_bit_ids].name = port_bit_ids; - ci->ports[port_bit_ids].type = dir; - // Resolve connectivity - NetInfo *net; + // Index of port net in flatindex + int net_ref = -1; if (impl.is_vector_bit_constant(bits, i)) { // Create a constant driver if one is needed - net = create_constant_net(m, name + "." + port_bit_name + "$const", - impl.get_vector_bit_constval(bits, i)); + std::string port_bit_name = get_bit_name(name, i, width); + NetInfo *cnet = create_constant_net(m, name + "." + port_bit_name + "$const", impl.get_vector_bit_constval(bits, i)); + cnet->udata = int(net_flatindex.size()); + net_flatindex.push_back(cnet); + net_ref = cnet->udata; } else { - // Otherwise, lookup (creating if needed) the net with this index - net = create_or_get_net(m, impl.get_vector_bit_signal(bits, i)); + // Otherwise, lookup (creating if needed) the net with given in-module index + net_ref = create_or_get_net(m, impl.get_vector_bit_signal(bits, i))->udata; } - NPNR_ASSERT(net != nullptr); - - // Check for multiple drivers - if (dir == PORT_OUT && net->driver.cell != nullptr) - log_error("Net '%s' is multiply driven by cell ports %s.%s and %s.%s\n", ctx->nameOf(net), - ctx->nameOf(net->driver.cell), ctx->nameOf(net->driver.port), ctx->nameOf(inst_name), - port_bit_name.c_str()); - connect_port(ctx, net, ci, port_bit_ids); + NPNR_ASSERT(net_ref != -1); + submod.port_to_bus[ctx->id(name)].push_back(net_ref); } }); - // Import attributes and parameters - impl.foreach_attr(cd, - [&](const std::string &name, const Property &value) { ci->attrs[ctx->id(name)] = value; }); - impl.foreach_param(cd, - [&](const std::string &name, const Property &value) { ci->params[ctx->id(name)] = value; }); + // Create prefix for submodule + submod.prefix = m.prefix; + submod.prefix += name; + submod.prefix += '.'; + // Do the submodule import + import_module(submod, mod_refs.at(ctx->id(impl.get_cell_type(cd)))); } - // Import a submodule cell - void import_submodule_cell(HierModuleState &m, const std::string &name, const cell_dat_t &cd) {} - // Import the cells section of a module - void import_module_cells(HierModuleState &m, const mod_dat_t &data) - { + void import_module_cells(HierModuleState &m, const mod_dat_t &data) { m.foreach_cell(data, [&](const std::string &cellname, const cell_dat_t &cd) { - IdString type = ctx->id(get_cell_type(cd)); - if (mods.count(type) && !mods.at(type).is_box()) { - // Module type is known; and not boxed. Import as a submodule by flattening hierarchy - import_submodule_cell(m, cellname, cd); - } else { - // Module type is unknown or boxes. Import as a leaf cell (nextpnr CellInfo) - import_leaf_cell(m, cellname, cd); - } + IdString type = ctx->id(get_cell_type(cd)); + if (mods.count(type) && !mods.at(type).is_box()) { + // Module type is known; and not boxed. Import as a submodule by flattening hierarchy + import_submodule_cell(m, cellname, cd); + } else { + // Module type is unknown or boxes. Import as a leaf cell (nextpnr CellInfo) + import_leaf_cell(m, cellname, cd); + } }); } -- cgit v1.2.3 From a26b1a276dbb367beec1f07da19224d11109ef51 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 15 Nov 2019 17:43:32 +0000 Subject: frontend: Top level port import Signed-off-by: David Shah --- frontend/frontend_base.h | 210 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 158 insertions(+), 52 deletions(-) diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h index dbf4b4bc..196a28a3 100644 --- a/frontend/frontend_base.h +++ b/frontend/frontend_base.h @@ -119,6 +119,14 @@ struct ModuleInfo template struct GenericFrontend { GenericFrontend(Context *ctx, const FrontendType &impl) : ctx(ctx), impl(impl) {} + void operator()() + { + find_top_module(); + HierModuleState m; + m.is_toplevel = true; + m.prefix = ""; + import_module(m, mod_refs.at(top)); + } Context *ctx; const FrontendType &impl; using mod_dat_t = typename FrontendType::ModuleDataType; @@ -128,7 +136,7 @@ template struct GenericFrontend using bitvector_t = typename FrontendType::BitVectorDataType; std::unordered_map mods; - std::unordered_map mod_refs; + std::unordered_map mod_refs; IdString top; // Process the list of modules and determine @@ -236,10 +244,18 @@ template struct GenericFrontend void import_module(HierModuleState &m, const mod_dat_t &data) { std::vector index_to_net; - // Import port connections; for submodules only if (!m.is_toplevel) { + // Import port connections; for submodules only import_port_connections(m, data); + } else { + // Just create a list of ports for netname resolution + impl.foreach_port(m, + [&](const std::string &name, const mod_port_dat_t &) { m.port_to_bus[ctx->id(name)]; }); } + import_module_netnames(m, data); + import_module_cells(m, data); + if (m.is_toplevel) + import_toplevel_ports(m, data); } // Multiple labels might refer to the same net. Resolve conflicts for the primary name thus: @@ -356,62 +372,63 @@ template struct GenericFrontend }); } - void create_constant_net(HierModuleState &m, const std::string name_hint, char constval) { + void create_constant_net(HierModuleState &m, const std::string name_hint, char constval) + { IdString name = unique_name(m.base, name_hint); NetInfo *ni = ctx->createNet(name); add_constant_driver(m, ni, constval); } // Import a leaf cell - (white|black)box - void import_leaf_cell(HierModuleState &m, const std::string &name, const cell_dat_t &cd) { + void import_leaf_cell(HierModuleState &m, const std::string &name, const cell_dat_t &cd) + { IdString inst_name = unique_name(m.base, name, false); CellInfo *ci = ctx->createCell(inst_name, ctx->id(get_cell_type(cd))); // Import port directions std::unordered_map port_dirs; - impl.foreach_port_dir(cd, [&](const std::string &port, PortType dir) { - port_dirs[ctx->id(port)] = dir; - }); + impl.foreach_port_dir(cd, [&](const std::string &port, PortType dir) { port_dirs[ctx->id(port)] = dir; }); // Import port connectivity impl.foreach_port_conn(cd, [&](const std::string &name, const bitvector_t &bits) { - if (!port_dirs.count(ctx->id(name))) - log_error("Failed to get direction for port '%s' of cell '%s'\n", name.c_str(), inst_name.c_str(ctx)); - PortType dir = port_dirs.at(ctx->id(name)); - int width = impl.get_vector_length(bits); - for (int i = 0; i < width; i++) { - std::string port_bit_name = get_bit_name(name, i, width); - IdString port_bit_ids = ctx->id(port_bit_name); - // Create cell port - ci->ports[port_bit_ids].name = port_bit_ids; - ci->ports[port_bit_ids].type = dir; - // Resolve connectivity - NetInfo *net; - if (impl.is_vector_bit_constant(bits, i)) { - // Create a constant driver if one is needed - net = create_constant_net(m, name + "." + port_bit_name + "$const", impl.get_vector_bit_constval(bits, i)); - } else { - // Otherwise, lookup (creating if needed) the net with this index - net = create_or_get_net(m, impl.get_vector_bit_signal(bits, i)); - } - NPNR_ASSERT(net != nullptr); - - // Check for multiple drivers - if (dir == PORT_OUT && net->driver.cell != nullptr) - log_error("Net '%s' is multiply driven by cell ports %s.%s and %s.%s\n", ctx->nameOf(net), - ctx->nameOf(net->driver.cell), ctx->nameOf(net->driver.port), ctx->nameOf(inst_name), port_bit_name.c_str()); - connect_port(ctx, net, ci, port_bit_ids); - } + if (!port_dirs.count(ctx->id(name))) + log_error("Failed to get direction for port '%s' of cell '%s'\n", name.c_str(), inst_name.c_str(ctx)); + PortType dir = port_dirs.at(ctx->id(name)); + int width = impl.get_vector_length(bits); + for (int i = 0; i < width; i++) { + std::string port_bit_name = get_bit_name(name, i, width); + IdString port_bit_ids = ctx->id(port_bit_name); + // Create cell port + ci->ports[port_bit_ids].name = port_bit_ids; + ci->ports[port_bit_ids].type = dir; + // Resolve connectivity + NetInfo *net; + if (impl.is_vector_bit_constant(bits, i)) { + // Create a constant driver if one is needed + net = create_constant_net(m, name + "." + port_bit_name + "$const", + impl.get_vector_bit_constval(bits, i)); + } else { + // Otherwise, lookup (creating if needed) the net with this index + net = create_or_get_net(m, impl.get_vector_bit_signal(bits, i)); + } + NPNR_ASSERT(net != nullptr); + + // Check for multiple drivers + if (dir == PORT_OUT && net->driver.cell != nullptr) + log_error("Net '%s' is multiply driven by cell ports %s.%s and %s.%s\n", ctx->nameOf(net), + ctx->nameOf(net->driver.cell), ctx->nameOf(net->driver.port), ctx->nameOf(inst_name), + port_bit_name.c_str()); + connect_port(ctx, net, ci, port_bit_ids); + } }); // Import attributes and parameters - impl.foreach_attr(cd, [&](const std::string &name, const Property &value) { - ci->attrs[ctx->id(name)] = value; - }); - impl.foreach_param(cd, [&](const std::string &name, const Property &value) { - ci->params[ctx->id(name)] = value; - }); + impl.foreach_attr(cd, + [&](const std::string &name, const Property &value) { ci->attrs[ctx->id(name)] = value; }); + impl.foreach_param(cd, + [&](const std::string &name, const Property &value) { ci->params[ctx->id(name)] = value; }); } // Import a submodule cell - void import_submodule_cell(HierModuleState &m, const std::string &name, const cell_dat_t &cd) { + void import_submodule_cell(HierModuleState &m, const std::string &name, const cell_dat_t &cd) + { HierModuleState submod; submod.is_toplevel = false; // Create mapping from submodule port to nets (referenced by index in flatindex) @@ -423,7 +440,8 @@ template struct GenericFrontend if (impl.is_vector_bit_constant(bits, i)) { // Create a constant driver if one is needed std::string port_bit_name = get_bit_name(name, i, width); - NetInfo *cnet = create_constant_net(m, name + "." + port_bit_name + "$const", impl.get_vector_bit_constval(bits, i)); + NetInfo *cnet = create_constant_net(m, name + "." + port_bit_name + "$const", + impl.get_vector_bit_constval(bits, i)); cnet->udata = int(net_flatindex.size()); net_flatindex.push_back(cnet); net_ref = cnet->udata; @@ -444,16 +462,104 @@ template struct GenericFrontend } // Import the cells section of a module - void import_module_cells(HierModuleState &m, const mod_dat_t &data) { + void import_module_cells(HierModuleState &m, const mod_dat_t &data) + { m.foreach_cell(data, [&](const std::string &cellname, const cell_dat_t &cd) { - IdString type = ctx->id(get_cell_type(cd)); - if (mods.count(type) && !mods.at(type).is_box()) { - // Module type is known; and not boxed. Import as a submodule by flattening hierarchy - import_submodule_cell(m, cellname, cd); - } else { - // Module type is unknown or boxes. Import as a leaf cell (nextpnr CellInfo) - import_leaf_cell(m, cellname, cd); - } + IdString type = ctx->id(get_cell_type(cd)); + if (mods.count(type) && !mods.at(type).is_box()) { + // Module type is known; and not boxed. Import as a submodule by flattening hierarchy + import_submodule_cell(m, cellname, cd); + } else { + // Module type is unknown or boxes. Import as a leaf cell (nextpnr CellInfo) + import_leaf_cell(m, cellname, cd); + } + }); + } + + // Create a top level input/output buffer + CellInfo *create_iobuf(NetInfo *net, PortType dir, const std::string &name) + { + // Skip IOBUF insertion if this is a design checkpoint (where they will already exist) + if (ctx->settings.count(ctx->id("synth"))) + return nullptr; + IdString name_id = ctx->id(name); + if (ctx->cells.count(name_id)) + log_error("Cell '%s' of type '%s' with the same name as a top-level IO is not allowed.\n", name.c_str(), + ctx->cells.at(name_id)->type.c_str(ctx)); + CellInfo *iobuf = ctx->createCell(name_id, ctx->id("unknown_iob")); + // Copy attributes from net to IOB + for (auto &attr : net->attrs) + iobuf->attrs[attr.first] = attr.second; + // What we do now depends on port type + if (dir == PORT_IN) { + iobuf->type = ctx->id("$nextpnr_ibuf"); + iobuf->addOutput(ctx->id("O")); + if (net->driver.cell != nullptr) { + CellInfo *drv = net->driver.cell; + if (drv->type != ctx->id("$nextpnr_iobuf")) + log_error("Net '%s' is multiply driven by cell port %s.%s and top level input '%s'.\n", + ctx->nameOf(net), ctx->nameOf(drv), ctx->nameOf(net->driver.port), name.c_str()); + // Special case: input, etc, directly drives inout + // Use the input net of the inout instead + net = drv->ports.at(ctx->id("I")).net; + } + NPNR_ASSERT(net->driver.cell == nullptr); + // Connect IBUF output and net + connect_port(ctx, net, iobuf, ctx->id("O")); + } else if (dir == PORT_OUT) { + iobuf->type = ctx->id("$nextpnr_obuf"); + iobuf->addInput(ctx->id("I")); + // Connect IBUF input and net + connect_port(ctx, net, iobuf, ctx->id("I")); + } else if (dir == PORT_INOUT) { + iobuf->type = ctx->id("$nextpnr_iobuf"); + iobuf->addInput(ctx->id("I")); + iobuf->addOutput(ctx->id("O")); + // Need to bifurcate the net to avoid multiple drivers and split + // the input/output parts of an inout + // Create a new net connecting only the current net's driver and the IOBUF input + // Then use the IOBUF output to drive all of the current net's users + NetInfo *split_iobuf_i = ctx->createNet(unique_name("", "$" + name + "$iobuf_i", true)); + auto drv = net->driver; + if (drv.cell != nullptr) { + disconnect_port(ctx, drv.cell, drv.port); + connect_port(ctx, split_iobuf_i, drv.cell, drv.port); + } + connect_port(ctx, split_iobuf_i, iobuf, ctx->id("I")); + NPNR_ASSERT(net->driver.cell == nullptr); + connect_port(ctx, net, iobuf, ctx->id("O")); + } + + PortInfo pinfo; + pinfo.name = name_id; + pinfo.net = net; + pinfo.type = dir; + ctx->ports[pinfo.name] = pinfo; + + return iobuf; + } + + // Import ports of the top level module + void import_toplevel_ports(HierModuleState &m, const mod_dat_t &data) + { + m.foreach_port(data, [&](const std::string &portname, const mod_port_dat_t &pd) { + const auto &port_bv = impl.get_port_bits(pd); + int offset = impl.get_array_offset(pd); + bool is_upto = impl.is_array_upto(pd); + int width = impl.get_vector_length(port_bv); + PortType dir = impl.get_port_dir(pd); + for (int i = 0; i < width; i++) { + std::string pbit_name = get_bit_name(portname, i, width, offset, is_upto); + NetInfo *port_net = nullptr; + if (impl.is_vector_bit_constant(port_bv, i)) { + // Port bit is constant. Need to create a new constant net. + port_net = create_constant_net(m, pbit_name + "$const", impl.get_vector_bit_constval(port_bv, i)); + } else { + // Port bit is a signal. Need to create/get the associated net + port_net = create_or_get_net(m, impl.get_vector_bit_signal(port_bv, i)); + } + create_iobuf(port_net, dir, pbit_name); + } }); } @@ -569,7 +675,7 @@ template struct GenericFrontend template void run_frontend(Context *ctx, const FrontendType &impl) { - GenericFrontend(ctx, impl); + GenericFrontend(ctx, impl)(); } NEXTPNR_NAMESPACE_END \ No newline at end of file -- cgit v1.2.3 From eb14cf09f4d2c81a3f01f33394b78ec29df44035 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 15 Nov 2019 17:55:02 +0000 Subject: frontend: Fix template compile issues Signed-off-by: David Shah --- frontend/frontend_base.h | 73 ++++++++++++++++++++++++----------------------- frontend/json_frontend.cc | 50 ++++++++++++++++---------------- 2 files changed, 62 insertions(+), 61 deletions(-) diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h index 196a28a3..3484b5b4 100644 --- a/frontend/frontend_base.h +++ b/frontend/frontend_base.h @@ -37,66 +37,66 @@ * * Functions: * - * void foreach_module(Func); + * void foreach_module(Func) const; * calls Func(const std::string &name, const ModuleDataType &mod); * for each module in the netlist * - * void foreach_port(const ModuleDataType &mod, Func); + * void foreach_port(const ModuleDataType &mod, Func) const; * calls Func(const std::string &name, const ModulePortDataType &port); * for each port of mod * - * void foreach_cell(const ModuleDataType &mod, Func); - * calls Func(const std::string &name, const CellDataType &cell); + * void foreach_cell(const ModuleDataType &mod, Func) const; + * calls Func(const std::string &name, const CellDataType &cell) * for each cell of mod * - * void foreach_netname(const ModuleDataType &mod, Func); + * void foreach_netname(const ModuleDataType &mod, Func) const; * calls Func(const std::string &name, const NetnameDataType &cell); * for each netname entry of mod * - * PortType get_port_dir(const ModulePortDataType &port); + * PortType get_port_dir(const ModulePortDataType &port) const; * gets the PortType direction of a module port * - * int get_array_offset(const ModulePortDataType &port); + * int get_array_offset(const ModulePortDataType &port) const; * gets the start bit number of a port or netname entry * - * bool is_array_upto(const ModulePortDataType &port); + * bool is_array_upto(const ModulePortDataType &port) const; * returns true if a port/net is an "upto" type port or netname entry * - * const BitVectorDataType &get_port_bits(const ModulePortDataType &port); + * const BitVectorDataType &get_port_bits(const ModulePortDataType &port) const; * gets the bit vector of a module port * - * const std::string& get_cell_type(const CellDataType &cell); + * const std::string& get_cell_type(const CellDataType &cell) const; * gets the type of a cell * - * void foreach_attr(const {ModuleDataType|CellDataType|ModulePortDataType|NetnameDataType} &obj, Func); + * void foreach_attr(const {ModuleDataType|CellDataType|ModulePortDataType|NetnameDataType} &obj, Func) const; * calls Func(const std::string &name, const Property &value); * for each attribute on a module, cell, module port or net * - * void foreach_param(const CellDataType &obj, Func); + * void foreach_param(const CellDataType &obj, Func) const; * calls Func(const std::string &name, const Property &value); * for each parameter of a cell * - * void foreach_port_dir(const CellDataType &cell, Func); + * void foreach_port_dir(const CellDataType &cell, Func) const; * calls Func(const std::string &name, PortType dir); * for each port direction of a cell * - * void foreach_port_conn(const CellDataType &cell, Func); + * void foreach_port_conn(const CellDataType &cell, Func) const; * calls Func(const std::string &name, const BitVectorDataType &conn); * for each port connection of a cell * - * const BitVectorDataType &get_net_bits(const NetnameDataType &net); + * const BitVectorDataType &get_net_bits(const NetnameDataType &net) const; * gets the BitVector corresponding to the bits entry of a netname field * - * int get_vector_length(const BitVectorDataType &bits); + * int get_vector_length(const BitVectorDataType &bits) const; * gets the length of a BitVector * - * bool is_vector_bit_constant(const BitVectorDataType &bits, int i); + * bool is_vector_bit_constant(const BitVectorDataType &bits, int i) const; * returns true if bit of bits is constant * - * char get_vector_bit_constval(const BitVectorDataType &bits, int i); + * char get_vector_bit_constval(const BitVectorDataType &bits, int i) const; * returns a char [01xz] corresponding to the constant value of bit * - * int get_vector_bit_signal(const BitVectorDataType &bits, int i); + * int get_vector_bit_signal(const BitVectorDataType &bits, int i) const; * returns the signal number of vector bit * */ @@ -121,12 +121,15 @@ template struct GenericFrontend GenericFrontend(Context *ctx, const FrontendType &impl) : ctx(ctx), impl(impl) {} void operator()() { + // Find which module is top find_top_module(); HierModuleState m; m.is_toplevel = true; m.prefix = ""; + // Do the actual import, starting from the top level module import_module(m, mod_refs.at(top)); } + Context *ctx; const FrontendType &impl; using mod_dat_t = typename FrontendType::ModuleDataType; @@ -146,7 +149,7 @@ template struct GenericFrontend impl.foreach_module([&](const std::string &name, const mod_dat_t &mod) { IdString mod_id = ctx->id(name); auto &mi = mods[mod_id]; - mod_refs[mod_id] = mod; + mod_refs.emplace(mod_id, mod); impl.foreach_attr(mod, [&](const std::string &name, const Property &value) { if (name == "top") mi.is_top = (value.intval != 0); @@ -249,7 +252,7 @@ template struct GenericFrontend import_port_connections(m, data); } else { // Just create a list of ports for netname resolution - impl.foreach_port(m, + impl.foreach_port(data, [&](const std::string &name, const mod_port_dat_t &) { m.port_to_bus[ctx->id(name)]; }); } import_module_netnames(m, data); @@ -293,7 +296,7 @@ template struct GenericFrontend // Use the rule above to find the preferred name for a net name = m.net_names.at(idx).at(0); for (size_t j = 1; j < m.net_names.at(idx).size(); j++) - if (prefer_netlabel(m.net_names.at(idx).at(j), name)) + if (prefer_netlabel(m, m.net_names.at(idx).at(j), name)) name = m.net_names.at(idx).at(j); } else { name = "$frontend$" + std::to_string(idx); @@ -318,6 +321,7 @@ template struct GenericFrontend net->aliases.push_back(net->name); ctx->net_aliases[net->name] = net->name; } + return net; } // Get the name of a vector bit given basename; settings and index @@ -351,7 +355,7 @@ template struct GenericFrontend std::string bit_name = get_bit_name(basename, i, width, offset, upto); - int net_bit = impl.get_vecotr_bit_signal(bits, i); + int net_bit = impl.get_vector_bit_signal(bits, i); int mapped_bit = m.net_by_idx(net_bit); if (mapped_bit == -1) { // Net doesn't exist yet. Add the name here to the list of candidate names so we have that for when @@ -372,18 +376,20 @@ template struct GenericFrontend }); } - void create_constant_net(HierModuleState &m, const std::string name_hint, char constval) + // Create a new constant net; given a hint for what the name should be and its value + NetInfo *create_constant_net(HierModuleState &m, const std::string &name_hint, char constval) { - IdString name = unique_name(m.base, name_hint); + IdString name = unique_name(m.prefix, name_hint, true); NetInfo *ni = ctx->createNet(name); add_constant_driver(m, ni, constval); + return ni; } // Import a leaf cell - (white|black)box void import_leaf_cell(HierModuleState &m, const std::string &name, const cell_dat_t &cd) { - IdString inst_name = unique_name(m.base, name, false); - CellInfo *ci = ctx->createCell(inst_name, ctx->id(get_cell_type(cd))); + IdString inst_name = unique_name(m.prefix, name, false); + CellInfo *ci = ctx->createCell(inst_name, ctx->id(impl.get_cell_type(cd))); // Import port directions std::unordered_map port_dirs; impl.foreach_port_dir(cd, [&](const std::string &port, PortType dir) { port_dirs[ctx->id(port)] = dir; }); @@ -464,8 +470,8 @@ template struct GenericFrontend // Import the cells section of a module void import_module_cells(HierModuleState &m, const mod_dat_t &data) { - m.foreach_cell(data, [&](const std::string &cellname, const cell_dat_t &cd) { - IdString type = ctx->id(get_cell_type(cd)); + impl.foreach_cell(data, [&](const std::string &cellname, const cell_dat_t &cd) { + IdString type = ctx->id(impl.get_cell_type(cd)); if (mods.count(type) && !mods.at(type).is_box()) { // Module type is known; and not boxed. Import as a submodule by flattening hierarchy import_submodule_cell(m, cellname, cd); @@ -542,7 +548,7 @@ template struct GenericFrontend // Import ports of the top level module void import_toplevel_ports(HierModuleState &m, const mod_dat_t &data) { - m.foreach_port(data, [&](const std::string &portname, const mod_port_dat_t &pd) { + impl.foreach_port(data, [&](const std::string &portname, const mod_port_dat_t &pd) { const auto &port_bv = impl.get_port_bits(pd); int offset = impl.get_array_offset(pd); bool is_upto = impl.is_array_upto(pd); @@ -650,7 +656,7 @@ template struct GenericFrontend // Inputs cannot be driving a constant back to the parent if (dir == PORT_IN) log_error("Input port %s%s[%d] cannot be driving a constant '%c'.\n", m.prefix.c_str(), - port.c_str(), i, constval); + name.c_str(), i, constval); // Insert the constant driver add_constant_driver(m, conn_ni, constval); } else { @@ -673,9 +679,4 @@ template struct GenericFrontend }; } // namespace -template void run_frontend(Context *ctx, const FrontendType &impl) -{ - GenericFrontend(ctx, impl)(); -} - NEXTPNR_NAMESPACE_END \ No newline at end of file diff --git a/frontend/json_frontend.cc b/frontend/json_frontend.cc index 91c9f06a..37c1dabd 100644 --- a/frontend/json_frontend.cc +++ b/frontend/json_frontend.cc @@ -40,13 +40,13 @@ struct JsonFrontendImpl typedef const Json &NetnameDataType; typedef const Json::array &BitVectorDataType; - template void foreach_module(TFunc Func) + template void foreach_module(TFunc Func) const { for (const auto &mod : root.object_items()) Func(mod.first, mod.second); } - template void foreach_port(const ModuleDataType &mod, TFunc Func) + template void foreach_port(const ModuleDataType &mod, TFunc Func) const { const auto &ports = mod["ports"]; if (ports.is_null()) @@ -55,7 +55,7 @@ struct JsonFrontendImpl Func(port.first, port.second); } - template void foreach_cell(const ModuleDataType &mod, TFunc Func) + template void foreach_cell(const ModuleDataType &mod, TFunc Func) const { const auto &cells = mod["cells"]; if (cells.is_null()) @@ -64,7 +64,7 @@ struct JsonFrontendImpl Func(cell.first, cell.second); } - template void foreach_netname(const ModuleDataType &mod, TFunc Func) + template void foreach_netname(const ModuleDataType &mod, TFunc Func) const { const auto &netnames = mod["netnames"]; if (netnames.is_null()) @@ -73,7 +73,7 @@ struct JsonFrontendImpl Func(netname.first, netname.second); } - PortType lookup_portdir(const std::string &dir) + PortType lookup_portdir(const std::string &dir) const { if (dir == "input") return PORT_IN; @@ -85,25 +85,28 @@ struct JsonFrontendImpl NPNR_ASSERT_FALSE("invalid json port direction"); } - PortType get_port_dir(const ModulePortDataType &port) { return lookup_portdir(port["direction"].string_value()); } + PortType get_port_dir(const ModulePortDataType &port) const + { + return lookup_portdir(port["direction"].string_value()); + } - int get_array_offset(const Json &obj) + int get_array_offset(const Json &obj) const { auto offset = obj["offset"]; return offset.is_null() ? 0 : offset.int_value(); } - bool is_array_upto(const Json &obj) + bool is_array_upto(const Json &obj) const { auto upto = obj["upto"]; return upto.is_null() ? false : bool(upto.int_value()); } - const BitVectorDataType &get_port_bits(const ModulePortDataType &port) { return port["bits"].array_items(); } + const BitVectorDataType &get_port_bits(const ModulePortDataType &port) const { return port["bits"].array_items(); } - const std::string &get_cell_type(const CellDataType &cell) { return cell["type"].string_value(); } + const std::string &get_cell_type(const CellDataType &cell) const { return cell["type"].string_value(); } - Property parse_property(const Json &val) + Property parse_property(const Json &val) const { if (val.is_number()) return Property(val.int_value(), 32); @@ -111,7 +114,7 @@ struct JsonFrontendImpl return Property::from_string(val.string_value()); } - template void foreach_attr(const Json &obj, TFunc Func) + template void foreach_attr(const Json &obj, TFunc Func) const { const auto &attrs = obj["attributes"]; if (attrs.is_null()) @@ -121,7 +124,7 @@ struct JsonFrontendImpl } } - template void foreach_param(const Json &obj, TFunc Func) + template void foreach_param(const Json &obj, TFunc Func) const { const auto ¶ms = obj["parameters"]; if (params.is_null()) @@ -131,39 +134,36 @@ struct JsonFrontendImpl } } - template void foreach_port_dir(const CellDataType &cell, TFunc Func) + template void foreach_port_dir(const CellDataType &cell, TFunc Func) const { for (const auto &pdir : cell["port_directions"].object_items()) Func(pdir.first, lookup_portdir(pdir.second.string_value())); } - template void foreach_port_conn(const CellDataType &cell, TFunc Func) + template void foreach_port_conn(const CellDataType &cell, TFunc Func) const { for (const auto &pconn : cell["connections"].object_items()) - Func(pconn.first, pconn.second); + Func(pconn.first, pconn.second.array_items()); } - template const BitVectorDataType &get_net_bits(const NetnameDataType &net) - { - return net["bits"].array_items(); - } + const BitVectorDataType &get_net_bits(const NetnameDataType &net) const { return net["bits"].array_items(); } - int get_vector_length(const BitVectorDataType &bits) { return int(bits.size()); } + int get_vector_length(const BitVectorDataType &bits) const { return int(bits.size()); } - bool is_vector_bit_constant(const BitVectorDataType &bits, int i) + bool is_vector_bit_constant(const BitVectorDataType &bits, int i) const { NPNR_ASSERT(i < int(bits.size())); return bits[i].is_string(); } - char get_vector_bit_constval(const BitVectorDataType &bits, int i) + char get_vector_bit_constval(const BitVectorDataType &bits, int i) const { auto s = bits.at(i).string_value(); NPNR_ASSERT(s.size() == 1); return s.at(0); } - int get_vector_bit_signal(const BitVectorDataType &bits, int i) + int get_vector_bit_signal(const BitVectorDataType &bits, int i) const { NPNR_ASSERT(bits.at(i).is_number()); return bits.at(i).int_value(); @@ -182,7 +182,7 @@ bool parse_json(std::istream &in, const std::string &filename, Context *ctx) if (root.is_null()) log_error("Failed to parse JSON file '%s': %s.\n", filename.c_str(), error.c_str()); } - run_frontend(ctx, JsonFrontendImpl(root)); + GenericFrontend(ctx, JsonFrontendImpl(root))(); return true; } -- cgit v1.2.3 From 9e6770af9086326b3d7c0cb29e8b144fb3578369 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 15 Nov 2019 17:59:42 +0000 Subject: command: Use new frontend experimentally Signed-off-by: David Shah --- common/command.cc | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/common/command.cc b/common/command.cc index 13cd3498..c75f91ef 100644 --- a/common/command.cc +++ b/common/command.cc @@ -35,7 +35,7 @@ #include #include "command.h" #include "design_utils.h" -#include "frontend_base.h" +#include "json_frontend.h" #include "jsonparse.h" #include "jsonwrite.h" #include "log.h" @@ -266,9 +266,13 @@ int CommandHandler::executeMain(std::unique_ptr ctx) if (vm.count("json")) { std::string filename = vm["json"].as(); std::ifstream f(filename); +#ifdef LEGACY_FRONTEND if (!parse_json_file(f, filename, w.getContext())) log_error("Loading design failed.\n"); - +#else + if (!parse_json(f, filename, w.getContext())) + log_error("Loading design failed.\n"); +#endif customAfterLoad(w.getContext()); w.notifyChangeContext(); w.updateActions(); @@ -285,8 +289,13 @@ int CommandHandler::executeMain(std::unique_ptr ctx) if (vm.count("json")) { std::string filename = vm["json"].as(); std::ifstream f(filename); +#ifdef LEGACY_FRONTEND if (!parse_json_file(f, filename, ctx.get())) log_error("Loading design failed.\n"); +#else + if (!parse_json(f, filename, ctx.get())) + log_error("Loading design failed.\n"); +#endif customAfterLoad(ctx.get()); } -- cgit v1.2.3 From 3e21f894f4cbf843fbf3c9d1603886e63f2a8d5b Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 15 Nov 2019 18:04:02 +0000 Subject: frontend: Improved error handling and fixes Signed-off-by: David Shah --- frontend/frontend_base.h | 9 ++++++++- frontend/json_frontend.cc | 4 ++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h index 3484b5b4..40d03863 100644 --- a/frontend/frontend_base.h +++ b/frontend/frontend_base.h @@ -104,6 +104,7 @@ #include "design_utils.h" #include "log.h" #include "nextpnr.h" +#include "util.h" NEXTPNR_NAMESPACE_BEGIN namespace { @@ -193,8 +194,14 @@ template struct GenericFrontend for (auto &mod : mods) for (auto &c : mod.second.instantiated_celltypes) candidate_top.erase(c); - if (candidate_top.size() != 1) + if (candidate_top.size() != 1) { + if (candidate_top.size() == 0) + log_info("No candidate top level modules.\n"); + else + for (auto ctp : sorted(candidate_top)) + log_info("Candidate top module: '%s'\n", ctx->nameOf(ctp)); log_error("Failed to autodetect top module, please specify using --top.\n"); + } top = *(candidate_top.begin()); } diff --git a/frontend/json_frontend.cc b/frontend/json_frontend.cc index 37c1dabd..2eb0a39b 100644 --- a/frontend/json_frontend.cc +++ b/frontend/json_frontend.cc @@ -181,6 +181,10 @@ bool parse_json(std::istream &in, const std::string &filename, Context *ctx) root = Json::parse(json_str, error, JsonParse::COMMENTS); if (root.is_null()) log_error("Failed to parse JSON file '%s': %s.\n", filename.c_str(), error.c_str()); + root = root["modules"]; + if (root.is_null()) + log_error("JSON file '%s' doesn't look like a netlist (doesn't contain \"modules\" key)\n", + filename.c_str()); } GenericFrontend(ctx, JsonFrontendImpl(root))(); return true; -- cgit v1.2.3 From c9a0033c5c7d5bfcf75df3afb0f877e61a1d0df5 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 15 Nov 2019 18:08:27 +0000 Subject: frontend/base: Fix lookup of nets by module index Signed-off-by: David Shah --- frontend/frontend_base.h | 58 +++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h index 40d03863..35558d2f 100644 --- a/frontend/frontend_base.h +++ b/frontend/frontend_base.h @@ -298,37 +298,39 @@ template struct GenericFrontend // Get a net by index in modulestate (not flatindex); creating it if it doesn't already exist NetInfo *create_or_get_net(HierModuleState &m, int idx) { - std::string name; - if (idx < int(m.net_names.size()) && !m.net_names.at(idx).empty()) { - // Use the rule above to find the preferred name for a net - name = m.net_names.at(idx).at(0); - for (size_t j = 1; j < m.net_names.at(idx).size(); j++) - if (prefer_netlabel(m, m.net_names.at(idx).at(j), name)) - name = m.net_names.at(idx).at(j); - } else { - name = "$frontend$" + std::to_string(idx); - } - NetInfo *net = ctx->createNet(unique_name(m.prefix, name, true)); - // Add to the flat index of nets - net->udata = int(net_flatindex.size()); - net_flatindex.push_back(net); - // Add to the module-level index of nets auto &midx = m.net_by_idx(idx); - // Check we don't try and create more than one net with the same index - NPNR_ASSERT(midx == -1); - midx = net->udata; - // Create aliases for all possible names - if (idx < int(m.net_names.size()) && !m.net_names.at(idx).empty()) { - for (const auto &name : m.net_names.at(idx)) { - IdString name_id = ctx->id(name); - net->aliases.push_back(name_id); - ctx->net_aliases[name_id] = net->name; - } + if (midx != -1) { + return net_flatindex.at(midx); } else { - net->aliases.push_back(net->name); - ctx->net_aliases[net->name] = net->name; + std::string name; + if (idx < int(m.net_names.size()) && !m.net_names.at(idx).empty()) { + // Use the rule above to find the preferred name for a net + name = m.net_names.at(idx).at(0); + for (size_t j = 1; j < m.net_names.at(idx).size(); j++) + if (prefer_netlabel(m, m.net_names.at(idx).at(j), name)) + name = m.net_names.at(idx).at(j); + } else { + name = "$frontend$" + std::to_string(idx); + } + NetInfo *net = ctx->createNet(unique_name(m.prefix, name, true)); + // Add to the flat index of nets + net->udata = int(net_flatindex.size()); + net_flatindex.push_back(net); + // Add to the module-level index of netsd + midx = net->udata; + // Create aliases for all possible names + if (idx < int(m.net_names.size()) && !m.net_names.at(idx).empty()) { + for (const auto &name : m.net_names.at(idx)) { + IdString name_id = ctx->id(name); + net->aliases.push_back(name_id); + ctx->net_aliases[name_id] = net->name; + } + } else { + net->aliases.push_back(net->name); + ctx->net_aliases[net->name] = net->name; + } + return net; } - return net; } // Get the name of a vector bit given basename; settings and index -- cgit v1.2.3 From 28279b18fe219c182a97efa992f022c8d243ae28 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 18 Nov 2019 15:07:19 +0000 Subject: frontend/generic: Fix regressions Signed-off-by: David Shah --- frontend/frontend_base.h | 46 +++++++++++++++++++++++++++------------------- frontend/json_frontend.cc | 2 +- frontend/json_frontend.h | 2 +- ice40/pack.cc | 4 +++- 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h index 35558d2f..d55e0329 100644 --- a/frontend/frontend_base.h +++ b/frontend/frontend_base.h @@ -538,6 +538,7 @@ template struct GenericFrontend auto drv = net->driver; if (drv.cell != nullptr) { disconnect_port(ctx, drv.cell, drv.port); + drv.cell->ports[drv.port].net = nullptr; connect_port(ctx, split_iobuf_i, drv.cell, drv.port); } connect_port(ctx, split_iobuf_i, iobuf, ctx->id("I")); @@ -557,25 +558,32 @@ template struct GenericFrontend // Import ports of the top level module void import_toplevel_ports(HierModuleState &m, const mod_dat_t &data) { - impl.foreach_port(data, [&](const std::string &portname, const mod_port_dat_t &pd) { - const auto &port_bv = impl.get_port_bits(pd); - int offset = impl.get_array_offset(pd); - bool is_upto = impl.is_array_upto(pd); - int width = impl.get_vector_length(port_bv); - PortType dir = impl.get_port_dir(pd); - for (int i = 0; i < width; i++) { - std::string pbit_name = get_bit_name(portname, i, width, offset, is_upto); - NetInfo *port_net = nullptr; - if (impl.is_vector_bit_constant(port_bv, i)) { - // Port bit is constant. Need to create a new constant net. - port_net = create_constant_net(m, pbit_name + "$const", impl.get_vector_bit_constval(port_bv, i)); - } else { - // Port bit is a signal. Need to create/get the associated net - port_net = create_or_get_net(m, impl.get_vector_bit_signal(port_bv, i)); + // For correct handling of inout ports driving other ports + // first import non-inouts then import inouts so that they bifurcate correctly + for (bool inout : {false, true}) { + impl.foreach_port(data, [&](const std::string &portname, const mod_port_dat_t &pd) { + const auto &port_bv = impl.get_port_bits(pd); + int offset = impl.get_array_offset(pd); + bool is_upto = impl.is_array_upto(pd); + int width = impl.get_vector_length(port_bv); + PortType dir = impl.get_port_dir(pd); + if ((dir == PORT_INOUT) != inout) + return; + for (int i = 0; i < width; i++) { + std::string pbit_name = get_bit_name(portname, i, width, offset, is_upto); + NetInfo *port_net = nullptr; + if (impl.is_vector_bit_constant(port_bv, i)) { + // Port bit is constant. Need to create a new constant net. + port_net = + create_constant_net(m, pbit_name + "$const", impl.get_vector_bit_constval(port_bv, i)); + } else { + // Port bit is a signal. Need to create/get the associated net + port_net = create_or_get_net(m, impl.get_vector_bit_signal(port_bv, i)); + } + create_iobuf(port_net, dir, pbit_name); } - create_iobuf(port_net, dir, pbit_name); - } - }); + }); + } } // Add a constant-driving VCC or GND cell to make a net constant @@ -688,4 +696,4 @@ template struct GenericFrontend }; } // namespace -NEXTPNR_NAMESPACE_END \ No newline at end of file +NEXTPNR_NAMESPACE_END diff --git a/frontend/json_frontend.cc b/frontend/json_frontend.cc index 2eb0a39b..0debd9f2 100644 --- a/frontend/json_frontend.cc +++ b/frontend/json_frontend.cc @@ -190,4 +190,4 @@ bool parse_json(std::istream &in, const std::string &filename, Context *ctx) return true; } -NEXTPNR_NAMESPACE_END \ No newline at end of file +NEXTPNR_NAMESPACE_END diff --git a/frontend/json_frontend.h b/frontend/json_frontend.h index 1e3deb8d..4d6c28f7 100644 --- a/frontend/json_frontend.h +++ b/frontend/json_frontend.h @@ -23,4 +23,4 @@ NEXTPNR_NAMESPACE_BEGIN bool parse_json(std::istream &in, const std::string &filename, Context *ctx); -NEXTPNR_NAMESPACE_END \ No newline at end of file +NEXTPNR_NAMESPACE_END diff --git a/ice40/pack.cc b/ice40/pack.cc index 90c6de31..cb430eb9 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -527,7 +527,9 @@ static void pack_io(Context *ctx) std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(sb->attrs, sb->attrs.begin())); } else if (is_sb_io(ctx, ci) || is_sb_gb_io(ctx, ci)) { NetInfo *net = ci->ports.at(ctx->id("PACKAGE_PIN")).net; - if ((net != nullptr) && (net->users.size() > 1)) + if ((net != nullptr) && ((net->users.size() > 2) || + (net->driver.cell != nullptr && + net->driver.cell->type == ctx->id("$nextpnr_obuf") && net->users.size() > 1))) log_error("PACKAGE_PIN of %s '%s' connected to more than a single top level IO.\n", ci->type.c_str(ctx), ci->name.c_str(ctx)); } -- cgit v1.2.3 From 14b18cb6fa8909cbe34bfb01c6ea43a2ccff9617 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 22 Nov 2019 17:29:45 +0000 Subject: frontend: Support for loading settings and nextpnr state Signed-off-by: David Shah --- frontend/frontend_base.h | 18 +++++++++++++++++- frontend/json_frontend.cc | 10 ++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h index d55e0329..c23e2196 100644 --- a/frontend/frontend_base.h +++ b/frontend/frontend_base.h @@ -76,6 +76,10 @@ * calls Func(const std::string &name, const Property &value); * for each parameter of a cell * + * void foreach_setting(const ModuleDataType &obj, Func) const; + * calls Func(const std::string &name, const Property &value); + * for each module-level setting + * * void foreach_port_dir(const CellDataType &cell, Func) const; * calls Func(const std::string &name, PortType dir); * for each port direction of a cell @@ -261,11 +265,23 @@ template struct GenericFrontend // Just create a list of ports for netname resolution impl.foreach_port(data, [&](const std::string &name, const mod_port_dat_t &) { m.port_to_bus[ctx->id(name)]; }); + // Import module-level attributes + impl.foreach_attr( + data, [&](const std::string &name, const Property &value) { ctx->attrs[ctx->id(name)] = value; }); + // Import settings + impl.foreach_setting(data, [&](const std::string &name, const Property &value) { + ctx->settings[ctx->id(name)] = value; + }); } import_module_netnames(m, data); import_module_cells(m, data); - if (m.is_toplevel) + if (m.is_toplevel) { import_toplevel_ports(m, data); + // Mark design as loaded through nextpnr + ctx->settings[ctx->id("synth")] = 1; + // Process nextpnr-specific attributes + ctx->attributesToArchInfo(); + } } // Multiple labels might refer to the same net. Resolve conflicts for the primary name thus: diff --git a/frontend/json_frontend.cc b/frontend/json_frontend.cc index 0debd9f2..d2e6248e 100644 --- a/frontend/json_frontend.cc +++ b/frontend/json_frontend.cc @@ -134,6 +134,16 @@ struct JsonFrontendImpl } } + template void foreach_setting(const Json &obj, TFunc Func) const + { + const auto &settings = obj["settings"]; + if (settings.is_null()) + return; + for (const auto &setting : settings.object_items()) { + Func(setting.first, parse_property(setting.second)); + } + } + template void foreach_port_dir(const CellDataType &cell, TFunc Func) const { for (const auto &pdir : cell["port_directions"].object_items()) -- cgit v1.2.3 From 035bfb0fe501fe5b840e83acaf781f2dffef8513 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 27 Nov 2019 12:11:18 +0000 Subject: json: Remove legacy frontend Signed-off-by: David Shah --- common/command.cc | 24 +- common/pybindings.cc | 4 +- gui/basewindow.cc | 1 - gui/ice40/mainwindow.cc | 1 - ice40/main.cc | 1 - json/jsonparse.cc | 1028 ----------------------------------------------- json/jsonparse.h | 34 -- 7 files changed, 3 insertions(+), 1090 deletions(-) delete mode 100644 json/jsonparse.cc delete mode 100644 json/jsonparse.h diff --git a/common/command.cc b/common/command.cc index c75f91ef..c2f02b27 100644 --- a/common/command.cc +++ b/common/command.cc @@ -36,7 +36,6 @@ #include "command.h" #include "design_utils.h" #include "json_frontend.h" -#include "jsonparse.h" #include "jsonwrite.h" #include "log.h" #include "timing.h" @@ -266,13 +265,8 @@ int CommandHandler::executeMain(std::unique_ptr ctx) if (vm.count("json")) { std::string filename = vm["json"].as(); std::ifstream f(filename); -#ifdef LEGACY_FRONTEND - if (!parse_json_file(f, filename, w.getContext())) - log_error("Loading design failed.\n"); -#else if (!parse_json(f, filename, w.getContext())) log_error("Loading design failed.\n"); -#endif customAfterLoad(w.getContext()); w.notifyChangeContext(); w.updateActions(); @@ -289,13 +283,8 @@ int CommandHandler::executeMain(std::unique_ptr ctx) if (vm.count("json")) { std::string filename = vm["json"].as(); std::ifstream f(filename); -#ifdef LEGACY_FRONTEND - if (!parse_json_file(f, filename, ctx.get())) - log_error("Loading design failed.\n"); -#else if (!parse_json(f, filename, ctx.get())) log_error("Loading design failed.\n"); -#endif customAfterLoad(ctx.get()); } @@ -392,12 +381,6 @@ int CommandHandler::exec() return 0; std::unordered_map values; - if (vm.count("json")) { - std::string filename = vm["json"].as(); - std::ifstream f(filename); - if (!load_json_settings(f, filename, values)) - log_error("Loading design failed.\n"); - } std::unique_ptr ctx = createContext(values); setupContext(ctx.get()); setupArchContext(ctx.get()); @@ -414,17 +397,12 @@ std::unique_ptr CommandHandler::load_json(std::string filename) { vm.clear(); std::unordered_map values; - { - std::ifstream f(filename); - if (!load_json_settings(f, filename, values)) - log_error("Loading design failed.\n"); - } std::unique_ptr ctx = createContext(values); setupContext(ctx.get()); setupArchContext(ctx.get()); { std::ifstream f(filename); - if (!parse_json_file(f, filename, ctx.get())) + if (!parse_json(f, filename, ctx.get())) log_error("Loading design failed.\n"); } customAfterLoad(ctx.get()); diff --git a/common/pybindings.cc b/common/pybindings.cc index 03979233..53830284 100644 --- a/common/pybindings.cc +++ b/common/pybindings.cc @@ -22,7 +22,7 @@ #include "pybindings.h" #include "arch_pybindings.h" -#include "jsonparse.h" +#include "json_frontend.h" #include "log.h" #include "nextpnr.h" @@ -53,7 +53,7 @@ void parse_json_shim(std::string filename, Context &d) std::ifstream inf(filename); if (!inf) throw std::runtime_error("failed to open file " + filename); - parse_json_file(inf, filename, &d); + parse_json(inf, filename, &d); } // Create a new Chip and load design from json file diff --git a/gui/basewindow.cc b/gui/basewindow.cc index 550a4b93..a470335d 100644 --- a/gui/basewindow.cc +++ b/gui/basewindow.cc @@ -28,7 +28,6 @@ #include #include "designwidget.h" #include "fpgaviewwidget.h" -#include "jsonparse.h" #include "jsonwrite.h" #include "log.h" #include "mainwindow.h" diff --git a/gui/ice40/mainwindow.cc b/gui/ice40/mainwindow.cc index ccff2117..dc8a3a23 100644 --- a/gui/ice40/mainwindow.cc +++ b/gui/ice40/mainwindow.cc @@ -27,7 +27,6 @@ #include #include "bitstream.h" #include "design_utils.h" -#include "jsonparse.h" #include "log.h" #include "pcf.h" diff --git a/ice40/main.cc b/ice40/main.cc index 5a5fa0c7..3b512a5a 100644 --- a/ice40/main.cc +++ b/ice40/main.cc @@ -24,7 +24,6 @@ #include "bitstream.h" #include "command.h" #include "design_utils.h" -#include "jsonparse.h" #include "log.h" #include "pcf.h" #include "timing.h" diff --git a/json/jsonparse.cc b/json/jsonparse.cc deleted file mode 100644 index fb712b28..00000000 --- a/json/jsonparse.cc +++ /dev/null @@ -1,1028 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2018 SymbioticEDA - * - * jsonparse.cc -- liberally copied from the yosys file of the same name by - * - * Copyright (C) 2018 Clifford Wolf - * - * 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 - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "jsonparse.h" -#include -#include -#include -#include -#include -#include -#include -#include "nextpnr.h" - -NEXTPNR_NAMESPACE_BEGIN - -extern bool check_all_nets_driven(Context *ctx); - -namespace JsonParser { - -const bool json_debug = false; - -typedef std::string string; - -template int GetSize(const T &obj) { return obj.size(); } - -struct JsonNode -{ - char type; // S=String, N=Number, A=Array, D=Dict - string data_string; - int data_number; - std::vector data_array; - std::map data_dict; - std::vector data_dict_keys; - - JsonNode(std::istream &f, int &lineno) - { - type = 0; - data_number = 0; - - while (1) { - int ch = f.get(); - - if (ch == EOF) - log_error("Unexpected EOF in JSON file.\n"); - - if (ch == '\n') - lineno++; - if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') - continue; - - if (ch == '\"') { - type = 'S'; - - while (1) { - ch = f.get(); - - if (ch == EOF) - log_error("Unexpected EOF in JSON string.\n"); - - if (ch == '\"') - break; - - if (ch == '\\') { - int ch = f.get(); - - if (ch == EOF) - log_error("Unexpected EOF in JSON string.\n"); - } - - data_string += ch; - } - - break; - } - - if (('0' <= ch && ch <= '9') || ('-' == ch)) { - type = 'N'; - if (ch == '-') - data_number = 0; - else - data_number = ch - '0'; - data_string += ch; - - while (1) { - ch = f.get(); - - if (ch == EOF) - break; - - if (ch == '.') - goto parse_real; - - if (ch < '0' || '9' < ch) { - f.unget(); - break; - } - - data_number = data_number * 10 + (ch - '0'); - data_string += ch; - } - - if (data_string[0] == '-') - data_number = -data_number; - data_string = ""; - break; - - parse_real: - type = 'S'; - data_number = 0; - data_string += ch; - - while (1) { - ch = f.get(); - - if (ch == EOF) - break; - - if (ch < '0' || '9' < ch) { - f.unget(); - break; - } - - data_string += ch; - } - - break; - } - - if (ch == '[') { - type = 'A'; - - while (1) { - ch = f.get(); - - if (ch == EOF) - log_error("Unexpected EOF in JSON file.\n"); - - if (ch == '\n') - lineno++; - if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == ',') - continue; - - if (ch == ']') - break; - - f.unget(); - data_array.push_back(new JsonNode(f, lineno)); - } - - break; - } - - if (ch == '{') { - type = 'D'; - - while (1) { - ch = f.get(); - - if (ch == EOF) - log_error("Unexpected EOF in JSON file.\n"); - - if (ch == '\n') - lineno++; - if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == ',') - continue; - - if (ch == '}') - break; - - f.unget(); - JsonNode key(f, lineno); - - while (1) { - ch = f.get(); - - if (ch == EOF) - log_error("Unexpected EOF in JSON file.\n"); - - if (ch == '\n') - lineno++; - if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == ':') - continue; - - f.unget(); - break; - } - - JsonNode *value = new JsonNode(f, lineno); - - if (key.type != 'S') - log_error("Unexpected non-string key in JSON dict, line %d.\n", lineno); - - data_dict[key.data_string] = value; - data_dict_keys.push_back(key.data_string); - } - - break; - } - - log_error("Unexpected character in JSON file, line %d: '%c'\n", lineno, ch); - } - } - - ~JsonNode() - { - for (auto it : data_array) - delete it; - for (auto &it : data_dict) - delete it.second; - } -}; - -inline Property json_parse_attr_param_value(JsonNode *node) -{ - Property value; - - if (node->type == 'S') { - value = Property::from_string(node->data_string); - } else if (node->type == 'N') { - value = Property(node->data_number, 32); - } else if (node->type == 'A') { - log_error("JSON attribute or parameter value is an array.\n"); - } else if (node->type == 'D') { - log_error("JSON attribute or parameter value is a dict.\n"); - } else { - log_abort(); - } - - return value; -} - -void ground_net(Context *ctx, NetInfo *net) -{ - std::unique_ptr cell = std::unique_ptr(new CellInfo); - PortInfo port_info; - PortRef port_ref; - - cell->name = ctx->id(net->name.str(ctx) + ".GND"); - cell->type = ctx->id("GND"); - - port_info.name = ctx->id(cell->name.str(ctx) + "[]"); - port_info.net = net; - port_info.type = PORT_OUT; - - port_ref.cell = cell.get(); - port_ref.port = port_info.name; - - net->driver = port_ref; - - cell->ports[port_info.name] = port_info; - - ctx->cells[cell->name] = std::move(cell); -} - -void vcc_net(Context *ctx, NetInfo *net) -{ - std::unique_ptr cell = std::unique_ptr(new CellInfo); - PortInfo port_info; - PortRef port_ref; - - cell->name = ctx->id(net->name.str(ctx) + ".VCC"); - cell->type = ctx->id("VCC"); - - port_info.name = ctx->id(cell->name.str(ctx) + "[]"); - port_info.net = net; - port_info.type = PORT_OUT; - - port_ref.cell = cell.get(); - port_ref.port = port_info.name; - - net->driver = port_ref; - - cell->ports[port_info.name] = port_info; - - ctx->cells[cell->name] = std::move(cell); -} - -// -// is_blackbox -// -// Checks the JsonNode for an attributes dictionary, with a "blackbox" entry. -// An item is deemed to be a blackbox if this entry exists and if its -// value is not zero. If the item is a black box, this routine will return -// true, false otherwise -bool is_blackbox(JsonNode *node) -{ - JsonNode *attr_node, *bbox_node = nullptr, *wbox_node = nullptr; - - if (node->data_dict.count("attributes") == 0) - return false; - attr_node = node->data_dict.at("attributes"); - if (attr_node == NULL) - return false; - if (attr_node->type != 'D') - return false; - if (GetSize(attr_node->data_dict) == 0) - return false; - if (attr_node->data_dict.count("blackbox")) - bbox_node = attr_node->data_dict.at("blackbox"); - if (attr_node->data_dict.count("whitebox")) - wbox_node = attr_node->data_dict.at("whitebox"); - if (bbox_node == NULL && wbox_node == NULL) - return false; - if (bbox_node && bbox_node->type != 'N') - log_error("JSON module blackbox attribute value is not a number\n"); - if (bbox_node && bbox_node->data_number == 0) - return false; - if (wbox_node && wbox_node->type != 'N') - log_error("JSON module whitebox attribute value is not a number\n"); - if (wbox_node && wbox_node->data_number == 0) - return false; - return true; -} - -void json_import_cell_params(Context *ctx, string &modname, CellInfo *cell, JsonNode *param_node, - std::unordered_map *dest, int param_id) -{ - // - JsonNode *param; - IdString pId; - // - param = param_node->data_dict.at(param_node->data_dict_keys[param_id]); - - pId = ctx->id(param_node->data_dict_keys[param_id]); - (*dest)[pId] = json_parse_attr_param_value(param); - - if (json_debug) - log_info(" Added parameter \'%s\'=%s to cell \'%s\' " - "of module \'%s\'\n", - pId.c_str(ctx), cell->params[pId].as_string().c_str(), cell->name.c_str(ctx), modname.c_str()); -} - -void json_import_net_attrib(Context *ctx, string &modname, NetInfo *net, JsonNode *param_node, - std::unordered_map *dest, int param_id) -{ - // - JsonNode *param; - IdString pId; - // - param = param_node->data_dict.at(param_node->data_dict_keys[param_id]); - - pId = ctx->id(param_node->data_dict_keys[param_id]); - (*dest)[pId] = json_parse_attr_param_value(param); - - if (json_debug) - log_info(" Added parameter \'%s\'=%s to net \'%s\' " - "of module \'%s\'\n", - pId.c_str(ctx), net->attrs[pId].as_string().c_str(), net->name.c_str(ctx), modname.c_str()); -} - -void json_import_top_attrib(Context *ctx, string &modname, JsonNode *param_node, - std::unordered_map *dest, int param_id) -{ - // - JsonNode *param; - IdString pId; - // - param = param_node->data_dict.at(param_node->data_dict_keys[param_id]); - - pId = ctx->id(param_node->data_dict_keys[param_id]); - (*dest)[pId] = json_parse_attr_param_value(param); - - if (json_debug) - log_info(" Added parameter \'%s\'=%s module \'%s\'\n", pId.c_str(ctx), (*dest)[pId].as_string().c_str(), - modname.c_str()); -} - -static int const_net_idx = 0; - -template -void json_import_ports(Context *ctx, const string &modname, const std::vector &netnames, - const string &obj_name, const string &port_name, JsonNode *dir_node, JsonNode *wire_group_node, - bool upto, int start_offset, F visitor) -{ - // Examine a port of a cell or the design. For every bit of the port, - // the connected net will be processed and `visitor` will be called - // with (PortType dir, std::string name, NetInfo *net) - assert(dir_node); - - if (json_debug) - log_info(" Examining port %s, node %s\n", port_name.c_str(), obj_name.c_str()); - - if (!wire_group_node) - log_error("JSON no connection match " - "for port_direction \'%s\' of node \'%s\' " - "in module \'%s\'\n", - port_name.c_str(), obj_name.c_str(), modname.c_str()); - - assert(wire_group_node); - - assert(dir_node->type == 'S'); - assert(wire_group_node->type == 'A'); - - PortInfo port_info; - - port_info.name = ctx->id(port_name); - if (dir_node->data_string.compare("input") == 0) - port_info.type = PORT_IN; - else if (dir_node->data_string.compare("output") == 0) - port_info.type = PORT_OUT; - else if (dir_node->data_string.compare("inout") == 0) - port_info.type = PORT_INOUT; - else - log_error("JSON unknown port direction \'%s\' in node \'%s\' " - "of module \'%s\'\n", - dir_node->data_string.c_str(), obj_name.c_str(), modname.c_str()); - // - // Find an update, or create a net to connect - // to this port. - // - NetInfo *this_net = nullptr; - bool is_bus; - - // - // If this port references a bus, then there will be multiple nets - // connected to it, all specified as part of an array. - // - is_bus = (wire_group_node->data_array.size() > 1); - - // Now loop through all of the connections to this port. - if (wire_group_node->data_array.size() == 0) { - // - // There is/are no connections to this port. - // - // Create the port, but leave the net NULL - - visitor(port_info.type, port_info.name.str(ctx), nullptr); - - if (json_debug) - log_info(" Port \'%s\' has no connection in \'%s\'\n", port_info.name.c_str(ctx), obj_name.c_str()); - - } else - for (int index = 0; index < int(wire_group_node->data_array.size()); index++) { - // - JsonNode *wire_node; - PortInfo this_port; - IdString net_id; - // - wire_node = wire_group_node->data_array[index]; - // - // Pick a name for this port - int ndx = index + start_offset; - if (upto) - ndx = start_offset + wire_group_node->data_array.size() - index - 1; - if (is_bus) - this_port.name = ctx->id(port_info.name.str(ctx) + "[" + std::to_string(ndx) + "]"); - else - this_port.name = port_info.name; - this_port.type = port_info.type; - - if (wire_node->type == 'N') { - int net_num; - - // A simple net, specified by a number - net_num = wire_node->data_number; - if (net_num < int(netnames.size())) - net_id = netnames.at(net_num); - else - net_id = ctx->id(std::to_string(net_num)); - if (ctx->nets.count(net_id) == 0) { - // The net doesn't exist in the design (yet) - // Create in now - - if (json_debug) - log_info(" Generating a new net, \'%d\'\n", net_num); - - std::unique_ptr net = std::unique_ptr(new NetInfo()); - net->name = net_id; - net->driver.cell = NULL; - net->driver.port = IdString(); - ctx->nets[net_id] = std::move(net); - - this_net = ctx->nets[net_id].get(); - } else { - // - // The net already exists within the design. - // We'll connect to it - // - this_net = ctx->nets[net_id].get(); - if (json_debug) - log_info(" Reusing net \'%s\', id \'%s\', " - "with driver \'%s\'\n", - this_net->name.c_str(ctx), net_id.c_str(ctx), - (this_net->driver.cell != NULL) ? this_net->driver.port.c_str(ctx) : "NULL"); - } - - } else if (wire_node->type == 'S') { - // Strings are only used to drive wires for the fixed - // values "0", "1", and "x". Handle those constant - // values here. - // - // Constants always get their own new net - std::unique_ptr net = std::unique_ptr(new NetInfo()); - net->name = ctx->id("$const_" + std::to_string(const_net_idx++)); - - if (wire_node->data_string.compare(string("0")) == 0) { - - if (json_debug) - log_info(" Generating a constant " - "zero net\n"); - ground_net(ctx, net.get()); - - } else if (wire_node->data_string.compare(string("1")) == 0) { - - if (json_debug) - log_info(" Generating a constant " - "one net\n"); - vcc_net(ctx, net.get()); - - } else if (wire_node->data_string.compare(string("x")) == 0) { - ground_net(ctx, net.get()); - } else - log_error(" Unknown fixed type wire node " - "value, \'%s\'\n", - wire_node->data_string.c_str()); - IdString n = net->name; - ctx->nets[net->name] = std::move(net); - this_net = ctx->nets[n].get(); - } - - if (json_debug) - log_info(" Inserting port \'%s\' into cell \'%s\'\n", this_port.name.c_str(ctx), obj_name.c_str()); - visitor(this_port.type, this_port.name.str(ctx), this_net); - } -} - -void json_import_cell(Context *ctx, string modname, const std::vector &netnames, JsonNode *cell_node, - string cell_name) -{ - JsonNode *cell_type, *param_node, *attr_node; - - cell_type = cell_node->data_dict.at("type"); - if (cell_type == NULL) - return; - - std::unique_ptr cell = std::unique_ptr(new CellInfo); - - cell->name = ctx->id(cell_name); - assert(cell_type->type == 'S'); - cell->type = ctx->id(cell_type->data_string); - // No BEL assignment here/yet - - if (json_debug) - log_info(" Processing %s $ %s\n", modname.c_str(), cell->name.c_str(ctx)); - - param_node = cell_node->data_dict.at("parameters"); - if (param_node->type != 'D') - log_error("JSON parameter list of \'%s\' is not a data dictionary\n", cell->name.c_str(ctx)); - - // - // Loop through all parameters, adding them into the - // design to annotate the cell - // - for (int paramid = 0; paramid < GetSize(param_node->data_dict_keys); paramid++) { - - json_import_cell_params(ctx, modname, cell.get(), param_node, &cell->params, paramid); - } - - attr_node = cell_node->data_dict.at("attributes"); - if (attr_node->type != 'D') - log_error("JSON attribute list of \'%s\' is not a data dictionary\n", cell->name.c_str(ctx)); - - // - // Loop through all attributes, adding them into the - // design to annotate the cell - // - for (int attrid = 0; attrid < GetSize(attr_node->data_dict_keys); attrid++) { - - json_import_cell_params(ctx, modname, cell.get(), attr_node, &cell->attrs, attrid); - } - - // - // Now connect the ports of this module. The ports are defined by - // both the port directions node as well as the connections node. - // Both should contain dictionaries having the same keys. - // - - JsonNode *pdir_node = NULL; - if (cell_node->data_dict.count("port_directions") > 0) { - - pdir_node = cell_node->data_dict.at("port_directions"); - if (pdir_node->type != 'D') - log_error("JSON port_directions node of \'%s\' " - "in module \'%s\' is not a " - "dictionary\n", - cell->name.c_str(ctx), modname.c_str()); - - } else if (cell_node->data_dict.count("ports") > 0) { - pdir_node = cell_node->data_dict.at("ports"); - if (pdir_node->type != 'D') - log_error("JSON ports node of \'%s\' " - "in module \'%s\' is not a " - "dictionary\n", - cell->name.c_str(ctx), modname.c_str()); - } - - JsonNode *connections = cell_node->data_dict.at("connections"); - if (connections->type != 'D') - log_error("JSON connections node of \'%s\' " - "in module \'%s\' is not a " - "dictionary\n", - cell->name.c_str(ctx), modname.c_str()); - - if (GetSize(pdir_node->data_dict_keys) != GetSize(connections->data_dict_keys)) - log_error("JSON number of connections doesnt " - "match number of ports in node \'%s\' " - "of module \'%s\'\n", - cell->name.c_str(ctx), modname.c_str()); - - // - // Loop through all of the ports of this logic element - // - for (int portid = 0; portid < GetSize(pdir_node->data_dict_keys); portid++) { - // - string port_name; - JsonNode *dir_node, *wire_group_node; - // - - port_name = pdir_node->data_dict_keys[portid]; - dir_node = pdir_node->data_dict.at(port_name); - wire_group_node = connections->data_dict.at(port_name); - - json_import_ports(ctx, modname, netnames, cell->name.str(ctx), port_name, dir_node, wire_group_node, false, 0, - [&cell, ctx](PortType type, const std::string &name, NetInfo *net) { - cell->ports[ctx->id(name)] = PortInfo{ctx->id(name), net, type}; - PortRef pr; - pr.cell = cell.get(); - pr.port = ctx->id(name); - if (net != nullptr) { - if (type == PORT_IN || type == PORT_INOUT) { - net->users.push_back(pr); - } else if (type == PORT_OUT) { - if (net->driver.cell != nullptr) - log_error("multiple drivers on net '%s' (%s.%s and %s.%s)\n", - net->name.c_str(ctx), net->driver.cell->name.c_str(ctx), - net->driver.port.c_str(ctx), pr.cell->name.c_str(ctx), - pr.port.c_str(ctx)); - net->driver = pr; - } - } - }); - } - - ctx->cells[cell->name] = std::move(cell); - // check_all_nets_driven(ctx); -} - -static void insert_iobuf(Context *ctx, NetInfo *net, PortType type, const string &name) -{ - // Instantiate a architecture-independent IO buffer connected to a given - // net, of a given type, and named after the IO port. - // - // During packing, this generic IO buffer will be converted to an - // architecure primitive. - // - if (ctx->settings.find(ctx->id("synth")) == ctx->settings.end()) { - std::unique_ptr iobuf = std::unique_ptr(new CellInfo()); - iobuf->name = ctx->id(name); - std::copy(net->attrs.begin(), net->attrs.end(), std::inserter(iobuf->attrs, iobuf->attrs.begin())); - if (type == PORT_IN) { - if (ctx->verbose) - log_info("processing input port %s\n", name.c_str()); - iobuf->type = ctx->id("$nextpnr_ibuf"); - iobuf->ports[ctx->id("O")] = PortInfo{ctx->id("O"), net, PORT_OUT}; - // Special case: input, etc, directly drives inout - if (net->driver.cell != nullptr) { - if (net->driver.cell->type != ctx->id("$nextpnr_iobuf")) - log_error("Top-level input '%s' also driven by %s.%s.\n", name.c_str(), - net->driver.cell->name.c_str(ctx), net->driver.port.c_str(ctx)); - net = net->driver.cell->ports.at(ctx->id("I")).net; - } - assert(net->driver.cell == nullptr); - net->driver.port = ctx->id("O"); - net->driver.cell = iobuf.get(); - } else if (type == PORT_OUT) { - if (ctx->verbose) - log_info("processing output port %s\n", name.c_str()); - iobuf->type = ctx->id("$nextpnr_obuf"); - iobuf->ports[ctx->id("I")] = PortInfo{ctx->id("I"), net, PORT_IN}; - PortRef ref; - ref.cell = iobuf.get(); - ref.port = ctx->id("I"); - net->users.push_back(ref); - } else if (type == PORT_INOUT) { - if (ctx->verbose) - log_info("processing inout port %s\n", name.c_str()); - iobuf->type = ctx->id("$nextpnr_iobuf"); - iobuf->ports[ctx->id("I")] = PortInfo{ctx->id("I"), nullptr, PORT_IN}; - - // Split the input and output nets for bidir ports - std::unique_ptr net2 = std::unique_ptr(new NetInfo()); - net2->name = ctx->id("$" + net->name.str(ctx) + "$iobuf_i"); - net2->driver = net->driver; - if (net->driver.cell != nullptr) { - net2->driver.cell->ports[net2->driver.port].net = net2.get(); - net->driver.cell = nullptr; - } - iobuf->ports[ctx->id("I")].net = net2.get(); - PortRef ref; - ref.cell = iobuf.get(); - ref.port = ctx->id("I"); - net2->users.push_back(ref); - ctx->nets[net2->name] = std::move(net2); - - iobuf->ports[ctx->id("O")] = PortInfo{ctx->id("O"), net, PORT_OUT}; - assert(net->driver.cell == nullptr); - net->driver.port = ctx->id("O"); - net->driver.cell = iobuf.get(); - } else { - assert(false); - } - ctx->cells[iobuf->name] = std::move(iobuf); - } - - PortInfo pinfo; - pinfo.name = ctx->id(name); - pinfo.net = net; - pinfo.type = type; - ctx->ports[pinfo.name] = pinfo; -} - -void json_import_toplevel_port(Context *ctx, const string &modname, const std::vector &netnames, - const string &portname, JsonNode *node) -{ - JsonNode *dir_node = node->data_dict.at("direction"); - JsonNode *nets_node = node->data_dict.at("bits"); - bool upto = false; - int start_offset = 0; - if (node->data_dict.count("upto") != 0) { - JsonNode *val = node->data_dict.at("upto"); - if (val->type == 'N') - upto = val->data_number != 0; - } - if (node->data_dict.count("offset") != 0) { - JsonNode *val = node->data_dict.at("offset"); - if (val->type == 'N') - start_offset = val->data_number; - } - json_import_ports( - ctx, modname, netnames, "Top Level IO", portname, dir_node, nets_node, upto, start_offset, - [ctx](PortType type, const std::string &name, NetInfo *net) { insert_iobuf(ctx, net, type, name); }); -} - -void json_import(Context *ctx, string modname, JsonNode *node) -{ - if (is_blackbox(node)) - return; - - log_info("Importing module %s\n", modname.c_str()); - ctx->attrs[ctx->id("module")] = modname; - JsonNode *attr_node = node->data_dict.at("attributes"); - for (int attrid = 0; attrid < GetSize(attr_node->data_dict_keys); attrid++) { - json_import_top_attrib(ctx, modname, attr_node, &ctx->attrs, attrid); - } - - JsonNode *ports_parent = nullptr; - if (node->data_dict.count("ports") > 0) - ports_parent = node->data_dict.at("ports"); - - // Multiple labels might refer to the same net. For now we resolve conflicts thus: - // - (toplevel) ports are always preferred - // - names with fewer $ are always prefered - // - between equal $ counts, fewer .s are prefered - // - ties are resolved alphabetically - auto prefer_netlabel = [ports_parent](const std::string &a, const std::string &b) { - if (ports_parent != nullptr) { - if (ports_parent->data_dict.count(a)) - return true; - if (ports_parent->data_dict.count(b)) - return false; - } - if (b.empty()) - return true; - long a_dollars = std::count(a.begin(), a.end(), '$'), b_dollars = std::count(b.begin(), b.end(), '$'); - if (a_dollars < b_dollars) - return true; - else if (a_dollars > b_dollars) - return false; - long a_dots = std::count(a.begin(), a.end(), '.'), b_dots = std::count(b.begin(), b.end(), '.'); - if (a_dots < b_dots) - return true; - else if (a_dots > b_dots) - return false; - return a < b; - }; - - // Import netnames - std::vector> netlabels; - if (node->data_dict.count("netnames")) { - JsonNode *cell_parent = node->data_dict.at("netnames"); - for (int nnid = 0; nnid < GetSize(cell_parent->data_dict_keys); nnid++) { - JsonNode *here; - - here = cell_parent->data_dict.at(cell_parent->data_dict_keys[nnid]); - std::string basename = cell_parent->data_dict_keys[nnid]; - bool upto = false; - int start_offset = 0; - if (here->data_dict.count("upto") != 0) { - JsonNode *val = here->data_dict.at("upto"); - if (val->type == 'N') - upto = val->data_number != 0; - } - if (here->data_dict.count("offset") != 0) { - JsonNode *val = here->data_dict.at("offset"); - if (val->type == 'N') - start_offset = val->data_number; - } - if (here->data_dict.count("bits")) { - JsonNode *bits = here->data_dict.at("bits"); - assert(bits->type == 'A'); - size_t num_bits = bits->data_array.size(); - for (size_t i = 0; i < num_bits; i++) { - int netid = bits->data_array.at(i)->data_number; - if (netid >= int(netlabels.size())) - netlabels.resize(netid + 1); - int ndx = i + start_offset; - if (upto) - ndx = start_offset + num_bits - i - 1; - std::string name = - basename + (num_bits == 1 ? "" : std::string("[") + std::to_string(ndx) + std::string("]")); - netlabels.at(netid).push_back(name); - } - } - } - } - std::vector netids; - for (size_t i = 0; i < netlabels.size(); i++) { - auto &labels = netlabels.at(i); - if (labels.empty()) { - // Backup for unnamed nets (not sure if these should actually happen) - netids.push_back(ctx->id("$nextpnr$unknown_netname$" + std::to_string(i))); - } else { - // Pick a primary name for the net according to a simple heuristic - std::string pref = labels.at(0); - for (size_t j = 1; j < labels.size(); j++) - if (prefer_netlabel(labels.at(j), pref)) - pref = labels.at(j); - netids.push_back(ctx->id(pref)); - } - } - if (node->data_dict.count("cells")) { - JsonNode *cell_parent = node->data_dict.at("cells"); - // - // - // Loop through all of the logic elements in a flattened design - // - // - for (int cellid = 0; cellid < GetSize(cell_parent->data_dict_keys); cellid++) { - JsonNode *here = cell_parent->data_dict.at(cell_parent->data_dict_keys[cellid]); - json_import_cell(ctx, modname, netids, here, cell_parent->data_dict_keys[cellid]); - } - } - - if (ports_parent != nullptr) { - // N.B. ports must be imported after cells for tristate behaviour - // to be correct - // Loop through all ports, first non-tristate then tristate to handle - // interconnected ports correctly - for (int portid = 0; portid < GetSize(ports_parent->data_dict_keys); portid++) { - JsonNode *here; - - here = ports_parent->data_dict.at(ports_parent->data_dict_keys[portid]); - JsonNode *dir_node = here->data_dict.at("direction"); - NPNR_ASSERT(dir_node->type == 'S'); - if (dir_node->data_string == "inout") - continue; - json_import_toplevel_port(ctx, modname, netids, ports_parent->data_dict_keys[portid], here); - } - for (int portid = 0; portid < GetSize(ports_parent->data_dict_keys); portid++) { - JsonNode *here; - - here = ports_parent->data_dict.at(ports_parent->data_dict_keys[portid]); - JsonNode *dir_node = here->data_dict.at("direction"); - NPNR_ASSERT(dir_node->type == 'S'); - if (dir_node->data_string != "inout") - continue; - json_import_toplevel_port(ctx, modname, netids, ports_parent->data_dict_keys[portid], here); - } - } - if (node->data_dict.count("netnames")) { - JsonNode *net_parent = node->data_dict.at("netnames"); - for (int nnid = 0; nnid < GetSize(net_parent->data_dict_keys); nnid++) { - JsonNode *here; - - here = net_parent->data_dict.at(net_parent->data_dict_keys[nnid]); - std::string basename = net_parent->data_dict_keys[nnid]; - if (here->data_dict.count("bits")) { - JsonNode *bits = here->data_dict.at("bits"); - assert(bits->type == 'A'); - size_t num_bits = bits->data_array.size(); - for (size_t i = 0; i < num_bits; i++) { - std::string name = - basename + (num_bits == 1 ? "" : std::string("[") + std::to_string(i) + std::string("]")); - IdString net_id = ctx->id(name); - if (here->data_dict.count("attributes") && ctx->nets.find(net_id) != ctx->nets.end()) { - NetInfo *this_net = ctx->nets[net_id].get(); - - JsonNode *attr_node = here->data_dict.at("attributes"); - if (attr_node->type != 'D') - log_error("JSON attribute list of \'%s\' is not a data dictionary\n", - this_net->name.c_str(ctx)); - - // - // Loop through all attributes, adding them into the - // design to annotate the cell - // - for (int attrid = 0; attrid < GetSize(attr_node->data_dict_keys); attrid++) { - json_import_net_attrib(ctx, modname, this_net, attr_node, &this_net->attrs, attrid); - } - } - } - } - } - } - // Import net aliases - for (size_t i = 0; i < netids.size(); i++) { - IdString netname = netids.at(i); - if (!ctx->nets.count(netname)) - continue; - for (auto &label : netlabels.at(i)) { - IdString labelid = ctx->id(label); - NPNR_ASSERT(!ctx->net_aliases.count(labelid)); - ctx->net_aliases[labelid] = netname; - } - } - check_all_nets_driven(ctx); - ctx->settings[ctx->id("synth")] = 1; -} -}; // End Namespace JsonParser - -bool parse_json_file(std::istream &f, std::string &filename, Context *ctx) -{ - try { - using namespace JsonParser; - - if (!f) - log_error("failed to open JSON file.\n"); - - int lineno = 1; - - JsonNode root(f, lineno); - - if (root.type != 'D') - log_error("JSON root node is not a dictionary.\n"); - - if (root.data_dict.count("modules") != 0) { - JsonNode *modules = root.data_dict.at("modules"); - - if (modules->type != 'D') - log_error("JSON modules node is not a dictionary.\n"); - - for (auto &it : modules->data_dict) - json_import(ctx, it.first, it.second); - } - - log_info("Checksum: 0x%08x\n", ctx->checksum()); - log_break(); - ctx->attributesToArchInfo(); - return true; - } catch (log_execution_error_exception) { - return false; - } -} - -bool load_json_settings(std::istream &f, std::string &filename, std::unordered_map &values) -{ - try { - using namespace JsonParser; - - if (!f) - log_error("failed to open JSON file.\n"); - - int lineno = 1; - - JsonNode root(f, lineno); - - if (root.type != 'D') - log_error("JSON root node is not a dictionary.\n"); - - if (root.data_dict.count("modules") != 0) { - JsonNode *modules = root.data_dict.at("modules"); - - if (modules->type != 'D') - log_error("JSON modules node is not a dictionary.\n"); - - for (auto &it : modules->data_dict) { - JsonNode *node = it.second; - if (is_blackbox(node)) - continue; - - if (node->data_dict.count("settings")) { - JsonNode *attr_node = node->data_dict.at("settings"); - for (int attrid = 0; attrid < GetSize(attr_node->data_dict_keys); attrid++) { - JsonNode *param = attr_node->data_dict.at(attr_node->data_dict_keys[attrid]); - std::string pId = attr_node->data_dict_keys[attrid]; - values[pId] = json_parse_attr_param_value(param); - } - } - } - } - - return true; - } catch (log_execution_error_exception) { - return false; - } -} - -NEXTPNR_NAMESPACE_END diff --git a/json/jsonparse.h b/json/jsonparse.h deleted file mode 100644 index 65e3f02e..00000000 --- a/json/jsonparse.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2018 SymbioticEDA - * - * 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 - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef JSON_PARSER -#define JSON_PARSER - -#include -#include -#include "nextpnr.h" - -NEXTPNR_NAMESPACE_BEGIN - -extern bool parse_json_file(std::istream &, std::string &, Context *); -extern bool load_json_settings(std::istream &f, std::string &filename, - std::unordered_map &values); -NEXTPNR_NAMESPACE_END - -#endif -- cgit v1.2.3 From 6bf3c261fa3e13319430380096aca65042476ae3 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 28 Nov 2019 20:45:49 +0000 Subject: First pass at data structures for hierarchy Signed-off-by: David Shah --- common/nextpnr.h | 31 +++++++++++++++++++++++++++++-- docs/netlist.md | 2 ++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/common/nextpnr.h b/common/nextpnr.h index 3fce97a2..ceea2088 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -387,7 +387,7 @@ struct ClockConstraint; struct NetInfo : ArchNetInfo { - IdString name; + IdString name, hierpath; int32_t udata = 0; PortRef driver; @@ -423,7 +423,7 @@ struct PortInfo struct CellInfo : ArchCellInfo { - IdString name, type; + IdString name, type, hierpath; int32_t udata; std::unordered_map ports; @@ -527,6 +527,28 @@ struct TimingConstraint std::unordered_set to; }; +// Represents the contents of a non-leaf cell in a design +// with hierarchy + +struct HierachicalPort +{ + IdString name; + PortType dir; + std::vector nets; + int offset; + bool upto; +}; + +struct HierachicalCell +{ + IdString name, type, parent, fullpath; + // Name inside cell instance -> global name + std::unordered_map leaf_cells, nets; + std::unordered_map ports; + // Name inside cell instance -> global name + std::unordered_map hier_cells; +}; + inline bool operator==(const std::pair &a, const std::pair &b) { @@ -620,6 +642,11 @@ struct BaseCtx std::unordered_map> nets; std::unordered_map> cells; + // Hierarchical (non-leaf) cells by full path + std::unordered_map hierarchy; + // This is the root of the above structure + IdString top_module; + // Aliases for nets, which may have more than one name due to assignments and hierarchy std::unordered_map net_aliases; diff --git a/docs/netlist.md b/docs/netlist.md index 0f9a8969..8886e4f8 100644 --- a/docs/netlist.md +++ b/docs/netlist.md @@ -19,6 +19,7 @@ Other structures used by these basic structures include: `CellInfo` instances have the following fields: - `name` and `type` are `IdString`s containing the instance name, and type + - `hierpath` is name of the hierarchical cell containing the instance, for designs with hierarchy - `ports` is a map from port name `IdString` to `PortInfo` structures for each cell port - `bel` and `belStrength` contain the ID of the Bel the cell is placed onto; and placement strength of the cell; if placed. Placement/ripup should always be done by `Arch::bindBel` and `Arch::unbindBel` rather than by manipulating these fields. - `params` and `attrs` store parameters and attributes - from the input JSON or assigned in flows to add metadata - by mapping from parameter name `IdString` to `Property`. @@ -34,6 +35,7 @@ Other structures used by these basic structures include: `NetInfo` instances have the following fields: - `name` is the IdString name of the net - for nets with multiple names, one name is chosen according to a set of rules by the JSON frontend + - `hierpath` is name of the hierarchical cell containing the instance, for designs with hierarchy - `driver` refers to the source of the net using `PortRef`; `driver.cell == nullptr` means that the net is undriven. Nets must have zero or one driver only. The corresponding cell port must be an output and its `PortInfo::net` must refer back to this net. - `users` contains a list of `PortRef` references to sink ports on the net. Nets can have zero or more sinks. Each corresponding cell port must be an input or inout; and its `PortInfo::net` must refer back to this net. - `wires` is a map that stores the routing tree of a net, if the net is routed. -- cgit v1.2.3 From 9f6031cda13a290903785a1c469af02838309b39 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 29 Nov 2019 15:58:02 +0000 Subject: frontend_base: Import cell hierarchy Signed-off-by: David Shah --- frontend/frontend_base.h | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h index c23e2196..be764266 100644 --- a/frontend/frontend_base.h +++ b/frontend/frontend_base.h @@ -131,8 +131,10 @@ template struct GenericFrontend HierModuleState m; m.is_toplevel = true; m.prefix = ""; + m.path = top; + ctx->top_module = top; // Do the actual import, starting from the top level module - import_module(m, mod_refs.at(top)); + import_module(m, top.str(ctx), top.str(ctx), mod_refs.at(top)); } Context *ctx; @@ -239,6 +241,7 @@ template struct GenericFrontend { bool is_toplevel; std::string prefix; + IdString parent_path, path; // Map from index in module to "flat" index of nets std::vector index_to_net_flatindex; // Get a reference to index_to_net; resizing if @@ -255,8 +258,14 @@ template struct GenericFrontend std::vector> net_names; }; - void import_module(HierModuleState &m, const mod_dat_t &data) + void import_module(HierModuleState &m, const std::string &name, const std::string &type, const mod_dat_t &data) { + NPNR_ASSERT(!ctx->hierarchy.count(m.path)); + ctx->hierarchy[m.path].name = ctx->id(name); + ctx->hierarchy[m.path].type = ctx->id(type); + ctx->hierarchy[m.path].parent = m.parent_path; + ctx->hierarchy[m.path].fullpath = m.path; + std::vector index_to_net; if (!m.is_toplevel) { // Import port connections; for submodules only @@ -414,6 +423,7 @@ template struct GenericFrontend void import_leaf_cell(HierModuleState &m, const std::string &name, const cell_dat_t &cd) { IdString inst_name = unique_name(m.prefix, name, false); + ctx->hierarchy[m.path].leaf_cells[ctx->id(name)] = inst_name; CellInfo *ci = ctx->createCell(inst_name, ctx->id(impl.get_cell_type(cd))); // Import port directions std::unordered_map port_dirs; @@ -488,8 +498,11 @@ template struct GenericFrontend submod.prefix = m.prefix; submod.prefix += name; submod.prefix += '.'; + submod.parent_path = m.path; + submod.path = ctx->id(m.path.str(ctx) + "/" + name); // Do the submodule import - import_module(submod, mod_refs.at(ctx->id(impl.get_cell_type(cd)))); + auto type = impl.get_cell_type(cd); + import_module(submod, name, type, mod_refs.at(ctx->id(type))); } // Import the cells section of a module -- cgit v1.2.3 From b1000870244dbb1a73198e23a859825865938b4c Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 29 Nov 2019 16:25:11 +0000 Subject: python: Add bindings for hierarchy structures Signed-off-by: David Shah --- common/arch_pybindings_shared.h | 4 ++++ common/nextpnr.h | 8 ++++---- common/pybindings.cc | 24 ++++++++++++++++++++---- ecp5/arch_pybindings.cc | 2 ++ frontend/frontend_base.h | 1 + generic/arch_pybindings.cc | 2 ++ ice40/arch_pybindings.cc | 2 ++ python/report_hierarchy.py | 10 ++++++++++ 8 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 python/report_hierarchy.py diff --git a/common/arch_pybindings_shared.h b/common/arch_pybindings_shared.h index f681af92..89a61dad 100644 --- a/common/arch_pybindings_shared.h +++ b/common/arch_pybindings_shared.h @@ -5,6 +5,10 @@ readonly_wrapper>::def_wrap(ctx_cls, "nets"); readonly_wrapper>::def_wrap( ctx_cls, "net_aliases"); +readonly_wrapper>::def_wrap( + ctx_cls, "hierarchy"); +readwrite_wrapper, + conv_from_str>::def_wrap(ctx_cls, "top_module"); fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getNetByAlias"); diff --git a/common/nextpnr.h b/common/nextpnr.h index ceea2088..7dfebd62 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -530,7 +530,7 @@ struct TimingConstraint // Represents the contents of a non-leaf cell in a design // with hierarchy -struct HierachicalPort +struct HierarchicalPort { IdString name; PortType dir; @@ -539,12 +539,12 @@ struct HierachicalPort bool upto; }; -struct HierachicalCell +struct HierarchicalCell { IdString name, type, parent, fullpath; // Name inside cell instance -> global name std::unordered_map leaf_cells, nets; - std::unordered_map ports; + std::unordered_map ports; // Name inside cell instance -> global name std::unordered_map hier_cells; }; @@ -643,7 +643,7 @@ struct BaseCtx std::unordered_map> cells; // Hierarchical (non-leaf) cells by full path - std::unordered_map hierarchy; + std::unordered_map hierarchy; // This is the root of the above structure IdString top_module; diff --git a/common/pybindings.cc b/common/pybindings.cc index 53830284..3b2a3744 100644 --- a/common/pybindings.cc +++ b/common/pybindings.cc @@ -131,7 +131,7 @@ BOOST_PYTHON_MODULE(MODULE_NAME) typedef std::unordered_map AttrMap; typedef std::unordered_map PortMap; - typedef std::unordered_map PinMap; + typedef std::unordered_map IdIdMap; typedef std::unordered_map> RegionMap; class_("BaseCtx", no_init); @@ -157,8 +157,8 @@ BOOST_PYTHON_MODULE(MODULE_NAME) conv_from_str>::def_wrap(ci_cls, "bel"); readwrite_wrapper, pass_through>::def_wrap(ci_cls, "belStrength"); - readonly_wrapper>::def_wrap(ci_cls, - "pins"); + readonly_wrapper>::def_wrap(ci_cls, + "pins"); fn_wrapper_1a_v>::def_wrap( ci_cls, "addInput"); @@ -230,9 +230,25 @@ BOOST_PYTHON_MODULE(MODULE_NAME) readonly_wrapper>::def_wrap(region_cls, "wires"); + auto hierarchy_cls = class_>("HierarchicalCell", no_init); + readwrite_wrapper, conv_from_str>::def_wrap(hierarchy_cls, "name"); + readwrite_wrapper, conv_from_str>::def_wrap(hierarchy_cls, "type"); + readwrite_wrapper, conv_from_str>::def_wrap(hierarchy_cls, "parent"); + readwrite_wrapper, conv_from_str>::def_wrap(hierarchy_cls, "fullpath"); + + readonly_wrapper>::def_wrap(hierarchy_cls, "leaf_cells"); + readonly_wrapper>::def_wrap(hierarchy_cls, "nets"); + readonly_wrapper>::def_wrap(hierarchy_cls, "hier_cells"); WRAP_MAP(AttrMap, conv_to_str, "AttrMap"); WRAP_MAP(PortMap, wrap_context, "PortMap"); - WRAP_MAP(PinMap, conv_to_str, "PinMap"); + WRAP_MAP(IdIdMap, conv_to_str, "IdIdMap"); WRAP_MAP(WireMap, wrap_context, "WireMap"); WRAP_MAP_UPTR(RegionMap, "RegionMap"); diff --git a/ecp5/arch_pybindings.cc b/ecp5/arch_pybindings.cc index da6d3e50..cd5e31c3 100644 --- a/ecp5/arch_pybindings.cc +++ b/ecp5/arch_pybindings.cc @@ -49,6 +49,7 @@ void arch_wrap_python() typedef std::unordered_map> CellMap; typedef std::unordered_map> NetMap; typedef std::unordered_map AliasMap; + typedef std::unordered_map HierarchyMap; auto belpin_cls = class_>("BelPin", no_init); readonly_wrapper>::def_wrap(belpin_cls, "bel"); @@ -64,6 +65,7 @@ void arch_wrap_python() WRAP_MAP_UPTR(CellMap, "IdCellMap"); WRAP_MAP_UPTR(NetMap, "IdNetMap"); + WRAP_MAP(HierarchyMap, wrap_context, "HierarchyMap"); } NEXTPNR_NAMESPACE_END diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h index be764266..9e16cb24 100644 --- a/frontend/frontend_base.h +++ b/frontend/frontend_base.h @@ -500,6 +500,7 @@ template struct GenericFrontend submod.prefix += '.'; submod.parent_path = m.path; submod.path = ctx->id(m.path.str(ctx) + "/" + name); + ctx->hierarchy[m.path].hier_cells[ctx->id(name)] = submod.path; // Do the submodule import auto type = impl.get_cell_type(cd); import_module(submod, name, type, mod_refs.at(ctx->id(type))); diff --git a/generic/arch_pybindings.cc b/generic/arch_pybindings.cc index 8526e409..2600cac0 100644 --- a/generic/arch_pybindings.cc +++ b/generic/arch_pybindings.cc @@ -141,6 +141,7 @@ void arch_wrap_python() typedef std::unordered_map> CellMap; typedef std::unordered_map> NetMap; + typedef std::unordered_map HierarchyMap; readonly_wrapper>::def_wrap(ctx_cls, "cells"); @@ -231,6 +232,7 @@ void arch_wrap_python() WRAP_MAP_UPTR(CellMap, "IdCellMap"); WRAP_MAP_UPTR(NetMap, "IdNetMap"); + WRAP_MAP(HierarchyMap, wrap_context, "HierarchyMap"); WRAP_VECTOR(const std::vector, conv_to_str); } diff --git a/ice40/arch_pybindings.cc b/ice40/arch_pybindings.cc index cef7c58f..e2022091 100644 --- a/ice40/arch_pybindings.cc +++ b/ice40/arch_pybindings.cc @@ -59,6 +59,7 @@ void arch_wrap_python() typedef std::unordered_map> CellMap; typedef std::unordered_map> NetMap; + typedef std::unordered_map HierarchyMap; typedef std::unordered_map AliasMap; auto belpin_cls = class_>("BelPin", no_init); @@ -75,6 +76,7 @@ void arch_wrap_python() WRAP_MAP_UPTR(CellMap, "IdCellMap"); WRAP_MAP_UPTR(NetMap, "IdNetMap"); + WRAP_MAP(HierarchyMap, wrap_context, "HierarchyMap"); } NEXTPNR_NAMESPACE_END diff --git a/python/report_hierarchy.py b/python/report_hierarchy.py new file mode 100644 index 00000000..6d409a9b --- /dev/null +++ b/python/report_hierarchy.py @@ -0,0 +1,10 @@ +def visit(indent, data): + istr = " " * indent + print("{}{}: {}".format(istr, data.name, data.type)) + for lname, gname in data.leaf_cells: + print("{} {} -> {}".format(istr, lname, gname)) + for lname, gname in data.hier_cells: + visit(indent + 4, ctx.hierarchy[gname]) + +visit(0, ctx.hierarchy[ctx.top_module]) + -- cgit v1.2.3 From fe40094216d0007797c51ff8894127b37a4ff045 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 1 Dec 2019 14:03:23 +0000 Subject: Preserve hierarchy through packing Signed-off-by: David Shah --- common/nextpnr.cc | 73 ++++++++++++++++++++++++++++++++++++++++++++++++ common/nextpnr.h | 7 +++++ ecp5/cells.cc | 10 +++++++ frontend/frontend_base.h | 2 ++ ice40/cells.cc | 4 +++ ice40/pack.cc | 1 + 6 files changed, 97 insertions(+) diff --git a/common/nextpnr.cc b/common/nextpnr.cc index 933f124c..a6345b42 100644 --- a/common/nextpnr.cc +++ b/common/nextpnr.cc @@ -21,6 +21,7 @@ #include #include "design_utils.h" #include "log.h" +#include "util.h" NEXTPNR_NAMESPACE_BEGIN @@ -723,4 +724,76 @@ void BaseCtx::copyBelPorts(IdString cell, BelId bel) } } +namespace { +struct FixupHierarchyWorker +{ + FixupHierarchyWorker(Context *ctx) : ctx(ctx){}; + Context *ctx; + void run() + { + trim_hierarchy(ctx->top_module); + rebuild_hierarchy(); + }; + // Remove cells and nets that no longer exist in the netlist + std::vector todelete_cells, todelete_nets; + void trim_hierarchy(IdString path) + { + auto &h = ctx->hierarchy.at(path); + todelete_cells.clear(); + todelete_nets.clear(); + for (auto &lc : h.leaf_cells) { + if (!ctx->cells.count(lc.second)) + todelete_cells.push_back(lc.first); + } + for (auto &n : h.nets) + if (!ctx->nets.count(n.second)) + todelete_nets.push_back(n.first); + for (auto tdc : todelete_cells) { + h.leaf_cells_by_gname.erase(h.leaf_cells.at(tdc)); + h.leaf_cells.erase(tdc); + } + for (auto tdn : todelete_nets) { + h.nets_by_gname.erase(h.nets.at(tdn)); + h.nets.erase(tdn); + } + for (auto &sc : h.hier_cells) + trim_hierarchy(sc.second); + } + + IdString construct_local_name(HierarchicalCell &hc, IdString global_name, bool is_cell) + { + std::string gn = global_name.str(ctx); + auto dp = gn.find_last_of('.'); + if (dp != std::string::npos) + gn = gn.substr(dp + 1); + IdString name = ctx->id(gn); + // Make sure name is unique + int adder = 0; + while (is_cell ? hc.leaf_cells.count(name) : hc.nets.count(name)) { + ++adder; + name = ctx->id(gn + "$" + std::to_string(adder)); + } + return name; + } + + // Update hierarchy structure for nets and cells that have hiercell set + void rebuild_hierarchy() + { + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (ci->hierpath == IdString()) + ci->hierpath = ctx->top_module; + auto &hc = ctx->hierarchy.at(ci->hierpath); + if (hc.leaf_cells_by_gname.count(ci->name)) + continue; // already known + IdString local_name = construct_local_name(hc, ci->name, true); + hc.leaf_cells_by_gname[ci->name] = local_name; + hc.leaf_cells[local_name] = ci->name; + } + } +}; +} // namespace + +void Context::fixupHierarchy() { FixupHierarchyWorker(this).run(); } + NEXTPNR_NAMESPACE_END diff --git a/common/nextpnr.h b/common/nextpnr.h index 7dfebd62..61e04415 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -544,6 +544,9 @@ struct HierarchicalCell IdString name, type, parent, fullpath; // Name inside cell instance -> global name std::unordered_map leaf_cells, nets; + // Global name -> name inside cell instance + std::unordered_map leaf_cells_by_gname, nets_by_gname; + // Cell port to net std::unordered_map ports; // Name inside cell instance -> global name std::unordered_map hier_cells; @@ -835,6 +838,10 @@ struct Context : Arch, DeterministicRNG bool getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t *delay = nullptr, std::unordered_map *route = nullptr, bool useEstimate = true); + // -------------------------------------------------------------- + // call after changing hierpath or adding/removing nets and cells + void fixupHierarchy(); + // -------------------------------------------------------------- // provided by sdf.cc diff --git a/ecp5/cells.cc b/ecp5/cells.cc index b06350c7..c630c2c3 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -233,6 +233,8 @@ static void replace_port_safe(bool has_ff, CellInfo *ff, IdString ff_port, CellI void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut) { + if (lc->hierpath == IdString()) + lc->hierpath = ff->hierpath; bool has_ff = lc->ports.at(ctx->id("Q0")).net != nullptr || lc->ports.at(ctx->id("Q1")).net != nullptr; std::string reg = "REG" + std::to_string(index); set_param_safe(has_ff, lc, ctx->id("SRMODE"), str_or_default(ff->params, ctx->id("SRMODE"), "LSR_OVER_CE")); @@ -271,6 +273,8 @@ void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool drive void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index) { + if (lc->hierpath == IdString()) + lc->hierpath = lut->hierpath; lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] = get_or_default(lut->params, ctx->id("INIT"), Property(0, 16)); replace_port(lut, ctx->id("A"), lc, ctx->id("A" + std::to_string(index))); @@ -282,6 +286,8 @@ void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index) void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc) { + if (lc->hierpath == IdString()) + lc->hierpath = ccu->hierpath; lc->params[ctx->id("MODE")] = std::string("CCU2"); lc->params[ctx->id("LUT0_INITVAL")] = get_or_default(ccu->params, ctx->id("INIT0"), Property(0, 16)); lc->params[ctx->id("LUT1_INITVAL")] = get_or_default(ccu->params, ctx->id("INIT1"), Property(0, 16)); @@ -309,6 +315,8 @@ void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc) void dram_to_ramw(Context *ctx, CellInfo *ram, CellInfo *lc) { + if (lc->hierpath == IdString()) + lc->hierpath = ram->hierpath; lc->params[ctx->id("MODE")] = std::string("RAMW"); replace_port(ram, ctx->id("WAD[0]"), lc, ctx->id("D0")); replace_port(ram, ctx->id("WAD[1]"), lc, ctx->id("B0")); @@ -340,6 +348,8 @@ static unsigned get_dram_init(const Context *ctx, const CellInfo *ram, int bit) void dram_to_ram_slice(Context *ctx, CellInfo *ram, CellInfo *lc, CellInfo *ramw, int index) { + if (lc->hierpath == IdString()) + lc->hierpath = ram->hierpath; lc->params[ctx->id("MODE")] = std::string("DPRAM"); lc->params[ctx->id("WREMUX")] = str_or_default(ram->params, ctx->id("WREMUX"), "WRE"); lc->params[ctx->id("WCKMUX")] = str_or_default(ram->params, ctx->id("WCKMUX"), "WCK"); diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h index 9e16cb24..45847e21 100644 --- a/frontend/frontend_base.h +++ b/frontend/frontend_base.h @@ -423,8 +423,10 @@ template struct GenericFrontend void import_leaf_cell(HierModuleState &m, const std::string &name, const cell_dat_t &cd) { IdString inst_name = unique_name(m.prefix, name, false); + ctx->hierarchy[m.path].leaf_cells_by_gname[inst_name] = ctx->id(name); ctx->hierarchy[m.path].leaf_cells[ctx->id(name)] = inst_name; CellInfo *ci = ctx->createCell(inst_name, ctx->id(impl.get_cell_type(cd))); + ci->hierpath = m.path; // Import port directions std::unordered_map port_dirs; impl.foreach_port_dir(cd, [&](const std::string &port, PortType dir) { port_dirs[ctx->id(port)] = dir; }); diff --git a/ice40/cells.cc b/ice40/cells.cc index 3def82bf..f1901c43 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -346,6 +346,8 @@ std::unique_ptr create_ice_cell(Context *ctx, IdString type, std::stri void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff) { + if (lc->hierpath == IdString()) + lc->hierpath = lut->hierpath; lc->params[ctx->id("LUT_INIT")] = lut->params[ctx->id("LUT_INIT")].extract(0, 16, Property::State::S0); replace_port(lut, ctx->id("I0"), lc, ctx->id("I0")); replace_port(lut, ctx->id("I1"), lc, ctx->id("I1")); @@ -359,6 +361,8 @@ void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff) void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut) { + if (lc->hierpath == IdString()) + lc->hierpath = dff->hierpath; lc->params[ctx->id("DFF_ENABLE")] = Property::State::S1; std::string config = dff->type.str(ctx).substr(6); auto citer = config.begin(); diff --git a/ice40/pack.cc b/ice40/pack.cc index cb430eb9..5b13e9ee 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -1487,6 +1487,7 @@ bool Arch::pack() promote_globals(ctx); ctx->assignArchInfo(); constrain_chains(ctx); + ctx->fixupHierarchy(); ctx->assignArchInfo(); ctx->settings[ctx->id("pack")] = 1; archInfoToAttributes(); -- cgit v1.2.3 From 0ea7f153a1f14ac6af079b5660afe1981e5f8b51 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 1 Dec 2019 14:18:33 +0000 Subject: Allow constraining non-leaf cells to regions Signed-off-by: David Shah --- common/nextpnr.cc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/common/nextpnr.cc b/common/nextpnr.cc index a6345b42..1156490c 100644 --- a/common/nextpnr.cc +++ b/common/nextpnr.cc @@ -523,7 +523,16 @@ void BaseCtx::createRectangularRegion(IdString name, int x0, int y0, int x1, int void BaseCtx::addBelToRegion(IdString name, BelId bel) { region[name]->bels.insert(bel); } void BaseCtx::constrainCellToRegion(IdString cell, IdString region_name) { - cells[cell]->region = region[region_name].get(); + // Support hierarchical cells as well as leaf ones + if (hierarchy.count(cell)) { + auto &hc = hierarchy.at(cell); + for (auto &lc : hc.leaf_cells) + constrainCellToRegion(lc.second, region_name); + for (auto &hsc : hc.hier_cells) + constrainCellToRegion(hsc.second, region_name); + } + if (cells.count(cell)) + cells.at(cell)->region = region[region_name].get(); } DecalXY BaseCtx::constructDecalXY(DecalId decal, float x, float y) { -- cgit v1.2.3 From 5774b13984bb151909b90ee2c668bdfb08387a2b Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 27 Dec 2019 10:44:20 +0000 Subject: Document hierarchy structures Signed-off-by: David Shah --- docs/netlist.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/netlist.md b/docs/netlist.md index 8886e4f8..3953241e 100644 --- a/docs/netlist.md +++ b/docs/netlist.md @@ -72,4 +72,18 @@ The second is `ArchCellInfo` and `ArchNetInfo`. These are provided by architectu - `getNetinfoSourceWire` gets the physical wire `WireId` associated with the source of a net - `getNetinfoSinkWire` gets the physical wire `WireId` associated with a given sink (specified by `PortRef`) - `getNetinfoRouteDelay` gets the routing delay - actual if the net is fully routed, estimated otherwise - between the source and a given sink of a net - - `getNetByAlias` returns the pointer to a net given any of its aliases - this should be used in preference to a direct lookup in `nets` whenever a net name is provided by the user \ No newline at end of file + - `getNetByAlias` returns the pointer to a net given any of its aliases - this should be used in preference to a direct lookup in `nets` whenever a net name is provided by the user + +## Hierarchy + +As most place and route algorithms require a flattened netlist to work with (consider - each leaf cell instance must have its own bel), the primary netlist structures are flattened. However, some tasks such as floorplanning require an understanding of hierarchy. + +`HierarchicalCell` is the main data structure for storing hierarchy. This represents an instance of a hierarchical, rather than leaf cell (leaf cells are represented by a `CellInfo`). + + - `name` and `type` are the instance name and cell type + - `parent` is the hierarchical path of the parent cell, and `fullpath` is the hierarchical path of this cell + - `leaf_cells`, `nets` map from a name inside the hierarchical cell to a 'global' name in the flattened netlist (i.e. one that indexes into `ctx->{cells,nets}`) + - `leaf_cells_by_gname`, `nets_by_gname` are the inverse of the above maps; going from `{CellInfo,NetInfo}::name` to an instance name inside the cell + - `hier_cells` maps instance names of sub-hierarchical (non-leaf) cells to global names (indexing into `ctx->hierarchy`) + +To preserve hierarchy during passes such as packing, ensure that `hierpath` is set on new cells derived from existing ones, and call `fixupHierarchy()` at the end to rebuild `HierarchicalCell` structures. -- cgit v1.2.3