Skip to content

Commit 5b6baa3

Browse files
authored
Merge pull request #4744 from YosysHQ/emil/clockgate-liberty
clockgate: add -liberty
2 parents 53a4ec3 + 4d96cbe commit 5b6baa3

File tree

3 files changed

+358
-8
lines changed

3 files changed

+358
-8
lines changed

passes/techmap/clockgate.cc

Lines changed: 196 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "kernel/yosys.h"
22
#include "kernel/ff.h"
3+
#include "libparse.h"
34
#include <optional>
45

56
USING_YOSYS_NAMESPACE
@@ -10,6 +11,7 @@ struct ClockGateCell {
1011
IdString ce_pin;
1112
IdString clk_in_pin;
1213
IdString clk_out_pin;
14+
std::vector<IdString> tie_lo_pins;
1315
};
1416

1517
ClockGateCell icg_from_arg(std::string& name, std::string& str) {
@@ -37,6 +39,166 @@ ClockGateCell icg_from_arg(std::string& name, std::string& str) {
3739
return c;
3840
}
3941

42+
static std::pair<std::optional<ClockGateCell>, std::optional<ClockGateCell>>
43+
find_icgs(std::string filename, std::vector<std::string> const& dont_use_cells) {
44+
std::ifstream f;
45+
f.open(filename.c_str());
46+
if (f.fail())
47+
log_cmd_error("Can't open liberty file `%s': %s\n", filename.c_str(), strerror(errno));
48+
LibertyParser libparser(f);
49+
f.close();
50+
auto ast = libparser.ast;
51+
52+
// We will pick the most suitable ICG absed on tie_lo count and area
53+
struct ICGRankable : public ClockGateCell { double area; };
54+
std::optional<ICGRankable> best_pos;
55+
std::optional<ICGRankable> best_neg;
56+
57+
if (ast->id != "library")
58+
log_error("Format error in liberty file.\n");
59+
60+
// This is a lot of boilerplate, isn't it?
61+
for (auto cell : ast->children)
62+
{
63+
if (cell->id != "cell" || cell->args.size() != 1)
64+
continue;
65+
66+
const LibertyAst *dn = cell->find("dont_use");
67+
if (dn != nullptr && dn->value == "true")
68+
continue;
69+
70+
bool dont_use = false;
71+
for (auto dont_use_cell : dont_use_cells)
72+
{
73+
if (patmatch(dont_use_cell.c_str(), cell->args[0].c_str()))
74+
{
75+
dont_use = true;
76+
break;
77+
}
78+
}
79+
if (dont_use)
80+
continue;
81+
82+
const LibertyAst *icg_kind_ast = cell->find("clock_gating_integrated_cell");
83+
if (icg_kind_ast == nullptr)
84+
continue;
85+
86+
auto cell_name = cell->args[0];
87+
auto icg_kind = icg_kind_ast->value;
88+
auto starts_with = [&](std::string prefix) {
89+
return icg_kind.compare(0, prefix.size(), prefix) == 0;
90+
};
91+
bool clk_pol;
92+
if (icg_kind == "latch_posedge" || starts_with("latch_posedge_")) {
93+
clk_pol = true;
94+
} else if (icg_kind == "latch_negedge" || starts_with("latch_negedge_")) {
95+
clk_pol = false;
96+
} else {
97+
log("Ignoring ICG primitive %s of kind '%s'\n", cell_name.c_str(), icg_kind.c_str());
98+
continue;
99+
}
100+
101+
log_debug("maybe valid icg: %s\n", cell_name.c_str());
102+
ClockGateCell icg_interface;
103+
icg_interface.name = RTLIL::escape_id(cell_name);
104+
105+
for (auto pin : cell->children) {
106+
if (pin->id != "pin" || pin->args.size() != 1)
107+
continue;
108+
109+
if (auto clk = pin->find("clock_gate_clock_pin")) {
110+
if (!icg_interface.clk_in_pin.empty()) {
111+
log_warning("Malformed liberty file - multiple clock_gate_clock_pin in cell %s\n",
112+
cell_name.c_str());
113+
continue;
114+
} else
115+
icg_interface.clk_in_pin = RTLIL::escape_id(pin->args[0]);
116+
} else if (auto gclk = pin->find("clock_gate_out_pin")) {
117+
if (!icg_interface.clk_out_pin.empty()) {
118+
log_warning("Malformed liberty file - multiple clock_gate_out_pin in cell %s\n",
119+
cell_name.c_str());
120+
continue;
121+
} else
122+
icg_interface.clk_out_pin = RTLIL::escape_id(pin->args[0]);
123+
} else if (auto en = pin->find("clock_gate_enable_pin")) {
124+
if (!icg_interface.ce_pin.empty()) {
125+
log_warning("Malformed liberty file - multiple clock_gate_enable_pin in cell %s\n",
126+
cell_name.c_str());
127+
continue;
128+
} else
129+
icg_interface.ce_pin = RTLIL::escape_id(pin->args[0]);
130+
} else if (auto se = pin->find("clock_gate_test_pin")) {
131+
icg_interface.tie_lo_pins.push_back(RTLIL::escape_id(pin->args[0]));
132+
} else {
133+
const LibertyAst *dir = pin->find("direction");
134+
if (dir->value == "internal")
135+
continue;
136+
137+
log_warning("Malformed liberty file - extra pin %s in cell %s\n",
138+
pin->args[0].c_str(), cell_name.c_str());
139+
continue;
140+
}
141+
}
142+
143+
if (icg_interface.clk_in_pin.empty()) {
144+
log_warning("Malformed liberty file - missing clock_gate_clock_pin in cell %s",
145+
cell_name.c_str());
146+
continue;
147+
}
148+
if (icg_interface.clk_out_pin.empty()) {
149+
log_warning("Malformed liberty file - missing clock_gate_out_pin in cell %s",
150+
cell_name.c_str());
151+
continue;
152+
}
153+
if (icg_interface.ce_pin.empty()) {
154+
log_warning("Malformed liberty file - missing clock_gate_enable_pin in cell %s",
155+
cell_name.c_str());
156+
continue;
157+
}
158+
159+
double area = 0;
160+
const LibertyAst *ar = cell->find("area");
161+
if (ar != nullptr && !ar->value.empty())
162+
area = atof(ar->value.c_str());
163+
164+
std::optional<ICGRankable>& icg_to_beat = clk_pol ? best_pos : best_neg;
165+
166+
bool winning = false;
167+
if (icg_to_beat) {
168+
log_debug("ties: %zu ? %zu\n", icg_to_beat->tie_lo_pins.size(),
169+
icg_interface.tie_lo_pins.size());
170+
log_debug("area: %f ? %f\n", icg_to_beat->area, area);
171+
172+
// Prefer fewer test enables over area reduction (unlikely to matter)
173+
auto goal = std::make_pair(icg_to_beat->tie_lo_pins.size(), icg_to_beat->area);
174+
auto cost = std::make_pair(icg_interface.tie_lo_pins.size(), area);
175+
winning = cost < goal;
176+
177+
if (winning)
178+
log_debug("%s beats %s\n", icg_interface.name.c_str(), icg_to_beat->name.c_str());
179+
} else {
180+
log_debug("%s is the first of its polarity\n", icg_interface.name.c_str());
181+
winning = true;
182+
}
183+
if (winning) {
184+
ICGRankable new_icg {icg_interface, area};
185+
icg_to_beat.emplace(new_icg);
186+
}
187+
}
188+
189+
std::optional<ClockGateCell> pos;
190+
std::optional<ClockGateCell> neg;
191+
if (best_pos) {
192+
log("Selected rising edge ICG %s from Liberty file\n", best_pos->name.c_str());
193+
pos.emplace(*best_pos);
194+
}
195+
if (best_neg) {
196+
log("Selected falling edge ICG %s from Liberty file\n", best_neg->name.c_str());
197+
neg.emplace(*best_neg);
198+
}
199+
return std::make_pair(pos, neg);
200+
}
201+
40202
struct ClockgatePass : public Pass {
41203
ClockgatePass() : Pass("clockgate", "extract clock gating out of flip flops") { }
42204
void help() override {
@@ -60,12 +222,20 @@ struct ClockgatePass : public Pass {
60222
log(" user-specified <celltype> ICG (integrated clock gating)\n");
61223
log(" cell with ports named <ce>, <clk>, <gclk>.\n");
62224
log(" The ICG's clock enable pin must be active high.\n");
225+
log(" -liberty <filename>\n");
226+
log(" If specified, ICGs will be selected from the liberty file\n");
227+
log(" if available. Priority is given to cells with fewer tie_lo\n");
228+
log(" inputs and smaller size. This removes the need to manually\n");
229+
log(" specify -pos or -neg and -tie_lo.\n");
230+
log(" -dont_use <celltype>\n");
231+
log(" Cells <celltype> won't be considered when searching for ICGs\n");
232+
log(" in the liberty file specified by -liberty.\n");
63233
log(" -tie_lo <port_name>\n");
64234
log(" Port <port_name> of the ICG will be tied to zero.\n");
65235
log(" Intended for DFT scan-enable pins.\n");
66236
log(" -min_net_size <n>\n");
67237
log(" Only transform sets of at least <n> eligible FFs.\n");
68-
// log(" \n");
238+
log(" \n");
69239
}
70240

71241
// One ICG will be generated per ClkNetInfo
@@ -110,7 +280,9 @@ struct ClockgatePass : public Pass {
110280

111281
std::optional<ClockGateCell> pos_icg_desc;
112282
std::optional<ClockGateCell> neg_icg_desc;
113-
std::vector<std::string> tie_lo_ports;
283+
std::vector<std::string> tie_lo_pins;
284+
std::string liberty_file;
285+
std::vector<std::string> dont_use_cells;
114286
int min_net_size = 0;
115287

116288
size_t argidx;
@@ -126,13 +298,33 @@ struct ClockgatePass : public Pass {
126298
neg_icg_desc = icg_from_arg(name, rest);
127299
}
128300
if (args[argidx] == "-tie_lo" && argidx+1 < args.size()) {
129-
tie_lo_ports.push_back(RTLIL::escape_id(args[++argidx]));
301+
tie_lo_pins.push_back(RTLIL::escape_id(args[++argidx]));
302+
}
303+
if (args[argidx] == "-liberty" && argidx+1 < args.size()) {
304+
liberty_file = args[++argidx];
305+
rewrite_filename(liberty_file);
306+
}
307+
if (args[argidx] == "-dont_use" && argidx+1 < args.size()) {
308+
dont_use_cells.push_back(args[++argidx]);
309+
continue;
130310
}
131311
if (args[argidx] == "-min_net_size" && argidx+1 < args.size()) {
132312
min_net_size = atoi(args[++argidx].c_str());
133313
}
134314
}
135315

316+
if (!liberty_file.empty())
317+
std::tie(pos_icg_desc, neg_icg_desc) =
318+
find_icgs(liberty_file, dont_use_cells);
319+
else {
320+
for (auto pin : tie_lo_pins) {
321+
if (pos_icg_desc)
322+
pos_icg_desc->tie_lo_pins.push_back(pin);
323+
if (neg_icg_desc)
324+
neg_icg_desc->tie_lo_pins.push_back(pin);
325+
}
326+
}
327+
136328
extra_args(args, argidx, design);
137329

138330
pool<Cell*> ce_ffs;
@@ -185,7 +377,7 @@ struct ClockgatePass : public Pass {
185377
gclk.new_net = module->addWire(NEW_ID);
186378
icg->setPort(matching_icg_desc->clk_out_pin, gclk.new_net);
187379
// Tie low DFT ports like scan chain enable
188-
for (auto port : tie_lo_ports)
380+
for (auto port : matching_icg_desc->tie_lo_pins)
189381
icg->setPort(port, Const(0, 1));
190382
// Fix CE polarity if needed
191383
if (!clk.pol_ce) {

tests/techmap/clockgate.lib

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
library(test) {
2+
/* Integrated clock gating cells */
3+
cell (pos_small_tielo) {
4+
area : 1;
5+
clock_gating_integrated_cell : latch_posedge_precontrol;
6+
pin (GCLK) {
7+
clock_gate_out_pin : true;
8+
direction : output;
9+
}
10+
pin (CLK) {
11+
clock_gate_clock_pin : true;
12+
direction : input;
13+
}
14+
pin (CE) {
15+
clock_gate_enable_pin : true;
16+
direction : input;
17+
}
18+
pin (SE) {
19+
clock_gate_test_pin : true;
20+
direction : input;
21+
}
22+
}
23+
cell (pos_big) {
24+
area : 10;
25+
clock_gating_integrated_cell : latch_posedge;
26+
pin (GCLK) {
27+
clock_gate_out_pin : true;
28+
direction : output;
29+
}
30+
pin (CLK) {
31+
clock_gate_clock_pin : true;
32+
direction : input;
33+
}
34+
pin (CE) {
35+
clock_gate_enable_pin : true;
36+
direction : input;
37+
}
38+
}
39+
cell (pos_small) {
40+
area : 1;
41+
clock_gating_integrated_cell : latch_posedge;
42+
pin (GCLK) {
43+
clock_gate_out_pin : true;
44+
direction : output;
45+
}
46+
pin (CLK) {
47+
clock_gate_clock_pin : true;
48+
direction : input;
49+
}
50+
pin (CE) {
51+
clock_gate_enable_pin : true;
52+
direction : input;
53+
}
54+
}
55+
cell (neg_big) {
56+
area : 10;
57+
clock_gating_integrated_cell : latch_negedge;
58+
pin (GCLK) {
59+
clock_gate_out_pin : true;
60+
direction : output;
61+
}
62+
pin (CLK) {
63+
clock_gate_clock_pin : true;
64+
direction : input;
65+
}
66+
pin (CE) {
67+
clock_gate_enable_pin : true;
68+
direction : input;
69+
}
70+
}
71+
cell (neg_small_tielo) {
72+
area : 1;
73+
clock_gating_integrated_cell : latch_negedge_precontrol;
74+
pin (GCLK) {
75+
clock_gate_out_pin : true;
76+
direction : output;
77+
}
78+
pin (CLK) {
79+
clock_gate_clock_pin : true;
80+
direction : input;
81+
}
82+
pin (CE) {
83+
clock_gate_enable_pin : true;
84+
direction : input;
85+
}
86+
pin (SE) {
87+
clock_gate_test_pin : true;
88+
direction : input;
89+
}
90+
}
91+
cell (neg_small) {
92+
area : 1;
93+
clock_gating_integrated_cell : latch_negedge;
94+
pin (GCLK) {
95+
clock_gate_out_pin : true;
96+
direction : output;
97+
}
98+
pin (CLK) {
99+
clock_gate_clock_pin : true;
100+
direction : input;
101+
}
102+
pin (CE) {
103+
clock_gate_enable_pin : true;
104+
direction : input;
105+
}
106+
}
107+
}

0 commit comments

Comments
 (0)