Issue warning for mixed-mode inputs

This commit is contained in:
Ross Schlaikjer 2020-04-29 14:39:52 -04:00
parent 6625284950
commit 0043ae0807
No known key found for this signature in database
GPG Key ID: 3C4CAA5FEDEF28DA
3 changed files with 46 additions and 40 deletions

View File

@ -938,30 +938,20 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in
} else if (cell->type == id_MULT18X18D) { } else if (cell->type == id_MULT18X18D) {
if (port == id_CLK0 || port == id_CLK1 || port == id_CLK2 || port == id_CLK3) if (port == id_CLK0 || port == id_CLK1 || port == id_CLK2 || port == id_CLK3)
return TMG_CLOCK_INPUT; 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); std::string pname = port.str(this);
if (pname.size() > 1) { if (pname.size() > 1) {
if (pname.front() == 'A' && std::isdigit(pname.at(1))) { if ((pname.front() == 'A' || pname.front() == 'B' || pname.front() == 'P') && std::isdigit(pname.at(1)))
if (cell->multInfo.is_in_a_registered) { if (cell->multInfo.is_clocked)
clockInfoCount = 1;
return TMG_REGISTER_INPUT; return TMG_REGISTER_INPUT;
}
return TMG_COMB_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; return TMG_IGNORE;
} else if (cell->type == id_ALU54B) { } else if (cell->type == id_ALU54B) {
return TMG_IGNORE; // FIXME return TMG_IGNORE; // FIXME
@ -1143,16 +1133,12 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
return base.compare(0, prefix.size(), prefix) == 0; return base.compare(0, prefix.size(), prefix) == 0;
}; };
IdString port_group; IdString port_group;
IdString clock_id = id_CLK0;
if (has_prefix(port_name, "A")) { if (has_prefix(port_name, "A")) {
port_group = id_A; port_group = id_A;
} else if (has_prefix(port_name, "B")) { } else if (has_prefix(port_name, "B")) {
port_group = id_B; port_group = id_B;
} else if (has_prefix(port_name, "P")) { } else if (has_prefix(port_name, "P")) {
port_group = id_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")) { } else if (has_prefix(port_name, "CE")) {
port_group = id_CE0; port_group = id_CE0;
} else if (has_prefix(port_name, "RST")) { } 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 // 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) { if (cell->ports.at(port).type == PORT_OUT) {
bool is_path = getDelayFromTimingDatabase(cell->multInfo.timing_id, clock_id, port_group, info.clockToQ); bool is_path = getDelayFromTimingDatabase(cell->multInfo.timing_id, clock_id, port_group, info.clockToQ);
NPNR_ASSERT(is_path); NPNR_ASSERT(is_path);

View File

@ -190,9 +190,7 @@ struct ArchCellInfo
} ramInfo; } ramInfo;
struct struct
{ {
bool is_in_a_registered; bool is_clocked;
bool is_in_b_registered;
bool is_output_registered;
nextpnr_ecp5::IdString timing_id; nextpnr_ecp5::IdString timing_id;
} multInfo; } multInfo;
}; };

View File

@ -3052,27 +3052,48 @@ void Arch::assignArchInfo()
log_error("MULT18X18D %s has invalid REG_INPUTB_CLK configuration '%s'\n", ci->name.c_str(this), log_error("MULT18X18D %s has invalid REG_INPUTB_CLK configuration '%s'\n", ci->name.c_str(this),
reg_inputb_clk.c_str()); reg_inputb_clk.c_str());
// Inputs are registered IFF the REG_INPUT value is not NONE // Inputs are registered IFF the REG_INPUT value is not NONE
ci->multInfo.is_in_a_registered = reg_inputa_clk != "NONE"; const bool is_in_a_registered = reg_inputa_clk != "NONE";
ci->multInfo.is_in_b_registered = reg_inputb_clk != "NONE"; const bool is_in_b_registered = reg_inputb_clk != "NONE";
// Similarly, get the output register clock // Similarly, get the output register clock
std::string reg_output_clk = str_or_default(ci->params, id("REG_OUTPUT_CLK"), "NONE"); 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" && if (reg_output_clk != "NONE" && reg_output_clk != "CLK0" && reg_output_clk != "CLK1" &&
reg_output_clk != "CLK2" && reg_output_clk != "CLK3") 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), log_error("MULT18X18D %s has invalid REG_OUTPUT_CLK configuration '%s'\n", ci->name.c_str(this),
reg_output_clk.c_str()); reg_output_clk.c_str());
ci->multInfo.is_output_registered = reg_output_clk != "NONE"; 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 // 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 && !is_output_registered) {
if (!both_inputs_registered && !ci->multInfo.is_output_registered) {
ci->multInfo.timing_id = id_MULT18X18D_REGS_NONE; ci->multInfo.timing_id = id_MULT18X18D_REGS_NONE;
} else if (both_inputs_registered && !ci->multInfo.is_output_registered) { } else if (both_inputs_registered && !is_output_registered) {
ci->multInfo.timing_id = id_MULT18X18D_REGS_INPUT; ci->multInfo.timing_id = id_MULT18X18D_REGS_INPUT;
} else if (!both_inputs_registered && ci->multInfo.is_output_registered) { } else if (!both_inputs_registered && is_output_registered) {
ci->multInfo.timing_id = id_MULT18X18D_REGS_OUTPUT; ci->multInfo.timing_id = id_MULT18X18D_REGS_OUTPUT;
} else if (both_inputs_registered && ci->multInfo.is_output_registered) { } else if (both_inputs_registered && is_output_registered) {
ci->multInfo.timing_id = id_MULT18X18D_REGS_ALL; 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)) { for (auto net : sorted(nets)) {
net.second->is_global = bool_or_default(net.second->attrs, id("ECP5_IS_GLOBAL")); net.second->is_global = bool_or_default(net.second->attrs, id("ECP5_IS_GLOBAL"));