mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 11:42:30 +00:00 
			
		
		
		
	Merge pull request #4525 from georgerennie/peepopt_clock_gate
peepopt: Add formal opt to rewrite latches to ffs in clock gates
This commit is contained in:
		
						commit
						1b1a6c4aed
					
				
					 5 changed files with 141 additions and 5 deletions
				
			
		|  | @ -57,6 +57,7 @@ PEEPOPT_PATTERN  = passes/pmgen/peepopt_shiftmul_right.pmg | ||||||
| PEEPOPT_PATTERN += passes/pmgen/peepopt_shiftmul_left.pmg | PEEPOPT_PATTERN += passes/pmgen/peepopt_shiftmul_left.pmg | ||||||
| PEEPOPT_PATTERN += passes/pmgen/peepopt_shiftadd.pmg | PEEPOPT_PATTERN += passes/pmgen/peepopt_shiftadd.pmg | ||||||
| PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg | PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg | ||||||
|  | PEEPOPT_PATTERN += passes/pmgen/peepopt_formal_clockgateff.pmg | ||||||
| 
 | 
 | ||||||
| passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) | passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) | ||||||
| 	$(P) mkdir -p passes/pmgen && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^) | 	$(P) mkdir -p passes/pmgen && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^) | ||||||
|  |  | ||||||
|  | @ -40,7 +40,7 @@ struct PeepoptPass : public Pass { | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("This pass applies a collection of peephole optimizers to the current design.\n"); | 		log("This pass applies a collection of peephole optimizers to the current design.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("This pass employs the following rules:\n"); | 		log("This pass employs the following rules by default:\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("   * muldiv - Replace (A*B)/B with A\n"); | 		log("   * muldiv - Replace (A*B)/B with A\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | @ -57,14 +57,26 @@ struct PeepoptPass : public Pass { | ||||||
| 		log("                limits the amount of padding to a multiple of the data, \n"); | 		log("                limits the amount of padding to a multiple of the data, \n"); | ||||||
| 		log("                to avoid high resource usage from large temporary MUX trees.\n"); | 		log("                to avoid high resource usage from large temporary MUX trees.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | 		log("If -formalclk is specified it instead employs the following rules:\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("   * clockgateff - Replace latch based clock gating patterns with a flip-flop\n"); | ||||||
|  | 		log("                   based pattern to prevent combinational paths from the\n"); | ||||||
|  | 		log("                   output to the enable input after running clk2fflogic.\n"); | ||||||
|  | 		log("\n"); | ||||||
| 	} | 	} | ||||||
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) override | 	void execute(std::vector<std::string> args, RTLIL::Design *design) override | ||||||
| 	{ | 	{ | ||||||
| 		log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\n"); | 		log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\n"); | ||||||
| 
 | 
 | ||||||
|  | 		bool formalclk = false; | ||||||
|  | 
 | ||||||
| 		size_t argidx; | 		size_t argidx; | ||||||
| 		for (argidx = 1; argidx < args.size(); argidx++) | 		for (argidx = 1; argidx < args.size(); argidx++) | ||||||
| 		{ | 		{ | ||||||
|  | 			if (args[argidx] == "-formalclk") { | ||||||
|  | 				formalclk = true; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 		extra_args(args, argidx, design); | 		extra_args(args, argidx, design); | ||||||
|  | @ -86,6 +98,9 @@ struct PeepoptPass : public Pass { | ||||||
| 
 | 
 | ||||||
| 				pm.setup(module->selected_cells()); | 				pm.setup(module->selected_cells()); | ||||||
| 
 | 
 | ||||||
|  | 				if (formalclk) { | ||||||
|  | 					pm.run_formal_clockgateff(); | ||||||
|  | 				} else { | ||||||
| 					pm.run_shiftadd(); | 					pm.run_shiftadd(); | ||||||
| 					pm.run_shiftmul_right(); | 					pm.run_shiftmul_right(); | ||||||
| 					pm.run_shiftmul_left(); | 					pm.run_shiftmul_left(); | ||||||
|  | @ -93,6 +108,7 @@ struct PeepoptPass : public Pass { | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
| } PeepoptPass; | } PeepoptPass; | ||||||
| 
 | 
 | ||||||
| PRIVATE_NAMESPACE_END | PRIVATE_NAMESPACE_END | ||||||
|  |  | ||||||
							
								
								
									
										59
									
								
								passes/pmgen/peepopt_formal_clockgateff.pmg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								passes/pmgen/peepopt_formal_clockgateff.pmg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | ||||||
|  | pattern formal_clockgateff | ||||||
|  | 
 | ||||||
|  | // Detects the most common clock gating pattern using a latch and replaces it | ||||||
|  | // with a functionally equivalent pattern based on a flip-flop. The latch | ||||||
|  | // based pattern has a combinational path from the enable input to output after | ||||||
|  | // clk2fflogic, but this is a stable loop and the flip-flop based pattern does | ||||||
|  | // not exhibit this. | ||||||
|  | // | ||||||
|  | // This optimization is suitable for formal to prevent false comb loops, but | ||||||
|  | // should not be used for synthesis where the latch is an intentional choice | ||||||
|  | // | ||||||
|  | // Latch style: | ||||||
|  | // always @* if (!clk_i) latched_en = en; | ||||||
|  | // assign gated_clk_o = latched_en & clk_i; | ||||||
|  | // | ||||||
|  | // Flip-flop style: | ||||||
|  | // always @(posedge clk) flopped_en <= en; | ||||||
|  | // assign gated_clk_o = flopped_en & clk_i; | ||||||
|  | 
 | ||||||
|  | state <SigSpec> clk en latched_en gated_clk | ||||||
|  | state <IdString> latched_en_port_name | ||||||
|  | 
 | ||||||
|  | match latch | ||||||
|  | 	select latch->type == $dlatch | ||||||
|  | 	select param(latch, \WIDTH) == 1 | ||||||
|  | 	select param(latch, \EN_POLARITY).as_bool() == false | ||||||
|  | 	set clk port(latch, \EN) | ||||||
|  | 	set en port(latch, \D) | ||||||
|  | 	set latched_en port(latch, \Q) | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | match and_gate | ||||||
|  | 	select and_gate->type.in($and, $logic_and) | ||||||
|  | 	select param(and_gate, \A_WIDTH) == 1 | ||||||
|  | 	select param(and_gate, \B_WIDTH) == 1 | ||||||
|  | 	select param(and_gate, \Y_WIDTH) == 1 | ||||||
|  | 	choice <IdString> clk_port {\A, \B} | ||||||
|  | 	define <IdString> latch_port {clk_port == \A ? \B : \A} | ||||||
|  | 	index <SigSpec> port(and_gate, clk_port) === clk | ||||||
|  | 	index <SigSpec> port(and_gate, latch_port) === latched_en | ||||||
|  | 	set gated_clk port(and_gate, \Y) | ||||||
|  | 	set latched_en_port_name latch_port | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | 	log("replacing clock gate pattern in %s with ff: latch=%s, and=%s\n", | ||||||
|  | 		log_id(module), log_id(latch), log_id(and_gate)); | ||||||
|  | 
 | ||||||
|  | 	// Add a flip-flop and rewire the AND gate to use the output of this flop | ||||||
|  | 	// instead of the latch. We don't delete the latch in case its output is | ||||||
|  | 	// used to drive other nodes. If it isn't, it will be trivially removed by | ||||||
|  | 	// clean | ||||||
|  | 	SigSpec flopped_en = module->addWire(NEW_ID); | ||||||
|  | 	module->addDff(NEW_ID, clk, en, flopped_en, true, latch->get_src_attribute()); | ||||||
|  | 	and_gate->setPort(latched_en_port_name, flopped_en); | ||||||
|  | 	did_something = true; | ||||||
|  | 
 | ||||||
|  | 	accept; | ||||||
|  | endcode | ||||||
|  | @ -51,6 +51,10 @@ struct Clk2fflogicPass : public Pass { | ||||||
| 		log("    -nolower\n"); | 		log("    -nolower\n"); | ||||||
| 		log("        Do not automatically run 'chformal -lower' to lower $check cells.\n"); | 		log("        Do not automatically run 'chformal -lower' to lower $check cells.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | 		log("    -nopeepopt\n"); | ||||||
|  | 		log("        Do not automatically run 'peepopt -formalclk' to rewrite clock patterns\n"); | ||||||
|  | 		log("        to more formal friendly forms.\n"); | ||||||
|  | 		log("\n"); | ||||||
| 	} | 	} | ||||||
| 	// Active-high sampled and current value of a level-triggered control signal. Initial sampled values is low/non-asserted.
 | 	// Active-high sampled and current value of a level-triggered control signal. Initial sampled values is low/non-asserted.
 | ||||||
| 	SampledSig sample_control(Module *module, SigSpec sig, bool polarity, bool is_fine) { | 	SampledSig sample_control(Module *module, SigSpec sig, bool polarity, bool is_fine) { | ||||||
|  | @ -121,6 +125,7 @@ struct Clk2fflogicPass : public Pass { | ||||||
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) override | 	void execute(std::vector<std::string> args, RTLIL::Design *design) override | ||||||
| 	{ | 	{ | ||||||
| 		bool flag_nolower = false; | 		bool flag_nolower = false; | ||||||
|  | 		bool flag_nopeepopt = false; | ||||||
| 
 | 
 | ||||||
| 		log_header(design, "Executing CLK2FFLOGIC pass (convert clocked FFs to generic $ff cells).\n"); | 		log_header(design, "Executing CLK2FFLOGIC pass (convert clocked FFs to generic $ff cells).\n"); | ||||||
| 
 | 
 | ||||||
|  | @ -131,10 +136,20 @@ struct Clk2fflogicPass : public Pass { | ||||||
| 				flag_nolower = true; | 				flag_nolower = true; | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
|  | 			if (args[argidx] == "-nopeepopt") { | ||||||
|  | 				flag_nopeepopt = true; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 		extra_args(args, argidx, design); | 		extra_args(args, argidx, design); | ||||||
| 
 | 
 | ||||||
|  | 		if (!flag_nopeepopt) { | ||||||
|  | 			log_push(); | ||||||
|  | 			Pass::call(design, "peepopt -formalclk"); | ||||||
|  | 			log_pop(); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		bool have_check_cells = false; | 		bool have_check_cells = false; | ||||||
| 
 | 
 | ||||||
| 		for (auto module : design->selected_modules()) | 		for (auto module : design->selected_modules()) | ||||||
|  |  | ||||||
							
								
								
									
										45
									
								
								tests/various/peepopt_formal.ys
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								tests/various/peepopt_formal.ys
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | ||||||
|  | read_verilog -sv <<EOT | ||||||
|  | module peepopt_formal_clockgateff_0( | ||||||
|  | 	input  logic clk_i, | ||||||
|  | 	input  logic ena_i, | ||||||
|  | 	input  logic enb_i, | ||||||
|  | 	output logic clk_o | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | logic en_latched; | ||||||
|  | 
 | ||||||
|  | always_latch | ||||||
|  | 	if (!clk_i) | ||||||
|  | 		en_latched <= ena_i | enb_i; | ||||||
|  | 
 | ||||||
|  | assign clk_o = en_latched & clk_i; | ||||||
|  | 
 | ||||||
|  | endmodule | ||||||
|  | EOT | ||||||
|  | 
 | ||||||
|  | # Check original design has latch | ||||||
|  | prep -auto-top | ||||||
|  | select -assert-count 1 t:$dlatch | ||||||
|  | select -assert-count 0 t:$dff | ||||||
|  | 
 | ||||||
|  | # Manually execute equiv_opt like pattern so clk2fflogic is called with | ||||||
|  | # -nopeepopt, otherwise this doesn't test anything | ||||||
|  | design -save preopt | ||||||
|  | check -assert | ||||||
|  | peepopt -formalclk | ||||||
|  | check -assert | ||||||
|  | design -stash postopt | ||||||
|  | 
 | ||||||
|  | # Create miter and clk2fflogic without peepopt | ||||||
|  | design -copy-from preopt -as gold A:top | ||||||
|  | design -copy-from postopt -as gate A:top | ||||||
|  | clk2fflogic -nopeepopt | ||||||
|  | equiv_make gold gate equiv | ||||||
|  | equiv_induct equiv | ||||||
|  | equiv_status -assert equiv | ||||||
|  | 
 | ||||||
|  | # Check final design has dff instead of latch | ||||||
|  | design -load postopt | ||||||
|  | clean | ||||||
|  | select -assert-count 0 t:$dlatch | ||||||
|  | select -assert-count 1 t:$dff | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue