11#include " kernel/yosys.h"
22#include " kernel/ff.h"
3+ #include " libparse.h"
34#include < optional>
45
56USING_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
1517ClockGateCell 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+
40202struct 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 ) {
0 commit comments