mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +00:00 
			
		
		
		
	Add actual DSP inference to ice40_dsp pass
Signed-off-by: Clifford Wolf <clifford@clifford.at>
This commit is contained in:
		
							parent
							
								
									c06c062469
								
							
						
					
					
						commit
						5a853ed46c
					
				
					 3 changed files with 214 additions and 24 deletions
				
			
		|  | @ -24,6 +24,180 @@ | |||
| USING_YOSYS_NAMESPACE | ||||
| PRIVATE_NAMESPACE_BEGIN | ||||
| 
 | ||||
| void create_ice40_dsp(ice40_dsp_pm &pm) | ||||
| { | ||||
| #if 0 | ||||
| 	log("\n"); | ||||
| 	log("ffA:   %s\n", log_id(pm.st.ffA, "--")); | ||||
| 	log("ffB:   %s\n", log_id(pm.st.ffB, "--")); | ||||
| 	log("mul:   %s\n", log_id(pm.st.mul, "--")); | ||||
| 	log("ffY:   %s\n", log_id(pm.st.ffY, "--")); | ||||
| 	log("addAB: %s\n", log_id(pm.st.addAB, "--")); | ||||
| 	log("muxAB: %s\n", log_id(pm.st.muxAB, "--")); | ||||
| 	log("ffS:   %s\n", log_id(pm.st.ffS, "--")); | ||||
| #endif | ||||
| 
 | ||||
| 	log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(pm.st.mul)); | ||||
| 
 | ||||
| 	if (GetSize(pm.st.sigA) > 16) { | ||||
| 		log("  input A (%s) is too large (%d > 16).\n", log_signal(pm.st.sigA), GetSize(pm.st.sigA)); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (GetSize(pm.st.sigB) > 16) { | ||||
| 		log("  input B (%s) is too large (%d > 16).\n", log_signal(pm.st.sigB), GetSize(pm.st.sigB)); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (GetSize(pm.st.sigS) > 32) { | ||||
| 		log("  accumulator (%s) is too large (%d > 32).\n", log_signal(pm.st.sigS), GetSize(pm.st.sigS)); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (GetSize(pm.st.sigY) > 32) { | ||||
| 		log("  output (%s) is too large (%d > 32).\n", log_signal(pm.st.sigY), GetSize(pm.st.sigY)); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	log("  replacing $mul with SB_MAC16 cell.\n"); | ||||
| 
 | ||||
| 	bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool(); | ||||
| 
 | ||||
| 	Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16"); | ||||
| 	pm.module->swap_names(cell, pm.st.mul); | ||||
| 
 | ||||
| 	// SB_MAC16 Input Interface
 | ||||
| 
 | ||||
| 	SigSpec A = pm.st.sigA; | ||||
| 	A.extend_u0(16, mul_signed); | ||||
| 
 | ||||
| 	SigSpec B = pm.st.sigB; | ||||
| 	B.extend_u0(16, mul_signed); | ||||
| 
 | ||||
| 	SigSpec CD; | ||||
| 	if (pm.st.muxA) | ||||
| 		CD = pm.st.muxA->getPort("\\B"); | ||||
| 	if (pm.st.muxB) | ||||
| 		CD = pm.st.muxB->getPort("\\A"); | ||||
| 	CD.extend_u0(32, mul_signed); | ||||
| 
 | ||||
| 	cell->setPort("\\A", A); | ||||
| 	cell->setPort("\\B", B); | ||||
| 	cell->setPort("\\C", CD.extract(0, 16)); | ||||
| 	cell->setPort("\\D", CD.extract(16, 16)); | ||||
| 
 | ||||
| 	cell->setParam("\\A_REG", pm.st.ffA ? State::S0 : State::S1); | ||||
| 	cell->setParam("\\B_REG", pm.st.ffB ? State::S0 : State::S1); | ||||
| 
 | ||||
| 	cell->setPort("\\AHOLD", State::S0); | ||||
| 	cell->setPort("\\BHOLD", State::S0); | ||||
| 	cell->setPort("\\CHOLD", State::S0); | ||||
| 	cell->setPort("\\DHOLD", State::S0); | ||||
| 
 | ||||
| 	cell->setPort("\\IRSTTOP", State::S0); | ||||
| 	cell->setPort("\\IRSTBOT", State::S0); | ||||
| 
 | ||||
| 	if (pm.st.clock_vld) | ||||
| 	{ | ||||
| 		cell->setPort("\\CLK", pm.st.clock); | ||||
| 		cell->setPort("\\CE", State::S1); | ||||
| 		cell->setParam("\\NEG_TRIGGER", pm.st.clock_pol ? State::S0 : State::S1); | ||||
| 
 | ||||
| 		log("  clock: %s (%s)", log_signal(pm.st.clock), pm.st.clock_pol ? "posedge" : "negedge"); | ||||
| 
 | ||||
| 		if (pm.st.ffA) | ||||
| 			log(" ffA:%s", log_id(pm.st.ffA)); | ||||
| 
 | ||||
| 		if (pm.st.ffB) | ||||
| 			log(" ffB:%s", log_id(pm.st.ffB)); | ||||
| 
 | ||||
| 		if (pm.st.ffY) | ||||
| 			log(" ffY:%s", log_id(pm.st.ffY)); | ||||
| 
 | ||||
| 		if (pm.st.ffS) | ||||
| 			log(" ffS:%s", log_id(pm.st.ffS)); | ||||
| 
 | ||||
| 		log("\n"); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		cell->setPort("\\CLK", State::S0); | ||||
| 		cell->setPort("\\CE", State::S0); | ||||
| 		cell->setParam("\\NEG_TRIGGER", State::S0); | ||||
| 	} | ||||
| 
 | ||||
| 	// SB_MAC16 Cascade Interface
 | ||||
| 
 | ||||
| 	cell->setPort("\\SIGNEXTIN", State::Sx); | ||||
| 	cell->setPort("\\SIGNEXTOUT", pm.module->addWire(NEW_ID)); | ||||
| 
 | ||||
| 	cell->setPort("\\CI", State::Sx); | ||||
| 	cell->setPort("\\CO", pm.module->addWire(NEW_ID)); | ||||
| 
 | ||||
| 	cell->setPort("\\ACCUMCI", State::Sx); | ||||
| 	cell->setPort("\\ACCUMCO", pm.module->addWire(NEW_ID)); | ||||
| 
 | ||||
| 	// SB_MAC16 Output Interface
 | ||||
| 
 | ||||
| 	SigSpec O = pm.st.ffS ? pm.st.sigS : pm.st.sigY; | ||||
| 	if (GetSize(O) < 32) | ||||
| 		O.append(pm.module->addWire(NEW_ID, 32-GetSize(O))); | ||||
| 
 | ||||
| 	cell->setPort("\\O", O); | ||||
| 
 | ||||
| 	if (pm.st.addAB) { | ||||
| 		log("  accumulator %s (%s)\n", log_id(pm.st.addAB), log_id(pm.st.addAB->type)); | ||||
| 		cell->setPort("\\ADDSUBTOP", pm.st.addAB->type == "$add" ? State::S0 : State::S1); | ||||
| 		cell->setPort("\\ADDSUBBOT", pm.st.addAB->type == "$add" ? State::S0 : State::S1); | ||||
| 	} else { | ||||
| 		cell->setPort("\\ADDSUBTOP", State::S0); | ||||
| 		cell->setPort("\\ADDSUBBOT", State::S0); | ||||
| 	} | ||||
| 
 | ||||
| 	cell->setPort("\\ORTSTOP", State::S0); | ||||
| 	cell->setPort("\\ORTSBOT", State::S0); | ||||
| 
 | ||||
| 	cell->setPort("\\OHOLDTOP", State::S0); | ||||
| 	cell->setPort("\\OHOLDBOT", State::S0); | ||||
| 
 | ||||
| 	SigSpec acc_reset = State::S0; | ||||
| 	if (pm.st.muxA) | ||||
| 		acc_reset = pm.st.muxA->getPort("\\S"); | ||||
| 	if (pm.st.muxB) | ||||
| 		acc_reset = pm.module->Not(NEW_ID, pm.st.muxB->getPort("\\S")); | ||||
| 
 | ||||
| 	cell->setPort("\\OLOADTOP", acc_reset); | ||||
| 	cell->setPort("\\OLOADBOT", acc_reset); | ||||
| 
 | ||||
| 	// SB_MAC16 Remaining Parameters
 | ||||
| 
 | ||||
| 	cell->setParam("\\C_REG", State::S0); | ||||
| 	cell->setParam("\\D_REG", State::S0); | ||||
| 
 | ||||
| 	cell->setParam("\\TOP_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0); | ||||
| 	cell->setParam("\\BOT_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0); | ||||
| 	cell->setParam("\\PIPELINE_16X16_MULT_REG1", pm.st.ffY ? State::S1 : State::S0); | ||||
| 	cell->setParam("\\PIPELINE_16X16_MULT_REG2", State::S0); | ||||
| 
 | ||||
| 	cell->setParam("\\TOPOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2)); | ||||
| 	cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2)); | ||||
| 	cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0); | ||||
| 	cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2)); | ||||
| 
 | ||||
| 	cell->setParam("\\BOTOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2)); | ||||
| 	cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2)); | ||||
| 	cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0); | ||||
| 	cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2)); | ||||
| 
 | ||||
| 	cell->setParam("\\MODE_8x8", State::S0); | ||||
| 	cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0); | ||||
| 	cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0); | ||||
| 
 | ||||
| 	pm.autoremove(pm.st.mul); | ||||
| 	pm.autoremove(pm.st.ffY); | ||||
| 	pm.autoremove(pm.st.ffS); | ||||
| } | ||||
| 
 | ||||
| struct Ice40DspPass : public Pass { | ||||
| 	Ice40DspPass() : Pass("ice40_dsp", "iCE40: map multipliers") { } | ||||
| 	void help() YS_OVERRIDE | ||||
|  | @ -32,7 +206,7 @@ struct Ice40DspPass : public Pass { | |||
| 		log("\n"); | ||||
| 		log("    ice40_dsp [options] [selection]\n"); | ||||
| 		log("\n"); | ||||
| 		log("Map multipliers and iCE40 DSP resources.\n"); | ||||
| 		log("Map multipliers and multiply-accumulate blocks to iCE40 DSP resources.\n"); | ||||
| 		log("\n"); | ||||
| 	} | ||||
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||
|  | @ -51,26 +225,7 @@ struct Ice40DspPass : public Pass { | |||
| 		extra_args(args, argidx, design); | ||||
| 
 | ||||
| 		for (auto module : design->selected_modules()) | ||||
| 		{ | ||||
| 			ice40_dsp_pm pm(module, module->selected_cells()); | ||||
| 			pm.run([&]() | ||||
| 			{ | ||||
| 				log("\n"); | ||||
| 				log("ffA:   %s\n", log_id(pm.st.ffA, "--")); | ||||
| 				log("ffB:   %s\n", log_id(pm.st.ffB, "--")); | ||||
| 				log("mul:   %s\n", log_id(pm.st.mul, "--")); | ||||
| 				log("ffY:   %s\n", log_id(pm.st.ffY, "--")); | ||||
| 				log("addAB: %s\n", log_id(pm.st.addAB, "--")); | ||||
| 				log("muxAB: %s\n", log_id(pm.st.muxAB, "--")); | ||||
| 				log("ffS:   %s\n", log_id(pm.st.ffS, "--")); | ||||
| 
 | ||||
| 				pm.blacklist(pm.st.mul); | ||||
| 				pm.blacklist(pm.st.ffA); | ||||
| 				pm.blacklist(pm.st.ffB); | ||||
| 				pm.blacklist(pm.st.ffY); | ||||
| 				pm.blacklist(pm.st.ffS); | ||||
| 			}); | ||||
| 		} | ||||
| 			ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp); | ||||
| 	} | ||||
| } Ice40DspPass; | ||||
| 
 | ||||
|  |  | |||
|  | @ -63,7 +63,7 @@ code sigY clock clock_pol clock_vld | |||
| 	sigY = port(mul, \Y); | ||||
| 
 | ||||
| 	if (ffY) { | ||||
| 		sigY = port(ffY, \D); | ||||
| 		sigY = port(ffY, \Q); | ||||
| 		SigBit c = port(ffY, \CLK).as_bit(); | ||||
| 		bool cp = param(ffY, \CLK_POLARITY).as_bool(); | ||||
| 
 | ||||
|  | @ -77,7 +77,7 @@ code sigY clock clock_pol clock_vld | |||
| endcode | ||||
| 
 | ||||
| match addA | ||||
| 	select addA->type.in($add, $sub) | ||||
| 	select addA->type.in($add) | ||||
| 	select nusers(port(addA, \A)) == 2 | ||||
| 	index <SigSpec> port(addA, \A) === sigY | ||||
| 	optional | ||||
|  | @ -134,3 +134,17 @@ match ffS | |||
| 	index <SigSpec> port(ffS, \D) === port(muxAB, \Y) | ||||
| 	index <SigSpec> port(ffS, \Q) === sigS | ||||
| endmatch | ||||
| 
 | ||||
| code clock clock_pol clock_vld | ||||
| 	if (ffS) { | ||||
| 		SigBit c = port(ffS, \CLK).as_bit(); | ||||
| 		bool cp = param(ffS, \CLK_POLARITY).as_bool(); | ||||
| 
 | ||||
| 		if (clock_vld && (c != clock || cp != clock_pol)) | ||||
| 			reject; | ||||
| 
 | ||||
| 		clock = c; | ||||
| 		clock_pol = cp; | ||||
| 		clock_vld = true; | ||||
| 	} | ||||
| endcode | ||||
|  |  | |||
|  | @ -203,6 +203,7 @@ with open("%s_pm.h" % prefix, "w") as f: | |||
|             print("  dict<index_{}_key_type, vector<Cell*>> index_{};".format(index, index), file=f) | ||||
|     print("  dict<SigBit, pool<Cell*>> sigusers;", file=f) | ||||
|     print("  pool<Cell*> blacklist_cells;", file=f) | ||||
|     print("  pool<Cell*> autoremove_cells;", file=f) | ||||
|     print("  bool blacklist_dirty;", file=f) | ||||
|     print("  int rollback;", file=f) | ||||
|     print("", file=f) | ||||
|  | @ -244,6 +245,15 @@ with open("%s_pm.h" % prefix, "w") as f: | |||
|     print("  }", file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("  void autoremove(Cell *cell) {", file=f) | ||||
|     print("    if (cell != nullptr) {", file=f) | ||||
|     print("      if (blacklist_cells.insert(cell).second)", file=f) | ||||
|     print("        blacklist_dirty = true;", file=f) | ||||
|     print("      autoremove_cells.insert(cell);", file=f) | ||||
|     print("    }", file=f) | ||||
|     print("  }", file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("  void check_blacklist() {", file=f) | ||||
|     print("    if (!blacklist_dirty)", file=f) | ||||
|     print("      return;", file=f) | ||||
|  | @ -308,7 +318,13 @@ with open("%s_pm.h" % prefix, "w") as f: | |||
|     print("  }", file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("  void run(std::function<void()> on_accept_f) {{".format(prefix), file=f) | ||||
|     print("  ~{}_pm() {{".format(prefix), file=f) | ||||
|     print("    for (auto cell : autoremove_cells)", file=f) | ||||
|     print("      module->remove(cell);", file=f) | ||||
|     print("  }", file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("  void run(std::function<void()> on_accept_f) {", file=f) | ||||
|     print("    on_accept = on_accept_f;", file=f) | ||||
|     print("    rollback = 0;", file=f) | ||||
|     print("    blacklist_dirty = false;", file=f) | ||||
|  | @ -321,6 +337,11 @@ with open("%s_pm.h" % prefix, "w") as f: | |||
|     print("  }", file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("  void run(std::function<void({}_pm&)> on_accept_f) {{".format(prefix), file=f) | ||||
|     print("    run([&](){on_accept_f(*this);});", file=f) | ||||
|     print("  }", file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     for index in range(len(blocks)): | ||||
|         block = blocks[index] | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue