Skip to content

Commit 303a386

Browse files
committed
create duplicate IOFFs if multiple output ports are connected to the same register
1 parent 25b4009 commit 303a386

File tree

3 files changed

+93
-15
lines changed

3 files changed

+93
-15
lines changed

techlibs/quicklogic/ql_ioff.cc

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,17 @@ struct QlIoffPass : public Pass {
2929
if (!module)
3030
return;
3131
modwalker.setup(module);
32-
pool<RTLIL::Cell *> cells_to_replace;
32+
pool<RTLIL::Cell *> input_ffs;
33+
dict<RTLIL::Wire *, std::vector<Cell*>> output_ffs;
34+
dict<SigBit, pool<SigBit>> output_bit_aliases;
35+
36+
for (Wire* wire : module->wires())
37+
if (wire->port_output) {
38+
output_ffs[wire].resize(wire->width, nullptr);
39+
for (SigBit bit : SigSpec(wire))
40+
output_bit_aliases[modwalker.sigmap(bit)].insert(bit);
41+
}
42+
3343
for (auto cell : module->selected_cells()) {
3444
if (cell->type.in(ID(dffsre), ID(sdffsre))) {
3545
log_debug("Checking cell %s.\n", cell->name.c_str());
@@ -53,32 +63,62 @@ struct QlIoffPass : public Pass {
5363
log_debug("not promoting: D has other consumers\n");
5464
continue;
5565
}
56-
cells_to_replace.insert(cell);
57-
continue; // no need to check Q if we already put it on the list
66+
input_ffs.insert(cell);
67+
continue; // prefer input FFs over output FFs
5868
}
69+
5970
SigSpec q = cell->getPort(ID::Q);
6071
log_assert(GetSize(q) == 1);
61-
if (modwalker.has_outputs(q)) {
72+
if (modwalker.has_outputs(q) && !modwalker.has_consumers(q)) {
6273
log_debug("Cell %s is potentially eligible for promotion to output IOFF.\n", cell->name.c_str());
63-
// check that q_sig has no other consumers
64-
pool<ModWalker::PortBit> portbits;
65-
modwalker.get_consumers(portbits, q);
66-
if (GetSize(portbits) > 0) {
67-
log_debug("not promoting: Q has other consumers\n");
68-
continue;
74+
for (SigBit bit : output_bit_aliases[modwalker.sigmap(q)]) {
75+
log_assert(bit.is_wire());
76+
output_ffs[bit.wire][bit.offset] = cell;
6977
}
70-
cells_to_replace.insert(cell);
78+
7179
}
7280
}
7381
}
7482

75-
for (auto cell : cells_to_replace) {
76-
log("Promoting register %s to IOFF.\n", log_signal(cell->getPort(ID::Q)));
83+
for (auto cell : input_ffs) {
84+
log("Promoting register %s to input IOFF.\n", log_signal(cell->getPort(ID::Q)));
7785
cell->type = ID(dff);
7886
cell->unsetPort(ID::E);
7987
cell->unsetPort(ID::R);
8088
cell->unsetPort(ID::S);
8189
}
90+
for (auto & [old_port_output, ioff_cells] : output_ffs) {
91+
if (std::any_of(ioff_cells.begin(), ioff_cells.end(), [](Cell * c) { return c != nullptr; }))
92+
{
93+
// create replacement output wire
94+
RTLIL::Wire* new_port_output = module->addWire(NEW_ID, old_port_output->width);
95+
new_port_output->start_offset = old_port_output->start_offset;
96+
module->swap_names(old_port_output, new_port_output);
97+
std::swap(old_port_output->port_id, new_port_output->port_id);
98+
std::swap(old_port_output->port_input, new_port_output->port_input);
99+
std::swap(old_port_output->port_output, new_port_output->port_output);
100+
std::swap(old_port_output->upto, new_port_output->upto);
101+
std::swap(old_port_output->is_signed, new_port_output->is_signed);
102+
std::swap(old_port_output->attributes, new_port_output->attributes);
103+
104+
// create new output FFs
105+
SigSpec sig_o(old_port_output);
106+
SigSpec sig_n(new_port_output);
107+
for (int i = 0; i < new_port_output->width; i++) {
108+
if (ioff_cells[i]) {
109+
log("Promoting %s to output IOFF.\n", log_signal(sig_n[i]));
110+
111+
RTLIL::Cell *new_cell = module->addCell(NEW_ID, ID(dff));
112+
new_cell->setPort(ID::C, ioff_cells[i]->getPort(ID::C));
113+
new_cell->setPort(ID::D, ioff_cells[i]->getPort(ID::D));
114+
new_cell->setPort(ID::Q, sig_n[i]);
115+
new_cell->set_bool_attribute(ID::keep);
116+
} else {
117+
module->connect(sig_n[i], sig_o[i]);
118+
}
119+
}
120+
}
121+
}
82122
}
83123
} QlIoffPass;
84124

techlibs/quicklogic/synth_quicklogic.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,9 +334,10 @@ struct SynthQuickLogicPass : public ScriptPass {
334334
run("opt_lut");
335335
}
336336

337-
if (check_label("iomap", "(for qlf_k6n10f)") && (family == "qlf_k6n10f" || help_mode)) {
337+
if (check_label("iomap", "(for qlf_k6n10f, skip if -noioff)") && (family == "qlf_k6n10f" || help_mode)) {
338338
if (ioff || help_mode) {
339-
run("ql_ioff", "(unless -noioff)");
339+
run("ql_ioff");
340+
run("opt_clean");
340341
}
341342
}
342343

tests/arch/quicklogic/qlf_k6n10f/ioff.ys

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,21 @@ EOF
2121
synth_quicklogic -family qlf_k6n10f -top top
2222
select -assert-count 4 t:dff
2323

24+
design -reset
25+
# test: acceptable for output IOFF promotion; duplicate output FF
26+
read_verilog <<EOF
27+
module top (input clk, input [3:0] a, output [3:0] o, output [3:0] p);
28+
reg [3:0] r;
29+
always @(posedge clk) begin
30+
r <= ~a;
31+
end
32+
assign o = r;
33+
assign p = r;
34+
endmodule
35+
EOF
36+
synth_quicklogic -family qlf_k6n10f -top top
37+
select -assert-count 8 t:dff
38+
2439
design -reset
2540
# test: acceptable for input IOFF promotion
2641
read_verilog <<EOF
@@ -170,3 +185,25 @@ endmodule
170185
EOF
171186
synth_quicklogic -family qlf_k6n10f -top top
172187
select -assert-count 0 t:dff
188+
189+
design -reset
190+
# test: duplicate registers driving multiple output ports
191+
read_verilog <<EOF
192+
module top (
193+
input clk,
194+
input en,
195+
input [3:0] a,
196+
output reg [3:0] o_1,
197+
output wire [3:0] o_2
198+
);
199+
always @(posedge clk) begin
200+
o_1[1:0] <= ~a[1:0];
201+
if (en)
202+
o_1[2] <= a[2];
203+
end
204+
always @(*) o_1[3] = a[3];
205+
assign o_2 = o_1;
206+
endmodule
207+
EOF
208+
synth_quicklogic -family qlf_k6n10f -top top
209+
select -assert-count 4 t:dff

0 commit comments

Comments
 (0)