mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-30 19:22:31 +00:00 
			
		
		
		
	Merge pull request #144 from azonenberg/master
Added COUNT_EXTRACT constraint to greenpak4_counters pass. Added support for inferring level-resettable counters. Fixed use-after-free.
This commit is contained in:
		
						commit
						27e0d29863
					
				
					 4 changed files with 217 additions and 56 deletions
				
			
		|  | @ -33,8 +33,13 @@ module \$lut (A, Y); | |||
| 
 | ||||
|   generate | ||||
|     if (WIDTH == 1) begin | ||||
|       GP_2LUT #(.INIT({2'b00, LUT})) _TECHMAP_REPLACE_ (.OUT(Y), | ||||
|       	.IN0(A[0]), .IN1(1'b0)); | ||||
| 		if(LUT == 2'b01) begin | ||||
| 			GP_INV _TECHMAP_REPLACE_ (.OUT(Y), .IN(A[0]) ); | ||||
| 		end | ||||
| 		else begin | ||||
| 			GP_2LUT #(.INIT({2'b00, LUT})) _TECHMAP_REPLACE_ (.OUT(Y), | ||||
| 				.IN0(A[0]), .IN1(1'b0)); | ||||
| 		end | ||||
|     end else | ||||
|     if (WIDTH == 2) begin | ||||
|       GP_2LUT #(.INIT(LUT)) _TECHMAP_REPLACE_ (.OUT(Y), | ||||
|  |  | |||
|  | @ -40,6 +40,10 @@ module GP_DFFSR(input D, CLK, nSR, output reg Q); | |||
| 	end | ||||
| endmodule | ||||
| 
 | ||||
| module GP_INV(input IN, output OUT); | ||||
| 	assign OUT = ~IN; | ||||
| endmodule | ||||
| 
 | ||||
| module GP_2LUT(input IN0, IN1, output OUT); | ||||
| 	parameter [3:0] INIT = 0; | ||||
| 	assign OUT = INIT[{IN1, IN0}]; | ||||
|  |  | |||
|  | @ -87,70 +87,84 @@ bool is_unconnected(const RTLIL::SigSpec& port, ModIndex& index) | |||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| void greenpak4_counters_worker(ModIndex& index, Module *module, Cell *cell, unsigned int& total_counters) | ||||
| struct CounterExtraction | ||||
| { | ||||
| 	int width;					//counter width
 | ||||
| 	RTLIL::Wire* rwire;			//the register output
 | ||||
| 	bool has_reset;				//true if we have a reset
 | ||||
| 	RTLIL::SigSpec rst;			//reset pin
 | ||||
| 	int count_value;			//value we count from
 | ||||
| 	RTLIL::SigSpec clk;			//clock signal
 | ||||
| 	RTLIL::SigSpec outsig;		//counter output signal
 | ||||
| 	RTLIL::Cell* count_mux;		//counter mux
 | ||||
| 	RTLIL::Cell* count_reg;		//counter register
 | ||||
| 	RTLIL::Cell* underflow_inv;	//inverter reduction for output-underflow detect
 | ||||
| }; | ||||
| 
 | ||||
| //attempt to extract a counter centered on the given cell
 | ||||
| int greenpak4_counters_tryextract(ModIndex& index, Cell *cell, CounterExtraction& extract) | ||||
| { | ||||
| 	SigMap& sigmap = index.sigmap; | ||||
| 	 | ||||
| 	//Core of the counter must be an ALU
 | ||||
| 	if (cell->type != "$alu") | ||||
| 		return; | ||||
| 	 | ||||
| 	//GreenPak does not support counters larger than 14 bits so immediately skip anything bigger
 | ||||
| 	int a_width = cell->getParam("\\A_WIDTH").as_int(); | ||||
| 	extract.width = a_width; | ||||
| 	if(a_width > 14) | ||||
| 		return; | ||||
| 		return 1; | ||||
| 		 | ||||
| 	//Second input must be a single bit
 | ||||
| 	int b_width = cell->getParam("\\B_WIDTH").as_int(); | ||||
| 	if(b_width != 1) | ||||
| 		return; | ||||
| 		return 2; | ||||
| 		 | ||||
| 	//Both inputs must be unsigned, so don't extract anything with a signed input
 | ||||
| 	bool a_sign = cell->getParam("\\A_SIGNED").as_bool(); | ||||
| 	bool b_sign = cell->getParam("\\B_SIGNED").as_bool(); | ||||
| 	if(a_sign || b_sign) | ||||
| 		return; | ||||
| 		return 3; | ||||
| 
 | ||||
| 	//To be a counter, one input of the ALU must be a constant 1
 | ||||
| 	//TODO: can A or B be swapped in synthesized RTL or is B always the 1?
 | ||||
| 	const RTLIL::SigSpec b_port = sigmap(cell->getPort("\\B")); | ||||
| 	if(!b_port.is_fully_const() || (b_port.as_int() != 1) ) | ||||
| 		return; | ||||
| 		return 4; | ||||
| 		 | ||||
| 	//BI and CI must be constant 1 as well
 | ||||
| 	const RTLIL::SigSpec bi_port = sigmap(cell->getPort("\\BI")); | ||||
| 	if(!bi_port.is_fully_const() || (bi_port.as_int() != 1) ) | ||||
| 		return; | ||||
| 		return 5; | ||||
| 	const RTLIL::SigSpec ci_port = sigmap(cell->getPort("\\CI")); | ||||
| 	if(!ci_port.is_fully_const() || (ci_port.as_int() != 1) ) | ||||
| 		return; | ||||
| 		return 6; | ||||
| 				 | ||||
| 	//CO and X must be unconnected (exactly one connection to each port)
 | ||||
| 	if(!is_unconnected(sigmap(cell->getPort("\\CO")), index)) | ||||
| 		return; | ||||
| 		return 7; | ||||
| 	if(!is_unconnected(sigmap(cell->getPort("\\X")), index)) | ||||
| 		return; | ||||
| 		return 8; | ||||
| 		 | ||||
| 	//Y must have exactly one connection, and it has to be a $mux cell.
 | ||||
| 	//We must have a direct bus connection from our Y to their A.
 | ||||
| 	const RTLIL::SigSpec aluy = sigmap(cell->getPort("\\Y")); | ||||
| 	pool<Cell*> y_loads = get_other_cells(aluy, index, cell); | ||||
| 	if(y_loads.size() != 1) | ||||
| 		return; | ||||
| 		return 9; | ||||
| 	Cell* count_mux = *y_loads.begin(); | ||||
| 	extract.count_mux = count_mux; | ||||
| 	if(count_mux->type != "$mux") | ||||
| 		return; | ||||
| 		return 10; | ||||
| 	if(!is_full_bus(aluy, index, cell, "\\Y", count_mux, "\\A")) | ||||
| 		return; | ||||
| 		return 11; | ||||
| 
 | ||||
| 	//B connection of the mux is our underflow value
 | ||||
| 	const RTLIL::SigSpec underflow = sigmap(count_mux->getPort("\\B")); | ||||
| 	if(!underflow.is_fully_const()) | ||||
| 		return; | ||||
| 	int count_value = underflow.as_int(); | ||||
| 		return 12; | ||||
| 	extract.count_value = underflow.as_int(); | ||||
| 	 | ||||
| 	//S connection of the mux must come from an inverter (need not be the only load)
 | ||||
| 	const RTLIL::SigSpec muxsel = sigmap(count_mux->getPort("\\S")); | ||||
| 	extract.outsig = muxsel; | ||||
| 	pool<Cell*> muxsel_conns = get_other_cells(muxsel, index, count_mux); | ||||
| 	Cell* underflow_inv = NULL; | ||||
| 	for(auto c : muxsel_conns) | ||||
|  | @ -164,54 +178,175 @@ void greenpak4_counters_worker(ModIndex& index, Module *module, Cell *cell, unsi | |||
| 		break; | ||||
| 	} | ||||
| 	if(underflow_inv == NULL) | ||||
| 		return; | ||||
| 		return 13; | ||||
| 	extract.underflow_inv = underflow_inv; | ||||
| 	 | ||||
| 	//Y connection of the mux must have exactly one load, the counter's internal register
 | ||||
| 	const RTLIL::SigSpec muxy = sigmap(count_mux->getPort("\\Y")); | ||||
| 	pool<Cell*> muxy_loads = get_other_cells(muxy, index, count_mux); | ||||
| 	if(muxy_loads.size() != 1) | ||||
| 		return; | ||||
| 		return 14; | ||||
| 	Cell* count_reg = *muxy_loads.begin(); | ||||
| 	if(count_reg->type != "$dff")			//TODO: support dffr/dffs?
 | ||||
| 		return; | ||||
| 	extract.count_reg = count_reg; | ||||
| 	if(count_reg->type == "$dff") | ||||
| 		extract.has_reset = false; | ||||
| 	else if(count_reg->type == "$adff") | ||||
| 	{ | ||||
| 		extract.has_reset = true; | ||||
| 		 | ||||
| 		//Verify ARST_VALUE is zero and ARST_POLARITY is 1
 | ||||
| 		//TODO: infer an inverter to make it 1 if necessary, so we can support negative level resets?
 | ||||
| 		if(count_reg->getParam("\\ARST_POLARITY").as_int() != 1) | ||||
| 			return 22; | ||||
| 		if(count_reg->getParam("\\ARST_VALUE").as_int() != 0) | ||||
| 			return 23; | ||||
| 			 | ||||
| 		//Save the reset
 | ||||
| 		extract.rst = sigmap(count_reg->getPort("\\ARST")); | ||||
| 	} | ||||
| 	//TODO: support synchronous reset
 | ||||
| 	else | ||||
| 		return 15; | ||||
| 	if(!is_full_bus(muxy, index, count_mux, "\\Y", count_reg, "\\D")) | ||||
| 		return; | ||||
| 		return 16; | ||||
| 		 | ||||
| 	//TODO: Verify count_reg CLK_POLARITY is 1
 | ||||
| 		 | ||||
| 	//Register output must have exactly two loads, the inverter and ALU
 | ||||
| 	const RTLIL::SigSpec cnout = sigmap(count_reg->getPort("\\Q")); | ||||
| 	pool<Cell*> cnout_loads = get_other_cells(cnout, index, count_reg); | ||||
| 	if(cnout_loads.size() != 2) | ||||
| 		return; | ||||
| 		return 17; | ||||
| 	if(!is_full_bus(cnout, index, count_reg, "\\Q", underflow_inv, "\\A", true)) | ||||
| 		return; | ||||
| 		return 18; | ||||
| 	if(!is_full_bus(cnout, index, count_reg, "\\Q", cell, "\\A", true)) | ||||
| 		return; | ||||
| 		return 19; | ||||
| 		 | ||||
| 	//Look up the clock from the register
 | ||||
| 	const RTLIL::SigSpec clk = sigmap(count_reg->getPort("\\CLK")); | ||||
| 	extract.clk = sigmap(count_reg->getPort("\\CLK")); | ||||
| 	 | ||||
| 	//Register output net must have an INIT attribute equal to the count value
 | ||||
| 	auto rwire = cnout.as_wire(); | ||||
| 	if(rwire->attributes.find("\\init") == rwire->attributes.end()) | ||||
| 	extract.rwire = cnout.as_wire(); | ||||
| 	if(extract.rwire->attributes.find("\\init") == extract.rwire->attributes.end()) | ||||
| 		return 20; | ||||
| 	int rinit = extract.rwire->attributes["\\init"].as_int(); | ||||
| 	if(rinit != extract.count_value) | ||||
| 		return 21; | ||||
| 		 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void greenpak4_counters_worker( | ||||
| 	ModIndex& index, | ||||
| 	Cell *cell, | ||||
| 	unsigned int& total_counters, | ||||
| 	pool<Cell*>& cells_to_remove) | ||||
| { | ||||
| 	SigMap& sigmap = index.sigmap; | ||||
| 	 | ||||
| 	//Core of the counter must be an ALU
 | ||||
| 	if (cell->type != "$alu") | ||||
| 		return; | ||||
| 	int rinit = rwire->attributes["\\init"].as_int(); | ||||
| 	if(rinit != count_value) | ||||
| 	 | ||||
| 	//A input is the count value. Check if it has COUNT_EXTRACT set
 | ||||
| 	RTLIL::Wire* a_wire = sigmap(cell->getPort("\\A")).as_wire(); | ||||
| 	bool force_extract = false; | ||||
| 	bool never_extract = false; | ||||
| 	string count_reg_src = a_wire->attributes["\\src"].decode_string().c_str(); | ||||
| 	if(a_wire->attributes.find("\\COUNT_EXTRACT") != a_wire->attributes.end()) | ||||
| 	{ | ||||
| 		pool<string> sa = a_wire->get_strpool_attribute("\\COUNT_EXTRACT"); | ||||
| 		string extract_value; | ||||
| 		if(sa.size() >= 1) | ||||
| 		{ | ||||
| 			extract_value = *sa.begin(); | ||||
| 			log("  Signal %s declared at %s has COUNT_EXTRACT = %s\n", | ||||
| 				log_id(a_wire), | ||||
| 				count_reg_src.c_str(), | ||||
| 				extract_value.c_str()); | ||||
| 				 | ||||
| 			if(extract_value == "FORCE") | ||||
| 				force_extract = true; | ||||
| 			else if(extract_value == "NO") | ||||
| 				never_extract = true; | ||||
| 			else if(extract_value == "AUTO") | ||||
| 			{}	//default
 | ||||
| 			else | ||||
| 				log_error("  Illegal COUNT_EXTRACT value %s (must be one of FORCE, NO, AUTO)\n", | ||||
| 					extract_value.c_str()); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	//If we're explicitly told not to extract, don't infer a counter
 | ||||
| 	if(never_extract) | ||||
| 		return; | ||||
| 	 | ||||
| 	//Attempt to extract a counter
 | ||||
| 	CounterExtraction extract; | ||||
| 	int reason = greenpak4_counters_tryextract(index, cell, extract); | ||||
| 	 | ||||
| 	//Nonzero code - we could not find a matchable counter.
 | ||||
| 	//Do nothing, unless extraction was forced in which case give an error
 | ||||
| 	if(reason != 0) | ||||
| 	{ | ||||
| 		static const char* reasons[24]= | ||||
| 		{ | ||||
| 			"no problem",									//0
 | ||||
| 			"counter is larger than 14 bits",				//1
 | ||||
| 			"counter does not count by one",				//2
 | ||||
| 			"counter uses signed math",						//3
 | ||||
| 			"counter does not count by one",				//4
 | ||||
| 			"ALU is not a subtractor",						//5
 | ||||
| 			"ALU is not a subtractor",						//6
 | ||||
| 			"ALU ports used outside counter",				//7
 | ||||
| 			"ALU ports used outside counter",				//8
 | ||||
| 			"ALU output used outside counter",				//9
 | ||||
| 			"ALU output is not a mux",						//10
 | ||||
| 			"ALU output is not full bus",					//11
 | ||||
| 			"Underflow value is not constant",				//12
 | ||||
| 			"No underflow detector found",					//13
 | ||||
| 			"Mux output is used outside counter",			//14
 | ||||
| 			"Counter reg is not DFF/ADFF",					//15
 | ||||
| 			"Counter input is not full bus",				//16
 | ||||
| 			"Count register is used outside counter",		//17
 | ||||
| 			"Register output is not full bus",				//18
 | ||||
| 			"Register output is not full bus",				//19
 | ||||
| 			"No init value found",							//20
 | ||||
| 			"Underflow value is not equal to init value",	//21
 | ||||
| 			"Reset polarity is not positive",				//22
 | ||||
| 			"Reset is not to zero"							//23
 | ||||
| 		}; | ||||
| 		 | ||||
| 		if(force_extract) | ||||
| 		{ | ||||
| 			log_error( | ||||
| 			"Counter extraction is set to FORCE on register %s, but a counter could not be inferred (%s)\n", | ||||
| 			log_id(a_wire), | ||||
| 			reasons[reason]); | ||||
| 		} | ||||
| 		return; | ||||
| 	} | ||||
| 	 | ||||
| 	//Figure out the final cell type based on the counter size
 | ||||
| 	string celltype = "\\GP_COUNT8"; | ||||
| 	if(a_width > 8) | ||||
| 	if(extract.width > 8) | ||||
| 		celltype = "\\GP_COUNT14"; | ||||
| 	 | ||||
| 	//Log it
 | ||||
| 	total_counters ++; | ||||
| 	string count_reg_src = rwire->attributes["\\src"].decode_string().c_str(); | ||||
| 	log("  Found %d-bit non-resettable down counter (from %d) for register %s declared at %s\n", | ||||
| 		a_width, | ||||
| 		count_value, | ||||
| 		log_id(rwire->name), | ||||
| 	string reset_type = "non-resettable"; | ||||
| 	if(extract.has_reset) | ||||
| 	{ | ||||
| 		//TODO: support other kind of reset
 | ||||
| 		reset_type = "async resettable"; | ||||
| 	} | ||||
| 	log("  Found %d-bit %s down counter (from %d) for register %s declared at %s\n", | ||||
| 		extract.width, | ||||
| 		reset_type.c_str(), | ||||
| 		extract.count_value, | ||||
| 		log_id(extract.rwire->name), | ||||
| 		count_reg_src.c_str()); | ||||
| 		 | ||||
| 	 | ||||
| 	//Wipe all of the old connections to the ALU
 | ||||
| 	cell->unsetPort("\\A"); | ||||
| 	cell->unsetPort("\\B"); | ||||
|  | @ -225,22 +360,34 @@ void greenpak4_counters_worker(ModIndex& index, Module *module, Cell *cell, unsi | |||
| 	cell->unsetParam("\\B_SIGNED"); | ||||
| 	cell->unsetParam("\\B_WIDTH"); | ||||
| 	cell->unsetParam("\\Y_WIDTH"); | ||||
| 	 | ||||
| 
 | ||||
| 	//Change the cell type
 | ||||
| 	cell->type = celltype; | ||||
| 	 | ||||
| 	//Hook it up to everything
 | ||||
| 	cell->setParam("\\RESET_MODE", RTLIL::Const("RISING")); | ||||
| 	//Hook up resets
 | ||||
| 	if(extract.has_reset) | ||||
| 	{ | ||||
| 		//TODO: support other kinds of reset
 | ||||
| 		cell->setParam("\\RESET_MODE", RTLIL::Const("LEVEL")); | ||||
| 		cell->setPort("\\RST", extract.rst); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		cell->setParam("\\RESET_MODE", RTLIL::Const("RISING")); | ||||
| 		cell->setPort("\\RST", RTLIL::SigSpec(false)); | ||||
| 	} | ||||
| 	 | ||||
| 	//Hook up other stuff
 | ||||
| 	cell->setParam("\\CLKIN_DIVIDE", RTLIL::Const(1)); | ||||
| 	cell->setParam("\\COUNT_TO", RTLIL::Const(count_value)); | ||||
| 	cell->setPort("\\CLK", clk); | ||||
| 	cell->setPort("\\RST", RTLIL::SigSpec(false)); | ||||
| 	cell->setPort("\\OUT", muxsel); | ||||
| 	cell->setParam("\\COUNT_TO", RTLIL::Const(extract.count_value)); | ||||
| 	 | ||||
| 	cell->setPort("\\CLK", extract.clk); | ||||
| 	cell->setPort("\\OUT", extract.outsig); | ||||
| 	 | ||||
| 	//Delete the cells we've replaced (let opt_clean handle deleting the now-redundant wires)
 | ||||
| 	module->remove(count_mux); | ||||
| 	module->remove(count_reg); | ||||
| 	module->remove(underflow_inv); | ||||
| 	cells_to_remove.insert(extract.count_mux); | ||||
| 	cells_to_remove.insert(extract.count_reg); | ||||
| 	cells_to_remove.insert(extract.underflow_inv); | ||||
| } | ||||
| 
 | ||||
| struct Greenpak4CountersPass : public Pass { | ||||
|  | @ -251,8 +398,8 @@ struct Greenpak4CountersPass : public Pass { | |||
| 		log("\n"); | ||||
| 		log("    greenpak4_counters [options] [selection]\n"); | ||||
| 		log("\n"); | ||||
| 		log("This pass converts non-resettable down counters to GreenPak4 counter cells\n"); | ||||
| 		log("(All other GreenPak4 counter modes must be instantiated manually for now.)\n"); | ||||
| 		log("This pass converts non-resettable or async resettable down counters to GreenPak4\n"); | ||||
| 		log("counter cells (All other GreenPak4 counter modes must be instantiated manually.)\n"); | ||||
| 		log("\n"); | ||||
| 	} | ||||
| 	virtual void execute(std::vector<std::string> args, RTLIL::Design *design) | ||||
|  | @ -269,17 +416,22 @@ struct Greenpak4CountersPass : public Pass { | |||
| 		} | ||||
| 		extra_args(args, argidx, design); | ||||
| 		 | ||||
| 		//Extract all of the counters we could find
 | ||||
| 		unsigned int total_counters = 0; | ||||
| 		for (auto module : design->selected_modules()) | ||||
| 		{ | ||||
| 			pool<Cell*> cells_to_remove; | ||||
| 			 | ||||
| 			ModIndex index(module); | ||||
| 			for (auto cell : module->selected_cells()) | ||||
| 				greenpak4_counters_worker(index, module, cell, total_counters); | ||||
| 				greenpak4_counters_worker(index, cell, total_counters, cells_to_remove); | ||||
| 				 | ||||
| 			for(auto cell : cells_to_remove) | ||||
| 				module->remove(cell); | ||||
| 		} | ||||
| 		 | ||||
| 		if(total_counters) | ||||
| 			log("Extracted %u counters\n", total_counters); | ||||
| 		 | ||||
| 	} | ||||
| } Greenpak4CountersPass; | ||||
| 
 | ||||
|  |  | |||
|  | @ -207,8 +207,8 @@ struct SynthGreenPAK4Pass : public Pass { | |||
| 		if (check_label(active, run_from, run_to, "map_luts")) | ||||
| 		{ | ||||
| 			if (part == "SLG46140V") Pass::call(design, "nlutmap -luts 0,6,8,2"); | ||||
| 			if (part == "SLG46620V") Pass::call(design, "nlutmap -luts 0,8,16,2"); | ||||
| 			if (part == "SLG46621V") Pass::call(design, "nlutmap -luts 0,8,16,2"); | ||||
| 			if (part == "SLG46620V") Pass::call(design, "nlutmap -luts 2,8,16,2"); | ||||
| 			if (part == "SLG46621V") Pass::call(design, "nlutmap -luts 2,8,16,2"); | ||||
| 			Pass::call(design, "clean"); | ||||
| 		} | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue