diff options
Diffstat (limited to 'frontends')
-rw-r--r-- | frontends/aiger/aigerparse.cc | 41 | ||||
-rw-r--r-- | frontends/aiger/aigerparse.h | 2 | ||||
-rw-r--r-- | frontends/ast/ast.cc | 4 | ||||
-rw-r--r-- | frontends/ast/ast.h | 20 | ||||
-rw-r--r-- | frontends/ast/genrtlil.cc | 29 | ||||
-rw-r--r-- | frontends/ast/simplify.cc | 615 | ||||
-rw-r--r-- | frontends/blif/blifparse.cc | 4 | ||||
-rw-r--r-- | frontends/ilang/ilang_frontend.cc | 4 | ||||
-rw-r--r-- | frontends/ilang/ilang_lexer.l | 4 | ||||
-rw-r--r-- | frontends/ilang/ilang_parser.y | 3 | ||||
-rw-r--r-- | frontends/json/jsonparse.cc | 11 | ||||
-rw-r--r-- | frontends/liberty/liberty.cc | 4 | ||||
-rw-r--r-- | frontends/rpc/rpc_frontend.cc | 16 | ||||
-rw-r--r-- | frontends/verific/verific.cc | 50 | ||||
-rw-r--r-- | frontends/verilog/preproc.cc | 2 | ||||
-rw-r--r-- | frontends/verilog/verilog_frontend.cc | 12 | ||||
-rw-r--r-- | frontends/verilog/verilog_lexer.l | 32 | ||||
-rw-r--r-- | frontends/verilog/verilog_parser.y | 455 |
18 files changed, 982 insertions, 326 deletions
diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc index 6fda92d73..07e3cd6e0 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -69,7 +69,7 @@ struct ConstEvalAig continue; for (auto &it2 : it.second->connections()) if (yosys_celltypes.cell_output(it.second->type, it2.first)) { - auto r YS_ATTRIBUTE(unused) = sig2driver.insert(std::make_pair(it2.second, it.second)); + auto r = sig2driver.insert(std::make_pair(it2.second, it.second)); log_assert(r.second); } } @@ -400,9 +400,9 @@ void AigerReader::parse_xaiger() for (int c = f.get(); c != EOF; c = f.get()) { // XAIGER extensions if (c == 'm') { - uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f); + uint32_t dataSize = parse_xaiger_literal(f); uint32_t lutNum = parse_xaiger_literal(f); - uint32_t lutSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f); + uint32_t lutSize = parse_xaiger_literal(f); log_debug("m: dataSize=%u lutNum=%u lutSize=%u\n", dataSize, lutNum, lutSize); ConstEvalAig ce(module); for (unsigned i = 0; i < lutNum; ++i) { @@ -434,7 +434,7 @@ void AigerReader::parse_xaiger() int gray = j ^ (j >> 1); ce.set_incremental(input_sig, RTLIL::Const{gray, GetSize(input_sig)}); RTLIL::SigBit o(output_sig); - bool success YS_ATTRIBUTE(unused) = ce.eval(o); + bool success = ce.eval(o); log_assert(success); log_assert(o.wire == nullptr); lut_mask[gray] = o.data; @@ -446,7 +446,7 @@ void AigerReader::parse_xaiger() } } else if (c == 'r') { - uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f); + uint32_t dataSize = parse_xaiger_literal(f); flopNum = parse_xaiger_literal(f); log_debug("flopNum = %u\n", flopNum); log_assert(dataSize == (flopNum+1) * sizeof(uint32_t)); @@ -454,6 +454,14 @@ void AigerReader::parse_xaiger() for (unsigned i = 0; i < flopNum; i++) mergeability.emplace_back(parse_xaiger_literal(f)); } + else if (c == 's') { + uint32_t dataSize = parse_xaiger_literal(f); + flopNum = parse_xaiger_literal(f); + log_assert(dataSize == (flopNum+1) * sizeof(uint32_t)); + initial_state.reserve(flopNum); + for (unsigned i = 0; i < flopNum; i++) + initial_state.emplace_back(parse_xaiger_literal(f)); + } else if (c == 'n') { parse_xaiger_literal(f); f >> s; @@ -461,15 +469,15 @@ void AigerReader::parse_xaiger() } else if (c == 'h') { f.ignore(sizeof(uint32_t)); - uint32_t version YS_ATTRIBUTE(unused) = parse_xaiger_literal(f); + uint32_t version = parse_xaiger_literal(f); log_assert(version == 1); - uint32_t ciNum YS_ATTRIBUTE(unused) = parse_xaiger_literal(f); + uint32_t ciNum = parse_xaiger_literal(f); log_debug("ciNum = %u\n", ciNum); - uint32_t coNum YS_ATTRIBUTE(unused) = parse_xaiger_literal(f); + uint32_t coNum = parse_xaiger_literal(f); log_debug("coNum = %u\n", coNum); piNum = parse_xaiger_literal(f); log_debug("piNum = %u\n", piNum); - uint32_t poNum YS_ATTRIBUTE(unused) = parse_xaiger_literal(f); + uint32_t poNum = parse_xaiger_literal(f); log_debug("poNum = %u\n", poNum); uint32_t boxNum = parse_xaiger_literal(f); log_debug("boxNum = %u\n", boxNum); @@ -778,10 +786,9 @@ void AigerReader::post_process() log_assert(q->port_input); q->port_input = false; - auto ff = module->addCell(NEW_ID, ID($__ABC9_FF_)); - ff->setPort(ID::D, d); - ff->setPort(ID::Q, q); + Cell* ff = module->addFfGate(NEW_ID, d, q); ff->attributes[ID::abc9_mergeability] = mergeability[i]; + q->attributes[ID::init] = initial_state[i]; } dict<RTLIL::IdString, std::pair<int,int>> wideports_cache; @@ -887,7 +894,9 @@ void AigerReader::post_process() } else if (type == "box") { RTLIL::Cell* cell = module->cell(stringf("$box%d", variable)); - if (cell) // ABC could have optimised this box away + if (!cell) + log_debug("Box %d (%s) no longer exists.\n", variable, log_id(escaped_s)); + else module->rename(cell, escaped_s); } else @@ -899,6 +908,8 @@ void AigerReader::post_process() auto name = wp.first; int min = wp.second.first; int max = wp.second.second; + if (min == 0 && max == 0) + continue; RTLIL::Wire *wire = module->wire(name); if (wire) @@ -959,7 +970,7 @@ void AigerReader::post_process() struct AigerFrontend : public Frontend { AigerFrontend() : Frontend("aiger", "read AIGER file") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -985,7 +996,7 @@ struct AigerFrontend : public Frontend { log(" read XAIGER extensions\n"); log("\n"); } - void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing AIGER frontend.\n"); diff --git a/frontends/aiger/aigerparse.h b/frontends/aiger/aigerparse.h index 46ac81212..251a24977 100644 --- a/frontends/aiger/aigerparse.h +++ b/frontends/aiger/aigerparse.h @@ -45,7 +45,7 @@ struct AigerReader std::vector<RTLIL::Wire*> outputs; std::vector<RTLIL::Wire*> bad_properties; std::vector<RTLIL::Cell*> boxes; - std::vector<int> mergeability; + std::vector<int> mergeability, initial_state; AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports); void parse_aiger(); diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 689fa9fb4..9520ae32c 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -95,6 +95,7 @@ std::string AST::type2str(AstNodeType type) X(AST_TO_SIGNED) X(AST_TO_UNSIGNED) X(AST_SELFSZ) + X(AST_CAST_SIZE) X(AST_CONCAT) X(AST_REPLICATE) X(AST_BIT_NOT) @@ -171,6 +172,9 @@ std::string AST::type2str(AstNodeType type) X(AST_PACKAGE) X(AST_WIRETYPE) X(AST_TYPEDEF) + X(AST_STRUCT) + X(AST_UNION) + X(AST_STRUCT_ITEM) #undef X default: log_abort(); diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 8932108e3..9a5aa15f9 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -76,6 +76,7 @@ namespace AST AST_TO_SIGNED, AST_TO_UNSIGNED, AST_SELFSZ, + AST_CAST_SIZE, AST_CONCAT, AST_REPLICATE, AST_BIT_NOT, @@ -143,7 +144,7 @@ namespace AST AST_GENCASE, AST_GENBLOCK, AST_TECALL, - + AST_POSEDGE, AST_NEGEDGE, AST_EDGE, @@ -156,7 +157,10 @@ namespace AST AST_PACKAGE, AST_WIRETYPE, - AST_TYPEDEF + AST_TYPEDEF, + AST_STRUCT, + AST_UNION, + AST_STRUCT_ITEM }; struct AstSrcLocType { @@ -254,6 +258,7 @@ namespace AST bool mem2reg_check(pool<AstNode*> &mem2reg_set); void mem2reg_remove(pool<AstNode*> &mem2reg_set, vector<AstNode*> &delnodes); void meminfo(int &mem_width, int &mem_size, int &addr_bits); + bool detect_latch(const std::string &var); // additional functionality for evaluating constant functions struct varinfo_t { RTLIL::Const val; int offset; bool is_signed; }; @@ -306,6 +311,7 @@ namespace AST // helpers for enum void allocateDefaultEnumValues(); + void annotateTypedEnums(AstNode *template_node); }; // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code @@ -317,12 +323,12 @@ namespace AST struct AstModule : RTLIL::Module { AstNode *ast; bool nolatches, nomeminit, nomem2reg, mem2reg, noblackbox, lib, nowb, noopt, icells, pwires, autowire; - ~AstModule() YS_OVERRIDE; - RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, bool mayfail) YS_OVERRIDE; - RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, const dict<RTLIL::IdString, RTLIL::Module*> &interfaces, const dict<RTLIL::IdString, RTLIL::IdString> &modports, bool mayfail) YS_OVERRIDE; + ~AstModule() override; + RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, bool mayfail) override; + RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, const dict<RTLIL::IdString, RTLIL::Module*> &interfaces, const dict<RTLIL::IdString, RTLIL::IdString> &modports, bool mayfail) override; std::string derive_common(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, AstNode **new_ast_out, bool quiet = false); - void reprocess_module(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces) YS_OVERRIDE; - RTLIL::Module *clone() const YS_OVERRIDE; + void reprocess_module(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces) override; + RTLIL::Module *clone() const override; void loadconfig() const; }; diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index d4e9baa5f..e878d0dd2 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -814,6 +814,16 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun children.at(0)->detectSignWidthWorker(sub_width_hint, sign_hint); break; + case AST_CAST_SIZE: + while (children.at(0)->simplify(true, false, false, 1, -1, false, false)) { } + if (children.at(0)->type != AST_CONSTANT) + log_file_error(filename, location.first_line, "Static cast with non constant expression!\n"); + children.at(1)->detectSignWidthWorker(width_hint, sign_hint); + width_hint = children.at(0)->bitsAsConst().as_int(); + if (width_hint <= 0) + log_file_error(filename, location.first_line, "Static cast with zero or negative size!\n"); + break; + case AST_CONCAT: for (auto child : children) { sub_width_hint = 0; @@ -991,6 +1001,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_MODPORT: 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' @@ -1055,7 +1067,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (!range_valid) log_file_error(filename, location.first_line, "Signal `%s' with non-constant width!\n", str.c_str()); - if (!(range_left >= range_right || (range_left == -1 && range_right == 0))) + if (!(range_left + 1 >= range_right)) log_file_error(filename, location.first_line, "Signal `%s' with invalid width range %d!\n", str.c_str(), range_left - range_right + 1); RTLIL::Wire *wire = current_module->addWire(str, range_left - range_right + 1); @@ -1065,6 +1077,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) wire->port_input = is_input; wire->port_output = is_output; wire->upto = range_swapped; + wire->is_signed = is_signed; for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) @@ -1286,6 +1299,20 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) return sig; } + // changing the size of signal can be done directly using RTLIL::SigSpec + case AST_CAST_SIZE: { + RTLIL::SigSpec size = children[0]->genRTLIL(); + RTLIL::SigSpec sig = children[1]->genRTLIL(); + if (!size.is_fully_const()) + log_file_error(filename, location.first_line, "Static cast with non constant expression!\n"); + int width = size.as_int(); + if (width <= 0) + log_file_error(filename, location.first_line, "Static cast with zero or negative size!\n"); + sig.extend_u0(width, sign_hint); + is_signed = sign_hint; + return sig; + } + // concatenation of signals can be done directly using RTLIL::SigSpec case AST_CONCAT: { RTLIL::SigSpec sig; diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index f629df387..c4df5c0a0 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -168,6 +168,321 @@ 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 int range_width(AstNode *node, AstNode *rnode) +{ + log_assert(rnode->type==AST_RANGE); + if (!rnode->range_valid) { + log_file_error(node->filename, node->location.first_line, "Size must be constant in packed struct/union member %s\n", node->str.c_str()); + + } + // note: range swapping has already been checked for + return rnode->range_left - rnode->range_right + 1; +} + +[[noreturn]] static void struct_array_packing_error(AstNode *node) +{ + log_file_error(node->filename, node->location.first_line, "Unpacked array in packed struct/union member %s\n", node->str.c_str()); +} + +static void save_struct_array_width(AstNode *node, int width) +{ + // stash the stride for the array + node->multirange_dimensions.push_back(width); + +} + +static int get_struct_array_width(AstNode *node) +{ + // the stride for the array, 1 if not an array + return (node->multirange_dimensions.empty() ? 1 : node->multirange_dimensions.back()); + +} + +static 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() > 0 && node->children[0]->type == AST_RANGE) { + // member width e.g. bit [7:0] a + width = range_width(node, node->children[0]); + if (node->children.size() == 2) { + if (node->children[1]->type == AST_RANGE) { + // unpacked array e.g. bit [63:0] a [0:3] + auto rnode = node->children[1]; + int array_count = range_width(node, rnode); + if (array_count == 1) { + // C-type array size e.g. bit [63:0] a [4] + array_count = rnode->range_left; + } + save_struct_array_width(node, width); + width *= array_count; + } + else { + // array element must be single bit for a packed array + struct_array_packing_error(node); + } + } + // range nodes are now redundant + node->children.clear(); + } + else if (node->children.size() == 1 && node->children[0]->type == AST_MULTIRANGE) { + // packed 2D array, e.g. bit [3:0][63:0] a + auto rnode = node->children[0]; + if (rnode->children.size() != 2) { + // packed arrays can only be 2D + struct_array_packing_error(node); + } + int array_count = range_width(node, rnode->children[0]); + width = range_width(node, rnode->children[1]); + save_struct_array_width(node, width); + width *= array_count; + // 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); +} + +[[noreturn]] static void struct_op_error(AstNode *node) +{ + log_file_error(node->filename, node->location.first_line, "Unsupported operation for struct/union member %s\n", node->str.c_str()+1); +} + +static AstNode *node_int(int ival) +{ + // maybe mkconst_int should have default values for the common integer case + return AstNode::mkconst_int(ival, true, 32); +} + +static AstNode *offset_indexed_range(int offset_right, int stride, AstNode *left_expr, AstNode *right_expr) +{ + // adjust the range expressions to add an offset into the struct + // and maybe index using an array stride + auto left = left_expr->clone(); + auto right = right_expr->clone(); + if (stride == 1) { + // just add the offset + left = new AstNode(AST_ADD, node_int(offset_right), left); + right = new AstNode(AST_ADD, node_int(offset_right), right); + } + else { + // newleft = offset_right - 1 + (left + 1) * stride + left = new AstNode(AST_ADD, new AstNode(AST_SUB, node_int(offset_right), node_int(1)), + new AstNode(AST_MUL, node_int(stride), new AstNode(AST_ADD, left, node_int(1)))); + // newright = offset_right + right * stride + right = new AstNode(AST_ADD, node_int(offset_right), new AstNode(AST_MUL, right, node_int(stride))); + } + return new AstNode(AST_RANGE, left, right); +} + +static AstNode *make_struct_member_range(AstNode *node, AstNode *member_node) +{ + // Work out the range in the packed array that corresponds to a struct member + // taking into account any range operations applicable to the current node + // such as array indexing or slicing + int range_left = member_node->range_left; + int range_right = member_node->range_right; + if (node->children.empty()) { + // no range operations apply, return the whole width + } + else if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) { + auto rnode = node->children[0]; + int stride = get_struct_array_width(member_node); + if (rnode->children.size() == 1) { + // index e.g. s.a[i] + return offset_indexed_range(range_right, stride, rnode->children[0], rnode->children[0]); + } + else if (rnode->children.size() == 2) { + // slice e.g. s.a[i:j] + return offset_indexed_range(range_right, stride, rnode->children[0], rnode->children[1]); + } + else { + struct_op_error(node); + } + } + else { + // TODO multirange, i.e. bit slice after array index s.a[i][p:q] + struct_op_error(node); + } + return make_range(range_left, range_right); +} + +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 + auto wnode = new AstNode(AST_WIRE); + wnode->str = name; + wnode->is_logic = true; + wnode->range_valid = true; + 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 all the struct members to scope under the wire's name + add_members_to_scope(template_node, name); + return wnode; +} + // 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 // is done with an AST it can be converted into RTLIL using genRTLIL(). @@ -463,7 +778,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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 YS_ATTRIBUTE(unused) : node->children){ + 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; @@ -567,6 +882,32 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } break; + case AST_STRUCT: + case AST_UNION: + if (!basic_prep) { + for (auto *node : children) { + // resolve any ranges + while (!node->basic_prep && node->simplify(true, false, false, stage, -1, false, false)) { + did_something = true; + } + } + // 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; + + case AST_STRUCT_ITEM: + break; + case AST_ENUM: //log("\nENUM %s: %d child %d\n", str.c_str(), basic_prep, children[0]->basic_prep); if (!basic_prep) { @@ -609,6 +950,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, case AST_TO_SIGNED: case AST_TO_UNSIGNED: case AST_SELFSZ: + case AST_CAST_SIZE: case AST_CONCAT: case AST_REPLICATE: case AST_REDUCE_AND: @@ -785,6 +1127,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, bool in_param_here = in_param; if (i == 0 && (type == AST_REPLICATE || type == AST_WIRE)) const_fold_here = true, in_param_here = true; + if (i == 0 && (type == AST_GENIF || type == AST_GENCASE)) + in_param_here = true; + if (i == 1 && (type == AST_FOR || type == AST_GENFOR)) + in_param_here = true; if (type == AST_PARAMETER || type == AST_LOCALPARAM) const_fold_here = true; if (i == 0 && (type == AST_ASSIGN || type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE)) @@ -884,10 +1230,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // resolve typedefs if (type == AST_TYPEDEF) { log_assert(children.size() == 1); - log_assert(children[0]->type == AST_WIRE || children[0]->type == AST_MEMORY); - while(children[0]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) + auto type_node = children[0]; + 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; - log_assert(!children[0]->is_custom_type); + } + log_assert(!type_node->is_custom_type); } // resolve types of wires @@ -895,100 +1243,57 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (is_custom_type) { log_assert(children.size() >= 1); log_assert(children[0]->type == AST_WIRETYPE); - if (!current_scope.count(children[0]->str)) - log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", children[0]->str.c_str()); - AstNode *resolved_type = current_scope.at(children[0]->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()); - log_assert(resolved_type->children.size() == 1); - AstNode *templ = resolved_type->children[0]; + auto type_name = children[0]->str; + if (!current_scope.count(type_name)) { + log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", type_name.c_str()); + } + AstNode *resolved_type_node = current_scope.at(type_name); + if (resolved_type_node->type != AST_TYPEDEF) + 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 || template_node->type == AST_UNION) { + // replace with wire representing the packed structure + newNode = make_packed_struct(template_node, str); + current_scope[str] = this; + goto apply_newNode; + } + // Remove type reference delete children[0]; 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) - type = templ->type; - is_reg = templ->is_reg; - is_logic = templ->is_logic; - is_signed = templ->is_signed; - is_string = templ->is_string; - is_custom_type = templ->is_custom_type; - - range_valid = templ->range_valid; - range_swapped = templ->range_swapped; - range_left = templ->range_left; - range_right = templ->range_right; - attributes[ID::wiretype] = mkconst_str(resolved_type->str); - //check if enum - if (templ->attributes.count(ID::enum_type)){ - //get reference to enum node: - const std::string &enum_type = templ->attributes[ID::enum_type]->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] = mkconst_str(enum_item->str); - } - } + type = template_node->type; + is_reg = template_node->is_reg; + is_logic = template_node->is_logic; + is_signed = template_node->is_signed; + is_string = template_node->is_string; + is_custom_type = template_node->is_custom_type; + + range_valid = template_node->range_valid; + range_swapped = template_node->range_swapped; + range_left = template_node->range_left; + range_right = template_node->range_right; + + attributes[ID::wiretype] = mkconst_str(resolved_type_node->str); + + // if an enum then add attributes to support simulator tracing + annotateTypedEnums(template_node); // 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()); + for (int i = 0; i < GetSize(template_node->children); i++) + children.insert(children.begin() + i, template_node->children[i]->clone()); if (type == AST_MEMORY && GetSize(children) == 1) { // Single-bit memories must have [0:0] range - AstNode *rng = new AstNode(AST_RANGE); - rng->children.push_back(AstNode::mkconst_int(0, true)); - rng->children.push_back(AstNode::mkconst_int(0, true)); + AstNode *rng = make_range(0, 0); children.insert(children.begin(), rng); } - did_something = true; } log_assert(!is_custom_type); @@ -1001,29 +1306,29 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, log_assert(children[1]->type == AST_WIRETYPE); 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()); - AstNode *resolved_type = current_scope.at(children[1]->str); - if (resolved_type->type != AST_TYPEDEF) + AstNode *resolved_type_node = current_scope.at(children[1]->str); + 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_assert(resolved_type->children.size() == 1); - AstNode *templ = resolved_type->children[0]; + log_assert(resolved_type_node->children.size() == 1); + AstNode *template_node = resolved_type_node->children[0]; delete children[1]; children.pop_back(); // 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()); - is_signed = templ->is_signed; - is_string = templ->is_string; - is_custom_type = templ->is_custom_type; - - range_valid = templ->range_valid; - range_swapped = templ->range_swapped; - range_left = templ->range_left; - range_right = templ->range_right; - attributes[ID::wiretype] = mkconst_str(resolved_type->str); - for (auto template_child : templ->children) + is_signed = template_node->is_signed; + is_string = template_node->is_string; + is_custom_type = template_node->is_custom_type; + + range_valid = template_node->range_valid; + range_swapped = template_node->range_swapped; + range_left = template_node->range_left; + range_right = template_node->range_right; + attributes[ID::wiretype] = mkconst_str(resolved_type_node->str); + for (auto template_child : template_node->children) children.push_back(template_child->clone()); did_something = true; } @@ -1098,6 +1403,25 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, range_swapped = children[0]->range_swapped; range_left = children[0]->range_left; range_right = children[0]->range_right; + bool force_upto = false, force_downto = false; + if (attributes.count(ID::force_upto)) { + AstNode *val = attributes[ID::force_upto]; + if (val->type != AST_CONSTANT) + log_file_error(filename, location.first_line, "Attribute `force_upto' with non-constant value!\n"); + force_upto = val->asAttrConst().as_bool(); + } + if (attributes.count(ID::force_downto)) { + AstNode *val = attributes[ID::force_downto]; + if (val->type != AST_CONSTANT) + log_file_error(filename, location.first_line, "Attribute `force_downto' with non-constant value!\n"); + force_downto = val->asAttrConst().as_bool(); + } + if (force_upto && force_downto) + log_file_error(filename, location.first_line, "Attributes `force_downto' and `force_upto' cannot be both set!\n"); + if ((force_upto && !range_swapped) || (force_downto && range_swapped)) { + std::swap(range_left, range_right); + range_swapped = force_upto; + } } } else { if (!range_valid) @@ -1197,6 +1521,23 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } } + if (type == AST_IDENTIFIER && !basic_prep) { + // check if a plausible struct member sss.mmmm + std::string sname; + if (name_has_dot(str, sname)) { + if (current_scope.count(str) > 0) { + auto item_node = current_scope[str]; + if (item_node->type == AST_STRUCT_ITEM) { + // structure member, rewrite this node to reference the packed struct wire + auto range = make_struct_member_range(this, item_node); + newNode = new AstNode(AST_IDENTIFIER, range); + newNode->str = sname; + newNode->basic_prep = true; + goto apply_newNode; + } + } + } + } // annotate identifiers using scope resolution and create auto-wires as needed if (type == AST_IDENTIFIER) { if (current_scope.count(str) == 0) { @@ -1606,7 +1947,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, continue; buf = child->clone(); - while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + while (buf->simplify(true, false, false, stage, width_hint, sign_hint, true)) { } if (buf->type != AST_CONSTANT) { // for (auto f : log_files) // dumpAst(f, "verilog-ast> "); @@ -1800,6 +2141,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, use_case_method = true; } + if (!use_case_method && current_always->detect_latch(children[0]->str)) + use_case_method = true; + if (use_case_method) { // big case block @@ -3144,6 +3488,13 @@ replace_fcall_later:; } } break; + case AST_CAST_SIZE: + if (children.at(0)->type == AST_CONSTANT && children.at(1)->type == AST_CONSTANT) { + int width = children[0]->bitsAsConst().as_int(); + RTLIL::Const val = children[1]->bitsAsConst(width); + newNode = mkconst_bits(val.bits, children[1]->is_signed); + } + break; case AST_CONCAT: string_op = !children.empty(); for (auto it = children.begin(); it != children.end(); it++) { @@ -3521,8 +3872,8 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg } } - // also activate if requested, either by using mem2reg attribute or by declaring array as 'wire' instead of 'reg' - if (type == AST_MEMORY && (get_bool_attribute(ID::mem2reg) || (flags & AstNode::MEM2REG_FL_ALL) || !is_reg)) + // also activate if requested, either by using mem2reg attribute or by declaring array as 'wire' instead of 'reg' or 'logic' + if (type == AST_MEMORY && (get_bool_attribute(ID::mem2reg) || (flags & AstNode::MEM2REG_FL_ALL) || !(is_reg || is_logic))) mem2reg_candidates[this] |= AstNode::MEM2REG_FL_FORCED; if (type == AST_MODULE && get_bool_attribute(ID::mem2reg)) @@ -3868,6 +4219,60 @@ void AstNode::meminfo(int &mem_width, int &mem_size, int &addr_bits) addr_bits++; } +bool AstNode::detect_latch(const std::string &var) +{ + switch (type) + { + case AST_ALWAYS: + for (auto &c : children) + { + switch (c->type) + { + case AST_POSEDGE: + case AST_NEGEDGE: + return false; + case AST_BLOCK: + if (!c->detect_latch(var)) + return false; + break; + default: + log_abort(); + } + } + return true; + case AST_BLOCK: + for (auto &c : children) + if (!c->detect_latch(var)) + return false; + return true; + case AST_CASE: + { + bool r = true; + for (auto &c : children) { + if (c->type == AST_COND) { + if (c->children.at(1)->detect_latch(var)) + return true; + r = false; + } + if (c->type == AST_DEFAULT) { + if (c->children.at(0)->detect_latch(var)) + return true; + r = false; + } + } + return r; + } + case AST_ASSIGN_EQ: + case AST_ASSIGN_LE: + if (children.at(0)->type == AST_IDENTIFIER && + children.at(0)->children.empty() && children.at(0)->str == var) + return false; + return true; + default: + return true; + } +} + bool AstNode::has_const_only_constructs(bool &recommend_const_eval) { if (type == AST_FOR) diff --git a/frontends/blif/blifparse.cc b/frontends/blif/blifparse.cc index 7cc157e49..9ae3fac2c 100644 --- a/frontends/blif/blifparse.cc +++ b/frontends/blif/blifparse.cc @@ -586,7 +586,7 @@ error_with_reason: struct BlifFrontend : public Frontend { BlifFrontend() : Frontend("blif", "read BLIF file") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -602,7 +602,7 @@ struct BlifFrontend : public Frontend { log(" multi-bit port 'name'.\n"); log("\n"); } - void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override { bool sop_mode = false; bool wideports = false; diff --git a/frontends/ilang/ilang_frontend.cc b/frontends/ilang/ilang_frontend.cc index 30d9ff79d..973e62f2c 100644 --- a/frontends/ilang/ilang_frontend.cc +++ b/frontends/ilang/ilang_frontend.cc @@ -35,7 +35,7 @@ YOSYS_NAMESPACE_BEGIN struct IlangFrontend : public Frontend { IlangFrontend() : Frontend("ilang", "read modules from ilang file") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -56,7 +56,7 @@ struct IlangFrontend : public Frontend { log(" only create empty blackbox modules\n"); log("\n"); } - void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override { ILANG_FRONTEND::flag_nooverwrite = false; ILANG_FRONTEND::flag_overwrite = false; diff --git a/frontends/ilang/ilang_lexer.l b/frontends/ilang/ilang_lexer.l index 62f53d18e..3362ed641 100644 --- a/frontends/ilang/ilang_lexer.l +++ b/frontends/ilang/ilang_lexer.l @@ -91,8 +91,10 @@ USING_YOSYS_NAMESPACE [0-9]+'[01xzm-]* { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_VALUE; } -?[0-9]+ { char *end = nullptr; + errno = 0; long value = strtol(yytext, &end, 10); - if (end != yytext + strlen(yytext)) + log_assert(end == yytext + strlen(yytext)); + if (errno == ERANGE) return TOK_INVALID; // literal out of range of long if (value < INT_MIN || value > INT_MAX) return TOK_INVALID; // literal out of range of int (relevant mostly for LP64 platforms) diff --git a/frontends/ilang/ilang_parser.y b/frontends/ilang/ilang_parser.y index 118f13de9..879ef4af9 100644 --- a/frontends/ilang/ilang_parser.y +++ b/frontends/ilang/ilang_parser.y @@ -192,6 +192,9 @@ wire_options: wire_options TOK_UPTO { current_wire->upto = true; } | + wire_options TOK_SIGNED { + current_wire->is_signed = true; + } | wire_options TOK_OFFSET TOK_INT { current_wire->start_offset = $3; } | diff --git a/frontends/json/jsonparse.cc b/frontends/json/jsonparse.cc index 7aceffbfc..1b34aaf3a 100644 --- a/frontends/json/jsonparse.cc +++ b/frontends/json/jsonparse.cc @@ -309,6 +309,12 @@ void json_import(Design *design, string &modname, JsonNode *node) port_wire->upto = val->data_number != 0; } + if (port_node->data_dict.count("signed") != 0) { + JsonNode *val = port_node->data_dict.at("signed"); + if (val->type == 'N') + port_wire->is_signed = val->data_number != 0; + } + if (port_node->data_dict.count("offset") != 0) { JsonNode *val = port_node->data_dict.at("offset"); if (val->type == 'N') @@ -529,7 +535,7 @@ void json_import(Design *design, string &modname, JsonNode *node) struct JsonFrontend : public Frontend { JsonFrontend() : Frontend("json", "read JSON file") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -539,7 +545,7 @@ struct JsonFrontend : public Frontend { log("for a description of the file format.\n"); log("\n"); } - void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing JSON frontend.\n"); @@ -573,4 +579,3 @@ struct JsonFrontend : public Frontend { } JsonFrontend; YOSYS_NAMESPACE_END - diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc index 6f0c3fefa..f77d7da56 100644 --- a/frontends/liberty/liberty.cc +++ b/frontends/liberty/liberty.cc @@ -453,7 +453,7 @@ void parse_type_map(std::map<std::string, std::tuple<int, int, bool>> &type_map, struct LibertyFrontend : public Frontend { LibertyFrontend() : Frontend("liberty", "read cells from liberty file") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -486,7 +486,7 @@ struct LibertyFrontend : public Frontend { log(" set the specified attribute (to the value 1) on all loaded modules\n"); log("\n"); } - void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override { bool flag_lib = false; bool flag_nooverwrite = false; diff --git a/frontends/rpc/rpc_frontend.cc b/frontends/rpc/rpc_frontend.cc index 46ee6a733..6d72cbff5 100644 --- a/frontends/rpc/rpc_frontend.cc +++ b/frontends/rpc/rpc_frontend.cc @@ -157,7 +157,7 @@ struct RpcServer { struct RpcModule : RTLIL::Module { std::shared_ptr<RpcServer> server; - RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, bool /*mayfail*/) YS_OVERRIDE { + RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, bool /*mayfail*/) override { std::string stripped_name = name.str(); if (stripped_name.compare(0, 9, "$abstract") == 0) stripped_name = stripped_name.substr(9); @@ -229,7 +229,7 @@ struct RpcModule : RTLIL::Module { return derived_name; } - RTLIL::Module *clone() const YS_OVERRIDE { + RTLIL::Module *clone() const override { RpcModule *new_mod = new RpcModule; new_mod->server = server; cloneInto(new_mod); @@ -250,7 +250,7 @@ struct HandleRpcServer : RpcServer { HandleRpcServer(const std::string &name, HANDLE hsend, HANDLE hrecv) : RpcServer(name), hsend(hsend), hrecv(hrecv) { } - void write(const std::string &data) YS_OVERRIDE { + void write(const std::string &data) override { log_assert(data.length() >= 1 && data.find('\n') == data.length() - 1); ssize_t offset = 0; do { @@ -261,7 +261,7 @@ struct HandleRpcServer : RpcServer { } while(offset < (ssize_t)data.length()); } - std::string read() YS_OVERRIDE { + std::string read() override { std::string data; ssize_t offset = 0; while (data.length() == 0 || data[data.length() - 1] != '\n') { @@ -304,7 +304,7 @@ struct FdRpcServer : RpcServer { log_cmd_error("RPC frontend terminated unexpectedly\n"); } - void write(const std::string &data) YS_OVERRIDE { + void write(const std::string &data) override { log_assert(data.length() >= 1 && data.find('\n') == data.length() - 1); ssize_t offset = 0; do { @@ -316,7 +316,7 @@ struct FdRpcServer : RpcServer { } while(offset < (ssize_t)data.length()); } - std::string read() YS_OVERRIDE { + std::string read() override { std::string data; ssize_t offset = 0; while (data.length() == 0 || data[data.length() - 1] != '\n') { @@ -346,7 +346,7 @@ struct FdRpcServer : RpcServer { // RpcFrontend does not inherit from Frontend since it does not read files. struct RpcFrontend : public Pass { RpcFrontend() : Pass("connect_rpc", "connect to RPC frontend") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -390,7 +390,7 @@ struct RpcFrontend : public Pass { log(" so the response should be the same whenever the same set of parameters\n"); log(" is provided.\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Connecting to RPC frontend.\n"); diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index ae0970aac..0276618b4 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -54,7 +54,7 @@ USING_YOSYS_NAMESPACE # error "Only Symbiotic EDA flavored Verific is supported. Please contact office@symbioticeda.com for commercial support for Yosys+Verific." #endif -#if SYMBIOTIC_VERIFIC_API_VERSION < 1 +#if SYMBIOTIC_VERIFIC_API_VERSION < 202006 # error "Please update your version of Symbiotic EDA flavored Verific." #endif @@ -975,6 +975,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se module->memories[memory->name] = memory; int number_of_bits = net->Size(); + number_of_bits = 1 << ceil_log2(number_of_bits); int bits_in_word = number_of_bits; FOREACH_PORTREF_OF_NET(net, si, pr) { if (pr->GetInst()->Type() == OPER_READ_PORT) { @@ -1109,7 +1110,12 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se RTLIL::Wire *wire = module->addWire(wire_name, netbus->Size()); wire->start_offset = min(netbus->LeftIndex(), netbus->RightIndex()); - import_attributes(wire->attributes, netbus, nl); + MapIter mibus; + FOREACH_NET_OF_NETBUS(netbus, mibus, net) { + if (net) + import_attributes(wire->attributes, net, nl); + break; + } RTLIL::Const initval = Const(State::Sx, GetSize(wire)); bool initval_valid = false; @@ -1262,23 +1268,18 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se if (inst->Type() == OPER_READ_PORT) { - RTLIL::Memory *memory = module->memories.at(RTLIL::escape_id(inst->GetInput()->Name())); + RTLIL::Memory *memory = module->memories.at(RTLIL::escape_id(inst->GetInput()->Name()), nullptr); + if (!memory) + log_error("Memory net '%s' missing, possibly no driver, use verific -flatten.\n", inst->GetInput()->Name()); + int numchunks = int(inst->OutputSize()) / memory->width; int chunksbits = ceil_log2(numchunks); - if ((numchunks * memory->width) != int(inst->OutputSize())) - log_error("Import of asymmetric memories of this type is not supported yet: %s %s\n", inst->Name(), inst->GetInput()->Name()); - for (int i = 0; i < numchunks; i++) { RTLIL::SigSpec addr = {operatorInput1(inst), RTLIL::Const(i, chunksbits)}; RTLIL::SigSpec data = operatorOutput(inst).extract(i * memory->width, memory->width); - if ((numchunks & (numchunks - 1)) != 0) { - addr = module->Mul(NEW_ID, operatorInput1(inst), RTLIL::Const(numchunks)); - addr = module->Add(NEW_ID, addr, RTLIL::Const(i)); - } - RTLIL::Cell *cell = module->addCell(numchunks == 1 ? inst_name : RTLIL::IdString(stringf("%s_%d", inst_name.c_str(), i)), ID($memrd)); cell->parameters[ID::MEMID] = memory->name.str(); @@ -1297,23 +1298,17 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se if (inst->Type() == OPER_WRITE_PORT || inst->Type() == OPER_CLOCKED_WRITE_PORT) { - RTLIL::Memory *memory = module->memories.at(RTLIL::escape_id(inst->GetOutput()->Name())); + RTLIL::Memory *memory = module->memories.at(RTLIL::escape_id(inst->GetOutput()->Name()), nullptr); + if (!memory) + log_error("Memory net '%s' missing, possibly no driver, use verific -flatten.\n", inst->GetInput()->Name()); int numchunks = int(inst->Input2Size()) / memory->width; int chunksbits = ceil_log2(numchunks); - if ((numchunks * memory->width) != int(inst->Input2Size())) - log_error("Import of asymmetric memories of this type is not supported yet: %s %s\n", inst->Name(), inst->GetOutput()->Name()); - for (int i = 0; i < numchunks; i++) { RTLIL::SigSpec addr = {operatorInput1(inst), RTLIL::Const(i, chunksbits)}; RTLIL::SigSpec data = operatorInput2(inst).extract(i * memory->width, memory->width); - if ((numchunks & (numchunks - 1)) != 0) { - addr = module->Mul(NEW_ID, operatorInput1(inst), RTLIL::Const(numchunks)); - addr = module->Add(NEW_ID, addr, RTLIL::Const(i)); - } - RTLIL::Cell *cell = module->addCell(numchunks == 1 ? inst_name : RTLIL::IdString(stringf("%s_%d", inst_name.c_str(), i)), ID($memwr)); cell->parameters[ID::MEMID] = memory->name.str(); @@ -1903,7 +1898,7 @@ struct VerificExtNets new_net = new Net(name.c_str()); nl->Add(new_net); - Net *n YS_ATTRIBUTE(unused) = route_up(new_net, port->IsOutput(), ca_nl, ca_net); + Net *n = route_up(new_net, port->IsOutput(), ca_nl, ca_net); log_assert(n == ca_net); } @@ -2032,7 +2027,7 @@ bool check_noverific_env() struct VerificPass : public Pass { VerificPass() : Pass("verific", "load Verilog and VHDL designs using Verific") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -2171,7 +2166,7 @@ struct VerificPass : public Pass { log("\n"); } #ifdef YOSYS_ENABLE_VERIFIC - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { static bool set_verific_global_flags = true; @@ -2204,6 +2199,9 @@ struct VerificPass : public Pass { RuntimeFlags::SetVar("vhdl_support_variable_slice", 1); RuntimeFlags::SetVar("vhdl_ignore_assertion_statements", 0); + RuntimeFlags::SetVar("veri_preserve_assignments", 1); + RuntimeFlags::SetVar("vhdl_preserve_assignments", 1); + // Workaround for VIPER #13851 RuntimeFlags::SetVar("veri_create_name_for_unnamed_gen_block", 1); @@ -2608,7 +2606,7 @@ struct VerificPass : public Pass { } #else /* YOSYS_ENABLE_VERIFIC */ - void execute(std::vector<std::string>, RTLIL::Design *) YS_OVERRIDE { + void execute(std::vector<std::string>, RTLIL::Design *) override { log_cmd_error("This version of Yosys is built without Verific support.\n" "\n" "Use Symbiotic EDA Suite if you need Yosys+Verifc.\n" @@ -2622,7 +2620,7 @@ struct VerificPass : public Pass { struct ReadPass : public Pass { ReadPass() : Pass("read", "load HDL designs") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -2663,7 +2661,7 @@ struct ReadPass : public Pass { log("Verific support. The default is to use Verific if it is available.\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { #ifdef YOSYS_ENABLE_VERIFIC static bool verific_available = !check_noverific_env(); diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc index 7905ea598..ea23139e2 100644 --- a/frontends/verilog/preproc.cc +++ b/frontends/verilog/preproc.cc @@ -591,7 +591,7 @@ read_define_args() default: // The only FSM states are 0-2 and we dealt with 2 at the start of the loop. - __builtin_unreachable(); + log_assert(false); } } diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index 26abe49b5..2e9c9b2e2 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -67,7 +67,7 @@ static void add_package_types(dict<std::string, AST::AstNode *> &user_types, std struct VerilogFrontend : public Frontend { VerilogFrontend() : Frontend("verilog", "read modules from Verilog file") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -232,7 +232,7 @@ struct VerilogFrontend : public Frontend { log("supported by the Yosys Verilog front-end.\n"); log("\n"); } - void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override { bool flag_dump_ast1 = false; bool flag_dump_ast2 = false; @@ -503,7 +503,7 @@ struct VerilogFrontend : public Frontend { struct VerilogDefaults : public Pass { VerilogDefaults() : Pass("verilog_defaults", "set default options for read_verilog") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -524,7 +524,7 @@ struct VerilogDefaults : public Pass { log("not imply -clear.\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design*) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design*) override { if (args.size() < 2) cmd_error(args, 1, "Missing argument."); @@ -561,7 +561,7 @@ struct VerilogDefaults : public Pass { struct VerilogDefines : public Pass { VerilogDefines() : Pass("verilog_defines", "define and undefine verilog defines") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -583,7 +583,7 @@ struct VerilogDefines : public Pass { log(" list currently defined preprocessor symbols\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index f6a3ac4db..f2241066f 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -48,16 +48,18 @@ USING_YOSYS_NAMESPACE using namespace AST; using namespace VERILOG_FRONTEND; +#define YYSTYPE FRONTEND_VERILOG_YYSTYPE +#define YYLTYPE FRONTEND_VERILOG_YYLTYPE + YOSYS_NAMESPACE_BEGIN namespace VERILOG_FRONTEND { std::vector<std::string> fn_stack; std::vector<int> ln_stack; + YYLTYPE real_location; + YYLTYPE old_location; } YOSYS_NAMESPACE_END -#define YYSTYPE FRONTEND_VERILOG_YYSTYPE -#define YYLTYPE FRONTEND_VERILOG_YYLTYPE - #define SV_KEYWORD(_tok) \ if (sv_mode) return _tok; \ log("Lexer warning: The SystemVerilog keyword `%s' (at %s:%d) is not "\ @@ -73,9 +75,6 @@ YOSYS_NAMESPACE_END #define YY_INPUT(buf,result,max_size) \ result = readsome(*VERILOG_FRONTEND::lexin, buf, max_size) -YYLTYPE real_location; -YYLTYPE old_location; - #define YY_USER_ACTION \ old_location = real_location; \ real_location.first_line = real_location.last_line; \ @@ -128,7 +127,9 @@ static bool isUserType(std::string &s) %x BASED_CONST %% - int comment_caller; + // Initialise comment_caller to something to avoid a "maybe undefined" + // warning from GCC. + int comment_caller = INITIAL; <INITIAL,SYNOPSYS_TRANSLATE_OFF>"`file_push "[^\n]* { fn_stack.push_back(current_filename); @@ -262,7 +263,10 @@ static bool isUserType(std::string &s) "final" { SV_KEYWORD(TOK_FINAL); } "logic" { SV_KEYWORD(TOK_LOGIC); } "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); } +"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); } @@ -276,11 +280,15 @@ static bool isUserType(std::string &s) "reg" { return TOK_REG; } "integer" { return TOK_INTEGER; } "signed" { return TOK_SIGNED; } +"unsigned" { SV_KEYWORD(TOK_UNSIGNED); } "genvar" { return TOK_GENVAR; } "real" { return TOK_REAL; } "enum" { SV_KEYWORD(TOK_ENUM); } "typedef" { SV_KEYWORD(TOK_TYPEDEF); } +"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); @@ -509,6 +517,8 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ { "<<<" { return OP_SSHL; } ">>>" { return OP_SSHR; } +"'" { return OP_CAST; } + "::" { return TOK_PACKAGESEP; } "++" { return TOK_INCREMENT; } "--" { return TOK_DECREMENT; } @@ -518,6 +528,12 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ { ".*" { return TOK_WILDCARD_CONNECT; } +"|=" { SV_KEYWORD(TOK_OR_ASSIGN); } +"&=" { SV_KEYWORD(TOK_AND_ASSIGN); } +"+=" { SV_KEYWORD(TOK_PLUS_ASSIGN); } +"-=" { SV_KEYWORD(TOK_SUB_ASSIGN); } +"^=" { SV_KEYWORD(TOK_XOR_ASSIGN); } + [-+]?[=*]> { if (!specify_mode) REJECT; yylval->string = new std::string(yytext); diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index f250d7685..656910c0c 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -161,6 +161,23 @@ static bool isInLocalScope(const std::string *name) 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) { 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); 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} @@ -210,7 +256,7 @@ static void addRange(AstNode *parent, int msb = 31, int lsb = 0, bool isSigned = %token TOK_PACKAGE TOK_ENDPACKAGE TOK_PACKAGESEP %token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT TOK_VAR TOK_WILDCARD_CONNECT %token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_WAND TOK_WOR TOK_REG TOK_LOGIC -%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL +%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_PLUS_ASSIGN TOK_ALWAYS TOK_INITIAL %token TOK_ALWAYS_FF TOK_ALWAYS_COMB TOK_ALWAYS_LATCH %token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT %token TOK_DPI_FUNCTION TOK_POSEDGE TOK_NEGEDGE TOK_OR TOK_AUTOMATIC @@ -223,14 +269,17 @@ 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_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 TOK_SHORTINT TOK_UNION +%token TOK_OR_ASSIGN TOK_XOR_ASSIGN TOK_AND_ASSIGN TOK_SUB_ASSIGN %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 integral_number %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 <al> attr case_attr +%type <ast> struct_union %type <specify_target_ptr> specify_target %type <specify_triple_ptr> specify_triple specify_opt_triple @@ -250,6 +299,7 @@ static void addRange(AstNode *parent, int msb = 31, int lsb = 0, bool isSigned = %left '+' '-' %left '*' '/' '%' %left OP_POW +%left OP_CAST %right UNARY_OPS %define parse.error verbose @@ -387,7 +437,7 @@ module: mod->str = *$4; append_attr(mod, $1); delete $4; - } module_para_opt module_args_opt ';' module_body TOK_ENDMODULE { + } module_para_opt module_args_opt ';' module_body TOK_ENDMODULE opt_label { if (port_stubs.size() != 0) frontend_verilog_yyerror("Missing details for module port `%s'.", port_stubs.begin()->first.c_str()); @@ -508,7 +558,7 @@ package: current_ast_mod = mod; mod->str = *$4; append_attr(mod, $1); - } ';' package_body TOK_ENDPACKAGE { + } ';' package_body TOK_ENDPACKAGE opt_label { ast_stack.pop_back(); current_ast_mod = NULL; exitTypeScope(); @@ -520,9 +570,10 @@ package_body: ; package_body_stmt: - typedef_decl | - localparam_decl | - param_decl; + typedef_decl + | localparam_decl + | param_decl + ; interface: TOK_INTERFACE { @@ -582,6 +633,7 @@ wire_type_token_list: astbuf3->is_custom_type = true; astbuf3->children.push_back(new AstNode(AST_WIRETYPE)); astbuf3->children.back()->str = *$1; + delete $1; }; wire_type_token_io: @@ -682,15 +734,9 @@ range_or_multirange: non_opt_multirange { $$ = $1; }; range_or_signed_int: - range { - $$ = $1; - } | - 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; - }; + range { $$ = $1; } + | TOK_INTEGER { $$ = makeRange(); } + ; module_body: module_body module_body_stmt | @@ -700,8 +746,8 @@ 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; + enum_decl | struct_decl | + always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block | /* empty statement */ ';'; checker_decl: TOK_CHECKER TOK_ID ';' { @@ -841,18 +887,7 @@ task_func_port: } albuf = $1; astbuf1 = $2; - astbuf2 = $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>]"); + astbuf2 = checkRange(astbuf1, $3); } wire_name | { if (!astbuf1) { @@ -1296,6 +1331,8 @@ ignspec_id: param_signed: TOK_SIGNED { astbuf1->is_signed = true; + } | TOK_UNSIGNED { + astbuf1->is_signed = false; } | /* empty */; param_integer: @@ -1306,14 +1343,14 @@ param_integer: astbuf1->children.back()->children.push_back(AstNode::mkconst_int(31, true)); astbuf1->children.back()->children.push_back(AstNode::mkconst_int(0, true)); astbuf1->is_signed = true; - } | /* empty */; + } param_real: TOK_REAL { if (astbuf1->children.size() != 1) frontend_verilog_yyerror("Parameter already declared as integer, cannot set to real."); astbuf1->children.push_back(new AstNode(AST_REALVALUE)); - } | /* empty */; + } param_range: range { @@ -1324,8 +1361,12 @@ param_range: } }; +param_integer_type: param_integer param_signed +param_range_type: type_vec param_signed param_range +param_implicit_type: param_signed param_range + param_type: - param_signed param_integer param_real param_range | + param_integer_type | param_real | param_range_type | param_implicit_type | hierarchical_type_id { astbuf1->is_custom_type = true; astbuf1->children.push_back(new AstNode(AST_WIRETYPE)); @@ -1387,6 +1428,10 @@ single_defparam_decl: ast_stack.back()->children.push_back(node); }; +///////// +// enum +///////// + enum_type: TOK_ENUM { static int enum_count; // create parent node for the enum @@ -1397,31 +1442,40 @@ enum_type: TOK_ENUM { // 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[ID::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 '{' enum_name_list '}' { // create template for the enum vars + auto tnode = astbuf1->clone(); + delete astbuf1; + astbuf1 = tnode; + tnode->type = AST_WIRE; + tnode->attributes[ID::enum_type] = AstNode::mkconst_str(astbuf2->str); + // drop constant but keep any range + delete tnode->children[0]; + tnode->children.erase(tnode->children.begin()); + $$ = astbuf1; } ; -enum_base_type: int_vec param_range - | int_atom - | /* nothing */ {astbuf1->is_reg = true; addRange(astbuf1); } +enum_base_type: type_atom type_signing + | type_vec type_signing range { if ($3) astbuf1->children.push_back($3); } + | /* 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_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 ; -int_vec: TOK_REG {astbuf1->is_reg = true;} - | TOK_LOGIC {astbuf1->is_logic = true;} +type_vec: TOK_REG { astbuf1->is_reg = true; } // unsigned + | TOK_LOGIC { astbuf1->is_logic = true; } // unsigned ; -enum_name_list: - enum_name_decl +type_signing: + 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 ; @@ -1433,8 +1487,9 @@ 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); + node->children[0] = $2 ? $2 : new AstNode(AST_NONE); astbuf2->children.push_back(node); } ; @@ -1456,32 +1511,122 @@ 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; } ; -enum_decl: enum_type enum_var_list ';' { - //enum_type creates astbuf1 for use by typedef only - delete astbuf1; - } +enum_decl: enum_type enum_var_list ';' { delete $1; } + ; + +////////////////// +// struct or union +////////////////// + +struct_decl: struct_type struct_var_list ';' { delete astbuf2; } ; +struct_type: struct_union { astbuf2 = $1; } struct_body { $$ = astbuf2; } + ; + +struct_union: + TOK_STRUCT { $$ = new AstNode(AST_STRUCT); } + | TOK_UNION { $$ = new AstNode(AST_UNION); } + ; + +struct_body: opt_packed '{' struct_member_list '}' + ; + +opt_packed: TOK_PACKED opt_signed_struct + | { frontend_verilog_yyerror("Only PACKED supported at this time"); } + ; + +opt_signed_struct: + TOK_SIGNED { astbuf2->is_signed = true; } + | TOK_UNSIGNED { astbuf2->is_signed = false; } + | // 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; + astbuf3 = astbuf1->clone(); + SET_AST_NODE_LOC(astbuf3, @1, @1); + astbuf2->children.push_back(astbuf3); + } range { if ($3) astbuf3->children.push_back($3); } + ; + +struct_member_type: { astbuf1 = new AstNode(AST_STRUCT_ITEM); } member_type_token + ; + +member_type_token: + member_type + | hierarchical_type_id { + // use a clone of the typedef definition nodes + auto template_node = copyTypeDefinition(*$1); + 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()); + } + delete astbuf1; + astbuf1 = template_node; + } + | struct_union { + // stash state on ast_stack + ast_stack.push_back(astbuf2); + astbuf2 = $1; + } struct_body { + astbuf1 = astbuf2; + // recover state + astbuf2 = ast_stack.back(); + ast_stack.pop_back(); + } + ; + +member_type: type_atom type_signing + | type_vec type_signing range_or_multirange { 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; + SET_AST_NODE_LOC(var_node, @1, @1); + ast_stack.back()->children.push_back(var_node); + } + ; + +///////// +// wire +///////// + wire_decl: attr wire_type range { albuf = $1; astbuf1 = $2; - astbuf2 = $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>]"); + astbuf2 = checkRange(astbuf1, $3); } delay wire_name_list { delete astbuf1; if (astbuf2 != NULL) @@ -1603,19 +1748,9 @@ wire_name: if (node->is_input || node->is_output) frontend_verilog_yyerror("input/output/inout ports cannot have unpacked dimensions."); if (!astbuf2 && !node->is_custom_type) { - AstNode *rng = new AstNode(AST_RANGE); - rng->children.push_back(AstNode::mkconst_int(0, true)); - rng->children.push_back(AstNode::mkconst_int(0, true)); - node->children.push_back(rng); + addRange(node, 0, 0, false); } - node->type = AST_MEMORY; - 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); + rewriteAsMemoryNode(node, $2); } if (current_function_or_task == NULL) { if (do_not_require_port_stubs && (node->is_input || node->is_output) && port_stubs.count(*$1) == 0) { @@ -1663,42 +1798,23 @@ type_name: TOK_ID // first time seen typedef_decl: TOK_TYPEDEF wire_type range type_name range_or_multirange ';' { astbuf1 = $2; - astbuf2 = $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>]"); + astbuf2 = checkRange(astbuf1, $3); if (astbuf2) astbuf1->children.push_back(astbuf2); if ($5 != NULL) { if (!astbuf2) { - AstNode *rng = new AstNode(AST_RANGE); - rng->children.push_back(AstNode::mkconst_int(0, true)); - rng->children.push_back(AstNode::mkconst_int(0, true)); - astbuf1->children.push_back(rng); + addRange(astbuf1, 0, 0, false); } - astbuf1->type = AST_MEMORY; - 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); + rewriteAsMemoryNode(astbuf1, $5); } - addTypedefNode($4, astbuf1); - } | - TOK_TYPEDEF enum_type type_name ';' { - addTypedefNode($3, astbuf1); - } + addTypedefNode($4, astbuf1); } + | TOK_TYPEDEF non_wire_data_type type_name ';' { addTypedefNode($3, $2); } + ; + +non_wire_data_type: + enum_type + | struct_type ; cell_stmt: @@ -2203,49 +2319,96 @@ assert_property: }; simple_behavioral_stmt: - lvalue '=' delay expr { - AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, $4); + attr lvalue '=' delay expr { + AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, $5); ast_stack.back()->children.push_back(node); - SET_AST_NODE_LOC(node, @1, @4); + SET_AST_NODE_LOC(node, @2, @5); + append_attr(node, $1); } | - lvalue TOK_INCREMENT { - AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, new AstNode(AST_ADD, $1->clone(), AstNode::mkconst_int(1, true))); + attr lvalue TOK_INCREMENT { + AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, new AstNode(AST_ADD, $2->clone(), AstNode::mkconst_int(1, true))); ast_stack.back()->children.push_back(node); - SET_AST_NODE_LOC(node, @1, @2); + SET_AST_NODE_LOC(node, @2, @3); + append_attr(node, $1); } | - lvalue TOK_DECREMENT { - AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, new AstNode(AST_SUB, $1->clone(), AstNode::mkconst_int(1, true))); + attr lvalue TOK_DECREMENT { + AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, new AstNode(AST_SUB, $2->clone(), AstNode::mkconst_int(1, true))); ast_stack.back()->children.push_back(node); - SET_AST_NODE_LOC(node, @1, @2); + SET_AST_NODE_LOC(node, @2, @3); + append_attr(node, $1); + } | + attr lvalue OP_LE delay expr { + AstNode *node = new AstNode(AST_ASSIGN_LE, $2, $5); + ast_stack.back()->children.push_back(node); + SET_AST_NODE_LOC(node, @2, @5); + append_attr(node, $1); + } | + attr lvalue TOK_XOR_ASSIGN delay expr { + AstNode *xor_node = new AstNode(AST_BIT_XOR, $2->clone(), $5); + AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, xor_node); + SET_AST_NODE_LOC(xor_node, @2, @5); + SET_AST_NODE_LOC(node, @2, @5); + ast_stack.back()->children.push_back(node); + append_attr(node, $1); + } | + attr lvalue TOK_OR_ASSIGN delay expr { + AstNode *or_node = new AstNode(AST_BIT_OR, $2->clone(), $5); + SET_AST_NODE_LOC(or_node, @2, @5); + AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, or_node); + SET_AST_NODE_LOC(node, @2, @5); + ast_stack.back()->children.push_back(node); + append_attr(node, $1); + } | + attr lvalue TOK_PLUS_ASSIGN delay expr { + AstNode *add_node = new AstNode(AST_ADD, $2->clone(), $5); + AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, add_node); + SET_AST_NODE_LOC(node, @2, @5); + SET_AST_NODE_LOC(add_node, @2, @5); + ast_stack.back()->children.push_back(node); + append_attr(node, $1); + } | + attr lvalue TOK_SUB_ASSIGN delay expr { + AstNode *sub_node = new AstNode(AST_SUB, $2->clone(), $5); + AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, sub_node); + SET_AST_NODE_LOC(node, @2, @5); + SET_AST_NODE_LOC(sub_node, @2, @5); + ast_stack.back()->children.push_back(node); + append_attr(node, $1); } | - lvalue OP_LE delay expr { - AstNode *node = new AstNode(AST_ASSIGN_LE, $1, $4); + attr lvalue TOK_AND_ASSIGN delay expr { + AstNode *and_node = new AstNode(AST_BIT_AND, $2->clone(), $5); + AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, and_node); + SET_AST_NODE_LOC(node, @2, @5); + SET_AST_NODE_LOC(and_node, @2, @5); ast_stack.back()->children.push_back(node); - SET_AST_NODE_LOC(node, @1, @4); + append_attr(node, $1); }; // this production creates the obligatory if-else shift/reduce conflict behavioral_stmt: defattr | assert | wire_decl | param_decl | localparam_decl | typedef_decl | non_opt_delay behavioral_stmt | - simple_behavioral_stmt ';' | ';' | - hierarchical_id attr { + simple_behavioral_stmt ';' | + attr ';' { + free_attr($1); + } | + attr hierarchical_id { AstNode *node = new AstNode(AST_TCALL); - node->str = *$1; - delete $1; + node->str = *$2; + delete $2; ast_stack.back()->children.push_back(node); ast_stack.push_back(node); - append_attr(node, $2); + append_attr(node, $1); } opt_arg_list ';'{ ast_stack.pop_back(); } | - TOK_MSG_TASKS attr { + attr TOK_MSG_TASKS { AstNode *node = new AstNode(AST_TCALL); - node->str = *$1; - delete $1; + node->str = *$2; + delete $2; ast_stack.back()->children.push_back(node); ast_stack.push_back(node); - append_attr(node, $2); + append_attr(node, $1); } opt_arg_list ';'{ ast_stack.pop_back(); } | @@ -2342,8 +2505,6 @@ behavioral_stmt: ast_stack.pop_back(); }; - ; - unique_case_attr: /* empty */ { $$ = false; @@ -2438,7 +2599,7 @@ gen_case_item: } case_select { case_type_stack.push_back(0); SET_AST_NODE_LOC(ast_stack.back(), @2, @2); - } gen_stmt_or_null { + } gen_stmt_block { case_type_stack.pop_back(); ast_stack.pop_back(); }; @@ -2530,7 +2691,10 @@ module_gen_body: /* empty */; gen_stmt_or_module_body_stmt: - gen_stmt | module_body_stmt; + gen_stmt | module_body_stmt | + attr ';' { + free_attr($1); + }; // this production creates the obligatory if-else shift/reduce conflict gen_stmt: @@ -2552,7 +2716,7 @@ gen_stmt: AstNode *block = new AstNode(AST_GENBLOCK); ast_stack.back()->children.push_back(block); ast_stack.push_back(block); - } gen_stmt_or_null { + } gen_stmt_block { ast_stack.pop_back(); } opt_gen_else { SET_AST_NODE_LOC(ast_stack.back(), @1, @7); @@ -2602,11 +2766,8 @@ gen_stmt_block: ast_stack.pop_back(); }; -gen_stmt_or_null: - gen_stmt_block | ';'; - opt_gen_else: - TOK_ELSE gen_stmt_or_null | /* empty */ %prec FAKE_THEN; + TOK_ELSE gen_stmt_block | /* empty */ %prec FAKE_THEN; expr: basic_expr { @@ -2888,6 +3049,24 @@ basic_expr: $$ = new AstNode(AST_LOGIC_NOT, $3); SET_AST_NODE_LOC($$, @1, @3); append_attr($$, $2); + } | + TOK_SIGNED OP_CAST '(' expr ')' { + if (!sv_mode) + frontend_verilog_yyerror("Static cast is only supported in SystemVerilog mode."); + $$ = new AstNode(AST_TO_SIGNED, $4); + SET_AST_NODE_LOC($$, @1, @4); + } | + TOK_UNSIGNED OP_CAST '(' expr ')' { + if (!sv_mode) + frontend_verilog_yyerror("Static cast is only supported in SystemVerilog mode."); + $$ = new AstNode(AST_TO_UNSIGNED, $4); + SET_AST_NODE_LOC($$, @1, @4); + } | + basic_expr OP_CAST '(' expr ')' { + if (!sv_mode) + frontend_verilog_yyerror("Static cast is only supported in SystemVerilog mode."); + $$ = new AstNode(AST_CAST_SIZE, $1, $4); + SET_AST_NODE_LOC($$, @1, @4); }; concat_list: |