mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-04 13:29:12 +00:00 
			
		
		
		
	Handling of attributes for struct / union variables
(* nowrshmsk *) on a struct / union variable now affects dynamic
bit slice assignments to members of the struct / union.
(* nowrshmsk *) can in some cases yield significant resource savings; the
combination of pipeline shifting and indexed writes is an example of this.
Constructs similar to the one below can benefit from (* nowrshmsk *), and
in addition it is no longer necessary to split out the shift assignments
on separate lines in order to avoid the error message "ERROR: incompatible
mix of lookahead and non-lookahead IDs in LHS expression."
    always_ff @(posedge clk) begin
        if (rotate) begin
            { v5, v4, v3, v2, v1, v0 } <= { v4, v3, v2, v1, v0, v5 };
            if (res) begin
                v0.bytes <= '0;
            end else if (w) begin
                v0.bytes[addr] <= data;
            end
        end
    end
			
			
This commit is contained in:
		
							parent
							
								
									cee3cb31b9
								
							
						
					
					
						commit
						ad437c178d
					
				
					 5 changed files with 33 additions and 8 deletions
				
			
		| 
						 | 
				
			
			@ -557,7 +557,7 @@ static int get_max_offset(AstNode *node)
 | 
			
		|||
	return node->range_left;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static AstNode *make_packed_struct(AstNode *template_node, std::string &name)
 | 
			
		||||
static AstNode *make_packed_struct(AstNode *template_node, std::string &name, decltype(AstNode::attributes) &attributes)
 | 
			
		||||
{
 | 
			
		||||
	// create a wire for the packed struct
 | 
			
		||||
	auto wnode = new AstNode(AST_WIRE);
 | 
			
		||||
| 
						 | 
				
			
			@ -565,6 +565,9 @@ static AstNode *make_packed_struct(AstNode *template_node, std::string &name)
 | 
			
		|||
	wnode->is_logic = true;
 | 
			
		||||
	wnode->range_valid = true;
 | 
			
		||||
	wnode->is_signed = template_node->is_signed;
 | 
			
		||||
	for (auto &pair : attributes) {
 | 
			
		||||
		wnode->attributes[pair.first] = pair.second->clone();
 | 
			
		||||
	}
 | 
			
		||||
	int offset = get_max_offset(template_node);
 | 
			
		||||
	auto range = make_range(offset, 0);
 | 
			
		||||
	wnode->children.push_back(range);
 | 
			
		||||
| 
						 | 
				
			
			@ -1368,7 +1371,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
 | 
			
		|||
			// 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);
 | 
			
		||||
				auto wnode = make_packed_struct(this, str, attributes);
 | 
			
		||||
				log_assert(current_ast_mod);
 | 
			
		||||
				current_ast_mod->children.push_back(wnode);
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -1792,7 +1795,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
 | 
			
		|||
 | 
			
		||||
			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);
 | 
			
		||||
				newNode = make_packed_struct(template_node, str, attributes);
 | 
			
		||||
				newNode->attributes[ID::wiretype] = mkconst_str(resolved_type_node->str);
 | 
			
		||||
				// add original input/output attribute to resolved wire
 | 
			
		||||
				newNode->is_input = this->is_input;
 | 
			
		||||
| 
						 | 
				
			
			@ -1857,7 +1860,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
 | 
			
		|||
 | 
			
		||||
			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);
 | 
			
		||||
				newNode = make_packed_struct(template_node, str, attributes);
 | 
			
		||||
				newNode->attributes[ID::wiretype] = mkconst_str(resolved_type_node->str);
 | 
			
		||||
				newNode->type = type;
 | 
			
		||||
				current_scope[str] = this;
 | 
			
		||||
| 
						 | 
				
			
			@ -2709,11 +2712,23 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
 | 
			
		|||
		int source_width = children[0]->id2ast->range_left - children[0]->id2ast->range_right + 1;
 | 
			
		||||
		int source_offset = children[0]->id2ast->range_right;
 | 
			
		||||
		int result_width = 1;
 | 
			
		||||
		int stride = 1;
 | 
			
		||||
		AST::AstNode *member_node = get_struct_member(children[0]);
 | 
			
		||||
		if (member_node) {
 | 
			
		||||
			// Clamp chunk to range of member within struct/union.
 | 
			
		||||
			log_assert(!source_offset && !children[0]->id2ast->range_swapped);
 | 
			
		||||
			source_width = member_node->range_left - member_node->range_right + 1;
 | 
			
		||||
 | 
			
		||||
			// When the (* nowrshmsk *) attribute is set, a CASE block is generated below
 | 
			
		||||
			// to select the indexed bit slice. When a multirange array is indexed, the
 | 
			
		||||
			// start of each possible slice is separated by the bit stride of the last
 | 
			
		||||
			// index dimension, and we can optimize the CASE block accordingly.
 | 
			
		||||
			// The dimension of the original array expression is saved in the 'integer' field.
 | 
			
		||||
			int dims = children[0]->integer;
 | 
			
		||||
			stride = source_width;
 | 
			
		||||
			for (int dim = 0; dim < dims; dim++) {
 | 
			
		||||
				stride /= get_struct_range_width(member_node, dim);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		AstNode *shift_expr = NULL;
 | 
			
		||||
| 
						 | 
				
			
			@ -2754,7 +2769,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
 | 
			
		|||
 | 
			
		||||
			did_something = true;
 | 
			
		||||
			newNode = new AstNode(AST_CASE, shift_expr);
 | 
			
		||||
			for (int i = 0; i < source_width; i++) {
 | 
			
		||||
			for (int i = 0; i < source_width; i += stride) {
 | 
			
		||||
				int start_bit = source_offset + i;
 | 
			
		||||
				int end_bit = std::min(start_bit+result_width,source_width) - 1;
 | 
			
		||||
				AstNode *cond = new AstNode(AST_COND, mkconst_int(start_bit, true));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1809,7 +1809,12 @@ enum_decl: enum_type enum_var_list ';'		{ delete $1; }
 | 
			
		|||
// struct or union
 | 
			
		||||
//////////////////
 | 
			
		||||
 | 
			
		||||
struct_decl: struct_type struct_var_list ';' 	{ delete astbuf2; }
 | 
			
		||||
struct_decl:
 | 
			
		||||
	attr struct_type {
 | 
			
		||||
		append_attr($2, $1);
 | 
			
		||||
	} struct_var_list ';' {
 | 
			
		||||
		delete astbuf2;
 | 
			
		||||
	}
 | 
			
		||||
	;
 | 
			
		||||
 | 
			
		||||
struct_type: struct_union { astbuf2 = $1; } struct_body { $$ = astbuf2; }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										1
									
								
								tests/svtypes/.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								tests/svtypes/.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -1,3 +1,4 @@
 | 
			
		|||
/*.log
 | 
			
		||||
/*.out
 | 
			
		||||
/run-test.mk
 | 
			
		||||
/temp
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ module range_shift_mask(
 | 
			
		|||
    input logic [2:0] addr_o,
 | 
			
		||||
    output logic [7:0] data_o
 | 
			
		||||
);
 | 
			
		||||
    // (* nowrshmsk = 0 *)
 | 
			
		||||
    (* nowrshmsk = 0 *)
 | 
			
		||||
    struct packed {
 | 
			
		||||
        logic [7:0] msb;
 | 
			
		||||
        logic [0:3][7:0] data;
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ module range_case(
 | 
			
		|||
    input logic [2:0] addr_o,
 | 
			
		||||
    output logic [7:0] data_o
 | 
			
		||||
);
 | 
			
		||||
    // (* nowrshmsk = 1 *)
 | 
			
		||||
    (* nowrshmsk = 1 *)
 | 
			
		||||
    struct packed {
 | 
			
		||||
        logic [7:0] msb;
 | 
			
		||||
        logic [0:3][7:0] data;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,8 @@
 | 
			
		|||
! mkdir -p temp
 | 
			
		||||
read_verilog -sv struct_dynamic_range.sv
 | 
			
		||||
write_rtlil temp/struct_dynamic_range.il
 | 
			
		||||
! grep -F -q ' cell $shift ' temp/struct_dynamic_range.il
 | 
			
		||||
! grep -F -q ' switch $mul' temp/struct_dynamic_range.il
 | 
			
		||||
prep -top top
 | 
			
		||||
flatten
 | 
			
		||||
sat -enable_undef -verify -prove-asserts
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue