mirror of https://github.com/YosysHQ/yosys.git
Implement SV structs.
This commit is contained in:
parent
aafaeb66df
commit
0b6b47ca67
|
@ -556,6 +556,8 @@ from SystemVerilog:
|
||||||
- enums are supported (including inside packages)
|
- enums are supported (including inside packages)
|
||||||
- but are currently not strongly typed
|
- but are currently not strongly typed
|
||||||
|
|
||||||
|
- structs are supported
|
||||||
|
|
||||||
- SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether
|
- SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether
|
||||||
ports are inputs or outputs are supported.
|
ports are inputs or outputs are supported.
|
||||||
|
|
||||||
|
|
|
@ -171,6 +171,8 @@ std::string AST::type2str(AstNodeType type)
|
||||||
X(AST_PACKAGE)
|
X(AST_PACKAGE)
|
||||||
X(AST_WIRETYPE)
|
X(AST_WIRETYPE)
|
||||||
X(AST_TYPEDEF)
|
X(AST_TYPEDEF)
|
||||||
|
X(AST_STRUCT)
|
||||||
|
X(AST_STRUCT_ITEM)
|
||||||
#undef X
|
#undef X
|
||||||
default:
|
default:
|
||||||
log_abort();
|
log_abort();
|
||||||
|
|
|
@ -143,7 +143,7 @@ namespace AST
|
||||||
AST_GENCASE,
|
AST_GENCASE,
|
||||||
AST_GENBLOCK,
|
AST_GENBLOCK,
|
||||||
AST_TECALL,
|
AST_TECALL,
|
||||||
|
|
||||||
AST_POSEDGE,
|
AST_POSEDGE,
|
||||||
AST_NEGEDGE,
|
AST_NEGEDGE,
|
||||||
AST_EDGE,
|
AST_EDGE,
|
||||||
|
@ -156,7 +156,9 @@ namespace AST
|
||||||
AST_PACKAGE,
|
AST_PACKAGE,
|
||||||
|
|
||||||
AST_WIRETYPE,
|
AST_WIRETYPE,
|
||||||
AST_TYPEDEF
|
AST_TYPEDEF,
|
||||||
|
AST_STRUCT,
|
||||||
|
AST_STRUCT_ITEM
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AstSrcLocType {
|
struct AstSrcLocType {
|
||||||
|
@ -306,6 +308,7 @@ namespace AST
|
||||||
|
|
||||||
// helpers for enum
|
// helpers for enum
|
||||||
void allocateDefaultEnumValues();
|
void allocateDefaultEnumValues();
|
||||||
|
void annotateTypedEnums(AstNode *template_node);
|
||||||
};
|
};
|
||||||
|
|
||||||
// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code
|
// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code
|
||||||
|
|
|
@ -991,6 +991,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
||||||
case AST_MODPORT:
|
case AST_MODPORT:
|
||||||
case AST_MODPORTMEMBER:
|
case AST_MODPORTMEMBER:
|
||||||
case AST_TYPEDEF:
|
case AST_TYPEDEF:
|
||||||
|
case AST_STRUCT:
|
||||||
break;
|
break;
|
||||||
case AST_INTERFACEPORT: {
|
case AST_INTERFACEPORT: {
|
||||||
// If a port in a module with unknown type is found, mark it with the attribute 'is_interface'
|
// If a port in a module with unknown type is found, mark it with the attribute 'is_interface'
|
||||||
|
|
|
@ -168,6 +168,110 @@ std::string AstNode::process_format_str(const std::string &sformat, int next_arg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AstNode::annotateTypedEnums(AstNode *template_node)
|
||||||
|
{
|
||||||
|
//check if enum
|
||||||
|
if (template_node->attributes.count(ID::enum_type)) {
|
||||||
|
//get reference to enum node:
|
||||||
|
std::string enum_type = template_node->attributes[ID::enum_type]->str.c_str();
|
||||||
|
// log("enum_type=%s (count=%lu)\n", enum_type.c_str(), current_scope.count(enum_type));
|
||||||
|
// log("current scope:\n");
|
||||||
|
// for (auto &it : current_scope)
|
||||||
|
// log(" %s\n", it.first.c_str());
|
||||||
|
log_assert(current_scope.count(enum_type) == 1);
|
||||||
|
AstNode *enum_node = current_scope.at(enum_type);
|
||||||
|
log_assert(enum_node->type == AST_ENUM);
|
||||||
|
//get width from 1st enum item:
|
||||||
|
log_assert(enum_node->children.size() >= 1);
|
||||||
|
AstNode *enum_item0 = enum_node->children[0];
|
||||||
|
log_assert(enum_item0->type == AST_ENUM_ITEM);
|
||||||
|
int width;
|
||||||
|
if (!enum_item0->range_valid)
|
||||||
|
width = 1;
|
||||||
|
else if (enum_item0->range_swapped)
|
||||||
|
width = enum_item0->range_right - enum_item0->range_left + 1;
|
||||||
|
else
|
||||||
|
width = enum_item0->range_left - enum_item0->range_right + 1;
|
||||||
|
log_assert(width > 0);
|
||||||
|
//add declared enum items:
|
||||||
|
for (auto enum_item : enum_node->children){
|
||||||
|
log_assert(enum_item->type == AST_ENUM_ITEM);
|
||||||
|
//get is_signed
|
||||||
|
bool is_signed;
|
||||||
|
if (enum_item->children.size() == 1){
|
||||||
|
is_signed = false;
|
||||||
|
} else if (enum_item->children.size() == 2){
|
||||||
|
log_assert(enum_item->children[1]->type == AST_RANGE);
|
||||||
|
is_signed = enum_item->children[1]->is_signed;
|
||||||
|
} else {
|
||||||
|
log_error("enum_item children size==%lu, expected 1 or 2 for %s (%s)\n",
|
||||||
|
enum_item->children.size(),
|
||||||
|
enum_item->str.c_str(), enum_node->str.c_str()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//start building attribute string
|
||||||
|
std::string enum_item_str = "\\enum_value_";
|
||||||
|
//get enum item value
|
||||||
|
if(enum_item->children[0]->type != AST_CONSTANT){
|
||||||
|
log_error("expected const, got %s for %s (%s)\n",
|
||||||
|
type2str(enum_item->children[0]->type).c_str(),
|
||||||
|
enum_item->str.c_str(), enum_node->str.c_str()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
RTLIL::Const val = enum_item->children[0]->bitsAsConst(width, is_signed);
|
||||||
|
enum_item_str.append(val.as_string());
|
||||||
|
//set attribute for available val to enum item name mappings
|
||||||
|
attributes[enum_item_str.c_str()] = mkconst_str(enum_item->str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool name_has_dot(const std::string &name, std::string &struct_name)
|
||||||
|
{
|
||||||
|
// check if plausible struct member name \sss.mmm
|
||||||
|
std::string::size_type pos;
|
||||||
|
if (name.substr(0, 1) == "\\" && (pos = name.find('.', 0)) != std::string::npos) {
|
||||||
|
struct_name = name.substr(0, pos);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AstNode *make_range(int left, int right, bool is_signed = false)
|
||||||
|
{
|
||||||
|
// generate a pre-validated range node for a fixed signal range.
|
||||||
|
auto range = new AstNode(AST_RANGE);
|
||||||
|
range->range_left = left;
|
||||||
|
range->range_right = right;
|
||||||
|
range->range_valid = true;
|
||||||
|
range->children.push_back(AstNode::mkconst_int(left, true));
|
||||||
|
range->children.push_back(AstNode::mkconst_int(right, true));
|
||||||
|
range->is_signed = is_signed;
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AstNode *make_packed_struct(AstNode *template_node, std::string &name)
|
||||||
|
{
|
||||||
|
// create a wire for the packed struct
|
||||||
|
auto wnode = new AstNode(AST_WIRE);
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
return wnode;
|
||||||
|
}
|
||||||
|
|
||||||
// convert the AST into a simpler AST that has all parameters substituted by their
|
// convert the AST into a simpler AST that has all parameters substituted by their
|
||||||
// values, unrolled for-loops, expanded generate blocks, etc. when this function
|
// values, unrolled for-loops, expanded generate blocks, etc. when this function
|
||||||
// is done with an AST it can be converted into RTLIL using genRTLIL().
|
// is done with an AST it can be converted into RTLIL using genRTLIL().
|
||||||
|
@ -567,6 +671,53 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case AST_STRUCT:
|
||||||
|
//log("STRUCT %d %d %d\n", stage, basic_prep, in_param);
|
||||||
|
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
|
||||||
|
auto wnode = make_packed_struct(this, str);
|
||||||
|
current_ast_mod->children.push_back(wnode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AST_STRUCT_ITEM:
|
||||||
|
break;
|
||||||
|
|
||||||
case AST_ENUM:
|
case AST_ENUM:
|
||||||
//log("\nENUM %s: %d child %d\n", str.c_str(), basic_prep, children[0]->basic_prep);
|
//log("\nENUM %s: %d child %d\n", str.c_str(), basic_prep, children[0]->basic_prep);
|
||||||
if (!basic_prep) {
|
if (!basic_prep) {
|
||||||
|
@ -884,10 +1035,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
||||||
// resolve typedefs
|
// resolve typedefs
|
||||||
if (type == AST_TYPEDEF) {
|
if (type == AST_TYPEDEF) {
|
||||||
log_assert(children.size() == 1);
|
log_assert(children.size() == 1);
|
||||||
log_assert(children[0]->type == AST_WIRE || children[0]->type == AST_MEMORY);
|
auto type_node = children[0];
|
||||||
while(children[0]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param))
|
log_assert(type_node->type == AST_WIRE || type_node->type == AST_MEMORY || type_node->type == AST_STRUCT);
|
||||||
|
while (type_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {
|
||||||
did_something = true;
|
did_something = true;
|
||||||
log_assert(!children[0]->is_custom_type);
|
}
|
||||||
|
log_assert(!type_node->is_custom_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve types of wires
|
// resolve types of wires
|
||||||
|
@ -895,100 +1048,57 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
||||||
if (is_custom_type) {
|
if (is_custom_type) {
|
||||||
log_assert(children.size() >= 1);
|
log_assert(children.size() >= 1);
|
||||||
log_assert(children[0]->type == AST_WIRETYPE);
|
log_assert(children[0]->type == AST_WIRETYPE);
|
||||||
if (!current_scope.count(children[0]->str))
|
auto type_name = children[0]->str;
|
||||||
log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", children[0]->str.c_str());
|
if (!current_scope.count(type_name)) {
|
||||||
AstNode *resolved_type = current_scope.at(children[0]->str);
|
log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", type_name.c_str());
|
||||||
if (resolved_type->type != AST_TYPEDEF)
|
}
|
||||||
log_file_error(filename, location.first_line, "`%s' does not name a type\n", children[0]->str.c_str());
|
AstNode *resolved_type_node = current_scope.at(type_name);
|
||||||
log_assert(resolved_type->children.size() == 1);
|
if (resolved_type_node->type != AST_TYPEDEF)
|
||||||
AstNode *templ = resolved_type->children[0];
|
log_file_error(filename, location.first_line, "`%s' does not name a type\n", type_name.c_str());
|
||||||
|
log_assert(resolved_type_node->children.size() == 1);
|
||||||
|
AstNode *template_node = resolved_type_node->children[0];
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
// replace with wire representing the packed structure
|
||||||
|
newNode = make_packed_struct(template_node, str);
|
||||||
|
current_scope[str] = this;
|
||||||
|
goto apply_newNode;
|
||||||
|
}
|
||||||
|
|
||||||
// Remove type reference
|
// Remove type reference
|
||||||
delete children[0];
|
delete children[0];
|
||||||
children.erase(children.begin());
|
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)
|
if (type == AST_WIRE)
|
||||||
type = templ->type;
|
type = template_node->type;
|
||||||
is_reg = templ->is_reg;
|
is_reg = template_node->is_reg;
|
||||||
is_logic = templ->is_logic;
|
is_logic = template_node->is_logic;
|
||||||
is_signed = templ->is_signed;
|
is_signed = template_node->is_signed;
|
||||||
is_string = templ->is_string;
|
is_string = template_node->is_string;
|
||||||
is_custom_type = templ->is_custom_type;
|
is_custom_type = template_node->is_custom_type;
|
||||||
|
|
||||||
range_valid = templ->range_valid;
|
range_valid = template_node->range_valid;
|
||||||
range_swapped = templ->range_swapped;
|
range_swapped = template_node->range_swapped;
|
||||||
range_left = templ->range_left;
|
range_left = template_node->range_left;
|
||||||
range_right = templ->range_right;
|
range_right = template_node->range_right;
|
||||||
attributes[ID::wiretype] = mkconst_str(resolved_type->str);
|
|
||||||
//check if enum
|
attributes[ID::wiretype] = mkconst_str(resolved_type_node->str);
|
||||||
if (templ->attributes.count(ID::enum_type)){
|
|
||||||
//get reference to enum node:
|
// if an enum then add attributes to support simulator tracing
|
||||||
const std::string &enum_type = templ->attributes[ID::enum_type]->str;
|
annotateTypedEnums(template_node);
|
||||||
// log("enum_type=%s (count=%lu)\n", enum_type.c_str(), current_scope.count(enum_type));
|
|
||||||
// log("current scope:\n");
|
|
||||||
// for (auto &it : current_scope)
|
|
||||||
// log(" %s\n", it.first.c_str());
|
|
||||||
log_assert(current_scope.count(enum_type) == 1);
|
|
||||||
AstNode *enum_node = current_scope.at(enum_type);
|
|
||||||
log_assert(enum_node->type == AST_ENUM);
|
|
||||||
//get width from 1st enum item:
|
|
||||||
log_assert(enum_node->children.size() >= 1);
|
|
||||||
AstNode *enum_item0 = enum_node->children[0];
|
|
||||||
log_assert(enum_item0->type == AST_ENUM_ITEM);
|
|
||||||
int width;
|
|
||||||
if (!enum_item0->range_valid)
|
|
||||||
width = 1;
|
|
||||||
else if (enum_item0->range_swapped)
|
|
||||||
width = enum_item0->range_right - enum_item0->range_left + 1;
|
|
||||||
else
|
|
||||||
width = enum_item0->range_left - enum_item0->range_right + 1;
|
|
||||||
log_assert(width > 0);
|
|
||||||
//add declared enum items:
|
|
||||||
for (auto enum_item : enum_node->children){
|
|
||||||
log_assert(enum_item->type == AST_ENUM_ITEM);
|
|
||||||
//get is_signed
|
|
||||||
bool is_signed;
|
|
||||||
if (enum_item->children.size() == 1){
|
|
||||||
is_signed = false;
|
|
||||||
} else if (enum_item->children.size() == 2){
|
|
||||||
log_assert(enum_item->children[1]->type == AST_RANGE);
|
|
||||||
is_signed = enum_item->children[1]->is_signed;
|
|
||||||
} else {
|
|
||||||
log_error("enum_item children size==%lu, expected 1 or 2 for %s (%s)\n",
|
|
||||||
enum_item->children.size(),
|
|
||||||
enum_item->str.c_str(), enum_node->str.c_str()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
//start building attribute string
|
|
||||||
std::string enum_item_str = "\\enum_value_";
|
|
||||||
//get enum item value
|
|
||||||
if(enum_item->children[0]->type != AST_CONSTANT){
|
|
||||||
log_error("expected const, got %s for %s (%s)\n",
|
|
||||||
type2str(enum_item->children[0]->type).c_str(),
|
|
||||||
enum_item->str.c_str(), enum_node->str.c_str()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
RTLIL::Const val = enum_item->children[0]->bitsAsConst(width, is_signed);
|
|
||||||
enum_item_str.append(val.as_string());
|
|
||||||
//set attribute for available val to enum item name mappings
|
|
||||||
attributes[enum_item_str] = mkconst_str(enum_item->str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert clones children from template at beginning
|
// Insert clones children from template at beginning
|
||||||
for (int i = 0; i < GetSize(templ->children); i++)
|
for (int i = 0; i < GetSize(template_node->children); i++)
|
||||||
children.insert(children.begin() + i, templ->children[i]->clone());
|
children.insert(children.begin() + i, template_node->children[i]->clone());
|
||||||
|
|
||||||
if (type == AST_MEMORY && GetSize(children) == 1) {
|
if (type == AST_MEMORY && GetSize(children) == 1) {
|
||||||
// Single-bit memories must have [0:0] range
|
// Single-bit memories must have [0:0] range
|
||||||
AstNode *rng = new AstNode(AST_RANGE);
|
AstNode *rng = make_range(0, 0);
|
||||||
rng->children.push_back(AstNode::mkconst_int(0, true));
|
|
||||||
rng->children.push_back(AstNode::mkconst_int(0, true));
|
|
||||||
children.insert(children.begin(), rng);
|
children.insert(children.begin(), rng);
|
||||||
}
|
}
|
||||||
|
|
||||||
did_something = true;
|
did_something = true;
|
||||||
}
|
}
|
||||||
log_assert(!is_custom_type);
|
log_assert(!is_custom_type);
|
||||||
|
@ -1001,29 +1111,29 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
||||||
log_assert(children[1]->type == AST_WIRETYPE);
|
log_assert(children[1]->type == AST_WIRETYPE);
|
||||||
if (!current_scope.count(children[1]->str))
|
if (!current_scope.count(children[1]->str))
|
||||||
log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", children[1]->str.c_str());
|
log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", children[1]->str.c_str());
|
||||||
AstNode *resolved_type = current_scope.at(children[1]->str);
|
AstNode *resolved_type_node = current_scope.at(children[1]->str);
|
||||||
if (resolved_type->type != AST_TYPEDEF)
|
if (resolved_type_node->type != AST_TYPEDEF)
|
||||||
log_file_error(filename, location.first_line, "`%s' does not name a type\n", children[1]->str.c_str());
|
log_file_error(filename, location.first_line, "`%s' does not name a type\n", children[1]->str.c_str());
|
||||||
log_assert(resolved_type->children.size() == 1);
|
log_assert(resolved_type_node->children.size() == 1);
|
||||||
AstNode *templ = resolved_type->children[0];
|
AstNode *template_node = resolved_type_node->children[0];
|
||||||
delete children[1];
|
delete children[1];
|
||||||
children.pop_back();
|
children.pop_back();
|
||||||
|
|
||||||
// Ensure typedef itself is fully simplified
|
// Ensure typedef itself is fully simplified
|
||||||
while(templ->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {};
|
while(template_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {};
|
||||||
|
|
||||||
if (templ->type == AST_MEMORY)
|
if (template_node->type == AST_MEMORY)
|
||||||
log_file_error(filename, location.first_line, "unpacked array type `%s' cannot be used for a parameter\n", children[1]->str.c_str());
|
log_file_error(filename, location.first_line, "unpacked array type `%s' cannot be used for a parameter\n", children[1]->str.c_str());
|
||||||
is_signed = templ->is_signed;
|
is_signed = template_node->is_signed;
|
||||||
is_string = templ->is_string;
|
is_string = template_node->is_string;
|
||||||
is_custom_type = templ->is_custom_type;
|
is_custom_type = template_node->is_custom_type;
|
||||||
|
|
||||||
range_valid = templ->range_valid;
|
range_valid = template_node->range_valid;
|
||||||
range_swapped = templ->range_swapped;
|
range_swapped = template_node->range_swapped;
|
||||||
range_left = templ->range_left;
|
range_left = template_node->range_left;
|
||||||
range_right = templ->range_right;
|
range_right = template_node->range_right;
|
||||||
attributes[ID::wiretype] = mkconst_str(resolved_type->str);
|
attributes[ID::wiretype] = mkconst_str(resolved_type_node->str);
|
||||||
for (auto template_child : templ->children)
|
for (auto template_child : template_node->children)
|
||||||
children.push_back(template_child->clone());
|
children.push_back(template_child->clone());
|
||||||
did_something = true;
|
did_something = true;
|
||||||
}
|
}
|
||||||
|
@ -1198,7 +1308,29 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
||||||
}
|
}
|
||||||
|
|
||||||
// annotate identifiers using scope resolution and create auto-wires as needed
|
// annotate identifiers using scope resolution and create auto-wires as needed
|
||||||
|
if (type == AST_IDENTIFIER && !basic_prep) {
|
||||||
|
// check if a plausible struct member sss.mmmm
|
||||||
|
std::string sname;
|
||||||
|
if (name_has_dot(str, sname) && children.size() == 0) {
|
||||||
|
//dumpScope();
|
||||||
|
if (current_scope.count(str) > 0) {
|
||||||
|
auto item_node = current_scope[str];
|
||||||
|
if (item_node->type == AST_STRUCT_ITEM) {
|
||||||
|
//log("found struct item %s\n", item_node->str.c_str());
|
||||||
|
// structure member, rewrite this node to reference the packed struct wire
|
||||||
|
auto range = make_range(item_node->range_left, item_node->range_right);
|
||||||
|
newNode = new AstNode(AST_IDENTIFIER, range);
|
||||||
|
newNode->str = sname;
|
||||||
|
//newNode->dumpAst(NULL, "* ");
|
||||||
|
newNode->basic_prep = true;
|
||||||
|
goto apply_newNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (type == AST_IDENTIFIER) {
|
if (type == AST_IDENTIFIER) {
|
||||||
|
//log("annotate ID %s, stage=%d cf=%d, ip=%d\n", str.c_str(), stage, const_fold, in_param);
|
||||||
|
//dumpScope();
|
||||||
if (current_scope.count(str) == 0) {
|
if (current_scope.count(str) == 0) {
|
||||||
AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod;
|
AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod;
|
||||||
for (auto node : current_scope_ast->children) {
|
for (auto node : current_scope_ast->children) {
|
||||||
|
|
|
@ -262,7 +262,9 @@ static bool isUserType(std::string &s)
|
||||||
"final" { SV_KEYWORD(TOK_FINAL); }
|
"final" { SV_KEYWORD(TOK_FINAL); }
|
||||||
"logic" { SV_KEYWORD(TOK_LOGIC); }
|
"logic" { SV_KEYWORD(TOK_LOGIC); }
|
||||||
"var" { SV_KEYWORD(TOK_VAR); }
|
"var" { SV_KEYWORD(TOK_VAR); }
|
||||||
"bit" { SV_KEYWORD(TOK_REG); }
|
"bit" { SV_KEYWORD(TOK_LOGIC); }
|
||||||
|
"int" { SV_KEYWORD(TOK_INT); }
|
||||||
|
"byte" { SV_KEYWORD(TOK_BYTE); }
|
||||||
|
|
||||||
"eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); }
|
"eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); }
|
||||||
"s_eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); }
|
"s_eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); }
|
||||||
|
@ -276,11 +278,14 @@ static bool isUserType(std::string &s)
|
||||||
"reg" { return TOK_REG; }
|
"reg" { return TOK_REG; }
|
||||||
"integer" { return TOK_INTEGER; }
|
"integer" { return TOK_INTEGER; }
|
||||||
"signed" { return TOK_SIGNED; }
|
"signed" { return TOK_SIGNED; }
|
||||||
|
"unsigned" { SV_KEYWORD(TOK_UNSIGNED); }
|
||||||
"genvar" { return TOK_GENVAR; }
|
"genvar" { return TOK_GENVAR; }
|
||||||
"real" { return TOK_REAL; }
|
"real" { return TOK_REAL; }
|
||||||
|
|
||||||
"enum" { SV_KEYWORD(TOK_ENUM); }
|
"enum" { SV_KEYWORD(TOK_ENUM); }
|
||||||
"typedef" { SV_KEYWORD(TOK_TYPEDEF); }
|
"typedef" { SV_KEYWORD(TOK_TYPEDEF); }
|
||||||
|
"struct" { SV_KEYWORD(TOK_STRUCT); }
|
||||||
|
"packed" { SV_KEYWORD(TOK_PACKED); }
|
||||||
|
|
||||||
[0-9][0-9_]* {
|
[0-9][0-9_]* {
|
||||||
yylval->string = new std::string(yytext);
|
yylval->string = new std::string(yytext);
|
||||||
|
|
|
@ -161,6 +161,23 @@ static bool isInLocalScope(const std::string *name)
|
||||||
return (user_types->count(*name) > 0);
|
return (user_types->count(*name) > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AstNode *getTypeDefinitionNode(std::string type_name)
|
||||||
|
{
|
||||||
|
// return the definition nodes from the typedef statement
|
||||||
|
auto user_types = user_type_stack.back();
|
||||||
|
log_assert(user_types->count(type_name) > 0);
|
||||||
|
auto typedef_node = (*user_types)[type_name];
|
||||||
|
log_assert(typedef_node->type == AST_TYPEDEF);
|
||||||
|
return typedef_node->children[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
static AstNode *copyTypeDefinition(std::string type_name)
|
||||||
|
{
|
||||||
|
// return a copy of the template from a typedef definition
|
||||||
|
auto typedef_node = getTypeDefinitionNode(type_name);
|
||||||
|
return typedef_node->clone();
|
||||||
|
}
|
||||||
|
|
||||||
static AstNode *makeRange(int msb = 31, int lsb = 0, bool isSigned = true)
|
static AstNode *makeRange(int msb = 31, int lsb = 0, bool isSigned = true)
|
||||||
{
|
{
|
||||||
auto range = new AstNode(AST_RANGE);
|
auto range = new AstNode(AST_RANGE);
|
||||||
|
@ -175,6 +192,35 @@ static void addRange(AstNode *parent, int msb = 31, int lsb = 0, bool isSigned =
|
||||||
auto range = makeRange(msb, lsb, isSigned);
|
auto range = makeRange(msb, lsb, isSigned);
|
||||||
parent->children.push_back(range);
|
parent->children.push_back(range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AstNode *checkRange(AstNode *type_node, AstNode *range_node)
|
||||||
|
{
|
||||||
|
if (type_node->range_left >= 0 && type_node->range_right >= 0) {
|
||||||
|
// type already restricts the range
|
||||||
|
if (range_node) {
|
||||||
|
frontend_verilog_yyerror("integer/genvar types cannot have packed dimensions.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
range_node = makeRange(type_node->range_left, type_node->range_right, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (range_node && range_node->children.size() != 2) {
|
||||||
|
frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]");
|
||||||
|
}
|
||||||
|
return range_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode)
|
||||||
|
{
|
||||||
|
node->type = AST_MEMORY;
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
node->children.push_back(rangeNode);
|
||||||
|
}
|
||||||
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%define api.prefix {frontend_verilog_yy}
|
%define api.prefix {frontend_verilog_yy}
|
||||||
|
@ -223,12 +269,13 @@ static void addRange(AstNode *parent, int msb = 31, int lsb = 0, bool isSigned =
|
||||||
%token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_PROPERTY TOK_ENUM TOK_TYPEDEF
|
%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_RAND TOK_CONST TOK_CHECKER TOK_ENDCHECKER TOK_EVENTUALLY
|
||||||
%token TOK_INCREMENT TOK_DECREMENT TOK_UNIQUE TOK_PRIORITY
|
%token TOK_INCREMENT TOK_DECREMENT TOK_UNIQUE TOK_PRIORITY
|
||||||
|
%token TOK_STRUCT TOK_PACKED TOK_UNSIGNED TOK_INT TOK_BYTE
|
||||||
|
|
||||||
%type <ast> range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int
|
%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 <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 hierarchical_type_id integral_number
|
%type <string> opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id integral_number
|
||||||
%type <string> type_name
|
%type <string> type_name
|
||||||
%type <ast> opt_enum_init
|
%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 <boolean> opt_signed opt_property unique_case_attr always_comb_or_latch always_or_always_ff
|
||||||
%type <al> attr case_attr
|
%type <al> attr case_attr
|
||||||
|
|
||||||
|
@ -281,6 +328,7 @@ design:
|
||||||
param_decl design |
|
param_decl design |
|
||||||
localparam_decl design |
|
localparam_decl design |
|
||||||
typedef_decl design |
|
typedef_decl design |
|
||||||
|
struct_decl design |
|
||||||
package design |
|
package design |
|
||||||
interface design |
|
interface design |
|
||||||
/* empty */;
|
/* empty */;
|
||||||
|
@ -520,9 +568,11 @@ package_body:
|
||||||
;
|
;
|
||||||
|
|
||||||
package_body_stmt:
|
package_body_stmt:
|
||||||
typedef_decl |
|
typedef_decl
|
||||||
localparam_decl |
|
| struct_decl
|
||||||
param_decl;
|
| localparam_decl
|
||||||
|
| param_decl
|
||||||
|
;
|
||||||
|
|
||||||
interface:
|
interface:
|
||||||
TOK_INTERFACE {
|
TOK_INTERFACE {
|
||||||
|
@ -551,7 +601,7 @@ interface_body:
|
||||||
interface_body interface_body_stmt |;
|
interface_body interface_body_stmt |;
|
||||||
|
|
||||||
interface_body_stmt:
|
interface_body_stmt:
|
||||||
param_decl | localparam_decl | typedef_decl | defparam_decl | wire_decl | always_stmt | assign_stmt |
|
param_decl | localparam_decl | typedef_decl | struct_decl | defparam_decl | wire_decl | always_stmt | assign_stmt |
|
||||||
modport_stmt;
|
modport_stmt;
|
||||||
|
|
||||||
non_opt_delay:
|
non_opt_delay:
|
||||||
|
@ -582,6 +632,7 @@ wire_type_token_list:
|
||||||
astbuf3->is_custom_type = true;
|
astbuf3->is_custom_type = true;
|
||||||
astbuf3->children.push_back(new AstNode(AST_WIRETYPE));
|
astbuf3->children.push_back(new AstNode(AST_WIRETYPE));
|
||||||
astbuf3->children.back()->str = *$1;
|
astbuf3->children.back()->str = *$1;
|
||||||
|
delete $1;
|
||||||
};
|
};
|
||||||
|
|
||||||
wire_type_token_io:
|
wire_type_token_io:
|
||||||
|
@ -682,15 +733,9 @@ range_or_multirange:
|
||||||
non_opt_multirange { $$ = $1; };
|
non_opt_multirange { $$ = $1; };
|
||||||
|
|
||||||
range_or_signed_int:
|
range_or_signed_int:
|
||||||
range {
|
range { $$ = $1; }
|
||||||
$$ = $1;
|
| TOK_INTEGER { $$ = makeRange(); }
|
||||||
} |
|
;
|
||||||
TOK_INTEGER {
|
|
||||||
$$ = new AstNode(AST_RANGE);
|
|
||||||
$$->children.push_back(AstNode::mkconst_int(31, true));
|
|
||||||
$$->children.push_back(AstNode::mkconst_int(0, true));
|
|
||||||
$$->is_signed = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
module_body:
|
module_body:
|
||||||
module_body module_body_stmt |
|
module_body module_body_stmt |
|
||||||
|
@ -700,7 +745,7 @@ module_body:
|
||||||
|
|
||||||
module_body_stmt:
|
module_body_stmt:
|
||||||
task_func_decl | specify_block | param_decl | localparam_decl | typedef_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 |
|
||||||
enum_decl |
|
enum_decl | struct_decl |
|
||||||
always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block;
|
always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block;
|
||||||
|
|
||||||
checker_decl:
|
checker_decl:
|
||||||
|
@ -841,18 +886,7 @@ task_func_port:
|
||||||
}
|
}
|
||||||
albuf = $1;
|
albuf = $1;
|
||||||
astbuf1 = $2;
|
astbuf1 = $2;
|
||||||
astbuf2 = $3;
|
astbuf2 = checkRange(astbuf1, $3);
|
||||||
if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) {
|
|
||||||
if (astbuf2) {
|
|
||||||
frontend_verilog_yyerror("integer/genvar types cannot have packed dimensions (task/function arguments)");
|
|
||||||
} 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("task/function argument range must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]");
|
|
||||||
} wire_name | wire_name;
|
} wire_name | wire_name;
|
||||||
|
|
||||||
task_func_body:
|
task_func_body:
|
||||||
|
@ -1375,6 +1409,10 @@ single_defparam_decl:
|
||||||
ast_stack.back()->children.push_back(node);
|
ast_stack.back()->children.push_back(node);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/////////
|
||||||
|
// enum
|
||||||
|
/////////
|
||||||
|
|
||||||
enum_type: TOK_ENUM {
|
enum_type: TOK_ENUM {
|
||||||
static int enum_count;
|
static int enum_count;
|
||||||
// create parent node for the enum
|
// create parent node for the enum
|
||||||
|
@ -1385,31 +1423,39 @@ enum_type: TOK_ENUM {
|
||||||
// create the template for the names
|
// create the template for the names
|
||||||
astbuf1 = new AstNode(AST_ENUM_ITEM);
|
astbuf1 = new AstNode(AST_ENUM_ITEM);
|
||||||
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
|
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
|
||||||
} param_signed enum_base_type '{' enum_name_list '}' { // create template for the enum vars
|
} enum_base_type '{' enum_name_list '}' { // create template for the enum vars
|
||||||
auto tnode = astbuf1->clone();
|
auto tnode = astbuf1->clone();
|
||||||
delete astbuf1;
|
delete astbuf1;
|
||||||
astbuf1 = tnode;
|
astbuf1 = tnode;
|
||||||
tnode->type = AST_WIRE;
|
tnode->type = AST_WIRE;
|
||||||
tnode->attributes[ID::enum_type] = AstNode::mkconst_str(astbuf2->str);
|
tnode->attributes[ID::enum_type] = AstNode::mkconst_str(astbuf2->str);
|
||||||
// drop constant but keep any range
|
// drop constant but keep any range
|
||||||
delete tnode->children[0];
|
delete tnode->children[0];
|
||||||
tnode->children.erase(tnode->children.begin()); }
|
tnode->children.erase(tnode->children.begin());
|
||||||
|
$$ = astbuf1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
enum_base_type: int_vec param_range
|
enum_base_type: type_atom type_signing
|
||||||
| int_atom
|
| type_vec type_signing range { if ($3) astbuf1->children.push_back($3); }
|
||||||
| /* nothing */ {astbuf1->is_reg = true; addRange(astbuf1); }
|
| /* nothing */ { astbuf1->is_reg = true; addRange(astbuf1); }
|
||||||
;
|
;
|
||||||
|
|
||||||
int_atom: TOK_INTEGER {astbuf1->is_reg=true; addRange(astbuf1); } // probably should do byte, range [7:0] here
|
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_BYTE { astbuf1->is_reg = true; addRange(astbuf1, 7, 0); } // 2-state signed
|
||||||
;
|
;
|
||||||
|
|
||||||
int_vec: TOK_REG {astbuf1->is_reg = true;}
|
type_vec: TOK_REG { astbuf1->is_reg = true; } // unsigned
|
||||||
| TOK_LOGIC {astbuf1->is_logic = true;}
|
| TOK_LOGIC { astbuf1->is_logic = true; } // unsigned
|
||||||
;
|
;
|
||||||
|
|
||||||
enum_name_list:
|
type_signing:
|
||||||
enum_name_decl
|
TOK_SIGNED { astbuf1->is_signed = true; }
|
||||||
|
| TOK_UNSIGNED { astbuf1->is_signed = false; }
|
||||||
|
| // optional
|
||||||
|
;
|
||||||
|
|
||||||
|
enum_name_list: enum_name_decl
|
||||||
| enum_name_list ',' enum_name_decl
|
| enum_name_list ',' enum_name_decl
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -1448,28 +1494,90 @@ enum_var: TOK_ID {
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
enum_decl: enum_type enum_var_list ';' {
|
enum_decl: enum_type enum_var_list ';' { delete $1; }
|
||||||
//enum_type creates astbuf1 for use by typedef only
|
|
||||||
delete astbuf1;
|
|
||||||
}
|
|
||||||
;
|
;
|
||||||
|
|
||||||
|
/////////
|
||||||
|
// struct
|
||||||
|
/////////
|
||||||
|
|
||||||
|
struct_decl: struct_type struct_var_list ';' { delete astbuf2; }
|
||||||
|
;
|
||||||
|
|
||||||
|
struct_type: TOK_STRUCT { astbuf2 = new AstNode(AST_STRUCT); } opt_packed '{' struct_member_list '}' { $$ = astbuf2; }
|
||||||
|
;
|
||||||
|
|
||||||
|
opt_packed: TOK_PACKED opt_signed_struct
|
||||||
|
| { frontend_verilog_yyerror("Only STRUCT PACKED supported at this time"); }
|
||||||
|
;
|
||||||
|
|
||||||
|
opt_signed_struct:
|
||||||
|
TOK_SIGNED { astbuf2->is_signed = true; }
|
||||||
|
| TOK_UNSIGNED
|
||||||
|
| // default is unsigned
|
||||||
|
;
|
||||||
|
|
||||||
|
struct_member_list: struct_member
|
||||||
|
| struct_member_list struct_member
|
||||||
|
;
|
||||||
|
|
||||||
|
struct_member: struct_member_type member_name_list ';' { delete astbuf1; }
|
||||||
|
;
|
||||||
|
|
||||||
|
member_name_list:
|
||||||
|
member_name
|
||||||
|
| member_name_list ',' member_name
|
||||||
|
;
|
||||||
|
|
||||||
|
member_name: TOK_ID {
|
||||||
|
astbuf1->str = $1->substr(1);
|
||||||
|
delete $1;
|
||||||
|
astbuf2->children.push_back(astbuf1->clone());
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
struct_member_type: { astbuf1 = new AstNode(AST_STRUCT_ITEM); } member_type_token_list { SET_RULE_LOC(@$, @2, @$); }
|
||||||
|
;
|
||||||
|
|
||||||
|
member_type_token_list:
|
||||||
|
member_type
|
||||||
|
| hierarchical_type_id {
|
||||||
|
// use a clone of the typedef definition nodes
|
||||||
|
auto template_node = copyTypeDefinition(*$1);
|
||||||
|
if (template_node->type != AST_WIRE) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
member_type: type_atom type_signing
|
||||||
|
| type_vec type_signing range { if ($3) astbuf1->children.push_back($3); }
|
||||||
|
;
|
||||||
|
|
||||||
|
struct_var_list: struct_var
|
||||||
|
| struct_var_list ',' struct_var
|
||||||
|
;
|
||||||
|
|
||||||
|
struct_var: TOK_ID { auto *var_node = astbuf2->clone();
|
||||||
|
var_node->str = *$1;
|
||||||
|
delete $1;
|
||||||
|
ast_stack.back()->children.push_back(var_node);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
/////////
|
||||||
|
// wire
|
||||||
|
/////////
|
||||||
|
|
||||||
wire_decl:
|
wire_decl:
|
||||||
attr wire_type range {
|
attr wire_type range {
|
||||||
albuf = $1;
|
albuf = $1;
|
||||||
astbuf1 = $2;
|
astbuf1 = $2;
|
||||||
astbuf2 = $3;
|
astbuf2 = checkRange(astbuf1, $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>]");
|
|
||||||
} delay wire_name_list {
|
} delay wire_name_list {
|
||||||
delete astbuf1;
|
delete astbuf1;
|
||||||
if (astbuf2 != NULL)
|
if (astbuf2 != NULL)
|
||||||
|
@ -1591,19 +1699,9 @@ wire_name:
|
||||||
if (node->is_input || node->is_output)
|
if (node->is_input || node->is_output)
|
||||||
frontend_verilog_yyerror("input/output/inout ports cannot have unpacked dimensions.");
|
frontend_verilog_yyerror("input/output/inout ports cannot have unpacked dimensions.");
|
||||||
if (!astbuf2 && !node->is_custom_type) {
|
if (!astbuf2 && !node->is_custom_type) {
|
||||||
AstNode *rng = new AstNode(AST_RANGE);
|
addRange(node, 0, 0, false);
|
||||||
rng->children.push_back(AstNode::mkconst_int(0, true));
|
|
||||||
rng->children.push_back(AstNode::mkconst_int(0, true));
|
|
||||||
node->children.push_back(rng);
|
|
||||||
}
|
}
|
||||||
node->type = AST_MEMORY;
|
rewriteAsMemoryNode(node, $2);
|
||||||
auto *rangeNode = $2;
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
node->children.push_back(rangeNode);
|
|
||||||
}
|
}
|
||||||
if (current_function_or_task == NULL) {
|
if (current_function_or_task == NULL) {
|
||||||
if (do_not_require_port_stubs && (node->is_input || node->is_output) && port_stubs.count(*$1) == 0) {
|
if (do_not_require_port_stubs && (node->is_input || node->is_output) && port_stubs.count(*$1) == 0) {
|
||||||
|
@ -1651,42 +1749,23 @@ type_name: TOK_ID // first time seen
|
||||||
typedef_decl:
|
typedef_decl:
|
||||||
TOK_TYPEDEF wire_type range type_name range_or_multirange ';' {
|
TOK_TYPEDEF wire_type range type_name range_or_multirange ';' {
|
||||||
astbuf1 = $2;
|
astbuf1 = $2;
|
||||||
astbuf2 = $3;
|
astbuf2 = checkRange(astbuf1, $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)
|
if (astbuf2)
|
||||||
astbuf1->children.push_back(astbuf2);
|
astbuf1->children.push_back(astbuf2);
|
||||||
|
|
||||||
if ($5 != NULL) {
|
if ($5 != NULL) {
|
||||||
if (!astbuf2) {
|
if (!astbuf2) {
|
||||||
AstNode *rng = new AstNode(AST_RANGE);
|
addRange(astbuf1, 0, 0, false);
|
||||||
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;
|
rewriteAsMemoryNode(astbuf1, $5);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
addTypedefNode($4, astbuf1);
|
addTypedefNode($4, astbuf1); }
|
||||||
} |
|
| TOK_TYPEDEF non_wire_data_type type_name ';' { addTypedefNode($3, $2); }
|
||||||
TOK_TYPEDEF enum_type type_name ';' {
|
;
|
||||||
addTypedefNode($3, astbuf1);
|
|
||||||
}
|
non_wire_data_type:
|
||||||
|
enum_type
|
||||||
|
| struct_type
|
||||||
;
|
;
|
||||||
|
|
||||||
cell_stmt:
|
cell_stmt:
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
module top;
|
||||||
|
localparam BITS=8;
|
||||||
|
|
||||||
|
struct packed {
|
||||||
|
logic a;
|
||||||
|
logic[BITS-1:0] b;
|
||||||
|
byte c;
|
||||||
|
logic x, y;
|
||||||
|
} s;
|
||||||
|
|
||||||
|
struct packed signed {
|
||||||
|
integer a;
|
||||||
|
logic[15:0] b;
|
||||||
|
logic[7:0] c;
|
||||||
|
bit [7:0] d;
|
||||||
|
} pack1;
|
||||||
|
|
||||||
|
assign s.a = '1;
|
||||||
|
assign s.b = '1;
|
||||||
|
assign s.c = 8'hAA;
|
||||||
|
assign s.x = '1;
|
||||||
|
logic[7:0] t;
|
||||||
|
assign t = s.b;
|
||||||
|
assign pack1.a = 42;
|
||||||
|
assign pack1.b = 16'hAAAA;
|
||||||
|
assign pack1.c = '1;
|
||||||
|
assign pack1.d = 8'h55;
|
||||||
|
|
||||||
|
always_comb assert(s.a == 1'b1);
|
||||||
|
always_comb assert(s.c == 8'hAA);
|
||||||
|
always_comb assert(s.x == 1'b1);
|
||||||
|
always_comb assert(t == 8'hFF);
|
||||||
|
always_comb assert(pack1.a == 42);
|
||||||
|
always_comb assert(pack1.b == 16'hAAAA);
|
||||||
|
always_comb assert(pack1.c == 8'hFF);
|
||||||
|
always_comb assert(pack1[15:8] == 8'hFF);
|
||||||
|
always_comb assert(pack1.d == 8'h55);
|
||||||
|
|
||||||
|
endmodule
|
|
@ -0,0 +1,42 @@
|
||||||
|
package p;
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
byte a;
|
||||||
|
byte b;
|
||||||
|
} p_t;
|
||||||
|
|
||||||
|
endpackage
|
||||||
|
|
||||||
|
|
||||||
|
module top;
|
||||||
|
|
||||||
|
typedef logic[7:0] t_t;
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
bit a;
|
||||||
|
logic[7:0] b;
|
||||||
|
t_t t;
|
||||||
|
} s_t;
|
||||||
|
|
||||||
|
s_t s;
|
||||||
|
s_t s1;
|
||||||
|
|
||||||
|
p::p_t ps;
|
||||||
|
|
||||||
|
assign s.a = '1;
|
||||||
|
assign s.b = '1;
|
||||||
|
assign s.t = 8'h55;
|
||||||
|
assign s1 = s;
|
||||||
|
assign ps.a = 8'hAA;
|
||||||
|
assign ps.b = 8'h55;
|
||||||
|
|
||||||
|
always_comb begin
|
||||||
|
assert(s.a == 1'b1);
|
||||||
|
assert(s.b == 8'hFF);
|
||||||
|
assert(s.t == 8'h55);
|
||||||
|
assert(s1.t == 8'h55);
|
||||||
|
assert(ps.a == 8'hAA);
|
||||||
|
assert(ps.b == 8'h55);
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
Loading…
Reference in New Issue