mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 11:42:30 +00:00 
			
		
		
		
	Merge remote-tracking branch 'origin/master' into xaig
This commit is contained in:
		
						commit
						a8803a1519
					
				
					 16 changed files with 1874 additions and 63 deletions
				
			
		|  | @ -2410,6 +2410,9 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed) | |||
| 	if (connections_.count("\\Y")) | ||||
| 		parameters["\\Y_WIDTH"] = GetSize(connections_["\\Y"]); | ||||
| 
 | ||||
| 	if (connections_.count("\\Q")) | ||||
| 		parameters["\\WIDTH"] = GetSize(connections_["\\Q"]); | ||||
| 
 | ||||
| 	check(); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -143,6 +143,9 @@ struct SetundefPass : public Pass { | |||
| 		log("    -init\n"); | ||||
| 		log("        also create/update init values for flip-flops\n"); | ||||
| 		log("\n"); | ||||
| 		log("    -params\n"); | ||||
| 		log("        replace undef in cell parameters\n"); | ||||
| 		log("\n"); | ||||
| 	} | ||||
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||
| 	{ | ||||
|  | @ -150,6 +153,7 @@ struct SetundefPass : public Pass { | |||
| 		bool undriven_mode = false; | ||||
| 		bool expose_mode = false; | ||||
| 		bool init_mode = false; | ||||
| 		bool params_mode = false; | ||||
| 		SetundefWorker worker; | ||||
| 
 | ||||
| 		log_header(design, "Executing SETUNDEF pass (replace undef values with defined constants).\n"); | ||||
|  | @ -199,6 +203,10 @@ struct SetundefPass : public Pass { | |||
| 				init_mode = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (args[argidx] == "-params") { | ||||
| 				params_mode = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) { | ||||
| 				got_value = true; | ||||
| 				worker.next_bit_mode = MODE_RANDOM; | ||||
|  | @ -228,6 +236,18 @@ struct SetundefPass : public Pass { | |||
| 
 | ||||
| 		for (auto module : design->selected_modules()) | ||||
| 		{ | ||||
| 			if (params_mode) | ||||
| 			{ | ||||
| 				for (auto *cell : module->selected_cells()) { | ||||
| 					for (auto ¶meter : cell->parameters) { | ||||
| 						for (auto &bit : parameter.second.bits) { | ||||
| 							if (bit > RTLIL::State::S1) | ||||
| 								bit = worker.next_bit(); | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			if (undriven_mode) | ||||
| 			{ | ||||
| 				if (!module->processes.empty()) | ||||
|  |  | |||
|  | @ -38,7 +38,8 @@ struct WreduceConfig | |||
| 			"$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", | ||||
| 			"$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt", | ||||
| 			"$add", "$sub", "$mul", // "$div", "$mod", "$pow",
 | ||||
| 			"$mux", "$pmux" | ||||
| 			"$mux", "$pmux", | ||||
| 			"$dff", "$adff" | ||||
| 		}); | ||||
| 	} | ||||
| }; | ||||
|  | @ -134,6 +135,71 @@ struct WreduceWorker | |||
| 		module->connect(sig_y.extract(n_kept, n_removed), sig_removed); | ||||
| 	} | ||||
| 
 | ||||
| 	void run_cell_dff(Cell *cell) | ||||
| 	{ | ||||
| 		// Reduce size of FF if inputs are just sign/zero extended or output bit is not used
 | ||||
| 
 | ||||
| 		SigSpec sig_d = mi.sigmap(cell->getPort("\\D")); | ||||
| 		SigSpec sig_q = mi.sigmap(cell->getPort("\\Q")); | ||||
| 
 | ||||
| 		int width_before = GetSize(sig_q); | ||||
| 
 | ||||
| 		if (width_before == 0) | ||||
| 			return; | ||||
| 
 | ||||
| 		bool zero_ext = sig_d[GetSize(sig_d)-1] == State::S0; | ||||
| 		bool sign_ext = !zero_ext; | ||||
| 
 | ||||
| 		for (int i = GetSize(sig_q)-1; i >= 0; i--) | ||||
| 		{ | ||||
| 			if (zero_ext && sig_d[i] == State::S0) { | ||||
| 				module->connect(sig_q[i], State::S0); | ||||
| 				sig_d.remove(i); | ||||
| 				sig_q.remove(i); | ||||
| 				continue; | ||||
| 			} | ||||
| 
 | ||||
| 			if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1]) { | ||||
| 				module->connect(sig_q[i], sig_q[i-1]); | ||||
| 				sig_d.remove(i); | ||||
| 				sig_q.remove(i); | ||||
| 				continue; | ||||
| 			} | ||||
| 
 | ||||
| 			auto info = mi.query(sig_q[i]); | ||||
| 			if (!info->is_output && GetSize(info->ports) <= 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) { | ||||
| 				sig_d.remove(i); | ||||
| 				sig_q.remove(i); | ||||
| 				zero_ext = false; | ||||
| 				sign_ext = false; | ||||
| 				continue; | ||||
| 			} | ||||
| 
 | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		if (width_before == GetSize(sig_q)) | ||||
| 			return; | ||||
| 
 | ||||
| 		if (GetSize(sig_q) == 0) { | ||||
| 			log("Removed cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type)); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		log("Removed top %d bits (of %d) from mux cell %s.%s (%s).\n", width_before - GetSize(sig_q), width_before, | ||||
| 				log_id(module), log_id(cell), log_id(cell->type)); | ||||
| 
 | ||||
| 		for (auto bit : sig_d) | ||||
| 			work_queue_bits.insert(bit); | ||||
| 
 | ||||
| 		for (auto bit : sig_q) | ||||
| 			work_queue_bits.insert(bit); | ||||
| 
 | ||||
| 		cell->setPort("\\D", sig_d); | ||||
| 		cell->setPort("\\Q", sig_q); | ||||
| 		cell->fixup_parameters(); | ||||
| 	} | ||||
| 
 | ||||
| 	void run_reduce_inport(Cell *cell, char port, int max_port_size, bool &port_signed, bool &did_something) | ||||
| 	{ | ||||
| 		port_signed = cell->getParam(stringf("\\%c_SIGNED", port)).as_bool(); | ||||
|  | @ -176,6 +242,9 @@ struct WreduceWorker | |||
| 		if (cell->type.in("$mux", "$pmux")) | ||||
| 			return run_cell_mux(cell); | ||||
| 
 | ||||
| 		if (cell->type.in("$dff", "$adff")) | ||||
| 			return run_cell_dff(cell); | ||||
| 
 | ||||
| 		SigSpec sig = mi.sigmap(cell->getPort("\\Y")); | ||||
| 
 | ||||
| 		if (sig.has_const()) | ||||
|  |  | |||
							
								
								
									
										1
									
								
								passes/pmgen/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								passes/pmgen/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | |||
| /ice40_dsp_pm.h | ||||
							
								
								
									
										8
									
								
								passes/pmgen/Makefile.inc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								passes/pmgen/Makefile.inc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | |||
| OBJS += passes/pmgen/ice40_dsp.o | ||||
| 
 | ||||
| passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h | ||||
| EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h | ||||
| .SECONDARY: passes/pmgen/ice40_dsp_pm.h | ||||
| 
 | ||||
| passes/pmgen/ice40_dsp_pm.h: passes/pmgen/ice40_dsp.pmg passes/pmgen/pmgen.py | ||||
| 	$(P) cd passes/pmgen && python3 pmgen.py ice40_dsp | ||||
							
								
								
									
										224
									
								
								passes/pmgen/README.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								passes/pmgen/README.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,224 @@ | |||
| Pattern Matcher Generator | ||||
| ========================= | ||||
| 
 | ||||
| The program `pmgen.py` reads a `.pmg` (Pattern Matcher Generator) file and | ||||
| writes a header-only C++ library that implements that pattern matcher. | ||||
| 
 | ||||
| The "patterns" in this context are subgraphs in a Yosys RTLIL netlist. | ||||
| 
 | ||||
| The algorithm used in the generated pattern matcher is a simple recursive | ||||
| search with backtracking. It is left to the author of the `.pmg` file to | ||||
| determine an efficient cell order for the search that allows for maximum | ||||
| use of indices and early backtracking. | ||||
| 
 | ||||
| 
 | ||||
| API of Generated Matcher | ||||
| ======================== | ||||
| 
 | ||||
| When `pmgen.py` reads a `foobar.pmg` file, it writes `foobar_pm.h` containing | ||||
| a class `foobar_pm`. That class is instanciated with an RTLIL module and a | ||||
| list of cells from that module: | ||||
| 
 | ||||
|     foobar_pm pm(module, module->selected_cells()); | ||||
| 
 | ||||
| The caller must make sure that none of the cells in the 2nd argument are | ||||
| deleted for as long as the patter matcher instance is used. | ||||
| 
 | ||||
| At any time it is possible to disable cells, preventing them from showing | ||||
| up in any future matches: | ||||
| 
 | ||||
|     pm.blacklist(some_cell); | ||||
| 
 | ||||
| The `.run(callback_function)` method searches for all matches and calls the | ||||
| callback function for each found match: | ||||
| 
 | ||||
|     pm.run([&](){ | ||||
|         log("found matching 'foo' cell: %s\n", log_id(pm.st.foo)); | ||||
|         log("          with 'bar' cell: %s\n", log_id(pm.st.bar)); | ||||
|     }); | ||||
| 
 | ||||
| The `.pmg` file declares matcher state variables that are accessible via the | ||||
| `.st.<state_name>` members. (The `.st` member is of type `foobar_pm::state_t`.) | ||||
| 
 | ||||
| Similarly the `.pmg` file declares user data variables that become members of | ||||
| `.ud`, a struct of type `foobar_pm::udata_t`. | ||||
| 
 | ||||
| 
 | ||||
| The .pmg File Format | ||||
| ==================== | ||||
| 
 | ||||
| The `.pmg` file format is a simple line-based file format. For the most part | ||||
| lines consist of whitespace-separated tokens. | ||||
| 
 | ||||
| Lines in `.pmg` files starting with `//` are comments. | ||||
| 
 | ||||
| Declaring state variables | ||||
| ------------------------- | ||||
| 
 | ||||
| One or more state variables can be declared using the `state` statement, | ||||
| followed by a C++ type in angle brackets, followed by a whitespace separated | ||||
| list of variable names. For example: | ||||
| 
 | ||||
|     state <bool> flag1 flag2 happy big | ||||
|     state <SigSpec> sigA sigB sigY | ||||
| 
 | ||||
| State variables are automatically managed by the generated backtracking algorithm | ||||
| and saved and restored as needed. | ||||
| 
 | ||||
| They are automatically initialized to the default constructed value of their type | ||||
| when `.run(callback_function)` is called. | ||||
| 
 | ||||
| Declaring udata variables | ||||
| ------------------------- | ||||
| 
 | ||||
| Udata (user-data) variables can be used for example to configure the matcher or | ||||
| the callback function used to perform actions on found matches. | ||||
| 
 | ||||
| There is no automatic management of udata variables. For this reason it is | ||||
| recommended that the user-supplied matcher code treats them as read-only | ||||
| variables. | ||||
| 
 | ||||
| They are declared like state variables, just using the `udata` statement: | ||||
| 
 | ||||
|     udata <int> min_data_width max_data_width | ||||
|     udata <IdString> data_port_name | ||||
| 
 | ||||
| They are atomatically initialzed to the default constructed value of their type | ||||
| when ther pattern matcher object is constructed. | ||||
| 
 | ||||
| Embedded C++ code | ||||
| ----------------- | ||||
| 
 | ||||
| Many statements in a `.pmg` file contain C++ code. However, there are some | ||||
| slight additions to regular C++/Yosys/RTLIL code that make it a bit easier to | ||||
| write matchers: | ||||
| 
 | ||||
| - Identifiers starting with a dollar sign or backslash are automatically | ||||
|   converted to special IdString variables that are initialized when the | ||||
|   matcher object is constructed. | ||||
| 
 | ||||
| - The `port(<cell>, <portname>)` function is a handy alias for | ||||
|   `sigmap(<cell>->getPort(<portname>))`. | ||||
| 
 | ||||
| - Similarly `param(<cell>, <paramname>)` looks up a parameter on a cell. | ||||
| 
 | ||||
| - The function `nusers(<sigspec>)` returns the number of different cells | ||||
|   connected to any of the given signal bits, plus one if any of the signal | ||||
|   bits is also a primary input or primary output. | ||||
| 
 | ||||
| - In `code..endcode` blocks there exist `accept`, `reject`, and `branch` | ||||
|   statements. | ||||
| 
 | ||||
| - In `index` statements there is a special `===` operator for the index | ||||
|   lookup. | ||||
| 
 | ||||
| Matching cells | ||||
| -------------- | ||||
| 
 | ||||
| Cells are matched using `match..endmatch` blocks. For example: | ||||
| 
 | ||||
|     match mul | ||||
|         if ff | ||||
|         select mul->type == $mul | ||||
|         select nusers(port(mul, \Y) == 2 | ||||
|         index <SigSpec> port(mul, \Y) === port(ff, \D) | ||||
|         filter some_weird_function(mul) < other_weird_function(ff) | ||||
|         optional | ||||
|     endmatch | ||||
| 
 | ||||
| A `match` block starts with `match <statevar>` and implicitly generates | ||||
| a state variable `<statevar>` of type `RTLIL::Cell*`. | ||||
| 
 | ||||
| All statements in the match block are optional. (An empty match block | ||||
| would simply match each and every cell in the module.) | ||||
| 
 | ||||
| The `if <expression>` statement makes the match block conditional. If | ||||
| `<expression>` evaluates to `false` then the match block will be ignored | ||||
| and the corresponding state variable is set to `nullptr`. In our example | ||||
| we only try to match the `mul` cell if the `ff` state variable points | ||||
| to a cell. (Presumably `ff` is provided by a prior `match` block.) | ||||
| 
 | ||||
| The `select` lines are evaluated once for each cell when the matcher is | ||||
| initialized. A `match` block will only consider cells for which all `select` | ||||
| expressions evaluated to `true`. Note that the state variable corresponding to | ||||
| the match (in the example `mul`) is the only state variable that may be used | ||||
| `select` lines. | ||||
| 
 | ||||
| Index lines are using the `index <type> expr1 === expr2` syntax.  `expr1` is | ||||
| evaluated during matcher initialization and the same restrictions apply as for | ||||
| `select` expressions. `expr2` is evaluated when the match is calulated. It is a | ||||
| function of any state variables assigned to by previous blocks. Both expression | ||||
| are converted to the given type and compared for equality. Only cells for which | ||||
| all `index` statements in the block pass are considered by the match. | ||||
| 
 | ||||
| Note that `select` and `index` are fast operations. Thus `select` and `index` | ||||
| should be used whenever possible to create efficient matchers. | ||||
| 
 | ||||
| Finally, `filter <expression>` narrows down the remaining list of cells. For | ||||
| performance reasons `filter` statements should only be used for things that | ||||
| can't be done using `select` and `index`. | ||||
| 
 | ||||
| The `optional` statement marks optional matches. I.e. the matcher will also | ||||
| explore the case where `mul` is set to `nullptr`. Without the `optional` | ||||
| statement a match may only be assigned nullptr when one of the `if` expressions | ||||
| evaluates to `false`. | ||||
| 
 | ||||
| Additional code | ||||
| --------------- | ||||
| 
 | ||||
| Interleaved with `match..endmatch` blocks there may be `code..endcode` blocks. | ||||
| Such a block starts with the keyword `code` followed by a list of state variables | ||||
| that the block may modify. For example: | ||||
| 
 | ||||
|     code addAB sigS | ||||
|         if (addA) { | ||||
|             addAB = addA; | ||||
|             sigS = port(addA, \B); | ||||
|         } | ||||
|         if (addB) { | ||||
|             addAB = addB; | ||||
|             sigS = port(addB, \A); | ||||
|         } | ||||
|     endcode | ||||
| 
 | ||||
| The special keyword `reject` can be used to reject the current state and | ||||
| backtrack. For example: | ||||
| 
 | ||||
|     code | ||||
|         if (ffA && ffB) { | ||||
|             if (port(ffA, \CLK) != port(ffB, \CLK)) | ||||
|                 reject; | ||||
|             if (param(ffA, \CLK_POLARITY) != param(ffB, \CLK_POLARITY)) | ||||
|                 reject; | ||||
|         } | ||||
|     endcode | ||||
| 
 | ||||
| Similarly, the special keyword `accept` can be used to accept the current | ||||
| state. (`accept` will not backtrack. This means it continues with the current | ||||
| branch and may accept a larger match later.) | ||||
| 
 | ||||
| The special keyword `branch` can be used to explore different cases. Note that | ||||
| each code block has an implicit `branch` at the end. So most use-cases of the | ||||
| `branch` keyword need to end the block with `reject` to avoid the implicit | ||||
| branch at the end. For example: | ||||
| 
 | ||||
|     state <int> mode | ||||
| 
 | ||||
|     code mode | ||||
|         for (mode = 0; mode < 8; mode++) | ||||
|             branch; | ||||
|         reject; | ||||
|     endcode | ||||
| 
 | ||||
| But in some cases it is more natural to utilize the implicit branch statement: | ||||
| 
 | ||||
|     state <IdString> portAB | ||||
| 
 | ||||
|     code portAB | ||||
|         portAB = \A; | ||||
|         branch; | ||||
|         portAB = \B; | ||||
|     endcode | ||||
| 
 | ||||
| There is an implicit `code..endcode` block at the end of each `.pgm` file | ||||
| that just accepts everything that gets all the way there. | ||||
							
								
								
									
										237
									
								
								passes/pmgen/ice40_dsp.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								passes/pmgen/ice40_dsp.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,237 @@ | |||
| /*
 | ||||
|  *  yosys -- Yosys Open SYnthesis Suite | ||||
|  * | ||||
|  *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> | ||||
|  * | ||||
|  *  Permission to use, copy, modify, and/or distribute this software for any | ||||
|  *  purpose with or without fee is hereby granted, provided that the above | ||||
|  *  copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
|  *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||
|  *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||
|  *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "kernel/yosys.h" | ||||
| #include "kernel/sigtools.h" | ||||
| #include "passes/pmgen/ice40_dsp_pm.h" | ||||
| 
 | ||||
| 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; | ||||
| 	} | ||||
| 
 | ||||
| 	bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool(); | ||||
| 
 | ||||
| 	if (mul_signed) { | ||||
| 		log("  inference of signed iCE40 DSP arithmetic is currently not supported.\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	log("  replacing $mul with SB_MAC16 cell.\n"); | ||||
| 
 | ||||
| 	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::S1 : State::S0); | ||||
| 	cell->setParam("\\B_REG", pm.st.ffB ? State::S1 : State::S0); | ||||
| 
 | ||||
| 	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("\\ORSTTOP", State::S0); | ||||
| 	cell->setPort("\\ORSTBOT", 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 | ||||
| 	{ | ||||
| 		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | ||||
| 		log("\n"); | ||||
| 		log("    ice40_dsp [options] [selection]\n"); | ||||
| 		log("\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 | ||||
| 	{ | ||||
| 		log_header(design, "Executing ICE40_DSP pass (map multipliers).\n"); | ||||
| 
 | ||||
| 		size_t argidx; | ||||
| 		for (argidx = 1; argidx < args.size(); argidx++) | ||||
| 		{ | ||||
| 			// if (args[argidx] == "-singleton") {
 | ||||
| 			// 	singleton_mode = true;
 | ||||
| 			// 	continue;
 | ||||
| 			// }
 | ||||
| 			break; | ||||
| 		} | ||||
| 		extra_args(args, argidx, design); | ||||
| 
 | ||||
| 		for (auto module : design->selected_modules()) | ||||
| 			ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp); | ||||
| 	} | ||||
| } Ice40DspPass; | ||||
| 
 | ||||
| PRIVATE_NAMESPACE_END | ||||
							
								
								
									
										160
									
								
								passes/pmgen/ice40_dsp.pmg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								passes/pmgen/ice40_dsp.pmg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,160 @@ | |||
| state <SigBit> clock | ||||
| state <bool> clock_pol clock_vld | ||||
| state <SigSpec> sigA sigB sigY sigS | ||||
| state <Cell*> addAB muxAB | ||||
| 
 | ||||
| match mul | ||||
| 	select mul->type.in($mul) | ||||
| 	select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10 | ||||
| 	select GetSize(mul->getPort(\Y)) > 10 | ||||
| endmatch | ||||
| 
 | ||||
| match ffA | ||||
| 	select ffA->type.in($dff) | ||||
| 	// select nusers(port(ffA, \Q)) == 2 | ||||
| 	index <SigSpec> port(ffA, \Q) === port(mul, \A) | ||||
| 	optional | ||||
| endmatch | ||||
| 
 | ||||
| code sigA clock clock_pol clock_vld | ||||
| 	sigA = port(mul, \A); | ||||
| 
 | ||||
| 	if (ffA) { | ||||
| 		sigA = port(ffA, \D); | ||||
| 
 | ||||
| 		clock = port(ffA, \CLK).as_bit(); | ||||
| 		clock_pol = param(ffA, \CLK_POLARITY).as_bool(); | ||||
| 		clock_vld = true; | ||||
| 	} | ||||
| endcode | ||||
| 
 | ||||
| match ffB | ||||
| 	select ffB->type.in($dff) | ||||
| 	// select nusers(port(ffB, \Q)) == 2 | ||||
| 	index <SigSpec> port(ffB, \Q) === port(mul, \B) | ||||
| 	optional | ||||
| endmatch | ||||
| 
 | ||||
| code sigB clock clock_pol clock_vld | ||||
| 	sigB = port(mul, \B); | ||||
| 
 | ||||
| 	if (ffB) { | ||||
| 		sigB = port(ffB, \D); | ||||
| 		SigBit c = port(ffB, \CLK).as_bit(); | ||||
| 		bool cp = param(ffB, \CLK_POLARITY).as_bool(); | ||||
| 
 | ||||
| 		if (clock_vld && (c != clock || cp != clock_pol)) | ||||
| 			reject; | ||||
| 
 | ||||
| 		clock = c; | ||||
| 		clock_pol = cp; | ||||
| 		clock_vld = true; | ||||
| 	} | ||||
| endcode | ||||
| 
 | ||||
| match ffY | ||||
| 	select ffY->type.in($dff) | ||||
| 	select nusers(port(ffY, \D)) == 2 | ||||
| 	index <SigSpec> port(ffY, \D) === port(mul, \Y) | ||||
| 	optional | ||||
| endmatch | ||||
| 
 | ||||
| code sigY clock clock_pol clock_vld | ||||
| 	sigY = port(mul, \Y); | ||||
| 
 | ||||
| 	if (ffY) { | ||||
| 		sigY = port(ffY, \Q); | ||||
| 		SigBit c = port(ffY, \CLK).as_bit(); | ||||
| 		bool cp = param(ffY, \CLK_POLARITY).as_bool(); | ||||
| 
 | ||||
| 		if (clock_vld && (c != clock || cp != clock_pol)) | ||||
| 			reject; | ||||
| 
 | ||||
| 		clock = c; | ||||
| 		clock_pol = cp; | ||||
| 		clock_vld = true; | ||||
| 	} | ||||
| endcode | ||||
| 
 | ||||
| match addA | ||||
| 	select addA->type.in($add) | ||||
| 	select nusers(port(addA, \A)) == 2 | ||||
| 	index <SigSpec> port(addA, \A) === sigY | ||||
| 	optional | ||||
| endmatch | ||||
| 
 | ||||
| match addB | ||||
| 	if !addA | ||||
| 	select addB->type.in($add, $sub) | ||||
| 	select nusers(port(addB, \B)) == 2 | ||||
| 	index <SigSpec> port(addB, \B) === sigY | ||||
| 	optional | ||||
| endmatch | ||||
| 
 | ||||
| code addAB sigS | ||||
| 	if (addA) { | ||||
| 		addAB = addA; | ||||
| 		sigS = port(addA, \B); | ||||
| 	} | ||||
| 	if (addB) { | ||||
| 		addAB = addB; | ||||
| 		sigS = port(addB, \A); | ||||
| 	} | ||||
| 	if (addAB) { | ||||
| 		int natural_mul_width = GetSize(sigA) + GetSize(sigB); | ||||
| 		int actual_mul_width = GetSize(sigY); | ||||
| 		int actual_acc_width = GetSize(sigS); | ||||
| 
 | ||||
| 		if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width)) | ||||
| 			reject; | ||||
| 		if ((actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(addAB, \A_SIGNED).as_bool())) | ||||
| 			reject; | ||||
| 	} | ||||
| endcode | ||||
| 
 | ||||
| match muxA | ||||
| 	if addAB | ||||
| 	select muxA->type.in($mux) | ||||
| 	select nusers(port(muxA, \A)) == 2 | ||||
| 	index <SigSpec> port(muxA, \A) === port(addAB, \Y) | ||||
| 	optional | ||||
| endmatch | ||||
| 
 | ||||
| match muxB | ||||
| 	if addAB | ||||
| 	if !muxA | ||||
| 	select muxB->type.in($mux) | ||||
| 	select nusers(port(muxB, \B)) == 2 | ||||
| 	index <SigSpec> port(muxB, \B) === port(addAB, \Y) | ||||
| 	optional | ||||
| endmatch | ||||
| 
 | ||||
| code muxAB | ||||
| 	muxAB = addAB; | ||||
| 	if (muxA) | ||||
| 		muxAB = muxA; | ||||
| 	if (muxB) | ||||
| 		muxAB = muxB; | ||||
| endcode | ||||
| 
 | ||||
| match ffS | ||||
| 	if muxAB | ||||
| 	select ffS->type.in($dff) | ||||
| 	select nusers(port(ffS, \D)) == 2 | ||||
| 	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 | ||||
							
								
								
									
										482
									
								
								passes/pmgen/pmgen.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										482
									
								
								passes/pmgen/pmgen.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,482 @@ | |||
| #!/usr/bin/env python3 | ||||
| 
 | ||||
| import re | ||||
| import sys | ||||
| import pprint | ||||
| 
 | ||||
| pp = pprint.PrettyPrinter(indent=4) | ||||
| 
 | ||||
| prefix = sys.argv[1] | ||||
| 
 | ||||
| state_types = dict() | ||||
| udata_types = dict() | ||||
| blocks = list() | ||||
| ids = dict() | ||||
| 
 | ||||
| def rewrite_cpp(s): | ||||
|     t = list() | ||||
|     i = 0 | ||||
|     while i < len(s): | ||||
|         if s[i] in ("'", '"') and i + 1 < len(s): | ||||
|             j = i + 1 | ||||
|             while j + 1 < len(s) and s[j] != s[i]: | ||||
|                 if s[j] == '\\' and j + 1 < len(s): | ||||
|                     j += 1 | ||||
|                 j += 1 | ||||
|             t.append(s[i:j+1]) | ||||
|             i = j + 1 | ||||
|             continue | ||||
| 
 | ||||
|         if s[i] in ('$', '\\') and i + 1 < len(s): | ||||
|             j = i + 1 | ||||
|             while True: | ||||
|                 if j == len(s): | ||||
|                     j -= 1 | ||||
|                     break | ||||
|                 if ord('a') <= ord(s[j]) <= ord('z'): | ||||
|                     j += 1 | ||||
|                     continue | ||||
|                 if ord('A') <= ord(s[j]) <= ord('Z'): | ||||
|                     j += 1 | ||||
|                     continue | ||||
|                 if ord('0') <= ord(s[j]) <= ord('9'): | ||||
|                     j += 1 | ||||
|                     continue | ||||
|                 if s[j] == '_': | ||||
|                     j += 1 | ||||
|                     continue | ||||
|                 j -= 1 | ||||
|                 break | ||||
| 
 | ||||
|             n = s[i:j+1] | ||||
|             i = j + 1 | ||||
| 
 | ||||
|             if n[0] == '$': | ||||
|                 v = "id_d_" + n[1:] | ||||
|             else: | ||||
|                 v = "id_b_" + n[1:] | ||||
| 
 | ||||
|             if v not in ids: | ||||
|                 ids[v] = n | ||||
|             else: | ||||
|                 assert ids[v] == n | ||||
| 
 | ||||
|             t.append(v) | ||||
|             continue | ||||
| 
 | ||||
|         if s[i] == "\t": | ||||
|             t.append("  ") | ||||
|         else: | ||||
|             t.append(s[i]) | ||||
| 
 | ||||
|         i += 1 | ||||
| 
 | ||||
|     return "".join(t) | ||||
| 
 | ||||
| with open("%s.pmg" % prefix, "r") as f: | ||||
|     while True: | ||||
|         line = f.readline() | ||||
|         if line == "": break | ||||
|         line = line.strip() | ||||
| 
 | ||||
|         cmd = line.split() | ||||
|         if len(cmd) == 0 or cmd[0].startswith("//"): continue | ||||
|         cmd = cmd[0] | ||||
|          | ||||
|         if cmd == "state": | ||||
|             m = re.match(r"^state\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line) | ||||
|             assert m | ||||
|             type_str = m.group(1) | ||||
|             states_str = m.group(2) | ||||
|             for s in re.split(r"\s+", states_str): | ||||
|                 assert s not in state_types | ||||
|                 state_types[s] = type_str | ||||
|             continue | ||||
| 
 | ||||
|         if cmd == "udata": | ||||
|             m = re.match(r"^udata\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line) | ||||
|             assert m | ||||
|             type_str = m.group(1) | ||||
|             udatas_str = m.group(2) | ||||
|             for s in re.split(r"\s+", udatas_str): | ||||
|                 assert s not in udata_types | ||||
|                 udata_types[s] = type_str | ||||
|             continue | ||||
| 
 | ||||
|         if cmd == "match": | ||||
|             block = dict() | ||||
|             block["type"] = "match" | ||||
| 
 | ||||
|             line = line.split() | ||||
|             assert len(line) == 2 | ||||
|             assert line[1] not in state_types | ||||
|             block["cell"] = line[1] | ||||
|             state_types[line[1]] = "Cell*"; | ||||
| 
 | ||||
|             block["if"] = list() | ||||
|             block["select"] = list() | ||||
|             block["index"] = list() | ||||
|             block["filter"] = list() | ||||
|             block["optional"] = False | ||||
| 
 | ||||
|             while True: | ||||
|                 l = f.readline() | ||||
|                 assert l != "" | ||||
|                 a = l.split() | ||||
|                 if len(a) == 0 or a[0].startswith("//"): continue | ||||
|                 if a[0] == "endmatch": break | ||||
| 
 | ||||
|                 if a[0] == "if": | ||||
|                     b = l.lstrip()[2:] | ||||
|                     block["if"].append(rewrite_cpp(b.strip())) | ||||
|                     continue | ||||
| 
 | ||||
|                 if a[0] == "select": | ||||
|                     b = l.lstrip()[6:] | ||||
|                     block["select"].append(rewrite_cpp(b.strip())) | ||||
|                     continue | ||||
| 
 | ||||
|                 if a[0] == "index": | ||||
|                     m = re.match(r"^\s*index\s+<(.*?)>\s+(.*?)\s*===\s*(.*?)\s*$", l) | ||||
|                     assert m | ||||
|                     block["index"].append((m.group(1), rewrite_cpp(m.group(2)), rewrite_cpp(m.group(3)))) | ||||
|                     continue | ||||
| 
 | ||||
|                 if a[0] == "filter": | ||||
|                     b = l.lstrip()[6:] | ||||
|                     block["filter"].append(rewrite_cpp(b.strip())) | ||||
|                     continue | ||||
| 
 | ||||
|                 if a[0] == "optional": | ||||
|                     block["optional"] = True | ||||
|                     continue | ||||
| 
 | ||||
|                 assert False | ||||
| 
 | ||||
|             blocks.append(block) | ||||
| 
 | ||||
|         if cmd == "code": | ||||
|             block = dict() | ||||
|             block["type"] = "code" | ||||
|             block["code"] = list() | ||||
|             block["states"] = set() | ||||
| 
 | ||||
|             for s in line.split()[1:]: | ||||
|                 assert s in state_types | ||||
|                 block["states"].add(s) | ||||
| 
 | ||||
|             while True: | ||||
|                 l = f.readline() | ||||
|                 assert l != "" | ||||
|                 a = l.split() | ||||
|                 if len(a) == 0: continue | ||||
|                 if a[0] == "endcode": break | ||||
| 
 | ||||
|                 block["code"].append(rewrite_cpp(l.rstrip())) | ||||
| 
 | ||||
|             blocks.append(block) | ||||
| 
 | ||||
| with open("%s_pm.h" % prefix, "w") as f: | ||||
|     print("// Generated by pmgen.py from {}.pgm".format(prefix), file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("#include \"kernel/yosys.h\"", file=f) | ||||
|     print("#include \"kernel/sigtools.h\"", file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("YOSYS_NAMESPACE_BEGIN", file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("struct {}_pm {{".format(prefix), file=f) | ||||
|     print("  Module *module;", file=f) | ||||
|     print("  SigMap sigmap;", file=f) | ||||
|     print("  std::function<void()> on_accept;".format(prefix), file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     for index in range(len(blocks)): | ||||
|         block = blocks[index] | ||||
|         if block["type"] == "match": | ||||
|             index_types = list() | ||||
|             for entry in block["index"]: | ||||
|                 index_types.append(entry[0]) | ||||
|             print("  typedef std::tuple<{}> index_{}_key_type;".format(", ".join(index_types), index), file=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) | ||||
| 
 | ||||
|     print("  struct state_t {", file=f) | ||||
|     for s, t in sorted(state_types.items()): | ||||
|         print("    {} {};".format(t, s), file=f) | ||||
|     print("  } st;", file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("  struct udata_t {", file=f) | ||||
|     for s, t in sorted(udata_types.items()): | ||||
|         print("    {} {};".format(t, s), file=f) | ||||
|     print("  } ud;", file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     for v, n in sorted(ids.items()): | ||||
|         if n[0] == "\\": | ||||
|             print("  IdString {}{{\"\\{}\"}};".format(v, n), file=f) | ||||
|         else: | ||||
|             print("  IdString {}{{\"{}\"}};".format(v, n), file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("  void add_siguser(const SigSpec &sig, Cell *cell) {", file=f) | ||||
|     print("    for (auto bit : sigmap(sig)) {", file=f) | ||||
|     print("      if (bit.wire == nullptr) continue;", file=f) | ||||
|     print("      if (sigusers.count(bit) == 0 && bit.wire->port_id)", file=f) | ||||
|     print("        sigusers[bit].insert(nullptr);", file=f) | ||||
|     print("      sigusers[bit].insert(cell);", file=f) | ||||
|     print("    }", file=f) | ||||
|     print("  }", file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("  void blacklist(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("    }", file=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) | ||||
|     print("    blacklist_dirty = false;", file=f) | ||||
|     for index in range(len(blocks)): | ||||
|         block = blocks[index] | ||||
|         if block["type"] == "match": | ||||
|             print("    if (st.{} != nullptr && blacklist_cells.count(st.{})) {{".format(block["cell"], block["cell"]), file=f) | ||||
|             print("      rollback = {};".format(index+1), file=f) | ||||
|             print("      return;", file=f) | ||||
|             print("    }", file=f) | ||||
|     print("    rollback = 0;", file=f) | ||||
|     print("  }", file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("  SigSpec port(Cell *cell, IdString portname) {", file=f) | ||||
|     print("    return sigmap(cell->getPort(portname));", file=f) | ||||
|     print("  }", file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("  Const param(Cell *cell, IdString paramname) {", file=f) | ||||
|     print("    return cell->getParam(paramname);", file=f) | ||||
|     print("  }", file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("  int nusers(const SigSpec &sig) {", file=f) | ||||
|     print("    pool<Cell*> users;", file=f) | ||||
|     print("    for (auto bit : sigmap(sig))", file=f) | ||||
|     print("      for (auto user : sigusers[bit])", file=f) | ||||
|     print("        users.insert(user);", file=f) | ||||
|     print("    return GetSize(users);", file=f) | ||||
|     print("  }", file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("  {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f) | ||||
|     print("      module(module), sigmap(module) {", file=f) | ||||
|     for s, t in sorted(udata_types.items()): | ||||
|         if t.endswith("*"): | ||||
|             print("    ud.{} = nullptr;".format(s), file=f) | ||||
|         else: | ||||
|             print("    ud.{} = {}();".format(s, t), file=f) | ||||
|     print("    for (auto cell : module->cells()) {", file=f) | ||||
|     print("      for (auto &conn : cell->connections())", file=f) | ||||
|     print("        add_siguser(conn.second, cell);", file=f) | ||||
|     print("    }", file=f) | ||||
|     print("    for (auto cell : cells) {", file=f) | ||||
| 
 | ||||
|     for index in range(len(blocks)): | ||||
|         block = blocks[index] | ||||
|         if block["type"] == "match": | ||||
|             print("      do {", file=f) | ||||
|             print("        Cell *{} = cell;".format(block["cell"]), file=f) | ||||
|             for expr in block["select"]: | ||||
|                 print("        if (!({})) break;".format(expr), file=f) | ||||
|             print("        index_{}_key_type key;".format(index), file=f) | ||||
|             for field, entry in enumerate(block["index"]): | ||||
|                 print("        std::get<{}>(key) = {};".format(field, entry[1]), file=f) | ||||
|             print("        index_{}[key].push_back(cell);".format(index), file=f) | ||||
|             print("      } while (0);", file=f) | ||||
| 
 | ||||
|     print("    }", file=f) | ||||
|     print("  }", file=f) | ||||
|     print("", 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) | ||||
|     for s, t in sorted(state_types.items()): | ||||
|         if t.endswith("*"): | ||||
|             print("    st.{} = nullptr;".format(s), file=f) | ||||
|         else: | ||||
|             print("    st.{} = {}();".format(s, t), file=f) | ||||
|     print("    block_0();", file=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] | ||||
| 
 | ||||
|         print("  void block_{}() {{".format(index), file=f) | ||||
| 
 | ||||
|         const_st = set() | ||||
|         nonconst_st = set() | ||||
|         restore_st = set() | ||||
| 
 | ||||
|         for i in range(index): | ||||
|             if blocks[i]["type"] == "code": | ||||
|                 for s in blocks[i]["states"]: | ||||
|                     const_st.add(s) | ||||
|             elif blocks[i]["type"] == "match": | ||||
|                 const_st.add(blocks[i]["cell"]) | ||||
|             else: | ||||
|                 assert False | ||||
| 
 | ||||
|         if block["type"] == "code": | ||||
|             for s in block["states"]: | ||||
|                 if s in const_st: | ||||
|                     const_st.remove(s) | ||||
|                     restore_st.add(s) | ||||
|                 nonconst_st.add(s) | ||||
|         elif block["type"] == "match": | ||||
|             s = block["cell"] | ||||
|             assert s not in const_st | ||||
|             nonconst_st.add(s) | ||||
|         else: | ||||
|             assert False | ||||
| 
 | ||||
|         for s in sorted(const_st): | ||||
|             t = state_types[s] | ||||
|             if t.endswith("*"): | ||||
|                 print("    {} const &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) | ||||
|             else: | ||||
|                 print("    const {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) | ||||
| 
 | ||||
|         for s in sorted(nonconst_st): | ||||
|             t = state_types[s] | ||||
|             print("    {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) | ||||
| 
 | ||||
|         if len(restore_st): | ||||
|             print("", file=f) | ||||
|             for s in sorted(restore_st): | ||||
|                 t = state_types[s] | ||||
|                 print("    {} backup_{} = {};".format(t, s, s), file=f) | ||||
| 
 | ||||
|         if block["type"] == "code": | ||||
|             print("", file=f) | ||||
|             print("    do {", file=f) | ||||
|             print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f) | ||||
|             print("#define accept do { on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f) | ||||
|             print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f) | ||||
| 
 | ||||
|             for line in block["code"]: | ||||
|                 print("    " + line, file=f) | ||||
| 
 | ||||
|             print("", file=f) | ||||
|             print("      block_{}();".format(index+1), file=f) | ||||
|             print("#undef reject", file=f) | ||||
|             print("#undef accept", file=f) | ||||
|             print("#undef branch", file=f) | ||||
|             print("    } while (0);", file=f) | ||||
|             print("", file=f) | ||||
|             print("rollback_label:", file=f) | ||||
|             print("    YS_ATTRIBUTE(unused);", file=f) | ||||
| 
 | ||||
|             if len(restore_st) or len(nonconst_st): | ||||
|                 print("", file=f) | ||||
|                 for s in sorted(restore_st): | ||||
|                     t = state_types[s] | ||||
|                     print("    {} = backup_{};".format(s, s), file=f) | ||||
|                 for s in sorted(nonconst_st): | ||||
|                     if s not in restore_st: | ||||
|                         t = state_types[s] | ||||
|                         if t.endswith("*"): | ||||
|                             print("    {} = nullptr;".format(s), file=f) | ||||
|                         else: | ||||
|                             print("    {} = {}();".format(s, t), file=f) | ||||
| 
 | ||||
|         elif block["type"] == "match": | ||||
|             assert len(restore_st) == 0 | ||||
| 
 | ||||
|             if len(block["if"]): | ||||
|                 for expr in block["if"]: | ||||
|                     print("", file=f) | ||||
|                     print("    if (!({})) {{".format(expr), file=f) | ||||
|                     print("      {} = nullptr;".format(block["cell"]), file=f) | ||||
|                     print("      block_{}();".format(index+1), file=f) | ||||
|                     print("      return;", file=f) | ||||
|                     print("    }", file=f) | ||||
| 
 | ||||
|             print("", file=f) | ||||
|             print("    index_{}_key_type key;".format(index), file=f) | ||||
|             for field, entry in enumerate(block["index"]): | ||||
|                 print("    std::get<{}>(key) = {};".format(field, entry[2]), file=f) | ||||
|             print("    const vector<Cell*> &cells = index_{}[key];".format(index), file=f) | ||||
| 
 | ||||
|             print("", file=f) | ||||
|             print("    for (int idx = 0; idx < GetSize(cells); idx++) {", file=f) | ||||
|             print("      {} = cells[idx];".format(block["cell"]), file=f) | ||||
|             print("      if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f) | ||||
|             for expr in block["filter"]: | ||||
|                 print("      if (!({})) continue;".format(expr), file=f) | ||||
|             print("      block_{}();".format(index+1), file=f) | ||||
|             print("      if (rollback) {", file=f) | ||||
|             print("        if (rollback != {}) {{".format(index+1), file=f) | ||||
|             print("          {} = nullptr;".format(block["cell"]), file=f) | ||||
|             print("          return;", file=f) | ||||
|             print("        }", file=f) | ||||
|             print("        rollback = 0;", file=f) | ||||
|             print("      }", file=f) | ||||
|             print("    }", file=f) | ||||
| 
 | ||||
|             print("", file=f) | ||||
|             print("    {} = nullptr;".format(block["cell"]), file=f) | ||||
| 
 | ||||
|             if block["optional"]: | ||||
|                 print("    block_{}();".format(index+1), file=f) | ||||
| 
 | ||||
|         else: | ||||
|             assert False | ||||
| 
 | ||||
| 
 | ||||
|         print("  }", file=f) | ||||
|         print("", file=f) | ||||
| 
 | ||||
|     print("  void block_{}() {{".format(len(blocks)), file=f) | ||||
|     print("    on_accept();", file=f) | ||||
|     print("    check_blacklist();", file=f) | ||||
|     print("  }", file=f) | ||||
|     print("};", file=f) | ||||
| 
 | ||||
|     print("", file=f) | ||||
|     print("YOSYS_NAMESPACE_END", file=f) | ||||
| 
 | ||||
| # pp.pprint(blocks) | ||||
|  | @ -886,59 +886,6 @@ module SB_WARMBOOT ( | |||
| ); | ||||
| endmodule | ||||
| 
 | ||||
| // UltraPlus feature cells | ||||
| (* blackbox *) | ||||
| module SB_MAC16 ( | ||||
| 	input CLK, | ||||
| 	input CE, | ||||
| 	input [15:0] C, | ||||
| 	input [15:0] A, | ||||
| 	input [15:0] B, | ||||
| 	input [15:0] D, | ||||
| 	input AHOLD, | ||||
| 	input BHOLD, | ||||
| 	input CHOLD, | ||||
| 	input DHOLD, | ||||
| 	input IRSTTOP, | ||||
| 	input IRSTBOT, | ||||
| 	input ORSTTOP, | ||||
| 	input ORSTBOT, | ||||
| 	input OLOADTOP, | ||||
| 	input OLOADBOT, | ||||
| 	input ADDSUBTOP, | ||||
| 	input ADDSUBBOT, | ||||
| 	input OHOLDTOP, | ||||
| 	input OHOLDBOT, | ||||
| 	input CI, | ||||
| 	input ACCUMCI, | ||||
| 	input SIGNEXTIN, | ||||
| 	output [31:0] O, | ||||
| 	output CO, | ||||
| 	output ACCUMCO, | ||||
| 	output SIGNEXTOUT | ||||
| ); | ||||
| parameter NEG_TRIGGER = 1'b0; | ||||
| parameter C_REG = 1'b0; | ||||
| parameter A_REG = 1'b0; | ||||
| parameter B_REG = 1'b0; | ||||
| parameter D_REG = 1'b0; | ||||
| parameter TOP_8x8_MULT_REG = 1'b0; | ||||
| parameter BOT_8x8_MULT_REG = 1'b0; | ||||
| parameter PIPELINE_16x16_MULT_REG1 = 1'b0; | ||||
| parameter PIPELINE_16x16_MULT_REG2 = 1'b0; | ||||
| parameter TOPOUTPUT_SELECT =  2'b00; | ||||
| parameter TOPADDSUB_LOWERINPUT = 2'b00; | ||||
| parameter TOPADDSUB_UPPERINPUT = 1'b0; | ||||
| parameter TOPADDSUB_CARRYSELECT = 2'b00; | ||||
| parameter BOTOUTPUT_SELECT =  2'b00; | ||||
| parameter BOTADDSUB_LOWERINPUT = 2'b00; | ||||
| parameter BOTADDSUB_UPPERINPUT = 1'b0; | ||||
| parameter BOTADDSUB_CARRYSELECT = 2'b00; | ||||
| parameter MODE_8x8 = 1'b0; | ||||
| parameter A_SIGNED = 1'b0; | ||||
| parameter B_SIGNED = 1'b0; | ||||
| endmodule | ||||
| 
 | ||||
| module SB_SPRAM256KA ( | ||||
| 	input [13:0] ADDRESS, | ||||
| 	input [15:0] DATAIN, | ||||
|  | @ -1273,3 +1220,171 @@ module SB_IO_OD ( | |||
| 	endgenerate | ||||
| `endif | ||||
| endmodule | ||||
| 
 | ||||
| module SB_MAC16 ( | ||||
| 	input CLK, CE, | ||||
| 	input [15:0] C, A, B, D, | ||||
| 	input AHOLD, BHOLD, CHOLD, DHOLD, | ||||
| 	input IRSTTOP, IRSTBOT, | ||||
| 	input ORSTTOP, ORSTBOT, | ||||
| 	input OLOADTOP, OLOADBOT, | ||||
| 	input ADDSUBTOP, ADDSUBBOT, | ||||
| 	input OHOLDTOP, OHOLDBOT, | ||||
| 	input CI, ACCUMCI, SIGNEXTIN, | ||||
| 	output [31:0] O, | ||||
| 	output CO, ACCUMCO, SIGNEXTOUT | ||||
| ); | ||||
| 	parameter [0:0] NEG_TRIGGER = 0; | ||||
| 	parameter [0:0] C_REG = 0; | ||||
| 	parameter [0:0] A_REG = 0; | ||||
| 	parameter [0:0] B_REG = 0; | ||||
| 	parameter [0:0] D_REG = 0; | ||||
| 	parameter [0:0] TOP_8x8_MULT_REG = 0; | ||||
| 	parameter [0:0] BOT_8x8_MULT_REG = 0; | ||||
| 	parameter [0:0] PIPELINE_16x16_MULT_REG1 = 0; | ||||
| 	parameter [0:0] PIPELINE_16x16_MULT_REG2 = 0; | ||||
| 	parameter [1:0] TOPOUTPUT_SELECT = 0; | ||||
| 	parameter [1:0] TOPADDSUB_LOWERINPUT = 0; | ||||
| 	parameter [0:0] TOPADDSUB_UPPERINPUT = 0; | ||||
| 	parameter [1:0] TOPADDSUB_CARRYSELECT = 0; | ||||
| 	parameter [1:0] BOTOUTPUT_SELECT = 0; | ||||
| 	parameter [1:0] BOTADDSUB_LOWERINPUT = 0; | ||||
| 	parameter [0:0] BOTADDSUB_UPPERINPUT = 0; | ||||
| 	parameter [1:0] BOTADDSUB_CARRYSELECT = 0; | ||||
| 	parameter [0:0] MODE_8x8 = 0; | ||||
| 	parameter [0:0] A_SIGNED = 0; | ||||
| 	parameter [0:0] B_SIGNED = 0; | ||||
| 
 | ||||
| 	wire clock = CLK ^ NEG_TRIGGER; | ||||
| 
 | ||||
| 	// internal wires, compare Figure on page 133 of ICE Technology Library 3.0 and Fig 2 on page 2 of Lattice TN1295-DSP | ||||
| 	// http://www.latticesemi.com/~/media/LatticeSemi/Documents/TechnicalBriefs/SBTICETechnologyLibrary201608.pdf | ||||
| 	// https://www.latticesemi.com/-/media/LatticeSemi/Documents/ApplicationNotes/AD/DSPFunctionUsageGuideforICE40Devices.ashx | ||||
| 	wire [15:0] iA, iB, iC, iD; | ||||
| 	wire [15:0] iF, iJ, iK, iG; | ||||
| 	wire [31:0] iL, iH; | ||||
| 	wire [15:0] iW, iX, iP, iQ; | ||||
| 	wire [15:0] iY, iZ, iR, iS; | ||||
| 	wire HCI, LCI, LCO; | ||||
| 
 | ||||
| 	// Regs C and A | ||||
| 	reg [15:0] rC, rA; | ||||
| 	always @(posedge clock, posedge IRSTTOP) begin | ||||
| 		if (IRSTTOP) begin | ||||
| 			rC <= 0; | ||||
| 			rA <= 0; | ||||
| 		end else if (CE) begin | ||||
| 			if (!CHOLD) rC <= C; | ||||
| 			if (!AHOLD) rA <= A; | ||||
| 		end | ||||
| 	end | ||||
| 	assign iC = C_REG ? rC : C; | ||||
| 	assign iA = A_REG ? rA : A; | ||||
| 
 | ||||
| 	// Regs B and D | ||||
| 	reg [15:0] rB, rD; | ||||
| 	always @(posedge clock, posedge IRSTBOT) begin | ||||
| 		if (IRSTBOT) begin | ||||
| 			rB <= 0; | ||||
| 			rD <= 0; | ||||
| 		end else if (CE) begin | ||||
| 			if (!BHOLD) rB <= B; | ||||
| 			if (!DHOLD) rD <= D; | ||||
| 		end | ||||
| 	end | ||||
| 	assign iB = B_REG ? rB : B; | ||||
| 	assign iD = D_REG ? rD : D; | ||||
| 
 | ||||
| 	// Multiplier Stage | ||||
| 	wire [15:0] p_Ah_Bh, p_Al_Bh, p_Ah_Bl, p_Al_Bl; | ||||
| 	wire [15:0] Ah, Al, Bh, Bl; | ||||
| 	assign Ah = {A_SIGNED ? {8{iA[15]}} : 8'b0, iA[15: 8]}; | ||||
| 	assign Al = {A_SIGNED ? {8{iA[ 7]}} : 8'b0, iA[ 7: 0]}; | ||||
| 	assign Bh = {B_SIGNED ? {8{iB[15]}} : 8'b0, iB[15: 8]}; | ||||
| 	assign Bl = {B_SIGNED ? {8{iB[ 7]}} : 8'b0, iB[ 7: 0]}; | ||||
| 	assign p_Ah_Bh = Ah * Bh; | ||||
| 	assign p_Al_Bh = Al * Bh; | ||||
| 	assign p_Ah_Bl = Ah * Bl; | ||||
| 	assign p_Al_Bl = Al * Bl; | ||||
| 
 | ||||
| 	// Regs F and J | ||||
| 	reg [15:0] rF, rJ; | ||||
| 	always @(posedge clock, posedge IRSTTOP) begin | ||||
| 		if (IRSTTOP) begin | ||||
| 			rF <= 0; | ||||
| 			rJ <= 0; | ||||
| 		end else if (CE) begin | ||||
| 			rF <= p_Ah_Bh; | ||||
| 			if (!MODE_8x8) rJ <= p_Al_Bh; | ||||
| 		end | ||||
| 	end | ||||
| 	assign iF = TOP_8x8_MULT_REG ? rF : p_Ah_Bh; | ||||
| 	assign iJ = PIPELINE_16x16_MULT_REG1 ? rJ : p_Al_Bh; | ||||
| 
 | ||||
| 	// Regs K and G | ||||
| 	reg [15:0] rK, rG; | ||||
| 	always @(posedge clock, posedge IRSTBOT) begin | ||||
| 		if (IRSTBOT) begin | ||||
| 			rK <= 0; | ||||
| 			rG <= 0; | ||||
| 		end else if (CE) begin | ||||
| 			if (!MODE_8x8) rK <= p_Ah_Bl; | ||||
| 			rG <= p_Al_Bl; | ||||
| 		end | ||||
| 	end | ||||
| 	assign iK = PIPELINE_16x16_MULT_REG1 ? rK : p_Ah_Bl; | ||||
| 	assign iG = BOT_8x8_MULT_REG ? rG : p_Al_Bl; | ||||
| 
 | ||||
| 	// Adder Stage | ||||
| 	assign iL = iG + (iK << 8) + (iJ << 8) + (iF << 16); | ||||
| 
 | ||||
| 	// Reg H | ||||
| 	reg [31:0] rH; | ||||
| 	always @(posedge clock, posedge IRSTBOT) begin | ||||
| 		if (IRSTBOT) begin | ||||
| 			rH <= 0; | ||||
| 		end else if (CE) begin | ||||
| 			if (!MODE_8x8) rH <= iL; | ||||
| 		end | ||||
| 	end | ||||
| 	assign iH = PIPELINE_16x16_MULT_REG2 ? rH : iL; | ||||
| 
 | ||||
| 	// Hi Output Stage | ||||
| 	wire [15:0] XW, Oh; | ||||
| 	reg [15:0] rQ; | ||||
| 	assign iW = TOPADDSUB_UPPERINPUT ? iC : iQ; | ||||
| 	assign iX = (TOPADDSUB_LOWERINPUT == 0) ? iA : (TOPADDSUB_LOWERINPUT == 1) ? iF : (TOPADDSUB_LOWERINPUT == 2) ? iH[31:16] : {16{iZ[15]}}; | ||||
| 	assign {ACCUMCO, XW} = iX + (iW ^ {16{ADDSUBTOP}}) + HCI; | ||||
| 	assign CO = ACCUMCO ^ ADDSUBTOP; | ||||
| 	assign iP = OLOADTOP ? iC : XW ^ {16{ADDSUBTOP}}; | ||||
| 	always @(posedge clock, posedge ORSTTOP) begin | ||||
| 		if (ORSTTOP) begin | ||||
| 			rQ <= 0; | ||||
| 		end else if (CE) begin | ||||
| 			if (!OHOLDTOP) rQ <= iP; | ||||
| 		end | ||||
| 	end | ||||
| 	assign iQ = rQ; | ||||
| 	assign Oh = (TOPOUTPUT_SELECT == 0) ? iP : (TOPOUTPUT_SELECT == 1) ? iQ : (TOPOUTPUT_SELECT == 2) ? iF : iH[31:16]; | ||||
| 	assign HCI = (TOPADDSUB_CARRYSELECT == 0) ? 1'b0 : (TOPADDSUB_CARRYSELECT == 1) ? 1'b1 : (TOPADDSUB_CARRYSELECT == 2) ? LCO : LCO ^ ADDSUBBOT; | ||||
| 	assign SIGNEXTOUT = iX[15]; | ||||
| 
 | ||||
| 	// Lo Output Stage | ||||
| 	wire [15:0] YZ, Ol; | ||||
| 	reg [15:0] rS; | ||||
| 	assign iY = BOTADDSUB_UPPERINPUT ? iD : iS; | ||||
| 	assign iZ = (BOTADDSUB_LOWERINPUT == 0) ? iB : (BOTADDSUB_LOWERINPUT == 1) ? iG : (BOTADDSUB_LOWERINPUT == 2) ? iH[15:0] : {16{SIGNEXTIN}}; | ||||
| 	assign {LCO, YZ} = iZ + (iY ^ {16{ADDSUBBOT}}) + LCI; | ||||
| 	assign iR = OLOADBOT ? iD : YZ ^ {16{ADDSUBBOT}}; | ||||
| 	always @(posedge clock, posedge ORSTBOT) begin | ||||
| 		if (ORSTBOT) begin | ||||
| 			rS <= 0; | ||||
| 		end else if (CE) begin | ||||
| 			if (!OHOLDBOT) rS <= iR; | ||||
| 		end | ||||
| 	end | ||||
| 	assign iS = rS; | ||||
| 	assign Ol = (BOTOUTPUT_SELECT == 0) ? iR : (BOTOUTPUT_SELECT == 1) ? iS : (BOTOUTPUT_SELECT == 2) ? iG : iH[15:0]; | ||||
| 	assign LCI = (BOTADDSUB_CARRYSELECT == 0) ? 1'b0 : (BOTADDSUB_CARRYSELECT == 1) ? 1'b1 : (BOTADDSUB_CARRYSELECT == 2) ? ACCUMCI : CI; | ||||
| 	assign O = {Oh, Ol}; | ||||
| endmodule | ||||
|  |  | |||
|  | @ -79,6 +79,9 @@ struct SynthIce40Pass : public ScriptPass | |||
| 		log("    -nobram\n"); | ||||
| 		log("        do not use SB_RAM40_4K* cells in output netlist\n"); | ||||
| 		log("\n"); | ||||
| 		log("    -dsp\n"); | ||||
| 		log("        use iCE40 UltraPlus DSP cells for large arithmetic\n"); | ||||
| 		log("\n"); | ||||
| 		log("    -noabc\n"); | ||||
| 		log("        use built-in Yosys LUT techmapping instead of abc\n"); | ||||
| 		log("\n"); | ||||
|  | @ -98,8 +101,9 @@ struct SynthIce40Pass : public ScriptPass | |||
| 		log("\n"); | ||||
| 	} | ||||
| 
 | ||||
| 
 | ||||
| 	string top_opt, blif_file, edif_file, json_file, abc; | ||||
| 	bool nocarry, nodffe, nobram, flatten, retime, relut, noabc, abc2, vpr; | ||||
| 	bool nocarry, nodffe, nobram, dsp, flatten, retime, relut, noabc, abc2, vpr; | ||||
| 	int min_ce_use; | ||||
| 
 | ||||
| 	void clear_flags() YS_OVERRIDE | ||||
|  | @ -112,6 +116,7 @@ struct SynthIce40Pass : public ScriptPass | |||
| 		nodffe = false; | ||||
| 		min_ce_use = -1; | ||||
| 		nobram = false; | ||||
| 		dsp = false; | ||||
| 		flatten = true; | ||||
| 		retime = false; | ||||
| 		relut = false; | ||||
|  | @ -185,6 +190,10 @@ struct SynthIce40Pass : public ScriptPass | |||
| 				nobram = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (args[argidx] == "-dsp") { | ||||
| 				dsp = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (args[argidx] == "-noabc") { | ||||
| 				noabc = true; | ||||
| 				continue; | ||||
|  | @ -222,11 +231,11 @@ struct SynthIce40Pass : public ScriptPass | |||
| 		{ | ||||
| 			run("read_verilog -lib +/ice40/cells_sim.v"); | ||||
| 			run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str())); | ||||
| 			run("proc"); | ||||
| 		} | ||||
| 
 | ||||
| 		if (flatten && check_label("flatten", "(unless -noflatten)")) | ||||
| 		{ | ||||
| 			run("proc"); | ||||
| 			run("flatten"); | ||||
| 			run("tribuf -logic"); | ||||
| 			run("deminout"); | ||||
|  | @ -234,7 +243,23 @@ struct SynthIce40Pass : public ScriptPass | |||
| 
 | ||||
| 		if (check_label("coarse")) | ||||
| 		{ | ||||
| 			run("synth -lut 4 -run coarse"); | ||||
| 			run("opt_expr"); | ||||
| 			run("opt_clean"); | ||||
| 			run("check"); | ||||
| 			run("opt"); | ||||
| 			run("wreduce"); | ||||
| 			run("share"); | ||||
| 			run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4"); | ||||
| 			run("opt_expr"); | ||||
| 			run("opt_clean"); | ||||
| 			if (help_mode || dsp) | ||||
| 				run("ice40_dsp", "(if -dsp)"); | ||||
| 			run("alumacc"); | ||||
| 			run("opt"); | ||||
| 			run("fsm"); | ||||
| 			run("opt -fast"); | ||||
| 			run("memory -nomap"); | ||||
| 			run("opt_clean"); | ||||
| 		} | ||||
| 
 | ||||
| 		if (!nobram && check_label("bram", "(skip if -nobram)")) | ||||
|  |  | |||
							
								
								
									
										13
									
								
								techlibs/ice40/tests/.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								techlibs/ice40/tests/.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -1,2 +1,11 @@ | |||
| test_ffs_[01][01][01][01][01]_* | ||||
| test_bram_[0-9]* | ||||
| /test_ffs_[01][01][01][01][01]_* | ||||
| /test_bram_[0-9]* | ||||
| /test_dsp_model | ||||
| /test_dsp_model.vcd | ||||
| /test_dsp_model_ref.v | ||||
| /test_dsp_model_uut.v | ||||
| /test_dsp_map | ||||
| /test_dsp_map.vcd | ||||
| /test_dsp_map_tb.v | ||||
| /test_dsp_map_top.v | ||||
| /test_dsp_map_syn.v | ||||
|  |  | |||
							
								
								
									
										107
									
								
								techlibs/ice40/tests/test_dsp_map.sh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								techlibs/ice40/tests/test_dsp_map.sh
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,107 @@ | |||
| #!/bin/bash | ||||
| set -ex | ||||
| 
 | ||||
| for iter in {1..100} | ||||
| do | ||||
| 	SZA=$(( 3 + $RANDOM % 13 )) | ||||
| 	SZB=$(( 3 + $RANDOM % 13 )) | ||||
| 	SZO=$(( 3 + $RANDOM % 29 )) | ||||
| 
 | ||||
| 	C0=clk$(( $RANDOM & 1)) | ||||
| 	C1=clk$(( $RANDOM & 1)) | ||||
| 	C2=clk$(( $RANDOM & 1)) | ||||
| 	C3=clk$(( $RANDOM & 1)) | ||||
| 
 | ||||
| 	E0=$( test $(( $RANDOM & 1 )) -eq 0 && echo posedge || echo negedge ) | ||||
| 	E1=$( test $(( $RANDOM & 1 )) -eq 0 && echo posedge || echo negedge ) | ||||
| 	E2=$( test $(( $RANDOM & 1 )) -eq 0 && echo posedge || echo negedge ) | ||||
| 	E3=$( test $(( $RANDOM & 1 )) -eq 0 && echo posedge || echo negedge ) | ||||
| 
 | ||||
| 	SP=$( test $(( $RANDOM & 1 )) -eq 0 && echo S || echo P ) | ||||
| 
 | ||||
| 	RC=$( test $(( $RANDOM & 1 )) -eq 0 && echo "reset" || echo "!reset" ) | ||||
| 	RV="32'h$( echo $RANDOM | md5sum | cut -c1-8 )" | ||||
| 
 | ||||
| 	cat > test_dsp_map_top.v << EOT | ||||
| module top ( | ||||
| 	input clk0, clk1, reset, | ||||
| 	input  [$SZA:0] A, | ||||
| 	input  [$SZB:0] B, | ||||
| 	output [$SZO:0] O | ||||
| ); | ||||
| 	reg [15:0] AA, BB; | ||||
| 	reg [31:0] P, S; | ||||
| 
 | ||||
| 	always @($E0 $C0) AA <= A; | ||||
| 	always @($E1 $C1) BB <= B; | ||||
| 	always @($E2 $C2) P <= AA * BB; | ||||
| 	always @($E3 $C3) S <= $RC ? $RV : S + P; | ||||
| 	assign O = $SP; | ||||
| endmodule | ||||
| EOT | ||||
| 
 | ||||
| 	cat > test_dsp_map_tb.v << EOT | ||||
| \`timescale 1ns / 1ps | ||||
| module testbench; | ||||
| 	reg clk1, clk0, reset; | ||||
| 	reg [$SZA:0] A; | ||||
| 	reg [$SZB:0] B; | ||||
| 
 | ||||
| 	wire [$SZO:0] O_top, O_syn; | ||||
| 
 | ||||
| 	top top_inst (.clk0(clk0), .clk1(clk1), .reset(reset), .A(A), .B(B), .O(O_top)); | ||||
| 	syn syn_inst (.clk0(clk0), .clk1(clk1), .reset(reset), .A(A), .B(B), .O(O_syn)); | ||||
| 
 | ||||
| 	initial begin | ||||
| 		// \$dumpfile("test_dsp_map.vcd"); | ||||
| 		// \$dumpvars(0, testbench); | ||||
| 
 | ||||
| 		#2; | ||||
| 		clk0 = 0; | ||||
| 		clk1 = 0; | ||||
| 		reset = 1; | ||||
| 		reset = $RC; | ||||
| 		A = 0; | ||||
| 		B = 0; | ||||
| 
 | ||||
| 		repeat (3) begin | ||||
| 			#2; clk0 = ~clk0; | ||||
| 			#2; clk0 = ~clk0; | ||||
| 			#2; clk1 = ~clk1; | ||||
| 			#2; clk1 = ~clk1; | ||||
| 		end | ||||
| 
 | ||||
| 		repeat (100) begin | ||||
| 			#2; | ||||
| 			A = \$urandom; | ||||
| 			B = \$urandom; | ||||
| 			reset = \$urandom & \$urandom & \$urandom & \$urandom; | ||||
| 			if (\$urandom & 1) begin | ||||
| 				#2; clk0 = ~clk0; | ||||
| 				#2; clk0 = ~clk0; | ||||
| 			end else begin | ||||
| 				#2; clk1 = ~clk1; | ||||
| 				#2; clk1 = ~clk1; | ||||
| 			end | ||||
| 			#2; | ||||
| 			if (O_top !== O_syn) begin | ||||
| 				\$display("ERROR: O_top=%b O_syn=%b", O_top, O_syn); | ||||
| 				\$stop; | ||||
| 			end | ||||
| 			// \$display("OK O_top=O_syn=%b", O_top); | ||||
| 		end | ||||
| 
 | ||||
| 		\$display("Test passed."); | ||||
| 		\$finish; | ||||
| 	end | ||||
| endmodule | ||||
| EOT | ||||
| 
 | ||||
| 	../../../yosys -p 'read_verilog test_dsp_map_top.v; synth_ice40 -dsp; rename top syn; write_verilog test_dsp_map_syn.v' | ||||
| 	iverilog -o test_dsp_map -s testbench test_dsp_map_tb.v test_dsp_map_top.v test_dsp_map_syn.v ../cells_sim.v | ||||
| 	vvp -N test_dsp_map | ||||
| done | ||||
| 
 | ||||
| : "" | ||||
| : "####  All tests passed.  ####" | ||||
| : "" | ||||
							
								
								
									
										11
									
								
								techlibs/ice40/tests/test_dsp_model.sh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								techlibs/ice40/tests/test_dsp_model.sh
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| #!/bin/bash | ||||
| set -ex | ||||
| sed 's/SB_MAC16/SB_MAC16_UUT/; /SB_MAC16_UUT/,/endmodule/ p; d;' < ../cells_sim.v > test_dsp_model_uut.v | ||||
| cat /opt/lscc/iCEcube2.2017.01/verilog/sb_ice_syn.v > test_dsp_model_ref.v | ||||
| for tb in testbench \ | ||||
| 		testbench_comb_8x8_A testbench_comb_8x8_B testbench_comb_16x16 \ | ||||
| 		testbench_seq_16x16_A testbench_seq_16x16_B | ||||
| do | ||||
| 	iverilog -s $tb -o test_dsp_model test_dsp_model.v test_dsp_model_uut.v test_dsp_model_ref.v | ||||
| 	vvp -N ./test_dsp_model | ||||
| done | ||||
							
								
								
									
										342
									
								
								techlibs/ice40/tests/test_dsp_model.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										342
									
								
								techlibs/ice40/tests/test_dsp_model.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,342 @@ | |||
| `timescale 1ns / 1ps | ||||
| 
 | ||||
| module testbench; | ||||
| 	parameter [0:0] NEG_TRIGGER = 0; | ||||
| 	parameter [0:0] C_REG = 0; | ||||
| 	parameter [0:0] A_REG = 0; | ||||
| 	parameter [0:0] B_REG = 0; | ||||
| 	parameter [0:0] D_REG = 0; | ||||
| 	parameter [0:0] TOP_8x8_MULT_REG = 0; | ||||
| 	parameter [0:0] BOT_8x8_MULT_REG = 0; | ||||
| 	parameter [0:0] PIPELINE_16x16_MULT_REG1 = 0; | ||||
| 	parameter [0:0] PIPELINE_16x16_MULT_REG2 = 0; | ||||
| 	parameter [1:0] TOPOUTPUT_SELECT = 0; | ||||
| 	parameter [1:0] TOPADDSUB_LOWERINPUT = 0; | ||||
| 	parameter [0:0] TOPADDSUB_UPPERINPUT = 1; | ||||
| 	parameter [1:0] TOPADDSUB_CARRYSELECT = 0; | ||||
| 	parameter [1:0] BOTOUTPUT_SELECT = 0; | ||||
| 	parameter [1:0] BOTADDSUB_LOWERINPUT = 0; | ||||
| 	parameter [0:0] BOTADDSUB_UPPERINPUT = 1; | ||||
| 	parameter [1:0] BOTADDSUB_CARRYSELECT = 0; | ||||
| 	parameter [0:0] MODE_8x8 = 0; | ||||
| 	parameter [0:0] A_SIGNED = 0; | ||||
| 	parameter [0:0] B_SIGNED = 0; | ||||
| 
 | ||||
| 	reg CLK, CE; | ||||
| 	reg [15:0] C, A, B, D; | ||||
| 	reg AHOLD, BHOLD, CHOLD, DHOLD; | ||||
| 	reg IRSTTOP, IRSTBOT; | ||||
| 	reg ORSTTOP, ORSTBOT; | ||||
| 	reg OLOADTOP, OLOADBOT; | ||||
| 	reg ADDSUBTOP, ADDSUBBOT; | ||||
| 	reg OHOLDTOP, OHOLDBOT; | ||||
| 	reg CI, ACCUMCI, SIGNEXTIN; | ||||
| 
 | ||||
| 	output [31:0] REF_O, UUT_O; | ||||
| 	output REF_CO, REF_ACCUMCO, REF_SIGNEXTOUT; | ||||
| 	output UUT_CO, UUT_ACCUMCO, UUT_SIGNEXTOUT; | ||||
| 
 | ||||
| 	integer errcount = 0; | ||||
| 
 | ||||
| 	task clkcycle; | ||||
| 		begin | ||||
| 			#5; | ||||
| 			CLK = ~CLK; | ||||
| 			#10; | ||||
| 			CLK = ~CLK; | ||||
| 			#2; | ||||
| 			if (REF_O !== UUT_O) begin | ||||
| 				$display("ERROR at %1t: REF_O=%b UUT_O=%b DIFF=%b", $time, REF_O, UUT_O, REF_O ^ UUT_O); | ||||
| 				errcount = errcount + 1; | ||||
| 			end | ||||
| 			if (REF_CO !== UUT_CO) begin | ||||
| 				$display("ERROR at %1t: REF_CO=%b UUT_CO=%b", $time, REF_CO, UUT_CO); | ||||
| 				errcount = errcount + 1; | ||||
| 			end | ||||
| 			if (REF_ACCUMCO !== UUT_ACCUMCO) begin | ||||
| 				$display("ERROR at %1t: REF_ACCUMCO=%b UUT_ACCUMCO=%b", $time, REF_ACCUMCO, UUT_ACCUMCO); | ||||
| 				errcount = errcount + 1; | ||||
| 			end | ||||
| 			if (REF_SIGNEXTOUT !== UUT_SIGNEXTOUT) begin | ||||
| 				$display("ERROR at %1t: REF_SIGNEXTOUT=%b UUT_SIGNEXTOUT=%b", $time, REF_SIGNEXTOUT, UUT_SIGNEXTOUT); | ||||
| 				errcount = errcount + 1; | ||||
| 			end | ||||
| 			#3; | ||||
| 		end | ||||
| 	endtask | ||||
| 
 | ||||
| 	initial begin | ||||
| 		$dumpfile("test_dsp_model.vcd"); | ||||
| 		$dumpvars(0, testbench); | ||||
| 
 | ||||
| 		#2; | ||||
| 		CLK = NEG_TRIGGER; | ||||
| 		CE = 1; | ||||
| 		{C, A, B, D} = 0; | ||||
| 		{AHOLD, BHOLD, CHOLD, DHOLD} = 0; | ||||
| 		{OLOADTOP, OLOADBOT} = 0; | ||||
| 		{ADDSUBTOP, ADDSUBBOT} = 0; | ||||
| 		{OHOLDTOP, OHOLDBOT} = 0; | ||||
| 		{CI, ACCUMCI, SIGNEXTIN} = 0; | ||||
| 
 | ||||
| 		{IRSTTOP, IRSTBOT} = ~0; | ||||
| 		{ORSTTOP, ORSTBOT} = ~0; | ||||
| 
 | ||||
| 		#3; | ||||
| 		{IRSTTOP, IRSTBOT} = 0; | ||||
| 		{ORSTTOP, ORSTBOT} = 0; | ||||
| 
 | ||||
| 		repeat (300) begin | ||||
| 			clkcycle; | ||||
| 
 | ||||
| 			A = $urandom; | ||||
| 			B = $urandom; | ||||
| 			C = $urandom; | ||||
| 			D = $urandom; | ||||
| 
 | ||||
| 			{AHOLD, BHOLD, CHOLD, DHOLD} = $urandom & $urandom & $urandom; | ||||
| 			{OLOADTOP, OLOADBOT} = $urandom & $urandom & $urandom; | ||||
| 			{ADDSUBTOP, ADDSUBBOT} = $urandom & $urandom & $urandom; | ||||
| 			{OHOLDTOP, OHOLDBOT} = $urandom & $urandom & $urandom; | ||||
| 			{CI, ACCUMCI, SIGNEXTIN} = $urandom & $urandom & $urandom; | ||||
| 
 | ||||
| 			{IRSTTOP, IRSTBOT} = $urandom & $urandom & $urandom; | ||||
| 			{ORSTTOP, ORSTBOT} = $urandom & $urandom & $urandom; | ||||
| 		end | ||||
| 
 | ||||
| 		if (errcount == 0) begin | ||||
| 			$display("All tests passed."); | ||||
| 			$finish; | ||||
| 		end else begin | ||||
| 			$display("Caught %1d errors.", errcount); | ||||
| 			$stop; | ||||
| 		end | ||||
| 	end | ||||
| 
 | ||||
| 	SB_MAC16 #( | ||||
| 		.NEG_TRIGGER              (NEG_TRIGGER             ), | ||||
| 		.C_REG                    (C_REG                   ), | ||||
| 		.A_REG                    (A_REG                   ), | ||||
| 		.B_REG                    (B_REG                   ), | ||||
| 		.D_REG                    (D_REG                   ), | ||||
| 		.TOP_8x8_MULT_REG         (TOP_8x8_MULT_REG        ), | ||||
| 		.BOT_8x8_MULT_REG         (BOT_8x8_MULT_REG        ), | ||||
| 		.PIPELINE_16x16_MULT_REG1 (PIPELINE_16x16_MULT_REG1), | ||||
| 		.PIPELINE_16x16_MULT_REG2 (PIPELINE_16x16_MULT_REG2), | ||||
| 		.TOPOUTPUT_SELECT         (TOPOUTPUT_SELECT        ), | ||||
| 		.TOPADDSUB_LOWERINPUT     (TOPADDSUB_LOWERINPUT    ), | ||||
| 		.TOPADDSUB_UPPERINPUT     (TOPADDSUB_UPPERINPUT    ), | ||||
| 		.TOPADDSUB_CARRYSELECT    (TOPADDSUB_CARRYSELECT   ), | ||||
| 		.BOTOUTPUT_SELECT         (BOTOUTPUT_SELECT        ), | ||||
| 		.BOTADDSUB_LOWERINPUT     (BOTADDSUB_LOWERINPUT    ), | ||||
| 		.BOTADDSUB_UPPERINPUT     (BOTADDSUB_UPPERINPUT    ), | ||||
| 		.BOTADDSUB_CARRYSELECT    (BOTADDSUB_CARRYSELECT   ), | ||||
| 		.MODE_8x8                 (MODE_8x8                ), | ||||
| 		.A_SIGNED                 (A_SIGNED                ), | ||||
| 		.B_SIGNED                 (B_SIGNED                ) | ||||
| 	) ref ( | ||||
| 		.CLK        (CLK           ), | ||||
| 		.CE         (CE            ), | ||||
| 		.C          (C             ), | ||||
| 		.A          (A             ), | ||||
| 		.B          (B             ), | ||||
| 		.D          (D             ), | ||||
| 		.AHOLD      (AHOLD         ), | ||||
| 		.BHOLD      (BHOLD         ), | ||||
| 		.CHOLD      (CHOLD         ), | ||||
| 		.DHOLD      (DHOLD         ), | ||||
| 		.IRSTTOP    (IRSTTOP       ), | ||||
| 		.IRSTBOT    (IRSTBOT       ), | ||||
| 		.ORSTTOP    (ORSTTOP       ), | ||||
| 		.ORSTBOT    (ORSTBOT       ), | ||||
| 		.OLOADTOP   (OLOADTOP      ), | ||||
| 		.OLOADBOT   (OLOADBOT      ), | ||||
| 		.ADDSUBTOP  (ADDSUBTOP     ), | ||||
| 		.ADDSUBBOT  (ADDSUBBOT     ), | ||||
| 		.OHOLDTOP   (OHOLDTOP      ), | ||||
| 		.OHOLDBOT   (OHOLDBOT      ), | ||||
| 		.CI         (CI            ), | ||||
| 		.ACCUMCI    (ACCUMCI       ), | ||||
| 		.SIGNEXTIN  (SIGNEXTIN     ), | ||||
| 		.O          (REF_O         ), | ||||
| 		.CO         (REF_CO        ), | ||||
| 		.ACCUMCO    (REF_ACCUMCO   ), | ||||
| 		.SIGNEXTOUT (REF_SIGNEXTOUT) | ||||
| 	); | ||||
| 
 | ||||
| 	SB_MAC16_UUT #( | ||||
| 		.NEG_TRIGGER              (NEG_TRIGGER             ), | ||||
| 		.C_REG                    (C_REG                   ), | ||||
| 		.A_REG                    (A_REG                   ), | ||||
| 		.B_REG                    (B_REG                   ), | ||||
| 		.D_REG                    (D_REG                   ), | ||||
| 		.TOP_8x8_MULT_REG         (TOP_8x8_MULT_REG        ), | ||||
| 		.BOT_8x8_MULT_REG         (BOT_8x8_MULT_REG        ), | ||||
| 		.PIPELINE_16x16_MULT_REG1 (PIPELINE_16x16_MULT_REG1), | ||||
| 		.PIPELINE_16x16_MULT_REG2 (PIPELINE_16x16_MULT_REG2), | ||||
| 		.TOPOUTPUT_SELECT         (TOPOUTPUT_SELECT        ), | ||||
| 		.TOPADDSUB_LOWERINPUT     (TOPADDSUB_LOWERINPUT    ), | ||||
| 		.TOPADDSUB_UPPERINPUT     (TOPADDSUB_UPPERINPUT    ), | ||||
| 		.TOPADDSUB_CARRYSELECT    (TOPADDSUB_CARRYSELECT   ), | ||||
| 		.BOTOUTPUT_SELECT         (BOTOUTPUT_SELECT        ), | ||||
| 		.BOTADDSUB_LOWERINPUT     (BOTADDSUB_LOWERINPUT    ), | ||||
| 		.BOTADDSUB_UPPERINPUT     (BOTADDSUB_UPPERINPUT    ), | ||||
| 		.BOTADDSUB_CARRYSELECT    (BOTADDSUB_CARRYSELECT   ), | ||||
| 		.MODE_8x8                 (MODE_8x8                ), | ||||
| 		.A_SIGNED                 (A_SIGNED                ), | ||||
| 		.B_SIGNED                 (B_SIGNED                ) | ||||
| 	) uut ( | ||||
| 		.CLK        (CLK           ), | ||||
| 		.CE         (CE            ), | ||||
| 		.C          (C             ), | ||||
| 		.A          (A             ), | ||||
| 		.B          (B             ), | ||||
| 		.D          (D             ), | ||||
| 		.AHOLD      (AHOLD         ), | ||||
| 		.BHOLD      (BHOLD         ), | ||||
| 		.CHOLD      (CHOLD         ), | ||||
| 		.DHOLD      (DHOLD         ), | ||||
| 		.IRSTTOP    (IRSTTOP       ), | ||||
| 		.IRSTBOT    (IRSTBOT       ), | ||||
| 		.ORSTTOP    (ORSTTOP       ), | ||||
| 		.ORSTBOT    (ORSTBOT       ), | ||||
| 		.OLOADTOP   (OLOADTOP      ), | ||||
| 		.OLOADBOT   (OLOADBOT      ), | ||||
| 		.ADDSUBTOP  (ADDSUBTOP     ), | ||||
| 		.ADDSUBBOT  (ADDSUBBOT     ), | ||||
| 		.OHOLDTOP   (OHOLDTOP      ), | ||||
| 		.OHOLDBOT   (OHOLDBOT      ), | ||||
| 		.CI         (CI            ), | ||||
| 		.ACCUMCI    (ACCUMCI       ), | ||||
| 		.SIGNEXTIN  (SIGNEXTIN     ), | ||||
| 		.O          (UUT_O         ), | ||||
| 		.CO         (UUT_CO        ), | ||||
| 		.ACCUMCO    (UUT_ACCUMCO   ), | ||||
| 		.SIGNEXTOUT (UUT_SIGNEXTOUT) | ||||
| 	); | ||||
| endmodule | ||||
| 
 | ||||
| module testbench_comb_8x8_A; | ||||
| 	testbench #( | ||||
| 		.NEG_TRIGGER               (0), | ||||
| 		.C_REG                     (0), | ||||
| 		.A_REG                     (0), | ||||
| 		.B_REG                     (0), | ||||
| 		.D_REG                     (0), | ||||
| 		.TOP_8x8_MULT_REG          (0), | ||||
| 		.BOT_8x8_MULT_REG          (0), | ||||
| 		.PIPELINE_16x16_MULT_REG1  (0), | ||||
| 		.PIPELINE_16x16_MULT_REG2  (0), | ||||
| 		.TOPOUTPUT_SELECT          (2),   // 0=P, 1=Q, 2=8x8, 3=16x16 | ||||
| 		.TOPADDSUB_LOWERINPUT      (0),   // 0=A, 1=8x8, 2=16x16, 3=S-EXT | ||||
| 		.TOPADDSUB_UPPERINPUT      (0),   // 0=Q, 1=C | ||||
| 		.TOPADDSUB_CARRYSELECT     (0),   // 0=0, 1=1, 2=ACI, 3=CI | ||||
| 		.BOTOUTPUT_SELECT          (2),   // 0=R, 1=S, 2=8x8, 3=16x16 | ||||
| 		.BOTADDSUB_LOWERINPUT      (0),   // 0=B, 1=8x8, 2=16x16, 3=S-EXT | ||||
| 		.BOTADDSUB_UPPERINPUT      (0),   // 0=S, 1=D | ||||
| 		.BOTADDSUB_CARRYSELECT     (0),   // 0=0, 1=1, 2=ACI, 3=CI | ||||
| 		.MODE_8x8                  (0), | ||||
| 		.A_SIGNED                  (0), | ||||
| 		.B_SIGNED                  (0) | ||||
| 	) testbench (); | ||||
| endmodule | ||||
| 
 | ||||
| module testbench_comb_8x8_B; | ||||
| 	testbench #( | ||||
| 		.NEG_TRIGGER               (0), | ||||
| 		.C_REG                     (0), | ||||
| 		.A_REG                     (0), | ||||
| 		.B_REG                     (0), | ||||
| 		.D_REG                     (0), | ||||
| 		.TOP_8x8_MULT_REG          (0), | ||||
| 		.BOT_8x8_MULT_REG          (0), | ||||
| 		.PIPELINE_16x16_MULT_REG1  (0), | ||||
| 		.PIPELINE_16x16_MULT_REG2  (0), | ||||
| 		.TOPOUTPUT_SELECT          (0),   // 0=P, 1=Q, 2=8x8, 3=16x16 | ||||
| 		.TOPADDSUB_LOWERINPUT      (1),   // 0=A, 1=8x8, 2=16x16, 3=S-EXT | ||||
| 		.TOPADDSUB_UPPERINPUT      (1),   // 0=Q, 1=C | ||||
| 		.TOPADDSUB_CARRYSELECT     (0),   // 0=0, 1=1, 2=ACI, 3=CI | ||||
| 		.BOTOUTPUT_SELECT          (0),   // 0=R, 1=S, 2=8x8, 3=16x16 | ||||
| 		.BOTADDSUB_LOWERINPUT      (1),   // 0=B, 1=8x8, 2=16x16, 3=S-EXT | ||||
| 		.BOTADDSUB_UPPERINPUT      (1),   // 0=S, 1=D | ||||
| 		.BOTADDSUB_CARRYSELECT     (0),   // 0=0, 1=1, 2=ACI, 3=CI | ||||
| 		.MODE_8x8                  (0), | ||||
| 		.A_SIGNED                  (0), | ||||
| 		.B_SIGNED                  (0) | ||||
| 	) testbench (); | ||||
| endmodule | ||||
| 
 | ||||
| module testbench_comb_16x16; | ||||
| 	testbench #( | ||||
| 		.NEG_TRIGGER               (0), | ||||
| 		.C_REG                     (0), | ||||
| 		.A_REG                     (0), | ||||
| 		.B_REG                     (0), | ||||
| 		.D_REG                     (0), | ||||
| 		.TOP_8x8_MULT_REG          (0), | ||||
| 		.BOT_8x8_MULT_REG          (0), | ||||
| 		.PIPELINE_16x16_MULT_REG1  (0), | ||||
| 		.PIPELINE_16x16_MULT_REG2  (0), | ||||
| 		.TOPOUTPUT_SELECT          (0),   // 0=P, 1=Q, 2=8x8, 3=16x16 | ||||
| 		.TOPADDSUB_LOWERINPUT      (2),   // 0=A, 1=8x8, 2=16x16, 3=S-EXT | ||||
| 		.TOPADDSUB_UPPERINPUT      (1),   // 0=Q, 1=C | ||||
| 		.TOPADDSUB_CARRYSELECT     (2),   // 0=0, 1=1, 2=ACI, 3=CI | ||||
| 		.BOTOUTPUT_SELECT          (0),   // 0=R, 1=S, 2=8x8, 3=16x16 | ||||
| 		.BOTADDSUB_LOWERINPUT      (2),   // 0=B, 1=8x8, 2=16x16, 3=S-EXT | ||||
| 		.BOTADDSUB_UPPERINPUT      (1),   // 0=S, 1=D | ||||
| 		.BOTADDSUB_CARRYSELECT     (2),   // 0=0, 1=1, 2=ACI, 3=CI | ||||
| 		.MODE_8x8                  (0), | ||||
| 		.A_SIGNED                  (0), | ||||
| 		.B_SIGNED                  (0) | ||||
| 	) testbench (); | ||||
| endmodule | ||||
| 
 | ||||
| module testbench_seq_16x16_A; | ||||
| 	testbench #( | ||||
| 		.NEG_TRIGGER               (0), | ||||
| 		.C_REG                     (1), | ||||
| 		.A_REG                     (1), | ||||
| 		.B_REG                     (1), | ||||
| 		.D_REG                     (1), | ||||
| 		.TOP_8x8_MULT_REG          (1), | ||||
| 		.BOT_8x8_MULT_REG          (1), | ||||
| 		.PIPELINE_16x16_MULT_REG1  (1), | ||||
| 		.PIPELINE_16x16_MULT_REG2  (1), | ||||
| 		.TOPOUTPUT_SELECT          (0),   // 0=P, 1=Q, 2=8x8, 3=16x16 | ||||
| 		.TOPADDSUB_LOWERINPUT      (2),   // 0=A, 1=8x8, 2=16x16, 3=S-EXT | ||||
| 		.TOPADDSUB_UPPERINPUT      (1),   // 0=Q, 1=C | ||||
| 		.TOPADDSUB_CARRYSELECT     (2),   // 0=0, 1=1, 2=ACI, 3=CI | ||||
| 		.BOTOUTPUT_SELECT          (0),   // 0=R, 1=S, 2=8x8, 3=16x16 | ||||
| 		.BOTADDSUB_LOWERINPUT      (2),   // 0=B, 1=8x8, 2=16x16, 3=S-EXT | ||||
| 		.BOTADDSUB_UPPERINPUT      (1),   // 0=S, 1=D | ||||
| 		.BOTADDSUB_CARRYSELECT     (2),   // 0=0, 1=1, 2=ACI, 3=CI | ||||
| 		.MODE_8x8                  (0), | ||||
| 		.A_SIGNED                  (0), | ||||
| 		.B_SIGNED                  (0) | ||||
| 	) testbench (); | ||||
| endmodule | ||||
| 
 | ||||
| module testbench_seq_16x16_B; | ||||
| 	testbench #( | ||||
| 		.NEG_TRIGGER               (0), | ||||
| 		.C_REG                     (1), | ||||
| 		.A_REG                     (1), | ||||
| 		.B_REG                     (1), | ||||
| 		.D_REG                     (1), | ||||
| 		.TOP_8x8_MULT_REG          (1), | ||||
| 		.BOT_8x8_MULT_REG          (1), | ||||
| 		.PIPELINE_16x16_MULT_REG1  (1), | ||||
| 		.PIPELINE_16x16_MULT_REG2  (0), | ||||
| 		.TOPOUTPUT_SELECT          (1),   // 0=P, 1=Q, 2=8x8, 3=16x16 | ||||
| 		.TOPADDSUB_LOWERINPUT      (2),   // 0=A, 1=8x8, 2=16x16, 3=S-EXT | ||||
| 		.TOPADDSUB_UPPERINPUT      (0),   // 0=Q, 1=C | ||||
| 		.TOPADDSUB_CARRYSELECT     (2),   // 0=0, 1=1, 2=ACI, 3=CI | ||||
| 		.BOTOUTPUT_SELECT          (1),   // 0=R, 1=S, 2=8x8, 3=16x16 | ||||
| 		.BOTADDSUB_LOWERINPUT      (2),   // 0=B, 1=8x8, 2=16x16, 3=S-EXT | ||||
| 		.BOTADDSUB_UPPERINPUT      (0),   // 0=S, 1=D | ||||
| 		.BOTADDSUB_CARRYSELECT     (2),   // 0=0, 1=1, 2=ACI, 3=CI | ||||
| 		.MODE_8x8                  (0), | ||||
| 		.A_SIGNED                  (0), | ||||
| 		.B_SIGNED                  (0) | ||||
| 	) testbench (); | ||||
| endmodule | ||||
|  | @ -28,7 +28,7 @@ if [ ! -f $toolsdir/cmp_tbdata -o $toolsdir/cmp_tbdata.c -nt $toolsdir/cmp_tbdat | |||
| 	( set -ex; ${CC:-gcc} -Wall -o $toolsdir/cmp_tbdata $toolsdir/cmp_tbdata.c; ) || exit 1 | ||||
| fi | ||||
| 
 | ||||
| while getopts xmGl:wkjvref:s:p:n:S:I:B:-: opt; do | ||||
| while getopts xmGl:wkjvref:s:p:n:S:I:-: opt; do | ||||
| 	case "$opt" in | ||||
| 		x) | ||||
| 			use_xsim=true ;; | ||||
|  | @ -65,8 +65,6 @@ while getopts xmGl:wkjvref:s:p:n:S:I:B:-: opt; do | |||
| 			include_opts="$include_opts -I $OPTARG" | ||||
| 			xinclude_opts="$xinclude_opts -i $OPTARG" | ||||
| 			minclude_opts="$minclude_opts +incdir+$OPTARG" ;; | ||||
|         B) | ||||
| 			backend_opts="$backend_opts $OPTARG" ;; | ||||
| 		-) | ||||
| 			case "${OPTARG}" in | ||||
| 			    xfirrtl) | ||||
|  | @ -84,7 +82,7 @@ while getopts xmGl:wkjvref:s:p:n:S:I:B:-: opt; do | |||
| 				;; | ||||
| 			esac;; | ||||
| 		*) | ||||
| 			echo "Usage: $0 [-x|-m] [-G] [-w] [-k] [-j] [-v] [-r] [-e] [-l libs] [-f frontend] [-s script] [-p cmdstring] [-n iters] [-S seed] [-I incdir] [-B backend_opt] [--xfirrtl FIRRTL test exclude file] [--firrtl2verilog command to generate verilog from firrtl] verilog-files\n" >&2 | ||||
| 			echo "Usage: $0 [-x|-m] [-G] [-w] [-k] [-j] [-v] [-r] [-e] [-l libs] [-f frontend] [-s script] [-p cmdstring] [-n iters] [-S seed] [-I incdir] [--xfirrtl FIRRTL test exclude file] [--firrtl2verilog command to generate verilog from firrtl] verilog-files\n" >&2 | ||||
| 			exit 1 | ||||
| 	esac | ||||
| done | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue