Merge branch 'master' of https://github.com/YosysHQ/yosys into feature/python_wrappers/globals_and_streams
diff --git a/Makefile b/Makefile
index 33c04cf..895cfbf 100644
--- a/Makefile
+++ b/Makefile
@@ -708,6 +708,7 @@
 	+cd tests/various && bash run-test.sh
 	+cd tests/sat && bash run-test.sh
 	+cd tests/svinterfaces && bash run-test.sh $(SEEDOPT)
+	+cd tests/svtypes && bash run-test.sh $(SEEDOPT)
 	+cd tests/proc && bash run-test.sh
 	+cd tests/opt && bash run-test.sh
 	+cd tests/aiger && bash run-test.sh $(ABCOPT)
diff --git a/README.md b/README.md
index fdd4bb4..db7810c 100644
--- a/README.md
+++ b/README.md
@@ -510,6 +510,8 @@
   into a design with ``read_verilog``, all its packages are available to
   SystemVerilog files being read into the same design afterwards.
 
+- typedefs are supported (including inside packages)
+
 - SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether
   ports are inputs or outputs are supported.
 
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
index 37a69d8..5bbea0f 100644
--- a/frontends/ast/ast.cc
+++ b/frontends/ast/ast.cc
@@ -164,6 +164,8 @@
 	X(AST_MODPORT)
 	X(AST_MODPORTMEMBER)
 	X(AST_PACKAGE)
+	X(AST_WIRETYPE)
+	X(AST_TYPEDEF)
 #undef X
 	default:
 		log_abort();
@@ -206,6 +208,7 @@
 	was_checked = false;
 	range_valid = false;
 	range_swapped = false;
+	is_custom_type = false;
 	port_id = 0;
 	range_left = -1;
 	range_right = 0;
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
index 0ec249a..918d178 100644
--- a/frontends/ast/ast.h
+++ b/frontends/ast/ast.h
@@ -148,7 +148,10 @@
 		AST_INTERFACEPORTTYPE,
 		AST_MODPORT,
 		AST_MODPORTMEMBER,
-		AST_PACKAGE
+		AST_PACKAGE,
+
+		AST_WIRETYPE,
+		AST_TYPEDEF
 	};
 
 	// convert an node type to a string (e.g. for debug output)
@@ -174,7 +177,7 @@
 		// node content - most of it is unused in most node types
 		std::string str;
 		std::vector<RTLIL::State> bits;
-		bool is_input, is_output, is_reg, is_logic, is_signed, is_string, is_wand, is_wor, range_valid, range_swapped, was_checked, is_unsized;
+		bool is_input, is_output, is_reg, is_logic, is_signed, is_string, is_wand, is_wor, range_valid, range_swapped, was_checked, is_unsized, is_custom_type;
 		int port_id, range_left, range_right;
 		uint32_t integer;
 		double realvalue;
diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc
index 407a344..94f5c0a 100644
--- a/frontends/ast/genrtlil.cc
+++ b/frontends/ast/genrtlil.cc
@@ -863,6 +863,7 @@
 	case AST_PACKAGE:
 	case AST_MODPORT:
 	case AST_MODPORTMEMBER:
+	case AST_TYPEDEF:
 		break;
 	case AST_INTERFACEPORT: {
 		// If a port in a module with unknown type is found, mark it with the attribute 'is_interface'
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index b1ee22f..44fd32c 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -318,7 +318,7 @@
 	}
 
 	// activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.)
-	if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX)
+	if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX || type == AST_TYPEDEF)
 		const_fold = true;
 	if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM))
 		const_fold = true;
@@ -336,6 +336,7 @@
 		std::map<std::string, AstNode*> this_wire_scope;
 		for (size_t i = 0; i < children.size(); i++) {
 			AstNode *node = children[i];
+
 			if (node->type == AST_WIRE) {
 				if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) {
 					for (auto c : node->children[0]->children) {
@@ -405,14 +406,15 @@
 				this_wire_scope[node->str] = node;
 			}
 			if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR ||
-					node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION || node->type == AST_CELL) {
+					node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION || node->type == AST_CELL ||
+					node->type == AST_TYPEDEF) {
 				backup_scope[node->str] = current_scope[node->str];
 				current_scope[node->str] = node;
 			}
 		}
 		for (size_t i = 0; i < children.size(); i++) {
 			AstNode *node = children[i];
-			if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_MEMORY)
+			if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_MEMORY || node->type == AST_TYPEDEF)
 				while (node->simplify(true, false, false, 1, -1, false, node->type == AST_PARAMETER || node->type == AST_LOCALPARAM))
 					did_something = true;
 		}
@@ -780,6 +782,99 @@
 		delete_children();
 	}
 
+	// resolve typedefs
+	if (type == AST_TYPEDEF) {
+		log_assert(children.size() == 1);
+		log_assert(children[0]->type == AST_WIRE || children[0]->type == AST_MEMORY);
+		while(children[0]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param))
+			did_something = true;
+		log_assert(!children[0]->is_custom_type);
+	}
+
+	// resolve types of wires
+	if (type == AST_WIRE || type == AST_MEMORY) {
+		if (is_custom_type) {
+			log_assert(children.size() >= 1);
+			log_assert(children[0]->type == AST_WIRETYPE);
+			if (!current_scope.count(children[0]->str))
+				log_file_error(filename, linenum, "Unknown identifier `%s' used as type name\n", children[0]->str.c_str());
+			AstNode *resolved_type = current_scope.at(children[0]->str);
+			if (resolved_type->type != AST_TYPEDEF)
+				log_file_error(filename, linenum, "`%s' does not name a type\n", children[0]->str.c_str());
+			log_assert(resolved_type->children.size() == 1);
+			AstNode *templ = resolved_type->children[0];
+			// Remove type reference
+			delete children[0];
+			children.erase(children.begin());
+
+			// Ensure typedef itself is fully simplified
+			while(templ->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {};
+
+			if (type == AST_WIRE)
+				type = templ->type;
+			is_reg = templ->is_reg;
+			is_logic = templ->is_logic;
+			is_signed = templ->is_signed;
+			is_string = templ->is_string;
+			is_custom_type = templ->is_custom_type;
+
+			range_valid = templ->range_valid;
+			range_swapped = templ->range_swapped;
+			range_left = templ->range_left;
+			range_right = templ->range_right;
+
+			// Insert clones children from template at beginning
+			for (int i  = 0; i < GetSize(templ->children); i++)
+				children.insert(children.begin() + i, templ->children[i]->clone());
+			
+			if (type == AST_MEMORY && GetSize(children) == 1) {
+				// Single-bit memories must have [0:0] range
+				AstNode *rng = new AstNode(AST_RANGE);
+				rng->children.push_back(AstNode::mkconst_int(0, true));
+				rng->children.push_back(AstNode::mkconst_int(0, true));
+				children.insert(children.begin(), rng);
+			}
+
+			did_something = true;
+		}
+		log_assert(!is_custom_type);
+	}
+
+	// resolve types of parameters
+	if (type == AST_LOCALPARAM || type == AST_PARAMETER) {
+		if (is_custom_type) {
+			log_assert(children.size() == 2);
+			log_assert(children[1]->type == AST_WIRETYPE);
+			if (!current_scope.count(children[1]->str))
+				log_file_error(filename, linenum, "Unknown identifier `%s' used as type name\n", children[1]->str.c_str());
+			AstNode *resolved_type = current_scope.at(children[1]->str);
+			if (resolved_type->type != AST_TYPEDEF)
+				log_file_error(filename, linenum, "`%s' does not name a type\n", children[1]->str.c_str());
+			log_assert(resolved_type->children.size() == 1);
+			AstNode *templ = resolved_type->children[0];
+			delete children[1];
+			children.pop_back();
+
+			// Ensure typedef itself is fully simplified
+			while(templ->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {};
+
+			if (templ->type == AST_MEMORY)
+				log_file_error(filename, linenum, "unpacked array type `%s' cannot be used for a parameter\n", children[1]->str.c_str());
+			is_signed = templ->is_signed;
+			is_string = templ->is_string;
+			is_custom_type = templ->is_custom_type;
+
+			range_valid = templ->range_valid;
+			range_swapped = templ->range_swapped;
+			range_left = templ->range_left;
+			range_right = templ->range_right;
+			for (auto template_child : templ->children)
+				children.push_back(template_child->clone());
+			did_something = true;
+		}
+		log_assert(!is_custom_type);
+	}	
+
 	// resolve constant prefixes
 	if (type == AST_PREFIX) {
 		if (children[0]->type != AST_CONSTANT) {
@@ -1194,7 +1289,7 @@
 	if (type == AST_BLOCK && str.empty())
 	{
 		for (size_t i = 0; i < children.size(); i++)
-			if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM)
+			if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF)
 				log_file_error(children[i]->filename, children[i]->linenum, "Local declaration in unnamed block is an unsupported SystemVerilog feature!\n");
 	}
 
@@ -1206,7 +1301,7 @@
 
 		std::vector<AstNode*> new_children;
 		for (size_t i = 0; i < children.size(); i++)
-			if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM) {
+			if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF) {
 				children[i]->simplify(false, false, false, stage, -1, false, false);
 				current_ast_mod->children.push_back(children[i]);
 				current_scope[children[i]->str] = children[i];
@@ -2906,7 +3001,7 @@
 		}
 	}
 
-	if ((type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL) && name_map.count(str) > 0)
+	if ((type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE) && name_map.count(str) > 0)
 		str = name_map[str];
 
 	std::map<std::string, std::string> backup_name_map;
@@ -2914,7 +3009,7 @@
 	for (size_t i = 0; i < children.size(); i++) {
 		AstNode *child = children[i];
 		if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM ||
-				child->type == AST_FUNCTION || child->type == AST_TASK || child->type == AST_CELL) {
+				child->type == AST_FUNCTION || child->type == AST_TASK || child->type == AST_CELL || child->type == AST_TYPEDEF) {
 			if (backup_name_map.size() == 0)
 				backup_name_map = name_map;
 			std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix;
@@ -2945,6 +3040,7 @@
 			child->expand_genblock(index_var, prefix, name_map);
 	}
 
+
 	if (backup_name_map.size() > 0)
 		name_map.swap(backup_name_map);
 }
@@ -2998,6 +3094,9 @@
 	uint32_t children_flags = 0;
 	int lhs_children_counter = 0;
 
+	if (type == AST_TYPEDEF)
+		return; // don't touch content of typedefs
+
 	if (type == AST_ASSIGN || type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ)
 	{
 		// mark all memories that are used in a complex expression on the left side of an assignment
@@ -3155,6 +3254,9 @@
 	if (type == AST_FUNCTION || type == AST_TASK)
 		return false;
 
+	if (type == AST_TYPEDEF)
+		return false;
+
 	if (type == AST_MEMINIT && id2ast && mem2reg_set.count(id2ast))
 	{
 		log_assert(children[0]->type == AST_CONSTANT);
diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y
index 4afd72b..77f6d20 100644
--- a/frontends/verilog/verilog_parser.y
+++ b/frontends/verilog/verilog_parser.y
@@ -155,7 +155,7 @@
 
 %type <ast> range range_or_multirange  non_opt_range non_opt_multirange range_or_signed_int
 %type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list
-%type <string> opt_label opt_sva_label tok_prim_wrapper hierarchical_id
+%type <string> opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id
 %type <boolean> opt_signed opt_property unique_case_attr
 %type <al> attr case_attr
 
@@ -206,6 +206,7 @@
 	task_func_decl design |
 	param_decl design |
 	localparam_decl design |
+	typedef_decl design |
 	package design |
 	interface design |
 	/* empty */;
@@ -290,6 +291,9 @@
 		$$ = $1;
 	};
 
+hierarchical_type_id:
+	'(' hierarchical_id ')' { $$ = $2; };
+
 module:
 	attr TOK_MODULE TOK_ID {
 		do_not_require_port_stubs = false;
@@ -324,13 +328,13 @@
 		astbuf1 = new AstNode(AST_PARAMETER);
 		astbuf1->children.push_back(AstNode::mkconst_int(0, true));
 		append_attr(astbuf1, $1);
-	} param_signed param_integer param_range single_param_decl |
+	} param_type single_param_decl |
 	attr TOK_LOCALPARAM {
 		if (astbuf1) delete astbuf1;
 		astbuf1 = new AstNode(AST_LOCALPARAM);
 		astbuf1->children.push_back(AstNode::mkconst_int(0, true));
 		append_attr(astbuf1, $1);
-	} param_signed param_integer param_range single_param_decl |
+	} param_type single_param_decl |
 	single_param_decl;
 
 module_args_opt:
@@ -426,6 +430,7 @@
 	package_body package_body_stmt |;
 
 package_body_stmt:
+	typedef_decl |
 	localparam_decl;
 
 interface:
@@ -452,7 +457,7 @@
 	interface_body interface_body_stmt |;
 
 interface_body_stmt:
-	param_decl | localparam_decl | defparam_decl | wire_decl | always_stmt | assign_stmt |
+	param_decl | localparam_decl | typedef_decl | defparam_decl | wire_decl | always_stmt | assign_stmt |
 	modport_stmt;
 
 non_opt_delay:
@@ -475,8 +480,14 @@
 	};
 
 wire_type_token_list:
-	wire_type_token | wire_type_token_list wire_type_token |
-	wire_type_token_io ;
+	wire_type_token |
+	wire_type_token_list wire_type_token |
+	wire_type_token_io |
+	hierarchical_type_id {
+		astbuf3->is_custom_type = true;
+		astbuf3->children.push_back(new AstNode(AST_WIRETYPE));
+		astbuf3->children.back()->str = *$1;
+	};
 
 wire_type_token_io:
 	TOK_INPUT {
@@ -591,7 +602,7 @@
 	/* empty */;
 
 module_body_stmt:
-	task_func_decl | specify_block |param_decl | localparam_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt |
+	task_func_decl | specify_block | param_decl | localparam_decl | typedef_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt |
 	always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block;
 
 checker_decl:
@@ -1149,12 +1160,20 @@
 		}
 	};
 
+param_type:
+	param_signed param_integer param_real param_range |
+	hierarchical_type_id {
+		astbuf1->is_custom_type = true;
+		astbuf1->children.push_back(new AstNode(AST_WIRETYPE));
+		astbuf1->children.back()->str = *$1;
+	};
+
 param_decl:
 	attr TOK_PARAMETER {
 		astbuf1 = new AstNode(AST_PARAMETER);
 		astbuf1->children.push_back(AstNode::mkconst_int(0, true));
 		append_attr(astbuf1, $1);
-	} param_signed param_integer param_real param_range param_decl_list ';' {
+	} param_type param_decl_list ';' {
 		delete astbuf1;
 	};
 
@@ -1163,7 +1182,7 @@
 		astbuf1 = new AstNode(AST_LOCALPARAM);
 		astbuf1->children.push_back(AstNode::mkconst_int(0, true));
 		append_attr(astbuf1, $1);
-	} param_signed param_integer param_real param_range param_decl_list ';' {
+	} param_type param_decl_list ';' {
 		delete astbuf1;
 	};
 
@@ -1327,7 +1346,7 @@
 		if ($2 != NULL) {
 			if (node->is_input || node->is_output)
 				frontend_verilog_yyerror("input/output/inout ports cannot have unpacked dimensions.");
-			if (!astbuf2) {
+			if (!astbuf2 && !node->is_custom_type) {
 				AstNode *rng = new AstNode(AST_RANGE);
 				rng->children.push_back(AstNode::mkconst_int(0, true));
 				rng->children.push_back(AstNode::mkconst_int(0, true));
@@ -1377,6 +1396,45 @@
 		ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, $1, $3));
 	};
 
+typedef_decl:
+	TOK_TYPEDEF wire_type range TOK_ID range_or_multirange ';' {
+		astbuf1 = $2;
+		astbuf2 = $3;
+		if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) {
+			if (astbuf2) {
+				frontend_verilog_yyerror("integer/genvar types cannot have packed dimensions.");
+			} else {
+				astbuf2 = new AstNode(AST_RANGE);
+				astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true));
+				astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_right, true));
+			}
+		}
+		if (astbuf2 && astbuf2->children.size() != 2)
+			frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]");
+		if (astbuf2)
+			astbuf1->children.push_back(astbuf2);
+
+		if ($5 != NULL) {
+			if (!astbuf2) {
+				AstNode *rng = new AstNode(AST_RANGE);
+				rng->children.push_back(AstNode::mkconst_int(0, true));
+				rng->children.push_back(AstNode::mkconst_int(0, true));
+				astbuf1->children.push_back(rng);
+			}
+			astbuf1->type = AST_MEMORY;
+			auto *rangeNode = $5;
+			if (rangeNode->type == AST_RANGE && rangeNode->children.size() == 1) {
+				// SV array size [n], rewrite as [n-1:0]
+				rangeNode->children[0] = new AstNode(AST_SUB, rangeNode->children[0], AstNode::mkconst_int(1, true));
+				rangeNode->children.push_back(AstNode::mkconst_int(0, false));
+			}
+			astbuf1->children.push_back(rangeNode);
+		}
+
+		ast_stack.back()->children.push_back(new AstNode(AST_TYPEDEF, astbuf1));
+		ast_stack.back()->children.back()->str = *$4;
+	};
+
 cell_stmt:
 	attr TOK_ID {
 		astbuf1 = new AstNode(AST_CELL);
@@ -1823,7 +1881,7 @@
 
 // this production creates the obligatory if-else shift/reduce conflict
 behavioral_stmt:
-	defattr | assert | wire_decl | param_decl | localparam_decl |
+	defattr | assert | wire_decl | param_decl | localparam_decl | typedef_decl |
 	non_opt_delay behavioral_stmt |
 	simple_behavioral_stmt ';' | ';' |
 	hierarchical_id attr {
diff --git a/techlibs/ecp5/cells_bb.v b/techlibs/ecp5/cells_bb.v
index 0a5046d..ae124e7 100644
--- a/techlibs/ecp5/cells_bb.v
+++ b/techlibs/ecp5/cells_bb.v
@@ -334,6 +334,13 @@
 endmodule
 
 (* blackbox *)
+module ECLKBRIDGECS(
+	input CLK0, CLK1, SEL,
+	output ECSOUT
+);
+endmodule
+
+(* blackbox *)
 module DCCA(
 	input CLKI, CE,
 	output CLKO
diff --git a/techlibs/ecp5/cells_ff.vh b/techlibs/ecp5/cells_ff.vh
index 0c9689e..501c1b3 100644
--- a/techlibs/ecp5/cells_ff.vh
+++ b/techlibs/ecp5/cells_ff.vh
@@ -23,15 +23,15 @@
 // module FL1S3AY(); endmodule
 
 // Diamond I/O registers
-module IFS1P3BX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("ASYNC"))       _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
-module IFS1P3DX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))       _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
-module IFS1P3IX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
-module IFS1P3JX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module IFS1P3BX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="input" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("ASYNC"))       _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module IFS1P3DX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="input" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))       _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module IFS1P3IX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="input" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module IFS1P3JX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="input" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
 
-module OFS1P3BX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("ASYNC"))       _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
-module OFS1P3DX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))       _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
-module OFS1P3IX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
-module OFS1P3JX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module OFS1P3BX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="output" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("ASYNC"))       _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module OFS1P3DX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="output" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))       _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module OFS1P3IX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="output" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module OFS1P3JX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="output" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
 
 // TODO: Diamond I/O latches
 // module IFS1S1B(input PD, D, SCLK, output Q); endmodule
diff --git a/techlibs/ecp5/synth_ecp5.cc b/techlibs/ecp5/synth_ecp5.cc
index 80aa1db..a79dee3 100644
--- a/techlibs/ecp5/synth_ecp5.cc
+++ b/techlibs/ecp5/synth_ecp5.cc
@@ -297,6 +297,7 @@
 			run("simplemap");
 			run("ecp5_ffinit");
 			run("ecp5_gsr");
+			run("attrmvcp -copy -attr syn_useioff");
 			run("opt_clean");
 		}
 
diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v
index 28cd208..03985b1 100644
--- a/techlibs/xilinx/cells_sim.v
+++ b/techlibs/xilinx/cells_sim.v
@@ -38,6 +38,17 @@
   assign O = I;
 endmodule
 
+module IBUFG(
+    output O,
+    (* iopad_external_pin *)
+    input I);
+  parameter CAPACITANCE = "DONT_CARE";
+  parameter IBUF_DELAY_VALUE = "0";
+  parameter IBUF_LOW_PWR = "TRUE";
+  parameter IOSTANDARD = "DEFAULT";
+  assign O = I;
+endmodule
+
 module OBUF(
     (* iopad_external_pin *)
     output O,
diff --git a/techlibs/xilinx/cells_xtra.py b/techlibs/xilinx/cells_xtra.py
index ee20ae9..9a4747f 100644
--- a/techlibs/xilinx/cells_xtra.py
+++ b/techlibs/xilinx/cells_xtra.py
@@ -53,7 +53,7 @@
     # Cell('IBUF', port_attrs={'I': ['iopad_external_pin']}),
     Cell('IBUFDS', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
     Cell('IBUFDS_DIFF_OUT', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
-    Cell('IBUFG', port_attrs={'I': ['iopad_external_pin']}),
+    # Cell('IBUFG', port_attrs={'I': ['iopad_external_pin']}),
     Cell('IBUFGDS', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
     Cell('IBUFGDS_DIFF_OUT', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
     Cell('IOBUF', port_attrs={'IO': ['iopad_external_pin']}),
@@ -174,7 +174,7 @@
     Cell('IBUFDS', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
     Cell('IBUFDS_DIFF_OUT', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
     Cell('IBUFDS_GTHE1', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
-    Cell('IBUFG', port_attrs={'I': ['iopad_external_pin']}),
+    # Cell('IBUFG', port_attrs={'I': ['iopad_external_pin']}),
     Cell('IBUFGDS', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
     Cell('IBUFGDS_DIFF_OUT', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
     Cell('IDELAYCTRL', keep=True, port_attrs={'REFCLK': ['clkbuf_sink']}),
@@ -307,7 +307,7 @@
     Cell('IBUFDS_GTE2', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
     Cell('IBUFDS_IBUFDISABLE', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
     Cell('IBUFDS_INTERMDISABLE', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
-    Cell('IBUFG', port_attrs={'I': ['iopad_external_pin']}),
+    # Cell('IBUFG', port_attrs={'I': ['iopad_external_pin']}),
     Cell('IBUFGDS', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
     Cell('IBUFGDS_DIFF_OUT', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
     Cell('IDELAYCTRL', keep=True, port_attrs={'REFCLK': ['clkbuf_sink']}),
diff --git a/techlibs/xilinx/xc6s_cells_xtra.v b/techlibs/xilinx/xc6s_cells_xtra.v
index f8dcce8..7c0462b 100644
--- a/techlibs/xilinx/xc6s_cells_xtra.v
+++ b/techlibs/xilinx/xc6s_cells_xtra.v
@@ -1282,16 +1282,6 @@
     input IB;
 endmodule
 
-module IBUFG (...);
-    parameter CAPACITANCE = "DONT_CARE";
-    parameter IBUF_DELAY_VALUE = "0";
-    parameter IBUF_LOW_PWR = "TRUE";
-    parameter IOSTANDARD = "DEFAULT";
-    output O;
-    (* iopad_external_pin *)
-    input I;
-endmodule
-
 module IBUFGDS (...);
     parameter CAPACITANCE = "DONT_CARE";
     parameter DIFF_TERM = "FALSE";
diff --git a/techlibs/xilinx/xc6v_cells_xtra.v b/techlibs/xilinx/xc6v_cells_xtra.v
index d9e06ea..87656fa 100644
--- a/techlibs/xilinx/xc6v_cells_xtra.v
+++ b/techlibs/xilinx/xc6v_cells_xtra.v
@@ -1821,16 +1821,6 @@
     input IB;
 endmodule
 
-module IBUFG (...);
-    parameter CAPACITANCE = "DONT_CARE";
-    parameter IBUF_DELAY_VALUE = "0";
-    parameter IBUF_LOW_PWR = "TRUE";
-    parameter IOSTANDARD = "DEFAULT";
-    output O;
-    (* iopad_external_pin *)
-    input I;
-endmodule
-
 module IBUFGDS (...);
     parameter CAPACITANCE = "DONT_CARE";
     parameter DIFF_TERM = "FALSE";
diff --git a/techlibs/xilinx/xc7_cells_xtra.v b/techlibs/xilinx/xc7_cells_xtra.v
index f36e4ba..10eea4a 100644
--- a/techlibs/xilinx/xc7_cells_xtra.v
+++ b/techlibs/xilinx/xc7_cells_xtra.v
@@ -3932,16 +3932,6 @@
     input INTERMDISABLE;
 endmodule
 
-module IBUFG (...);
-    parameter CAPACITANCE = "DONT_CARE";
-    parameter IBUF_DELAY_VALUE = "0";
-    parameter IBUF_LOW_PWR = "TRUE";
-    parameter IOSTANDARD = "DEFAULT";
-    output O;
-    (* iopad_external_pin *)
-    input I;
-endmodule
-
 module IBUFGDS (...);
     parameter CAPACITANCE = "DONT_CARE";
     parameter DIFF_TERM = "FALSE";
diff --git a/tests/svtypes/.gitignore b/tests/svtypes/.gitignore
new file mode 100644
index 0000000..b48f808
--- /dev/null
+++ b/tests/svtypes/.gitignore
@@ -0,0 +1,3 @@
+/*.log
+/*.out
+/run-test.mk
diff --git a/tests/svtypes/run-test.sh b/tests/svtypes/run-test.sh
new file mode 100755
index 0000000..09a30ee
--- /dev/null
+++ b/tests/svtypes/run-test.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+set -e
+{
+echo "all::"
+for x in *.ys; do
+	echo "all:: run-$x"
+	echo "run-$x:"
+	echo "	@echo 'Running $x..'"
+	echo "	@../../yosys -ql ${x%.ys}.log $x"
+done
+for x in *.sv; do
+	if [ ! -f "${x%.sv}.ys"  ]; then
+		echo "all:: check-$x"
+		echo "check-$x:"
+		echo "	@echo 'Checking $x..'"
+		echo "	@../../yosys -ql ${x%.sv}.log -p \"prep -top top; sat -verify -prove-asserts\" $x"
+	fi
+done
+} > run-test.mk
+exec ${MAKE:-make} -f run-test.mk
diff --git a/tests/svtypes/typedef_memory.sv b/tests/svtypes/typedef_memory.sv
new file mode 100644
index 0000000..577e484
--- /dev/null
+++ b/tests/svtypes/typedef_memory.sv
@@ -0,0 +1,10 @@
+module top(input [3:0] addr, wdata, input clk, wen, output reg [3:0] rdata);
+	typedef logic [3:0] ram16x4_t[0:15];
+
+	(ram16x4_t) mem;
+
+	always @(posedge clk) begin
+		if (wen) mem[addr] <= wdata;
+		rdata <= mem[addr];
+	end
+endmodule
diff --git a/tests/svtypes/typedef_memory.ys b/tests/svtypes/typedef_memory.ys
new file mode 100644
index 0000000..93cf47b
--- /dev/null
+++ b/tests/svtypes/typedef_memory.ys
@@ -0,0 +1,3 @@
+read_verilog -sv typedef_memory.sv
+prep -top top
+select -assert-count 1 t:$mem r:SIZE=16 %i r:WIDTH=4 %i
diff --git a/tests/svtypes/typedef_memory_2.sv b/tests/svtypes/typedef_memory_2.sv
new file mode 100644
index 0000000..f3089bf
--- /dev/null
+++ b/tests/svtypes/typedef_memory_2.sv
@@ -0,0 +1,10 @@
+module top(input [3:0] addr, wdata, input clk, wen, output reg [3:0] rdata);
+	typedef logic [3:0] nibble;
+
+	(nibble) mem[0:15];
+
+	always @(posedge clk) begin
+		if (wen) mem[addr] <= wdata;
+		rdata <= mem[addr];
+	end
+endmodule
diff --git a/tests/svtypes/typedef_memory_2.ys b/tests/svtypes/typedef_memory_2.ys
new file mode 100644
index 0000000..854e554
--- /dev/null
+++ b/tests/svtypes/typedef_memory_2.ys
@@ -0,0 +1,4 @@
+read_verilog -sv typedef_memory_2.sv
+prep -top top
+dump
+select -assert-count 1 t:$mem r:SIZE=16 %i r:WIDTH=4 %i
diff --git a/tests/svtypes/typedef_package.sv b/tests/svtypes/typedef_package.sv
new file mode 100644
index 0000000..a1e16d4
--- /dev/null
+++ b/tests/svtypes/typedef_package.sv
@@ -0,0 +1,11 @@
+package pkg;
+	typedef logic [7:0] uint8_t;
+endpackage
+
+module top;
+
+	(* keep *) (pkg::uint8_t) a = 8'hAA;
+
+	always @* assert(a == 8'hAA);
+
+endmodule
diff --git a/tests/svtypes/typedef_param.sv b/tests/svtypes/typedef_param.sv
new file mode 100644
index 0000000..ddbd471
--- /dev/null
+++ b/tests/svtypes/typedef_param.sv
@@ -0,0 +1,22 @@
+`define STRINGIFY(x) `"x`"
+`define STATIC_ASSERT(x) if(!(x)) $error({"assert failed: ", `STRINGIFY(x)})
+
+module top;
+
+	typedef logic [1:0] uint2_t;
+	typedef logic signed [3:0] int4_t;
+	typedef logic signed [7:0] int8_t;
+	typedef (int8_t) char_t;
+
+	parameter (uint2_t) int2 = 2'b10;
+	localparam (int4_t) int4 = -1;
+	localparam (int8_t) int8 = int4;
+	localparam (char_t) ch = int8;
+
+
+	`STATIC_ASSERT(int2 == 2'b10);
+	`STATIC_ASSERT(int4 == 4'b1111);
+	`STATIC_ASSERT(int8 == 8'b11111111);
+	`STATIC_ASSERT(ch   == 8'b11111111);
+
+endmodule
diff --git a/tests/svtypes/typedef_scopes.sv b/tests/svtypes/typedef_scopes.sv
new file mode 100644
index 0000000..faa385b
--- /dev/null
+++ b/tests/svtypes/typedef_scopes.sv
@@ -0,0 +1,23 @@
+
+typedef logic [3:0] outer_uint4_t;
+
+module top;
+
+	(outer_uint4_t) u4_i = 8'hA5;
+	always @(*) assert(u4_i == 4'h5);
+
+	typedef logic [3:0] inner_type;
+	(inner_type) inner_i1 = 8'h5A;
+	always @(*) assert(inner_i1 == 4'hA);
+
+	if (1) begin: genblock
+		typedef logic [7:0] inner_type;
+		(inner_type) inner_gb_i = 8'hA5;
+		always @(*) assert(inner_gb_i == 8'hA5);
+	end
+
+	(inner_type) inner_i2 = 8'h42;
+	always @(*) assert(inner_i2 == 4'h2);
+
+
+endmodule
diff --git a/tests/svtypes/typedef_simple.sv b/tests/svtypes/typedef_simple.sv
new file mode 100644
index 0000000..7e760de
--- /dev/null
+++ b/tests/svtypes/typedef_simple.sv
@@ -0,0 +1,19 @@
+module top;
+
+	typedef logic [1:0] uint2_t;
+	typedef logic signed [3:0] int4_t;
+	typedef logic signed [7:0] int8_t;
+	typedef (int8_t) char_t;
+
+	(* keep *) (uint2_t) int2 = 2'b10;
+	(* keep *) (int4_t) int4 = -1;
+	(* keep *) (int8_t) int8 = int4;
+	(* keep *) (char_t) ch = int8;
+
+
+	always @* assert(int2 == 2'b10);
+	always @* assert(int4 == 4'b1111);
+	always @* assert(int8 == 8'b11111111);
+	always @* assert(ch   == 8'b11111111);
+
+endmodule