mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +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")) | 	if (connections_.count("\\Y")) | ||||||
| 		parameters["\\Y_WIDTH"] = GetSize(connections_["\\Y"]); | 		parameters["\\Y_WIDTH"] = GetSize(connections_["\\Y"]); | ||||||
| 
 | 
 | ||||||
|  | 	if (connections_.count("\\Q")) | ||||||
|  | 		parameters["\\WIDTH"] = GetSize(connections_["\\Q"]); | ||||||
|  | 
 | ||||||
| 	check(); | 	check(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -143,6 +143,9 @@ struct SetundefPass : public Pass { | ||||||
| 		log("    -init\n"); | 		log("    -init\n"); | ||||||
| 		log("        also create/update init values for flip-flops\n"); | 		log("        also create/update init values for flip-flops\n"); | ||||||
| 		log("\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 | 	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 undriven_mode = false; | ||||||
| 		bool expose_mode = false; | 		bool expose_mode = false; | ||||||
| 		bool init_mode = false; | 		bool init_mode = false; | ||||||
|  | 		bool params_mode = false; | ||||||
| 		SetundefWorker worker; | 		SetundefWorker worker; | ||||||
| 
 | 
 | ||||||
| 		log_header(design, "Executing SETUNDEF pass (replace undef values with defined constants).\n"); | 		log_header(design, "Executing SETUNDEF pass (replace undef values with defined constants).\n"); | ||||||
|  | @ -199,6 +203,10 @@ struct SetundefPass : public Pass { | ||||||
| 				init_mode = true; | 				init_mode = true; | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
|  | 			if (args[argidx] == "-params") { | ||||||
|  | 				params_mode = true; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
| 			if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) { | 			if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) { | ||||||
| 				got_value = true; | 				got_value = true; | ||||||
| 				worker.next_bit_mode = MODE_RANDOM; | 				worker.next_bit_mode = MODE_RANDOM; | ||||||
|  | @ -228,6 +236,18 @@ struct SetundefPass : public Pass { | ||||||
| 
 | 
 | ||||||
| 		for (auto module : design->selected_modules()) | 		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 (undriven_mode) | ||||||
| 			{ | 			{ | ||||||
| 				if (!module->processes.empty()) | 				if (!module->processes.empty()) | ||||||
|  |  | ||||||
|  | @ -38,7 +38,8 @@ struct WreduceConfig | ||||||
| 			"$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", | 			"$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", | ||||||
| 			"$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt", | 			"$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt", | ||||||
| 			"$add", "$sub", "$mul", // "$div", "$mod", "$pow",
 | 			"$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); | 		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) | 	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(); | 		port_signed = cell->getParam(stringf("\\%c_SIGNED", port)).as_bool(); | ||||||
|  | @ -176,6 +242,9 @@ struct WreduceWorker | ||||||
| 		if (cell->type.in("$mux", "$pmux")) | 		if (cell->type.in("$mux", "$pmux")) | ||||||
| 			return run_cell_mux(cell); | 			return run_cell_mux(cell); | ||||||
| 
 | 
 | ||||||
|  | 		if (cell->type.in("$dff", "$adff")) | ||||||
|  | 			return run_cell_dff(cell); | ||||||
|  | 
 | ||||||
| 		SigSpec sig = mi.sigmap(cell->getPort("\\Y")); | 		SigSpec sig = mi.sigmap(cell->getPort("\\Y")); | ||||||
| 
 | 
 | ||||||
| 		if (sig.has_const()) | 		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 | 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 ( | module SB_SPRAM256KA ( | ||||||
| 	input [13:0] ADDRESS, | 	input [13:0] ADDRESS, | ||||||
| 	input [15:0] DATAIN, | 	input [15:0] DATAIN, | ||||||
|  | @ -1273,3 +1220,171 @@ module SB_IO_OD ( | ||||||
| 	endgenerate | 	endgenerate | ||||||
| `endif | `endif | ||||||
| endmodule | 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("    -nobram\n"); | ||||||
| 		log("        do not use SB_RAM40_4K* cells in output netlist\n"); | 		log("        do not use SB_RAM40_4K* cells in output netlist\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | 		log("    -dsp\n"); | ||||||
|  | 		log("        use iCE40 UltraPlus DSP cells for large arithmetic\n"); | ||||||
|  | 		log("\n"); | ||||||
| 		log("    -noabc\n"); | 		log("    -noabc\n"); | ||||||
| 		log("        use built-in Yosys LUT techmapping instead of abc\n"); | 		log("        use built-in Yosys LUT techmapping instead of abc\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | @ -98,8 +101,9 @@ struct SynthIce40Pass : public ScriptPass | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| 	string top_opt, blif_file, edif_file, json_file, abc; | 	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; | 	int min_ce_use; | ||||||
| 
 | 
 | ||||||
| 	void clear_flags() YS_OVERRIDE | 	void clear_flags() YS_OVERRIDE | ||||||
|  | @ -112,6 +116,7 @@ struct SynthIce40Pass : public ScriptPass | ||||||
| 		nodffe = false; | 		nodffe = false; | ||||||
| 		min_ce_use = -1; | 		min_ce_use = -1; | ||||||
| 		nobram = false; | 		nobram = false; | ||||||
|  | 		dsp = false; | ||||||
| 		flatten = true; | 		flatten = true; | ||||||
| 		retime = false; | 		retime = false; | ||||||
| 		relut = false; | 		relut = false; | ||||||
|  | @ -185,6 +190,10 @@ struct SynthIce40Pass : public ScriptPass | ||||||
| 				nobram = true; | 				nobram = true; | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
|  | 			if (args[argidx] == "-dsp") { | ||||||
|  | 				dsp = true; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
| 			if (args[argidx] == "-noabc") { | 			if (args[argidx] == "-noabc") { | ||||||
| 				noabc = true; | 				noabc = true; | ||||||
| 				continue; | 				continue; | ||||||
|  | @ -222,11 +231,11 @@ struct SynthIce40Pass : public ScriptPass | ||||||
| 		{ | 		{ | ||||||
| 			run("read_verilog -lib +/ice40/cells_sim.v"); | 			run("read_verilog -lib +/ice40/cells_sim.v"); | ||||||
| 			run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str())); | 			run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str())); | ||||||
|  | 			run("proc"); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (flatten && check_label("flatten", "(unless -noflatten)")) | 		if (flatten && check_label("flatten", "(unless -noflatten)")) | ||||||
| 		{ | 		{ | ||||||
| 			run("proc"); |  | ||||||
| 			run("flatten"); | 			run("flatten"); | ||||||
| 			run("tribuf -logic"); | 			run("tribuf -logic"); | ||||||
| 			run("deminout"); | 			run("deminout"); | ||||||
|  | @ -234,7 +243,23 @@ struct SynthIce40Pass : public ScriptPass | ||||||
| 
 | 
 | ||||||
| 		if (check_label("coarse")) | 		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)")) | 		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_ffs_[01][01][01][01][01]_* | ||||||
| test_bram_[0-9]* | /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 | 	( set -ex; ${CC:-gcc} -Wall -o $toolsdir/cmp_tbdata $toolsdir/cmp_tbdata.c; ) || exit 1 | ||||||
| fi | 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 | 	case "$opt" in | ||||||
| 		x) | 		x) | ||||||
| 			use_xsim=true ;; | 			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" | 			include_opts="$include_opts -I $OPTARG" | ||||||
| 			xinclude_opts="$xinclude_opts -i $OPTARG" | 			xinclude_opts="$xinclude_opts -i $OPTARG" | ||||||
| 			minclude_opts="$minclude_opts +incdir+$OPTARG" ;; | 			minclude_opts="$minclude_opts +incdir+$OPTARG" ;; | ||||||
|         B) |  | ||||||
| 			backend_opts="$backend_opts $OPTARG" ;; |  | ||||||
| 		-) | 		-) | ||||||
| 			case "${OPTARG}" in | 			case "${OPTARG}" in | ||||||
| 			    xfirrtl) | 			    xfirrtl) | ||||||
|  | @ -84,7 +82,7 @@ while getopts xmGl:wkjvref:s:p:n:S:I:B:-: opt; do | ||||||
| 				;; | 				;; | ||||||
| 			esac;; | 			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 | 			exit 1 | ||||||
| 	esac | 	esac | ||||||
| done | done | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue