mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 11:42:30 +00:00 
			
		
		
		
	Generalise structs and add support for packed unions.
This commit is contained in:
		
							parent
							
								
									0b6b47ca67
								
							
						
					
					
						commit
						f482c9c016
					
				
					 8 changed files with 209 additions and 59 deletions
				
			
		|  | @ -556,7 +556,7 @@ from SystemVerilog: | |||
| - enums are supported (including inside packages) | ||||
| 	- but are currently not strongly typed | ||||
| 
 | ||||
| - structs are supported | ||||
| - packed structs and unions are supported. | ||||
| 
 | ||||
| - SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether | ||||
|   ports are inputs or outputs are supported. | ||||
|  |  | |||
|  | @ -172,6 +172,7 @@ std::string AST::type2str(AstNodeType type) | |||
| 	X(AST_WIRETYPE) | ||||
| 	X(AST_TYPEDEF) | ||||
| 	X(AST_STRUCT) | ||||
| 	X(AST_UNION) | ||||
| 	X(AST_STRUCT_ITEM) | ||||
| #undef X | ||||
| 	default: | ||||
|  |  | |||
|  | @ -158,6 +158,7 @@ namespace AST | |||
| 		AST_WIRETYPE, | ||||
| 		AST_TYPEDEF, | ||||
| 		AST_STRUCT, | ||||
| 		AST_UNION, | ||||
| 		AST_STRUCT_ITEM | ||||
| 	}; | ||||
| 
 | ||||
|  |  | |||
|  | @ -992,6 +992,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) | |||
| 	case AST_MODPORTMEMBER: | ||||
| 	case AST_TYPEDEF: | ||||
| 	case AST_STRUCT: | ||||
| 	case AST_UNION: | ||||
| 		break; | ||||
| 	case AST_INTERFACEPORT: { | ||||
| 		// If a port in a module with unknown type is found, mark it with the attribute 'is_interface'
 | ||||
|  |  | |||
|  | @ -250,6 +250,97 @@ static AstNode *make_range(int left, int right, bool is_signed = false) | |||
| 	return range; | ||||
| } | ||||
| 
 | ||||
| int size_packed_struct(AstNode *snode, int base_offset) | ||||
| { | ||||
| 	// Struct members will be laid out in the structure contiguously from left to right.
 | ||||
| 	// Union members all have zero offset from the start of the union.
 | ||||
| 	// Determine total packed size and assign offsets.  Store these in the member node.
 | ||||
| 	bool is_union = (snode->type == AST_UNION); | ||||
| 	int offset = 0; | ||||
| 	int packed_width = -1; | ||||
| 	// examine members from last to first
 | ||||
| 	for (auto it = snode->children.rbegin(); it != snode->children.rend(); ++it) { | ||||
| 		auto node = *it; | ||||
| 		int width; | ||||
| 		if (node->type == AST_STRUCT || node->type == AST_UNION) { | ||||
| 			// embedded struct or union
 | ||||
| 			width = size_packed_struct(node, base_offset + offset); | ||||
| 		} | ||||
| 		else { | ||||
| 			log_assert(node->type == AST_STRUCT_ITEM); | ||||
| 			if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) { | ||||
| 				auto rnode = node->children[0]; | ||||
| 				width = (rnode->range_swapped ? rnode->range_right - rnode->range_left : | ||||
| 								rnode->range_left - rnode->range_right) + 1; | ||||
| 				// range nodes are now redundant
 | ||||
| 				node->children.clear(); | ||||
| 			} | ||||
| 			else if (node->range_left < 0) { | ||||
| 				// 1 bit signal: bit, logic or reg
 | ||||
| 				width = 1; | ||||
| 			} | ||||
| 			else { | ||||
| 				// already resolved and compacted
 | ||||
| 				width = node->range_left - node->range_right + 1; | ||||
| 			} | ||||
| 			if (is_union) { | ||||
| 				node->range_right = base_offset; | ||||
| 				node->range_left = base_offset + width - 1; | ||||
| 			} | ||||
| 			else { | ||||
| 				node->range_right = base_offset + offset; | ||||
| 				node->range_left = base_offset + offset + width - 1; | ||||
| 			} | ||||
| 			node->range_valid = true; | ||||
| 		} | ||||
| 		if (is_union) { | ||||
| 			// check that all members have the same size
 | ||||
| 			if (packed_width == -1) { | ||||
| 				// first member
 | ||||
| 				packed_width = width; | ||||
| 			} | ||||
| 			else { | ||||
| 				if (packed_width != width) { | ||||
| 
 | ||||
| 					log_file_error(node->filename, node->location.first_line, "member %s of a packed union has %d bits, expecting %d\n", node->str.c_str(), width, packed_width); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		else { | ||||
| 			offset += width; | ||||
| 		} | ||||
| 	} | ||||
| 	return (is_union ? packed_width : offset); | ||||
| } | ||||
| 
 | ||||
| static void add_members_to_scope(AstNode *snode, std::string name) | ||||
| { | ||||
| 	// add all the members in a struct or union to local scope
 | ||||
| 	// in case later referenced in assignments
 | ||||
| 	log_assert(snode->type==AST_STRUCT || snode->type==AST_UNION); | ||||
| 	for (auto *node : snode->children) { | ||||
| 		if (node->type != AST_STRUCT_ITEM) { | ||||
| 			// embedded struct or union
 | ||||
| 			add_members_to_scope(node, name + "." + node->str); | ||||
| 		} | ||||
| 		else { | ||||
| 			auto member_name = name + "." + node->str; | ||||
| 			current_scope[member_name] = node; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int get_max_offset(AstNode *node) | ||||
| { | ||||
| 	// get the width from the MS member in the struct
 | ||||
| 	// as members are laid out from left to right in the packed wire
 | ||||
| 	log_assert(node->type==AST_STRUCT || node->type==AST_UNION); | ||||
| 	while (node->type != AST_STRUCT_ITEM) { | ||||
| 		node = node->children[0]; | ||||
| 	} | ||||
| 	return node->range_left; | ||||
| } | ||||
| 
 | ||||
| static AstNode *make_packed_struct(AstNode *template_node, std::string &name) | ||||
| { | ||||
| 	// create a wire for the packed struct
 | ||||
|  | @ -257,18 +348,14 @@ static AstNode *make_packed_struct(AstNode *template_node, std::string &name) | |||
| 	wnode->str = name; | ||||
| 	wnode->is_logic = true; | ||||
| 	wnode->range_valid = true; | ||||
| 	// get the width from the MS member in the template
 | ||||
| 	// as members are laid out from left to right
 | ||||
| 	int offset = template_node->children[0]->range_left; | ||||
| 	wnode->is_signed = template_node->is_signed; | ||||
| 	int offset = get_max_offset(template_node); | ||||
| 	auto range = make_range(offset, 0); | ||||
| 	wnode->children.push_back(range); | ||||
| 	// make sure this node is the one in scope for this name
 | ||||
| 	current_scope[name] = wnode; | ||||
| 	// add members to scope
 | ||||
| 	for (auto *node : template_node->children) { | ||||
| 		auto member_name = name + "." + node->str; | ||||
| 		current_scope[member_name] = node; | ||||
| 	} | ||||
| 	// add all the struct members to scope under the wire's name
 | ||||
| 	add_members_to_scope(template_node, name); | ||||
| 	return wnode; | ||||
| } | ||||
| 
 | ||||
|  | @ -672,46 +759,25 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | |||
| 		break; | ||||
| 
 | ||||
| 	case AST_STRUCT: | ||||
| 		//log("STRUCT %d %d %d\n", stage, basic_prep, in_param);
 | ||||
| 	case AST_UNION: | ||||
| 		if (!basic_prep) { | ||||
| 			//dumpAst(NULL, "1> ");
 | ||||
| 			for (auto *node : children) { | ||||
| 				// resolve any ranges
 | ||||
| 				while (!node->basic_prep && node->simplify(true, false, false, stage, -1, false, false)) { | ||||
| 					did_something = true; | ||||
| 				} | ||||
| 			} | ||||
| 			basic_prep = true; | ||||
| 			// The members will be laid out in the structure contiguously from left to right.
 | ||||
| 			// Determine total packed size and assign offsets.  Store these in the member node.
 | ||||
| 			// dumpAst(NULL, "2> ");
 | ||||
| 			int offset = 0; | ||||
| 			for (auto it = children.rbegin(); it != children.rend(); ++it) { | ||||
| 				auto node = *it; | ||||
| 				if (is_signed) | ||||
| 					node->is_signed = true; | ||||
| 				int width; | ||||
| 				if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) { | ||||
| 					auto rnode = node->children[0]; | ||||
| 					width = (rnode->range_swapped ? rnode->range_right - rnode->range_left : | ||||
| 									rnode->range_left - rnode->range_right) + 1; | ||||
| 					// range nodes are now redundant
 | ||||
| 					node->children.clear(); | ||||
| 				} | ||||
| 				else { | ||||
| 					width = 1; | ||||
| 				} | ||||
| 				node->range_right = offset; | ||||
| 				node->range_left = offset + width - 1; | ||||
| 				node->range_valid = true; | ||||
| 				offset += width; | ||||
| 			} | ||||
| 			if (!str.empty()) { | ||||
| 				// instance rather than just a type in a typedef
 | ||||
| 				// so add a wire for the packed structure
 | ||||
| 			// determine member offsets and widths
 | ||||
| 			size_packed_struct(this, 0); | ||||
| 
 | ||||
| 			// instance rather than just a type in a typedef or outer struct?
 | ||||
| 			if (!str.empty() && str[0] == '\\') { | ||||
| 				// instance so add a wire for the packed structure
 | ||||
| 				auto wnode = make_packed_struct(this, str); | ||||
| 				log_assert(current_ast_mod); | ||||
| 				current_ast_mod->children.push_back(wnode); | ||||
| 			} | ||||
| 			basic_prep = true; | ||||
| 		} | ||||
| 		break; | ||||
| 
 | ||||
|  | @ -1036,7 +1102,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | |||
| 	if (type == AST_TYPEDEF) { | ||||
| 		log_assert(children.size() == 1); | ||||
| 		auto type_node = children[0]; | ||||
| 		log_assert(type_node->type == AST_WIRE || type_node->type == AST_MEMORY || type_node->type == AST_STRUCT); | ||||
| 		log_assert(type_node->type == AST_WIRE || type_node->type == AST_MEMORY || type_node->type == AST_STRUCT || type_node->type == AST_UNION); | ||||
| 		while (type_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { | ||||
| 			did_something = true; | ||||
| 		} | ||||
|  | @ -1061,7 +1127,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | |||
| 			// Ensure typedef itself is fully simplified
 | ||||
| 			while (template_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {}; | ||||
| 
 | ||||
| 			if (template_node->type == AST_STRUCT) { | ||||
| 			if (template_node->type == AST_STRUCT || template_node->type == AST_UNION) { | ||||
| 				// replace with wire representing the packed structure
 | ||||
| 				newNode = make_packed_struct(template_node, str); | ||||
| 				current_scope[str] = this; | ||||
|  |  | |||
|  | @ -265,6 +265,7 @@ static bool isUserType(std::string &s) | |||
| "bit"        { SV_KEYWORD(TOK_LOGIC); } | ||||
| "int"        { SV_KEYWORD(TOK_INT); } | ||||
| "byte"       { SV_KEYWORD(TOK_BYTE); } | ||||
| "shortint"   { SV_KEYWORD(TOK_SHORTINT); } | ||||
| 
 | ||||
| "eventually"   { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); } | ||||
| "s_eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); } | ||||
|  | @ -284,8 +285,9 @@ static bool isUserType(std::string &s) | |||
| 
 | ||||
| "enum"    { SV_KEYWORD(TOK_ENUM); } | ||||
| "typedef" { SV_KEYWORD(TOK_TYPEDEF); } | ||||
| "struct" { SV_KEYWORD(TOK_STRUCT); } | ||||
| "packed" { SV_KEYWORD(TOK_PACKED); } | ||||
| "struct"  { SV_KEYWORD(TOK_STRUCT); } | ||||
| "union"   { SV_KEYWORD(TOK_UNION); } | ||||
| "packed"  { SV_KEYWORD(TOK_PACKED); } | ||||
| 
 | ||||
| [0-9][0-9_]* { | ||||
| 	yylval->string = new std::string(yytext); | ||||
|  |  | |||
|  | @ -238,6 +238,7 @@ static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode) | |||
| %union { | ||||
| 	std::string *string; | ||||
| 	struct YOSYS_NAMESPACE_PREFIX AST::AstNode *ast; | ||||
| 	YOSYS_NAMESPACE_PREFIX AST::AstNodeType type; | ||||
| 	YOSYS_NAMESPACE_PREFIX dict<YOSYS_NAMESPACE_PREFIX RTLIL::IdString, YOSYS_NAMESPACE_PREFIX AST::AstNode*> *al; | ||||
| 	struct specify_target *specify_target_ptr; | ||||
| 	struct specify_triple *specify_triple_ptr; | ||||
|  | @ -269,7 +270,7 @@ static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode) | |||
| %token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_PROPERTY TOK_ENUM TOK_TYPEDEF | ||||
| %token TOK_RAND TOK_CONST TOK_CHECKER TOK_ENDCHECKER TOK_EVENTUALLY | ||||
| %token TOK_INCREMENT TOK_DECREMENT TOK_UNIQUE TOK_PRIORITY | ||||
| %token TOK_STRUCT TOK_PACKED TOK_UNSIGNED TOK_INT TOK_BYTE | ||||
| %token TOK_STRUCT TOK_PACKED TOK_UNSIGNED TOK_INT TOK_BYTE TOK_SHORTINT TOK_UNION  | ||||
| 
 | ||||
| %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 | ||||
|  | @ -278,6 +279,7 @@ static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode) | |||
| %type <ast> opt_enum_init enum_type struct_type non_wire_data_type | ||||
| %type <boolean> opt_signed opt_property unique_case_attr always_comb_or_latch always_or_always_ff | ||||
| %type <al> attr case_attr | ||||
| %type <type> struct_union | ||||
| 
 | ||||
| %type <specify_target_ptr> specify_target | ||||
| %type <specify_triple_ptr> specify_triple specify_opt_triple | ||||
|  | @ -328,7 +330,6 @@ design: | |||
| 	param_decl design | | ||||
| 	localparam_decl design | | ||||
| 	typedef_decl design | | ||||
| 	struct_decl design | | ||||
| 	package design | | ||||
| 	interface design | | ||||
| 	/* empty */; | ||||
|  | @ -568,8 +569,7 @@ package_body: | |||
| 	; | ||||
| 
 | ||||
| package_body_stmt: | ||||
| 	typedef_decl | ||||
| 	| struct_decl | ||||
| 	  typedef_decl | ||||
| 	| localparam_decl | ||||
| 	| param_decl | ||||
| 	; | ||||
|  | @ -601,7 +601,7 @@ interface_body: | |||
| 	interface_body interface_body_stmt |; | ||||
| 
 | ||||
| interface_body_stmt: | ||||
| 	param_decl | localparam_decl | typedef_decl | struct_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: | ||||
|  | @ -1442,6 +1442,7 @@ enum_base_type: type_atom type_signing | |||
| 
 | ||||
| type_atom: TOK_INTEGER		{ astbuf1->is_reg = true; addRange(astbuf1); }		// 4-state signed | ||||
| 	|  TOK_INT		{ astbuf1->is_reg = true; addRange(astbuf1); }		// 2-state signed | ||||
| 	|  TOK_SHORTINT		{ astbuf1->is_reg = true; addRange(astbuf1, 15, 0); }	// 2-state signed | ||||
| 	|  TOK_BYTE		{ astbuf1->is_reg = true; addRange(astbuf1,  7, 0); }	// 2-state signed | ||||
| 	; | ||||
| 
 | ||||
|  | @ -1467,6 +1468,7 @@ enum_name_decl: | |||
| 		auto node = astbuf1->clone(); | ||||
| 		node->str = *$1; | ||||
| 		delete $1; | ||||
| 		SET_AST_NODE_LOC(node, @1, @1); | ||||
| 		delete node->children[0]; | ||||
| 		node->children[0] = $2 ?: new AstNode(AST_NONE); | ||||
| 		astbuf2->children.push_back(node); | ||||
|  | @ -1490,6 +1492,7 @@ enum_var: TOK_ID { | |||
| 		ast_stack.back()->children.push_back(node); | ||||
| 		node->str = *$1; | ||||
| 		delete $1; | ||||
| 		SET_AST_NODE_LOC(node, @1, @1); | ||||
| 		node->is_enum = true; | ||||
| 	} | ||||
| 	; | ||||
|  | @ -1497,23 +1500,29 @@ enum_var: TOK_ID { | |||
| enum_decl: enum_type enum_var_list ';'		{ delete $1; } | ||||
| 	; | ||||
| 
 | ||||
| ///////// | ||||
| // struct | ||||
| ///////// | ||||
| ////////////////// | ||||
| // struct or union | ||||
| ////////////////// | ||||
| 
 | ||||
| struct_decl: struct_type struct_var_list ';' 	{ delete astbuf2; } | ||||
| 	; | ||||
| 
 | ||||
| struct_type: TOK_STRUCT { astbuf2 = new AstNode(AST_STRUCT); } opt_packed '{' struct_member_list '}' 	{ $$ = astbuf2; } | ||||
| struct_type: struct_union { astbuf2 = new AstNode($1); } opt_packed '{' struct_member_list '}' 	{ $$ = astbuf2; } | ||||
| 	; | ||||
| 
 | ||||
| struct_union: | ||||
| 	  TOK_STRUCT		{ $$ = AST_STRUCT; } | ||||
| 	| TOK_UNION		{ $$ = AST_UNION; } | ||||
| 	; | ||||
| 
 | ||||
| 
 | ||||
| opt_packed: TOK_PACKED opt_signed_struct | ||||
| 	| { frontend_verilog_yyerror("Only STRUCT PACKED supported at this time"); } | ||||
| 	| { frontend_verilog_yyerror("Only PACKED supported at this time"); } | ||||
| 	; | ||||
| 
 | ||||
| opt_signed_struct: | ||||
| 	  TOK_SIGNED		{ astbuf2->is_signed = true; } | ||||
| 	| TOK_UNSIGNED | ||||
| 	| TOK_UNSIGNED		{ astbuf2->is_signed = false; } | ||||
| 	| // default is unsigned | ||||
| 	; | ||||
| 
 | ||||
|  | @ -1532,11 +1541,13 @@ member_name_list: | |||
| member_name: TOK_ID { | ||||
| 			astbuf1->str = $1->substr(1); | ||||
| 			delete $1; | ||||
| 			astbuf2->children.push_back(astbuf1->clone()); | ||||
| 			auto member_node = astbuf1->clone(); | ||||
| 			SET_AST_NODE_LOC(member_node, @1, @1); | ||||
| 			astbuf2->children.push_back(member_node); | ||||
| 		} | ||||
| 	; | ||||
| 
 | ||||
| struct_member_type: { astbuf1 = new AstNode(AST_STRUCT_ITEM); } member_type_token_list { SET_RULE_LOC(@$, @2, @$); } | ||||
| struct_member_type: { astbuf1 = new AstNode(AST_STRUCT_ITEM); } member_type_token_list | ||||
| 	; | ||||
| 
 | ||||
| member_type_token_list: | ||||
|  | @ -1544,12 +1555,18 @@ member_type_token_list: | |||
| 	| hierarchical_type_id { | ||||
| 			// use a clone of the typedef definition nodes | ||||
| 			auto template_node = copyTypeDefinition(*$1); | ||||
| 			if (template_node->type != AST_WIRE) { | ||||
| 			delete $1; | ||||
| 			switch (template_node->type) { | ||||
| 			case AST_WIRE: | ||||
| 				template_node->type = AST_STRUCT_ITEM; | ||||
| 				break; | ||||
| 			case AST_STRUCT: | ||||
| 			case AST_UNION: | ||||
| 				break; | ||||
| 			default: | ||||
| 				frontend_verilog_yyerror("Invalid type for struct member: %s", type2str(template_node->type).c_str()); | ||||
| 			} | ||||
| 			template_node->type = AST_STRUCT_ITEM; | ||||
| 			delete astbuf1; | ||||
| 			delete $1; | ||||
| 			astbuf1 = template_node; | ||||
| 		} | ||||
| 	; | ||||
|  | @ -1565,6 +1582,7 @@ struct_var_list: struct_var | |||
| struct_var: TOK_ID	{	auto *var_node = astbuf2->clone(); | ||||
| 				var_node->str = *$1; | ||||
| 				delete $1; | ||||
| 				SET_AST_NODE_LOC(var_node, @1, @1); | ||||
| 				ast_stack.back()->children.push_back(var_node); | ||||
| 			} | ||||
| 	; | ||||
|  |  | |||
							
								
								
									
										61
									
								
								tests/svtypes/union_simple.sv
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								tests/svtypes/union_simple.sv
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,61 @@ | |||
| module top; | ||||
| 
 | ||||
| 	typedef struct packed { | ||||
| 		byte a,b,c,d; | ||||
| 	} byte4_t; | ||||
| 
 | ||||
| 	typedef union packed { | ||||
| 		int	x; | ||||
| 		byte4_t	y; | ||||
| 	} w_t; | ||||
| 
 | ||||
| 	w_t w; | ||||
| 
 | ||||
| 	assign w.x = 'h42; | ||||
| 	always_comb begin | ||||
| 		assert(w.y.d == 8'h42); | ||||
| 	end | ||||
| 
 | ||||
| 	typedef logic[4:0] reg_addr_t; | ||||
| 	typedef logic[6:0] opcode_t; | ||||
| 
 | ||||
| 	typedef struct packed { | ||||
| 		bit [6:0]  func7; | ||||
| 		reg_addr_t rs2; | ||||
| 		reg_addr_t rs1; | ||||
| 		bit [2:0]  func3; | ||||
| 		reg_addr_t rd; | ||||
| 		opcode_t   opcode; | ||||
| 	} R_t; | ||||
| 
 | ||||
| 	typedef struct packed { | ||||
| 		bit[11:0]  imm; | ||||
| 		reg_addr_t rs1; | ||||
| 		bit[2:0]   func3; | ||||
| 		reg_addr_t rd; | ||||
| 		opcode_t   opcode; | ||||
| 	} I_t; | ||||
| 
 | ||||
| 	typedef struct packed { | ||||
| 		bit[19:0]  imm; | ||||
| 		reg_addr_t rd; | ||||
| 		opcode_t   opcode; | ||||
| 	} U_t; | ||||
| 
 | ||||
| 	typedef union packed { | ||||
| 		R_t	r; | ||||
| 		I_t	i; | ||||
| 		U_t	u; | ||||
| 	} instruction_t; | ||||
| 
 | ||||
| 	instruction_t ir1; | ||||
| 	assign ir1 = 32'h0AA01EB7;          //	lui t4,0xAA01
 | ||||
| 	always_comb begin | ||||
| 		assert(ir1.u.opcode == 'h37); | ||||
| 		assert(ir1.r.opcode == 'h37); | ||||
| 		assert(ir1.u.rd == 'd29); | ||||
| 		assert(ir1.r.rd == 'd29); | ||||
| 		assert(ir1.u.imm == 'hAA01); | ||||
| 	end | ||||
| 
 | ||||
| endmodule | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue