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  | 
