mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-04 05:19:11 +00:00 
			
		
		
		
	clockgate: prototype clock gating
This commit is contained in:
		
							parent
							
								
									0fc5812dcd
								
							
						
					
					
						commit
						e64fceef70
					
				
					 3 changed files with 318 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -49,6 +49,7 @@ OBJS += passes/techmap/dffunmap.o
 | 
			
		|||
OBJS += passes/techmap/flowmap.o
 | 
			
		||||
OBJS += passes/techmap/extractinv.o
 | 
			
		||||
OBJS += passes/techmap/cellmatch.o
 | 
			
		||||
OBJS += passes/techmap/clockgate.o
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(DISABLE_SPAWN),0)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										229
									
								
								passes/techmap/clockgate.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								passes/techmap/clockgate.cc
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,229 @@
 | 
			
		|||
#include "kernel/yosys.h"
 | 
			
		||||
#include "kernel/ff.h"
 | 
			
		||||
#include <optional>
 | 
			
		||||
 | 
			
		||||
USING_YOSYS_NAMESPACE
 | 
			
		||||
PRIVATE_NAMESPACE_BEGIN
 | 
			
		||||
 | 
			
		||||
struct ClockGateCell {
 | 
			
		||||
	IdString name;
 | 
			
		||||
	IdString ce_pin;
 | 
			
		||||
	IdString clk_in_pin;
 | 
			
		||||
	IdString clk_out_pin;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ClockGateCell icg_from_arg(std::string& name, std::string& str) {
 | 
			
		||||
	ClockGateCell c;
 | 
			
		||||
	c.name = RTLIL::escape_id(name);
 | 
			
		||||
	char delimiter = ':';
 | 
			
		||||
	size_t pos1 = str.find(delimiter);
 | 
			
		||||
	if (pos1 == std::string::npos)
 | 
			
		||||
		log_cmd_error("Not enough ports in descriptor string");
 | 
			
		||||
	size_t pos2 = str.find(delimiter, pos1 + 1);
 | 
			
		||||
	if (pos2 == std::string::npos)
 | 
			
		||||
		log_cmd_error("Not enough ports in descriptor string");
 | 
			
		||||
	size_t pos3 = str.find(delimiter, pos2 + 1);
 | 
			
		||||
	if (pos3 != std::string::npos)
 | 
			
		||||
		log_cmd_error("Too many ports in descriptor string");
 | 
			
		||||
 | 
			
		||||
	std::string ce = str.substr(0, pos1);
 | 
			
		||||
	c.ce_pin = RTLIL::escape_id(ce);
 | 
			
		||||
 | 
			
		||||
	std::string clk_in = str.substr(pos1 + 1, pos2 - (pos1 + 1));
 | 
			
		||||
	c.clk_in_pin = RTLIL::escape_id(clk_in);
 | 
			
		||||
 | 
			
		||||
	std::string clk_out = str.substr(pos2 + 1, str.size() - (pos2 + 1));
 | 
			
		||||
	c.clk_out_pin = RTLIL::escape_id(clk_out);
 | 
			
		||||
	return c;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ClockgatePass : public Pass {
 | 
			
		||||
	ClockgatePass() : Pass("clockgate", "extract clock gating out of flip flops") { }
 | 
			
		||||
	void help() override {
 | 
			
		||||
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    clockgate [options] [selection]\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("Creates gated clock nets for sets of FFs with clock enable\n");
 | 
			
		||||
		log("sharing a clock and replaces the FFs with versions without\n");
 | 
			
		||||
		log("clock enable inputs. Intended to reduce power consumption\n");
 | 
			
		||||
		log("in ASIC designs.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -pos <celltype> <ce>:<clk>:<gclk>\n");
 | 
			
		||||
		log("        If specified, rising-edge FFs will have CE inputs\n");
 | 
			
		||||
		log("        removed and a gated clock will be created by the\n");
 | 
			
		||||
		log("        user-specified <celltype> ICG (integrated clock gating)\n");
 | 
			
		||||
		log("        cell with ports named <ce>, <clk>, <gclk>.\n");
 | 
			
		||||
		log("        The ICG's clock enable pin must be active high.\n");
 | 
			
		||||
		log("    -neg <celltype> <ce>:<clk>:<gclk>\n");
 | 
			
		||||
		log("        If specified, falling-edge FFs will have CE inputs\n");
 | 
			
		||||
		log("        removed and a gated clock will be created by the\n");
 | 
			
		||||
		log("        user-specified <celltype> ICG (integrated clock gating)\n");
 | 
			
		||||
		log("        cell with ports named <ce>, <clk>, <gclk>.\n");
 | 
			
		||||
		log("        The ICG's clock enable pin must be active high.\n");
 | 
			
		||||
		log("    -tie_lo <port_name>\n");
 | 
			
		||||
		log("        Port <port_name> of the ICG will be tied to zero.\n");
 | 
			
		||||
		log("        Intended for DFT scan-enable pins.\n");
 | 
			
		||||
		log("    -min_net_size <n>\n");
 | 
			
		||||
		log("        Only work on clocks with at least <n> eligible FFs.\n");
 | 
			
		||||
		// log("        \n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	SigMap sigmap;
 | 
			
		||||
	FfInitVals initvals;
 | 
			
		||||
 | 
			
		||||
	// One ICG will be generated per ClkNetInfo
 | 
			
		||||
	// if the number of FFs associated with it is sufficent
 | 
			
		||||
	struct ClkNetInfo {
 | 
			
		||||
		// Original, ungated clock into enabled FF
 | 
			
		||||
		Wire* clk_net;
 | 
			
		||||
		// Original clock enable into enabled FF
 | 
			
		||||
		Wire* ce_net;
 | 
			
		||||
		bool pol_clk;
 | 
			
		||||
		bool pol_ce;
 | 
			
		||||
		unsigned int hash() const {
 | 
			
		||||
			auto t = std::make_tuple(clk_net, ce_net, pol_clk, pol_ce);
 | 
			
		||||
			unsigned int h = mkhash_init;
 | 
			
		||||
			h = mkhash(h, hash_ops<decltype(t)>::hash(t));
 | 
			
		||||
			return h;
 | 
			
		||||
		}
 | 
			
		||||
		bool operator==(const ClkNetInfo& other) const {
 | 
			
		||||
			return (clk_net == other.clk_net) &&
 | 
			
		||||
			       (ce_net == other.ce_net) &&
 | 
			
		||||
			       (pol_clk == other.pol_clk) &&
 | 
			
		||||
			       (pol_ce == other.pol_ce);
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct GClkNetInfo {
 | 
			
		||||
		// How many CE FFs on this CLK net have we seen?
 | 
			
		||||
		int net_size;
 | 
			
		||||
		// After ICG generation, we have new gated CLK signals
 | 
			
		||||
		Wire* new_net;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	ClkNetInfo clk_info_from_ff(FfData& ff) {
 | 
			
		||||
		Wire* clk = ff.sig_clk.as_wire();
 | 
			
		||||
		Wire* ce = ff.sig_ce.as_wire();
 | 
			
		||||
		ClkNetInfo info{clk, ce, ff.pol_clk, ff.pol_ce};
 | 
			
		||||
		return info;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void execute(std::vector<std::string> args, RTLIL::Design *design) override {
 | 
			
		||||
		log_header(design, "Executing CLOCK_GATE pass (extract clock gating out of flip flops).\n");
 | 
			
		||||
 | 
			
		||||
		std::optional<ClockGateCell> pos_icg_desc;
 | 
			
		||||
		std::optional<ClockGateCell> neg_icg_desc;
 | 
			
		||||
		std::vector<std::string> tie_lo_ports;
 | 
			
		||||
		int min_net_size = 0;
 | 
			
		||||
 | 
			
		||||
		size_t argidx;
 | 
			
		||||
		for (argidx = 1; argidx < args.size(); argidx++) {
 | 
			
		||||
			if (args[argidx] == "-pos" && argidx+2 < args.size()) {
 | 
			
		||||
				auto name = args[++argidx];
 | 
			
		||||
				auto rest = args[++argidx];
 | 
			
		||||
				pos_icg_desc = icg_from_arg(name, rest);
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-neg" && argidx+2 < args.size()) {
 | 
			
		||||
				auto name = args[++argidx];
 | 
			
		||||
				auto rest = args[++argidx];
 | 
			
		||||
				neg_icg_desc = icg_from_arg(name, rest);
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-tie_lo" && argidx+1 < args.size()) {
 | 
			
		||||
				tie_lo_ports.push_back(RTLIL::escape_id(args[++argidx]));
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-min_net_size" && argidx+1 < args.size()) {
 | 
			
		||||
				min_net_size = atoi(args[++argidx].c_str());
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		extra_args(args, argidx, design);
 | 
			
		||||
 | 
			
		||||
		pool<Cell*> ce_ffs;
 | 
			
		||||
		dict<ClkNetInfo, GClkNetInfo> clk_nets;
 | 
			
		||||
 | 
			
		||||
		int gated_flop_count = 0;
 | 
			
		||||
		for (auto module : design->selected_whole_modules()) {
 | 
			
		||||
			sigmap.set(module);
 | 
			
		||||
			initvals.set(&sigmap, module);
 | 
			
		||||
			for (auto cell : module->cells()) {
 | 
			
		||||
				if (!RTLIL::builtin_ff_cell_types().count(cell->type))
 | 
			
		||||
					continue;
 | 
			
		||||
 | 
			
		||||
				FfData ff(&initvals, cell);
 | 
			
		||||
				if (ff.has_ce) {
 | 
			
		||||
					ce_ffs.insert(cell);
 | 
			
		||||
 | 
			
		||||
					ClkNetInfo info = clk_info_from_ff(ff);
 | 
			
		||||
					auto it = clk_nets.find(info);
 | 
			
		||||
					if (it == clk_nets.end())
 | 
			
		||||
						clk_nets[info] = GClkNetInfo();
 | 
			
		||||
					clk_nets[info].net_size++;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for (auto& clk_net : clk_nets) {
 | 
			
		||||
				log_debug("checking clk net %s\n", clk_net.first.clk_net->name.c_str());
 | 
			
		||||
				auto& clk = clk_net.first;
 | 
			
		||||
				auto& gclk = clk_net.second;
 | 
			
		||||
 | 
			
		||||
				if (gclk.net_size < min_net_size)
 | 
			
		||||
					continue;
 | 
			
		||||
 | 
			
		||||
				std::optional<ClockGateCell> matching_icg_desc;
 | 
			
		||||
 | 
			
		||||
				if (pos_icg_desc && clk.pol_clk)
 | 
			
		||||
					matching_icg_desc = pos_icg_desc;
 | 
			
		||||
				else if (neg_icg_desc && !clk.pol_clk)
 | 
			
		||||
					matching_icg_desc = neg_icg_desc;
 | 
			
		||||
 | 
			
		||||
				if (!matching_icg_desc)
 | 
			
		||||
					continue;
 | 
			
		||||
 | 
			
		||||
				log_debug("building ICG for clk net %s\n", clk_net.first.clk_net->name.c_str());
 | 
			
		||||
				Cell* icg = module->addCell(NEW_ID, matching_icg_desc->name);
 | 
			
		||||
				icg->setPort(matching_icg_desc->ce_pin, clk.ce_net);
 | 
			
		||||
				icg->setPort(matching_icg_desc->clk_in_pin, clk.clk_net);
 | 
			
		||||
				gclk.new_net = module->addWire(NEW_ID);
 | 
			
		||||
				icg->setPort(matching_icg_desc->clk_out_pin, gclk.new_net);
 | 
			
		||||
				// Tie low DFT ports like scan chain enable
 | 
			
		||||
				for (auto port : tie_lo_ports)
 | 
			
		||||
					icg->setPort(port, Const(0));
 | 
			
		||||
				// Fix CE polarity if needed
 | 
			
		||||
				if (!clk.pol_ce) {
 | 
			
		||||
					SigBit ce_fixed_pol = module->NotGate(NEW_ID, clk.ce_net);
 | 
			
		||||
					icg->setPort(matching_icg_desc->ce_pin, ce_fixed_pol);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for (auto cell : ce_ffs) {
 | 
			
		||||
				FfData ff(&initvals, cell);
 | 
			
		||||
				ClkNetInfo info = clk_info_from_ff(ff);
 | 
			
		||||
				auto it = clk_nets.find(info);
 | 
			
		||||
				log_assert(it != clk_nets.end() && "Bug: desync ce_ffs and clk_nets");
 | 
			
		||||
 | 
			
		||||
				if (!it->second.new_net)
 | 
			
		||||
					continue;
 | 
			
		||||
 | 
			
		||||
				log_debug("Fix up FF %s\n", cell->name.c_str());
 | 
			
		||||
				// Now we start messing with the design
 | 
			
		||||
				ff.has_ce = false;
 | 
			
		||||
				// Construct the clock gate
 | 
			
		||||
				// ICG = integrated clock gate, industry shorthand
 | 
			
		||||
				ff.sig_clk = (*it).second.new_net;
 | 
			
		||||
 | 
			
		||||
				// Rebuild the flop
 | 
			
		||||
				(void)ff.emit();
 | 
			
		||||
 | 
			
		||||
				gated_flop_count++;
 | 
			
		||||
			}
 | 
			
		||||
			ce_ffs.clear();
 | 
			
		||||
			clk_nets.clear();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		log("Converted %d FFs.\n", gated_flop_count);
 | 
			
		||||
    }
 | 
			
		||||
} ClockgatePass;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PRIVATE_NAMESPACE_END
 | 
			
		||||
							
								
								
									
										88
									
								
								tests/techmap/clockgate.ys
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								tests/techmap/clockgate.ys
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,88 @@
 | 
			
		|||
read_verilog << EOT
 | 
			
		||||
 | 
			
		||||
module dffe_00( input clk, en,
 | 
			
		||||
			input d1, output reg q1,
 | 
			
		||||
		);
 | 
			
		||||
	always @( negedge clk ) begin
 | 
			
		||||
		if ( ~en )
 | 
			
		||||
			q1 <= d1;
 | 
			
		||||
	end
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
module dffe_01( input clk, en,
 | 
			
		||||
			input d1, output reg q1,
 | 
			
		||||
		);
 | 
			
		||||
	always @( negedge clk ) begin
 | 
			
		||||
		if ( en )
 | 
			
		||||
			q1 <= d1;
 | 
			
		||||
	end
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
module dffe_10( input clk, en,
 | 
			
		||||
			input d1, output reg q1,
 | 
			
		||||
		);
 | 
			
		||||
	always @( posedge clk ) begin
 | 
			
		||||
		if ( ~en )
 | 
			
		||||
			q1 <= d1;
 | 
			
		||||
	end
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
module dffe_11( input clk, en,
 | 
			
		||||
			input d1, output reg q1,
 | 
			
		||||
		);
 | 
			
		||||
	always @( posedge clk ) begin
 | 
			
		||||
		if ( en )
 | 
			
		||||
			q1 <= d1;
 | 
			
		||||
	end
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
EOT
 | 
			
		||||
 | 
			
		||||
proc
 | 
			
		||||
opt
 | 
			
		||||
 | 
			
		||||
design -save before
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
clockgate -pos pdk_icg ce:clkin:clkout -tie_lo scanen
 | 
			
		||||
 | 
			
		||||
# falling edge clock flops don't get matched on -pos
 | 
			
		||||
select -module dffe_00 -assert-count 0 t:\\pdk_icg
 | 
			
		||||
select -module dffe_01 -assert-count 0 t:\\pdk_icg
 | 
			
		||||
# falling edge clock flops do get matched on -pos
 | 
			
		||||
select -module dffe_10 -assert-count 1 t:\\pdk_icg
 | 
			
		||||
select -module dffe_11 -assert-count 1 t:\\pdk_icg
 | 
			
		||||
# if necessary, EN is inverted, since the given ICG
 | 
			
		||||
# is assumed to have an active-high EN
 | 
			
		||||
select -module dffe_10 -assert-count 1 t:\$_NOT_
 | 
			
		||||
select -module dffe_11 -assert-count 0 t:\$_NOT_
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
design -load before
 | 
			
		||||
clockgate -min_net_size 1 -neg pdk_icg ce:clkin:clkout -tie_lo scanen
 | 
			
		||||
 | 
			
		||||
# rising edge clock flops don't get matched on -neg
 | 
			
		||||
select -module dffe_00 -assert-count 1 t:\\pdk_icg
 | 
			
		||||
select -module dffe_01 -assert-count 1 t:\\pdk_icg
 | 
			
		||||
# rising edge clock flops do get matched on -neg
 | 
			
		||||
select -module dffe_10 -assert-count 0 t:\\pdk_icg
 | 
			
		||||
select -module dffe_11 -assert-count 0 t:\\pdk_icg
 | 
			
		||||
# if necessary, EN is inverted, since the given ICG
 | 
			
		||||
# is assumed to have an active-high EN
 | 
			
		||||
select -module dffe_00 -assert-count 1 t:\$_NOT_
 | 
			
		||||
select -module dffe_01 -assert-count 0 t:\$_NOT_
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
design -load before
 | 
			
		||||
clockgate -min_net_size 2 -neg pdk_icg ce:clkin:clkout -tie_lo scanen
 | 
			
		||||
 | 
			
		||||
# No FF set sharing a (clock, clock enable) pair is large enough
 | 
			
		||||
select -module dffe_00 -assert-count 0 t:\\pdk_icg
 | 
			
		||||
select -module dffe_01 -assert-count 0 t:\\pdk_icg
 | 
			
		||||
select -module dffe_10 -assert-count 0 t:\\pdk_icg
 | 
			
		||||
select -module dffe_11 -assert-count 0 t:\\pdk_icg
 | 
			
		||||
 | 
			
		||||
# TODO test -tie_lo
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue