| /* |
| * nextpnr -- Next Generation Place and Route |
| * |
| * Copyright (C) 2019 David Shah <dave@ds0.me> |
| * |
| * 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) const; |
| * calls Func(const std::string &name, const ModuleDataType &mod); |
| * for each module in the netlist |
| * |
| * 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) const; |
| * calls Func(const std::string &name, const CellDataType &cell) |
| * for each cell of mod |
| * |
| * 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) const; |
| * gets the PortType direction of a module 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) const; |
| * returns true if a port/net is an "upto" type port or netname entry |
| * |
| * 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; |
| * gets the type of a cell |
| * |
| * 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) const; |
| * 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 |
| * |
| * 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; |
| * gets the BitVector corresponding to the bits entry of a netname field |
| * |
| * int get_vector_length(const BitVectorDataType &bits) const; |
| * gets the length of a BitVector |
| * |
| * bool is_vector_bit_constant(const BitVectorDataType &bits, int i) const; |
| * returns true if bit <i> of bits is constant |
| * |
| * char get_vector_bit_constval(const BitVectorDataType &bits, int i) const; |
| * returns a char [01xz] corresponding to the constant value of bit <i> |
| * |
| * int get_vector_bit_signal(const BitVectorDataType &bits, int i) const; |
| * returns the signal number of vector bit <i> |
| * |
| */ |
| |
| #include "design_utils.h" |
| #include "log.h" |
| #include "nextpnr.h" |
| #include "util.h" |
| 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<IdString> instantiated_celltypes; |
| }; |
| |
| template <typename FrontendType> 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 = ""; |
| m.path = top; |
| ctx->top_module = top; |
| // Do the actual import, starting from the top level module |
| import_module(m, top.str(ctx), top.str(ctx), mod_refs.at(top)); |
| } |
| |
| 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; |
| |
| std::unordered_map<IdString, ModuleInfo> mods; |
| std::unordered_map<IdString, const mod_dat_t &> mod_refs; |
| 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]; |
| 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); |
| 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_celltypes.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<IdString> 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) { |
| 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()); |
| } |
| |
| // 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 merging nets where pointers to nets would go stale |
| // A net's udata points into this index |
| std::vector<NetInfo *> net_flatindex; |
| std::vector<std::vector<int>> 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 |
| struct HierModuleState |
| { |
| bool is_toplevel; |
| std::string prefix; |
| IdString parent_path, path; |
| // Map from index in module to "flat" index of nets |
| std::vector<int> index_to_net_flatindex; |
| // Get a reference to index_to_net; resizing if |
| // appropriate |
| 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, -1); |
| return index_to_net_flatindex.at(idx); |
| } |
| std::unordered_map<IdString, std::vector<int>> port_to_bus; |
| // All of the names given to a net |
| std::vector<std::vector<std::string>> net_names; |
| }; |
| |
| 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<NetInfo *> index_to_net; |
| 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(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) { |
| 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: |
| // - (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) |
| { |
| auto &midx = m.net_by_idx(idx); |
| if (midx != -1) { |
| return net_flatindex.at(midx); |
| } else { |
| 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; |
| } |
| } |
| |
| // 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_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 |
| // 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); |
| } |
| } |
| }); |
| } |
| |
| // 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.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.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<IdString, PortType> 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) |
| { |
| 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++) { |
| // 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 |
| 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 given in-module index |
| net_ref = create_or_get_net(m, impl.get_vector_bit_signal(bits, i))->udata; |
| } |
| NPNR_ASSERT(net_ref != -1); |
| submod.port_to_bus[ctx->id(name)].push_back(net_ref); |
| } |
| }); |
| // Create prefix for submodule |
| submod.prefix = m.prefix; |
| submod.prefix += name; |
| 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))); |
| } |
| |
| // Import the cells section of a module |
| void import_module_cells(HierModuleState &m, const mod_dat_t &data) |
| { |
| 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); |
| } 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); |
| 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")); |
| 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) |
| { |
| // 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); |
| } |
| }); |
| } |
| } |
| |
| // 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 |
| for (auto old_idx : net_old_indices.at(mergee->udata)) { |
| net_old_indices.at(base->udata).push_back(old_idx); |
| net_flatindex.at(old_idx) = base; |
| } |
| 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 |
| 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<int>(bv_size, p2b.size()); i++) { |
| 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 |
| 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(), |
| name.c_str(), i, constval); |
| // Insert the constant driver |
| 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)); |
| } |
| } |
| } |
| }); |
| } |
| }; |
| } // namespace |
| |
| NEXTPNR_NAMESPACE_END |