clkbufmap: Add support for inverters in clock path.

This commit is contained in:
Marcin Kościelnicki 2019-11-24 16:05:45 +01:00 committed by Marcin Kościelnicki
parent 7562e7304e
commit 6cdea425b8
4 changed files with 69 additions and 6 deletions

View File

@ -343,6 +343,13 @@ Verilog Attributes and non-standard features
- The ``clkbuf_sink`` attribute can be set on an input port of a module to - The ``clkbuf_sink`` attribute can be set on an input port of a module to
request clock buffer insertion by the ``clkbufmap`` pass. request clock buffer insertion by the ``clkbufmap`` pass.
- The ``clkbuf_inv`` attribute can be set on an output port of a module
with the value set to the name of an input port of that module. When
the ``clkbufmap`` would otherwise insert a clock buffer on this output,
it will instead try inserting the clock buffer on the input port (this
is used to implement clock inverter cells that clock buffer insertion
will "see through").
- The ``clkbuf_inhibit`` is the default attribute to set on a wire to prevent - The ``clkbuf_inhibit`` is the default attribute to set on a wire to prevent
automatic clock buffer insertion by ``clkbufmap``. This behaviour can be automatic clock buffer insertion by ``clkbufmap``. This behaviour can be
overridden by providing a custom selection to ``clkbufmap``. overridden by providing a custom selection to ``clkbufmap``.

View File

@ -115,6 +115,8 @@ struct ClkbufmapPass : public Pass {
// Cell type, port name, bit index. // Cell type, port name, bit index.
pool<pair<IdString, pair<IdString, int>>> sink_ports; pool<pair<IdString, pair<IdString, int>>> sink_ports;
pool<pair<IdString, pair<IdString, int>>> buf_ports; pool<pair<IdString, pair<IdString, int>>> buf_ports;
dict<pair<IdString, pair<IdString, int>>, pair<IdString, int>> inv_ports_out;
dict<pair<IdString, pair<IdString, int>>, pair<IdString, int>> inv_ports_in;
// Process submodules before module using them. // Process submodules before module using them.
std::vector<Module *> modules_sorted; std::vector<Module *> modules_sorted;
@ -133,6 +135,14 @@ struct ClkbufmapPass : public Pass {
if (wire->get_bool_attribute("\\clkbuf_sink")) if (wire->get_bool_attribute("\\clkbuf_sink"))
for (int i = 0; i < GetSize(wire); i++) for (int i = 0; i < GetSize(wire); i++)
sink_ports.insert(make_pair(module->name, make_pair(wire->name, i))); sink_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
auto it = wire->attributes.find("\\clkbuf_inv");
if (it != wire->attributes.end()) {
IdString in_name = RTLIL::escape_id(it->second.decode_string());
for (int i = 0; i < GetSize(wire); i++) {
inv_ports_out[make_pair(module->name, make_pair(wire->name, i))] = make_pair(in_name, i);
inv_ports_in[make_pair(module->name, make_pair(in_name, i))] = make_pair(wire->name, i);
}
}
} }
continue; continue;
} }
@ -157,6 +167,37 @@ struct ClkbufmapPass : public Pass {
if (buf_ports.count(make_pair(cell->type, make_pair(port.first, i)))) if (buf_ports.count(make_pair(cell->type, make_pair(port.first, i))))
buf_wire_bits.insert(sigmap(port.second[i])); buf_wire_bits.insert(sigmap(port.second[i]));
// Third, propagate tags through inverters.
bool retry = true;
while (retry) {
retry = false;
for (auto cell : module->cells())
for (auto port : cell->connections())
for (int i = 0; i < port.second.size(); i++) {
auto it = inv_ports_out.find(make_pair(cell->type, make_pair(port.first, i)));
auto bit = sigmap(port.second[i]);
// If output of an inverter is connected to a sink, mark it as buffered,
// and request a buffer on the inverter's input instead.
if (it != inv_ports_out.end() && !buf_wire_bits.count(bit) && sink_wire_bits.count(bit)) {
buf_wire_bits.insert(bit);
auto other_bit = sigmap(cell->getPort(it->second.first)[it->second.second]);
sink_wire_bits.insert(other_bit);
retry = true;
}
// If input of an inverter is marked as already-buffered,
// mark its output already-buffered as well.
auto it2 = inv_ports_in.find(make_pair(cell->type, make_pair(port.first, i)));
if (it2 != inv_ports_in.end() && buf_wire_bits.count(bit)) {
auto other_bit = sigmap(cell->getPort(it2->second.first)[it2->second.second]);
if (!buf_wire_bits.count(other_bit)) {
buf_wire_bits.insert(other_bit);
retry = true;
}
}
}
};
// Collect all driven bits. // Collect all driven bits.
for (auto cell : module->cells()) for (auto cell : module->cells())
for (auto port : cell->connections()) for (auto port : cell->connections())

View File

@ -126,7 +126,11 @@ endmodule
// assign O = IO, IO = T ? 1'bz : I; // assign O = IO, IO = T ? 1'bz : I;
// endmodule // endmodule
module INV(output O, input I); module INV(
(* clkbuf_inv = "I" *)
output O,
input I
);
assign O = !I; assign O = !I;
endmodule endmodule

View File

@ -4,6 +4,7 @@ module dff ((* clkbuf_sink *) input clk, input d, output q); endmodule
module dffe ((* clkbuf_sink *) input c, input d, e, output q); endmodule module dffe ((* clkbuf_sink *) input c, input d, e, output q); endmodule
module latch (input e, d, output q); endmodule module latch (input e, d, output q); endmodule
module clkgen (output o); endmodule module clkgen (output o); endmodule
module inv ((* clkbuf_inv = "i" *) output o, input i); endmodule
module top(input clk1, clk2, clk3, d, e, output [4:0] q); module top(input clk1, clk2, clk3, d, e, output [4:0] q);
wire clk4, clk5, clk6; wire clk4, clk5, clk6;
@ -17,12 +18,18 @@ dff s6 (.clk(clk6), .d(d), .q(q[4]));
endmodule endmodule
module sub(output sclk4, output sclk5, output sclk6, input sd, output sq); module sub(output sclk4, output sclk5, output sclk6, input sd, output sq);
wire sclk7, sclk8, sclk9;
wire siq;
wire tmp; wire tmp;
clkgen s7(.o(sclk4)); clkgen s7(.o(sclk4));
clkgen s8(.o(sclk5)); clkgen s8(.o(sclk5));
clkgen s9(.o(tmp)); clkgen s9(.o(tmp));
clkbuf s10(.i(tmp), .o(sclk6)); clkbuf s10(.i(tmp), .o(sclk7));
dff s11(.clk(sclk4), .d(sd), .q(sq)); dff s11(.clk(sclk4), .d(sd), .q(siq));
inv s15(.i(sclk7), .o(sclk6));
clkgen s12(.o(sclk8));
inv s13(.o(sclk9), .i(sclk8));
dff s14(.clk(sclk9), .d(siq), .q(sq));
endmodule endmodule
EOT EOT
@ -34,7 +41,7 @@ design -save ref
design -load ref design -load ref
clkbufmap -buf clkbuf o:i clkbufmap -buf clkbuf o:i
select -assert-count 3 top/t:clkbuf select -assert-count 3 top/t:clkbuf
select -assert-count 2 sub/t:clkbuf select -assert-count 3 sub/t:clkbuf
select -set clk1 w:clk1 %a %co t:clkbuf %i # Find 'clk1' fanouts that are 'clkbuf' select -set clk1 w:clk1 %a %co t:clkbuf %i # Find 'clk1' fanouts that are 'clkbuf'
select -assert-count 1 @clk1 # Check there is one such fanout select -assert-count 1 @clk1 # Check there is one such fanout
select -assert-count 1 @clk1 %x:+[o] %co c:s* %i # Check that the 'o' of that clkbuf drives one fanout select -assert-count 1 @clk1 %x:+[o] %co c:s* %i # Check that the 'o' of that clkbuf drives one fanout
@ -51,6 +58,10 @@ select -set sclk4 w:sclk4 %a %ci t:clkbuf %i
select -assert-count 1 @sclk4 select -assert-count 1 @sclk4
select -assert-count 1 @sclk4 %x:+[o] %co c:s11 %i select -assert-count 1 @sclk4 %x:+[o] %co c:s11 %i
select -assert-count 1 @sclk4 %x:+[i] %ci c:s7 %i select -assert-count 1 @sclk4 %x:+[i] %ci c:s7 %i
select -set sclk8 w:sclk8 %a %ci t:clkbuf %i
select -assert-count 1 @sclk8
select -assert-count 1 @sclk8 %x:+[o] %co c:s13 %i
select -assert-count 1 @sclk8 %x:+[i] %ci c:s12 %i
# ---------------------- # ----------------------
@ -72,7 +83,7 @@ setattr -set clkbuf_inhibit 1 w:clk1
setattr -set buffer_type "bufg" w:clk2 setattr -set buffer_type "bufg" w:clk2
clkbufmap -buf clkbuf o:i w:* a:buffer_type=none a:buffer_type=bufr %u %d clkbufmap -buf clkbuf o:i w:* a:buffer_type=none a:buffer_type=bufr %u %d
select -assert-count 3 top/t:clkbuf select -assert-count 3 top/t:clkbuf
select -assert-count 2 sub/t:clkbuf select -assert-count 3 sub/t:clkbuf
select -set clk1 w:clk1 %a %co t:clkbuf %i # Find 'clk1' fanouts that are 'clkbuf' select -set clk1 w:clk1 %a %co t:clkbuf %i # Find 'clk1' fanouts that are 'clkbuf'
select -assert-count 1 @clk1 # Check there is one such fanout select -assert-count 1 @clk1 # Check there is one such fanout
select -assert-count 1 @clk1 %x:+[o] %co c:s* %i # Check that the 'o' of that clkbuf drives one fanout select -assert-count 1 @clk1 %x:+[o] %co c:s* %i # Check that the 'o' of that clkbuf drives one fanout
@ -93,4 +104,4 @@ clkbufmap -buf clkbuf o:i w:* a:buffer_type=none a:buffer_type=bufr %u %d
select -assert-count 0 w:clk1 %a %co t:clkbuf %i select -assert-count 0 w:clk1 %a %co t:clkbuf %i
select -assert-count 0 w:clk2 %a %co t:clkbuf %i select -assert-count 0 w:clk2 %a %co t:clkbuf %i
select -assert-count 0 top/t:clkbuf select -assert-count 0 top/t:clkbuf
select -assert-count 1 sub/t:clkbuf select -assert-count 2 sub/t:clkbuf