aboutsummaryrefslogtreecommitdiffstats
path: root/frontend
diff options
context:
space:
mode:
authorDavid Shah <dave@ds0.me>2019-11-13 12:11:17 +0000
committerDavid Shah <dave@ds0.me>2019-12-27 10:44:29 +0000
commit6aaa9f5a3d02224f2760d993d114163ce7678e1f (patch)
tree8397a723f1c4e225e12f1ff4808f218b5c9268f9 /frontend
parentfffc3b844730d1241d0056f989b3a9492d62005c (diff)
downloadnextpnr-6aaa9f5a3d02224f2760d993d114163ce7678e1f.tar.gz
nextpnr-6aaa9f5a3d02224f2760d993d114163ce7678e1f.tar.bz2
nextpnr-6aaa9f5a3d02224f2760d993d114163ce7678e1f.zip
frontend/base: Functions for port import
Signed-off-by: David Shah <dave@ds0.me>
Diffstat (limited to 'frontend')
-rw-r--r--frontend/frontend_base.h145
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