frontend/base: Functions for port import

Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
David Shah 2019-11-13 12:11:17 +00:00
parent fffc3b8447
commit 6aaa9f5a3d
2 changed files with 146 additions and 1 deletions

View File

@ -397,6 +397,8 @@ struct NetInfo : ArchNetInfo
// wire -> uphill_pip
std::unordered_map<WireId, PipMap> wires;
std::vector<IdString> aliases; // entries in net_aliases that point to this net
std::unique_ptr<ClockConstraint> clkconstr;
TimingConstrObjectId tmg_id;

View File

@ -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