aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Crozier <peter@crozier.com>2020-05-12 14:25:33 +0100
committerPeter Crozier <peter@crozier.com>2020-05-12 14:25:33 +0100
commitf482c9c0168a6857383e7d9360c8ca1df36ba2bc (patch)
tree366454dd200407cc54ec7bec0c12b145715e5c25
parent0b6b47ca670b9219bcb81ab7d3599267c2ef7571 (diff)
downloadyosys-f482c9c0168a6857383e7d9360c8ca1df36ba2bc.tar.gz
yosys-f482c9c0168a6857383e7d9360c8ca1df36ba2bc.tar.bz2
yosys-f482c9c0168a6857383e7d9360c8ca1df36ba2bc.zip
Generalise structs and add support for packed unions.
-rw-r--r--README.md2
-rw-r--r--frontends/ast/ast.cc1
-rw-r--r--frontends/ast/ast.h1
-rw-r--r--frontends/ast/genrtlil.cc1
-rw-r--r--frontends/ast/simplify.cc146
-rw-r--r--frontends/verilog/verilog_lexer.l6
-rw-r--r--frontends/verilog/verilog_parser.y50
-rw-r--r--tests/svtypes/union_simple.sv61
8 files changed, 209 insertions, 59 deletions
diff --git a/README.md b/README.md
index 3058220e7..770c62459 100644
--- a/README.md
+++ b/README.md
@@ -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.
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
index 2c16de0a4..03fd272da 100644
--- a/frontends/ast/ast.cc
+++ b/frontends/ast/ast.cc
@@ -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:
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
index 29bd1b84d..6d556fae2 100644
--- a/frontends/ast/ast.h
+++ b/frontends/ast/ast.h
@@ -158,6 +158,7 @@ namespace AST
AST_WIRETYPE,
AST_TYPEDEF,
AST_STRUCT,
+ AST_UNION,
AST_STRUCT_ITEM
};
diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc
index a57333314..83f34c9e1 100644
--- a/frontends/ast/genrtlil.cc
+++ b/frontends/ast/genrtlil.cc
@@ -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'
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index ce435c26b..e8ac4b2f2 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -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;
diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l
index 19e54816d..6217b5728 100644
--- a/frontends/verilog/verilog_lexer.l
+++ b/frontends/verilog/verilog_lexer.l
@@ -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);
diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y
index dbd18bb53..fff02f33a 100644
--- a/frontends/verilog/verilog_parser.y
+++ b/frontends/verilog/verilog_parser.y
@@ -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);
}
;
diff --git a/tests/svtypes/union_simple.sv b/tests/svtypes/union_simple.sv
new file mode 100644
index 000000000..fc23fe6e8
--- /dev/null
+++ b/tests/svtypes/union_simple.sv
@@ -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