nexus: Preliminary integration of DSP timing data
Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
parent
76543d05e7
commit
df3c6dfe3e
@ -452,6 +452,10 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort
|
|||||||
if (toPort == id_F || toPort == id_OFX)
|
if (toPort == id_F || toPort == id_OFX)
|
||||||
return lookup_cell_delay(cell->tmg_index, fromPort, toPort, delay);
|
return lookup_cell_delay(cell->tmg_index, fromPort, toPort, delay);
|
||||||
}
|
}
|
||||||
|
} else if (is_dsp_cell(cell)) {
|
||||||
|
if (fromPort == id_CLK)
|
||||||
|
return false; // don't include delays that are actually clock-to-out here
|
||||||
|
return lookup_cell_delay(cell->tmg_index, lookup_port(fromPort), lookup_port(toPort), delay);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -459,6 +463,11 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort
|
|||||||
TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const
|
TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const
|
||||||
{
|
{
|
||||||
auto disconnected = [cell](IdString p) { return !cell->ports.count(p) || cell->ports.at(p).net == nullptr; };
|
auto disconnected = [cell](IdString p) { return !cell->ports.count(p) || cell->ports.at(p).net == nullptr; };
|
||||||
|
auto lookup_port = [&](IdString p) {
|
||||||
|
auto fnd = cell->tmg_portmap.find(p);
|
||||||
|
return fnd == cell->tmg_portmap.end() ? p : fnd->second;
|
||||||
|
};
|
||||||
|
clockInfoCount = 0;
|
||||||
if (cell->type == id_OXIDE_COMB) {
|
if (cell->type == id_OXIDE_COMB) {
|
||||||
if (port == id_A || port == id_B || port == id_C || port == id_D || port == id_SEL || port == id_F1 ||
|
if (port == id_A || port == id_B || port == id_C || port == id_D || port == id_SEL || port == id_F1 ||
|
||||||
port == id_FCI || port == id_WDI)
|
port == id_FCI || port == id_WDI)
|
||||||
@ -498,6 +507,15 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in
|
|||||||
return TMG_CLOCK_INPUT;
|
return TMG_CLOCK_INPUT;
|
||||||
clockInfoCount = 1;
|
clockInfoCount = 1;
|
||||||
return (cell->ports.at(port).type == PORT_IN) ? TMG_REGISTER_INPUT : TMG_REGISTER_OUTPUT;
|
return (cell->ports.at(port).type == PORT_IN) ? TMG_REGISTER_INPUT : TMG_REGISTER_OUTPUT;
|
||||||
|
} else if (cell->type == id_MULT18_CORE || cell->type == id_MULT18X36_CORE || cell->type == id_MULT36_CORE) {
|
||||||
|
return (cell->ports.at(port).type == PORT_IN) ? TMG_COMB_INPUT : TMG_COMB_OUTPUT;
|
||||||
|
} else if (cell->type == id_PREADD9_CORE || cell->type == id_REG18_CORE || cell->type == id_MULT9_CORE) {
|
||||||
|
if (port == id_CLK)
|
||||||
|
return TMG_CLOCK_INPUT;
|
||||||
|
auto type = lookup_port_type(cell->tmg_index, lookup_port(port), cell->ports.at(port).type, id_CLK);
|
||||||
|
if (type == TMG_REGISTER_INPUT || type == TMG_REGISTER_OUTPUT)
|
||||||
|
clockInfoCount = 1;
|
||||||
|
return type;
|
||||||
}
|
}
|
||||||
return TMG_IGNORE;
|
return TMG_IGNORE;
|
||||||
}
|
}
|
||||||
@ -531,6 +549,14 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
|
|||||||
}
|
}
|
||||||
// Lookup edge based on inversion
|
// Lookup edge based on inversion
|
||||||
info.edge = (get_cell_pinmux(cell, info.clock_port) == PINMUX_INV) ? FALLING_EDGE : RISING_EDGE;
|
info.edge = (get_cell_pinmux(cell, info.clock_port) == PINMUX_INV) ? FALLING_EDGE : RISING_EDGE;
|
||||||
|
} else if (cell->type == id_PREADD9_CORE || cell->type == id_REG18_CORE || cell->type == id_MULT9_CORE) {
|
||||||
|
info.clock_port = id_CLK;
|
||||||
|
if (cell->ports.at(port).type == PORT_IN) {
|
||||||
|
lookup_cell_setuphold(cell->tmg_index, lookup_port(port), id_CLK, info.setup, info.hold);
|
||||||
|
} else {
|
||||||
|
NPNR_ASSERT(lookup_cell_delay(cell->tmg_index, id_CLK, lookup_port(port), info.clockToQ));
|
||||||
|
}
|
||||||
|
info.edge = (get_cell_pinmux(cell, info.clock_port) == PINMUX_INV) ? FALLING_EDGE : RISING_EDGE;
|
||||||
} else {
|
} else {
|
||||||
NPNR_ASSERT_FALSE("missing clocking info");
|
NPNR_ASSERT_FALSE("missing clocking info");
|
||||||
}
|
}
|
||||||
@ -819,6 +845,12 @@ int db_binary_search(const Tres *list, int count, Tgetter key_getter, Tkey key)
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
bool Arch::is_dsp_cell(const CellInfo *cell) const
|
||||||
|
{
|
||||||
|
return cell->type == id_MULT18_CORE || cell->type == id_MULT18X36_CORE || cell->type == id_MULT36_CORE ||
|
||||||
|
cell->type == id_PREADD9_CORE || cell->type == id_REG18_CORE || cell->type == id_MULT9_CORE;
|
||||||
|
}
|
||||||
|
|
||||||
int Arch::get_cell_timing_idx(IdString cell_type, IdString cell_variant) const
|
int Arch::get_cell_timing_idx(IdString cell_type, IdString cell_variant) const
|
||||||
{
|
{
|
||||||
return db_binary_search(
|
return db_binary_search(
|
||||||
@ -885,6 +917,23 @@ void Arch::lookup_cell_clock_out(int type_idx, IdString to_port, IdString &clock
|
|||||||
delay.min_delay = ct.prop_delays[dly_idx].min_delay;
|
delay.min_delay = ct.prop_delays[dly_idx].min_delay;
|
||||||
delay.max_delay = ct.prop_delays[dly_idx].max_delay;
|
delay.max_delay = ct.prop_delays[dly_idx].max_delay;
|
||||||
}
|
}
|
||||||
|
TimingPortClass Arch::lookup_port_type(int type_idx, IdString port, PortType dir, IdString clock) const
|
||||||
|
{
|
||||||
|
if (dir == PORT_IN) {
|
||||||
|
NPNR_ASSERT(type_idx != -1);
|
||||||
|
const auto &ct = speed_grade->cell_types[type_idx];
|
||||||
|
// If a setup-hold entry exists, then this is a register input
|
||||||
|
int sh_idx = db_binary_search(
|
||||||
|
ct.setup_holds.get(), ct.num_setup_holds,
|
||||||
|
[](const CellSetupHoldPOD &sh) { return std::make_pair(sh.sig_port, sh.clock_port); },
|
||||||
|
std::make_pair(port.index, clock.index));
|
||||||
|
return (sh_idx != -1) ? TMG_REGISTER_INPUT : TMG_COMB_INPUT;
|
||||||
|
} else {
|
||||||
|
DelayInfo dly;
|
||||||
|
// If a clock-to-out entry exists, then this is a register output
|
||||||
|
return lookup_cell_delay(type_idx, clock, port, dly) ? TMG_REGISTER_OUTPUT : TMG_COMB_OUTPUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -1559,6 +1559,8 @@ struct Arch : BaseCtx
|
|||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
// Cell timing lookup helpers
|
// Cell timing lookup helpers
|
||||||
|
|
||||||
|
bool is_dsp_cell(const CellInfo *cell) const;
|
||||||
|
|
||||||
// Given cell type and variant, get the index inside the speed grade timing data
|
// Given cell type and variant, get the index inside the speed grade timing data
|
||||||
int get_cell_timing_idx(IdString cell_type, IdString cell_variant = IdString()) const;
|
int get_cell_timing_idx(IdString cell_type, IdString cell_variant = IdString()) const;
|
||||||
// Return true and set delay if a comb path exists in a given cell timing index
|
// Return true and set delay if a comb path exists in a given cell timing index
|
||||||
@ -1571,6 +1573,8 @@ struct Arch : BaseCtx
|
|||||||
DelayInfo &hold) const;
|
DelayInfo &hold) const;
|
||||||
// Similar to lookup_cell_delay but only needs the 'to' signal, intended for clk->out delays
|
// Similar to lookup_cell_delay but only needs the 'to' signal, intended for clk->out delays
|
||||||
void lookup_cell_clock_out(int type_idx, IdString to_port, IdString &clock, DelayInfo &delay) const;
|
void lookup_cell_clock_out(int type_idx, IdString to_port, IdString &clock, DelayInfo &delay) const;
|
||||||
|
// Attempt to look up port type based on database
|
||||||
|
TimingPortClass lookup_port_type(int type_idx, IdString port, PortType dir, IdString clock) const;
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
|
|
||||||
// List of IO constraints, used by PDC parser
|
// List of IO constraints, used by PDC parser
|
||||||
|
@ -264,7 +264,7 @@ struct NexusPacker
|
|||||||
|
|
||||||
std::unordered_map<IdString, BelId> reference_bels;
|
std::unordered_map<IdString, BelId> reference_bels;
|
||||||
|
|
||||||
void autocreate_ports(CellInfo *cell, bool include_outputs = false)
|
void autocreate_ports(CellInfo *cell)
|
||||||
{
|
{
|
||||||
// Automatically create ports for all inputs, and maybe outputs, of a cell; even if they were left off the
|
// Automatically create ports for all inputs, and maybe outputs, of a cell; even if they were left off the
|
||||||
// instantiation so we can tie them to constants as appropriate This also checks for any cells that don't have
|
// instantiation so we can tie them to constants as appropriate This also checks for any cells that don't have
|
||||||
@ -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 && !include_outputs)
|
if (dir != PORT_IN)
|
||||||
continue;
|
continue;
|
||||||
if (cell->ports.count(pin))
|
if (cell->ports.count(pin))
|
||||||
continue;
|
continue;
|
||||||
@ -1236,6 +1236,14 @@ struct NexusPacker
|
|||||||
// using the temporary placement that we use solely to access the routing graph
|
// 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)
|
void auto_cascade_cell(CellInfo *cell, BelId bel, const std::unordered_map<BelId, CellInfo *> &bel2cell)
|
||||||
{
|
{
|
||||||
|
// Create outputs based on the actual bel
|
||||||
|
for (auto bp : ctx->getBelPins(bel)) {
|
||||||
|
if (ctx->getBelPinType(bel, bp) != PORT_OUT)
|
||||||
|
continue;
|
||||||
|
if (cell->ports.count(bp))
|
||||||
|
continue;
|
||||||
|
cell->addOutput(bp);
|
||||||
|
}
|
||||||
for (auto port : sorted_ref(cell->ports)) {
|
for (auto port : sorted_ref(cell->ports)) {
|
||||||
// Skip if not an output, or being used already for something else
|
// Skip if not an output, or being used already for something else
|
||||||
if (port.second.type != PORT_OUT || port.second.net != nullptr)
|
if (port.second.type != PORT_OUT || port.second.net != nullptr)
|
||||||
@ -1371,9 +1379,9 @@ struct NexusPacker
|
|||||||
ctx->nameOf(root->type));
|
ctx->nameOf(root->type));
|
||||||
|
|
||||||
// Create the necessary new ports
|
// Create the necessary new ports
|
||||||
autocreate_ports(root, true);
|
autocreate_ports(root);
|
||||||
for (auto child : root->constr_children)
|
for (auto child : root->constr_children)
|
||||||
autocreate_ports(child, true);
|
autocreate_ports(child);
|
||||||
|
|
||||||
// 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);
|
||||||
@ -1738,6 +1746,12 @@ void Arch::assignArchInfo()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string> dsp_bus_prefices = {
|
||||||
|
"M9ADDSUB", "ADDSUB", "SFTCTRL", "DSPIN", "CINPUT", "DSPOUT", "CASCOUT", "CASCIN", "PML72", "PMH72", "SUM1",
|
||||||
|
"SUM0", "BRS1", "BRS2", "BLS1", "BLS2", "BLSO", "BRSO", "PL18", "PH18", "PL36", "PH36",
|
||||||
|
"PL72", "PH72", "P72", "P36", "P18", "AS1", "AS2", "ARL", "ARH", "BRL", "BRH",
|
||||||
|
"AO", "BO", "AB", "AR", "BR", "PM", "PP", "A", "B", "C"};
|
||||||
|
|
||||||
void Arch::assignCellInfo(CellInfo *cell)
|
void Arch::assignCellInfo(CellInfo *cell)
|
||||||
{
|
{
|
||||||
cell->tmg_index = -1;
|
cell->tmg_index = -1;
|
||||||
@ -1806,6 +1820,46 @@ void Arch::assignCellInfo(CellInfo *cell)
|
|||||||
|
|
||||||
cell->tmg_index = get_cell_timing_idx(id(str_or_default(cell->params, id_MODE, "DP16K") + "_MODE"));
|
cell->tmg_index = get_cell_timing_idx(id(str_or_default(cell->params, id_MODE, "DP16K") + "_MODE"));
|
||||||
NPNR_ASSERT(cell->tmg_index != -1);
|
NPNR_ASSERT(cell->tmg_index != -1);
|
||||||
|
} else if (is_dsp_cell(cell)) {
|
||||||
|
// Strip off bus indices to get the timing ports
|
||||||
|
// as timing is generally word-wide
|
||||||
|
for (const auto &port : cell->ports) {
|
||||||
|
const std::string &name = port.first.str(this);
|
||||||
|
size_t idx_end = name.find_last_not_of("0123456789");
|
||||||
|
if (idx_end == std::string::npos)
|
||||||
|
continue;
|
||||||
|
for (const auto &p : dsp_bus_prefices) {
|
||||||
|
if (name.size() > p.size() && name.substr(0, p.size()) == p && idx_end <= p.size()) {
|
||||||
|
cell->tmg_portmap[port.first] = id(p);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Build up the configuration string
|
||||||
|
std::set<std::string> config;
|
||||||
|
for (const auto ¶m : cell->params) {
|
||||||
|
const std::string &name = param.first.str(this);
|
||||||
|
size_t byp_pos = name.find("REGBYPS");
|
||||||
|
if (byp_pos != std::string::npos && param.second.str == "REGISTER") {
|
||||||
|
// Register enabled
|
||||||
|
config.insert(name.substr(0, byp_pos + 3) + name.substr(byp_pos + 7));
|
||||||
|
} else if (param.first == id_BYPASS_PREADD9 && param.second.str == "BYPASS") {
|
||||||
|
// PREADD9 bypass
|
||||||
|
config.insert("BYPASS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string config_str;
|
||||||
|
for (const auto &cfg : config) {
|
||||||
|
if (!config_str.empty())
|
||||||
|
config_str += ',';
|
||||||
|
config_str += cfg;
|
||||||
|
}
|
||||||
|
cell->tmg_index = get_cell_timing_idx(cell->type, id(config_str));
|
||||||
|
if (cell->tmg_index == -1) {
|
||||||
|
log_warning("Unsupported timing config '%s' on %s cell '%s', falling back to default.\n",
|
||||||
|
config_str.c_str(), nameOf(cell->type), nameOf(cell));
|
||||||
|
cell->tmg_index = get_cell_timing_idx(cell->type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user