mirror of https://github.com/YosysHQ/yosys.git
create duplicate IOFFs if multiple output ports are connected to the same register
This commit is contained in:
parent
25b400982b
commit
303a386ecc
|
@ -29,7 +29,17 @@ struct QlIoffPass : public Pass {
|
||||||
if (!module)
|
if (!module)
|
||||||
return;
|
return;
|
||||||
modwalker.setup(module);
|
modwalker.setup(module);
|
||||||
pool<RTLIL::Cell *> cells_to_replace;
|
pool<RTLIL::Cell *> input_ffs;
|
||||||
|
dict<RTLIL::Wire *, std::vector<Cell*>> output_ffs;
|
||||||
|
dict<SigBit, pool<SigBit>> output_bit_aliases;
|
||||||
|
|
||||||
|
for (Wire* wire : module->wires())
|
||||||
|
if (wire->port_output) {
|
||||||
|
output_ffs[wire].resize(wire->width, nullptr);
|
||||||
|
for (SigBit bit : SigSpec(wire))
|
||||||
|
output_bit_aliases[modwalker.sigmap(bit)].insert(bit);
|
||||||
|
}
|
||||||
|
|
||||||
for (auto cell : module->selected_cells()) {
|
for (auto cell : module->selected_cells()) {
|
||||||
if (cell->type.in(ID(dffsre), ID(sdffsre))) {
|
if (cell->type.in(ID(dffsre), ID(sdffsre))) {
|
||||||
log_debug("Checking cell %s.\n", cell->name.c_str());
|
log_debug("Checking cell %s.\n", cell->name.c_str());
|
||||||
|
@ -53,32 +63,62 @@ struct QlIoffPass : public Pass {
|
||||||
log_debug("not promoting: D has other consumers\n");
|
log_debug("not promoting: D has other consumers\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
cells_to_replace.insert(cell);
|
input_ffs.insert(cell);
|
||||||
continue; // no need to check Q if we already put it on the list
|
continue; // prefer input FFs over output FFs
|
||||||
}
|
}
|
||||||
|
|
||||||
SigSpec q = cell->getPort(ID::Q);
|
SigSpec q = cell->getPort(ID::Q);
|
||||||
log_assert(GetSize(q) == 1);
|
log_assert(GetSize(q) == 1);
|
||||||
if (modwalker.has_outputs(q)) {
|
if (modwalker.has_outputs(q) && !modwalker.has_consumers(q)) {
|
||||||
log_debug("Cell %s is potentially eligible for promotion to output IOFF.\n", cell->name.c_str());
|
log_debug("Cell %s is potentially eligible for promotion to output IOFF.\n", cell->name.c_str());
|
||||||
// check that q_sig has no other consumers
|
for (SigBit bit : output_bit_aliases[modwalker.sigmap(q)]) {
|
||||||
pool<ModWalker::PortBit> portbits;
|
log_assert(bit.is_wire());
|
||||||
modwalker.get_consumers(portbits, q);
|
output_ffs[bit.wire][bit.offset] = cell;
|
||||||
if (GetSize(portbits) > 0) {
|
|
||||||
log_debug("not promoting: Q has other consumers\n");
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
cells_to_replace.insert(cell);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto cell : cells_to_replace) {
|
for (auto cell : input_ffs) {
|
||||||
log("Promoting register %s to IOFF.\n", log_signal(cell->getPort(ID::Q)));
|
log("Promoting register %s to input IOFF.\n", log_signal(cell->getPort(ID::Q)));
|
||||||
cell->type = ID(dff);
|
cell->type = ID(dff);
|
||||||
cell->unsetPort(ID::E);
|
cell->unsetPort(ID::E);
|
||||||
cell->unsetPort(ID::R);
|
cell->unsetPort(ID::R);
|
||||||
cell->unsetPort(ID::S);
|
cell->unsetPort(ID::S);
|
||||||
}
|
}
|
||||||
|
for (auto & [old_port_output, ioff_cells] : output_ffs) {
|
||||||
|
if (std::any_of(ioff_cells.begin(), ioff_cells.end(), [](Cell * c) { return c != nullptr; }))
|
||||||
|
{
|
||||||
|
// create replacement output wire
|
||||||
|
RTLIL::Wire* new_port_output = module->addWire(NEW_ID, old_port_output->width);
|
||||||
|
new_port_output->start_offset = old_port_output->start_offset;
|
||||||
|
module->swap_names(old_port_output, new_port_output);
|
||||||
|
std::swap(old_port_output->port_id, new_port_output->port_id);
|
||||||
|
std::swap(old_port_output->port_input, new_port_output->port_input);
|
||||||
|
std::swap(old_port_output->port_output, new_port_output->port_output);
|
||||||
|
std::swap(old_port_output->upto, new_port_output->upto);
|
||||||
|
std::swap(old_port_output->is_signed, new_port_output->is_signed);
|
||||||
|
std::swap(old_port_output->attributes, new_port_output->attributes);
|
||||||
|
|
||||||
|
// create new output FFs
|
||||||
|
SigSpec sig_o(old_port_output);
|
||||||
|
SigSpec sig_n(new_port_output);
|
||||||
|
for (int i = 0; i < new_port_output->width; i++) {
|
||||||
|
if (ioff_cells[i]) {
|
||||||
|
log("Promoting %s to output IOFF.\n", log_signal(sig_n[i]));
|
||||||
|
|
||||||
|
RTLIL::Cell *new_cell = module->addCell(NEW_ID, ID(dff));
|
||||||
|
new_cell->setPort(ID::C, ioff_cells[i]->getPort(ID::C));
|
||||||
|
new_cell->setPort(ID::D, ioff_cells[i]->getPort(ID::D));
|
||||||
|
new_cell->setPort(ID::Q, sig_n[i]);
|
||||||
|
new_cell->set_bool_attribute(ID::keep);
|
||||||
|
} else {
|
||||||
|
module->connect(sig_n[i], sig_o[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} QlIoffPass;
|
} QlIoffPass;
|
||||||
|
|
||||||
|
|
|
@ -334,9 +334,10 @@ struct SynthQuickLogicPass : public ScriptPass {
|
||||||
run("opt_lut");
|
run("opt_lut");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_label("iomap", "(for qlf_k6n10f)") && (family == "qlf_k6n10f" || help_mode)) {
|
if (check_label("iomap", "(for qlf_k6n10f, skip if -noioff)") && (family == "qlf_k6n10f" || help_mode)) {
|
||||||
if (ioff || help_mode) {
|
if (ioff || help_mode) {
|
||||||
run("ql_ioff", "(unless -noioff)");
|
run("ql_ioff");
|
||||||
|
run("opt_clean");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,21 @@ EOF
|
||||||
synth_quicklogic -family qlf_k6n10f -top top
|
synth_quicklogic -family qlf_k6n10f -top top
|
||||||
select -assert-count 4 t:dff
|
select -assert-count 4 t:dff
|
||||||
|
|
||||||
|
design -reset
|
||||||
|
# test: acceptable for output IOFF promotion; duplicate output FF
|
||||||
|
read_verilog <<EOF
|
||||||
|
module top (input clk, input [3:0] a, output [3:0] o, output [3:0] p);
|
||||||
|
reg [3:0] r;
|
||||||
|
always @(posedge clk) begin
|
||||||
|
r <= ~a;
|
||||||
|
end
|
||||||
|
assign o = r;
|
||||||
|
assign p = r;
|
||||||
|
endmodule
|
||||||
|
EOF
|
||||||
|
synth_quicklogic -family qlf_k6n10f -top top
|
||||||
|
select -assert-count 8 t:dff
|
||||||
|
|
||||||
design -reset
|
design -reset
|
||||||
# test: acceptable for input IOFF promotion
|
# test: acceptable for input IOFF promotion
|
||||||
read_verilog <<EOF
|
read_verilog <<EOF
|
||||||
|
@ -170,3 +185,25 @@ endmodule
|
||||||
EOF
|
EOF
|
||||||
synth_quicklogic -family qlf_k6n10f -top top
|
synth_quicklogic -family qlf_k6n10f -top top
|
||||||
select -assert-count 0 t:dff
|
select -assert-count 0 t:dff
|
||||||
|
|
||||||
|
design -reset
|
||||||
|
# test: duplicate registers driving multiple output ports
|
||||||
|
read_verilog <<EOF
|
||||||
|
module top (
|
||||||
|
input clk,
|
||||||
|
input en,
|
||||||
|
input [3:0] a,
|
||||||
|
output reg [3:0] o_1,
|
||||||
|
output wire [3:0] o_2
|
||||||
|
);
|
||||||
|
always @(posedge clk) begin
|
||||||
|
o_1[1:0] <= ~a[1:0];
|
||||||
|
if (en)
|
||||||
|
o_1[2] <= a[2];
|
||||||
|
end
|
||||||
|
always @(*) o_1[3] = a[3];
|
||||||
|
assign o_2 = o_1;
|
||||||
|
endmodule
|
||||||
|
EOF
|
||||||
|
synth_quicklogic -family qlf_k6n10f -top top
|
||||||
|
select -assert-count 4 t:dff
|
||||||
|
|
Loading…
Reference in New Issue