nexus: Add some infrastructure for DSP packing
Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
parent
c7ad3cece6
commit
90608f2c89
@ -164,4 +164,26 @@ void rename_net(Context *ctx, NetInfo *net, IdString new_name)
|
|||||||
net->name = new_name;
|
net->name = new_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<NetInfo *> create_bus(Context *ctx, IdString base_name, const std::string &postfix, int width)
|
||||||
|
{
|
||||||
|
std::vector<NetInfo *> nets;
|
||||||
|
for (int i = 0; i < width; i++)
|
||||||
|
nets.push_back(ctx->createNet(ctx->id(stringf("%s/%s[%d]", base_name.c_str(ctx), postfix.c_str(), i))));
|
||||||
|
return nets;
|
||||||
|
}
|
||||||
|
|
||||||
|
void connect_bus(Context *ctx, CellInfo *cell, IdString port, std::vector<NetInfo *> &bus, PortType dir)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < int(bus.size()); i++) {
|
||||||
|
IdString p = ctx->id(stringf("%s%d", port.c_str(ctx), i));
|
||||||
|
if (!cell->ports.count(p)) {
|
||||||
|
cell->ports[p].name = p;
|
||||||
|
cell->ports[p].type = dir;
|
||||||
|
} else {
|
||||||
|
NPNR_ASSERT(cell->ports.at(p).type == dir);
|
||||||
|
}
|
||||||
|
connect_port(ctx, bus.at(i), cell, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -104,6 +104,12 @@ void rename_port(Context *ctx, CellInfo *cell, IdString old_name, IdString new_n
|
|||||||
// Rename a net without invalidating pointers to it
|
// Rename a net without invalidating pointers to it
|
||||||
void rename_net(Context *ctx, NetInfo *net, IdString new_name);
|
void rename_net(Context *ctx, NetInfo *net, IdString new_name);
|
||||||
|
|
||||||
|
// Create a bus of nets
|
||||||
|
std::vector<NetInfo *> create_bus(Context *ctx, IdString base_name, const std::string &postfix, int width);
|
||||||
|
|
||||||
|
// Connect a bus of nets to a bus of ports
|
||||||
|
void connect_bus(Context *ctx, CellInfo *cell, IdString port, std::vector<NetInfo *> &bus, PortType dir);
|
||||||
|
|
||||||
void print_utilisation(const Context *ctx);
|
void print_utilisation(const Context *ctx);
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
140
nexus/pack.cc
140
nexus/pack.cc
@ -264,11 +264,11 @@ struct NexusPacker
|
|||||||
|
|
||||||
std::unordered_map<IdString, BelId> reference_bels;
|
std::unordered_map<IdString, BelId> reference_bels;
|
||||||
|
|
||||||
void autocreate_ports(CellInfo *cell)
|
void autocreate_ports(CellInfo *cell, bool include_outputs = false)
|
||||||
{
|
{
|
||||||
// Automatically create ports for all inputs of a cell; even if they were left off the instantiation
|
// Automatically create ports for all inputs, and maybe outputs, of a cell; even if they were left off the
|
||||||
// so we can tie them to constants as appropriate
|
// instantiation so we can tie them to constants as appropriate This also checks for any cells that don't have
|
||||||
// This also checks for any cells that don't have corresponding bels
|
// corresponding bels
|
||||||
|
|
||||||
if (!reference_bels.count(cell->type)) {
|
if (!reference_bels.count(cell->type)) {
|
||||||
// We need to look up a corresponding bel to get the list of input ports
|
// We need to look up a corresponding bel to get the list of input ports
|
||||||
@ -288,7 +288,7 @@ struct NexusPacker
|
|||||||
BelId bel = reference_bels.at(cell->type);
|
BelId bel = reference_bels.at(cell->type);
|
||||||
for (IdString pin : ctx->getBelPins(bel)) {
|
for (IdString pin : ctx->getBelPins(bel)) {
|
||||||
PortType dir = ctx->getBelPinType(bel, pin);
|
PortType dir = ctx->getBelPinType(bel, pin);
|
||||||
if (dir != PORT_IN)
|
if (dir != PORT_IN && !include_outputs)
|
||||||
continue;
|
continue;
|
||||||
if (cell->ports.count(pin))
|
if (cell->ports.count(pin))
|
||||||
continue;
|
continue;
|
||||||
@ -1221,6 +1221,136 @@ struct NexusPacker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to check if a wire is general routing; and therefore skipped for cascade purposes
|
||||||
|
bool is_general_routing(WireId wire)
|
||||||
|
{
|
||||||
|
std::string name = ctx->nameOf(ctx->wire_data(wire).name);
|
||||||
|
if (name.size() == 3 && (name.substr(0, 2) == "JF" || name.substr(0, 2) == "JQ"))
|
||||||
|
return false;
|
||||||
|
if (name.size() == 12 && (name.substr(0, 10) == "JCIBMUXOUT"))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Automatically generate cascade connections downstream of a cell
|
||||||
|
// using the temporary placement that we use solely to access the routing graph
|
||||||
|
void auto_cascade_cell(CellInfo *cell, BelId bel, const std::unordered_map<BelId, CellInfo *> &bel2cell)
|
||||||
|
{
|
||||||
|
for (auto port : sorted_ref(cell->ports)) {
|
||||||
|
// Skip if not an output, or being used already for something else
|
||||||
|
if (port.second.type != PORT_OUT || port.second.net != nullptr)
|
||||||
|
continue;
|
||||||
|
// Get the corresponding start wire
|
||||||
|
WireId start_wire = ctx->getBelPinWire(bel, port.first);
|
||||||
|
|
||||||
|
// Skip if the start wire doesn't actually exist
|
||||||
|
if (start_wire == WireId())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Standard BFS-type exploration
|
||||||
|
std::queue<WireId> visit;
|
||||||
|
visit.push(start_wire);
|
||||||
|
int iter = 0;
|
||||||
|
const int iter_limit = 1000;
|
||||||
|
|
||||||
|
while (!visit.empty() && (iter++ < iter_limit)) {
|
||||||
|
WireId cursor = visit.front();
|
||||||
|
visit.pop();
|
||||||
|
|
||||||
|
// Check for downstream bel pins
|
||||||
|
bool found_active_pins = false;
|
||||||
|
for (auto bp : ctx->getWireBelPins(cursor)) {
|
||||||
|
auto fnd_cell = bel2cell.find(bp.bel);
|
||||||
|
// Always skip unused bels, and don't set found_active_pins
|
||||||
|
// so we can route through these if needed
|
||||||
|
if (fnd_cell == bel2cell.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
found_active_pins = true;
|
||||||
|
|
||||||
|
// Skip outputs
|
||||||
|
if (ctx->getBelPinType(bp.bel, bp.pin) != PORT_IN)
|
||||||
|
continue;
|
||||||
|
CellInfo *other_cell = fnd_cell->second;
|
||||||
|
// Skip pins that are already in use
|
||||||
|
if (get_net_or_empty(other_cell, bp.pin) != nullptr)
|
||||||
|
continue;
|
||||||
|
// Create the input if it doesn't exist
|
||||||
|
if (!other_cell->ports.count(bp.pin))
|
||||||
|
other_cell->addInput(bp.pin);
|
||||||
|
// Make the connection
|
||||||
|
connect_ports(ctx, cell, port.first, other_cell, bp.pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// By doing this we never attempt to route-through bels
|
||||||
|
// that are actually in use
|
||||||
|
if (found_active_pins)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Search downstream pips for wires to add to the queue
|
||||||
|
for (auto pip : ctx->getPipsDownhill(cursor))
|
||||||
|
visit.push(ctx->getPipDstWire(pip));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert all the cascade connections for a group of cells given the root
|
||||||
|
void auto_cascade_group(CellInfo *root)
|
||||||
|
{
|
||||||
|
|
||||||
|
auto get_child_loc = [&](Loc base, const CellInfo *sub) {
|
||||||
|
Loc l = base;
|
||||||
|
l.x += sub->constr_x;
|
||||||
|
l.y += sub->constr_y;
|
||||||
|
l.z = sub->constr_abs_z ? sub->constr_z : (sub->constr_z + base.z);
|
||||||
|
return l;
|
||||||
|
};
|
||||||
|
|
||||||
|
// We first create a temporary placement so we can access the routing graph
|
||||||
|
bool found = false;
|
||||||
|
std::unordered_map<BelId, CellInfo *> bel2cell;
|
||||||
|
std::unordered_map<IdString, BelId> cell2bel;
|
||||||
|
|
||||||
|
for (BelId root_bel : ctx->getBels()) {
|
||||||
|
if (ctx->getBelType(root_bel) != root->type)
|
||||||
|
continue;
|
||||||
|
Loc root_loc = ctx->getBelLocation(root_bel);
|
||||||
|
found = true;
|
||||||
|
bel2cell.clear();
|
||||||
|
cell2bel.clear();
|
||||||
|
bel2cell[root_bel] = root;
|
||||||
|
cell2bel[root->name] = root_bel;
|
||||||
|
|
||||||
|
for (auto child : root->constr_children) {
|
||||||
|
// Check that a valid placement exists for all children in the macro at this location
|
||||||
|
Loc c_loc = get_child_loc(root_loc, child);
|
||||||
|
BelId c_bel = ctx->getBelByLocation(c_loc);
|
||||||
|
if (c_bel == BelId()) {
|
||||||
|
found = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ctx->getBelType(c_bel) != child->type) {
|
||||||
|
found = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bel2cell[c_bel] = child;
|
||||||
|
cell2bel[child->name] = c_bel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
log_error("Failed to create temporary placement for cell '%s' of type '%s'\n", ctx->nameOf(root),
|
||||||
|
ctx->nameOf(root->type));
|
||||||
|
|
||||||
|
// Insert cascade connections from all cells in the macro
|
||||||
|
auto_cascade_cell(root, cell2bel.at(root->name), bel2cell);
|
||||||
|
for (auto child : root->constr_children)
|
||||||
|
auto_cascade_cell(child, cell2bel.at(child->name), bel2cell);
|
||||||
|
}
|
||||||
|
|
||||||
explicit NexusPacker(Context *ctx) : ctx(ctx) {}
|
explicit NexusPacker(Context *ctx) : ctx(ctx) {}
|
||||||
|
|
||||||
void operator()()
|
void operator()()
|
||||||
|
Loading…
Reference in New Issue
Block a user