aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ecp5/arch.cc2
-rw-r--r--ecp5/archdefs.h8
-rw-r--r--ecp5/pack.cc35
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)) {