diff options
author | Ross Schlaikjer <ross@schlaikjer.net> | 2020-04-28 20:01:29 -0400 |
---|---|---|
committer | Ross Schlaikjer <ross@schlaikjer.net> | 2020-04-28 20:01:29 -0400 |
commit | 5e763b1afc2f966d794bc3fc6579ee1233f53a2c (patch) | |
tree | 3e574cd5ec49f0f0cc5544a2cf5a3165b8e136a0 /ecp5 | |
parent | 5c6b2cbafef7435bd697cedf30436bf16e70dc15 (diff) | |
download | nextpnr-5e763b1afc2f966d794bc3fc6579ee1233f53a2c.tar.gz nextpnr-5e763b1afc2f966d794bc3fc6579ee1233f53a2c.tar.bz2 nextpnr-5e763b1afc2f966d794bc3fc6579ee1233f53a2c.zip |
Alter MULT18X18D timing db based on register config
If the REG_INPUTA_CLK and REG_INPUTB_CLK values are set, then we should
use the faster setup/hold timings for the 18x8 multiplier.
Similarly, check the value of REG_OUTPUT_CLK for whether or not to use
faster timings for the output.
This is based on how I currently understand the registers to work - if
anyone knows the actual rules for when each timing applies please do
chime in to correct this implementation if necessary.
Along the same lines, this PR does not address the case when the
pipeline registers are enabled, since it is not clear to me how exactly
that affects the timing.
Diffstat (limited to 'ecp5')
-rw-r--r-- | ecp5/arch.cc | 2 | ||||
-rw-r--r-- | ecp5/archdefs.h | 8 | ||||
-rw-r--r-- | ecp5/pack.cc | 35 |
3 files changed, 43 insertions, 2 deletions
diff --git a/ecp5/arch.cc b/ecp5/arch.cc index ab24842e..fa8dc242 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -855,7 +855,7 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort std::string fn = fromPort.str(this), tn = toPort.str(this); if (fn.size() > 1 && (fn.front() == 'A' || fn.front() == 'B') && std::isdigit(fn.at(1))) { if (tn.size() > 1 && tn.front() == 'P' && std::isdigit(tn.at(1))) - return getDelayFromTimingDatabase(id_MULT18X18D_REGS_NONE, id(std::string("") + fn.front()), id_P, + return getDelayFromTimingDatabase(cell->multInfo.timing_id, id(std::string("") + fn.front()), id_P, delay); } return false; diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h index 0f197345..d32e4a23 100644 --- a/ecp5/archdefs.h +++ b/ecp5/archdefs.h @@ -187,8 +187,14 @@ struct ArchCellInfo // Which timing information to use for a DP16KD. Depends on registering // configuration. nextpnr_ecp5::IdString regmode_timing_id; - } ramInfo; + struct + { + bool is_in_a_registered; + bool is_in_b_registered; + bool is_output_registered; + nextpnr_ecp5::IdString timing_id; + } multInfo; }; NEXTPNR_NAMESPACE_END diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 0872ae58..b92a2dfd 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -3037,6 +3037,41 @@ void Arch::assignArchInfo() } else if (ci->ramInfo.is_output_a_registered && ci->ramInfo.is_output_b_registered) { ci->ramInfo.regmode_timing_id = id_DP16KD_REGMODE_A_OUTREG_REGMODE_B_OUTREG; } + } else if (ci->type == id_MULT18X18D) { + // Check if the inputs are registered + // Get the input clock setting from the cell + std::string reg_inputa_clk = str_or_default(ci->params, id("REG_INPUTA_CLK"), "NONE"); + std::string reg_inputb_clk = str_or_default(ci->params, id("REG_INPUTB_CLK"), "NONE"); + // Assert we have valid settings for the input clocks + if (reg_inputa_clk != "NONE" && reg_inputa_clk != "CLK0" && reg_inputa_clk != "CLK1" && + reg_inputa_clk != "CLK2" && reg_inputa_clk != "CLK3") + log_error("MULT18X18D %s has invalid REG_INPUTA_CLK configuration '%s'\n", ci->name.c_str(this), + reg_inputa_clk.c_str()); + if (reg_inputb_clk != "NONE" && reg_inputb_clk != "CLK0" && reg_inputb_clk != "CLK1" && + reg_inputb_clk != "CLK2" && reg_inputb_clk != "CLK3") + log_error("MULT18X18D %s has invalid REG_INPUTB_CLK configuration '%s'\n", ci->name.c_str(this), + reg_inputb_clk.c_str()); + // Inputs are registered IFF the REG_INPUT value is not NONE + ci->multInfo.is_in_a_registered = reg_inputa_clk != "NONE"; + ci->multInfo.is_in_b_registered = reg_inputb_clk != "NONE"; + // Similarly, get the output register clock + std::string reg_output_clk = str_or_default(ci->params, id("REG_OUTPUT_CLK"), "NONE"); + if (reg_output_clk != "NONE" && reg_output_clk != "CLK0" && reg_output_clk != "CLK1" && + reg_output_clk != "CLK2" && reg_output_clk != "CLK3") + log_error("MULT18X18D %s has invalid REG_OUTPUT_CLK configuration '%s'\n", ci->name.c_str(this), + reg_output_clk.c_str()); + ci->multInfo.is_output_registered = reg_output_clk != "NONE"; + // Based on our register settings, pick the timing data to use for this cell + const bool both_inputs_registered = ci->multInfo.is_in_a_registered && ci->multInfo.is_in_b_registered; + if (!both_inputs_registered && !ci->multInfo.is_output_registered) { + ci->multInfo.timing_id = id_MULT18X18D_REGS_NONE; + } else if (both_inputs_registered && !ci->multInfo.is_output_registered) { + ci->multInfo.timing_id = id_MULT18X18D_REGS_INPUT; + } else if (!both_inputs_registered && ci->multInfo.is_output_registered) { + ci->multInfo.timing_id = id_MULT18X18D_REGS_OUTPUT; + } else if (both_inputs_registered && ci->multInfo.is_output_registered) { + ci->multInfo.timing_id = id_MULT18X18D_REGS_ALL; + } } } for (auto net : sorted(nets)) { |