From 5e763b1afc2f966d794bc3fc6579ee1233f53a2c Mon Sep 17 00:00:00 2001 From: Ross Schlaikjer Date: Tue, 28 Apr 2020 20:01:29 -0400 Subject: 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. --- ecp5/arch.cc | 2 +- ecp5/archdefs.h | 8 +++++++- 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)) { -- cgit v1.2.3 From a4fa95374057d700a873be7687ed26a3dceae9ef Mon Sep 17 00:00:00 2001 From: Ross Schlaikjer Date: Wed, 29 Apr 2020 11:08:53 -0400 Subject: Use registered port class on mult18x18 --- ecp5/arch.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index fa8dc242..a6ac9f84 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -940,10 +940,12 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return TMG_CLOCK_INPUT; std::string pname = port.str(this); if (pname.size() > 1) { - if ((pname.front() == 'A' || pname.front() == 'B') && std::isdigit(pname.at(1))) - return TMG_COMB_INPUT; + if (pname.front() == 'A' && std::isdigit(pname.at(1))) + return cell->multInfo.is_in_a_registered ? TMG_REGISTER_INPUT : TMG_COMB_INPUT; + if (pname.front() == 'B' && std::isdigit(pname.at(1))) + return cell->multInfo.is_in_b_registered ? TMG_REGISTER_INPUT : TMG_COMB_INPUT; if (pname.front() == 'P' && std::isdigit(pname.at(1))) - return TMG_COMB_OUTPUT; + return cell->multInfo.is_output_registered ? TMG_REGISTER_OUTPUT : TMG_COMB_OUTPUT; } return TMG_IGNORE; } else if (cell->type == id_ALU54B) { -- cgit v1.2.3 From 66252849506738f16c70bc8d78486500aaccd84c Mon Sep 17 00:00:00 2001 From: Ross Schlaikjer Date: Wed, 29 Apr 2020 13:57:04 -0400 Subject: Handle register timing case --- ecp5/arch.cc | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index a6ac9f84..7dd85b70 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -940,12 +940,27 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return TMG_CLOCK_INPUT; std::string pname = port.str(this); if (pname.size() > 1) { - if (pname.front() == 'A' && std::isdigit(pname.at(1))) - return cell->multInfo.is_in_a_registered ? TMG_REGISTER_INPUT : TMG_COMB_INPUT; - if (pname.front() == 'B' && std::isdigit(pname.at(1))) - return cell->multInfo.is_in_b_registered ? TMG_REGISTER_INPUT : TMG_COMB_INPUT; - if (pname.front() == 'P' && std::isdigit(pname.at(1))) - return cell->multInfo.is_output_registered ? TMG_REGISTER_OUTPUT : TMG_COMB_OUTPUT; + if (pname.front() == 'A' && std::isdigit(pname.at(1))) { + if (cell->multInfo.is_in_a_registered) { + clockInfoCount = 1; + return TMG_REGISTER_INPUT; + } + return TMG_COMB_INPUT; + } + if (pname.front() == 'B' && std::isdigit(pname.at(1))) { + if (cell->multInfo.is_in_b_registered) { + clockInfoCount = 1; + return TMG_REGISTER_INPUT; + } + return TMG_COMB_INPUT; + } + if (pname.front() == 'P' && std::isdigit(pname.at(1))) { + if (cell->multInfo.is_output_registered) { + clockInfoCount = 1; + return TMG_REGISTER_OUTPUT; + } + return TMG_COMB_OUTPUT; + } } return TMG_IGNORE; } else if (cell->type == id_ALU54B) { @@ -1119,6 +1134,43 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port } else { NPNR_ASSERT_FALSE("unknown DQSBUFM register port"); } + } else if (cell->type == id_MULT18X18D) { + std::string port_name = port.str(this); + // To keep the timing DB small, like signals (e.g. P[35:0] have been + // grouped. To look up the timing, we therefore need to map this port + // to the enclosing port group. + auto has_prefix = [](std::string base, std::string prefix) { + return base.compare(0, prefix.size(), prefix) == 0; + }; + IdString port_group; + IdString clock_id = id_CLK0; + if (has_prefix(port_name, "A")) { + port_group = id_A; + } else if (has_prefix(port_name, "B")) { + port_group = id_B; + } else if (has_prefix(port_name, "P")) { + port_group = id_P; + // If the output is registered, we care about propagation delay from CLK. + // If it is not registered, our propagation delay is from A/B + clock_id = cell->multInfo.is_output_registered ? id_CLK0 : id_A; + } else if (has_prefix(port_name, "CE")) { + port_group = id_CE0; + } else if (has_prefix(port_name, "RST")) { + port_group = id_RST0; + } else if (has_prefix(port_name, "SIGNED")) { + // Both SIGNEDA and SIGNEDB exist in the DB, so can directly use these here + port_group = port; + } else { + NPNR_ASSERT_FALSE("Unknown MULT18X18D register port"); + } + + // If this port is clocked at all, it must be clocked from CLK0 + if (cell->ports.at(port).type == PORT_OUT) { + bool is_path = getDelayFromTimingDatabase(cell->multInfo.timing_id, clock_id, port_group, info.clockToQ); + NPNR_ASSERT(is_path); + } else { + getSetupHoldFromTimingDatabase(cell->multInfo.timing_id, clock_id, port_group, info.setup, info.hold); + } } return info; } -- cgit v1.2.3 From 0043ae0807affd77749b162199cd4da3b37d994c Mon Sep 17 00:00:00 2001 From: Ross Schlaikjer Date: Wed, 29 Apr 2020 14:39:52 -0400 Subject: Issue warning for mixed-mode inputs --- ecp5/arch.cc | 35 +++++++++++------------------------ ecp5/archdefs.h | 4 +--- ecp5/pack.cc | 47 ++++++++++++++++++++++++++++++++++------------- 3 files changed, 46 insertions(+), 40 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 7dd85b70..4e149806 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -938,29 +938,19 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in } else if (cell->type == id_MULT18X18D) { if (port == id_CLK0 || port == id_CLK1 || port == id_CLK2 || port == id_CLK3) return TMG_CLOCK_INPUT; + if (port == id_CE0 || port == id_CE1 || port == id_CE2 || port == id_CE3) + return cell->multInfo.is_clocked ? TMG_REGISTER_INPUT : TMG_COMB_INPUT; + if (port == id_RST0 || port == id_RST1 || port == id_RST2 || port == id_RST3) + return cell->multInfo.is_clocked ? TMG_REGISTER_INPUT : TMG_COMB_INPUT; + if (port == id_SIGNEDA || port == id_SIGNEDB) + return cell->multInfo.is_clocked ? TMG_REGISTER_INPUT : TMG_COMB_INPUT; std::string pname = port.str(this); if (pname.size() > 1) { - if (pname.front() == 'A' && std::isdigit(pname.at(1))) { - if (cell->multInfo.is_in_a_registered) { - clockInfoCount = 1; + if ((pname.front() == 'A' || pname.front() == 'B' || pname.front() == 'P') && std::isdigit(pname.at(1))) + if (cell->multInfo.is_clocked) return TMG_REGISTER_INPUT; - } - return TMG_COMB_INPUT; - } - if (pname.front() == 'B' && std::isdigit(pname.at(1))) { - if (cell->multInfo.is_in_b_registered) { - clockInfoCount = 1; - return TMG_REGISTER_INPUT; - } - return TMG_COMB_INPUT; - } - if (pname.front() == 'P' && std::isdigit(pname.at(1))) { - if (cell->multInfo.is_output_registered) { - clockInfoCount = 1; - return TMG_REGISTER_OUTPUT; - } - return TMG_COMB_OUTPUT; - } + + return TMG_COMB_INPUT; } return TMG_IGNORE; } else if (cell->type == id_ALU54B) { @@ -1143,16 +1133,12 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port return base.compare(0, prefix.size(), prefix) == 0; }; IdString port_group; - IdString clock_id = id_CLK0; if (has_prefix(port_name, "A")) { port_group = id_A; } else if (has_prefix(port_name, "B")) { port_group = id_B; } else if (has_prefix(port_name, "P")) { port_group = id_P; - // If the output is registered, we care about propagation delay from CLK. - // If it is not registered, our propagation delay is from A/B - clock_id = cell->multInfo.is_output_registered ? id_CLK0 : id_A; } else if (has_prefix(port_name, "CE")) { port_group = id_CE0; } else if (has_prefix(port_name, "RST")) { @@ -1165,6 +1151,7 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port } // If this port is clocked at all, it must be clocked from CLK0 + IdString clock_id = id_CLK0; if (cell->ports.at(port).type == PORT_OUT) { bool is_path = getDelayFromTimingDatabase(cell->multInfo.timing_id, clock_id, port_group, info.clockToQ); NPNR_ASSERT(is_path); diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h index d32e4a23..586c385f 100644 --- a/ecp5/archdefs.h +++ b/ecp5/archdefs.h @@ -190,9 +190,7 @@ struct ArchCellInfo } ramInfo; struct { - bool is_in_a_registered; - bool is_in_b_registered; - bool is_output_registered; + bool is_clocked; nextpnr_ecp5::IdString timing_id; } multInfo; }; diff --git a/ecp5/pack.cc b/ecp5/pack.cc index b92a2dfd..1c1df2ee 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -3052,26 +3052,47 @@ void Arch::assignArchInfo() 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"; + const bool is_in_a_registered = reg_inputa_clk != "NONE"; + const bool 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; + const bool is_output_registered = reg_output_clk != "NONE"; + + // If only one of the inputs is registered, we are going to treat that as + // neither input registered so that we don't have to deal with mixed timing. + // Emit a warning to that effect. + const bool any_input_registered = is_in_a_registered || is_in_b_registered; + const bool both_inputs_registered = is_in_a_registered && is_in_b_registered; + const bool input_registers_mismatched = any_input_registered && !both_inputs_registered; + if (input_registers_mismatched) { + log_warning("MULT18X18D %s has unsupported mixed input register modes (reg_inputa_clk=%s, " + "reg_inputb_clk=%s)\n", + ci->name.c_str(this), reg_inputa_clk.c_str(), reg_inputb_clk.c_str()); + log_warning("Timings for MULT18X18D %s will be calculated as though neither input were registered\n", + ci->name.c_str(this)); + + // Act as though the inputs are unregistered, so select timing DB based only on the + // output register mode + ci->multInfo.timing_id = is_output_registered ? id_MULT18X18D_REGS_OUTPUT : id_MULT18X18D_REGS_NONE; + } else { + // Based on our register settings, pick the timing data to use for this cell + if (!both_inputs_registered && !is_output_registered) { + ci->multInfo.timing_id = id_MULT18X18D_REGS_NONE; + } else if (both_inputs_registered && !is_output_registered) { + ci->multInfo.timing_id = id_MULT18X18D_REGS_INPUT; + } else if (!both_inputs_registered && is_output_registered) { + ci->multInfo.timing_id = id_MULT18X18D_REGS_OUTPUT; + } else if (both_inputs_registered && is_output_registered) { + ci->multInfo.timing_id = id_MULT18X18D_REGS_ALL; + } } + // If we aren't a pure combinatorial multiplier, then our timings are + // calculated with respect to CLK0 + ci->multInfo.is_clocked = ci->multInfo.timing_id != id_MULT18X18D_REGS_NONE; } } for (auto net : sorted(nets)) { -- cgit v1.2.3 From 6e8082860e2e7e0a31bb258ab633ef3146e5c9b3 Mon Sep 17 00:00:00 2001 From: Ross Schlaikjer Date: Wed, 29 Apr 2020 14:46:09 -0400 Subject: Dedupe clock error check --- ecp5/pack.cc | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 1c1df2ee..c268e2ac 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -3042,24 +3042,25 @@ void Arch::assignArchInfo() // 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"); + + // The clock check is the same IN_A/B and OUT, so hoist it to a function + auto verify_clock_parameter = [&](std::string param_name, std::string clk) { + if (clk != "NONE" && clk != "CLK0" && clk != "CLK1" && clk != "CLK2" && clk != "CLK3") + log_error("MULT18X18D %s has invalid %s configuration '%s'\n", ci->name.c_str(this), + param_name.c_str(), clk.c_str()); + }; + // 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()); + verify_clock_parameter("REG_INPUTA_CLK", reg_inputa_clk); + verify_clock_parameter("REG_INPUTB_CLK", reg_inputb_clk); + // Inputs are registered IFF the REG_INPUT value is not NONE const bool is_in_a_registered = reg_inputa_clk != "NONE"; const bool 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()); + verify_clock_parameter("REG_OUTPUT_CLK", reg_output_clk); const bool is_output_registered = reg_output_clk != "NONE"; // If only one of the inputs is registered, we are going to treat that as -- cgit v1.2.3 From de6ddc470b944a0d0be81c5b6469ef183ba7268e Mon Sep 17 00:00:00 2001 From: Ross Schlaikjer Date: Wed, 29 Apr 2020 14:51:07 -0400 Subject: Further condense --- ecp5/pack.cc | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/ecp5/pack.cc b/ecp5/pack.cc index c268e2ac..55b2c791 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -3038,29 +3038,28 @@ void Arch::assignArchInfo() 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"); - + // For the multiplier block, our timing db is dictated by whether any of the input/output registers are + // enabled. To that end, we need to work out what the parameters are for the INPUTA_CLK, INPUTB_CLK and + // OUTPUT_CLK are. // The clock check is the same IN_A/B and OUT, so hoist it to a function - auto verify_clock_parameter = [&](std::string param_name, std::string clk) { + auto get_clock_parameter = [&](std::string param_name) { + std::string clk = str_or_default(ci->params, id(param_name), "NONE"); if (clk != "NONE" && clk != "CLK0" && clk != "CLK1" && clk != "CLK2" && clk != "CLK3") log_error("MULT18X18D %s has invalid %s configuration '%s'\n", ci->name.c_str(this), param_name.c_str(), clk.c_str()); + return clk; }; - // Assert we have valid settings for the input clocks - verify_clock_parameter("REG_INPUTA_CLK", reg_inputa_clk); - verify_clock_parameter("REG_INPUTB_CLK", reg_inputb_clk); + // Get the input clock setting from the cell + std::string reg_inputa_clk = get_clock_parameter("REG_INPUTA_CLK"); + std::string reg_inputb_clk = get_clock_parameter("REG_INPUTB_CLK"); // Inputs are registered IFF the REG_INPUT value is not NONE const bool is_in_a_registered = reg_inputa_clk != "NONE"; const bool 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"); - verify_clock_parameter("REG_OUTPUT_CLK", reg_output_clk); + std::string reg_output_clk = get_clock_parameter("REG_OUTPUT_CLK"); const bool is_output_registered = reg_output_clk != "NONE"; // If only one of the inputs is registered, we are going to treat that as -- cgit v1.2.3 From a1160068c8d3efccce85a88749e1d58da6aaaf37 Mon Sep 17 00:00:00 2001 From: Ross Schlaikjer Date: Thu, 30 Apr 2020 11:09:22 -0400 Subject: No cell delay for clocked MULT18X18D --- ecp5/arch.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 4e149806..590cd89c 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -852,6 +852,8 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort } else if (cell->type == id_DP16KD) { return false; } else if (cell->type == id_MULT18X18D) { + if (cell->multInfo.is_clocked) + return false; 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))) -- cgit v1.2.3 From 84327b634c09b67ef965e3056dd29a7ee4169039 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 1 May 2020 08:17:29 +0100 Subject: ecp5: MULT18X18D timing fixes Signed-off-by: David Shah --- ecp5/arch.cc | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 590cd89c..db043f35 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -940,19 +940,33 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in } else if (cell->type == id_MULT18X18D) { if (port == id_CLK0 || port == id_CLK1 || port == id_CLK2 || port == id_CLK3) return TMG_CLOCK_INPUT; - if (port == id_CE0 || port == id_CE1 || port == id_CE2 || port == id_CE3) - return cell->multInfo.is_clocked ? TMG_REGISTER_INPUT : TMG_COMB_INPUT; - if (port == id_RST0 || port == id_RST1 || port == id_RST2 || port == id_RST3) - return cell->multInfo.is_clocked ? TMG_REGISTER_INPUT : TMG_COMB_INPUT; - if (port == id_SIGNEDA || port == id_SIGNEDB) - return cell->multInfo.is_clocked ? TMG_REGISTER_INPUT : TMG_COMB_INPUT; + if (port == id_CE0 || port == id_CE1 || port == id_CE2 || port == id_CE3 || port == id_RST0 || + port == id_RST1 || port == id_RST2 || port == id_RST3 || port == id_SIGNEDA || port == id_SIGNEDB) { + if (cell->multInfo.is_clocked) { + clockInfoCount = 1; + return TMG_REGISTER_INPUT; + } else { + return TMG_COMB_INPUT; + } + } std::string pname = port.str(this); if (pname.size() > 1) { - if ((pname.front() == 'A' || pname.front() == 'B' || pname.front() == 'P') && std::isdigit(pname.at(1))) - if (cell->multInfo.is_clocked) + if ((pname.front() == 'A' || pname.front() == 'B') && std::isdigit(pname.at(1))) { + if (cell->multInfo.is_clocked) { + clockInfoCount = 1; return TMG_REGISTER_INPUT; - - return TMG_COMB_INPUT; + } else { + return TMG_COMB_INPUT; + } + } + if ((pname.front() == 'P') && std::isdigit(pname.at(1))) { + if (cell->multInfo.is_clocked) { + clockInfoCount = 1; + return TMG_REGISTER_OUTPUT; + } else { + return TMG_COMB_OUTPUT; + } + } } return TMG_IGNORE; } else if (cell->type == id_ALU54B) { @@ -1154,6 +1168,8 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port // If this port is clocked at all, it must be clocked from CLK0 IdString clock_id = id_CLK0; + info.clock_port = clock_id; + info.edge = RISING_EDGE; if (cell->ports.at(port).type == PORT_OUT) { bool is_path = getDelayFromTimingDatabase(cell->multInfo.timing_id, clock_id, port_group, info.clockToQ); NPNR_ASSERT(is_path); -- cgit v1.2.3