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
|
// wire -> uphill_pip
|
||||||
std::unordered_map<WireId, PipMap> wires;
|
std::unordered_map<WireId, PipMap> wires;
|
||||||
|
|
||||||
|
std::vector<IdString> aliases; // entries in net_aliases that point to this net
|
||||||
|
|
||||||
std::unique_ptr<ClockConstraint> clkconstr;
|
std::unique_ptr<ClockConstraint> clkconstr;
|
||||||
|
|
||||||
TimingConstrObjectId tmg_id;
|
TimingConstrObjectId tmg_id;
|
||||||
|
@ -101,6 +101,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "design_utils.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "nextpnr.h"
|
#include "nextpnr.h"
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
@ -121,7 +122,7 @@ template <typename FrontendType> struct GenericFrontend
|
|||||||
// Used for hierarchy resolution
|
// Used for hierarchy resolution
|
||||||
struct ModuleInfo
|
struct ModuleInfo
|
||||||
{
|
{
|
||||||
mod_dat_t *mod_data;
|
const mod_dat_t *mod_data;
|
||||||
bool is_top = false, is_blackbox = false, is_whitebox = false;
|
bool is_top = false, is_blackbox = false, is_whitebox = false;
|
||||||
inline bool is_box() const { return is_blackbox || is_whitebox; }
|
inline bool is_box() const { return is_blackbox || is_whitebox; }
|
||||||
std::unordered_set<IdString> instantiated_celltypes;
|
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");
|
log_error("Failed to autodetect top module, please specify using --top.\n");
|
||||||
top = *(candidate_top.begin());
|
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
|
} // namespace
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user