mirror of https://github.com/YosysHQ/yosys.git
commit
cd044a2bb6
17
README.md
17
README.md
|
@ -446,6 +446,17 @@ Verilog Attributes and non-standard features
|
|||
...
|
||||
endmodule
|
||||
|
||||
- The ``wiretype`` attribute is added by the verilog parser for wires of a
|
||||
typedef'd type to indicate the type identifier.
|
||||
|
||||
- Various ``enum_{width}_{value}`` attributes are added to wires of an
|
||||
enumerated type to give a map of possible enum items to their values.
|
||||
|
||||
- The ``enum_base_type`` attribute is added to enum items to indicate which
|
||||
enum they belong to (enums -- anonymous and otherwise -- are
|
||||
automatically named with an auto-incrementing counter). Note that enums
|
||||
are currently not strongly typed.
|
||||
|
||||
- A limited subset of DPI-C functions is supported. The plugin mechanism
|
||||
(see ``help plugin``) can be used to load .so files with implementations
|
||||
of DPI-C routines. As a non-standard extension it is possible to specify
|
||||
|
@ -536,6 +547,12 @@ from SystemVerilog:
|
|||
SystemVerilog files being read into the same design afterwards.
|
||||
|
||||
- typedefs are supported (including inside packages)
|
||||
- type identifiers must currently be enclosed in (parentheses) when declaring
|
||||
signals of that type (this is syntactically incorrect SystemVerilog)
|
||||
- type casts are currently not supported
|
||||
|
||||
- enums are supported (including inside packages)
|
||||
- but are currently not strongly typed
|
||||
|
||||
- SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether
|
||||
ports are inputs or outputs are supported.
|
||||
|
|
|
@ -88,6 +88,8 @@ std::string AST::type2str(AstNodeType type)
|
|||
X(AST_LIVE)
|
||||
X(AST_FAIR)
|
||||
X(AST_COVER)
|
||||
X(AST_ENUM)
|
||||
X(AST_ENUM_ITEM)
|
||||
X(AST_FCALL)
|
||||
X(AST_TO_BITS)
|
||||
X(AST_TO_SIGNED)
|
||||
|
@ -202,6 +204,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
|
|||
is_logic = false;
|
||||
is_signed = false;
|
||||
is_string = false;
|
||||
is_enum = false;
|
||||
is_wand = false;
|
||||
is_wor = false;
|
||||
is_unsized = false;
|
||||
|
@ -321,6 +324,9 @@ void AstNode::dumpAst(FILE *f, std::string indent) const
|
|||
fprintf(f, " %d", v);
|
||||
fprintf(f, " ]");
|
||||
}
|
||||
if (is_enum) {
|
||||
fprintf(f, " type=enum");
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
|
||||
for (auto &it : attributes) {
|
||||
|
@ -1174,7 +1180,15 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
|
|||
for (auto n : design->verilog_packages){
|
||||
for (auto o : n->children) {
|
||||
AstNode *cloned_node = o->clone();
|
||||
cloned_node->str = n->str + std::string("::") + cloned_node->str.substr(1);
|
||||
log("cloned node %s\n", type2str(cloned_node->type).c_str());
|
||||
if (cloned_node->type == AST_ENUM){
|
||||
for (auto e : cloned_node->children){
|
||||
log_assert(e->type == AST_ENUM_ITEM);
|
||||
e->str = n->str + std::string("::") + e->str.substr(1);
|
||||
}
|
||||
} else {
|
||||
cloned_node->str = n->str + std::string("::") + cloned_node->str.substr(1);
|
||||
}
|
||||
(*it)->children.push_back(cloned_node);
|
||||
}
|
||||
}
|
||||
|
@ -1203,10 +1217,14 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
|
|||
|
||||
design->add(process_module(*it, defer));
|
||||
}
|
||||
else if ((*it)->type == AST_PACKAGE)
|
||||
else if ((*it)->type == AST_PACKAGE) {
|
||||
design->verilog_packages.push_back((*it)->clone());
|
||||
else
|
||||
}
|
||||
else {
|
||||
// must be global definition
|
||||
(*it)->simplify(false, false, false, 1, -1, false, false); //process enum/other declarations
|
||||
design->verilog_globals.push_back((*it)->clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -68,6 +68,8 @@ namespace AST
|
|||
AST_LIVE,
|
||||
AST_FAIR,
|
||||
AST_COVER,
|
||||
AST_ENUM,
|
||||
AST_ENUM_ITEM,
|
||||
|
||||
AST_FCALL,
|
||||
AST_TO_BITS,
|
||||
|
@ -181,6 +183,8 @@ namespace AST
|
|||
int port_id, range_left, range_right;
|
||||
uint32_t integer;
|
||||
double realvalue;
|
||||
// set for IDs typed to an enumeration, not used
|
||||
bool is_enum;
|
||||
|
||||
// if this is a multirange memory then this vector contains offset and length of each dimension
|
||||
std::vector<int> multirange_dimensions;
|
||||
|
@ -286,6 +290,9 @@ namespace AST
|
|||
int isConst() const; // return '1' for AST_CONSTANT and '2' for AST_REALVALUE
|
||||
double asReal(bool is_signed);
|
||||
RTLIL::Const realAsConst(int width);
|
||||
|
||||
// helpers for enum
|
||||
void allocateDefaultEnumValues();
|
||||
};
|
||||
|
||||
// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code
|
||||
|
|
|
@ -595,6 +595,9 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
|||
|
||||
switch (type)
|
||||
{
|
||||
case AST_NONE:
|
||||
// unallocated enum, ignore
|
||||
break;
|
||||
case AST_CONSTANT:
|
||||
width_hint = max(width_hint, int(bits.size()));
|
||||
if (!is_signed)
|
||||
|
@ -612,7 +615,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
|||
id_ast = current_scope.at(str);
|
||||
if (!id_ast)
|
||||
log_file_error(filename, linenum, "Failed to resolve identifier %s for width detection!\n", str.c_str());
|
||||
if (id_ast->type == AST_PARAMETER || id_ast->type == AST_LOCALPARAM) {
|
||||
if (id_ast->type == AST_PARAMETER || id_ast->type == AST_LOCALPARAM || id_ast->type == AST_ENUM_ITEM) {
|
||||
if (id_ast->children.size() > 1 && id_ast->children[1]->range_valid) {
|
||||
this_width = id_ast->children[1]->range_left - id_ast->children[1]->range_right + 1;
|
||||
} else
|
||||
|
@ -861,6 +864,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
case AST_GENIF:
|
||||
case AST_GENCASE:
|
||||
case AST_PACKAGE:
|
||||
case AST_ENUM:
|
||||
case AST_MODPORT:
|
||||
case AST_MODPORTMEMBER:
|
||||
case AST_TYPEDEF:
|
||||
|
@ -1022,7 +1026,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
else
|
||||
log_file_error(filename, linenum, "Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str());
|
||||
}
|
||||
else if (id2ast->type == AST_PARAMETER || id2ast->type == AST_LOCALPARAM) {
|
||||
else if (id2ast->type == AST_PARAMETER || id2ast->type == AST_LOCALPARAM || id2ast->type == AST_ENUM_ITEM) {
|
||||
if (id2ast->children[0]->type != AST_CONSTANT)
|
||||
log_file_error(filename, linenum, "Parameter %s does not evaluate to constant value!\n", str.c_str());
|
||||
chunk = RTLIL::Const(id2ast->children[0]->bits);
|
||||
|
|
|
@ -323,9 +323,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
}
|
||||
|
||||
// activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.)
|
||||
if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX || type == AST_TYPEDEF)
|
||||
if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_ENUM_ITEM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX || type == AST_TYPEDEF)
|
||||
const_fold = true;
|
||||
if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM))
|
||||
if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM || current_scope[str]->type == AST_ENUM_ITEM))
|
||||
const_fold = true;
|
||||
|
||||
// in certain cases a function must be evaluated constant. this is what in_param controls.
|
||||
|
@ -410,18 +410,35 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
}
|
||||
this_wire_scope[node->str] = node;
|
||||
}
|
||||
// these nodes appear at the top level in a module and can define names
|
||||
if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR ||
|
||||
node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION || node->type == AST_CELL ||
|
||||
node->type == AST_TYPEDEF) {
|
||||
backup_scope[node->str] = current_scope[node->str];
|
||||
current_scope[node->str] = node;
|
||||
}
|
||||
if (node->type == AST_ENUM) {
|
||||
current_scope[node->str] = node;
|
||||
for (auto enode : node->children) {
|
||||
log_assert(enode->type==AST_ENUM_ITEM);
|
||||
if (current_scope.count(enode->str) == 0) {
|
||||
current_scope[enode->str] = enode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < children.size(); i++) {
|
||||
AstNode *node = children[i];
|
||||
if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_MEMORY || node->type == AST_TYPEDEF)
|
||||
while (node->simplify(true, false, false, 1, -1, false, node->type == AST_PARAMETER || node->type == AST_LOCALPARAM))
|
||||
did_something = true;
|
||||
if (node->type == AST_ENUM) {
|
||||
for (auto enode : node->children){
|
||||
log_assert(enode->type==AST_ENUM_ITEM);
|
||||
while (node->simplify(true, false, false, 1, -1, false, in_param))
|
||||
did_something = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -497,6 +514,18 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
}
|
||||
break;
|
||||
|
||||
case AST_ENUM:
|
||||
//log("\nENUM %s: %d child %d\n", str.c_str(), basic_prep, children[0]->basic_prep);
|
||||
if (!basic_prep) {
|
||||
for (auto item_node : children) {
|
||||
while (!item_node->basic_prep && item_node->simplify(false, false, false, stage, -1, false, in_param))
|
||||
did_something = true;
|
||||
}
|
||||
// allocate values (called more than once)
|
||||
allocateDefaultEnumValues();
|
||||
}
|
||||
break;
|
||||
|
||||
case AST_PARAMETER:
|
||||
case AST_LOCALPARAM:
|
||||
while (!children[0]->basic_prep && children[0]->simplify(false, false, false, stage, -1, false, true) == true)
|
||||
|
@ -510,6 +539,18 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
width_hint = max(width_hint, children[1]->range_left - children[1]->range_right + 1);
|
||||
}
|
||||
break;
|
||||
case AST_ENUM_ITEM:
|
||||
while (!children[0]->basic_prep && children[0]->simplify(false, false, false, stage, -1, false, in_param))
|
||||
did_something = true;
|
||||
children[0]->detectSignWidth(width_hint, sign_hint);
|
||||
if (children.size() > 1 && children[1]->type == AST_RANGE) {
|
||||
while (!children[1]->basic_prep && children[1]->simplify(false, false, false, stage, -1, false, in_param))
|
||||
did_something = true;
|
||||
if (!children[1]->range_valid)
|
||||
log_file_error(filename, linenum, "Non-constant width range on enum item decl.\n");
|
||||
width_hint = max(width_hint, children[1]->range_left - children[1]->range_right + 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case AST_TO_BITS:
|
||||
case AST_TO_SIGNED:
|
||||
|
@ -827,11 +868,68 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
range_swapped = templ->range_swapped;
|
||||
range_left = templ->range_left;
|
||||
range_right = templ->range_right;
|
||||
attributes["\\wiretype"] = mkconst_str(resolved_type->str);
|
||||
//check if enum
|
||||
if (templ->attributes.count("\\enum_type")){
|
||||
//get reference to enum node:
|
||||
std::string enum_type = templ->attributes["\\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_";
|
||||
enum_item_str.append(std::to_string(width));
|
||||
enum_item_str.append("_");
|
||||
//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()
|
||||
);
|
||||
}
|
||||
int val = enum_item->children[0]->asInt(is_signed);
|
||||
enum_item_str.append(std::to_string(val));
|
||||
//set attribute for available val to enum item name mappings
|
||||
attributes[enum_item_str.c_str()] = mkconst_str(enum_item->str);
|
||||
}
|
||||
}
|
||||
|
||||
// Insert clones children from template at beginning
|
||||
for (int i = 0; i < GetSize(templ->children); i++)
|
||||
children.insert(children.begin() + i, templ->children[i]->clone());
|
||||
|
||||
|
||||
if (type == AST_MEMORY && GetSize(children) == 1) {
|
||||
// Single-bit memories must have [0:0] range
|
||||
AstNode *rng = new AstNode(AST_RANGE);
|
||||
|
@ -873,12 +971,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
range_swapped = templ->range_swapped;
|
||||
range_left = templ->range_left;
|
||||
range_right = templ->range_right;
|
||||
attributes["\\wiretype"] = mkconst_str(resolved_type->str);
|
||||
for (auto template_child : templ->children)
|
||||
children.push_back(template_child->clone());
|
||||
did_something = true;
|
||||
}
|
||||
log_assert(!is_custom_type);
|
||||
}
|
||||
}
|
||||
|
||||
// resolve constant prefixes
|
||||
if (type == AST_PREFIX) {
|
||||
|
@ -1010,7 +1109,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
}
|
||||
|
||||
// trim/extend parameters
|
||||
if (type == AST_PARAMETER || type == AST_LOCALPARAM) {
|
||||
if (type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_ENUM_ITEM) {
|
||||
if (children.size() > 1 && children[1]->type == AST_RANGE) {
|
||||
if (!children[1]->range_valid)
|
||||
log_file_error(filename, linenum, "Non-constant width range on parameter decl.\n");
|
||||
|
@ -1051,9 +1150,34 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
if (type == AST_IDENTIFIER) {
|
||||
if (current_scope.count(str) == 0) {
|
||||
for (auto node : current_ast_mod->children) {
|
||||
if ((node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR ||
|
||||
node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION) && str == node->str) {
|
||||
//log("looking at mod scope child %s\n", type2str(node->type).c_str());
|
||||
switch (node->type) {
|
||||
case AST_PARAMETER:
|
||||
case AST_LOCALPARAM:
|
||||
case AST_WIRE:
|
||||
case AST_AUTOWIRE:
|
||||
case AST_GENVAR:
|
||||
case AST_MEMORY:
|
||||
case AST_FUNCTION:
|
||||
case AST_TASK:
|
||||
case AST_DPI_FUNCTION:
|
||||
//log("found child %s, %s\n", type2str(node->type).c_str(), node->str.c_str());
|
||||
if (str == node->str) {
|
||||
log("add %s, type %s to scope\n", str.c_str(), type2str(node->type).c_str());
|
||||
current_scope[node->str] = node;
|
||||
}
|
||||
break;
|
||||
case AST_ENUM:
|
||||
current_scope[node->str] = node;
|
||||
for (auto enum_node : node->children) {
|
||||
log_assert(enum_node->type==AST_ENUM_ITEM);
|
||||
if (str == enum_node->str) {
|
||||
//log("\nadding enum item %s to scope\n", str.c_str());
|
||||
current_scope[str] = enum_node;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1279,7 +1403,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
}
|
||||
|
||||
if (buf->type != AST_CONSTANT)
|
||||
log_file_error(filename, linenum, "Right hand side of 3rd expression of generate for-loop is not constant!\n");
|
||||
log_file_error(filename, linenum, "Right hand side of 3rd expression of generate for-loop is not constant (%s)!\n", type2str(buf->type).c_str());
|
||||
|
||||
delete varbuf->children[0];
|
||||
varbuf->children[0] = buf;
|
||||
|
@ -2498,7 +2622,7 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
}
|
||||
|
||||
for (auto child : decl->children)
|
||||
if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM)
|
||||
if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM || child->type == AST_ENUM_ITEM)
|
||||
{
|
||||
AstNode *wire = nullptr;
|
||||
|
||||
|
@ -2529,6 +2653,9 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
wire->is_output = false;
|
||||
wire->is_reg = true;
|
||||
wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
|
||||
if (child->type == AST_ENUM_ITEM)
|
||||
wire->attributes["\\enum_base_type"] = child->attributes["\\enum_base_type"];
|
||||
|
||||
wire_cache[child->str] = wire;
|
||||
|
||||
current_ast_mod->children.push_back(wire);
|
||||
|
@ -2604,7 +2731,7 @@ replace_fcall_later:;
|
|||
switch (type)
|
||||
{
|
||||
case AST_IDENTIFIER:
|
||||
if (current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM)) {
|
||||
if (current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM || current_scope[str]->type == AST_ENUM_ITEM)) {
|
||||
if (current_scope[str]->children[0]->type == AST_CONSTANT) {
|
||||
if (children.size() != 0 && children[0]->type == AST_RANGE && children[0]->range_valid) {
|
||||
std::vector<RTLIL::State> data;
|
||||
|
@ -3051,7 +3178,7 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma
|
|||
for (size_t i = 0; i < children.size(); i++) {
|
||||
AstNode *child = children[i];
|
||||
if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM ||
|
||||
child->type == AST_FUNCTION || child->type == AST_TASK || child->type == AST_CELL || child->type == AST_TYPEDEF) {
|
||||
child->type == AST_FUNCTION || child->type == AST_TASK || child->type == AST_CELL || child->type == AST_TYPEDEF || child->type == AST_ENUM_ITEM) {
|
||||
if (backup_name_map.size() == 0)
|
||||
backup_name_map = name_map;
|
||||
std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix;
|
||||
|
@ -3070,6 +3197,27 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma
|
|||
child->str = new_name;
|
||||
current_scope[new_name] = child;
|
||||
}
|
||||
if (child->type == AST_ENUM){
|
||||
current_scope[child->str] = child;
|
||||
for (auto enode : child->children){
|
||||
log_assert(enode->type == AST_ENUM_ITEM);
|
||||
if (backup_name_map.size() == 0)
|
||||
backup_name_map = name_map;
|
||||
std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix;
|
||||
size_t pos = enode->str.rfind('.');
|
||||
if (pos == std::string::npos)
|
||||
pos = enode->str[0] == '\\' && prefix[0] == '\\' ? 1 : 0;
|
||||
else
|
||||
pos = pos + 1;
|
||||
new_name = enode->str.substr(0, pos) + new_name + enode->str.substr(pos);
|
||||
if (new_name[0] != '$' && new_name[0] != '\\')
|
||||
new_name = prefix[0] + new_name;
|
||||
name_map[enode->str] = new_name;
|
||||
|
||||
enode->str = new_name;
|
||||
current_scope[new_name] = enode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < children.size(); i++) {
|
||||
|
@ -3808,4 +3956,32 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
|
|||
return AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed);
|
||||
}
|
||||
|
||||
void AstNode::allocateDefaultEnumValues()
|
||||
{
|
||||
log_assert(type==AST_ENUM);
|
||||
int last_enum_int = -1;
|
||||
for (auto node : children) {
|
||||
log_assert(node->type==AST_ENUM_ITEM);
|
||||
node->attributes["\\enum_base_type"] = mkconst_str(str);
|
||||
for (size_t i = 0; i < node->children.size(); i++) {
|
||||
switch (node->children[i]->type) {
|
||||
case AST_NONE:
|
||||
// replace with auto-incremented constant
|
||||
delete node->children[i];
|
||||
node->children[i] = AstNode::mkconst_int(++last_enum_int, true);
|
||||
break;
|
||||
case AST_CONSTANT:
|
||||
// explicit constant (or folded expression)
|
||||
// TODO: can't extend 'x or 'z item
|
||||
last_enum_int = node->children[i]->integer;
|
||||
break;
|
||||
default:
|
||||
// ignore ranges
|
||||
break;
|
||||
}
|
||||
// TODO: range check
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
|
@ -108,6 +108,20 @@ struct specify_rise_fall {
|
|||
specify_triple fall;
|
||||
};
|
||||
|
||||
static AstNode *makeRange(int msb = 31, int lsb = 0, bool isSigned = true)
|
||||
{
|
||||
auto range = new AstNode(AST_RANGE);
|
||||
range->children.push_back(AstNode::mkconst_int(msb, true));
|
||||
range->children.push_back(AstNode::mkconst_int(lsb, true));
|
||||
range->is_signed = isSigned;
|
||||
return range;
|
||||
}
|
||||
|
||||
static void addRange(AstNode *parent, int msb = 31, int lsb = 0, bool isSigned = true)
|
||||
{
|
||||
auto range = makeRange(msb, lsb, isSigned);
|
||||
parent->children.push_back(range);
|
||||
}
|
||||
%}
|
||||
|
||||
%define api.prefix {frontend_verilog_yy}
|
||||
|
@ -157,6 +171,7 @@ struct specify_rise_fall {
|
|||
%type <ast> range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int
|
||||
%type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list
|
||||
%type <string> opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id
|
||||
%type <ast> opt_enum_init
|
||||
%type <boolean> opt_signed opt_property unique_case_attr always_comb_or_latch always_or_always_ff
|
||||
%type <al> attr case_attr
|
||||
|
||||
|
@ -428,7 +443,9 @@ package:
|
|||
};
|
||||
|
||||
package_body:
|
||||
package_body package_body_stmt |;
|
||||
package_body package_body_stmt
|
||||
| // optional
|
||||
;
|
||||
|
||||
package_body_stmt:
|
||||
typedef_decl |
|
||||
|
@ -604,6 +621,7 @@ module_body:
|
|||
|
||||
module_body_stmt:
|
||||
task_func_decl | specify_block | param_decl | localparam_decl | typedef_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt |
|
||||
enum_decl |
|
||||
always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block;
|
||||
|
||||
checker_decl:
|
||||
|
@ -1224,6 +1242,85 @@ single_defparam_decl:
|
|||
ast_stack.back()->children.push_back(node);
|
||||
};
|
||||
|
||||
enum_type: TOK_ENUM {
|
||||
static int enum_count;
|
||||
// create parent node for the enum
|
||||
astbuf2 = new AstNode(AST_ENUM);
|
||||
ast_stack.back()->children.push_back(astbuf2);
|
||||
astbuf2->str = std::string("$enum");
|
||||
astbuf2->str += std::to_string(enum_count++);
|
||||
// create the template for the names
|
||||
astbuf1 = new AstNode(AST_ENUM_ITEM);
|
||||
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
|
||||
} param_signed enum_base_type '{' enum_name_list '}' { // create template for the enum vars
|
||||
auto tnode = astbuf1->clone();
|
||||
delete astbuf1;
|
||||
astbuf1 = tnode;
|
||||
tnode->type = AST_WIRE;
|
||||
tnode->attributes["\\enum_type"] = AstNode::mkconst_str(astbuf2->str);
|
||||
// drop constant but keep any range
|
||||
delete tnode->children[0];
|
||||
tnode->children.erase(tnode->children.begin()); }
|
||||
;
|
||||
|
||||
enum_base_type: int_vec param_range
|
||||
| int_atom
|
||||
| /* 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
|
||||
;
|
||||
|
||||
int_vec: TOK_REG {astbuf1->is_reg = true;}
|
||||
| TOK_LOGIC {astbuf1->is_logic = true;}
|
||||
;
|
||||
|
||||
enum_name_list:
|
||||
enum_name_decl
|
||||
| enum_name_list ',' enum_name_decl
|
||||
;
|
||||
|
||||
enum_name_decl:
|
||||
TOK_ID opt_enum_init {
|
||||
// put in fn
|
||||
log_assert(astbuf1);
|
||||
log_assert(astbuf2);
|
||||
auto node = astbuf1->clone();
|
||||
node->str = *$1;
|
||||
delete $1;
|
||||
delete node->children[0];
|
||||
node->children[0] = $2 ?: new AstNode(AST_NONE);
|
||||
astbuf2->children.push_back(node);
|
||||
}
|
||||
;
|
||||
|
||||
opt_enum_init:
|
||||
'=' basic_expr { $$ = $2; } // TODO: restrict this
|
||||
| /* optional */ { $$ = NULL; }
|
||||
;
|
||||
|
||||
enum_var_list:
|
||||
enum_var
|
||||
| enum_var_list ',' enum_var
|
||||
;
|
||||
|
||||
enum_var: TOK_ID {
|
||||
log_assert(astbuf1);
|
||||
log_assert(astbuf2);
|
||||
auto node = astbuf1->clone();
|
||||
ast_stack.back()->children.push_back(node);
|
||||
node->str = *$1;
|
||||
delete $1;
|
||||
node->is_enum = true;
|
||||
}
|
||||
;
|
||||
|
||||
enum_decl: enum_type enum_var_list ';' {
|
||||
//enum_type creates astbuf1 for use by typedef only
|
||||
delete astbuf1;
|
||||
}
|
||||
;
|
||||
|
||||
wire_decl:
|
||||
attr wire_type range {
|
||||
albuf = $1;
|
||||
|
@ -1434,7 +1531,12 @@ typedef_decl:
|
|||
|
||||
ast_stack.back()->children.push_back(new AstNode(AST_TYPEDEF, astbuf1));
|
||||
ast_stack.back()->children.back()->str = *$4;
|
||||
};
|
||||
} |
|
||||
TOK_TYPEDEF enum_type TOK_ID ';' {
|
||||
ast_stack.back()->children.push_back(new AstNode(AST_TYPEDEF, astbuf1));
|
||||
ast_stack.back()->children.back()->str = *$3;
|
||||
}
|
||||
;
|
||||
|
||||
cell_stmt:
|
||||
attr TOK_ID {
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
|
||||
module enum_simple(input clk, input rst);
|
||||
|
||||
enum {s0, s1, s2, s3} test_enum;
|
||||
typedef enum logic [1:0] {
|
||||
ts0, ts1, ts2, ts3
|
||||
} states_t;
|
||||
(states_t) state;
|
||||
(states_t) enum_const = ts1;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
test_enum <= s3;
|
||||
state <= ts0;
|
||||
end else begin
|
||||
//test_enum
|
||||
if (test_enum == s0)
|
||||
test_enum <= s1;
|
||||
else if (test_enum == s1)
|
||||
test_enum <= s2;
|
||||
else if (test_enum == s2)
|
||||
test_enum <= s3;
|
||||
else if (test_enum == s3)
|
||||
test_enum <= s0;
|
||||
else
|
||||
assert(1'b0); //should be unreachable
|
||||
|
||||
//state
|
||||
if (state == ts0)
|
||||
state <= ts1;
|
||||
else if (state == ts1)
|
||||
state <= ts2;
|
||||
else if (state == ts2)
|
||||
state <= ts0;
|
||||
else
|
||||
assert(1'b0); //should be unreachable
|
||||
end
|
||||
end
|
||||
|
||||
always @(*) begin
|
||||
assert(state != 2'h3);
|
||||
assert(s0 == '0);
|
||||
assert(ts0 == '0);
|
||||
assert(enum_const == ts1);
|
||||
end
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
read_verilog -sv enum_simple.sv
|
||||
hierarchy; proc; opt
|
||||
sat -verify -seq 1 -set-at 1 rst 1 -tempinduct -prove-asserts -show-all
|
||||
|
|
@ -1,11 +1,14 @@
|
|||
package pkg;
|
||||
typedef logic [7:0] uint8_t;
|
||||
typedef enum logic [7:0] {bb=8'hBB} enum8_t;
|
||||
endpackage
|
||||
|
||||
module top;
|
||||
|
||||
(* keep *) (pkg::uint8_t) a = 8'hAA;
|
||||
(* keep *) (pkg::enum8_t) b_enum = pkg::bb;
|
||||
|
||||
always @* assert(a == 8'hAA);
|
||||
always @* assert(b_enum == 8'hBB);
|
||||
|
||||
endmodule
|
||||
|
|
|
@ -1,23 +1,35 @@
|
|||
|
||||
typedef logic [3:0] outer_uint4_t;
|
||||
typedef enum logic {s0, s1} outer_enum_t;
|
||||
|
||||
module top;
|
||||
|
||||
(outer_uint4_t) u4_i = 8'hA5;
|
||||
(outer_enum_t) enum4_i = s0;
|
||||
always @(*) assert(u4_i == 4'h5);
|
||||
always @(*) assert(enum4_i == 1'b0);
|
||||
|
||||
typedef logic [3:0] inner_type;
|
||||
typedef enum logic [2:0] {s2=2, s3, s4} inner_enum_t;
|
||||
(inner_type) inner_i1 = 8'h5A;
|
||||
(inner_enum_t) inner_enum1 = s3;
|
||||
always @(*) assert(inner_i1 == 4'hA);
|
||||
always @(*) assert(inner_enum1 == 3'h3);
|
||||
|
||||
if (1) begin: genblock
|
||||
typedef logic [7:0] inner_type;
|
||||
(inner_type) inner_gb_i = 8'hA5;
|
||||
parameter (inner_type) inner_const = 8'hA5;
|
||||
typedef enum logic [2:0] {s5=5, s6, s7} inner_enum_t;
|
||||
(inner_type) inner_gb_i = inner_const; //8'hA5;
|
||||
(inner_enum_t) inner_gb_enum1 = s7;
|
||||
always @(*) assert(inner_gb_i == 8'hA5);
|
||||
always @(*) assert(inner_gb_enum1 == 3'h7);
|
||||
end
|
||||
|
||||
(inner_type) inner_i2 = 8'h42;
|
||||
(inner_enum_t) inner_enum2 = s4;
|
||||
always @(*) assert(inner_i2 == 4'h2);
|
||||
always @(*) assert(inner_enum2 == 3'h4);
|
||||
|
||||
|
||||
endmodule
|
||||
|
|
Loading…
Reference in New Issue