mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-03 21:09:12 +00:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/master'
This commit is contained in:
		
						commit
						81abb2517c
					
				
					 39 changed files with 2479 additions and 222 deletions
				
			
		| 
						 | 
				
			
			@ -36,6 +36,8 @@ echo
 | 
			
		|||
 | 
			
		||||
##########################################################################
 | 
			
		||||
 | 
			
		||||
./yosys tests/simple/fiedler-cooley.v
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
echo 'Testing...' && echo -en 'travis_fold:start:script.test\\r'
 | 
			
		||||
echo
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,48 +6,15 @@ source .travis/common.sh
 | 
			
		|||
 | 
			
		||||
##########################################################################
 | 
			
		||||
 | 
			
		||||
# Fixing Travis's git clone
 | 
			
		||||
echo
 | 
			
		||||
echo 'Fixing git setup...' && echo -en 'travis_fold:start:before_install.git\\r'
 | 
			
		||||
echo
 | 
			
		||||
git fetch --unshallow && git fetch --tags
 | 
			
		||||
 | 
			
		||||
# For pull requests, we get more info about the git source.
 | 
			
		||||
if [ z"$TRAVIS_PULL_REQUEST_SLUG" != z ]; then
 | 
			
		||||
	echo "- Fetching from pull request source"
 | 
			
		||||
	git remote add source https://github.com/$TRAVIS_PULL_REQUEST_SLUG.git
 | 
			
		||||
	git fetch source && git fetch --tags
 | 
			
		||||
 | 
			
		||||
	echo "- Fetching the actual pull request"
 | 
			
		||||
	git fetch origin pull/$TRAVIS_PULL_REQUEST/head:pull-$TRAVIS_PULL_REQUEST-head
 | 
			
		||||
	git fetch origin pull/$TRAVIS_PULL_REQUEST/merge:pull-$TRAVIS_PULL_REQUEST-merge
 | 
			
		||||
 | 
			
		||||
	git log -n 5 --graph pull-$TRAVIS_PULL_REQUEST-merge
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# For building branches we need to fix the "detached head" state.
 | 
			
		||||
if [ z"$TRAVIS_BRANCH" != z ]; then
 | 
			
		||||
	TRAVIS_COMMIT_ACTUAL=$(git log --pretty=format:'%H' -n 1)
 | 
			
		||||
	echo "- Fixing detached head (current $TRAVIS_COMMIT_ACTUAL -> $TRAVIS_COMMIT)"
 | 
			
		||||
	git remote -v
 | 
			
		||||
	git branch -v
 | 
			
		||||
	if [ x"$(git show-ref -s HEAD)" = x"$TRAVIS_COMMIT" ]; then
 | 
			
		||||
		echo "Checked out at $TRAVIS_COMMIT"
 | 
			
		||||
	else
 | 
			
		||||
		if [ z"$TRAVIS_PULL_REQUEST_SLUG" != z ]; then
 | 
			
		||||
			git fetch source $TRAVIS_COMMIT || echo "Unable to fetch $TRAVIS_COMMIT from source"
 | 
			
		||||
		fi
 | 
			
		||||
		git fetch origin $TRAVIS_COMMIT || echo "Unable to fetch $TRAVIS_COMMIT from origin"
 | 
			
		||||
	fi
 | 
			
		||||
	git branch -D $TRAVIS_BRANCH || true
 | 
			
		||||
	git checkout $TRAVIS_COMMIT -b $TRAVIS_BRANCH
 | 
			
		||||
	git branch -v
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Output status information.
 | 
			
		||||
git status
 | 
			
		||||
git describe --tags
 | 
			
		||||
git log -n 5 --graph
 | 
			
		||||
(
 | 
			
		||||
	set +e
 | 
			
		||||
	set -x
 | 
			
		||||
	git status
 | 
			
		||||
	git branch -v
 | 
			
		||||
	git log -n 5 --graph
 | 
			
		||||
	git log --format=oneline -n 20 --graph
 | 
			
		||||
)
 | 
			
		||||
echo
 | 
			
		||||
echo -en 'travis_fold:end:before_install.git\\r'
 | 
			
		||||
echo
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -100,7 +100,7 @@ LDFLAGS += -rdynamic
 | 
			
		|||
LDLIBS += -lrt
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
YOSYS_VER := 0.8+$(shell cd $(YOSYS_SRC) && test -e .git && { git log --author=clifford@clifford.at --oneline 4d4665b.. | wc -l; })
 | 
			
		||||
YOSYS_VER := 0.8+$(shell cd $(YOSYS_SRC) && test -e .git && { git log --author=clifford@clifford.at --oneline 4d4665b.. 2> /dev/null | wc -l; })
 | 
			
		||||
GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN)
 | 
			
		||||
OBJS = kernel/version_$(GIT_REV).o
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,11 @@
 | 
			
		|||
#include "kernel/celltypes.h"
 | 
			
		||||
#include "kernel/cellaigs.h"
 | 
			
		||||
#include "kernel/log.h"
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <regex>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <cmath>
 | 
			
		||||
 | 
			
		||||
USING_YOSYS_NAMESPACE
 | 
			
		||||
PRIVATE_NAMESPACE_BEGIN
 | 
			
		||||
| 
						 | 
				
			
			@ -37,6 +41,7 @@ static const FDirection FD_NODIRECTION = 0x0;
 | 
			
		|||
static const FDirection FD_IN = 0x1;
 | 
			
		||||
static const FDirection FD_OUT = 0x2;
 | 
			
		||||
static const FDirection FD_INOUT = 0x3;
 | 
			
		||||
static const int FIRRTL_MAX_DSH_WIDTH_ERROR = 20; // For historic reasons, this is actually one greater than the maximum allowed shift width
 | 
			
		||||
 | 
			
		||||
// Get a port direction with respect to a specific module.
 | 
			
		||||
FDirection getPortFDirection(IdString id, Module *module)
 | 
			
		||||
| 
						 | 
				
			
			@ -173,6 +178,26 @@ struct FirrtlWorker
 | 
			
		|||
	void process_instance(RTLIL::Cell *cell, vector<string> &wire_exprs)
 | 
			
		||||
	{
 | 
			
		||||
		std::string cell_type = fid(cell->type);
 | 
			
		||||
		std::string instanceOf;
 | 
			
		||||
		// If this is a parameterized module, its parent module is encoded in the cell type
 | 
			
		||||
		if (cell->type.substr(0, 8) == "$paramod")
 | 
			
		||||
		{
 | 
			
		||||
			std::string::iterator it;
 | 
			
		||||
			for (it = cell_type.begin(); it < cell_type.end(); it++)
 | 
			
		||||
			{
 | 
			
		||||
				switch (*it) {
 | 
			
		||||
					case '\\': /* FALL_THROUGH */
 | 
			
		||||
					case '=': /* FALL_THROUGH */
 | 
			
		||||
					case '\'': /* FALL_THROUGH */
 | 
			
		||||
					case '$': instanceOf.append("_"); break;
 | 
			
		||||
					default: instanceOf.append(1, *it); break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			instanceOf = cell_type;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		std::string cell_name = cellname(cell);
 | 
			
		||||
		std::string cell_name_comment;
 | 
			
		||||
| 
						 | 
				
			
			@ -182,7 +207,13 @@ struct FirrtlWorker
 | 
			
		|||
			cell_name_comment = "";
 | 
			
		||||
		// Find the module corresponding to this instance.
 | 
			
		||||
		auto instModule = design->module(cell->type);
 | 
			
		||||
		wire_exprs.push_back(stringf("%s" "inst %s%s of %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), cell_type.c_str()));
 | 
			
		||||
		// If there is no instance for this, just return.
 | 
			
		||||
		if (instModule == NULL)
 | 
			
		||||
		{
 | 
			
		||||
			log_warning("No instance for %s.%s\n", cell_type.c_str(), cell_name.c_str());
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		wire_exprs.push_back(stringf("%s" "inst %s%s of %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceOf.c_str()));
 | 
			
		||||
 | 
			
		||||
		for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) {
 | 
			
		||||
			if (it->second.size() > 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -194,20 +225,20 @@ struct FirrtlWorker
 | 
			
		|||
				std::string source, sink;
 | 
			
		||||
				switch (dir) {
 | 
			
		||||
					case FD_INOUT:
 | 
			
		||||
						log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", log_id(cell_type), log_signal(it->second));
 | 
			
		||||
						log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", cell_type.c_str(), log_signal(it->second));
 | 
			
		||||
					case FD_OUT:
 | 
			
		||||
						source = firstName;
 | 
			
		||||
						sink = secondName;
 | 
			
		||||
						break;
 | 
			
		||||
					case FD_NODIRECTION:
 | 
			
		||||
						log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", log_id(cell_type), log_signal(it->second));
 | 
			
		||||
						log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", cell_type.c_str(), log_signal(it->second));
 | 
			
		||||
						/* FALL_THROUGH */
 | 
			
		||||
					case FD_IN:
 | 
			
		||||
						source = secondName;
 | 
			
		||||
						sink = firstName;
 | 
			
		||||
						break;
 | 
			
		||||
					default:
 | 
			
		||||
						log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", log_id(cell_type), log_signal(it->second), dir);
 | 
			
		||||
						log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", cell_type.c_str(), log_signal(it->second), dir);
 | 
			
		||||
						break;
 | 
			
		||||
				}
 | 
			
		||||
				wire_exprs.push_back(stringf("\n%s%s <= %s", indent.c_str(), sink.c_str(), source.c_str()));
 | 
			
		||||
| 
						 | 
				
			
			@ -217,6 +248,20 @@ struct FirrtlWorker
 | 
			
		|||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Given an expression for a shift amount, and a maximum width,
 | 
			
		||||
	//  generate the FIRRTL expression for equivalent dynamic shift taking into account FIRRTL shift semantics.
 | 
			
		||||
	std::string gen_dshl(const string b_expr, const int b_padded_width)
 | 
			
		||||
	{
 | 
			
		||||
		string result = b_expr;
 | 
			
		||||
		if (b_padded_width >= FIRRTL_MAX_DSH_WIDTH_ERROR) {
 | 
			
		||||
			int max_shift_width_bits = FIRRTL_MAX_DSH_WIDTH_ERROR - 1;
 | 
			
		||||
			string max_shift_string = stringf("UInt<%d>(%d)", max_shift_width_bits, (1<<max_shift_width_bits) - 1);
 | 
			
		||||
			// Deal with the difference in semantics between FIRRTL and verilog
 | 
			
		||||
			result = stringf("mux(gt(%s, %s), %s, bits(%s, %d, 0))", b_expr.c_str(), max_shift_string.c_str(), max_shift_string.c_str(), b_expr.c_str(), max_shift_width_bits - 1);
 | 
			
		||||
		}
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void run()
 | 
			
		||||
	{
 | 
			
		||||
		f << stringf("  module %s:\n", make_id(module->name));
 | 
			
		||||
| 
						 | 
				
			
			@ -225,6 +270,12 @@ struct FirrtlWorker
 | 
			
		|||
		for (auto wire : module->wires())
 | 
			
		||||
		{
 | 
			
		||||
			const auto wireName = make_id(wire->name);
 | 
			
		||||
			// If a wire has initial data, issue a warning since FIRRTL doesn't currently support it.
 | 
			
		||||
			if (wire->attributes.count("\\init")) {
 | 
			
		||||
				log_warning("Initial value (%s) for (%s.%s) not supported\n",
 | 
			
		||||
							wire->attributes.at("\\init").as_string().c_str(),
 | 
			
		||||
							log_id(module), log_id(wire));
 | 
			
		||||
			}
 | 
			
		||||
			if (wire->port_id)
 | 
			
		||||
			{
 | 
			
		||||
				if (wire->port_input && wire->port_output)
 | 
			
		||||
| 
						 | 
				
			
			@ -240,7 +291,8 @@ struct FirrtlWorker
 | 
			
		|||
 | 
			
		||||
		for (auto cell : module->cells())
 | 
			
		||||
		{
 | 
			
		||||
		  // Is this cell is a module instance?
 | 
			
		||||
			bool extract_y_bits = false;		// Assume no extraction of final bits will be required.
 | 
			
		||||
		    // Is this cell is a module instance?
 | 
			
		||||
			if (cell->type[0] != '$')
 | 
			
		||||
			{
 | 
			
		||||
				process_instance(cell, wire_exprs);
 | 
			
		||||
| 
						 | 
				
			
			@ -264,21 +316,21 @@ struct FirrtlWorker
 | 
			
		|||
				}
 | 
			
		||||
 | 
			
		||||
				string primop;
 | 
			
		||||
                                bool always_uint = false;
 | 
			
		||||
				bool always_uint = false;
 | 
			
		||||
				if (cell->type == "$not") primop = "not";
 | 
			
		||||
				if (cell->type == "$neg") primop = "neg";
 | 
			
		||||
				if (cell->type == "$logic_not") {
 | 
			
		||||
				else if (cell->type == "$neg") primop = "neg";
 | 
			
		||||
				else if (cell->type == "$logic_not") {
 | 
			
		||||
                                        primop = "eq";
 | 
			
		||||
                                        a_expr = stringf("%s, UInt(0)", a_expr.c_str());
 | 
			
		||||
                                }
 | 
			
		||||
				if (cell->type == "$reduce_and") primop = "andr";
 | 
			
		||||
				if (cell->type == "$reduce_or") primop = "orr";
 | 
			
		||||
				if (cell->type == "$reduce_xor") primop = "xorr";
 | 
			
		||||
				if (cell->type == "$reduce_xnor") {
 | 
			
		||||
				else if (cell->type == "$reduce_and") primop = "andr";
 | 
			
		||||
				else if (cell->type == "$reduce_or") primop = "orr";
 | 
			
		||||
				else if (cell->type == "$reduce_xor") primop = "xorr";
 | 
			
		||||
				else if (cell->type == "$reduce_xnor") {
 | 
			
		||||
                                        primop = "not";
 | 
			
		||||
                                        a_expr = stringf("xorr(%s)", a_expr.c_str());
 | 
			
		||||
                                }
 | 
			
		||||
				if (cell->type == "$reduce_bool") {
 | 
			
		||||
				else if (cell->type == "$reduce_bool") {
 | 
			
		||||
					primop = "neq";
 | 
			
		||||
					// Use the sign of the a_expr and its width as the type (UInt/SInt) and width of the comparand.
 | 
			
		||||
					bool a_signed = cell->parameters.at("\\A_SIGNED").as_bool();
 | 
			
		||||
| 
						 | 
				
			
			@ -297,14 +349,15 @@ struct FirrtlWorker
 | 
			
		|||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (cell->type.in("$add", "$sub", "$mul", "$div", "$mod", "$xor", "$and", "$or", "$eq", "$eqx",
 | 
			
		||||
                                        "$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$shr", "$sshr", "$sshl", "$shl",
 | 
			
		||||
                                        "$logic_and", "$logic_or"))
 | 
			
		||||
							  "$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$shr", "$sshr", "$sshl", "$shl",
 | 
			
		||||
							  "$logic_and", "$logic_or"))
 | 
			
		||||
			{
 | 
			
		||||
				string y_id = make_id(cell->name);
 | 
			
		||||
				bool is_signed = cell->parameters.at("\\A_SIGNED").as_bool();
 | 
			
		||||
				int y_width =  cell->parameters.at("\\Y_WIDTH").as_int();
 | 
			
		||||
				string a_expr = make_expr(cell->getPort("\\A"));
 | 
			
		||||
				string b_expr = make_expr(cell->getPort("\\B"));
 | 
			
		||||
				int b_padded_width = cell->parameters.at("\\B_WIDTH").as_int();
 | 
			
		||||
				wire_decls.push_back(stringf("    wire %s: UInt<%d>\n", y_id.c_str(), y_width));
 | 
			
		||||
 | 
			
		||||
				if (cell->parameters.at("\\A_SIGNED").as_bool()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -315,67 +368,93 @@ struct FirrtlWorker
 | 
			
		|||
					if (cell->parameters.at("\\B_SIGNED").as_bool()) {
 | 
			
		||||
						b_expr = "asSInt(" + b_expr + ")";
 | 
			
		||||
					}
 | 
			
		||||
					b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width);
 | 
			
		||||
					if (b_padded_width < y_width) {
 | 
			
		||||
						auto b_sig = cell->getPort("\\B");
 | 
			
		||||
						b_padded_width = y_width;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
 | 
			
		||||
				auto a_sig = cell->getPort("\\A");
 | 
			
		||||
 | 
			
		||||
				if (cell->parameters.at("\\A_SIGNED").as_bool()  & (cell->type == "$shr")) {
 | 
			
		||||
					a_expr = "asUInt(" + a_expr + ")";
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				string primop;
 | 
			
		||||
                                bool always_uint = false;
 | 
			
		||||
				bool always_uint = false;
 | 
			
		||||
				if (cell->type == "$add") primop = "add";
 | 
			
		||||
				if (cell->type == "$sub") primop = "sub";
 | 
			
		||||
				if (cell->type == "$mul") primop = "mul";
 | 
			
		||||
				if (cell->type == "$div") primop = "div";
 | 
			
		||||
				if (cell->type == "$mod") primop = "rem";
 | 
			
		||||
				if (cell->type == "$and") {
 | 
			
		||||
				else if (cell->type == "$sub") primop = "sub";
 | 
			
		||||
				else if (cell->type == "$mul") primop = "mul";
 | 
			
		||||
				else if (cell->type == "$div") primop = "div";
 | 
			
		||||
				else if (cell->type == "$mod") primop = "rem";
 | 
			
		||||
				else if (cell->type == "$and") {
 | 
			
		||||
                                        primop = "and";
 | 
			
		||||
                                        always_uint = true;
 | 
			
		||||
                                }
 | 
			
		||||
				if (cell->type == "$or" ) {
 | 
			
		||||
				else if (cell->type == "$or" ) {
 | 
			
		||||
                                        primop =  "or";
 | 
			
		||||
                                        always_uint = true;
 | 
			
		||||
                                }
 | 
			
		||||
				if (cell->type == "$xor") {
 | 
			
		||||
				else if (cell->type == "$xor") {
 | 
			
		||||
                                        primop = "xor";
 | 
			
		||||
                                        always_uint = true;
 | 
			
		||||
                                }
 | 
			
		||||
				if ((cell->type == "$eq") | (cell->type == "$eqx")) {
 | 
			
		||||
				else if ((cell->type == "$eq") | (cell->type == "$eqx")) {
 | 
			
		||||
                                        primop = "eq";
 | 
			
		||||
                                        always_uint = true;
 | 
			
		||||
                                }
 | 
			
		||||
				if ((cell->type == "$ne") | (cell->type == "$nex")) {
 | 
			
		||||
				else if ((cell->type == "$ne") | (cell->type == "$nex")) {
 | 
			
		||||
                                        primop = "neq";
 | 
			
		||||
                                        always_uint = true;
 | 
			
		||||
                                }
 | 
			
		||||
				if (cell->type == "$gt") {
 | 
			
		||||
				else if (cell->type == "$gt") {
 | 
			
		||||
                                        primop = "gt";
 | 
			
		||||
                                        always_uint = true;
 | 
			
		||||
                                }
 | 
			
		||||
				if (cell->type == "$ge") {
 | 
			
		||||
				else if (cell->type == "$ge") {
 | 
			
		||||
                                        primop = "geq";
 | 
			
		||||
                                        always_uint = true;
 | 
			
		||||
                                }
 | 
			
		||||
				if (cell->type == "$lt") {
 | 
			
		||||
				else if (cell->type == "$lt") {
 | 
			
		||||
                                        primop = "lt";
 | 
			
		||||
                                        always_uint = true;
 | 
			
		||||
                                }
 | 
			
		||||
				if (cell->type == "$le") {
 | 
			
		||||
				else if (cell->type == "$le") {
 | 
			
		||||
                                        primop = "leq";
 | 
			
		||||
                                        always_uint = true;
 | 
			
		||||
                                }
 | 
			
		||||
				if ((cell->type == "$shl") | (cell->type == "$sshl")) primop = "dshl";
 | 
			
		||||
				if ((cell->type == "$shr") | (cell->type == "$sshr")) primop = "dshr";
 | 
			
		||||
				if ((cell->type == "$logic_and")) {
 | 
			
		||||
				else if ((cell->type == "$shl") | (cell->type == "$sshl")) {
 | 
			
		||||
					// FIRRTL will widen the result (y) by the amount of the shift.
 | 
			
		||||
					// We'll need to offset this by extracting the un-widened portion as Verilog would do.
 | 
			
		||||
					extract_y_bits = true;
 | 
			
		||||
					// Is the shift amount constant?
 | 
			
		||||
					auto b_sig = cell->getPort("\\B");
 | 
			
		||||
					if (b_sig.is_fully_const()) {
 | 
			
		||||
						primop = "shl";
 | 
			
		||||
					} else {
 | 
			
		||||
						primop = "dshl";
 | 
			
		||||
						// Convert from FIRRTL left shift semantics.
 | 
			
		||||
						b_expr = gen_dshl(b_expr, b_padded_width);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				else if ((cell->type == "$shr") | (cell->type == "$sshr")) {
 | 
			
		||||
					// We don't need to extract a specific range of bits.
 | 
			
		||||
					extract_y_bits = false;
 | 
			
		||||
					// Is the shift amount constant?
 | 
			
		||||
					auto b_sig = cell->getPort("\\B");
 | 
			
		||||
					if (b_sig.is_fully_const()) {
 | 
			
		||||
						primop = "shr";
 | 
			
		||||
					} else {
 | 
			
		||||
						primop = "dshr";
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				else if ((cell->type == "$logic_and")) {
 | 
			
		||||
                                        primop = "and";
 | 
			
		||||
                                        a_expr = "neq(" + a_expr + ", UInt(0))";
 | 
			
		||||
                                        b_expr = "neq(" + b_expr + ", UInt(0))";
 | 
			
		||||
                                        always_uint = true;
 | 
			
		||||
                                }
 | 
			
		||||
				if ((cell->type == "$logic_or")) {
 | 
			
		||||
				else if ((cell->type == "$logic_or")) {
 | 
			
		||||
                                        primop = "or";
 | 
			
		||||
                                        a_expr = "neq(" + a_expr + ", UInt(0))";
 | 
			
		||||
                                        b_expr = "neq(" + b_expr + ", UInt(0))";
 | 
			
		||||
| 
						 | 
				
			
			@ -388,6 +467,11 @@ struct FirrtlWorker
 | 
			
		|||
 | 
			
		||||
				string expr = stringf("%s(%s, %s)", primop.c_str(), a_expr.c_str(), b_expr.c_str());
 | 
			
		||||
 | 
			
		||||
				// Deal with FIRRTL's "shift widens" semantics
 | 
			
		||||
				if (extract_y_bits) {
 | 
			
		||||
					expr = stringf("bits(%s, %d, 0)", expr.c_str(), y_width - 1);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if ((is_signed && !always_uint) || cell->type.in("$sub"))
 | 
			
		||||
					expr = stringf("asUInt(%s)", expr.c_str());
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -513,7 +597,65 @@ struct FirrtlWorker
 | 
			
		|||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			log_error("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
 | 
			
		||||
			// This may be a parameterized module - paramod.
 | 
			
		||||
			if (cell->type.substr(0, 8) == "$paramod")
 | 
			
		||||
			{
 | 
			
		||||
				process_instance(cell, wire_exprs);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (cell->type == "$shiftx") {
 | 
			
		||||
				// assign y = a[b +: y_width];
 | 
			
		||||
				// We'll extract the correct bits as part of the primop.
 | 
			
		||||
 | 
			
		||||
				string y_id = make_id(cell->name);
 | 
			
		||||
				int y_width =  cell->parameters.at("\\Y_WIDTH").as_int();
 | 
			
		||||
				string a_expr = make_expr(cell->getPort("\\A"));
 | 
			
		||||
				// Get the initial bit selector
 | 
			
		||||
				string b_expr = make_expr(cell->getPort("\\B"));
 | 
			
		||||
				wire_decls.push_back(stringf("    wire %s: UInt<%d>\n", y_id.c_str(), y_width));
 | 
			
		||||
 | 
			
		||||
				if (cell->getParam("\\B_SIGNED").as_bool()) {
 | 
			
		||||
					// Use validif to constrain the selection (test the sign bit)
 | 
			
		||||
					auto b_string = b_expr.c_str();
 | 
			
		||||
					int b_sign = cell->parameters.at("\\B_WIDTH").as_int() - 1;
 | 
			
		||||
					b_expr = stringf("validif(not(bits(%s, %d, %d)), %s)", b_string, b_sign, b_sign, b_string);
 | 
			
		||||
				}
 | 
			
		||||
				string expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_expr.c_str());
 | 
			
		||||
 | 
			
		||||
				cell_exprs.push_back(stringf("    %s <= %s\n", y_id.c_str(), expr.c_str()));
 | 
			
		||||
				register_reverse_wire_map(y_id, cell->getPort("\\Y"));
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (cell->type == "$shift") {
 | 
			
		||||
				// assign y = a >> b;
 | 
			
		||||
				//  where b may be negative
 | 
			
		||||
 | 
			
		||||
				string y_id = make_id(cell->name);
 | 
			
		||||
				int y_width =  cell->parameters.at("\\Y_WIDTH").as_int();
 | 
			
		||||
				string a_expr = make_expr(cell->getPort("\\A"));
 | 
			
		||||
				string b_expr = make_expr(cell->getPort("\\B"));
 | 
			
		||||
				auto b_string = b_expr.c_str();
 | 
			
		||||
				int b_padded_width = cell->parameters.at("\\B_WIDTH").as_int();
 | 
			
		||||
				string expr;
 | 
			
		||||
				wire_decls.push_back(stringf("    wire %s: UInt<%d>\n", y_id.c_str(), y_width));
 | 
			
		||||
 | 
			
		||||
				if (cell->getParam("\\B_SIGNED").as_bool()) {
 | 
			
		||||
					// We generate a left or right shift based on the sign of b.
 | 
			
		||||
					std::string dshl = stringf("bits(dshl(%s, %s), 0, %d)", a_expr.c_str(), gen_dshl(b_expr, b_padded_width).c_str(), y_width);
 | 
			
		||||
					std::string dshr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string);
 | 
			
		||||
					expr = stringf("mux(%s < 0, %s, %s)",
 | 
			
		||||
									 b_string,
 | 
			
		||||
									 dshl.c_str(),
 | 
			
		||||
									 dshr.c_str()
 | 
			
		||||
									 );
 | 
			
		||||
				} else {
 | 
			
		||||
					expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string);
 | 
			
		||||
				}
 | 
			
		||||
				cell_exprs.push_back(stringf("    %s <= %s\n", y_id.c_str(), expr.c_str()));
 | 
			
		||||
				register_reverse_wire_map(y_id, cell->getPort("\\Y"));
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			log_warning("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (auto conn : module->connections())
 | 
			
		||||
| 
						 | 
				
			
			@ -629,38 +771,53 @@ struct FirrtlBackend : public Backend {
 | 
			
		|||
		log("    write_firrtl [options] [filename]\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("Write a FIRRTL netlist of the current design.\n");
 | 
			
		||||
		log("The following commands are executed by this command:\n");
 | 
			
		||||
		log("        pmuxtree\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
	}
 | 
			
		||||
	void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
 | 
			
		||||
	{
 | 
			
		||||
		size_t argidx;
 | 
			
		||||
		for (argidx = 1; argidx < args.size(); argidx++)
 | 
			
		||||
		{
 | 
			
		||||
			// if (args[argidx] == "-aig") {
 | 
			
		||||
			// 	aig_mode = true;
 | 
			
		||||
			// 	continue;
 | 
			
		||||
			// }
 | 
			
		||||
			break;
 | 
			
		||||
		size_t argidx = args.size();	// We aren't expecting any arguments.
 | 
			
		||||
 | 
			
		||||
		// If we weren't explicitly passed a filename, use the last argument (if it isn't a flag).
 | 
			
		||||
		if (filename == "") {
 | 
			
		||||
			if (argidx > 0 && args[argidx - 1][0] != '-') {
 | 
			
		||||
				// extra_args and friends need to see this argument.
 | 
			
		||||
				argidx -= 1;
 | 
			
		||||
				filename = args[argidx];
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		extra_args(f, filename, args, argidx);
 | 
			
		||||
 | 
			
		||||
		if (!design->full_selection())
 | 
			
		||||
			log_cmd_error("This command only operates on fully selected designs!\n");
 | 
			
		||||
 | 
			
		||||
		log_header(design, "Executing FIRRTL backend.\n");
 | 
			
		||||
		log_push();
 | 
			
		||||
 | 
			
		||||
		Module *top = design->top_module();
 | 
			
		||||
 | 
			
		||||
		if (top == nullptr)
 | 
			
		||||
			log_error("No top module found!\n");
 | 
			
		||||
		Pass::call(design, stringf("pmuxtree"));
 | 
			
		||||
 | 
			
		||||
		namecache.clear();
 | 
			
		||||
		autoid_counter = 0;
 | 
			
		||||
 | 
			
		||||
		// Get the top module, or a reasonable facsimile - we need something for the circuit name.
 | 
			
		||||
		Module *top = design->top_module();
 | 
			
		||||
		Module *last = nullptr;
 | 
			
		||||
		// Generate module and wire names.
 | 
			
		||||
		for (auto module : design->modules()) {
 | 
			
		||||
			make_id(module->name);
 | 
			
		||||
			last = module;
 | 
			
		||||
			if (top == nullptr && module->get_bool_attribute("\\top")) {
 | 
			
		||||
				top = module;
 | 
			
		||||
			}
 | 
			
		||||
			for (auto wire : module->wires())
 | 
			
		||||
				if (wire->port_id)
 | 
			
		||||
					make_id(wire->name);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (top == nullptr)
 | 
			
		||||
			top = last;
 | 
			
		||||
 | 
			
		||||
		*f << stringf("circuit %s:\n", make_id(top->name));
 | 
			
		||||
 | 
			
		||||
		for (auto module : design->modules())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1310,6 +1310,15 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (reg_ct.count(cell->type) && cell->hasPort("\\Q")) {
 | 
			
		||||
		std::stringstream ss;
 | 
			
		||||
		dump_reg_init(ss, cell->getPort("\\Q"));
 | 
			
		||||
		if (!ss.str().empty()) {
 | 
			
		||||
			f << stringf("%sinitial %s.Q", indent.c_str(), cell_name.c_str());
 | 
			
		||||
			f << ss.str();
 | 
			
		||||
			f << ";\n";
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -644,7 +644,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
 | 
			
		|||
				while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
 | 
			
		||||
				if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
 | 
			
		||||
					log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
 | 
			
		||||
				this_width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
 | 
			
		||||
				this_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
 | 
			
		||||
				delete left_at_zero_ast;
 | 
			
		||||
				delete right_at_zero_ast;
 | 
			
		||||
			} else
 | 
			
		||||
| 
						 | 
				
			
			@ -792,7 +792,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
 | 
			
		|||
	// everything should have been handled above -> print error if not.
 | 
			
		||||
	default:
 | 
			
		||||
		for (auto f : log_files)
 | 
			
		||||
			current_ast->dumpAst(f, "verilog-ast> ");
 | 
			
		||||
			current_ast_mod->dumpAst(f, "verilog-ast> ");
 | 
			
		||||
		log_file_error(filename, linenum, "Don't know how to detect sign and width for %s node!\n", type2str(type).c_str());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1034,7 +1034,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
 | 
			
		|||
					while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
 | 
			
		||||
					if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
 | 
			
		||||
						log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
 | 
			
		||||
					int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
 | 
			
		||||
					int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
 | 
			
		||||
					AstNode *fake_ast = new AstNode(AST_NONE, clone(), children[0]->children.size() >= 2 ?
 | 
			
		||||
							children[0]->children[1]->clone() : children[0]->children[0]->clone());
 | 
			
		||||
					fake_ast->children[0]->delete_children();
 | 
			
		||||
| 
						 | 
				
			
			@ -1565,7 +1565,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
 | 
			
		|||
	// everything should have been handled above -> print error if not.
 | 
			
		||||
	default:
 | 
			
		||||
		for (auto f : log_files)
 | 
			
		||||
			current_ast->dumpAst(f, "verilog-ast> ");
 | 
			
		||||
			current_ast_mod->dumpAst(f, "verilog-ast> ");
 | 
			
		||||
		type_name = type2str(type);
 | 
			
		||||
		log_file_error(filename, linenum, "Don't know how to generate RTLIL code for %s node!\n", type_name.c_str());
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -642,6 +642,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
 | 
			
		|||
	// (iterate by index as e.g. auto wires can add new children in the process)
 | 
			
		||||
	for (size_t i = 0; i < children.size(); i++) {
 | 
			
		||||
		bool did_something_here = true;
 | 
			
		||||
		bool backup_flag_autowire = flag_autowire;
 | 
			
		||||
		if ((type == AST_GENFOR || type == AST_FOR) && i >= 3)
 | 
			
		||||
			break;
 | 
			
		||||
		if ((type == AST_GENIF || type == AST_GENCASE) && i >= 1)
 | 
			
		||||
| 
						 | 
				
			
			@ -652,6 +653,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
 | 
			
		|||
			break;
 | 
			
		||||
		if (type == AST_PREFIX && i >= 1)
 | 
			
		||||
			break;
 | 
			
		||||
		if (type == AST_DEFPARAM && i == 0)
 | 
			
		||||
			flag_autowire = true;
 | 
			
		||||
		while (did_something_here && i < children.size()) {
 | 
			
		||||
			bool const_fold_here = const_fold, in_lvalue_here = in_lvalue;
 | 
			
		||||
			int width_hint_here = width_hint;
 | 
			
		||||
| 
						 | 
				
			
			@ -686,6 +689,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
 | 
			
		|||
			children.erase(children.begin() + (i--));
 | 
			
		||||
			did_something = true;
 | 
			
		||||
		}
 | 
			
		||||
		flag_autowire = backup_flag_autowire;
 | 
			
		||||
	}
 | 
			
		||||
	for (auto &attr : attributes) {
 | 
			
		||||
		while (attr.second->simplify(true, false, false, stage, -1, false, true))
 | 
			
		||||
| 
						 | 
				
			
			@ -934,12 +938,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (current_scope.count(str) == 0) {
 | 
			
		||||
			// log_warning("Creating auto-wire `%s' in module `%s'.\n", str.c_str(), current_ast_mod->str.c_str());
 | 
			
		||||
			AstNode *auto_wire = new AstNode(AST_AUTOWIRE);
 | 
			
		||||
			auto_wire->str = str;
 | 
			
		||||
			current_ast_mod->children.push_back(auto_wire);
 | 
			
		||||
			current_scope[str] = auto_wire;
 | 
			
		||||
			did_something = true;
 | 
			
		||||
			if (flag_autowire) {
 | 
			
		||||
				AstNode *auto_wire = new AstNode(AST_AUTOWIRE);
 | 
			
		||||
				auto_wire->str = str;
 | 
			
		||||
				current_ast_mod->children.push_back(auto_wire);
 | 
			
		||||
				current_scope[str] = auto_wire;
 | 
			
		||||
				did_something = true;
 | 
			
		||||
			} else {
 | 
			
		||||
				log_file_error(filename, linenum, "Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str());
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (id2ast != current_scope[str]) {
 | 
			
		||||
			id2ast = current_scope[str];
 | 
			
		||||
| 
						 | 
				
			
			@ -1689,7 +1696,7 @@ skip_dynamic_range_lvalue_expansion:;
 | 
			
		|||
				while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
 | 
			
		||||
				if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
 | 
			
		||||
					log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
 | 
			
		||||
				int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
 | 
			
		||||
				int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
 | 
			
		||||
 | 
			
		||||
				assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
 | 
			
		||||
						new AstNode(AST_SHIFT_LEFT, children[1]->clone(), offset_ast->clone()));
 | 
			
		||||
| 
						 | 
				
			
			@ -1778,7 +1785,7 @@ skip_dynamic_range_lvalue_expansion:;
 | 
			
		|||
 | 
			
		||||
			if (str == "\\$past")
 | 
			
		||||
			{
 | 
			
		||||
				if (width_hint <= 0)
 | 
			
		||||
				if (width_hint < 0)
 | 
			
		||||
					goto replace_fcall_later;
 | 
			
		||||
 | 
			
		||||
				int num_steps = 1;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1920,6 +1920,10 @@ struct VerificPass : public Pass {
 | 
			
		|||
			// WARNING: instantiating unknown module 'XYZ' (VERI-1063)
 | 
			
		||||
			Message::SetMessageType("VERI-1063", VERIFIC_ERROR);
 | 
			
		||||
 | 
			
		||||
#ifndef DB_PRESERVE_INITIAL_VALUE
 | 
			
		||||
#  warning Verific was built without DB_PRESERVE_INITIAL_VALUE.
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
			set_verific_global_flags = false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -179,6 +179,7 @@ int main(int argc, char **argv)
 | 
			
		|||
{
 | 
			
		||||
	std::string frontend_command = "auto";
 | 
			
		||||
	std::string backend_command = "auto";
 | 
			
		||||
	std::vector<std::string> vlog_defines;
 | 
			
		||||
	std::vector<std::string> passes_commands;
 | 
			
		||||
	std::vector<std::string> plugin_filenames;
 | 
			
		||||
	std::string output_filename = "";
 | 
			
		||||
| 
						 | 
				
			
			@ -268,7 +269,10 @@ int main(int argc, char **argv)
 | 
			
		|||
		printf("    -A\n");
 | 
			
		||||
		printf("        will call abort() at the end of the script. for debugging\n");
 | 
			
		||||
		printf("\n");
 | 
			
		||||
		printf("    -D <header_id>[:<filename>]\n");
 | 
			
		||||
		printf("    -D <macro>[=<value>]\n");
 | 
			
		||||
		printf("        set the specified Verilog define (via \"read -define\")\n");
 | 
			
		||||
		printf("\n");
 | 
			
		||||
		printf("    -P <header_id>[:<filename>]\n");
 | 
			
		||||
		printf("        dump the design when printing the specified log header to a file.\n");
 | 
			
		||||
		printf("        yosys_dump_<header_id>.il is used as filename if none is specified.\n");
 | 
			
		||||
		printf("        Use 'ALL' as <header_id> to dump at every header.\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -307,7 +311,7 @@ int main(int argc, char **argv)
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	int opt;
 | 
			
		||||
	while ((opt = getopt(argc, argv, "MXAQTVSm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:E:")) != -1)
 | 
			
		||||
	while ((opt = getopt(argc, argv, "MXAQTVSm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:")) != -1)
 | 
			
		||||
	{
 | 
			
		||||
		switch (opt)
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -408,6 +412,9 @@ int main(int argc, char **argv)
 | 
			
		|||
					std::regex_constants::egrep));
 | 
			
		||||
			break;
 | 
			
		||||
		case 'D':
 | 
			
		||||
			vlog_defines.push_back(optarg);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'P':
 | 
			
		||||
			{
 | 
			
		||||
				auto args = split_tokens(optarg, ":");
 | 
			
		||||
				if (!args.empty() && args[0] == "ALL") {
 | 
			
		||||
| 
						 | 
				
			
			@ -473,6 +480,13 @@ int main(int argc, char **argv)
 | 
			
		|||
		shell(yosys_design);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!vlog_defines.empty()) {
 | 
			
		||||
		std::string vdef_cmd = "read -define";
 | 
			
		||||
		for (auto vdef : vlog_defines)
 | 
			
		||||
			vdef_cmd += " " + vdef;
 | 
			
		||||
		run_pass(vdef_cmd);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while (optind < argc)
 | 
			
		||||
		run_frontend(argv[optind++], frontend_command, output_filename == "-" ? &backend_command : NULL);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2410,6 +2410,9 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed)
 | 
			
		|||
	if (connections_.count("\\Y"))
 | 
			
		||||
		parameters["\\Y_WIDTH"] = GetSize(connections_["\\Y"]);
 | 
			
		||||
 | 
			
		||||
	if (connections_.count("\\Q"))
 | 
			
		||||
		parameters["\\WIDTH"] = GetSize(connections_["\\Q"]);
 | 
			
		||||
 | 
			
		||||
	check();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -143,6 +143,9 @@ struct SetundefPass : public Pass {
 | 
			
		|||
		log("    -init\n");
 | 
			
		||||
		log("        also create/update init values for flip-flops\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -params\n");
 | 
			
		||||
		log("        replace undef in cell parameters\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
	}
 | 
			
		||||
	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -150,6 +153,7 @@ struct SetundefPass : public Pass {
 | 
			
		|||
		bool undriven_mode = false;
 | 
			
		||||
		bool expose_mode = false;
 | 
			
		||||
		bool init_mode = false;
 | 
			
		||||
		bool params_mode = false;
 | 
			
		||||
		SetundefWorker worker;
 | 
			
		||||
 | 
			
		||||
		log_header(design, "Executing SETUNDEF pass (replace undef values with defined constants).\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -199,6 +203,10 @@ struct SetundefPass : public Pass {
 | 
			
		|||
				init_mode = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-params") {
 | 
			
		||||
				params_mode = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) {
 | 
			
		||||
				got_value = true;
 | 
			
		||||
				worker.next_bit_mode = MODE_RANDOM;
 | 
			
		||||
| 
						 | 
				
			
			@ -228,6 +236,18 @@ struct SetundefPass : public Pass {
 | 
			
		|||
 | 
			
		||||
		for (auto module : design->selected_modules())
 | 
			
		||||
		{
 | 
			
		||||
			if (params_mode)
 | 
			
		||||
			{
 | 
			
		||||
				for (auto *cell : module->selected_cells()) {
 | 
			
		||||
					for (auto ¶meter : cell->parameters) {
 | 
			
		||||
						for (auto &bit : parameter.second.bits) {
 | 
			
		||||
							if (bit > RTLIL::State::S1)
 | 
			
		||||
								bit = worker.next_bit();
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (undriven_mode)
 | 
			
		||||
			{
 | 
			
		||||
				if (!module->processes.empty())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -140,6 +140,23 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Return the "basic" type for an array item.
 | 
			
		||||
std::string basic_cell_type(const std::string celltype, int pos[3] = nullptr) {
 | 
			
		||||
	std::string basicType = celltype;
 | 
			
		||||
	if (celltype.substr(0, 7) == "$array:") {
 | 
			
		||||
		int pos_idx = celltype.find_first_of(':');
 | 
			
		||||
		int pos_num = celltype.find_first_of(':', pos_idx + 1);
 | 
			
		||||
		int pos_type = celltype.find_first_of(':', pos_num + 1);
 | 
			
		||||
		basicType = celltype.substr(pos_type + 1);
 | 
			
		||||
		if (pos != nullptr) {
 | 
			
		||||
			pos[0] = pos_idx;
 | 
			
		||||
			pos[1] = pos_num;
 | 
			
		||||
			pos[2] = pos_type;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return basicType;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, std::vector<std::string> &libdirs)
 | 
			
		||||
{
 | 
			
		||||
	bool did_something = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -178,9 +195,11 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
 | 
			
		|||
		std::vector<RTLIL::SigSpec> connections_to_add_signal;
 | 
			
		||||
 | 
			
		||||
		if (cell->type.substr(0, 7) == "$array:") {
 | 
			
		||||
			int pos_idx = cell->type.str().find_first_of(':');
 | 
			
		||||
			int pos_num = cell->type.str().find_first_of(':', pos_idx + 1);
 | 
			
		||||
			int pos_type = cell->type.str().find_first_of(':', pos_num + 1);
 | 
			
		||||
			int pos[3];
 | 
			
		||||
			basic_cell_type(cell->type.str(), pos);
 | 
			
		||||
			int pos_idx = pos[0];
 | 
			
		||||
			int pos_num = pos[1];
 | 
			
		||||
			int pos_type = pos[2];
 | 
			
		||||
			int idx = atoi(cell->type.str().substr(pos_idx + 1, pos_num).c_str());
 | 
			
		||||
			int num = atoi(cell->type.str().substr(pos_num + 1, pos_type).c_str());
 | 
			
		||||
			array_cells[cell] = std::pair<int, int>(idx, num);
 | 
			
		||||
| 
						 | 
				
			
			@ -439,10 +458,7 @@ void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*, IdString::
 | 
			
		|||
	for (auto cell : mod->cells()) {
 | 
			
		||||
		std::string celltype = cell->type.str();
 | 
			
		||||
		if (celltype.substr(0, 7) == "$array:") {
 | 
			
		||||
			int pos_idx = celltype.find_first_of(':');
 | 
			
		||||
			int pos_num = celltype.find_first_of(':', pos_idx + 1);
 | 
			
		||||
			int pos_type = celltype.find_first_of(':', pos_num + 1);
 | 
			
		||||
			celltype = celltype.substr(pos_type + 1);
 | 
			
		||||
			celltype = basic_cell_type(celltype);
 | 
			
		||||
		}
 | 
			
		||||
		if (design->module(celltype))
 | 
			
		||||
			hierarchy_worker(design, used, design->module(celltype), indent+4);
 | 
			
		||||
| 
						 | 
				
			
			@ -502,9 +518,19 @@ int find_top_mod_score(Design *design, Module *module, dict<Module*, int> &db)
 | 
			
		|||
	if (db.count(module) == 0) {
 | 
			
		||||
		int score = 0;
 | 
			
		||||
		db[module] = 0;
 | 
			
		||||
		for (auto cell : module->cells())
 | 
			
		||||
			if (design->module(cell->type))
 | 
			
		||||
				score = max(score, find_top_mod_score(design, design->module(cell->type), db) + 1);
 | 
			
		||||
		for (auto cell : module->cells()) {
 | 
			
		||||
			std::string celltype = cell->type.str();
 | 
			
		||||
			// Is this an array instance
 | 
			
		||||
			if (celltype.substr(0, 7) == "$array:") {
 | 
			
		||||
				celltype = basic_cell_type(celltype);
 | 
			
		||||
			}
 | 
			
		||||
			// Is this cell a module instance?
 | 
			
		||||
			auto instModule = design->module(celltype);
 | 
			
		||||
			// If there is no instance for this, issue a warning.
 | 
			
		||||
			if (instModule != nullptr) {
 | 
			
		||||
				score = max(score, find_top_mod_score(design, instModule, db) + 1);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		db[module] = score;
 | 
			
		||||
	}
 | 
			
		||||
	return db.at(module);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -174,8 +174,6 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
 | 
			
		|||
			cell->unsetParam("\\CLR_POLARITY");
 | 
			
		||||
			cell->unsetPort("\\SET");
 | 
			
		||||
			cell->unsetPort("\\CLR");
 | 
			
		||||
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -186,11 +184,12 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
 | 
			
		|||
			cell->unsetParam("\\CLR_POLARITY");
 | 
			
		||||
			cell->unsetPort("\\SET");
 | 
			
		||||
			cell->unsetPort("\\CLR");
 | 
			
		||||
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
 | 
			
		||||
	if (!hasreset)
 | 
			
		||||
	{
 | 
			
		||||
		IdString new_type;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -207,8 +206,10 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
 | 
			
		|||
		cell->unsetPort("\\S");
 | 
			
		||||
		cell->unsetPort("\\R");
 | 
			
		||||
 | 
			
		||||
		return did_something;
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return did_something;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,7 +38,8 @@ struct WreduceConfig
 | 
			
		|||
			"$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
 | 
			
		||||
			"$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
 | 
			
		||||
			"$add", "$sub", "$mul", // "$div", "$mod", "$pow",
 | 
			
		||||
			"$mux", "$pmux"
 | 
			
		||||
			"$mux", "$pmux",
 | 
			
		||||
			"$dff", "$adff"
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -134,6 +135,78 @@ struct WreduceWorker
 | 
			
		|||
		module->connect(sig_y.extract(n_kept, n_removed), sig_removed);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void run_cell_dff(Cell *cell)
 | 
			
		||||
	{
 | 
			
		||||
		// Reduce size of FF if inputs are just sign/zero extended or output bit is not used
 | 
			
		||||
 | 
			
		||||
		SigSpec sig_d = mi.sigmap(cell->getPort("\\D"));
 | 
			
		||||
		SigSpec sig_q = mi.sigmap(cell->getPort("\\Q"));
 | 
			
		||||
 | 
			
		||||
		int width_before = GetSize(sig_q);
 | 
			
		||||
 | 
			
		||||
		if (width_before == 0)
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		bool zero_ext = sig_d[GetSize(sig_d)-1] == State::S0;
 | 
			
		||||
		bool sign_ext = !zero_ext;
 | 
			
		||||
 | 
			
		||||
		for (int i = GetSize(sig_q)-1; i >= 0; i--)
 | 
			
		||||
		{
 | 
			
		||||
			if (zero_ext && sig_d[i] == State::S0) {
 | 
			
		||||
				module->connect(sig_q[i], State::S0);
 | 
			
		||||
				sig_d.remove(i);
 | 
			
		||||
				sig_q.remove(i);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1]) {
 | 
			
		||||
				module->connect(sig_q[i], sig_q[i-1]);
 | 
			
		||||
				sig_d.remove(i);
 | 
			
		||||
				sig_q.remove(i);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			auto info = mi.query(sig_q[i]);
 | 
			
		||||
			if (!info->is_output && GetSize(info->ports) <= 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) {
 | 
			
		||||
				sig_d.remove(i);
 | 
			
		||||
				sig_q.remove(i);
 | 
			
		||||
				zero_ext = false;
 | 
			
		||||
				sign_ext = false;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (width_before == GetSize(sig_q))
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		if (GetSize(sig_q) == 0) {
 | 
			
		||||
			log("Removed cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type));
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		log("Removed top %d bits (of %d) from mux cell %s.%s (%s).\n", width_before - GetSize(sig_q), width_before,
 | 
			
		||||
				log_id(module), log_id(cell), log_id(cell->type));
 | 
			
		||||
 | 
			
		||||
		for (auto bit : sig_d)
 | 
			
		||||
			work_queue_bits.insert(bit);
 | 
			
		||||
 | 
			
		||||
		for (auto bit : sig_q)
 | 
			
		||||
			work_queue_bits.insert(bit);
 | 
			
		||||
 | 
			
		||||
		// Narrow ARST_VALUE parameter to new size.
 | 
			
		||||
		if (cell->parameters.count("\\ARST_VALUE")) {
 | 
			
		||||
			Const arst_value = cell->getParam("\\ARST_VALUE");
 | 
			
		||||
			arst_value.bits.resize(GetSize(sig_q));
 | 
			
		||||
			cell->setParam("\\ARST_VALUE", arst_value);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cell->setPort("\\D", sig_d);
 | 
			
		||||
		cell->setPort("\\Q", sig_q);
 | 
			
		||||
		cell->fixup_parameters();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void run_reduce_inport(Cell *cell, char port, int max_port_size, bool &port_signed, bool &did_something)
 | 
			
		||||
	{
 | 
			
		||||
		port_signed = cell->getParam(stringf("\\%c_SIGNED", port)).as_bool();
 | 
			
		||||
| 
						 | 
				
			
			@ -176,6 +249,9 @@ struct WreduceWorker
 | 
			
		|||
		if (cell->type.in("$mux", "$pmux"))
 | 
			
		||||
			return run_cell_mux(cell);
 | 
			
		||||
 | 
			
		||||
		if (cell->type.in("$dff", "$adff"))
 | 
			
		||||
			return run_cell_dff(cell);
 | 
			
		||||
 | 
			
		||||
		SigSpec sig = mi.sigmap(cell->getPort("\\Y"));
 | 
			
		||||
 | 
			
		||||
		if (sig.has_const())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										1
									
								
								passes/pmgen/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								passes/pmgen/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
/ice40_dsp_pm.h
 | 
			
		||||
							
								
								
									
										8
									
								
								passes/pmgen/Makefile.inc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								passes/pmgen/Makefile.inc
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
OBJS += passes/pmgen/ice40_dsp.o
 | 
			
		||||
 | 
			
		||||
passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h
 | 
			
		||||
EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h
 | 
			
		||||
.SECONDARY: passes/pmgen/ice40_dsp_pm.h
 | 
			
		||||
 | 
			
		||||
passes/pmgen/ice40_dsp_pm.h: passes/pmgen/ice40_dsp.pmg passes/pmgen/pmgen.py
 | 
			
		||||
	$(P) cd passes/pmgen && python3 pmgen.py ice40_dsp
 | 
			
		||||
							
								
								
									
										224
									
								
								passes/pmgen/README.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								passes/pmgen/README.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,224 @@
 | 
			
		|||
Pattern Matcher Generator
 | 
			
		||||
=========================
 | 
			
		||||
 | 
			
		||||
The program `pmgen.py` reads a `.pmg` (Pattern Matcher Generator) file and
 | 
			
		||||
writes a header-only C++ library that implements that pattern matcher.
 | 
			
		||||
 | 
			
		||||
The "patterns" in this context are subgraphs in a Yosys RTLIL netlist.
 | 
			
		||||
 | 
			
		||||
The algorithm used in the generated pattern matcher is a simple recursive
 | 
			
		||||
search with backtracking. It is left to the author of the `.pmg` file to
 | 
			
		||||
determine an efficient cell order for the search that allows for maximum
 | 
			
		||||
use of indices and early backtracking.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
API of Generated Matcher
 | 
			
		||||
========================
 | 
			
		||||
 | 
			
		||||
When `pmgen.py` reads a `foobar.pmg` file, it writes `foobar_pm.h` containing
 | 
			
		||||
a class `foobar_pm`. That class is instanciated with an RTLIL module and a
 | 
			
		||||
list of cells from that module:
 | 
			
		||||
 | 
			
		||||
    foobar_pm pm(module, module->selected_cells());
 | 
			
		||||
 | 
			
		||||
The caller must make sure that none of the cells in the 2nd argument are
 | 
			
		||||
deleted for as long as the patter matcher instance is used.
 | 
			
		||||
 | 
			
		||||
At any time it is possible to disable cells, preventing them from showing
 | 
			
		||||
up in any future matches:
 | 
			
		||||
 | 
			
		||||
    pm.blacklist(some_cell);
 | 
			
		||||
 | 
			
		||||
The `.run(callback_function)` method searches for all matches and calls the
 | 
			
		||||
callback function for each found match:
 | 
			
		||||
 | 
			
		||||
    pm.run([&](){
 | 
			
		||||
        log("found matching 'foo' cell: %s\n", log_id(pm.st.foo));
 | 
			
		||||
        log("          with 'bar' cell: %s\n", log_id(pm.st.bar));
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
The `.pmg` file declares matcher state variables that are accessible via the
 | 
			
		||||
`.st.<state_name>` members. (The `.st` member is of type `foobar_pm::state_t`.)
 | 
			
		||||
 | 
			
		||||
Similarly the `.pmg` file declares user data variables that become members of
 | 
			
		||||
`.ud`, a struct of type `foobar_pm::udata_t`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
The .pmg File Format
 | 
			
		||||
====================
 | 
			
		||||
 | 
			
		||||
The `.pmg` file format is a simple line-based file format. For the most part
 | 
			
		||||
lines consist of whitespace-separated tokens.
 | 
			
		||||
 | 
			
		||||
Lines in `.pmg` files starting with `//` are comments.
 | 
			
		||||
 | 
			
		||||
Declaring state variables
 | 
			
		||||
-------------------------
 | 
			
		||||
 | 
			
		||||
One or more state variables can be declared using the `state` statement,
 | 
			
		||||
followed by a C++ type in angle brackets, followed by a whitespace separated
 | 
			
		||||
list of variable names. For example:
 | 
			
		||||
 | 
			
		||||
    state <bool> flag1 flag2 happy big
 | 
			
		||||
    state <SigSpec> sigA sigB sigY
 | 
			
		||||
 | 
			
		||||
State variables are automatically managed by the generated backtracking algorithm
 | 
			
		||||
and saved and restored as needed.
 | 
			
		||||
 | 
			
		||||
They are automatically initialized to the default constructed value of their type
 | 
			
		||||
when `.run(callback_function)` is called.
 | 
			
		||||
 | 
			
		||||
Declaring udata variables
 | 
			
		||||
-------------------------
 | 
			
		||||
 | 
			
		||||
Udata (user-data) variables can be used for example to configure the matcher or
 | 
			
		||||
the callback function used to perform actions on found matches.
 | 
			
		||||
 | 
			
		||||
There is no automatic management of udata variables. For this reason it is
 | 
			
		||||
recommended that the user-supplied matcher code treats them as read-only
 | 
			
		||||
variables.
 | 
			
		||||
 | 
			
		||||
They are declared like state variables, just using the `udata` statement:
 | 
			
		||||
 | 
			
		||||
    udata <int> min_data_width max_data_width
 | 
			
		||||
    udata <IdString> data_port_name
 | 
			
		||||
 | 
			
		||||
They are atomatically initialzed to the default constructed value of their type
 | 
			
		||||
when ther pattern matcher object is constructed.
 | 
			
		||||
 | 
			
		||||
Embedded C++ code
 | 
			
		||||
-----------------
 | 
			
		||||
 | 
			
		||||
Many statements in a `.pmg` file contain C++ code. However, there are some
 | 
			
		||||
slight additions to regular C++/Yosys/RTLIL code that make it a bit easier to
 | 
			
		||||
write matchers:
 | 
			
		||||
 | 
			
		||||
- Identifiers starting with a dollar sign or backslash are automatically
 | 
			
		||||
  converted to special IdString variables that are initialized when the
 | 
			
		||||
  matcher object is constructed.
 | 
			
		||||
 | 
			
		||||
- The `port(<cell>, <portname>)` function is a handy alias for
 | 
			
		||||
  `sigmap(<cell>->getPort(<portname>))`.
 | 
			
		||||
 | 
			
		||||
- Similarly `param(<cell>, <paramname>)` looks up a parameter on a cell.
 | 
			
		||||
 | 
			
		||||
- The function `nusers(<sigspec>)` returns the number of different cells
 | 
			
		||||
  connected to any of the given signal bits, plus one if any of the signal
 | 
			
		||||
  bits is also a primary input or primary output.
 | 
			
		||||
 | 
			
		||||
- In `code..endcode` blocks there exist `accept`, `reject`, and `branch`
 | 
			
		||||
  statements.
 | 
			
		||||
 | 
			
		||||
- In `index` statements there is a special `===` operator for the index
 | 
			
		||||
  lookup.
 | 
			
		||||
 | 
			
		||||
Matching cells
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
Cells are matched using `match..endmatch` blocks. For example:
 | 
			
		||||
 | 
			
		||||
    match mul
 | 
			
		||||
        if ff
 | 
			
		||||
        select mul->type == $mul
 | 
			
		||||
        select nusers(port(mul, \Y) == 2
 | 
			
		||||
        index <SigSpec> port(mul, \Y) === port(ff, \D)
 | 
			
		||||
        filter some_weird_function(mul) < other_weird_function(ff)
 | 
			
		||||
        optional
 | 
			
		||||
    endmatch
 | 
			
		||||
 | 
			
		||||
A `match` block starts with `match <statevar>` and implicitly generates
 | 
			
		||||
a state variable `<statevar>` of type `RTLIL::Cell*`.
 | 
			
		||||
 | 
			
		||||
All statements in the match block are optional. (An empty match block
 | 
			
		||||
would simply match each and every cell in the module.)
 | 
			
		||||
 | 
			
		||||
The `if <expression>` statement makes the match block conditional. If
 | 
			
		||||
`<expression>` evaluates to `false` then the match block will be ignored
 | 
			
		||||
and the corresponding state variable is set to `nullptr`. In our example
 | 
			
		||||
we only try to match the `mul` cell if the `ff` state variable points
 | 
			
		||||
to a cell. (Presumably `ff` is provided by a prior `match` block.)
 | 
			
		||||
 | 
			
		||||
The `select` lines are evaluated once for each cell when the matcher is
 | 
			
		||||
initialized. A `match` block will only consider cells for which all `select`
 | 
			
		||||
expressions evaluated to `true`. Note that the state variable corresponding to
 | 
			
		||||
the match (in the example `mul`) is the only state variable that may be used
 | 
			
		||||
`select` lines.
 | 
			
		||||
 | 
			
		||||
Index lines are using the `index <type> expr1 === expr2` syntax.  `expr1` is
 | 
			
		||||
evaluated during matcher initialization and the same restrictions apply as for
 | 
			
		||||
`select` expressions. `expr2` is evaluated when the match is calulated. It is a
 | 
			
		||||
function of any state variables assigned to by previous blocks. Both expression
 | 
			
		||||
are converted to the given type and compared for equality. Only cells for which
 | 
			
		||||
all `index` statements in the block pass are considered by the match.
 | 
			
		||||
 | 
			
		||||
Note that `select` and `index` are fast operations. Thus `select` and `index`
 | 
			
		||||
should be used whenever possible to create efficient matchers.
 | 
			
		||||
 | 
			
		||||
Finally, `filter <expression>` narrows down the remaining list of cells. For
 | 
			
		||||
performance reasons `filter` statements should only be used for things that
 | 
			
		||||
can't be done using `select` and `index`.
 | 
			
		||||
 | 
			
		||||
The `optional` statement marks optional matches. I.e. the matcher will also
 | 
			
		||||
explore the case where `mul` is set to `nullptr`. Without the `optional`
 | 
			
		||||
statement a match may only be assigned nullptr when one of the `if` expressions
 | 
			
		||||
evaluates to `false`.
 | 
			
		||||
 | 
			
		||||
Additional code
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
Interleaved with `match..endmatch` blocks there may be `code..endcode` blocks.
 | 
			
		||||
Such a block starts with the keyword `code` followed by a list of state variables
 | 
			
		||||
that the block may modify. For example:
 | 
			
		||||
 | 
			
		||||
    code addAB sigS
 | 
			
		||||
        if (addA) {
 | 
			
		||||
            addAB = addA;
 | 
			
		||||
            sigS = port(addA, \B);
 | 
			
		||||
        }
 | 
			
		||||
        if (addB) {
 | 
			
		||||
            addAB = addB;
 | 
			
		||||
            sigS = port(addB, \A);
 | 
			
		||||
        }
 | 
			
		||||
    endcode
 | 
			
		||||
 | 
			
		||||
The special keyword `reject` can be used to reject the current state and
 | 
			
		||||
backtrack. For example:
 | 
			
		||||
 | 
			
		||||
    code
 | 
			
		||||
        if (ffA && ffB) {
 | 
			
		||||
            if (port(ffA, \CLK) != port(ffB, \CLK))
 | 
			
		||||
                reject;
 | 
			
		||||
            if (param(ffA, \CLK_POLARITY) != param(ffB, \CLK_POLARITY))
 | 
			
		||||
                reject;
 | 
			
		||||
        }
 | 
			
		||||
    endcode
 | 
			
		||||
 | 
			
		||||
Similarly, the special keyword `accept` can be used to accept the current
 | 
			
		||||
state. (`accept` will not backtrack. This means it continues with the current
 | 
			
		||||
branch and may accept a larger match later.)
 | 
			
		||||
 | 
			
		||||
The special keyword `branch` can be used to explore different cases. Note that
 | 
			
		||||
each code block has an implicit `branch` at the end. So most use-cases of the
 | 
			
		||||
`branch` keyword need to end the block with `reject` to avoid the implicit
 | 
			
		||||
branch at the end. For example:
 | 
			
		||||
 | 
			
		||||
    state <int> mode
 | 
			
		||||
 | 
			
		||||
    code mode
 | 
			
		||||
        for (mode = 0; mode < 8; mode++)
 | 
			
		||||
            branch;
 | 
			
		||||
        reject;
 | 
			
		||||
    endcode
 | 
			
		||||
 | 
			
		||||
But in some cases it is more natural to utilize the implicit branch statement:
 | 
			
		||||
 | 
			
		||||
    state <IdString> portAB
 | 
			
		||||
 | 
			
		||||
    code portAB
 | 
			
		||||
        portAB = \A;
 | 
			
		||||
        branch;
 | 
			
		||||
        portAB = \B;
 | 
			
		||||
    endcode
 | 
			
		||||
 | 
			
		||||
There is an implicit `code..endcode` block at the end of each `.pgm` file
 | 
			
		||||
that just accepts everything that gets all the way there.
 | 
			
		||||
							
								
								
									
										237
									
								
								passes/pmgen/ice40_dsp.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								passes/pmgen/ice40_dsp.cc
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,237 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  yosys -- Yosys Open SYnthesis Suite
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 | 
			
		||||
 *
 | 
			
		||||
 *  Permission to use, copy, modify, and/or distribute this software for any
 | 
			
		||||
 *  purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 *  copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "kernel/yosys.h"
 | 
			
		||||
#include "kernel/sigtools.h"
 | 
			
		||||
#include "passes/pmgen/ice40_dsp_pm.h"
 | 
			
		||||
 | 
			
		||||
USING_YOSYS_NAMESPACE
 | 
			
		||||
PRIVATE_NAMESPACE_BEGIN
 | 
			
		||||
 | 
			
		||||
void create_ice40_dsp(ice40_dsp_pm &pm)
 | 
			
		||||
{
 | 
			
		||||
#if 0
 | 
			
		||||
	log("\n");
 | 
			
		||||
	log("ffA:   %s\n", log_id(pm.st.ffA, "--"));
 | 
			
		||||
	log("ffB:   %s\n", log_id(pm.st.ffB, "--"));
 | 
			
		||||
	log("mul:   %s\n", log_id(pm.st.mul, "--"));
 | 
			
		||||
	log("ffY:   %s\n", log_id(pm.st.ffY, "--"));
 | 
			
		||||
	log("addAB: %s\n", log_id(pm.st.addAB, "--"));
 | 
			
		||||
	log("muxAB: %s\n", log_id(pm.st.muxAB, "--"));
 | 
			
		||||
	log("ffS:   %s\n", log_id(pm.st.ffS, "--"));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(pm.st.mul));
 | 
			
		||||
 | 
			
		||||
	if (GetSize(pm.st.sigA) > 16) {
 | 
			
		||||
		log("  input A (%s) is too large (%d > 16).\n", log_signal(pm.st.sigA), GetSize(pm.st.sigA));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (GetSize(pm.st.sigB) > 16) {
 | 
			
		||||
		log("  input B (%s) is too large (%d > 16).\n", log_signal(pm.st.sigB), GetSize(pm.st.sigB));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (GetSize(pm.st.sigS) > 32) {
 | 
			
		||||
		log("  accumulator (%s) is too large (%d > 32).\n", log_signal(pm.st.sigS), GetSize(pm.st.sigS));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (GetSize(pm.st.sigY) > 32) {
 | 
			
		||||
		log("  output (%s) is too large (%d > 32).\n", log_signal(pm.st.sigY), GetSize(pm.st.sigY));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool();
 | 
			
		||||
 | 
			
		||||
	if (mul_signed) {
 | 
			
		||||
		log("  inference of signed iCE40 DSP arithmetic is currently not supported.\n");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log("  replacing $mul with SB_MAC16 cell.\n");
 | 
			
		||||
 | 
			
		||||
	Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16");
 | 
			
		||||
	pm.module->swap_names(cell, pm.st.mul);
 | 
			
		||||
 | 
			
		||||
	// SB_MAC16 Input Interface
 | 
			
		||||
 | 
			
		||||
	SigSpec A = pm.st.sigA;
 | 
			
		||||
	A.extend_u0(16, mul_signed);
 | 
			
		||||
 | 
			
		||||
	SigSpec B = pm.st.sigB;
 | 
			
		||||
	B.extend_u0(16, mul_signed);
 | 
			
		||||
 | 
			
		||||
	SigSpec CD;
 | 
			
		||||
	if (pm.st.muxA)
 | 
			
		||||
		CD = pm.st.muxA->getPort("\\B");
 | 
			
		||||
	if (pm.st.muxB)
 | 
			
		||||
		CD = pm.st.muxB->getPort("\\A");
 | 
			
		||||
	CD.extend_u0(32, mul_signed);
 | 
			
		||||
 | 
			
		||||
	cell->setPort("\\A", A);
 | 
			
		||||
	cell->setPort("\\B", B);
 | 
			
		||||
	cell->setPort("\\C", CD.extract(0, 16));
 | 
			
		||||
	cell->setPort("\\D", CD.extract(16, 16));
 | 
			
		||||
 | 
			
		||||
	cell->setParam("\\A_REG", pm.st.ffA ? State::S1 : State::S0);
 | 
			
		||||
	cell->setParam("\\B_REG", pm.st.ffB ? State::S1 : State::S0);
 | 
			
		||||
 | 
			
		||||
	cell->setPort("\\AHOLD", State::S0);
 | 
			
		||||
	cell->setPort("\\BHOLD", State::S0);
 | 
			
		||||
	cell->setPort("\\CHOLD", State::S0);
 | 
			
		||||
	cell->setPort("\\DHOLD", State::S0);
 | 
			
		||||
 | 
			
		||||
	cell->setPort("\\IRSTTOP", State::S0);
 | 
			
		||||
	cell->setPort("\\IRSTBOT", State::S0);
 | 
			
		||||
 | 
			
		||||
	if (pm.st.clock_vld)
 | 
			
		||||
	{
 | 
			
		||||
		cell->setPort("\\CLK", pm.st.clock);
 | 
			
		||||
		cell->setPort("\\CE", State::S1);
 | 
			
		||||
		cell->setParam("\\NEG_TRIGGER", pm.st.clock_pol ? State::S0 : State::S1);
 | 
			
		||||
 | 
			
		||||
		log("  clock: %s (%s)", log_signal(pm.st.clock), pm.st.clock_pol ? "posedge" : "negedge");
 | 
			
		||||
 | 
			
		||||
		if (pm.st.ffA)
 | 
			
		||||
			log(" ffA:%s", log_id(pm.st.ffA));
 | 
			
		||||
 | 
			
		||||
		if (pm.st.ffB)
 | 
			
		||||
			log(" ffB:%s", log_id(pm.st.ffB));
 | 
			
		||||
 | 
			
		||||
		if (pm.st.ffY)
 | 
			
		||||
			log(" ffY:%s", log_id(pm.st.ffY));
 | 
			
		||||
 | 
			
		||||
		if (pm.st.ffS)
 | 
			
		||||
			log(" ffS:%s", log_id(pm.st.ffS));
 | 
			
		||||
 | 
			
		||||
		log("\n");
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		cell->setPort("\\CLK", State::S0);
 | 
			
		||||
		cell->setPort("\\CE", State::S0);
 | 
			
		||||
		cell->setParam("\\NEG_TRIGGER", State::S0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// SB_MAC16 Cascade Interface
 | 
			
		||||
 | 
			
		||||
	cell->setPort("\\SIGNEXTIN", State::Sx);
 | 
			
		||||
	cell->setPort("\\SIGNEXTOUT", pm.module->addWire(NEW_ID));
 | 
			
		||||
 | 
			
		||||
	cell->setPort("\\CI", State::Sx);
 | 
			
		||||
	cell->setPort("\\CO", pm.module->addWire(NEW_ID));
 | 
			
		||||
 | 
			
		||||
	cell->setPort("\\ACCUMCI", State::Sx);
 | 
			
		||||
	cell->setPort("\\ACCUMCO", pm.module->addWire(NEW_ID));
 | 
			
		||||
 | 
			
		||||
	// SB_MAC16 Output Interface
 | 
			
		||||
 | 
			
		||||
	SigSpec O = pm.st.ffS ? pm.st.sigS : pm.st.sigY;
 | 
			
		||||
	if (GetSize(O) < 32)
 | 
			
		||||
		O.append(pm.module->addWire(NEW_ID, 32-GetSize(O)));
 | 
			
		||||
 | 
			
		||||
	cell->setPort("\\O", O);
 | 
			
		||||
 | 
			
		||||
	if (pm.st.addAB) {
 | 
			
		||||
		log("  accumulator %s (%s)\n", log_id(pm.st.addAB), log_id(pm.st.addAB->type));
 | 
			
		||||
		cell->setPort("\\ADDSUBTOP", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
 | 
			
		||||
		cell->setPort("\\ADDSUBBOT", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
 | 
			
		||||
	} else {
 | 
			
		||||
		cell->setPort("\\ADDSUBTOP", State::S0);
 | 
			
		||||
		cell->setPort("\\ADDSUBBOT", State::S0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cell->setPort("\\ORSTTOP", State::S0);
 | 
			
		||||
	cell->setPort("\\ORSTBOT", State::S0);
 | 
			
		||||
 | 
			
		||||
	cell->setPort("\\OHOLDTOP", State::S0);
 | 
			
		||||
	cell->setPort("\\OHOLDBOT", State::S0);
 | 
			
		||||
 | 
			
		||||
	SigSpec acc_reset = State::S0;
 | 
			
		||||
	if (pm.st.muxA)
 | 
			
		||||
		acc_reset = pm.st.muxA->getPort("\\S");
 | 
			
		||||
	if (pm.st.muxB)
 | 
			
		||||
		acc_reset = pm.module->Not(NEW_ID, pm.st.muxB->getPort("\\S"));
 | 
			
		||||
 | 
			
		||||
	cell->setPort("\\OLOADTOP", acc_reset);
 | 
			
		||||
	cell->setPort("\\OLOADBOT", acc_reset);
 | 
			
		||||
 | 
			
		||||
	// SB_MAC16 Remaining Parameters
 | 
			
		||||
 | 
			
		||||
	cell->setParam("\\C_REG", State::S0);
 | 
			
		||||
	cell->setParam("\\D_REG", State::S0);
 | 
			
		||||
 | 
			
		||||
	cell->setParam("\\TOP_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
 | 
			
		||||
	cell->setParam("\\BOT_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
 | 
			
		||||
	cell->setParam("\\PIPELINE_16x16_MULT_REG1", pm.st.ffY ? State::S1 : State::S0);
 | 
			
		||||
	cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0);
 | 
			
		||||
 | 
			
		||||
	cell->setParam("\\TOPOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
 | 
			
		||||
	cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2));
 | 
			
		||||
	cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0);
 | 
			
		||||
	cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2));
 | 
			
		||||
 | 
			
		||||
	cell->setParam("\\BOTOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
 | 
			
		||||
	cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2));
 | 
			
		||||
	cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0);
 | 
			
		||||
	cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2));
 | 
			
		||||
 | 
			
		||||
	cell->setParam("\\MODE_8x8", State::S0);
 | 
			
		||||
	cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0);
 | 
			
		||||
	cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0);
 | 
			
		||||
 | 
			
		||||
	pm.autoremove(pm.st.mul);
 | 
			
		||||
	pm.autoremove(pm.st.ffY);
 | 
			
		||||
	pm.autoremove(pm.st.ffS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Ice40DspPass : public Pass {
 | 
			
		||||
	Ice40DspPass() : Pass("ice40_dsp", "iCE40: map multipliers") { }
 | 
			
		||||
	void help() YS_OVERRIDE
 | 
			
		||||
	{
 | 
			
		||||
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    ice40_dsp [options] [selection]\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("Map multipliers and multiply-accumulate blocks to iCE40 DSP resources.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
	}
 | 
			
		||||
	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
 | 
			
		||||
	{
 | 
			
		||||
		log_header(design, "Executing ICE40_DSP pass (map multipliers).\n");
 | 
			
		||||
 | 
			
		||||
		size_t argidx;
 | 
			
		||||
		for (argidx = 1; argidx < args.size(); argidx++)
 | 
			
		||||
		{
 | 
			
		||||
			// if (args[argidx] == "-singleton") {
 | 
			
		||||
			// 	singleton_mode = true;
 | 
			
		||||
			// 	continue;
 | 
			
		||||
			// }
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		extra_args(args, argidx, design);
 | 
			
		||||
 | 
			
		||||
		for (auto module : design->selected_modules())
 | 
			
		||||
			ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp);
 | 
			
		||||
	}
 | 
			
		||||
} Ice40DspPass;
 | 
			
		||||
 | 
			
		||||
PRIVATE_NAMESPACE_END
 | 
			
		||||
							
								
								
									
										160
									
								
								passes/pmgen/ice40_dsp.pmg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								passes/pmgen/ice40_dsp.pmg
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,160 @@
 | 
			
		|||
state <SigBit> clock
 | 
			
		||||
state <bool> clock_pol clock_vld
 | 
			
		||||
state <SigSpec> sigA sigB sigY sigS
 | 
			
		||||
state <Cell*> addAB muxAB
 | 
			
		||||
 | 
			
		||||
match mul
 | 
			
		||||
	select mul->type.in($mul)
 | 
			
		||||
	select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10
 | 
			
		||||
	select GetSize(mul->getPort(\Y)) > 10
 | 
			
		||||
endmatch
 | 
			
		||||
 | 
			
		||||
match ffA
 | 
			
		||||
	select ffA->type.in($dff)
 | 
			
		||||
	// select nusers(port(ffA, \Q)) == 2
 | 
			
		||||
	index <SigSpec> port(ffA, \Q) === port(mul, \A)
 | 
			
		||||
	optional
 | 
			
		||||
endmatch
 | 
			
		||||
 | 
			
		||||
code sigA clock clock_pol clock_vld
 | 
			
		||||
	sigA = port(mul, \A);
 | 
			
		||||
 | 
			
		||||
	if (ffA) {
 | 
			
		||||
		sigA = port(ffA, \D);
 | 
			
		||||
 | 
			
		||||
		clock = port(ffA, \CLK).as_bit();
 | 
			
		||||
		clock_pol = param(ffA, \CLK_POLARITY).as_bool();
 | 
			
		||||
		clock_vld = true;
 | 
			
		||||
	}
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
match ffB
 | 
			
		||||
	select ffB->type.in($dff)
 | 
			
		||||
	// select nusers(port(ffB, \Q)) == 2
 | 
			
		||||
	index <SigSpec> port(ffB, \Q) === port(mul, \B)
 | 
			
		||||
	optional
 | 
			
		||||
endmatch
 | 
			
		||||
 | 
			
		||||
code sigB clock clock_pol clock_vld
 | 
			
		||||
	sigB = port(mul, \B);
 | 
			
		||||
 | 
			
		||||
	if (ffB) {
 | 
			
		||||
		sigB = port(ffB, \D);
 | 
			
		||||
		SigBit c = port(ffB, \CLK).as_bit();
 | 
			
		||||
		bool cp = param(ffB, \CLK_POLARITY).as_bool();
 | 
			
		||||
 | 
			
		||||
		if (clock_vld && (c != clock || cp != clock_pol))
 | 
			
		||||
			reject;
 | 
			
		||||
 | 
			
		||||
		clock = c;
 | 
			
		||||
		clock_pol = cp;
 | 
			
		||||
		clock_vld = true;
 | 
			
		||||
	}
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
match ffY
 | 
			
		||||
	select ffY->type.in($dff)
 | 
			
		||||
	select nusers(port(ffY, \D)) == 2
 | 
			
		||||
	index <SigSpec> port(ffY, \D) === port(mul, \Y)
 | 
			
		||||
	optional
 | 
			
		||||
endmatch
 | 
			
		||||
 | 
			
		||||
code sigY clock clock_pol clock_vld
 | 
			
		||||
	sigY = port(mul, \Y);
 | 
			
		||||
 | 
			
		||||
	if (ffY) {
 | 
			
		||||
		sigY = port(ffY, \Q);
 | 
			
		||||
		SigBit c = port(ffY, \CLK).as_bit();
 | 
			
		||||
		bool cp = param(ffY, \CLK_POLARITY).as_bool();
 | 
			
		||||
 | 
			
		||||
		if (clock_vld && (c != clock || cp != clock_pol))
 | 
			
		||||
			reject;
 | 
			
		||||
 | 
			
		||||
		clock = c;
 | 
			
		||||
		clock_pol = cp;
 | 
			
		||||
		clock_vld = true;
 | 
			
		||||
	}
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
match addA
 | 
			
		||||
	select addA->type.in($add)
 | 
			
		||||
	select nusers(port(addA, \A)) == 2
 | 
			
		||||
	index <SigSpec> port(addA, \A) === sigY
 | 
			
		||||
	optional
 | 
			
		||||
endmatch
 | 
			
		||||
 | 
			
		||||
match addB
 | 
			
		||||
	if !addA
 | 
			
		||||
	select addB->type.in($add, $sub)
 | 
			
		||||
	select nusers(port(addB, \B)) == 2
 | 
			
		||||
	index <SigSpec> port(addB, \B) === sigY
 | 
			
		||||
	optional
 | 
			
		||||
endmatch
 | 
			
		||||
 | 
			
		||||
code addAB sigS
 | 
			
		||||
	if (addA) {
 | 
			
		||||
		addAB = addA;
 | 
			
		||||
		sigS = port(addA, \B);
 | 
			
		||||
	}
 | 
			
		||||
	if (addB) {
 | 
			
		||||
		addAB = addB;
 | 
			
		||||
		sigS = port(addB, \A);
 | 
			
		||||
	}
 | 
			
		||||
	if (addAB) {
 | 
			
		||||
		int natural_mul_width = GetSize(sigA) + GetSize(sigB);
 | 
			
		||||
		int actual_mul_width = GetSize(sigY);
 | 
			
		||||
		int actual_acc_width = GetSize(sigS);
 | 
			
		||||
 | 
			
		||||
		if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
 | 
			
		||||
			reject;
 | 
			
		||||
		if ((actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(addAB, \A_SIGNED).as_bool()))
 | 
			
		||||
			reject;
 | 
			
		||||
	}
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
match muxA
 | 
			
		||||
	if addAB
 | 
			
		||||
	select muxA->type.in($mux)
 | 
			
		||||
	select nusers(port(muxA, \A)) == 2
 | 
			
		||||
	index <SigSpec> port(muxA, \A) === port(addAB, \Y)
 | 
			
		||||
	optional
 | 
			
		||||
endmatch
 | 
			
		||||
 | 
			
		||||
match muxB
 | 
			
		||||
	if addAB
 | 
			
		||||
	if !muxA
 | 
			
		||||
	select muxB->type.in($mux)
 | 
			
		||||
	select nusers(port(muxB, \B)) == 2
 | 
			
		||||
	index <SigSpec> port(muxB, \B) === port(addAB, \Y)
 | 
			
		||||
	optional
 | 
			
		||||
endmatch
 | 
			
		||||
 | 
			
		||||
code muxAB
 | 
			
		||||
	muxAB = addAB;
 | 
			
		||||
	if (muxA)
 | 
			
		||||
		muxAB = muxA;
 | 
			
		||||
	if (muxB)
 | 
			
		||||
		muxAB = muxB;
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
match ffS
 | 
			
		||||
	if muxAB
 | 
			
		||||
	select ffS->type.in($dff)
 | 
			
		||||
	select nusers(port(ffS, \D)) == 2
 | 
			
		||||
	index <SigSpec> port(ffS, \D) === port(muxAB, \Y)
 | 
			
		||||
	index <SigSpec> port(ffS, \Q) === sigS
 | 
			
		||||
endmatch
 | 
			
		||||
 | 
			
		||||
code clock clock_pol clock_vld
 | 
			
		||||
	if (ffS) {
 | 
			
		||||
		SigBit c = port(ffS, \CLK).as_bit();
 | 
			
		||||
		bool cp = param(ffS, \CLK_POLARITY).as_bool();
 | 
			
		||||
 | 
			
		||||
		if (clock_vld && (c != clock || cp != clock_pol))
 | 
			
		||||
			reject;
 | 
			
		||||
 | 
			
		||||
		clock = c;
 | 
			
		||||
		clock_pol = cp;
 | 
			
		||||
		clock_vld = true;
 | 
			
		||||
	}
 | 
			
		||||
endcode
 | 
			
		||||
							
								
								
									
										482
									
								
								passes/pmgen/pmgen.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										482
									
								
								passes/pmgen/pmgen.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,482 @@
 | 
			
		|||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
import re
 | 
			
		||||
import sys
 | 
			
		||||
import pprint
 | 
			
		||||
 | 
			
		||||
pp = pprint.PrettyPrinter(indent=4)
 | 
			
		||||
 | 
			
		||||
prefix = sys.argv[1]
 | 
			
		||||
 | 
			
		||||
state_types = dict()
 | 
			
		||||
udata_types = dict()
 | 
			
		||||
blocks = list()
 | 
			
		||||
ids = dict()
 | 
			
		||||
 | 
			
		||||
def rewrite_cpp(s):
 | 
			
		||||
    t = list()
 | 
			
		||||
    i = 0
 | 
			
		||||
    while i < len(s):
 | 
			
		||||
        if s[i] in ("'", '"') and i + 1 < len(s):
 | 
			
		||||
            j = i + 1
 | 
			
		||||
            while j + 1 < len(s) and s[j] != s[i]:
 | 
			
		||||
                if s[j] == '\\' and j + 1 < len(s):
 | 
			
		||||
                    j += 1
 | 
			
		||||
                j += 1
 | 
			
		||||
            t.append(s[i:j+1])
 | 
			
		||||
            i = j + 1
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        if s[i] in ('$', '\\') and i + 1 < len(s):
 | 
			
		||||
            j = i + 1
 | 
			
		||||
            while True:
 | 
			
		||||
                if j == len(s):
 | 
			
		||||
                    j -= 1
 | 
			
		||||
                    break
 | 
			
		||||
                if ord('a') <= ord(s[j]) <= ord('z'):
 | 
			
		||||
                    j += 1
 | 
			
		||||
                    continue
 | 
			
		||||
                if ord('A') <= ord(s[j]) <= ord('Z'):
 | 
			
		||||
                    j += 1
 | 
			
		||||
                    continue
 | 
			
		||||
                if ord('0') <= ord(s[j]) <= ord('9'):
 | 
			
		||||
                    j += 1
 | 
			
		||||
                    continue
 | 
			
		||||
                if s[j] == '_':
 | 
			
		||||
                    j += 1
 | 
			
		||||
                    continue
 | 
			
		||||
                j -= 1
 | 
			
		||||
                break
 | 
			
		||||
 | 
			
		||||
            n = s[i:j+1]
 | 
			
		||||
            i = j + 1
 | 
			
		||||
 | 
			
		||||
            if n[0] == '$':
 | 
			
		||||
                v = "id_d_" + n[1:]
 | 
			
		||||
            else:
 | 
			
		||||
                v = "id_b_" + n[1:]
 | 
			
		||||
 | 
			
		||||
            if v not in ids:
 | 
			
		||||
                ids[v] = n
 | 
			
		||||
            else:
 | 
			
		||||
                assert ids[v] == n
 | 
			
		||||
 | 
			
		||||
            t.append(v)
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        if s[i] == "\t":
 | 
			
		||||
            t.append("  ")
 | 
			
		||||
        else:
 | 
			
		||||
            t.append(s[i])
 | 
			
		||||
 | 
			
		||||
        i += 1
 | 
			
		||||
 | 
			
		||||
    return "".join(t)
 | 
			
		||||
 | 
			
		||||
with open("%s.pmg" % prefix, "r") as f:
 | 
			
		||||
    while True:
 | 
			
		||||
        line = f.readline()
 | 
			
		||||
        if line == "": break
 | 
			
		||||
        line = line.strip()
 | 
			
		||||
 | 
			
		||||
        cmd = line.split()
 | 
			
		||||
        if len(cmd) == 0 or cmd[0].startswith("//"): continue
 | 
			
		||||
        cmd = cmd[0]
 | 
			
		||||
        
 | 
			
		||||
        if cmd == "state":
 | 
			
		||||
            m = re.match(r"^state\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line)
 | 
			
		||||
            assert m
 | 
			
		||||
            type_str = m.group(1)
 | 
			
		||||
            states_str = m.group(2)
 | 
			
		||||
            for s in re.split(r"\s+", states_str):
 | 
			
		||||
                assert s not in state_types
 | 
			
		||||
                state_types[s] = type_str
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        if cmd == "udata":
 | 
			
		||||
            m = re.match(r"^udata\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line)
 | 
			
		||||
            assert m
 | 
			
		||||
            type_str = m.group(1)
 | 
			
		||||
            udatas_str = m.group(2)
 | 
			
		||||
            for s in re.split(r"\s+", udatas_str):
 | 
			
		||||
                assert s not in udata_types
 | 
			
		||||
                udata_types[s] = type_str
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        if cmd == "match":
 | 
			
		||||
            block = dict()
 | 
			
		||||
            block["type"] = "match"
 | 
			
		||||
 | 
			
		||||
            line = line.split()
 | 
			
		||||
            assert len(line) == 2
 | 
			
		||||
            assert line[1] not in state_types
 | 
			
		||||
            block["cell"] = line[1]
 | 
			
		||||
            state_types[line[1]] = "Cell*";
 | 
			
		||||
 | 
			
		||||
            block["if"] = list()
 | 
			
		||||
            block["select"] = list()
 | 
			
		||||
            block["index"] = list()
 | 
			
		||||
            block["filter"] = list()
 | 
			
		||||
            block["optional"] = False
 | 
			
		||||
 | 
			
		||||
            while True:
 | 
			
		||||
                l = f.readline()
 | 
			
		||||
                assert l != ""
 | 
			
		||||
                a = l.split()
 | 
			
		||||
                if len(a) == 0 or a[0].startswith("//"): continue
 | 
			
		||||
                if a[0] == "endmatch": break
 | 
			
		||||
 | 
			
		||||
                if a[0] == "if":
 | 
			
		||||
                    b = l.lstrip()[2:]
 | 
			
		||||
                    block["if"].append(rewrite_cpp(b.strip()))
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
                if a[0] == "select":
 | 
			
		||||
                    b = l.lstrip()[6:]
 | 
			
		||||
                    block["select"].append(rewrite_cpp(b.strip()))
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
                if a[0] == "index":
 | 
			
		||||
                    m = re.match(r"^\s*index\s+<(.*?)>\s+(.*?)\s*===\s*(.*?)\s*$", l)
 | 
			
		||||
                    assert m
 | 
			
		||||
                    block["index"].append((m.group(1), rewrite_cpp(m.group(2)), rewrite_cpp(m.group(3))))
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
                if a[0] == "filter":
 | 
			
		||||
                    b = l.lstrip()[6:]
 | 
			
		||||
                    block["filter"].append(rewrite_cpp(b.strip()))
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
                if a[0] == "optional":
 | 
			
		||||
                    block["optional"] = True
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
                assert False
 | 
			
		||||
 | 
			
		||||
            blocks.append(block)
 | 
			
		||||
 | 
			
		||||
        if cmd == "code":
 | 
			
		||||
            block = dict()
 | 
			
		||||
            block["type"] = "code"
 | 
			
		||||
            block["code"] = list()
 | 
			
		||||
            block["states"] = set()
 | 
			
		||||
 | 
			
		||||
            for s in line.split()[1:]:
 | 
			
		||||
                assert s in state_types
 | 
			
		||||
                block["states"].add(s)
 | 
			
		||||
 | 
			
		||||
            while True:
 | 
			
		||||
                l = f.readline()
 | 
			
		||||
                assert l != ""
 | 
			
		||||
                a = l.split()
 | 
			
		||||
                if len(a) == 0: continue
 | 
			
		||||
                if a[0] == "endcode": break
 | 
			
		||||
 | 
			
		||||
                block["code"].append(rewrite_cpp(l.rstrip()))
 | 
			
		||||
 | 
			
		||||
            blocks.append(block)
 | 
			
		||||
 | 
			
		||||
with open("%s_pm.h" % prefix, "w") as f:
 | 
			
		||||
    print("// Generated by pmgen.py from {}.pgm".format(prefix), file=f)
 | 
			
		||||
    print("", file=f)
 | 
			
		||||
 | 
			
		||||
    print("#include \"kernel/yosys.h\"", file=f)
 | 
			
		||||
    print("#include \"kernel/sigtools.h\"", file=f)
 | 
			
		||||
    print("", file=f)
 | 
			
		||||
 | 
			
		||||
    print("YOSYS_NAMESPACE_BEGIN", file=f)
 | 
			
		||||
    print("", file=f)
 | 
			
		||||
 | 
			
		||||
    print("struct {}_pm {{".format(prefix), file=f)
 | 
			
		||||
    print("  Module *module;", file=f)
 | 
			
		||||
    print("  SigMap sigmap;", file=f)
 | 
			
		||||
    print("  std::function<void()> on_accept;".format(prefix), file=f)
 | 
			
		||||
    print("", file=f)
 | 
			
		||||
 | 
			
		||||
    for index in range(len(blocks)):
 | 
			
		||||
        block = blocks[index]
 | 
			
		||||
        if block["type"] == "match":
 | 
			
		||||
            index_types = list()
 | 
			
		||||
            for entry in block["index"]:
 | 
			
		||||
                index_types.append(entry[0])
 | 
			
		||||
            print("  typedef std::tuple<{}> index_{}_key_type;".format(", ".join(index_types), index), file=f)
 | 
			
		||||
            print("  dict<index_{}_key_type, vector<Cell*>> index_{};".format(index, index), file=f)
 | 
			
		||||
    print("  dict<SigBit, pool<Cell*>> sigusers;", file=f)
 | 
			
		||||
    print("  pool<Cell*> blacklist_cells;", file=f)
 | 
			
		||||
    print("  pool<Cell*> autoremove_cells;", file=f)
 | 
			
		||||
    print("  bool blacklist_dirty;", file=f)
 | 
			
		||||
    print("  int rollback;", file=f)
 | 
			
		||||
    print("", file=f)
 | 
			
		||||
 | 
			
		||||
    print("  struct state_t {", file=f)
 | 
			
		||||
    for s, t in sorted(state_types.items()):
 | 
			
		||||
        print("    {} {};".format(t, s), file=f)
 | 
			
		||||
    print("  } st;", file=f)
 | 
			
		||||
    print("", file=f)
 | 
			
		||||
 | 
			
		||||
    print("  struct udata_t {", file=f)
 | 
			
		||||
    for s, t in sorted(udata_types.items()):
 | 
			
		||||
        print("    {} {};".format(t, s), file=f)
 | 
			
		||||
    print("  } ud;", file=f)
 | 
			
		||||
    print("", file=f)
 | 
			
		||||
 | 
			
		||||
    for v, n in sorted(ids.items()):
 | 
			
		||||
        if n[0] == "\\":
 | 
			
		||||
            print("  IdString {}{{\"\\{}\"}};".format(v, n), file=f)
 | 
			
		||||
        else:
 | 
			
		||||
            print("  IdString {}{{\"{}\"}};".format(v, n), file=f)
 | 
			
		||||
    print("", file=f)
 | 
			
		||||
 | 
			
		||||
    print("  void add_siguser(const SigSpec &sig, Cell *cell) {", file=f)
 | 
			
		||||
    print("    for (auto bit : sigmap(sig)) {", file=f)
 | 
			
		||||
    print("      if (bit.wire == nullptr) continue;", file=f)
 | 
			
		||||
    print("      if (sigusers.count(bit) == 0 && bit.wire->port_id)", file=f)
 | 
			
		||||
    print("        sigusers[bit].insert(nullptr);", file=f)
 | 
			
		||||
    print("      sigusers[bit].insert(cell);", file=f)
 | 
			
		||||
    print("    }", file=f)
 | 
			
		||||
    print("  }", file=f)
 | 
			
		||||
    print("", file=f)
 | 
			
		||||
 | 
			
		||||
    print("  void blacklist(Cell *cell) {", file=f)
 | 
			
		||||
    print("    if (cell != nullptr) {", file=f)
 | 
			
		||||
    print("      if (blacklist_cells.insert(cell).second)", file=f)
 | 
			
		||||
    print("        blacklist_dirty = true;", file=f)
 | 
			
		||||
    print("    }", file=f)
 | 
			
		||||
    print("  }", file=f)
 | 
			
		||||
    print("", file=f)
 | 
			
		||||
 | 
			
		||||
    print("  void autoremove(Cell *cell) {", file=f)
 | 
			
		||||
    print("    if (cell != nullptr) {", file=f)
 | 
			
		||||
    print("      if (blacklist_cells.insert(cell).second)", file=f)
 | 
			
		||||
    print("        blacklist_dirty = true;", file=f)
 | 
			
		||||
    print("      autoremove_cells.insert(cell);", file=f)
 | 
			
		||||
    print("    }", file=f)
 | 
			
		||||
    print("  }", file=f)
 | 
			
		||||
    print("", file=f)
 | 
			
		||||
 | 
			
		||||
    print("  void check_blacklist() {", file=f)
 | 
			
		||||
    print("    if (!blacklist_dirty)", file=f)
 | 
			
		||||
    print("      return;", file=f)
 | 
			
		||||
    print("    blacklist_dirty = false;", file=f)
 | 
			
		||||
    for index in range(len(blocks)):
 | 
			
		||||
        block = blocks[index]
 | 
			
		||||
        if block["type"] == "match":
 | 
			
		||||
            print("    if (st.{} != nullptr && blacklist_cells.count(st.{})) {{".format(block["cell"], block["cell"]), file=f)
 | 
			
		||||
            print("      rollback = {};".format(index+1), file=f)
 | 
			
		||||
            print("      return;", file=f)
 | 
			
		||||
            print("    }", file=f)
 | 
			
		||||
    print("    rollback = 0;", file=f)
 | 
			
		||||
    print("  }", file=f)
 | 
			
		||||
    print("", file=f)
 | 
			
		||||
 | 
			
		||||
    print("  SigSpec port(Cell *cell, IdString portname) {", file=f)
 | 
			
		||||
    print("    return sigmap(cell->getPort(portname));", file=f)
 | 
			
		||||
    print("  }", file=f)
 | 
			
		||||
    print("", file=f)
 | 
			
		||||
 | 
			
		||||
    print("  Const param(Cell *cell, IdString paramname) {", file=f)
 | 
			
		||||
    print("    return cell->getParam(paramname);", file=f)
 | 
			
		||||
    print("  }", file=f)
 | 
			
		||||
    print("", file=f)
 | 
			
		||||
 | 
			
		||||
    print("  int nusers(const SigSpec &sig) {", file=f)
 | 
			
		||||
    print("    pool<Cell*> users;", file=f)
 | 
			
		||||
    print("    for (auto bit : sigmap(sig))", file=f)
 | 
			
		||||
    print("      for (auto user : sigusers[bit])", file=f)
 | 
			
		||||
    print("        users.insert(user);", file=f)
 | 
			
		||||
    print("    return GetSize(users);", file=f)
 | 
			
		||||
    print("  }", file=f)
 | 
			
		||||
    print("", file=f)
 | 
			
		||||
 | 
			
		||||
    print("  {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f)
 | 
			
		||||
    print("      module(module), sigmap(module) {", file=f)
 | 
			
		||||
    for s, t in sorted(udata_types.items()):
 | 
			
		||||
        if t.endswith("*"):
 | 
			
		||||
            print("    ud.{} = nullptr;".format(s), file=f)
 | 
			
		||||
        else:
 | 
			
		||||
            print("    ud.{} = {}();".format(s, t), file=f)
 | 
			
		||||
    print("    for (auto cell : module->cells()) {", file=f)
 | 
			
		||||
    print("      for (auto &conn : cell->connections())", file=f)
 | 
			
		||||
    print("        add_siguser(conn.second, cell);", file=f)
 | 
			
		||||
    print("    }", file=f)
 | 
			
		||||
    print("    for (auto cell : cells) {", file=f)
 | 
			
		||||
 | 
			
		||||
    for index in range(len(blocks)):
 | 
			
		||||
        block = blocks[index]
 | 
			
		||||
        if block["type"] == "match":
 | 
			
		||||
            print("      do {", file=f)
 | 
			
		||||
            print("        Cell *{} = cell;".format(block["cell"]), file=f)
 | 
			
		||||
            for expr in block["select"]:
 | 
			
		||||
                print("        if (!({})) break;".format(expr), file=f)
 | 
			
		||||
            print("        index_{}_key_type key;".format(index), file=f)
 | 
			
		||||
            for field, entry in enumerate(block["index"]):
 | 
			
		||||
                print("        std::get<{}>(key) = {};".format(field, entry[1]), file=f)
 | 
			
		||||
            print("        index_{}[key].push_back(cell);".format(index), file=f)
 | 
			
		||||
            print("      } while (0);", file=f)
 | 
			
		||||
 | 
			
		||||
    print("    }", file=f)
 | 
			
		||||
    print("  }", file=f)
 | 
			
		||||
    print("", file=f)
 | 
			
		||||
 | 
			
		||||
    print("  ~{}_pm() {{".format(prefix), file=f)
 | 
			
		||||
    print("    for (auto cell : autoremove_cells)", file=f)
 | 
			
		||||
    print("      module->remove(cell);", file=f)
 | 
			
		||||
    print("  }", file=f)
 | 
			
		||||
    print("", file=f)
 | 
			
		||||
 | 
			
		||||
    print("  void run(std::function<void()> on_accept_f) {", file=f)
 | 
			
		||||
    print("    on_accept = on_accept_f;", file=f)
 | 
			
		||||
    print("    rollback = 0;", file=f)
 | 
			
		||||
    print("    blacklist_dirty = false;", file=f)
 | 
			
		||||
    for s, t in sorted(state_types.items()):
 | 
			
		||||
        if t.endswith("*"):
 | 
			
		||||
            print("    st.{} = nullptr;".format(s), file=f)
 | 
			
		||||
        else:
 | 
			
		||||
            print("    st.{} = {}();".format(s, t), file=f)
 | 
			
		||||
    print("    block_0();", file=f)
 | 
			
		||||
    print("  }", file=f)
 | 
			
		||||
    print("", file=f)
 | 
			
		||||
 | 
			
		||||
    print("  void run(std::function<void({}_pm&)> on_accept_f) {{".format(prefix), file=f)
 | 
			
		||||
    print("    run([&](){on_accept_f(*this);});", file=f)
 | 
			
		||||
    print("  }", file=f)
 | 
			
		||||
    print("", file=f)
 | 
			
		||||
 | 
			
		||||
    for index in range(len(blocks)):
 | 
			
		||||
        block = blocks[index]
 | 
			
		||||
 | 
			
		||||
        print("  void block_{}() {{".format(index), file=f)
 | 
			
		||||
 | 
			
		||||
        const_st = set()
 | 
			
		||||
        nonconst_st = set()
 | 
			
		||||
        restore_st = set()
 | 
			
		||||
 | 
			
		||||
        for i in range(index):
 | 
			
		||||
            if blocks[i]["type"] == "code":
 | 
			
		||||
                for s in blocks[i]["states"]:
 | 
			
		||||
                    const_st.add(s)
 | 
			
		||||
            elif blocks[i]["type"] == "match":
 | 
			
		||||
                const_st.add(blocks[i]["cell"])
 | 
			
		||||
            else:
 | 
			
		||||
                assert False
 | 
			
		||||
 | 
			
		||||
        if block["type"] == "code":
 | 
			
		||||
            for s in block["states"]:
 | 
			
		||||
                if s in const_st:
 | 
			
		||||
                    const_st.remove(s)
 | 
			
		||||
                    restore_st.add(s)
 | 
			
		||||
                nonconst_st.add(s)
 | 
			
		||||
        elif block["type"] == "match":
 | 
			
		||||
            s = block["cell"]
 | 
			
		||||
            assert s not in const_st
 | 
			
		||||
            nonconst_st.add(s)
 | 
			
		||||
        else:
 | 
			
		||||
            assert False
 | 
			
		||||
 | 
			
		||||
        for s in sorted(const_st):
 | 
			
		||||
            t = state_types[s]
 | 
			
		||||
            if t.endswith("*"):
 | 
			
		||||
                print("    {} const &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
 | 
			
		||||
            else:
 | 
			
		||||
                print("    const {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
 | 
			
		||||
 | 
			
		||||
        for s in sorted(nonconst_st):
 | 
			
		||||
            t = state_types[s]
 | 
			
		||||
            print("    {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
 | 
			
		||||
 | 
			
		||||
        if len(restore_st):
 | 
			
		||||
            print("", file=f)
 | 
			
		||||
            for s in sorted(restore_st):
 | 
			
		||||
                t = state_types[s]
 | 
			
		||||
                print("    {} backup_{} = {};".format(t, s, s), file=f)
 | 
			
		||||
 | 
			
		||||
        if block["type"] == "code":
 | 
			
		||||
            print("", file=f)
 | 
			
		||||
            print("    do {", file=f)
 | 
			
		||||
            print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f)
 | 
			
		||||
            print("#define accept do { on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f)
 | 
			
		||||
            print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f)
 | 
			
		||||
 | 
			
		||||
            for line in block["code"]:
 | 
			
		||||
                print("    " + line, file=f)
 | 
			
		||||
 | 
			
		||||
            print("", file=f)
 | 
			
		||||
            print("      block_{}();".format(index+1), file=f)
 | 
			
		||||
            print("#undef reject", file=f)
 | 
			
		||||
            print("#undef accept", file=f)
 | 
			
		||||
            print("#undef branch", file=f)
 | 
			
		||||
            print("    } while (0);", file=f)
 | 
			
		||||
            print("", file=f)
 | 
			
		||||
            print("rollback_label:", file=f)
 | 
			
		||||
            print("    YS_ATTRIBUTE(unused);", file=f)
 | 
			
		||||
 | 
			
		||||
            if len(restore_st) or len(nonconst_st):
 | 
			
		||||
                print("", file=f)
 | 
			
		||||
                for s in sorted(restore_st):
 | 
			
		||||
                    t = state_types[s]
 | 
			
		||||
                    print("    {} = backup_{};".format(s, s), file=f)
 | 
			
		||||
                for s in sorted(nonconst_st):
 | 
			
		||||
                    if s not in restore_st:
 | 
			
		||||
                        t = state_types[s]
 | 
			
		||||
                        if t.endswith("*"):
 | 
			
		||||
                            print("    {} = nullptr;".format(s), file=f)
 | 
			
		||||
                        else:
 | 
			
		||||
                            print("    {} = {}();".format(s, t), file=f)
 | 
			
		||||
 | 
			
		||||
        elif block["type"] == "match":
 | 
			
		||||
            assert len(restore_st) == 0
 | 
			
		||||
 | 
			
		||||
            if len(block["if"]):
 | 
			
		||||
                for expr in block["if"]:
 | 
			
		||||
                    print("", file=f)
 | 
			
		||||
                    print("    if (!({})) {{".format(expr), file=f)
 | 
			
		||||
                    print("      {} = nullptr;".format(block["cell"]), file=f)
 | 
			
		||||
                    print("      block_{}();".format(index+1), file=f)
 | 
			
		||||
                    print("      return;", file=f)
 | 
			
		||||
                    print("    }", file=f)
 | 
			
		||||
 | 
			
		||||
            print("", file=f)
 | 
			
		||||
            print("    index_{}_key_type key;".format(index), file=f)
 | 
			
		||||
            for field, entry in enumerate(block["index"]):
 | 
			
		||||
                print("    std::get<{}>(key) = {};".format(field, entry[2]), file=f)
 | 
			
		||||
            print("    const vector<Cell*> &cells = index_{}[key];".format(index), file=f)
 | 
			
		||||
 | 
			
		||||
            print("", file=f)
 | 
			
		||||
            print("    for (int idx = 0; idx < GetSize(cells); idx++) {", file=f)
 | 
			
		||||
            print("      {} = cells[idx];".format(block["cell"]), file=f)
 | 
			
		||||
            print("      if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f)
 | 
			
		||||
            for expr in block["filter"]:
 | 
			
		||||
                print("      if (!({})) continue;".format(expr), file=f)
 | 
			
		||||
            print("      block_{}();".format(index+1), file=f)
 | 
			
		||||
            print("      if (rollback) {", file=f)
 | 
			
		||||
            print("        if (rollback != {}) {{".format(index+1), file=f)
 | 
			
		||||
            print("          {} = nullptr;".format(block["cell"]), file=f)
 | 
			
		||||
            print("          return;", file=f)
 | 
			
		||||
            print("        }", file=f)
 | 
			
		||||
            print("        rollback = 0;", file=f)
 | 
			
		||||
            print("      }", file=f)
 | 
			
		||||
            print("    }", file=f)
 | 
			
		||||
 | 
			
		||||
            print("", file=f)
 | 
			
		||||
            print("    {} = nullptr;".format(block["cell"]), file=f)
 | 
			
		||||
 | 
			
		||||
            if block["optional"]:
 | 
			
		||||
                print("    block_{}();".format(index+1), file=f)
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            assert False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        print("  }", file=f)
 | 
			
		||||
        print("", file=f)
 | 
			
		||||
 | 
			
		||||
    print("  void block_{}() {{".format(len(blocks)), file=f)
 | 
			
		||||
    print("    on_accept();", file=f)
 | 
			
		||||
    print("    check_blacklist();", file=f)
 | 
			
		||||
    print("  }", file=f)
 | 
			
		||||
    print("};", file=f)
 | 
			
		||||
 | 
			
		||||
    print("", file=f)
 | 
			
		||||
    print("YOSYS_NAMESPACE_END", file=f)
 | 
			
		||||
 | 
			
		||||
# pp.pprint(blocks)
 | 
			
		||||
| 
						 | 
				
			
			@ -327,8 +327,26 @@ void extract_cell(RTLIL::Cell *cell, bool keepff)
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string remap_name(RTLIL::IdString abc_name)
 | 
			
		||||
std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullptr)
 | 
			
		||||
{
 | 
			
		||||
	std::string abc_sname = abc_name.substr(1);
 | 
			
		||||
	if (abc_sname.substr(0, 5) == "ys__n") {
 | 
			
		||||
		int sid = std::stoi(abc_sname.substr(5));
 | 
			
		||||
		bool inv = abc_sname.back() == 'v';
 | 
			
		||||
		for (auto sig : signal_list) {
 | 
			
		||||
			if (sig.id == sid && sig.bit.wire != nullptr) {
 | 
			
		||||
				std::stringstream sstr;
 | 
			
		||||
				sstr << "$abc$" << map_autoidx << "$" << sig.bit.wire->name.substr(1);
 | 
			
		||||
				if (sig.bit.wire->width != 1)
 | 
			
		||||
					sstr << "[" << sig.bit.offset << "]";
 | 
			
		||||
				if (inv)
 | 
			
		||||
					sstr << "_inv";
 | 
			
		||||
				if (orig_wire != nullptr)
 | 
			
		||||
					*orig_wire = sig.bit.wire;
 | 
			
		||||
				return sstr.str();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	std::stringstream sstr;
 | 
			
		||||
	sstr << "$abc$" << map_autoidx << "$" << abc_name.substr(1);
 | 
			
		||||
	return sstr.str();
 | 
			
		||||
| 
						 | 
				
			
			@ -353,12 +371,12 @@ void dump_loop_graph(FILE *f, int &nr, std::map<int, std::set<int>> &edges, std:
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	for (auto n : nodes)
 | 
			
		||||
		fprintf(f, "  n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, log_signal(signal_list[n].bit),
 | 
			
		||||
		fprintf(f, "  ys__n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, log_signal(signal_list[n].bit),
 | 
			
		||||
				n, in_counts[n], workpool.count(n) ? ", shape=box" : "");
 | 
			
		||||
 | 
			
		||||
	for (auto &e : edges)
 | 
			
		||||
	for (auto n : e.second)
 | 
			
		||||
		fprintf(f, "  n%d -> n%d;\n", e.first, n);
 | 
			
		||||
		fprintf(f, "  ys__n%d -> ys__n%d;\n", e.first, n);
 | 
			
		||||
 | 
			
		||||
	fprintf(f, "}\n");
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -624,7 +642,7 @@ struct abc_output_filter
 | 
			
		|||
void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file,
 | 
			
		||||
		std::string liberty_file, std::string constr_file, bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str,
 | 
			
		||||
		bool keepff, std::string delay_target, std::string sop_inputs, std::string sop_products, std::string lutin_shared, bool fast_mode,
 | 
			
		||||
		const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode)
 | 
			
		||||
		const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode, bool abc_dress)
 | 
			
		||||
{
 | 
			
		||||
	module = current_module;
 | 
			
		||||
	map_autoidx = autoidx++;
 | 
			
		||||
| 
						 | 
				
			
			@ -728,7 +746,8 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
 | 
			
		|||
 | 
			
		||||
	for (size_t pos = abc_script.find("{S}"); pos != std::string::npos; pos = abc_script.find("{S}", pos))
 | 
			
		||||
		abc_script = abc_script.substr(0, pos) + lutin_shared + abc_script.substr(pos+3);
 | 
			
		||||
 | 
			
		||||
	if (abc_dress)
 | 
			
		||||
		abc_script += "; dress";
 | 
			
		||||
	abc_script += stringf("; write_blif %s/output.blif", tempdir_name.c_str());
 | 
			
		||||
	abc_script = add_echos_to_abc_cmd(abc_script);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -784,7 +803,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
 | 
			
		|||
	for (auto &si : signal_list) {
 | 
			
		||||
		if (!si.is_port || si.type != G(NONE))
 | 
			
		||||
			continue;
 | 
			
		||||
		fprintf(f, " n%d", si.id);
 | 
			
		||||
		fprintf(f, " ys__n%d", si.id);
 | 
			
		||||
		pi_map[count_input++] = log_signal(si.bit);
 | 
			
		||||
	}
 | 
			
		||||
	if (count_input == 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -796,17 +815,17 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
 | 
			
		|||
	for (auto &si : signal_list) {
 | 
			
		||||
		if (!si.is_port || si.type == G(NONE))
 | 
			
		||||
			continue;
 | 
			
		||||
		fprintf(f, " n%d", si.id);
 | 
			
		||||
		fprintf(f, " ys__n%d", si.id);
 | 
			
		||||
		po_map[count_output++] = log_signal(si.bit);
 | 
			
		||||
	}
 | 
			
		||||
	fprintf(f, "\n");
 | 
			
		||||
 | 
			
		||||
	for (auto &si : signal_list)
 | 
			
		||||
		fprintf(f, "# n%-5d %s\n", si.id, log_signal(si.bit));
 | 
			
		||||
		fprintf(f, "# ys__n%-5d %s\n", si.id, log_signal(si.bit));
 | 
			
		||||
 | 
			
		||||
	for (auto &si : signal_list) {
 | 
			
		||||
		if (si.bit.wire == NULL) {
 | 
			
		||||
			fprintf(f, ".names n%d\n", si.id);
 | 
			
		||||
			fprintf(f, ".names ys__n%d\n", si.id);
 | 
			
		||||
			if (si.bit == RTLIL::State::S1)
 | 
			
		||||
				fprintf(f, "1\n");
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -815,68 +834,68 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
 | 
			
		|||
	int count_gates = 0;
 | 
			
		||||
	for (auto &si : signal_list) {
 | 
			
		||||
		if (si.type == G(BUF)) {
 | 
			
		||||
			fprintf(f, ".names n%d n%d\n", si.in1, si.id);
 | 
			
		||||
			fprintf(f, ".names ys__n%d ys__n%d\n", si.in1, si.id);
 | 
			
		||||
			fprintf(f, "1 1\n");
 | 
			
		||||
		} else if (si.type == G(NOT)) {
 | 
			
		||||
			fprintf(f, ".names n%d n%d\n", si.in1, si.id);
 | 
			
		||||
			fprintf(f, ".names ys__n%d ys__n%d\n", si.in1, si.id);
 | 
			
		||||
			fprintf(f, "0 1\n");
 | 
			
		||||
		} else if (si.type == G(AND)) {
 | 
			
		||||
			fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
 | 
			
		||||
			fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
 | 
			
		||||
			fprintf(f, "11 1\n");
 | 
			
		||||
		} else if (si.type == G(NAND)) {
 | 
			
		||||
			fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
 | 
			
		||||
			fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
 | 
			
		||||
			fprintf(f, "0- 1\n");
 | 
			
		||||
			fprintf(f, "-0 1\n");
 | 
			
		||||
		} else if (si.type == G(OR)) {
 | 
			
		||||
			fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
 | 
			
		||||
			fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
 | 
			
		||||
			fprintf(f, "-1 1\n");
 | 
			
		||||
			fprintf(f, "1- 1\n");
 | 
			
		||||
		} else if (si.type == G(NOR)) {
 | 
			
		||||
			fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
 | 
			
		||||
			fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
 | 
			
		||||
			fprintf(f, "00 1\n");
 | 
			
		||||
		} else if (si.type == G(XOR)) {
 | 
			
		||||
			fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
 | 
			
		||||
			fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
 | 
			
		||||
			fprintf(f, "01 1\n");
 | 
			
		||||
			fprintf(f, "10 1\n");
 | 
			
		||||
		} else if (si.type == G(XNOR)) {
 | 
			
		||||
			fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
 | 
			
		||||
			fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
 | 
			
		||||
			fprintf(f, "00 1\n");
 | 
			
		||||
			fprintf(f, "11 1\n");
 | 
			
		||||
		} else if (si.type == G(ANDNOT)) {
 | 
			
		||||
			fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
 | 
			
		||||
			fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
 | 
			
		||||
			fprintf(f, "10 1\n");
 | 
			
		||||
		} else if (si.type == G(ORNOT)) {
 | 
			
		||||
			fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
 | 
			
		||||
			fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
 | 
			
		||||
			fprintf(f, "1- 1\n");
 | 
			
		||||
			fprintf(f, "-0 1\n");
 | 
			
		||||
		} else if (si.type == G(MUX)) {
 | 
			
		||||
			fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id);
 | 
			
		||||
			fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id);
 | 
			
		||||
			fprintf(f, "1-0 1\n");
 | 
			
		||||
			fprintf(f, "-11 1\n");
 | 
			
		||||
		} else if (si.type == G(AOI3)) {
 | 
			
		||||
			fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id);
 | 
			
		||||
			fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id);
 | 
			
		||||
			fprintf(f, "-00 1\n");
 | 
			
		||||
			fprintf(f, "0-0 1\n");
 | 
			
		||||
		} else if (si.type == G(OAI3)) {
 | 
			
		||||
			fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id);
 | 
			
		||||
			fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id);
 | 
			
		||||
			fprintf(f, "00- 1\n");
 | 
			
		||||
			fprintf(f, "--0 1\n");
 | 
			
		||||
		} else if (si.type == G(AOI4)) {
 | 
			
		||||
			fprintf(f, ".names n%d n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);
 | 
			
		||||
			fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);
 | 
			
		||||
			fprintf(f, "-0-0 1\n");
 | 
			
		||||
			fprintf(f, "-00- 1\n");
 | 
			
		||||
			fprintf(f, "0--0 1\n");
 | 
			
		||||
			fprintf(f, "0-0- 1\n");
 | 
			
		||||
		} else if (si.type == G(OAI4)) {
 | 
			
		||||
			fprintf(f, ".names n%d n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);
 | 
			
		||||
			fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);
 | 
			
		||||
			fprintf(f, "00-- 1\n");
 | 
			
		||||
			fprintf(f, "--00 1\n");
 | 
			
		||||
		} else if (si.type == G(FF)) {
 | 
			
		||||
			if (si.init == State::S0 || si.init == State::S1) {
 | 
			
		||||
				fprintf(f, ".latch n%d n%d %d\n", si.in1, si.id, si.init == State::S1 ? 1 : 0);
 | 
			
		||||
				fprintf(f, ".latch ys__n%d ys__n%d %d\n", si.in1, si.id, si.init == State::S1 ? 1 : 0);
 | 
			
		||||
				recover_init = true;
 | 
			
		||||
			} else
 | 
			
		||||
				fprintf(f, ".latch n%d n%d 2\n", si.in1, si.id);
 | 
			
		||||
				fprintf(f, ".latch ys__n%d ys__n%d 2\n", si.in1, si.id);
 | 
			
		||||
		} else if (si.type != G(NONE))
 | 
			
		||||
			log_abort();
 | 
			
		||||
		if (si.type != G(NONE))
 | 
			
		||||
| 
						 | 
				
			
			@ -889,7 +908,6 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
 | 
			
		|||
	log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n",
 | 
			
		||||
			count_gates, GetSize(signal_list), count_input, count_output);
 | 
			
		||||
	log_push();
 | 
			
		||||
 | 
			
		||||
	if (count_output > 0)
 | 
			
		||||
	{
 | 
			
		||||
		log_header(design, "Executing ABC.\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -988,7 +1006,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
 | 
			
		|||
			log_error("ABC output file does not contain a module `netlist'.\n");
 | 
			
		||||
		for (auto &it : mapped_mod->wires_) {
 | 
			
		||||
			RTLIL::Wire *w = it.second;
 | 
			
		||||
			RTLIL::Wire *wire = module->addWire(remap_name(w->name));
 | 
			
		||||
			RTLIL::Wire *orig_wire = nullptr;
 | 
			
		||||
			RTLIL::Wire *wire = module->addWire(remap_name(w->name, &orig_wire));
 | 
			
		||||
			if (orig_wire != nullptr && orig_wire->attributes.count("\\src"))
 | 
			
		||||
				wire->attributes["\\src"] = orig_wire->attributes["\\src"];
 | 
			
		||||
			if (markgroups) wire->attributes["\\abcgroup"] = map_autoidx;
 | 
			
		||||
			design->select(module, wire);
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -1213,7 +1234,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
 | 
			
		|||
		for (auto &si : signal_list)
 | 
			
		||||
			if (si.is_port) {
 | 
			
		||||
				char buffer[100];
 | 
			
		||||
				snprintf(buffer, 100, "\\n%d", si.id);
 | 
			
		||||
				snprintf(buffer, 100, "\\ys__n%d", si.id);
 | 
			
		||||
				RTLIL::SigSig conn;
 | 
			
		||||
				if (si.type != G(NONE)) {
 | 
			
		||||
					conn.first = si.bit;
 | 
			
		||||
| 
						 | 
				
			
			@ -1407,6 +1428,11 @@ struct AbcPass : public Pass {
 | 
			
		|||
		log("        this attribute is a unique integer for each ABC process started. This\n");
 | 
			
		||||
		log("        is useful for debugging the partitioning of clock domains.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -dress\n");
 | 
			
		||||
		log("        run the 'dress' command after all other ABC commands. This aims to\n");
 | 
			
		||||
		log("        preserve naming by an equivalence check between the original and post-ABC\n");
 | 
			
		||||
		log("        netlists (experimental).\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("When neither -liberty nor -lut is used, the Yosys standard cell library is\n");
 | 
			
		||||
		log("loaded into ABC before the ABC script is executed.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -1441,6 +1467,7 @@ struct AbcPass : public Pass {
 | 
			
		|||
		std::string delay_target, sop_inputs, sop_products, lutin_shared = "-S 1";
 | 
			
		||||
		bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true;
 | 
			
		||||
		bool show_tempdir = false, sop_mode = false;
 | 
			
		||||
		bool abc_dress = false;
 | 
			
		||||
		vector<int> lut_costs;
 | 
			
		||||
		markgroups = false;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1555,6 +1582,10 @@ struct AbcPass : public Pass {
 | 
			
		|||
				map_mux16 = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (arg == "-dress") {
 | 
			
		||||
				abc_dress = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (arg == "-g" && argidx+1 < args.size()) {
 | 
			
		||||
				for (auto g : split_tokens(args[++argidx], ",")) {
 | 
			
		||||
					vector<string> gate_list;
 | 
			
		||||
| 
						 | 
				
			
			@ -1704,7 +1735,7 @@ struct AbcPass : public Pass {
 | 
			
		|||
 | 
			
		||||
			if (!dff_mode || !clk_str.empty()) {
 | 
			
		||||
				abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff,
 | 
			
		||||
						delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode);
 | 
			
		||||
						delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode, abc_dress);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1849,7 +1880,7 @@ struct AbcPass : public Pass {
 | 
			
		|||
				en_polarity = std::get<2>(it.first);
 | 
			
		||||
				en_sig = assign_map(std::get<3>(it.first));
 | 
			
		||||
				abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$",
 | 
			
		||||
						keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode);
 | 
			
		||||
						keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode, abc_dress);
 | 
			
		||||
				assign_map.set(mod);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -268,9 +268,9 @@ struct SynthEcp5Pass : public ScriptPass
 | 
			
		|||
			}
 | 
			
		||||
			run("techmap -map +/ecp5/latches_map.v");
 | 
			
		||||
			if (nomux)
 | 
			
		||||
				run("abc -lut 4");
 | 
			
		||||
				run("abc -lut 4 -dress");
 | 
			
		||||
			else
 | 
			
		||||
				run("abc -lut 4:7");
 | 
			
		||||
				run("abc -lut 4:7 -dress");
 | 
			
		||||
			run("clean");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -886,59 +886,6 @@ module SB_WARMBOOT (
 | 
			
		|||
);
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// UltraPlus feature cells
 | 
			
		||||
(* blackbox *)
 | 
			
		||||
module SB_MAC16 (
 | 
			
		||||
	input CLK,
 | 
			
		||||
	input CE,
 | 
			
		||||
	input [15:0] C,
 | 
			
		||||
	input [15:0] A,
 | 
			
		||||
	input [15:0] B,
 | 
			
		||||
	input [15:0] D,
 | 
			
		||||
	input AHOLD,
 | 
			
		||||
	input BHOLD,
 | 
			
		||||
	input CHOLD,
 | 
			
		||||
	input DHOLD,
 | 
			
		||||
	input IRSTTOP,
 | 
			
		||||
	input IRSTBOT,
 | 
			
		||||
	input ORSTTOP,
 | 
			
		||||
	input ORSTBOT,
 | 
			
		||||
	input OLOADTOP,
 | 
			
		||||
	input OLOADBOT,
 | 
			
		||||
	input ADDSUBTOP,
 | 
			
		||||
	input ADDSUBBOT,
 | 
			
		||||
	input OHOLDTOP,
 | 
			
		||||
	input OHOLDBOT,
 | 
			
		||||
	input CI,
 | 
			
		||||
	input ACCUMCI,
 | 
			
		||||
	input SIGNEXTIN,
 | 
			
		||||
	output [31:0] O,
 | 
			
		||||
	output CO,
 | 
			
		||||
	output ACCUMCO,
 | 
			
		||||
	output SIGNEXTOUT
 | 
			
		||||
);
 | 
			
		||||
parameter NEG_TRIGGER = 1'b0;
 | 
			
		||||
parameter C_REG = 1'b0;
 | 
			
		||||
parameter A_REG = 1'b0;
 | 
			
		||||
parameter B_REG = 1'b0;
 | 
			
		||||
parameter D_REG = 1'b0;
 | 
			
		||||
parameter TOP_8x8_MULT_REG = 1'b0;
 | 
			
		||||
parameter BOT_8x8_MULT_REG = 1'b0;
 | 
			
		||||
parameter PIPELINE_16x16_MULT_REG1 = 1'b0;
 | 
			
		||||
parameter PIPELINE_16x16_MULT_REG2 = 1'b0;
 | 
			
		||||
parameter TOPOUTPUT_SELECT =  2'b00;
 | 
			
		||||
parameter TOPADDSUB_LOWERINPUT = 2'b00;
 | 
			
		||||
parameter TOPADDSUB_UPPERINPUT = 1'b0;
 | 
			
		||||
parameter TOPADDSUB_CARRYSELECT = 2'b00;
 | 
			
		||||
parameter BOTOUTPUT_SELECT =  2'b00;
 | 
			
		||||
parameter BOTADDSUB_LOWERINPUT = 2'b00;
 | 
			
		||||
parameter BOTADDSUB_UPPERINPUT = 1'b0;
 | 
			
		||||
parameter BOTADDSUB_CARRYSELECT = 2'b00;
 | 
			
		||||
parameter MODE_8x8 = 1'b0;
 | 
			
		||||
parameter A_SIGNED = 1'b0;
 | 
			
		||||
parameter B_SIGNED = 1'b0;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
module SB_SPRAM256KA (
 | 
			
		||||
	input [13:0] ADDRESS,
 | 
			
		||||
	input [15:0] DATAIN,
 | 
			
		||||
| 
						 | 
				
			
			@ -1273,3 +1220,171 @@ module SB_IO_OD (
 | 
			
		|||
	endgenerate
 | 
			
		||||
`endif
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
module SB_MAC16 (
 | 
			
		||||
	input CLK, CE,
 | 
			
		||||
	input [15:0] C, A, B, D,
 | 
			
		||||
	input AHOLD, BHOLD, CHOLD, DHOLD,
 | 
			
		||||
	input IRSTTOP, IRSTBOT,
 | 
			
		||||
	input ORSTTOP, ORSTBOT,
 | 
			
		||||
	input OLOADTOP, OLOADBOT,
 | 
			
		||||
	input ADDSUBTOP, ADDSUBBOT,
 | 
			
		||||
	input OHOLDTOP, OHOLDBOT,
 | 
			
		||||
	input CI, ACCUMCI, SIGNEXTIN,
 | 
			
		||||
	output [31:0] O,
 | 
			
		||||
	output CO, ACCUMCO, SIGNEXTOUT
 | 
			
		||||
);
 | 
			
		||||
	parameter [0:0] NEG_TRIGGER = 0;
 | 
			
		||||
	parameter [0:0] C_REG = 0;
 | 
			
		||||
	parameter [0:0] A_REG = 0;
 | 
			
		||||
	parameter [0:0] B_REG = 0;
 | 
			
		||||
	parameter [0:0] D_REG = 0;
 | 
			
		||||
	parameter [0:0] TOP_8x8_MULT_REG = 0;
 | 
			
		||||
	parameter [0:0] BOT_8x8_MULT_REG = 0;
 | 
			
		||||
	parameter [0:0] PIPELINE_16x16_MULT_REG1 = 0;
 | 
			
		||||
	parameter [0:0] PIPELINE_16x16_MULT_REG2 = 0;
 | 
			
		||||
	parameter [1:0] TOPOUTPUT_SELECT = 0;
 | 
			
		||||
	parameter [1:0] TOPADDSUB_LOWERINPUT = 0;
 | 
			
		||||
	parameter [0:0] TOPADDSUB_UPPERINPUT = 0;
 | 
			
		||||
	parameter [1:0] TOPADDSUB_CARRYSELECT = 0;
 | 
			
		||||
	parameter [1:0] BOTOUTPUT_SELECT = 0;
 | 
			
		||||
	parameter [1:0] BOTADDSUB_LOWERINPUT = 0;
 | 
			
		||||
	parameter [0:0] BOTADDSUB_UPPERINPUT = 0;
 | 
			
		||||
	parameter [1:0] BOTADDSUB_CARRYSELECT = 0;
 | 
			
		||||
	parameter [0:0] MODE_8x8 = 0;
 | 
			
		||||
	parameter [0:0] A_SIGNED = 0;
 | 
			
		||||
	parameter [0:0] B_SIGNED = 0;
 | 
			
		||||
 | 
			
		||||
	wire clock = CLK ^ NEG_TRIGGER;
 | 
			
		||||
 | 
			
		||||
	// internal wires, compare Figure on page 133 of ICE Technology Library 3.0 and Fig 2 on page 2 of Lattice TN1295-DSP
 | 
			
		||||
	// http://www.latticesemi.com/~/media/LatticeSemi/Documents/TechnicalBriefs/SBTICETechnologyLibrary201608.pdf
 | 
			
		||||
	// https://www.latticesemi.com/-/media/LatticeSemi/Documents/ApplicationNotes/AD/DSPFunctionUsageGuideforICE40Devices.ashx
 | 
			
		||||
	wire [15:0] iA, iB, iC, iD;
 | 
			
		||||
	wire [15:0] iF, iJ, iK, iG;
 | 
			
		||||
	wire [31:0] iL, iH;
 | 
			
		||||
	wire [15:0] iW, iX, iP, iQ;
 | 
			
		||||
	wire [15:0] iY, iZ, iR, iS;
 | 
			
		||||
	wire HCI, LCI, LCO;
 | 
			
		||||
 | 
			
		||||
	// Regs C and A
 | 
			
		||||
	reg [15:0] rC, rA;
 | 
			
		||||
	always @(posedge clock, posedge IRSTTOP) begin
 | 
			
		||||
		if (IRSTTOP) begin
 | 
			
		||||
			rC <= 0;
 | 
			
		||||
			rA <= 0;
 | 
			
		||||
		end else if (CE) begin
 | 
			
		||||
			if (!CHOLD) rC <= C;
 | 
			
		||||
			if (!AHOLD) rA <= A;
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
	assign iC = C_REG ? rC : C;
 | 
			
		||||
	assign iA = A_REG ? rA : A;
 | 
			
		||||
 | 
			
		||||
	// Regs B and D
 | 
			
		||||
	reg [15:0] rB, rD;
 | 
			
		||||
	always @(posedge clock, posedge IRSTBOT) begin
 | 
			
		||||
		if (IRSTBOT) begin
 | 
			
		||||
			rB <= 0;
 | 
			
		||||
			rD <= 0;
 | 
			
		||||
		end else if (CE) begin
 | 
			
		||||
			if (!BHOLD) rB <= B;
 | 
			
		||||
			if (!DHOLD) rD <= D;
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
	assign iB = B_REG ? rB : B;
 | 
			
		||||
	assign iD = D_REG ? rD : D;
 | 
			
		||||
 | 
			
		||||
	// Multiplier Stage
 | 
			
		||||
	wire [15:0] p_Ah_Bh, p_Al_Bh, p_Ah_Bl, p_Al_Bl;
 | 
			
		||||
	wire [15:0] Ah, Al, Bh, Bl;
 | 
			
		||||
	assign Ah = {A_SIGNED ? {8{iA[15]}} : 8'b0, iA[15: 8]};
 | 
			
		||||
	assign Al = {A_SIGNED ? {8{iA[ 7]}} : 8'b0, iA[ 7: 0]};
 | 
			
		||||
	assign Bh = {B_SIGNED ? {8{iB[15]}} : 8'b0, iB[15: 8]};
 | 
			
		||||
	assign Bl = {B_SIGNED ? {8{iB[ 7]}} : 8'b0, iB[ 7: 0]};
 | 
			
		||||
	assign p_Ah_Bh = Ah * Bh;
 | 
			
		||||
	assign p_Al_Bh = Al * Bh;
 | 
			
		||||
	assign p_Ah_Bl = Ah * Bl;
 | 
			
		||||
	assign p_Al_Bl = Al * Bl;
 | 
			
		||||
 | 
			
		||||
	// Regs F and J
 | 
			
		||||
	reg [15:0] rF, rJ;
 | 
			
		||||
	always @(posedge clock, posedge IRSTTOP) begin
 | 
			
		||||
		if (IRSTTOP) begin
 | 
			
		||||
			rF <= 0;
 | 
			
		||||
			rJ <= 0;
 | 
			
		||||
		end else if (CE) begin
 | 
			
		||||
			rF <= p_Ah_Bh;
 | 
			
		||||
			if (!MODE_8x8) rJ <= p_Al_Bh;
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
	assign iF = TOP_8x8_MULT_REG ? rF : p_Ah_Bh;
 | 
			
		||||
	assign iJ = PIPELINE_16x16_MULT_REG1 ? rJ : p_Al_Bh;
 | 
			
		||||
 | 
			
		||||
	// Regs K and G
 | 
			
		||||
	reg [15:0] rK, rG;
 | 
			
		||||
	always @(posedge clock, posedge IRSTBOT) begin
 | 
			
		||||
		if (IRSTBOT) begin
 | 
			
		||||
			rK <= 0;
 | 
			
		||||
			rG <= 0;
 | 
			
		||||
		end else if (CE) begin
 | 
			
		||||
			if (!MODE_8x8) rK <= p_Ah_Bl;
 | 
			
		||||
			rG <= p_Al_Bl;
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
	assign iK = PIPELINE_16x16_MULT_REG1 ? rK : p_Ah_Bl;
 | 
			
		||||
	assign iG = BOT_8x8_MULT_REG ? rG : p_Al_Bl;
 | 
			
		||||
 | 
			
		||||
	// Adder Stage
 | 
			
		||||
	assign iL = iG + (iK << 8) + (iJ << 8) + (iF << 16);
 | 
			
		||||
 | 
			
		||||
	// Reg H
 | 
			
		||||
	reg [31:0] rH;
 | 
			
		||||
	always @(posedge clock, posedge IRSTBOT) begin
 | 
			
		||||
		if (IRSTBOT) begin
 | 
			
		||||
			rH <= 0;
 | 
			
		||||
		end else if (CE) begin
 | 
			
		||||
			if (!MODE_8x8) rH <= iL;
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
	assign iH = PIPELINE_16x16_MULT_REG2 ? rH : iL;
 | 
			
		||||
 | 
			
		||||
	// Hi Output Stage
 | 
			
		||||
	wire [15:0] XW, Oh;
 | 
			
		||||
	reg [15:0] rQ;
 | 
			
		||||
	assign iW = TOPADDSUB_UPPERINPUT ? iC : iQ;
 | 
			
		||||
	assign iX = (TOPADDSUB_LOWERINPUT == 0) ? iA : (TOPADDSUB_LOWERINPUT == 1) ? iF : (TOPADDSUB_LOWERINPUT == 2) ? iH[31:16] : {16{iZ[15]}};
 | 
			
		||||
	assign {ACCUMCO, XW} = iX + (iW ^ {16{ADDSUBTOP}}) + HCI;
 | 
			
		||||
	assign CO = ACCUMCO ^ ADDSUBTOP;
 | 
			
		||||
	assign iP = OLOADTOP ? iC : XW ^ {16{ADDSUBTOP}};
 | 
			
		||||
	always @(posedge clock, posedge ORSTTOP) begin
 | 
			
		||||
		if (ORSTTOP) begin
 | 
			
		||||
			rQ <= 0;
 | 
			
		||||
		end else if (CE) begin
 | 
			
		||||
			if (!OHOLDTOP) rQ <= iP;
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
	assign iQ = rQ;
 | 
			
		||||
	assign Oh = (TOPOUTPUT_SELECT == 0) ? iP : (TOPOUTPUT_SELECT == 1) ? iQ : (TOPOUTPUT_SELECT == 2) ? iF : iH[31:16];
 | 
			
		||||
	assign HCI = (TOPADDSUB_CARRYSELECT == 0) ? 1'b0 : (TOPADDSUB_CARRYSELECT == 1) ? 1'b1 : (TOPADDSUB_CARRYSELECT == 2) ? LCO : LCO ^ ADDSUBBOT;
 | 
			
		||||
	assign SIGNEXTOUT = iX[15];
 | 
			
		||||
 | 
			
		||||
	// Lo Output Stage
 | 
			
		||||
	wire [15:0] YZ, Ol;
 | 
			
		||||
	reg [15:0] rS;
 | 
			
		||||
	assign iY = BOTADDSUB_UPPERINPUT ? iD : iS;
 | 
			
		||||
	assign iZ = (BOTADDSUB_LOWERINPUT == 0) ? iB : (BOTADDSUB_LOWERINPUT == 1) ? iG : (BOTADDSUB_LOWERINPUT == 2) ? iH[15:0] : {16{SIGNEXTIN}};
 | 
			
		||||
	assign {LCO, YZ} = iZ + (iY ^ {16{ADDSUBBOT}}) + LCI;
 | 
			
		||||
	assign iR = OLOADBOT ? iD : YZ ^ {16{ADDSUBBOT}};
 | 
			
		||||
	always @(posedge clock, posedge ORSTBOT) begin
 | 
			
		||||
		if (ORSTBOT) begin
 | 
			
		||||
			rS <= 0;
 | 
			
		||||
		end else if (CE) begin
 | 
			
		||||
			if (!OHOLDBOT) rS <= iR;
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
	assign iS = rS;
 | 
			
		||||
	assign Ol = (BOTOUTPUT_SELECT == 0) ? iR : (BOTOUTPUT_SELECT == 1) ? iS : (BOTOUTPUT_SELECT == 2) ? iG : iH[15:0];
 | 
			
		||||
	assign LCI = (BOTADDSUB_CARRYSELECT == 0) ? 1'b0 : (BOTADDSUB_CARRYSELECT == 1) ? 1'b1 : (BOTADDSUB_CARRYSELECT == 2) ? ACCUMCI : CI;
 | 
			
		||||
	assign O = {Oh, Ol};
 | 
			
		||||
endmodule
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -79,6 +79,9 @@ struct SynthIce40Pass : public ScriptPass
 | 
			
		|||
		log("    -nobram\n");
 | 
			
		||||
		log("        do not use SB_RAM40_4K* cells in output netlist\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -dsp\n");
 | 
			
		||||
		log("        use iCE40 UltraPlus DSP cells for large arithmetic\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -noabc\n");
 | 
			
		||||
		log("        use built-in Yosys LUT techmapping instead of abc\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -96,7 +99,7 @@ struct SynthIce40Pass : public ScriptPass
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	string top_opt, blif_file, edif_file, json_file;
 | 
			
		||||
	bool nocarry, nodffe, nobram, flatten, retime, relut, noabc, abc2, vpr;
 | 
			
		||||
	bool nocarry, nodffe, nobram, dsp, flatten, retime, relut, noabc, abc2, vpr;
 | 
			
		||||
	int min_ce_use;
 | 
			
		||||
 | 
			
		||||
	void clear_flags() YS_OVERRIDE
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +112,7 @@ struct SynthIce40Pass : public ScriptPass
 | 
			
		|||
		nodffe = false;
 | 
			
		||||
		min_ce_use = -1;
 | 
			
		||||
		nobram = false;
 | 
			
		||||
		dsp = false;
 | 
			
		||||
		flatten = true;
 | 
			
		||||
		retime = false;
 | 
			
		||||
		relut = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -181,6 +185,10 @@ struct SynthIce40Pass : public ScriptPass
 | 
			
		|||
				nobram = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-dsp") {
 | 
			
		||||
				dsp = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-noabc") {
 | 
			
		||||
				noabc = true;
 | 
			
		||||
				continue;
 | 
			
		||||
| 
						 | 
				
			
			@ -214,11 +222,11 @@ struct SynthIce40Pass : public ScriptPass
 | 
			
		|||
		{
 | 
			
		||||
			run("read_verilog -lib +/ice40/cells_sim.v");
 | 
			
		||||
			run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
 | 
			
		||||
			run("proc");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (flatten && check_label("flatten", "(unless -noflatten)"))
 | 
			
		||||
		{
 | 
			
		||||
			run("proc");
 | 
			
		||||
			run("flatten");
 | 
			
		||||
			run("tribuf -logic");
 | 
			
		||||
			run("deminout");
 | 
			
		||||
| 
						 | 
				
			
			@ -226,7 +234,23 @@ struct SynthIce40Pass : public ScriptPass
 | 
			
		|||
 | 
			
		||||
		if (check_label("coarse"))
 | 
			
		||||
		{
 | 
			
		||||
			run("synth -lut 4 -run coarse");
 | 
			
		||||
			run("opt_expr");
 | 
			
		||||
			run("opt_clean");
 | 
			
		||||
			run("check");
 | 
			
		||||
			run("opt");
 | 
			
		||||
			run("wreduce");
 | 
			
		||||
			run("share");
 | 
			
		||||
			run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4");
 | 
			
		||||
			run("opt_expr");
 | 
			
		||||
			run("opt_clean");
 | 
			
		||||
			if (help_mode || dsp)
 | 
			
		||||
				run("ice40_dsp", "(if -dsp)");
 | 
			
		||||
			run("alumacc");
 | 
			
		||||
			run("opt");
 | 
			
		||||
			run("fsm");
 | 
			
		||||
			run("opt -fast");
 | 
			
		||||
			run("memory -nomap");
 | 
			
		||||
			run("opt_clean");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!nobram && check_label("bram", "(skip if -nobram)"))
 | 
			
		||||
| 
						 | 
				
			
			@ -282,7 +306,7 @@ struct SynthIce40Pass : public ScriptPass
 | 
			
		|||
				run("techmap -map +/gate2lut.v -D LUT_WIDTH=4", "(only if -noabc)");
 | 
			
		||||
			}
 | 
			
		||||
			if (!noabc) {
 | 
			
		||||
				run("abc -lut 4", "(skip if -noabc)");
 | 
			
		||||
				run("abc -dress -lut 4", "(skip if -noabc)");
 | 
			
		||||
			}
 | 
			
		||||
			run("clean");
 | 
			
		||||
			if (relut || help_mode) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										13
									
								
								techlibs/ice40/tests/.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								techlibs/ice40/tests/.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -1,2 +1,11 @@
 | 
			
		|||
test_ffs_[01][01][01][01][01]_*
 | 
			
		||||
test_bram_[0-9]*
 | 
			
		||||
/test_ffs_[01][01][01][01][01]_*
 | 
			
		||||
/test_bram_[0-9]*
 | 
			
		||||
/test_dsp_model
 | 
			
		||||
/test_dsp_model.vcd
 | 
			
		||||
/test_dsp_model_ref.v
 | 
			
		||||
/test_dsp_model_uut.v
 | 
			
		||||
/test_dsp_map
 | 
			
		||||
/test_dsp_map.vcd
 | 
			
		||||
/test_dsp_map_tb.v
 | 
			
		||||
/test_dsp_map_top.v
 | 
			
		||||
/test_dsp_map_syn.v
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										107
									
								
								techlibs/ice40/tests/test_dsp_map.sh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								techlibs/ice40/tests/test_dsp_map.sh
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,107 @@
 | 
			
		|||
#!/bin/bash
 | 
			
		||||
set -ex
 | 
			
		||||
 | 
			
		||||
for iter in {1..100}
 | 
			
		||||
do
 | 
			
		||||
	SZA=$(( 3 + $RANDOM % 13 ))
 | 
			
		||||
	SZB=$(( 3 + $RANDOM % 13 ))
 | 
			
		||||
	SZO=$(( 3 + $RANDOM % 29 ))
 | 
			
		||||
 | 
			
		||||
	C0=clk$(( $RANDOM & 1))
 | 
			
		||||
	C1=clk$(( $RANDOM & 1))
 | 
			
		||||
	C2=clk$(( $RANDOM & 1))
 | 
			
		||||
	C3=clk$(( $RANDOM & 1))
 | 
			
		||||
 | 
			
		||||
	E0=$( test $(( $RANDOM & 1 )) -eq 0 && echo posedge || echo negedge )
 | 
			
		||||
	E1=$( test $(( $RANDOM & 1 )) -eq 0 && echo posedge || echo negedge )
 | 
			
		||||
	E2=$( test $(( $RANDOM & 1 )) -eq 0 && echo posedge || echo negedge )
 | 
			
		||||
	E3=$( test $(( $RANDOM & 1 )) -eq 0 && echo posedge || echo negedge )
 | 
			
		||||
 | 
			
		||||
	SP=$( test $(( $RANDOM & 1 )) -eq 0 && echo S || echo P )
 | 
			
		||||
 | 
			
		||||
	RC=$( test $(( $RANDOM & 1 )) -eq 0 && echo "reset" || echo "!reset" )
 | 
			
		||||
	RV="32'h$( echo $RANDOM | md5sum | cut -c1-8 )"
 | 
			
		||||
 | 
			
		||||
	cat > test_dsp_map_top.v << EOT
 | 
			
		||||
module top (
 | 
			
		||||
	input clk0, clk1, reset,
 | 
			
		||||
	input  [$SZA:0] A,
 | 
			
		||||
	input  [$SZB:0] B,
 | 
			
		||||
	output [$SZO:0] O
 | 
			
		||||
);
 | 
			
		||||
	reg [15:0] AA, BB;
 | 
			
		||||
	reg [31:0] P, S;
 | 
			
		||||
 | 
			
		||||
	always @($E0 $C0) AA <= A;
 | 
			
		||||
	always @($E1 $C1) BB <= B;
 | 
			
		||||
	always @($E2 $C2) P <= AA * BB;
 | 
			
		||||
	always @($E3 $C3) S <= $RC ? $RV : S + P;
 | 
			
		||||
	assign O = $SP;
 | 
			
		||||
endmodule
 | 
			
		||||
EOT
 | 
			
		||||
 | 
			
		||||
	cat > test_dsp_map_tb.v << EOT
 | 
			
		||||
\`timescale 1ns / 1ps
 | 
			
		||||
module testbench;
 | 
			
		||||
	reg clk1, clk0, reset;
 | 
			
		||||
	reg [$SZA:0] A;
 | 
			
		||||
	reg [$SZB:0] B;
 | 
			
		||||
 | 
			
		||||
	wire [$SZO:0] O_top, O_syn;
 | 
			
		||||
 | 
			
		||||
	top top_inst (.clk0(clk0), .clk1(clk1), .reset(reset), .A(A), .B(B), .O(O_top));
 | 
			
		||||
	syn syn_inst (.clk0(clk0), .clk1(clk1), .reset(reset), .A(A), .B(B), .O(O_syn));
 | 
			
		||||
 | 
			
		||||
	initial begin
 | 
			
		||||
		// \$dumpfile("test_dsp_map.vcd");
 | 
			
		||||
		// \$dumpvars(0, testbench);
 | 
			
		||||
 | 
			
		||||
		#2;
 | 
			
		||||
		clk0 = 0;
 | 
			
		||||
		clk1 = 0;
 | 
			
		||||
		reset = 1;
 | 
			
		||||
		reset = $RC;
 | 
			
		||||
		A = 0;
 | 
			
		||||
		B = 0;
 | 
			
		||||
 | 
			
		||||
		repeat (3) begin
 | 
			
		||||
			#2; clk0 = ~clk0;
 | 
			
		||||
			#2; clk0 = ~clk0;
 | 
			
		||||
			#2; clk1 = ~clk1;
 | 
			
		||||
			#2; clk1 = ~clk1;
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		repeat (100) begin
 | 
			
		||||
			#2;
 | 
			
		||||
			A = \$urandom;
 | 
			
		||||
			B = \$urandom;
 | 
			
		||||
			reset = \$urandom & \$urandom & \$urandom & \$urandom;
 | 
			
		||||
			if (\$urandom & 1) begin
 | 
			
		||||
				#2; clk0 = ~clk0;
 | 
			
		||||
				#2; clk0 = ~clk0;
 | 
			
		||||
			end else begin
 | 
			
		||||
				#2; clk1 = ~clk1;
 | 
			
		||||
				#2; clk1 = ~clk1;
 | 
			
		||||
			end
 | 
			
		||||
			#2;
 | 
			
		||||
			if (O_top !== O_syn) begin
 | 
			
		||||
				\$display("ERROR: O_top=%b O_syn=%b", O_top, O_syn);
 | 
			
		||||
				\$stop;
 | 
			
		||||
			end
 | 
			
		||||
			// \$display("OK O_top=O_syn=%b", O_top);
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		\$display("Test passed.");
 | 
			
		||||
		\$finish;
 | 
			
		||||
	end
 | 
			
		||||
endmodule
 | 
			
		||||
EOT
 | 
			
		||||
 | 
			
		||||
	../../../yosys -p 'read_verilog test_dsp_map_top.v; synth_ice40 -dsp; rename top syn; write_verilog test_dsp_map_syn.v'
 | 
			
		||||
	iverilog -o test_dsp_map -s testbench test_dsp_map_tb.v test_dsp_map_top.v test_dsp_map_syn.v ../cells_sim.v
 | 
			
		||||
	vvp -N test_dsp_map
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
: ""
 | 
			
		||||
: "####  All tests passed.  ####"
 | 
			
		||||
: ""
 | 
			
		||||
							
								
								
									
										11
									
								
								techlibs/ice40/tests/test_dsp_model.sh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								techlibs/ice40/tests/test_dsp_model.sh
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
#!/bin/bash
 | 
			
		||||
set -ex
 | 
			
		||||
sed 's/SB_MAC16/SB_MAC16_UUT/; /SB_MAC16_UUT/,/endmodule/ p; d;' < ../cells_sim.v > test_dsp_model_uut.v
 | 
			
		||||
cat /opt/lscc/iCEcube2.2017.01/verilog/sb_ice_syn.v > test_dsp_model_ref.v
 | 
			
		||||
for tb in testbench \
 | 
			
		||||
		testbench_comb_8x8_A testbench_comb_8x8_B testbench_comb_16x16 \
 | 
			
		||||
		testbench_seq_16x16_A testbench_seq_16x16_B
 | 
			
		||||
do
 | 
			
		||||
	iverilog -s $tb -o test_dsp_model test_dsp_model.v test_dsp_model_uut.v test_dsp_model_ref.v
 | 
			
		||||
	vvp -N ./test_dsp_model
 | 
			
		||||
done
 | 
			
		||||
							
								
								
									
										342
									
								
								techlibs/ice40/tests/test_dsp_model.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										342
									
								
								techlibs/ice40/tests/test_dsp_model.v
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,342 @@
 | 
			
		|||
`timescale 1ns / 1ps
 | 
			
		||||
 | 
			
		||||
module testbench;
 | 
			
		||||
	parameter [0:0] NEG_TRIGGER = 0;
 | 
			
		||||
	parameter [0:0] C_REG = 0;
 | 
			
		||||
	parameter [0:0] A_REG = 0;
 | 
			
		||||
	parameter [0:0] B_REG = 0;
 | 
			
		||||
	parameter [0:0] D_REG = 0;
 | 
			
		||||
	parameter [0:0] TOP_8x8_MULT_REG = 0;
 | 
			
		||||
	parameter [0:0] BOT_8x8_MULT_REG = 0;
 | 
			
		||||
	parameter [0:0] PIPELINE_16x16_MULT_REG1 = 0;
 | 
			
		||||
	parameter [0:0] PIPELINE_16x16_MULT_REG2 = 0;
 | 
			
		||||
	parameter [1:0] TOPOUTPUT_SELECT = 0;
 | 
			
		||||
	parameter [1:0] TOPADDSUB_LOWERINPUT = 0;
 | 
			
		||||
	parameter [0:0] TOPADDSUB_UPPERINPUT = 1;
 | 
			
		||||
	parameter [1:0] TOPADDSUB_CARRYSELECT = 0;
 | 
			
		||||
	parameter [1:0] BOTOUTPUT_SELECT = 0;
 | 
			
		||||
	parameter [1:0] BOTADDSUB_LOWERINPUT = 0;
 | 
			
		||||
	parameter [0:0] BOTADDSUB_UPPERINPUT = 1;
 | 
			
		||||
	parameter [1:0] BOTADDSUB_CARRYSELECT = 0;
 | 
			
		||||
	parameter [0:0] MODE_8x8 = 0;
 | 
			
		||||
	parameter [0:0] A_SIGNED = 0;
 | 
			
		||||
	parameter [0:0] B_SIGNED = 0;
 | 
			
		||||
 | 
			
		||||
	reg CLK, CE;
 | 
			
		||||
	reg [15:0] C, A, B, D;
 | 
			
		||||
	reg AHOLD, BHOLD, CHOLD, DHOLD;
 | 
			
		||||
	reg IRSTTOP, IRSTBOT;
 | 
			
		||||
	reg ORSTTOP, ORSTBOT;
 | 
			
		||||
	reg OLOADTOP, OLOADBOT;
 | 
			
		||||
	reg ADDSUBTOP, ADDSUBBOT;
 | 
			
		||||
	reg OHOLDTOP, OHOLDBOT;
 | 
			
		||||
	reg CI, ACCUMCI, SIGNEXTIN;
 | 
			
		||||
 | 
			
		||||
	output [31:0] REF_O, UUT_O;
 | 
			
		||||
	output REF_CO, REF_ACCUMCO, REF_SIGNEXTOUT;
 | 
			
		||||
	output UUT_CO, UUT_ACCUMCO, UUT_SIGNEXTOUT;
 | 
			
		||||
 | 
			
		||||
	integer errcount = 0;
 | 
			
		||||
 | 
			
		||||
	task clkcycle;
 | 
			
		||||
		begin
 | 
			
		||||
			#5;
 | 
			
		||||
			CLK = ~CLK;
 | 
			
		||||
			#10;
 | 
			
		||||
			CLK = ~CLK;
 | 
			
		||||
			#2;
 | 
			
		||||
			if (REF_O !== UUT_O) begin
 | 
			
		||||
				$display("ERROR at %1t: REF_O=%b UUT_O=%b DIFF=%b", $time, REF_O, UUT_O, REF_O ^ UUT_O);
 | 
			
		||||
				errcount = errcount + 1;
 | 
			
		||||
			end
 | 
			
		||||
			if (REF_CO !== UUT_CO) begin
 | 
			
		||||
				$display("ERROR at %1t: REF_CO=%b UUT_CO=%b", $time, REF_CO, UUT_CO);
 | 
			
		||||
				errcount = errcount + 1;
 | 
			
		||||
			end
 | 
			
		||||
			if (REF_ACCUMCO !== UUT_ACCUMCO) begin
 | 
			
		||||
				$display("ERROR at %1t: REF_ACCUMCO=%b UUT_ACCUMCO=%b", $time, REF_ACCUMCO, UUT_ACCUMCO);
 | 
			
		||||
				errcount = errcount + 1;
 | 
			
		||||
			end
 | 
			
		||||
			if (REF_SIGNEXTOUT !== UUT_SIGNEXTOUT) begin
 | 
			
		||||
				$display("ERROR at %1t: REF_SIGNEXTOUT=%b UUT_SIGNEXTOUT=%b", $time, REF_SIGNEXTOUT, UUT_SIGNEXTOUT);
 | 
			
		||||
				errcount = errcount + 1;
 | 
			
		||||
			end
 | 
			
		||||
			#3;
 | 
			
		||||
		end
 | 
			
		||||
	endtask
 | 
			
		||||
 | 
			
		||||
	initial begin
 | 
			
		||||
		$dumpfile("test_dsp_model.vcd");
 | 
			
		||||
		$dumpvars(0, testbench);
 | 
			
		||||
 | 
			
		||||
		#2;
 | 
			
		||||
		CLK = NEG_TRIGGER;
 | 
			
		||||
		CE = 1;
 | 
			
		||||
		{C, A, B, D} = 0;
 | 
			
		||||
		{AHOLD, BHOLD, CHOLD, DHOLD} = 0;
 | 
			
		||||
		{OLOADTOP, OLOADBOT} = 0;
 | 
			
		||||
		{ADDSUBTOP, ADDSUBBOT} = 0;
 | 
			
		||||
		{OHOLDTOP, OHOLDBOT} = 0;
 | 
			
		||||
		{CI, ACCUMCI, SIGNEXTIN} = 0;
 | 
			
		||||
 | 
			
		||||
		{IRSTTOP, IRSTBOT} = ~0;
 | 
			
		||||
		{ORSTTOP, ORSTBOT} = ~0;
 | 
			
		||||
 | 
			
		||||
		#3;
 | 
			
		||||
		{IRSTTOP, IRSTBOT} = 0;
 | 
			
		||||
		{ORSTTOP, ORSTBOT} = 0;
 | 
			
		||||
 | 
			
		||||
		repeat (300) begin
 | 
			
		||||
			clkcycle;
 | 
			
		||||
 | 
			
		||||
			A = $urandom;
 | 
			
		||||
			B = $urandom;
 | 
			
		||||
			C = $urandom;
 | 
			
		||||
			D = $urandom;
 | 
			
		||||
 | 
			
		||||
			{AHOLD, BHOLD, CHOLD, DHOLD} = $urandom & $urandom & $urandom;
 | 
			
		||||
			{OLOADTOP, OLOADBOT} = $urandom & $urandom & $urandom;
 | 
			
		||||
			{ADDSUBTOP, ADDSUBBOT} = $urandom & $urandom & $urandom;
 | 
			
		||||
			{OHOLDTOP, OHOLDBOT} = $urandom & $urandom & $urandom;
 | 
			
		||||
			{CI, ACCUMCI, SIGNEXTIN} = $urandom & $urandom & $urandom;
 | 
			
		||||
 | 
			
		||||
			{IRSTTOP, IRSTBOT} = $urandom & $urandom & $urandom;
 | 
			
		||||
			{ORSTTOP, ORSTBOT} = $urandom & $urandom & $urandom;
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		if (errcount == 0) begin
 | 
			
		||||
			$display("All tests passed.");
 | 
			
		||||
			$finish;
 | 
			
		||||
		end else begin
 | 
			
		||||
			$display("Caught %1d errors.", errcount);
 | 
			
		||||
			$stop;
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	SB_MAC16 #(
 | 
			
		||||
		.NEG_TRIGGER              (NEG_TRIGGER             ),
 | 
			
		||||
		.C_REG                    (C_REG                   ),
 | 
			
		||||
		.A_REG                    (A_REG                   ),
 | 
			
		||||
		.B_REG                    (B_REG                   ),
 | 
			
		||||
		.D_REG                    (D_REG                   ),
 | 
			
		||||
		.TOP_8x8_MULT_REG         (TOP_8x8_MULT_REG        ),
 | 
			
		||||
		.BOT_8x8_MULT_REG         (BOT_8x8_MULT_REG        ),
 | 
			
		||||
		.PIPELINE_16x16_MULT_REG1 (PIPELINE_16x16_MULT_REG1),
 | 
			
		||||
		.PIPELINE_16x16_MULT_REG2 (PIPELINE_16x16_MULT_REG2),
 | 
			
		||||
		.TOPOUTPUT_SELECT         (TOPOUTPUT_SELECT        ),
 | 
			
		||||
		.TOPADDSUB_LOWERINPUT     (TOPADDSUB_LOWERINPUT    ),
 | 
			
		||||
		.TOPADDSUB_UPPERINPUT     (TOPADDSUB_UPPERINPUT    ),
 | 
			
		||||
		.TOPADDSUB_CARRYSELECT    (TOPADDSUB_CARRYSELECT   ),
 | 
			
		||||
		.BOTOUTPUT_SELECT         (BOTOUTPUT_SELECT        ),
 | 
			
		||||
		.BOTADDSUB_LOWERINPUT     (BOTADDSUB_LOWERINPUT    ),
 | 
			
		||||
		.BOTADDSUB_UPPERINPUT     (BOTADDSUB_UPPERINPUT    ),
 | 
			
		||||
		.BOTADDSUB_CARRYSELECT    (BOTADDSUB_CARRYSELECT   ),
 | 
			
		||||
		.MODE_8x8                 (MODE_8x8                ),
 | 
			
		||||
		.A_SIGNED                 (A_SIGNED                ),
 | 
			
		||||
		.B_SIGNED                 (B_SIGNED                )
 | 
			
		||||
	) ref (
 | 
			
		||||
		.CLK        (CLK           ),
 | 
			
		||||
		.CE         (CE            ),
 | 
			
		||||
		.C          (C             ),
 | 
			
		||||
		.A          (A             ),
 | 
			
		||||
		.B          (B             ),
 | 
			
		||||
		.D          (D             ),
 | 
			
		||||
		.AHOLD      (AHOLD         ),
 | 
			
		||||
		.BHOLD      (BHOLD         ),
 | 
			
		||||
		.CHOLD      (CHOLD         ),
 | 
			
		||||
		.DHOLD      (DHOLD         ),
 | 
			
		||||
		.IRSTTOP    (IRSTTOP       ),
 | 
			
		||||
		.IRSTBOT    (IRSTBOT       ),
 | 
			
		||||
		.ORSTTOP    (ORSTTOP       ),
 | 
			
		||||
		.ORSTBOT    (ORSTBOT       ),
 | 
			
		||||
		.OLOADTOP   (OLOADTOP      ),
 | 
			
		||||
		.OLOADBOT   (OLOADBOT      ),
 | 
			
		||||
		.ADDSUBTOP  (ADDSUBTOP     ),
 | 
			
		||||
		.ADDSUBBOT  (ADDSUBBOT     ),
 | 
			
		||||
		.OHOLDTOP   (OHOLDTOP      ),
 | 
			
		||||
		.OHOLDBOT   (OHOLDBOT      ),
 | 
			
		||||
		.CI         (CI            ),
 | 
			
		||||
		.ACCUMCI    (ACCUMCI       ),
 | 
			
		||||
		.SIGNEXTIN  (SIGNEXTIN     ),
 | 
			
		||||
		.O          (REF_O         ),
 | 
			
		||||
		.CO         (REF_CO        ),
 | 
			
		||||
		.ACCUMCO    (REF_ACCUMCO   ),
 | 
			
		||||
		.SIGNEXTOUT (REF_SIGNEXTOUT)
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	SB_MAC16_UUT #(
 | 
			
		||||
		.NEG_TRIGGER              (NEG_TRIGGER             ),
 | 
			
		||||
		.C_REG                    (C_REG                   ),
 | 
			
		||||
		.A_REG                    (A_REG                   ),
 | 
			
		||||
		.B_REG                    (B_REG                   ),
 | 
			
		||||
		.D_REG                    (D_REG                   ),
 | 
			
		||||
		.TOP_8x8_MULT_REG         (TOP_8x8_MULT_REG        ),
 | 
			
		||||
		.BOT_8x8_MULT_REG         (BOT_8x8_MULT_REG        ),
 | 
			
		||||
		.PIPELINE_16x16_MULT_REG1 (PIPELINE_16x16_MULT_REG1),
 | 
			
		||||
		.PIPELINE_16x16_MULT_REG2 (PIPELINE_16x16_MULT_REG2),
 | 
			
		||||
		.TOPOUTPUT_SELECT         (TOPOUTPUT_SELECT        ),
 | 
			
		||||
		.TOPADDSUB_LOWERINPUT     (TOPADDSUB_LOWERINPUT    ),
 | 
			
		||||
		.TOPADDSUB_UPPERINPUT     (TOPADDSUB_UPPERINPUT    ),
 | 
			
		||||
		.TOPADDSUB_CARRYSELECT    (TOPADDSUB_CARRYSELECT   ),
 | 
			
		||||
		.BOTOUTPUT_SELECT         (BOTOUTPUT_SELECT        ),
 | 
			
		||||
		.BOTADDSUB_LOWERINPUT     (BOTADDSUB_LOWERINPUT    ),
 | 
			
		||||
		.BOTADDSUB_UPPERINPUT     (BOTADDSUB_UPPERINPUT    ),
 | 
			
		||||
		.BOTADDSUB_CARRYSELECT    (BOTADDSUB_CARRYSELECT   ),
 | 
			
		||||
		.MODE_8x8                 (MODE_8x8                ),
 | 
			
		||||
		.A_SIGNED                 (A_SIGNED                ),
 | 
			
		||||
		.B_SIGNED                 (B_SIGNED                )
 | 
			
		||||
	) uut (
 | 
			
		||||
		.CLK        (CLK           ),
 | 
			
		||||
		.CE         (CE            ),
 | 
			
		||||
		.C          (C             ),
 | 
			
		||||
		.A          (A             ),
 | 
			
		||||
		.B          (B             ),
 | 
			
		||||
		.D          (D             ),
 | 
			
		||||
		.AHOLD      (AHOLD         ),
 | 
			
		||||
		.BHOLD      (BHOLD         ),
 | 
			
		||||
		.CHOLD      (CHOLD         ),
 | 
			
		||||
		.DHOLD      (DHOLD         ),
 | 
			
		||||
		.IRSTTOP    (IRSTTOP       ),
 | 
			
		||||
		.IRSTBOT    (IRSTBOT       ),
 | 
			
		||||
		.ORSTTOP    (ORSTTOP       ),
 | 
			
		||||
		.ORSTBOT    (ORSTBOT       ),
 | 
			
		||||
		.OLOADTOP   (OLOADTOP      ),
 | 
			
		||||
		.OLOADBOT   (OLOADBOT      ),
 | 
			
		||||
		.ADDSUBTOP  (ADDSUBTOP     ),
 | 
			
		||||
		.ADDSUBBOT  (ADDSUBBOT     ),
 | 
			
		||||
		.OHOLDTOP   (OHOLDTOP      ),
 | 
			
		||||
		.OHOLDBOT   (OHOLDBOT      ),
 | 
			
		||||
		.CI         (CI            ),
 | 
			
		||||
		.ACCUMCI    (ACCUMCI       ),
 | 
			
		||||
		.SIGNEXTIN  (SIGNEXTIN     ),
 | 
			
		||||
		.O          (UUT_O         ),
 | 
			
		||||
		.CO         (UUT_CO        ),
 | 
			
		||||
		.ACCUMCO    (UUT_ACCUMCO   ),
 | 
			
		||||
		.SIGNEXTOUT (UUT_SIGNEXTOUT)
 | 
			
		||||
	);
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
module testbench_comb_8x8_A;
 | 
			
		||||
	testbench #(
 | 
			
		||||
		.NEG_TRIGGER               (0),
 | 
			
		||||
		.C_REG                     (0),
 | 
			
		||||
		.A_REG                     (0),
 | 
			
		||||
		.B_REG                     (0),
 | 
			
		||||
		.D_REG                     (0),
 | 
			
		||||
		.TOP_8x8_MULT_REG          (0),
 | 
			
		||||
		.BOT_8x8_MULT_REG          (0),
 | 
			
		||||
		.PIPELINE_16x16_MULT_REG1  (0),
 | 
			
		||||
		.PIPELINE_16x16_MULT_REG2  (0),
 | 
			
		||||
		.TOPOUTPUT_SELECT          (2),   // 0=P, 1=Q, 2=8x8, 3=16x16
 | 
			
		||||
		.TOPADDSUB_LOWERINPUT      (0),   // 0=A, 1=8x8, 2=16x16, 3=S-EXT
 | 
			
		||||
		.TOPADDSUB_UPPERINPUT      (0),   // 0=Q, 1=C
 | 
			
		||||
		.TOPADDSUB_CARRYSELECT     (0),   // 0=0, 1=1, 2=ACI, 3=CI
 | 
			
		||||
		.BOTOUTPUT_SELECT          (2),   // 0=R, 1=S, 2=8x8, 3=16x16
 | 
			
		||||
		.BOTADDSUB_LOWERINPUT      (0),   // 0=B, 1=8x8, 2=16x16, 3=S-EXT
 | 
			
		||||
		.BOTADDSUB_UPPERINPUT      (0),   // 0=S, 1=D
 | 
			
		||||
		.BOTADDSUB_CARRYSELECT     (0),   // 0=0, 1=1, 2=ACI, 3=CI
 | 
			
		||||
		.MODE_8x8                  (0),
 | 
			
		||||
		.A_SIGNED                  (0),
 | 
			
		||||
		.B_SIGNED                  (0)
 | 
			
		||||
	) testbench ();
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
module testbench_comb_8x8_B;
 | 
			
		||||
	testbench #(
 | 
			
		||||
		.NEG_TRIGGER               (0),
 | 
			
		||||
		.C_REG                     (0),
 | 
			
		||||
		.A_REG                     (0),
 | 
			
		||||
		.B_REG                     (0),
 | 
			
		||||
		.D_REG                     (0),
 | 
			
		||||
		.TOP_8x8_MULT_REG          (0),
 | 
			
		||||
		.BOT_8x8_MULT_REG          (0),
 | 
			
		||||
		.PIPELINE_16x16_MULT_REG1  (0),
 | 
			
		||||
		.PIPELINE_16x16_MULT_REG2  (0),
 | 
			
		||||
		.TOPOUTPUT_SELECT          (0),   // 0=P, 1=Q, 2=8x8, 3=16x16
 | 
			
		||||
		.TOPADDSUB_LOWERINPUT      (1),   // 0=A, 1=8x8, 2=16x16, 3=S-EXT
 | 
			
		||||
		.TOPADDSUB_UPPERINPUT      (1),   // 0=Q, 1=C
 | 
			
		||||
		.TOPADDSUB_CARRYSELECT     (0),   // 0=0, 1=1, 2=ACI, 3=CI
 | 
			
		||||
		.BOTOUTPUT_SELECT          (0),   // 0=R, 1=S, 2=8x8, 3=16x16
 | 
			
		||||
		.BOTADDSUB_LOWERINPUT      (1),   // 0=B, 1=8x8, 2=16x16, 3=S-EXT
 | 
			
		||||
		.BOTADDSUB_UPPERINPUT      (1),   // 0=S, 1=D
 | 
			
		||||
		.BOTADDSUB_CARRYSELECT     (0),   // 0=0, 1=1, 2=ACI, 3=CI
 | 
			
		||||
		.MODE_8x8                  (0),
 | 
			
		||||
		.A_SIGNED                  (0),
 | 
			
		||||
		.B_SIGNED                  (0)
 | 
			
		||||
	) testbench ();
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
module testbench_comb_16x16;
 | 
			
		||||
	testbench #(
 | 
			
		||||
		.NEG_TRIGGER               (0),
 | 
			
		||||
		.C_REG                     (0),
 | 
			
		||||
		.A_REG                     (0),
 | 
			
		||||
		.B_REG                     (0),
 | 
			
		||||
		.D_REG                     (0),
 | 
			
		||||
		.TOP_8x8_MULT_REG          (0),
 | 
			
		||||
		.BOT_8x8_MULT_REG          (0),
 | 
			
		||||
		.PIPELINE_16x16_MULT_REG1  (0),
 | 
			
		||||
		.PIPELINE_16x16_MULT_REG2  (0),
 | 
			
		||||
		.TOPOUTPUT_SELECT          (0),   // 0=P, 1=Q, 2=8x8, 3=16x16
 | 
			
		||||
		.TOPADDSUB_LOWERINPUT      (2),   // 0=A, 1=8x8, 2=16x16, 3=S-EXT
 | 
			
		||||
		.TOPADDSUB_UPPERINPUT      (1),   // 0=Q, 1=C
 | 
			
		||||
		.TOPADDSUB_CARRYSELECT     (2),   // 0=0, 1=1, 2=ACI, 3=CI
 | 
			
		||||
		.BOTOUTPUT_SELECT          (0),   // 0=R, 1=S, 2=8x8, 3=16x16
 | 
			
		||||
		.BOTADDSUB_LOWERINPUT      (2),   // 0=B, 1=8x8, 2=16x16, 3=S-EXT
 | 
			
		||||
		.BOTADDSUB_UPPERINPUT      (1),   // 0=S, 1=D
 | 
			
		||||
		.BOTADDSUB_CARRYSELECT     (2),   // 0=0, 1=1, 2=ACI, 3=CI
 | 
			
		||||
		.MODE_8x8                  (0),
 | 
			
		||||
		.A_SIGNED                  (0),
 | 
			
		||||
		.B_SIGNED                  (0)
 | 
			
		||||
	) testbench ();
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
module testbench_seq_16x16_A;
 | 
			
		||||
	testbench #(
 | 
			
		||||
		.NEG_TRIGGER               (0),
 | 
			
		||||
		.C_REG                     (1),
 | 
			
		||||
		.A_REG                     (1),
 | 
			
		||||
		.B_REG                     (1),
 | 
			
		||||
		.D_REG                     (1),
 | 
			
		||||
		.TOP_8x8_MULT_REG          (1),
 | 
			
		||||
		.BOT_8x8_MULT_REG          (1),
 | 
			
		||||
		.PIPELINE_16x16_MULT_REG1  (1),
 | 
			
		||||
		.PIPELINE_16x16_MULT_REG2  (1),
 | 
			
		||||
		.TOPOUTPUT_SELECT          (0),   // 0=P, 1=Q, 2=8x8, 3=16x16
 | 
			
		||||
		.TOPADDSUB_LOWERINPUT      (2),   // 0=A, 1=8x8, 2=16x16, 3=S-EXT
 | 
			
		||||
		.TOPADDSUB_UPPERINPUT      (1),   // 0=Q, 1=C
 | 
			
		||||
		.TOPADDSUB_CARRYSELECT     (2),   // 0=0, 1=1, 2=ACI, 3=CI
 | 
			
		||||
		.BOTOUTPUT_SELECT          (0),   // 0=R, 1=S, 2=8x8, 3=16x16
 | 
			
		||||
		.BOTADDSUB_LOWERINPUT      (2),   // 0=B, 1=8x8, 2=16x16, 3=S-EXT
 | 
			
		||||
		.BOTADDSUB_UPPERINPUT      (1),   // 0=S, 1=D
 | 
			
		||||
		.BOTADDSUB_CARRYSELECT     (2),   // 0=0, 1=1, 2=ACI, 3=CI
 | 
			
		||||
		.MODE_8x8                  (0),
 | 
			
		||||
		.A_SIGNED                  (0),
 | 
			
		||||
		.B_SIGNED                  (0)
 | 
			
		||||
	) testbench ();
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
module testbench_seq_16x16_B;
 | 
			
		||||
	testbench #(
 | 
			
		||||
		.NEG_TRIGGER               (0),
 | 
			
		||||
		.C_REG                     (1),
 | 
			
		||||
		.A_REG                     (1),
 | 
			
		||||
		.B_REG                     (1),
 | 
			
		||||
		.D_REG                     (1),
 | 
			
		||||
		.TOP_8x8_MULT_REG          (1),
 | 
			
		||||
		.BOT_8x8_MULT_REG          (1),
 | 
			
		||||
		.PIPELINE_16x16_MULT_REG1  (1),
 | 
			
		||||
		.PIPELINE_16x16_MULT_REG2  (0),
 | 
			
		||||
		.TOPOUTPUT_SELECT          (1),   // 0=P, 1=Q, 2=8x8, 3=16x16
 | 
			
		||||
		.TOPADDSUB_LOWERINPUT      (2),   // 0=A, 1=8x8, 2=16x16, 3=S-EXT
 | 
			
		||||
		.TOPADDSUB_UPPERINPUT      (0),   // 0=Q, 1=C
 | 
			
		||||
		.TOPADDSUB_CARRYSELECT     (2),   // 0=0, 1=1, 2=ACI, 3=CI
 | 
			
		||||
		.BOTOUTPUT_SELECT          (1),   // 0=R, 1=S, 2=8x8, 3=16x16
 | 
			
		||||
		.BOTADDSUB_LOWERINPUT      (2),   // 0=B, 1=8x8, 2=16x16, 3=S-EXT
 | 
			
		||||
		.BOTADDSUB_UPPERINPUT      (0),   // 0=S, 1=D
 | 
			
		||||
		.BOTADDSUB_CARRYSELECT     (2),   // 0=0, 1=1, 2=ACI, 3=CI
 | 
			
		||||
		.MODE_8x8                  (0),
 | 
			
		||||
		.A_SIGNED                  (0),
 | 
			
		||||
		.B_SIGNED                  (0)
 | 
			
		||||
	) testbench ();
 | 
			
		||||
endmodule
 | 
			
		||||
| 
						 | 
				
			
			@ -11,4 +11,4 @@ do
 | 
			
		|||
done
 | 
			
		||||
shift "$((OPTIND-1))"
 | 
			
		||||
 | 
			
		||||
exec ${MAKE:-make} -f ../tools/autotest.mk $seed EXTRA_FLAGS="-e" *.v
 | 
			
		||||
exec ${MAKE:-make} -f ../tools/autotest.mk $seed EXTRA_FLAGS+="-e" *.v
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										24
									
								
								tests/asicworld/xfirrtl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								tests/asicworld/xfirrtl
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
# This file contains the names of verilog files to exclude from verilog to FIRRTL regression tests due to known failures.
 | 
			
		||||
code_hdl_models_arbiter.v	error: reg rst; cannot be driven by primitives or continuous assignment.
 | 
			
		||||
code_hdl_models_clk_div_45.v	yosys issue: 2nd PMUXTREE pass yields: ERROR: Negative edge clock on FF clk_div_45.$procdff$49.
 | 
			
		||||
code_hdl_models_d_ff_gates.v	combinational loop
 | 
			
		||||
code_hdl_models_d_latch_gates.v	combinational loop
 | 
			
		||||
code_hdl_models_dff_async_reset.v	$adff
 | 
			
		||||
code_hdl_models_tff_async_reset.v	$adff
 | 
			
		||||
code_hdl_models_uart.v	$adff
 | 
			
		||||
code_specman_switch_fabric.v	subfield assignment (bits() <= ...)
 | 
			
		||||
code_tidbits_asyn_reset.v	$adff
 | 
			
		||||
code_tidbits_reg_seq_example.v	$adff
 | 
			
		||||
code_verilog_tutorial_always_example.v	empty module
 | 
			
		||||
code_verilog_tutorial_escape_id.v	make_id issues (name begins with a digit)
 | 
			
		||||
code_verilog_tutorial_explicit.v	firrtl backend bug (empty module)
 | 
			
		||||
code_verilog_tutorial_first_counter.v	error: reg rst; cannot be driven by primitives or continuous assignment.
 | 
			
		||||
code_verilog_tutorial_fsm_full.v	error: reg reset; cannot be driven by primitives or continuous assignment.
 | 
			
		||||
code_verilog_tutorial_if_else.v	empty module (everything is under 'always @ (posedge clk)')
 | 
			
		||||
[code_verilog_tutorial_n_out_primitive.v	empty module
 | 
			
		||||
code_verilog_tutorial_parallel_if.v	empty module (everything is under 'always @ (posedge clk)')
 | 
			
		||||
code_verilog_tutorial_simple_function.v	empty module (no hardware)
 | 
			
		||||
code_verilog_tutorial_simple_if.v	empty module (everything is under 'always @ (posedge clk)')
 | 
			
		||||
code_verilog_tutorial_task_global.v	empty module (everything is under 'always @ (posedge clk)')
 | 
			
		||||
code_verilog_tutorial_v2k_reg.v		empty module
 | 
			
		||||
code_verilog_tutorial_which_clock.v	 $adff
 | 
			
		||||
							
								
								
									
										21
									
								
								tests/opt/opt_ff.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tests/opt/opt_ff.v
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
module top(
 | 
			
		||||
    input clk,
 | 
			
		||||
    input rst,
 | 
			
		||||
    input [2:0] a,
 | 
			
		||||
    output [1:0] b
 | 
			
		||||
);
 | 
			
		||||
    reg [2:0] b_reg;
 | 
			
		||||
    initial begin
 | 
			
		||||
        b_reg <= 3'b0;
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    assign b = b_reg[1:0];
 | 
			
		||||
    always @(posedge clk or posedge rst) begin
 | 
			
		||||
        if(rst) begin
 | 
			
		||||
            b_reg <= 3'b0;
 | 
			
		||||
        end else begin
 | 
			
		||||
            b_reg <= a;
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								tests/opt/opt_ff.ys
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								tests/opt/opt_ff.ys
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
read_verilog opt_ff.v
 | 
			
		||||
synth_ice40
 | 
			
		||||
ice40_unlut
 | 
			
		||||
							
								
								
									
										42
									
								
								tests/simple/dff_init.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								tests/simple/dff_init.v
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,42 @@
 | 
			
		|||
module dff0_test(n1, n1_inv, clk);
 | 
			
		||||
  input clk;
 | 
			
		||||
  output n1;
 | 
			
		||||
  reg n1 = 32'd0;
 | 
			
		||||
  output n1_inv;
 | 
			
		||||
  always @(posedge clk)
 | 
			
		||||
      n1 <= n1_inv;
 | 
			
		||||
  assign n1_inv = ~n1;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
module dff1_test(n1, n1_inv, clk);
 | 
			
		||||
  input clk;
 | 
			
		||||
  (* init = 32'd1 *)
 | 
			
		||||
  output n1;
 | 
			
		||||
  reg n1 = 32'd1;
 | 
			
		||||
  output n1_inv;
 | 
			
		||||
  always @(posedge clk)
 | 
			
		||||
      n1 <= n1_inv;
 | 
			
		||||
  assign n1_inv = ~n1;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
module dff0a_test(n1, n1_inv, clk);
 | 
			
		||||
  input clk;
 | 
			
		||||
  (* init = 32'd0 *) // Must be consistent with reg initialiser below
 | 
			
		||||
  output n1;
 | 
			
		||||
  reg n1 = 32'd0;
 | 
			
		||||
  output n1_inv;
 | 
			
		||||
  always @(posedge clk)
 | 
			
		||||
      n1 <= n1_inv;
 | 
			
		||||
  assign n1_inv = ~n1;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
module dff1a_test(n1, n1_inv, clk);
 | 
			
		||||
  input clk;
 | 
			
		||||
  (* init = 32'd1 *) // Must be consistent with reg initialiser below
 | 
			
		||||
  output n1;
 | 
			
		||||
  reg n1 = 32'd1;
 | 
			
		||||
  output n1_inv;
 | 
			
		||||
  always @(posedge clk)
 | 
			
		||||
      n1 <= n1_inv;
 | 
			
		||||
  assign n1_inv = ~n1;
 | 
			
		||||
endmodule
 | 
			
		||||
| 
						 | 
				
			
			@ -1,3 +1,5 @@
 | 
			
		|||
`default_nettype none
 | 
			
		||||
 | 
			
		||||
module hierdefparam_top(input [7:0] A, output [7:0] Y);
 | 
			
		||||
  generate begin:foo
 | 
			
		||||
    hierdefparam_a mod_a(.A(A), .Y(Y));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										26
									
								
								tests/simple/xfirrtl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								tests/simple/xfirrtl
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,26 @@
 | 
			
		|||
# This file contains the names of verilog files to exclude from verilog to FIRRTL regression tests due to known failures.
 | 
			
		||||
arraycells.v	inst id[0] of
 | 
			
		||||
dff_different_styles.v
 | 
			
		||||
generate.v	combinational loop
 | 
			
		||||
hierdefparam.v	inst id[0] of
 | 
			
		||||
i2c_master_tests.v   $adff
 | 
			
		||||
macros.v	drops modules
 | 
			
		||||
mem2reg.v	drops modules
 | 
			
		||||
mem_arst.v	$adff
 | 
			
		||||
memory.v	$adff
 | 
			
		||||
multiplier.v	inst id[0] of
 | 
			
		||||
muxtree.v	drops modules
 | 
			
		||||
omsp_dbg_uart.v	$adff
 | 
			
		||||
operators.v	$pow
 | 
			
		||||
paramods.v	subfield assignment (bits() <= ...)
 | 
			
		||||
partsel.v	drops modules
 | 
			
		||||
process.v	drops modules
 | 
			
		||||
realexpr.v	drops modules
 | 
			
		||||
scopes.v	original verilog issues ( -x where x isn't declared signed)
 | 
			
		||||
sincos.v	$adff
 | 
			
		||||
specify.v	no code (empty module generates error
 | 
			
		||||
subbytes.v	$adff
 | 
			
		||||
task_func.v	drops modules
 | 
			
		||||
values.v	combinational loop
 | 
			
		||||
vloghammer.v	combinational loop
 | 
			
		||||
wreduce.v	original verilog issues ( -x where x isn't declared signed)
 | 
			
		||||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
 | 
			
		||||
EXTRA_FLAGS=
 | 
			
		||||
SEED=
 | 
			
		||||
 | 
			
		||||
# Don't bother defining default values for SEED and EXTRA_FLAGS.
 | 
			
		||||
# Their "natural" default values should be sufficient,
 | 
			
		||||
#   and they may be overridden in the environment.
 | 
			
		||||
ifneq ($(strip $(SEED)),)
 | 
			
		||||
SEEDOPT=-S$(SEED)
 | 
			
		||||
endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,12 +17,18 @@ scriptfiles=""
 | 
			
		|||
scriptopt=""
 | 
			
		||||
toolsdir="$(cd $(dirname $0); pwd)"
 | 
			
		||||
warn_iverilog_git=false
 | 
			
		||||
# The following are used in verilog to firrtl regression tests.
 | 
			
		||||
# Typically these will be passed as environment variables:
 | 
			
		||||
#EXTRA_FLAGS="--firrtl2verilog 'java -cp /.../firrtl/utils/bin/firrtl.jar firrtl.Driver'"
 | 
			
		||||
# The tests are skipped if firrtl2verilog is the empty string (the default).
 | 
			
		||||
firrtl2verilog=""
 | 
			
		||||
xfirrtl="../xfirrtl"
 | 
			
		||||
 | 
			
		||||
if [ ! -f $toolsdir/cmp_tbdata -o $toolsdir/cmp_tbdata.c -nt $toolsdir/cmp_tbdata ]; then
 | 
			
		||||
	( set -ex; ${CC:-gcc} -Wall -o $toolsdir/cmp_tbdata $toolsdir/cmp_tbdata.c; ) || exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
while getopts xmGl:wkjvref:s:p:n:S:I: opt; do
 | 
			
		||||
while getopts xmGl:wkjvref:s:p:n:S:I:-: opt; do
 | 
			
		||||
	case "$opt" in
 | 
			
		||||
		x)
 | 
			
		||||
			use_xsim=true ;;
 | 
			
		||||
| 
						 | 
				
			
			@ -59,8 +65,24 @@ while getopts xmGl:wkjvref:s:p:n:S:I: opt; do
 | 
			
		|||
			include_opts="$include_opts -I $OPTARG"
 | 
			
		||||
			xinclude_opts="$xinclude_opts -i $OPTARG"
 | 
			
		||||
			minclude_opts="$minclude_opts +incdir+$OPTARG" ;;
 | 
			
		||||
		-)
 | 
			
		||||
			case "${OPTARG}" in
 | 
			
		||||
			    xfirrtl)
 | 
			
		||||
			    	xfirrtl="${!OPTIND}"
 | 
			
		||||
				OPTIND=$(( $OPTIND + 1 ))
 | 
			
		||||
				;;
 | 
			
		||||
			    firrtl2verilog)
 | 
			
		||||
			    	firrtl2verilog="${!OPTIND}"
 | 
			
		||||
				OPTIND=$(( $OPTIND + 1 ))
 | 
			
		||||
				;;
 | 
			
		||||
			    *)
 | 
			
		||||
			    	if [ "$OPTERR" == 1 ] && [ "${optspec:0:1}" != ":" ]; then
 | 
			
		||||
				    echo "Unknown option --${OPTARG}" >&2
 | 
			
		||||
				fi
 | 
			
		||||
				;;
 | 
			
		||||
			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] verilog-files\n" >&2
 | 
			
		||||
			echo "Usage: $0 [-x|-m] [-G] [-w] [-k] [-j] [-v] [-r] [-e] [-l libs] [-f frontend] [-s script] [-p cmdstring] [-n iters] [-S seed] [-I incdir] [--xfirrtl FIRRTL test exclude file] [--firrtl2verilog command to generate verilog from firrtl] verilog-files\n" >&2
 | 
			
		||||
			exit 1
 | 
			
		||||
	esac
 | 
			
		||||
done
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +131,8 @@ do
 | 
			
		|||
		fn=$(basename $fn)
 | 
			
		||||
		bn=$(basename $bn)
 | 
			
		||||
 | 
			
		||||
		rm -f ${bn}_ref.fir
 | 
			
		||||
 | 
			
		||||
		egrep -v '^\s*`timescale' ../$fn > ${bn}_ref.v
 | 
			
		||||
 | 
			
		||||
		if [ ! -f ../${bn}_tb.v ]; then
 | 
			
		||||
| 
						 | 
				
			
			@ -148,6 +172,13 @@ do
 | 
			
		|||
		else
 | 
			
		||||
			test_passes -f "$frontend $include_opts" -p "hierarchy; proc; opt; memory; opt; fsm; opt -full -fine" ${bn}_ref.v
 | 
			
		||||
			test_passes -f "$frontend $include_opts" -p "hierarchy; synth -run coarse; techmap; opt; abc -dff" ${bn}_ref.v
 | 
			
		||||
			if [ -n "$firrtl2verilog" ]; then
 | 
			
		||||
			    if test -z "$xfirrtl" || ! grep "$fn" "$xfirrtl" ; then
 | 
			
		||||
				"$toolsdir"/../../yosys -b "firrtl" -o ${bn}_ref.fir -f "$frontend $include_opts" -p "prep -nordff; proc; opt; memory; opt; fsm; opt -full -fine; pmuxtree" ${bn}_ref.v
 | 
			
		||||
				$firrtl2verilog -i ${bn}_ref.fir -o ${bn}_ref.fir.v  -X verilog
 | 
			
		||||
				test_passes -f "$frontend $include_opts" -p "hierarchy; proc; opt; memory; opt; fsm; opt -full -fine" ${bn}_ref.fir.v
 | 
			
		||||
			    fi
 | 
			
		||||
			fi
 | 
			
		||||
		fi
 | 
			
		||||
		touch ../${bn}.log
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -160,14 +191,18 @@ do
 | 
			
		|||
		( set -ex; body; ) > ${bn}.err 2>&1
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
	did_firrtl=""
 | 
			
		||||
	if [ -f ${bn}.out/${bn}_ref.fir ]; then
 | 
			
		||||
	    did_firrtl="+FIRRTL "
 | 
			
		||||
	fi
 | 
			
		||||
	if [ -f ${bn}.log ]; then
 | 
			
		||||
		mv ${bn}.err ${bn}.log
 | 
			
		||||
		echo "${status_prefix}-> ok"
 | 
			
		||||
		echo "${status_prefix}${did_firrtl}-> ok"
 | 
			
		||||
	elif [ -f ${bn}.skip ]; then
 | 
			
		||||
		mv ${bn}.err ${bn}.skip
 | 
			
		||||
		echo "${status_prefix}-> skip"
 | 
			
		||||
	else
 | 
			
		||||
		echo "${status_prefix}-> ERROR!"
 | 
			
		||||
		echo "${status_prefix}${did_firrtl}-> ERROR!"
 | 
			
		||||
		if $warn_iverilog_git; then
 | 
			
		||||
			echo "Note: Make sure that 'iverilog' is an up-to-date git checkout of Icarus Verilog."
 | 
			
		||||
		fi
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										59
									
								
								tests/various/hierarchy.sh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								tests/various/hierarchy.sh
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,59 @@
 | 
			
		|||
#!/usr/bin/env bash
 | 
			
		||||
# Simple test of hierarchy -auto-top.
 | 
			
		||||
 | 
			
		||||
set -e
 | 
			
		||||
 | 
			
		||||
echo -n "  TOP first - "
 | 
			
		||||
../../yosys -s - <<- EOY | grep "Automatically selected TOP as design top module"
 | 
			
		||||
  read_verilog << EOV
 | 
			
		||||
    module TOP(a, y);
 | 
			
		||||
      input a;
 | 
			
		||||
      output [31:0] y;
 | 
			
		||||
 | 
			
		||||
      aoi12 p [31:0] (a, y);
 | 
			
		||||
    endmodule
 | 
			
		||||
 | 
			
		||||
    module aoi12(a, y);
 | 
			
		||||
      input a;
 | 
			
		||||
      output y;
 | 
			
		||||
      assign y = ~a;
 | 
			
		||||
    endmodule
 | 
			
		||||
  EOV
 | 
			
		||||
  hierarchy -auto-top
 | 
			
		||||
EOY
 | 
			
		||||
 | 
			
		||||
echo -n "  TOP last - "
 | 
			
		||||
../../yosys -s - <<- EOY | grep "Automatically selected TOP as design top module"
 | 
			
		||||
  read_verilog << EOV
 | 
			
		||||
    module aoi12(a, y);
 | 
			
		||||
      input a;
 | 
			
		||||
      output y;
 | 
			
		||||
      assign y = ~a;
 | 
			
		||||
    endmodule
 | 
			
		||||
 | 
			
		||||
    module TOP(a, y);
 | 
			
		||||
      input a;
 | 
			
		||||
      output [31:0] y;
 | 
			
		||||
 | 
			
		||||
      aoi12 foo (a, y);
 | 
			
		||||
    endmodule
 | 
			
		||||
  EOV
 | 
			
		||||
  hierarchy -auto-top
 | 
			
		||||
EOY
 | 
			
		||||
 | 
			
		||||
echo -n "  no explicit top - "
 | 
			
		||||
../../yosys -s - <<- EOY | grep "Automatically selected noTop as design top module."
 | 
			
		||||
  read_verilog << EOV
 | 
			
		||||
    module aoi12(a, y);
 | 
			
		||||
      input a;
 | 
			
		||||
      output y;
 | 
			
		||||
      assign y = ~a;
 | 
			
		||||
    endmodule
 | 
			
		||||
 | 
			
		||||
    module noTop(a, y);
 | 
			
		||||
      input a;
 | 
			
		||||
      output [31:0] y;
 | 
			
		||||
    endmodule
 | 
			
		||||
  EOV
 | 
			
		||||
  hierarchy -auto-top
 | 
			
		||||
EOY
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,14 @@
 | 
			
		|||
#!/bin/bash
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
set -e
 | 
			
		||||
for x in *.ys; do
 | 
			
		||||
	echo "Running $x.."
 | 
			
		||||
	../../yosys -ql ${x%.ys}.log $x
 | 
			
		||||
done
 | 
			
		||||
# Run any .sh files in this directory (with the exception of the file - run-test.sh
 | 
			
		||||
shell_tests=$(echo *.sh | sed -e 's/run-test.sh//')
 | 
			
		||||
if [ "$shell_tests" ]; then
 | 
			
		||||
    for s in $shell_tests; do
 | 
			
		||||
        echo "Running $s.."
 | 
			
		||||
        bash $s
 | 
			
		||||
    done
 | 
			
		||||
fi
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue