diff options
author | David Shah <dave@ds0.me> | 2020-11-20 10:27:04 +0000 |
---|---|---|
committer | David Shah <dave@ds0.me> | 2020-11-30 08:45:28 +0000 |
commit | df3c6dfe3ec3740007d4dbd8ae6347e2af345c79 (patch) | |
tree | f4844a5c61ba9ba662c38d7f89f1e12eac12e62c /nexus | |
parent | 76543d05e7ef9b7bd87b7641ace08360d81dc023 (diff) | |
download | nextpnr-df3c6dfe3ec3740007d4dbd8ae6347e2af345c79.tar.gz nextpnr-df3c6dfe3ec3740007d4dbd8ae6347e2af345c79.tar.bz2 nextpnr-df3c6dfe3ec3740007d4dbd8ae6347e2af345c79.zip |
nexus: Preliminary integration of DSP timing data
Signed-off-by: David Shah <dave@ds0.me>
Diffstat (limited to 'nexus')
-rw-r--r-- | nexus/arch.cc | 49 | ||||
-rw-r--r-- | nexus/arch.h | 4 | ||||
-rw-r--r-- | nexus/pack.cc | 62 |
3 files changed, 111 insertions, 4 deletions
diff --git a/nexus/arch.cc b/nexus/arch.cc index 1ecf168d..d5e901eb 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -452,6 +452,10 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort if (toPort == id_F || toPort == id_OFX) 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; } @@ -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 { 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 (port == id_A || port == id_B || port == id_C || port == id_D || port == id_SEL || port == id_F1 || port == id_FCI || port == id_WDI) @@ -498,6 +507,15 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return TMG_CLOCK_INPUT; clockInfoCount = 1; 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; } @@ -531,6 +549,14 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port } // Lookup edge based on inversion 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 { 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 +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 { 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.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; + } +} // ----------------------------------------------------------------------- diff --git a/nexus/arch.h b/nexus/arch.h index debf66b0..1367161c 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1559,6 +1559,8 @@ struct Arch : BaseCtx // ------------------------------------------------- // 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 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 @@ -1571,6 +1573,8 @@ struct Arch : BaseCtx DelayInfo &hold) const; // 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; + // 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 diff --git a/nexus/pack.cc b/nexus/pack.cc index a410e2bd..ff5d1047 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -264,7 +264,7 @@ struct NexusPacker 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 // 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); for (IdString pin : ctx->getBelPins(bel)) { PortType dir = ctx->getBelPinType(bel, pin); - if (dir != PORT_IN && !include_outputs) + if (dir != PORT_IN) continue; if (cell->ports.count(pin)) continue; @@ -1236,6 +1236,14 @@ struct NexusPacker // 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) { + // 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)) { // Skip if not an output, or being used already for something else if (port.second.type != PORT_OUT || port.second.net != nullptr) @@ -1371,9 +1379,9 @@ struct NexusPacker ctx->nameOf(root->type)); // Create the necessary new ports - autocreate_ports(root, true); + autocreate_ports(root); for (auto child : root->constr_children) - autocreate_ports(child, true); + autocreate_ports(child); // Insert cascade connections from all cells in the macro 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) { 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")); 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); + } } } |