diff options
Diffstat (limited to 'frontend')
-rw-r--r-- | frontend/frontend_base.h | 145 |
1 files changed, 144 insertions, 1 deletions
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 <typename FrontendType> 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<IdString> instantiated_celltypes; @@ -184,6 +185,148 @@ template <typename FrontendType> 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<NetInfo *> 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<NetInfo *> 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<IdString, std::vector<NetInfo *>> port_to_bus; + }; + + void import_module(HierModuleState &m, mod_dat_t *data) + { + std::vector<NetInfo *> 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<int>(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 |