mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +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_shiftadd.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) | ||||
| 	$(P) mkdir -p passes/pmgen && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^) | ||||
|  |  | |||
|  | @ -40,7 +40,7 @@ struct PeepoptPass : public Pass { | |||
| 		log("\n"); | ||||
| 		log("This pass applies a collection of peephole optimizers to the current design.\n"); | ||||
| 		log("\n"); | ||||
| 		log("This pass employs the following rules:\n"); | ||||
| 		log("This pass employs the following rules by default:\n"); | ||||
| 		log("\n"); | ||||
| 		log("   * muldiv - Replace (A*B)/B with A\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("                to avoid high resource usage from large temporary MUX trees.\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 | ||||
| 	{ | ||||
| 		log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\n"); | ||||
| 
 | ||||
| 		bool formalclk = false; | ||||
| 
 | ||||
| 		size_t argidx; | ||||
| 		for (argidx = 1; argidx < args.size(); argidx++) | ||||
| 		{ | ||||
| 			if (args[argidx] == "-formalclk") { | ||||
| 				formalclk = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		extra_args(args, argidx, design); | ||||
|  | @ -86,10 +98,14 @@ struct PeepoptPass : public Pass { | |||
| 
 | ||||
| 				pm.setup(module->selected_cells()); | ||||
| 
 | ||||
| 				pm.run_shiftadd(); | ||||
| 				pm.run_shiftmul_right(); | ||||
| 				pm.run_shiftmul_left(); | ||||
| 				pm.run_muldiv(); | ||||
| 				if (formalclk) { | ||||
| 					pm.run_formal_clockgateff(); | ||||
| 				} else { | ||||
| 					pm.run_shiftadd(); | ||||
| 					pm.run_shiftmul_right(); | ||||
| 					pm.run_shiftmul_left(); | ||||
| 					pm.run_muldiv(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
							
								
								
									
										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("        Do not automatically run 'chformal -lower' to lower $check cells.\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.
 | ||||
| 	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 | ||||
| 	{ | ||||
| 		bool flag_nolower = false; | ||||
| 		bool flag_nopeepopt = false; | ||||
| 
 | ||||
| 		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; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (args[argidx] == "-nopeepopt") { | ||||
| 				flag_nopeepopt = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		extra_args(args, argidx, design); | ||||
| 
 | ||||
| 		if (!flag_nopeepopt) { | ||||
| 			log_push(); | ||||
| 			Pass::call(design, "peepopt -formalclk"); | ||||
| 			log_pop(); | ||||
| 		} | ||||
| 
 | ||||
| 		bool have_check_cells = false; | ||||
| 
 | ||||
| 		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