nexus: Support for unclocked 9x9 multiplies
Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
parent
d9a19897c4
commit
9203181625
@ -164,12 +164,12 @@ void rename_net(Context *ctx, NetInfo *net, IdString new_name)
|
|||||||
net->name = new_name;
|
net->name = new_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
void replace_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, CellInfo *new_cell,
|
void replace_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, bool old_brackets,
|
||||||
IdString new_name, int new_offset, int width, bool square_brackets)
|
CellInfo *new_cell, IdString new_name, int new_offset, bool new_brackets, int width)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < width; i++) {
|
for (int i = 0; i < width; i++) {
|
||||||
IdString old_port = ctx->id(stringf(square_brackets ? "%s[%d]" : "%s%d", old_name.c_str(ctx), i + old_offset));
|
IdString old_port = ctx->id(stringf(old_brackets ? "%s[%d]" : "%s%d", old_name.c_str(ctx), i + old_offset));
|
||||||
IdString new_port = ctx->id(stringf(square_brackets ? "%s[%d]" : "%s%d", new_name.c_str(ctx), i + new_offset));
|
IdString new_port = ctx->id(stringf(new_brackets ? "%s[%d]" : "%s%d", new_name.c_str(ctx), i + new_offset));
|
||||||
replace_port(old_cell, old_port, new_cell, new_port);
|
replace_port(old_cell, old_port, new_cell, new_port);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,8 +107,8 @@ void rename_net(Context *ctx, NetInfo *net, IdString new_name);
|
|||||||
void print_utilisation(const Context *ctx);
|
void print_utilisation(const Context *ctx);
|
||||||
|
|
||||||
// Disconnect a bus of nets (if connected) from old, and connect it to the new ports
|
// Disconnect a bus of nets (if connected) from old, and connect it to the new ports
|
||||||
void replace_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, CellInfo *new_cell,
|
void replace_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, bool old_brackets,
|
||||||
IdString new_name, int new_offset, int new_width, bool square_brackets = true);
|
CellInfo *new_cell, IdString new_name, int new_offset, bool new_brackets, int width);
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
|
@ -301,3 +301,5 @@ X(REGBYPSB)
|
|||||||
X(SHIFTA)
|
X(SHIFTA)
|
||||||
|
|
||||||
X(REGBYPS)
|
X(REGBYPS)
|
||||||
|
|
||||||
|
X(PP)
|
||||||
|
@ -515,6 +515,27 @@ struct NexusFasmWriter
|
|||||||
pop();
|
pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_mux_param(const std::string &key)
|
||||||
|
{
|
||||||
|
return (key.size() >= 3 && (key.compare(key.size() - 3, 3, "MUX") == 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write config for some kind of DSP cell
|
||||||
|
void write_dsp(const CellInfo *cell)
|
||||||
|
{
|
||||||
|
BelId bel = cell->bel;
|
||||||
|
push_bel(bel);
|
||||||
|
write_bit(stringf("MODE.%s", ctx->nameOf(cell->type)));
|
||||||
|
for (auto param : sorted_cref(cell->params)) {
|
||||||
|
const std::string ¶m_name = param.first.str(ctx);
|
||||||
|
if (is_mux_param(param_name))
|
||||||
|
continue;
|
||||||
|
write_enum(cell, param_name);
|
||||||
|
}
|
||||||
|
write_cell_muxes(cell);
|
||||||
|
pop();
|
||||||
|
}
|
||||||
// Write out FASM for unused bels where needed
|
// Write out FASM for unused bels where needed
|
||||||
void write_unused()
|
void write_unused()
|
||||||
{
|
{
|
||||||
@ -623,6 +644,10 @@ struct NexusFasmWriter
|
|||||||
write_osc(ci);
|
write_osc(ci);
|
||||||
else if (ci->type == id_OXIDE_EBR)
|
else if (ci->type == id_OXIDE_EBR)
|
||||||
write_bram(ci);
|
write_bram(ci);
|
||||||
|
else if (ci->type == id_MULT9_CORE || ci->type == id_PREADD9_CORE || ci->type == id_MULT18_CORE ||
|
||||||
|
ci->type == id_MULT18X36_CORE || ci->type == id_MULT36_CORE || ci->type == id_REG18_CORE ||
|
||||||
|
ci->type == id_ACC54_CORE)
|
||||||
|
write_dsp(ci);
|
||||||
blank();
|
blank();
|
||||||
}
|
}
|
||||||
// Write config for unused bels
|
// Write config for unused bels
|
||||||
|
@ -1226,10 +1226,10 @@ struct NexusPacker
|
|||||||
{
|
{
|
||||||
std::string name = ctx->nameOf(ctx->wire_data(wire).name);
|
std::string name = ctx->nameOf(ctx->wire_data(wire).name);
|
||||||
if (name.size() == 3 && (name.substr(0, 2) == "JF" || name.substr(0, 2) == "JQ"))
|
if (name.size() == 3 && (name.substr(0, 2) == "JF" || name.substr(0, 2) == "JQ"))
|
||||||
return false;
|
return true;
|
||||||
if (name.size() == 12 && (name.substr(0, 10) == "JCIBMUXOUT"))
|
if (name.size() == 12 && (name.substr(0, 10) == "JCIBMUXOUT"))
|
||||||
return false;
|
return true;
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Automatically generate cascade connections downstream of a cell
|
// Automatically generate cascade connections downstream of a cell
|
||||||
@ -1247,9 +1247,14 @@ struct NexusPacker
|
|||||||
if (start_wire == WireId())
|
if (start_wire == WireId())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (ctx->debug)
|
||||||
|
log_info(" searching cascade routing for wire %s:\n", ctx->nameOfWire(start_wire));
|
||||||
|
|
||||||
// Standard BFS-type exploration
|
// Standard BFS-type exploration
|
||||||
std::queue<WireId> visit;
|
std::queue<WireId> visit;
|
||||||
|
std::unordered_set<WireId> in_queue;
|
||||||
visit.push(start_wire);
|
visit.push(start_wire);
|
||||||
|
in_queue.insert(start_wire);
|
||||||
int iter = 0;
|
int iter = 0;
|
||||||
const int iter_limit = 1000;
|
const int iter_limit = 1000;
|
||||||
|
|
||||||
@ -1257,6 +1262,9 @@ struct NexusPacker
|
|||||||
WireId cursor = visit.front();
|
WireId cursor = visit.front();
|
||||||
visit.pop();
|
visit.pop();
|
||||||
|
|
||||||
|
if (ctx->debug)
|
||||||
|
log_info(" visit '%s'\n", ctx->nameOfWire(cursor));
|
||||||
|
|
||||||
// Check for downstream bel pins
|
// Check for downstream bel pins
|
||||||
bool found_active_pins = false;
|
bool found_active_pins = false;
|
||||||
for (auto bp : ctx->getWireBelPins(cursor)) {
|
for (auto bp : ctx->getWireBelPins(cursor)) {
|
||||||
@ -1265,13 +1273,19 @@ struct NexusPacker
|
|||||||
// so we can route through these if needed
|
// so we can route through these if needed
|
||||||
if (fnd_cell == bel2cell.end())
|
if (fnd_cell == bel2cell.end())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
found_active_pins = true;
|
|
||||||
|
|
||||||
// Skip outputs
|
// Skip outputs
|
||||||
if (ctx->getBelPinType(bp.bel, bp.pin) != PORT_IN)
|
if (ctx->getBelPinType(bp.bel, bp.pin) != PORT_IN)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (ctx->debug)
|
||||||
|
log_info(" bel %s pin %s\n", ctx->nameOfBel(bp.bel), ctx->nameOf(bp.pin));
|
||||||
|
|
||||||
|
found_active_pins = true;
|
||||||
CellInfo *other_cell = fnd_cell->second;
|
CellInfo *other_cell = fnd_cell->second;
|
||||||
|
|
||||||
|
if (other_cell == cell)
|
||||||
|
continue;
|
||||||
|
|
||||||
// Skip pins that are already in use
|
// Skip pins that are already in use
|
||||||
if (get_net_or_empty(other_cell, bp.pin) != nullptr)
|
if (get_net_or_empty(other_cell, bp.pin) != nullptr)
|
||||||
continue;
|
continue;
|
||||||
@ -1280,6 +1294,9 @@ struct NexusPacker
|
|||||||
other_cell->addInput(bp.pin);
|
other_cell->addInput(bp.pin);
|
||||||
// Make the connection
|
// Make the connection
|
||||||
connect_ports(ctx, cell, port.first, other_cell, bp.pin);
|
connect_ports(ctx, cell, port.first, other_cell, bp.pin);
|
||||||
|
|
||||||
|
if (ctx->debug)
|
||||||
|
log_info(" found %s.%s\n", ctx->nameOf(other_cell), ctx->nameOf(bp.pin));
|
||||||
}
|
}
|
||||||
|
|
||||||
// By doing this we never attempt to route-through bels
|
// By doing this we never attempt to route-through bels
|
||||||
@ -1288,8 +1305,16 @@ struct NexusPacker
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Search downstream pips for wires to add to the queue
|
// Search downstream pips for wires to add to the queue
|
||||||
for (auto pip : ctx->getPipsDownhill(cursor))
|
for (auto pip : ctx->getPipsDownhill(cursor)) {
|
||||||
visit.push(ctx->getPipDstWire(pip));
|
WireId dst = ctx->getPipDstWire(pip);
|
||||||
|
// Ignore general routing, as that isn't a useful cascade path
|
||||||
|
if (is_general_routing(dst))
|
||||||
|
continue;
|
||||||
|
if (in_queue.count(dst))
|
||||||
|
continue;
|
||||||
|
in_queue.insert(dst);
|
||||||
|
visit.push(dst);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1345,6 +1370,11 @@ struct NexusPacker
|
|||||||
log_error("Failed to create temporary placement for cell '%s' of type '%s'\n", ctx->nameOf(root),
|
log_error("Failed to create temporary placement for cell '%s' of type '%s'\n", ctx->nameOf(root),
|
||||||
ctx->nameOf(root->type));
|
ctx->nameOf(root->type));
|
||||||
|
|
||||||
|
// Create the necessary new ports
|
||||||
|
autocreate_ports(root, true);
|
||||||
|
for (auto child : root->constr_children)
|
||||||
|
autocreate_ports(child, true);
|
||||||
|
|
||||||
// Insert cascade connections from all cells in the macro
|
// Insert cascade connections from all cells in the macro
|
||||||
auto_cascade_cell(root, cell2bel.at(root->name), bel2cell);
|
auto_cascade_cell(root, cell2bel.at(root->name), bel2cell);
|
||||||
for (auto child : root->constr_children)
|
for (auto child : root->constr_children)
|
||||||
@ -1395,11 +1425,11 @@ struct NexusPacker
|
|||||||
cell->params[id_SUBSTRACT_EN] = std::string("SUBTRACTION");
|
cell->params[id_SUBSTRACT_EN] = std::string("SUBTRACTION");
|
||||||
} else if (type == id_MULT9_CORE) {
|
} else if (type == id_MULT9_CORE) {
|
||||||
cell->params[id_ASIGNED_OPERAND_EN] = std::string("DISABLED");
|
cell->params[id_ASIGNED_OPERAND_EN] = std::string("DISABLED");
|
||||||
cell->params[id_BYPASS_MULT9] = std::string("DISABLED");
|
cell->params[id_BYPASS_MULT9] = std::string("USED");
|
||||||
cell->params[id_GSR] = std::string("DISABLED");
|
cell->params[id_GSR] = std::string("DISABLED");
|
||||||
cell->params[id_REGBYPSA1] = std::string("DISABLED");
|
cell->params[id_REGBYPSA1] = std::string("BYPASS");
|
||||||
cell->params[id_REGBYPSA2] = std::string("DISABLED");
|
cell->params[id_REGBYPSA2] = std::string("BYPASS");
|
||||||
cell->params[id_REGBYPSB] = std::string("DISABLED");
|
cell->params[id_REGBYPSB] = std::string("BYPASS");
|
||||||
cell->params[id_RESET] = std::string("SYNC");
|
cell->params[id_RESET] = std::string("SYNC");
|
||||||
cell->params[id_GSR] = std::string("DISABLED");
|
cell->params[id_GSR] = std::string("DISABLED");
|
||||||
cell->params[id_SHIFTA] = std::string("DISABLED");
|
cell->params[id_SHIFTA] = std::string("DISABLED");
|
||||||
@ -1413,13 +1443,39 @@ struct NexusPacker
|
|||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pack_dsps() { log_info("Packing DSPs...\n"); }
|
void pack_dsps()
|
||||||
|
{
|
||||||
|
log_info("Packing DSPs...\n");
|
||||||
|
std::vector<CellInfo *> to_remove;
|
||||||
|
|
||||||
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
if (ci->type == id_MULT9X9) {
|
||||||
|
// MULT9X9: PREADD9 -> MULT9 -> REG18
|
||||||
|
CellInfo *preadd9_0 = create_dsp_cell(ci->name, id_PREADD9_CORE, nullptr, 0, 0);
|
||||||
|
CellInfo *mult9_0 = create_dsp_cell(ci->name, id_MULT9_CORE, preadd9_0, 0, 2);
|
||||||
|
CellInfo *reg18_0 = create_dsp_cell(ci->name, id_REG18_CORE, preadd9_0, 2, 0);
|
||||||
|
replace_bus(ctx, ci, id_B, 0, true, preadd9_0, id_B, 0, false, 9);
|
||||||
|
replace_bus(ctx, ci, id_A, 0, true, mult9_0, id_A, 0, false, 9);
|
||||||
|
replace_bus(ctx, ci, id_Z, 0, true, reg18_0, id_PP, 0, false, 18);
|
||||||
|
auto_cascade_group(preadd9_0);
|
||||||
|
to_remove.push_back(ci);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto cell : to_remove) {
|
||||||
|
for (auto port : sorted_ref(cell->ports))
|
||||||
|
disconnect_port(ctx, cell, port.first);
|
||||||
|
ctx->cells.erase(cell->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
explicit NexusPacker(Context *ctx) : ctx(ctx) {}
|
explicit NexusPacker(Context *ctx) : ctx(ctx) {}
|
||||||
|
|
||||||
void operator()()
|
void operator()()
|
||||||
{
|
{
|
||||||
pack_io();
|
pack_io();
|
||||||
|
pack_dsps();
|
||||||
convert_prims();
|
convert_prims();
|
||||||
pack_bram();
|
pack_bram();
|
||||||
pack_lutram();
|
pack_lutram();
|
||||||
|
Loading…
Reference in New Issue
Block a user