frontend/base: Functions for port import
Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
parent
fffc3b8447
commit
6aaa9f5a3d
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user