systemverilog-plugin: Adjusting simplify.cc, removing unneded code
Signed-off-by: Magdalena Andrys <mandrys@antmicro.com>
diff --git a/systemverilog-plugin/third_party/yosys/simplify.cc b/systemverilog-plugin/third_party/yosys/simplify.cc
index 78e5768..71d15a4 100644
--- a/systemverilog-plugin/third_party/yosys/simplify.cc
+++ b/systemverilog-plugin/third_party/yosys/simplify.cc
@@ -28,8 +28,8 @@
#include "kernel/log.h"
#include "libs/sha1/sha1.h"
-#include "frontends/verilog/verilog_frontend.h"
-#include "ast.h"
+
+#include "const2ast.h"
#include <sstream>
#include <stdarg.h>
@@ -37,149 +37,22 @@
#include <math.h>
YOSYS_NAMESPACE_BEGIN
-
-using namespace AST;
-using namespace AST_INTERNAL;
-
-// Process a format string and arguments for $display, $write, $sprintf, etc
-
-std::string AstNode::process_format_str(const std::string &sformat, int next_arg, int stage, int width_hint, bool sign_hint) {
- // Other arguments are placeholders. Process the string as we go through it
- std::string sout;
- for (size_t i = 0; i < sformat.length(); i++)
- {
- // format specifier
- if (sformat[i] == '%')
- {
- // If there's no next character, that's a problem
- if (i+1 >= sformat.length())
- log_file_error(filename, location.first_line, "System task `%s' called with `%%' at end of string.\n", str.c_str());
-
- char cformat = sformat[++i];
-
- // %% is special, does not need a matching argument
- if (cformat == '%')
- {
- sout += '%';
- continue;
- }
-
- bool got_len = false;
- bool got_zlen = false;
- int len_value = 0;
-
- while ('0' <= cformat && cformat <= '9')
- {
- if (!got_len && cformat == '0')
- got_zlen = true;
-
- got_len = true;
- len_value = 10*len_value + (cformat - '0');
-
- cformat = sformat[++i];
- }
-
- // Simplify the argument
- AstNode *node_arg = nullptr;
-
- // Everything from here on depends on the format specifier
- switch (cformat)
- {
- case 's':
- case 'S':
- case 'd':
- case 'D':
- if (got_len && len_value != 0)
- goto unsupported_format;
- YS_FALLTHROUGH
- case 'x':
- case 'X':
- if (next_arg >= GetSize(children))
- log_file_error(filename, location.first_line, "Missing argument for %%%c format specifier in system task `%s'.\n",
- cformat, str.c_str());
-
- node_arg = children[next_arg++];
- while (node_arg->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
- if (node_arg->type != AST_CONSTANT)
- log_file_error(filename, location.first_line, "Failed to evaluate system task `%s' with non-constant argument.\n", str.c_str());
- break;
-
- case 'm':
- case 'M':
- if (got_len)
- goto unsupported_format;
- break;
-
- case 'l':
- case 'L':
- if (got_len)
- goto unsupported_format;
- break;
-
- default:
- unsupported_format:
- log_file_error(filename, location.first_line, "System task `%s' called with invalid/unsupported format specifier.\n", str.c_str());
- break;
- }
-
- switch (cformat)
- {
- case 's':
- case 'S':
- sout += node_arg->bitsAsConst().decode_string();
- break;
-
- case 'd':
- case 'D':
- sout += stringf("%d", node_arg->bitsAsConst().as_int());
- break;
-
- case 'x':
- case 'X':
- {
- Const val = node_arg->bitsAsConst();
-
- while (GetSize(val) % 4 != 0)
- val.bits.push_back(State::S0);
-
- int len = GetSize(val) / 4;
- for (int i = len; i < len_value; i++)
- sout += got_zlen ? '0' : ' ';
-
- for (int i = len-1; i >= 0; i--) {
- Const digit = val.extract(4*i, 4);
- if (digit.is_fully_def())
- sout += stringf(cformat == 'x' ? "%x" : "%X", digit.as_int());
- else
- sout += cformat == 'x' ? "x" : "X";
- }
- }
- break;
-
- case 'm':
- case 'M':
- sout += log_id(current_module->name);
- break;
-
- case 'l':
- case 'L':
- sout += log_id(current_module->name);
- break;
-
- default:
- log_abort();
- }
- }
-
- // not a format specifier
- else
- sout += sformat[i];
- }
- return sout;
+namespace VERILOG_FRONTEND
+{
+extern bool sv_mode;
}
+YOSYS_NAMESPACE_END
+namespace systemverilog_plugin
+{
-void AstNode::annotateTypedEnums(AstNode *template_node)
+using namespace ::Yosys;
+using namespace ::Yosys::AST;
+using namespace ::Yosys::AST_INTERNAL;
+
+bool simplify(AstNode *ast_node, bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param);
+
+void annotateTypedEnums(AstNode *ast_node, AstNode *template_node)
{
//check if enum
if (template_node->attributes.count(ID::enum_type)) {
@@ -192,7 +65,7 @@
log_assert(current_scope.count(enum_type) == 1);
AstNode *enum_node = current_scope.at(enum_type);
log_assert(enum_node->type == AST_ENUM);
- while (enum_node->simplify(true, false, false, 1, -1, false, true)) { }
+ while (simplify(enum_node, true, false, false, 1, -1, false, true)) { }
//get width from 1st enum item:
log_assert(enum_node->children.size() >= 1);
AstNode *enum_item0 = enum_node->children[0];
@@ -233,7 +106,7 @@
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);
+ ast_node->attributes[enum_item_str.c_str()] = AstNode::mkconst_str(enum_item->str);
}
}
}
@@ -407,120 +280,7 @@
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)
-{
- return AstNode::mkconst_int(ival, true);
-}
-
-static AstNode *multiply_by_const(AstNode *expr_node, int stride)
-{
- return new AstNode(AST_MUL, expr_node, node_int(stride));
-}
-
-static AstNode *normalize_struct_index(AstNode *expr, AstNode *member_node, int dimension)
-{
- expr = expr->clone();
-
- int offset = get_struct_range_offset(member_node, dimension);
- if (offset) {
- expr = new AstNode(AST_SUB, expr, node_int(offset));
- }
-
- if (member_node->multirange_swapped[dimension]) {
- // The dimension has swapped range; swap index into the struct accordingly.
- int msb = get_struct_range_width(member_node, dimension) - 1;
- expr = new AstNode(AST_SUB, node_int(msb), expr);
- }
-
- return expr;
-}
-
-static AstNode *struct_index_lsb_offset(AstNode *lsb_offset, AstNode *rnode, AstNode *member_node, int dimension, int &stride)
-{
- stride /= get_struct_range_width(member_node, dimension);
- auto right = normalize_struct_index(rnode->children.back(), member_node, dimension);
- auto offset = stride > 1 ? multiply_by_const(right, stride) : right;
- return lsb_offset ? new AstNode(AST_ADD, lsb_offset, offset) : offset;
-}
-
-static AstNode *struct_index_msb_offset(AstNode *lsb_offset, AstNode *rnode, AstNode *member_node, int dimension, int stride)
-{
- log_assert(rnode->children.size() <= 2);
-
- // Offset to add to LSB
- AstNode *offset;
- if (rnode->children.size() == 1) {
- // Index, e.g. s.a[i]
- offset = node_int(stride - 1);
- }
- else {
- // rnode->children.size() == 2
- // Slice, e.g. s.a[i:j]
- auto left = normalize_struct_index(rnode->children[0], member_node, dimension);
- auto right = normalize_struct_index(rnode->children[1], member_node, dimension);
- offset = new AstNode(AST_SUB, left, right);
- if (stride > 1) {
- // offset = (msb - lsb + 1)*stride - 1
- auto slice_width = new AstNode(AST_ADD, offset, node_int(1));
- offset = new AstNode(AST_SUB, multiply_by_const(slice_width, stride), node_int(1));
- }
- }
-
- return new AstNode(AST_ADD, lsb_offset, offset);
-}
-
-
-AstNode *AST::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
- return make_range(range_left - range_right, 0);
- }
-
- if (node->children.size() != 1) {
- struct_op_error(node);
- }
-
- // Range operations
- auto rnode = node->children[0];
- AstNode *lsb_offset = NULL;
- int stride = range_left - range_right + 1;
- size_t i = 0;
-
- // Calculate LSB offset for the final index / slice
- if (rnode->type == AST_RANGE) {
- lsb_offset = struct_index_lsb_offset(lsb_offset, rnode, member_node, i, stride);
- }
- else if (rnode->type == AST_MULTIRANGE) {
- // Add offset for each dimension
- auto mrnode = rnode;
- for (i = 0; i < mrnode->children.size(); i++) {
- rnode = mrnode->children[i];
- lsb_offset = struct_index_lsb_offset(lsb_offset, rnode, member_node, i, stride);
- }
- i--; // Step back to the final index / slice
- }
- else {
- struct_op_error(node);
- }
-
- // Calculate MSB offset for the final index / slice
- auto msb_offset = struct_index_msb_offset(lsb_offset->clone(), rnode, member_node, i, stride);
-
- return new AstNode(AST_RANGE, msb_offset, lsb_offset);
-}
-
-AstNode *AST::get_struct_member(const AstNode *node)
+AstNode get_struct_member(const AstNode *node)
{
AST::AstNode *member_node;
if (node->attributes.count(ID::wiretype) && (member_node = node->attributes.at(ID::wiretype)) &&
@@ -606,84 +366,6 @@
return prefix + str;
}
-// direct access to this global should be limited to the following two functions
-static const RTLIL::Design *simplify_design_context = nullptr;
-
-void AST::set_simplify_design_context(const RTLIL::Design *design)
-{
- log_assert(!simplify_design_context || !design);
- simplify_design_context = design;
-}
-
-// lookup the module with the given name in the current design context
-static const RTLIL::Module* lookup_module(const std::string &name)
-{
- return simplify_design_context->module(name);
-}
-
-const RTLIL::Module* AstNode::lookup_cell_module()
-{
- log_assert(type == AST_CELL);
-
- auto reprocess_after = [this] (const std::string &modname) {
- if (!attributes.count(ID::reprocess_after))
- attributes[ID::reprocess_after] = AstNode::mkconst_str(modname);
- };
-
- const AstNode *celltype = nullptr;
- for (const AstNode *child : children)
- if (child->type == AST_CELLTYPE) {
- celltype = child;
- break;
- }
- log_assert(celltype != nullptr);
-
- const RTLIL::Module *module = lookup_module(celltype->str);
- if (!module)
- module = lookup_module("$abstract" + celltype->str);
- if (!module) {
- if (celltype->str.at(0) != '$')
- reprocess_after(celltype->str);
- return nullptr;
- }
-
- // build a mapping from true param name to param value
- size_t para_counter = 0;
- dict<RTLIL::IdString, RTLIL::Const> cell_params_map;
- for (AstNode *child : children) {
- if (child->type != AST_PARASET)
- continue;
-
- if (child->str.empty() && para_counter >= module->avail_parameters.size())
- return nullptr; // let hierarchy handle this error
- IdString paraname = child->str.empty() ? module->avail_parameters[para_counter++] : child->str;
-
- const AstNode *value = child->children[0];
- if (value->type != AST_REALVALUE && value->type != AST_CONSTANT)
- return nullptr; // let genrtlil handle this error
- cell_params_map[paraname] = value->asParaConst();
- }
-
- // put the parameters in order and generate the derived module name
- std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> named_parameters;
- for (RTLIL::IdString param : module->avail_parameters) {
- auto it = cell_params_map.find(param);
- if (it != cell_params_map.end())
- named_parameters.emplace_back(it->first, it->second);
- }
- std::string modname = celltype->str;
- if (cell_params_map.size()) // not named_parameters to cover hierarchical defparams
- modname = derived_module_name(celltype->str, named_parameters);
-
- // try to find the resolved module
- module = lookup_module(modname);
- if (!module) {
- reprocess_after(modname);
- return nullptr;
- }
- return module;
-}
-
// returns whether an expression contains an unbased unsized literal; does not
// check the literal exists in a self-determined context
static bool contains_unbased_unsized(const AstNode *node)
@@ -837,6 +519,21 @@
check_auto_nosync(child);
}
+static inline std::string encode_filename(const std::string &filename)
+{
+ std::stringstream val;
+ if (!std::any_of(filename.begin(), filename.end(), [](char c) {
+ return static_cast<unsigned char>(c) < 33 || static_cast<unsigned char>(c) > 126;
+ })) return filename;
+ for (unsigned char const c : filename) {
+ if (c < 33 || c > 126)
+ val << stringf("$%02x", c);
+ else
+ val << c;
+ }
+ return val.str();
+}
+
// 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().
@@ -1280,7 +977,7 @@
// create the indirection wire
std::stringstream sstr;
- sstr << "$indirect$" << ref->name.c_str() << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++);
+ sstr << "$indirect$" << ref->name.c_str() << "$" << encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++);
std::string tmp_str = sstr.str();
add_wire_for_ref(ref, tmp_str);
@@ -1825,7 +1522,7 @@
attributes[ID::wiretype] = mkconst_str(resolved_type_node->str);
// if an enum then add attributes to support simulator tracing
- annotateTypedEnums(template_node);
+ annotateTypedEnums(ast_node, template_node);
// Insert clones children from template at beginning
for (int i = 0; i < GetSize(template_node->children); i++)
@@ -2181,7 +1878,7 @@
std::swap(data_range_left, data_range_right);
std::stringstream sstr;
- sstr << "$mem2bits$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++);
+ sstr << "$mem2bits$" << str << "$" << encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++);
std::string wire_id = sstr.str();
AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(data_range_left, true), mkconst_int(data_range_right, true)));
@@ -2777,14 +2474,14 @@
// mask and shift operations, disabled for now
AstNode *wire_mask = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(source_width-1, true), mkconst_int(0, true)));
- wire_mask->str = stringf("$bitselwrite$mask$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
+ wire_mask->str = stringf("$bitselwrite$mask$%s:%d$%d", encode_filename(filename).c_str(), location.first_line, autoidx++);
wire_mask->attributes[ID::nosync] = AstNode::mkconst_int(1, false);
wire_mask->is_logic = true;
while (wire_mask->simplify(true, false, false, 1, -1, false, false)) { }
current_ast_mod->children.push_back(wire_mask);
AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(source_width-1, true), mkconst_int(0, true)));
- wire_data->str = stringf("$bitselwrite$data$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
+ wire_data->str = stringf("$bitselwrite$data$%s:%d$%d", encode_filename(filename).c_str(), location.first_line, autoidx++);
wire_data->attributes[ID::nosync] = AstNode::mkconst_int(1, false);
wire_data->is_logic = true;
while (wire_data->simplify(true, false, false, 1, -1, false, false)) { }
@@ -2795,7 +2492,7 @@
shift_expr->detectSignWidth(shamt_width_hint, shamt_sign_hint);
AstNode *wire_sel = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(shamt_width_hint-1, true), mkconst_int(0, true)));
- wire_sel->str = stringf("$bitselwrite$sel$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
+ wire_sel->str = stringf("$bitselwrite$sel$%s:%d$%d", encode_filename(filename).c_str(), location.first_line, autoidx++);
wire_sel->attributes[ID::nosync] = AstNode::mkconst_int(1, false);
wire_sel->is_logic = true;
wire_sel->is_signed = shamt_sign_hint;
@@ -2874,7 +2571,7 @@
if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME || type == AST_LIVE || type == AST_FAIR || type == AST_COVER) && current_block != NULL)
{
std::stringstream sstr;
- sstr << "$formal$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++);
+ sstr << "$formal$" << encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++);
std::string id_check = sstr.str() + "_CHECK", id_en = sstr.str() + "_EN";
AstNode *wire_check = new AstNode(AST_WIRE);
@@ -2983,7 +2680,7 @@
newNode = new AstNode(AST_BLOCK);
AstNode *wire_tmp = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(width_hint-1, true), mkconst_int(0, true)));
- wire_tmp->str = stringf("$splitcmplxassign$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
+ wire_tmp->str = stringf("$splitcmplxassign$%s:%d$%d", encode_filename(filename).c_str(), location.first_line, autoidx++);
current_ast_mod->children.push_back(wire_tmp);
current_scope[wire_tmp->str] = wire_tmp;
wire_tmp->attributes[ID::nosync] = AstNode::mkconst_int(1, false);
@@ -3021,7 +2718,7 @@
(children[0]->children.size() == 1 || children[0]->children.size() == 2) && children[0]->children[0]->type == AST_RANGE)
{
std::stringstream sstr;
- sstr << "$memwr$" << children[0]->str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++);
+ sstr << "$memwr$" << children[0]->str << "$" << encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++);
std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA", id_en = sstr.str() + "_EN";
int mem_width, mem_size, addr_bits;
@@ -3293,7 +2990,7 @@
AstNode *reg = new AstNode(AST_WIRE, new AstNode(AST_RANGE,
mkconst_int(width_hint-1, true), mkconst_int(0, true)));
- reg->str = stringf("$past$%s:%d$%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, myidx, i);
+ reg->str = stringf("$past$%s:%d$%d$%d", encode_filename(filename).c_str(), location.first_line, myidx, i);
reg->is_reg = true;
reg->is_signed = sign_hint;
@@ -3828,7 +3525,7 @@
std::stringstream sstr;
- sstr << str << "$func$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++) << '.';
+ sstr << str << "$func$" << encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++) << '.';
std::string prefix = sstr.str();
AstNode *decl = current_scope[str];
@@ -4375,1300 +4072,4 @@
return did_something;
}
-void AstNode::replace_result_wire_name_in_function(const std::string &from, const std::string &to)
-{
- for (AstNode *child : children)
- child->replace_result_wire_name_in_function(from, to);
- if (str == from && type != AST_FCALL && type != AST_TCALL)
- str = to;
-}
-
-// replace a readmem[bh] TCALL ast node with a block of memory assignments
-AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init)
-{
- int mem_width, mem_size, addr_bits;
- memory->meminfo(mem_width, mem_size, addr_bits);
-
- AstNode *block = new AstNode(AST_BLOCK);
-
- AstNode *meminit = nullptr;
- int next_meminit_cursor=0;
- vector<State> meminit_bits;
- vector<State> en_bits;
- int meminit_size=0;
-
- for (int i = 0; i < mem_width; i++)
- en_bits.push_back(State::S1);
-
- std::ifstream f;
- f.open(mem_filename.c_str());
- if (f.fail()) {
-#ifdef _WIN32
- char slash = '\\';
-#else
- char slash = '/';
-#endif
- std::string path = filename.substr(0, filename.find_last_of(slash)+1);
- f.open(path + mem_filename.c_str());
- yosys_input_files.insert(path + mem_filename);
- } else {
- yosys_input_files.insert(mem_filename);
- }
- if (f.fail() || GetSize(mem_filename) == 0)
- log_file_error(filename, location.first_line, "Can not open file `%s` for %s.\n", mem_filename.c_str(), str.c_str());
-
- log_assert(GetSize(memory->children) == 2 && memory->children[1]->type == AST_RANGE && memory->children[1]->range_valid);
- int range_left = memory->children[1]->range_left, range_right = memory->children[1]->range_right;
- int range_min = min(range_left, range_right), range_max = max(range_left, range_right);
-
- if (start_addr < 0)
- start_addr = range_min;
-
- if (finish_addr < 0)
- finish_addr = range_max + 1;
-
- bool in_comment = false;
- int increment = start_addr <= finish_addr ? +1 : -1;
- int cursor = start_addr;
-
- while (!f.eof())
- {
- std::string line, token;
- std::getline(f, line);
-
- for (int i = 0; i < GetSize(line); i++) {
- if (in_comment && line.compare(i, 2, "*/") == 0) {
- line[i] = ' ';
- line[i+1] = ' ';
- in_comment = false;
- continue;
- }
- if (!in_comment && line.compare(i, 2, "/*") == 0)
- in_comment = true;
- if (in_comment)
- line[i] = ' ';
- }
-
- while (1)
- {
- token = next_token(line, " \t\r\n");
- if (token.empty() || token.compare(0, 2, "//") == 0)
- break;
-
- if (token[0] == '@') {
- token = token.substr(1);
- const char *nptr = token.c_str();
- char *endptr;
- cursor = strtol(nptr, &endptr, 16);
- if (!*nptr || *endptr)
- log_file_error(filename, location.first_line, "Can not parse address `%s` for %s.\n", nptr, str.c_str());
- continue;
- }
-
- AstNode *value = VERILOG_FRONTEND::const2ast(stringf("%d'%c", mem_width, is_readmemh ? 'h' : 'b') + token);
-
- if (unconditional_init)
- {
- if (meminit == nullptr || cursor != next_meminit_cursor)
- {
- if (meminit != nullptr) {
- meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false);
- meminit->children[3] = AstNode::mkconst_int(meminit_size, false);
- }
-
- meminit = new AstNode(AST_MEMINIT);
- meminit->children.push_back(AstNode::mkconst_int(cursor, false));
- meminit->children.push_back(nullptr);
- meminit->children.push_back(AstNode::mkconst_bits(en_bits, false));
- meminit->children.push_back(nullptr);
- meminit->str = memory->str;
- meminit->id2ast = memory;
- meminit_bits.clear();
- meminit_size = 0;
-
- current_ast_mod->children.push_back(meminit);
- next_meminit_cursor = cursor;
- }
-
- meminit_size++;
- next_meminit_cursor++;
- meminit_bits.insert(meminit_bits.end(), value->bits.begin(), value->bits.end());
- delete value;
- }
- else
- {
- block->children.push_back(new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER, new AstNode(AST_RANGE, AstNode::mkconst_int(cursor, false))), value));
- block->children.back()->children[0]->str = memory->str;
- block->children.back()->children[0]->id2ast = memory;
- block->children.back()->children[0]->was_checked = true;
- }
-
- cursor += increment;
- if ((cursor == finish_addr+increment) || (increment > 0 && cursor > range_max) || (increment < 0 && cursor < range_min))
- break;
- }
-
- if ((cursor == finish_addr+increment) || (increment > 0 && cursor > range_max) || (increment < 0 && cursor < range_min))
- break;
- }
-
- if (meminit != nullptr) {
- meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false);
- meminit->children[3] = AstNode::mkconst_int(meminit_size, false);
- }
-
- return block;
-}
-
-// annotate the names of all wires and other named objects in a named generate
-// or procedural block; nested blocks are themselves annotated such that the
-// prefix is carried forward, but resolution of their children is deferred
-void AstNode::expand_genblock(const std::string &prefix)
-{
- if (type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE || type == AST_PREFIX) {
- log_assert(!str.empty());
-
- // search starting in the innermost scope and then stepping outward
- for (size_t ppos = prefix.size() - 1; ppos; --ppos) {
- if (prefix.at(ppos) != '.') continue;
-
- std::string new_prefix = prefix.substr(0, ppos + 1);
- auto attempt_resolve = [&new_prefix](const std::string &ident) -> std::string {
- std::string new_name = prefix_id(new_prefix, ident);
- if (current_scope.count(new_name))
- return new_name;
- return {};
- };
-
- // attempt to resolve the full identifier
- std::string resolved = attempt_resolve(str);
- if (!resolved.empty()) {
- str = resolved;
- break;
- }
-
- // attempt to resolve hierarchical prefixes within the identifier,
- // as the prefix could refer to a local scope which exists but
- // hasn't yet been elaborated
- for (size_t spos = str.size() - 1; spos; --spos) {
- if (str.at(spos) != '.') continue;
- resolved = attempt_resolve(str.substr(0, spos));
- if (!resolved.empty()) {
- str = resolved + str.substr(spos);
- ppos = 1; // break outer loop
- break;
- }
- }
-
- }
- }
-
- auto prefix_node = [&prefix](AstNode* child) {
- if (child->str.empty()) return;
- std::string new_name = prefix_id(prefix, child->str);
- if (child->type == AST_FUNCTION)
- child->replace_result_wire_name_in_function(child->str, new_name);
- else
- child->str = new_name;
- current_scope[new_name] = child;
- };
-
- for (size_t i = 0; i < children.size(); i++) {
- AstNode *child = children[i];
-
- switch (child->type) {
- case AST_WIRE:
- case AST_MEMORY:
- case AST_PARAMETER:
- case AST_LOCALPARAM:
- case AST_FUNCTION:
- case AST_TASK:
- case AST_CELL:
- case AST_TYPEDEF:
- case AST_ENUM_ITEM:
- case AST_GENVAR:
- prefix_node(child);
- break;
-
- case AST_BLOCK:
- case AST_GENBLOCK:
- if (!child->str.empty())
- prefix_node(child);
- break;
-
- case AST_ENUM:
- current_scope[child->str] = child;
- for (auto enode : child->children){
- log_assert(enode->type == AST_ENUM_ITEM);
- prefix_node(enode);
- }
- break;
-
- default:
- break;
- }
- }
-
- for (size_t i = 0; i < children.size(); i++) {
- AstNode *child = children[i];
- // AST_PREFIX member names should not be prefixed; we recurse into them
- // as normal to ensure indices and ranges are properly resolved, and
- // then restore the previous string
- if (type == AST_PREFIX && i == 1) {
- std::string backup_scope_name = child->str;
- child->expand_genblock(prefix);
- child->str = backup_scope_name;
- continue;
- }
- // functions/tasks may reference wires, constants, etc. in this scope
- if (child->type == AST_FUNCTION || child->type == AST_TASK)
- continue;
- // named blocks pick up the current prefix and will expanded later
- if ((child->type == AST_GENBLOCK || child->type == AST_BLOCK) && !child->str.empty())
- continue;
-
- child->expand_genblock(prefix);
- }
-}
-
-// add implicit AST_GENBLOCK names according to IEEE 1364-2005 Section 12.4.3 or
-// IEEE 1800-2017 Section 27.6
-void AstNode::label_genblks(std::set<std::string>& existing, int &counter)
-{
- switch (type) {
- case AST_GENIF:
- case AST_GENFOR:
- case AST_GENCASE:
- // seeing a proper generate control flow construct increments the
- // counter once
- ++counter;
- for (AstNode *child : children)
- child->label_genblks(existing, counter);
- break;
-
- case AST_GENBLOCK: {
- // if this block is unlabeled, generate its corresponding unique name
- for (int padding = 0; str.empty(); ++padding) {
- std::string candidate = "\\genblk";
- for (int i = 0; i < padding; ++i)
- candidate += '0';
- candidate += std::to_string(counter);
- if (!existing.count(candidate))
- str = candidate;
- }
- // within a genblk, the counter starts fresh
- std::set<std::string> existing_local = existing;
- int counter_local = 0;
- for (AstNode *child : children)
- child->label_genblks(existing_local, counter_local);
- break;
- }
-
- default:
- // track names which could conflict with implicit genblk names
- if (str.rfind("\\genblk", 0) == 0)
- existing.insert(str);
- for (AstNode *child : children)
- child->label_genblks(existing, counter);
- break;
- }
-}
-
-// helper function for mem2reg_as_needed_pass1
-static void mark_memories_assign_lhs_complex(dict<AstNode*, pool<std::string>> &mem2reg_places,
- dict<AstNode*, uint32_t> &mem2reg_candidates, AstNode *that)
-{
- for (auto &child : that->children)
- mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, child);
-
- if (that->type == AST_IDENTIFIER && that->id2ast && that->id2ast->type == AST_MEMORY) {
- AstNode *mem = that->id2ast;
- if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_CMPLX_LHS))
- mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(that->filename).c_str(), that->location.first_line));
- mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_CMPLX_LHS;
- }
-}
-
-// find memories that should be replaced by registers
-void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg_places,
- dict<AstNode*, uint32_t> &mem2reg_candidates, dict<AstNode*, uint32_t> &proc_flags, uint32_t &flags)
-{
- uint32_t children_flags = 0;
- int lhs_children_counter = 0;
-
- if (type == AST_TYPEDEF)
- return; // don't touch content of typedefs
-
- if (type == AST_ASSIGN || type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ)
- {
- // mark all memories that are used in a complex expression on the left side of an assignment
- for (auto &lhs_child : children[0]->children)
- mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, lhs_child);
-
- if (children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY)
- {
- AstNode *mem = children[0]->id2ast;
-
- // activate mem2reg if this is assigned in an async proc
- if (flags & AstNode::MEM2REG_FL_ASYNC) {
- if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_ASYNC))
- mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line));
- mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_ASYNC;
- }
-
- // remember if this is assigned blocking (=)
- if (type == AST_ASSIGN_EQ) {
- if (!(proc_flags[mem] & AstNode::MEM2REG_FL_EQ1))
- mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line));
- proc_flags[mem] |= AstNode::MEM2REG_FL_EQ1;
- }
-
- // for proper (non-init) writes: remember if this is a constant index or not
- if ((flags & MEM2REG_FL_INIT) == 0) {
- if (children[0]->children.size() && children[0]->children[0]->type == AST_RANGE && children[0]->children[0]->children.size()) {
- if (children[0]->children[0]->children[0]->type == AST_CONSTANT)
- mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_CONST_LHS;
- else
- mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_VAR_LHS;
- }
- }
-
- // remember where this is
- if (flags & MEM2REG_FL_INIT) {
- if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_INIT))
- mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line));
- mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_INIT;
- } else {
- if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_ELSE))
- mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line));
- mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_ELSE;
- }
- }
-
- lhs_children_counter = 1;
- }
-
- if (type == AST_IDENTIFIER && id2ast && id2ast->type == AST_MEMORY)
- {
- AstNode *mem = id2ast;
-
- // flag if used after blocking assignment (in same proc)
- if ((proc_flags[mem] & AstNode::MEM2REG_FL_EQ1) && !(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_EQ2)) {
- mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line));
- mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_EQ2;
- }
- }
-
- // 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 || type == AST_INTERFACE) && get_bool_attribute(ID::mem2reg))
- children_flags |= AstNode::MEM2REG_FL_ALL;
-
- dict<AstNode*, uint32_t> *proc_flags_p = NULL;
-
- if (type == AST_ALWAYS) {
- int count_edge_events = 0;
- for (auto child : children)
- if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE)
- count_edge_events++;
- if (count_edge_events != 1)
- children_flags |= AstNode::MEM2REG_FL_ASYNC;
- proc_flags_p = new dict<AstNode*, uint32_t>;
- }
- else if (type == AST_INITIAL) {
- children_flags |= AstNode::MEM2REG_FL_INIT;
- proc_flags_p = new dict<AstNode*, uint32_t>;
- }
-
- uint32_t backup_flags = flags;
- flags |= children_flags;
- log_assert((flags & ~0x000000ff) == 0);
-
- for (auto child : children)
- {
- if (lhs_children_counter > 0) {
- lhs_children_counter--;
- if (child->children.size() && child->children[0]->type == AST_RANGE && child->children[0]->children.size()) {
- for (auto c : child->children[0]->children) {
- if (proc_flags_p)
- c->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, *proc_flags_p, flags);
- else
- c->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, proc_flags, flags);
- }
- }
- } else
- if (proc_flags_p)
- child->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, *proc_flags_p, flags);
- else
- child->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, proc_flags, flags);
- }
-
- flags &= ~children_flags | backup_flags;
-
- if (proc_flags_p) {
-#ifndef NDEBUG
- for (auto it : *proc_flags_p)
- log_assert((it.second & ~0xff000000) == 0);
-#endif
- delete proc_flags_p;
- }
-}
-
-bool AstNode::mem2reg_check(pool<AstNode*> &mem2reg_set)
-{
- if (type != AST_IDENTIFIER || !id2ast || !mem2reg_set.count(id2ast))
- return false;
-
- if (children.empty() || children[0]->type != AST_RANGE || GetSize(children[0]->children) != 1)
- log_file_error(filename, location.first_line, "Invalid array access.\n");
-
- return true;
-}
-
-void AstNode::mem2reg_remove(pool<AstNode*> &mem2reg_set, vector<AstNode*> &delnodes)
-{
- log_assert(mem2reg_set.count(this) == 0);
-
- if (mem2reg_set.count(id2ast))
- id2ast = nullptr;
-
- for (size_t i = 0; i < children.size(); i++) {
- if (mem2reg_set.count(children[i]) > 0) {
- delnodes.push_back(children[i]);
- children.erase(children.begin() + (i--));
- } else {
- children[i]->mem2reg_remove(mem2reg_set, delnodes);
- }
- }
-}
-
-// actually replace memories with registers
-bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block, AstNode *&async_block)
-{
- bool did_something = false;
-
- if (type == AST_BLOCK)
- block = this;
-
- if (type == AST_FUNCTION || type == AST_TASK)
- return false;
-
- if (type == AST_TYPEDEF)
- return false;
-
- if (type == AST_MEMINIT && id2ast && mem2reg_set.count(id2ast))
- {
- log_assert(children[0]->type == AST_CONSTANT);
- log_assert(children[1]->type == AST_CONSTANT);
- log_assert(children[2]->type == AST_CONSTANT);
- log_assert(children[3]->type == AST_CONSTANT);
-
- int cursor = children[0]->asInt(false);
- Const data = children[1]->bitsAsConst();
- Const en = children[2]->bitsAsConst();
- int length = children[3]->asInt(false);
-
- if (length != 0)
- {
- AstNode *block = new AstNode(AST_INITIAL, new AstNode(AST_BLOCK));
- mod->children.push_back(block);
- block = block->children[0];
-
- int wordsz = GetSize(data) / length;
-
- for (int i = 0; i < length; i++) {
- int pos = 0;
- while (pos < wordsz) {
- if (en[pos] != State::S1) {
- pos++;
- } else {
- int epos = pos + 1;
- while (epos < wordsz && en[epos] == State::S1)
- epos++;
- int clen = epos - pos;
- AstNode *range = new AstNode(AST_RANGE, AstNode::mkconst_int(cursor+i, false));
- if (pos != 0 || epos != wordsz) {
- int left;
- int right;
- AstNode *mrange = id2ast->children[0];
- if (mrange->range_left < mrange->range_right) {
- right = mrange->range_right - pos;
- left = mrange->range_right - epos + 1;
- } else {
- right = mrange->range_right + pos;
- left = mrange->range_right + epos - 1;
- }
- range = new AstNode(AST_MULTIRANGE, range, new AstNode(AST_RANGE, AstNode::mkconst_int(left, true), AstNode::mkconst_int(right, true)));
- }
- AstNode *target = new AstNode(AST_IDENTIFIER, range);
- target->str = str;
- target->id2ast = id2ast;
- target->was_checked = true;
- block->children.push_back(new AstNode(AST_ASSIGN_EQ, target, mkconst_bits(data.extract(i*wordsz + pos, clen).bits, false)));
- pos = epos;
- }
- }
- }
- }
-
- AstNode *newNode = new AstNode(AST_NONE);
- newNode->cloneInto(this);
- delete newNode;
-
- did_something = true;
- }
-
- if (type == AST_ASSIGN && block == NULL && children[0]->mem2reg_check(mem2reg_set))
- {
- if (async_block == NULL) {
- async_block = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK));
- mod->children.push_back(async_block);
- }
-
- AstNode *newNode = clone();
- newNode->type = AST_ASSIGN_EQ;
- newNode->children[0]->was_checked = true;
- async_block->children[0]->children.push_back(newNode);
-
- newNode = new AstNode(AST_NONE);
- newNode->cloneInto(this);
- delete newNode;
-
- did_something = true;
- }
-
- if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && children[0]->mem2reg_check(mem2reg_set) &&
- children[0]->children[0]->children[0]->type != AST_CONSTANT)
- {
- std::stringstream sstr;
- sstr << "$mem2reg_wr$" << children[0]->str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++);
- std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA";
-
- int mem_width, mem_size, addr_bits;
- bool mem_signed = children[0]->id2ast->is_signed;
- children[0]->id2ast->meminfo(mem_width, mem_size, addr_bits);
-
- AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true)));
- wire_addr->str = id_addr;
- wire_addr->is_reg = true;
- wire_addr->was_checked = true;
- wire_addr->attributes[ID::nosync] = AstNode::mkconst_int(1, false);
- mod->children.push_back(wire_addr);
- while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { }
-
- AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
- wire_data->str = id_data;
- wire_data->is_reg = true;
- wire_data->was_checked = true;
- wire_data->is_signed = mem_signed;
- wire_data->attributes[ID::nosync] = AstNode::mkconst_int(1, false);
- mod->children.push_back(wire_data);
- while (wire_data->simplify(true, false, false, 1, -1, false, false)) { }
-
- log_assert(block != NULL);
- size_t assign_idx = 0;
- while (assign_idx < block->children.size() && block->children[assign_idx] != this)
- assign_idx++;
- log_assert(assign_idx < block->children.size());
-
- AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone());
- assign_addr->children[0]->str = id_addr;
- assign_addr->children[0]->was_checked = true;
- block->children.insert(block->children.begin()+assign_idx+1, assign_addr);
-
- AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER));
- case_node->children[0]->str = id_addr;
- for (int i = 0; i < mem_size; i++) {
- if (children[0]->children[0]->children[0]->type == AST_CONSTANT && int(children[0]->children[0]->children[0]->integer) != i)
- continue;
- AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK));
- AstNode *assign_reg = new AstNode(type, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER));
- if (children[0]->children.size() == 2)
- assign_reg->children[0]->children.push_back(children[0]->children[1]->clone());
- assign_reg->children[0]->str = stringf("%s[%d]", children[0]->str.c_str(), i);
- assign_reg->children[1]->str = id_data;
- cond_node->children[1]->children.push_back(assign_reg);
- case_node->children.push_back(cond_node);
- }
- block->children.insert(block->children.begin()+assign_idx+2, case_node);
-
- children[0]->delete_children();
- children[0]->range_valid = false;
- children[0]->id2ast = NULL;
- children[0]->str = id_data;
- type = AST_ASSIGN_EQ;
- children[0]->was_checked = true;
-
- did_something = true;
- }
-
- if (mem2reg_check(mem2reg_set))
- {
- AstNode *bit_part_sel = NULL;
- if (children.size() == 2)
- bit_part_sel = children[1]->clone();
-
- if (children[0]->children[0]->type == AST_CONSTANT)
- {
- int id = children[0]->children[0]->integer;
- int left = id2ast->children[1]->children[0]->integer;
- int right = id2ast->children[1]->children[1]->integer;
- bool valid_const_access =
- (left <= id && id <= right) ||
- (right <= id && id <= left);
- if (valid_const_access)
- {
- str = stringf("%s[%d]", str.c_str(), id);
- delete_children();
- range_valid = false;
- id2ast = NULL;
- }
- else
- {
- int width;
- if (bit_part_sel)
- {
- bit_part_sel->dumpAst(nullptr, "? ");
- if (bit_part_sel->children.size() == 1)
- width = 0;
- else
- width = bit_part_sel->children[0]->integer -
- bit_part_sel->children[1]->integer;
- delete bit_part_sel;
- bit_part_sel = nullptr;
- }
- else
- {
- width = id2ast->children[0]->children[0]->integer -
- id2ast->children[0]->children[1]->integer;
- }
- width = abs(width) + 1;
-
- delete_children();
-
- std::vector<RTLIL::State> x_bits;
- for (int i = 0; i < width; i++)
- x_bits.push_back(RTLIL::State::Sx);
- AstNode *constant = AstNode::mkconst_bits(x_bits, false);
- constant->cloneInto(this);
- delete constant;
- }
- }
- else
- {
- std::stringstream sstr;
- sstr << "$mem2reg_rd$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++);
- std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA";
-
- int mem_width, mem_size, addr_bits;
- bool mem_signed = id2ast->is_signed;
- id2ast->meminfo(mem_width, mem_size, addr_bits);
-
- AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true)));
- wire_addr->str = id_addr;
- wire_addr->is_reg = true;
- wire_addr->was_checked = true;
- if (block)
- wire_addr->attributes[ID::nosync] = AstNode::mkconst_int(1, false);
- mod->children.push_back(wire_addr);
- while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { }
-
- AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
- wire_data->str = id_data;
- wire_data->is_reg = true;
- wire_data->was_checked = true;
- wire_data->is_signed = mem_signed;
- if (block)
- wire_data->attributes[ID::nosync] = AstNode::mkconst_int(1, false);
- mod->children.push_back(wire_data);
- while (wire_data->simplify(true, false, false, 1, -1, false, false)) { }
-
- AstNode *assign_addr = new AstNode(block ? AST_ASSIGN_EQ : AST_ASSIGN, new AstNode(AST_IDENTIFIER), children[0]->children[0]->clone());
- assign_addr->children[0]->str = id_addr;
- assign_addr->children[0]->was_checked = true;
-
- AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER));
- case_node->children[0]->str = id_addr;
-
- for (int i = 0; i < mem_size; i++) {
- if (children[0]->children[0]->type == AST_CONSTANT && int(children[0]->children[0]->integer) != i)
- continue;
- AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK));
- AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER));
- assign_reg->children[0]->str = id_data;
- assign_reg->children[0]->was_checked = true;
- assign_reg->children[1]->str = stringf("%s[%d]", str.c_str(), i);
- cond_node->children[1]->children.push_back(assign_reg);
- case_node->children.push_back(cond_node);
- }
-
- std::vector<RTLIL::State> x_bits;
- for (int i = 0; i < mem_width; i++)
- x_bits.push_back(RTLIL::State::Sx);
-
- AstNode *cond_node = new AstNode(AST_COND, new AstNode(AST_DEFAULT), new AstNode(AST_BLOCK));
- AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), AstNode::mkconst_bits(x_bits, false));
- assign_reg->children[0]->str = id_data;
- assign_reg->children[0]->was_checked = true;
- cond_node->children[1]->children.push_back(assign_reg);
- case_node->children.push_back(cond_node);
-
- if (block)
- {
- size_t assign_idx = 0;
- while (assign_idx < block->children.size() && !block->children[assign_idx]->contains(this))
- assign_idx++;
- log_assert(assign_idx < block->children.size());
- block->children.insert(block->children.begin()+assign_idx, case_node);
- block->children.insert(block->children.begin()+assign_idx, assign_addr);
- }
- else
- {
- AstNode *proc = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK));
- proc->children[0]->children.push_back(case_node);
- mod->children.push_back(proc);
- mod->children.push_back(assign_addr);
- }
-
- delete_children();
- range_valid = false;
- id2ast = NULL;
- str = id_data;
- }
-
- if (bit_part_sel)
- children.push_back(bit_part_sel);
-
- did_something = true;
- }
-
- log_assert(id2ast == NULL || mem2reg_set.count(id2ast) == 0);
-
- auto children_list = children;
- for (size_t i = 0; i < children_list.size(); i++)
- if (children_list[i]->mem2reg_as_needed_pass2(mem2reg_set, mod, block, async_block))
- did_something = true;
-
- return did_something;
-}
-
-// calculate memory dimensions
-void AstNode::meminfo(int &mem_width, int &mem_size, int &addr_bits)
-{
- log_assert(type == AST_MEMORY);
-
- mem_width = children[0]->range_left - children[0]->range_right + 1;
- mem_size = children[1]->range_left - children[1]->range_right;
-
- if (mem_size < 0)
- mem_size *= -1;
- mem_size += min(children[1]->range_left, children[1]->range_right) + 1;
-
- addr_bits = 1;
- while ((1 << addr_bits) < mem_size)
- 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_EDGE:
- break;
- 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()
-{
- if (type == AST_WHILE || type == AST_REPEAT)
- return true;
- for (auto child : children)
- if (child->has_const_only_constructs())
- return true;
- return false;
-}
-
-bool AstNode::is_simple_const_expr()
-{
- if (type == AST_IDENTIFIER)
- return false;
- for (auto child : children)
- if (!child->is_simple_const_expr())
- return false;
- return true;
-}
-
-// helper function for AstNode::eval_const_function()
-bool AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall, bool must_succeed)
-{
- if (type == AST_IDENTIFIER && variables.count(str)) {
- int offset = variables.at(str).offset, width = variables.at(str).val.bits.size();
- if (!children.empty()) {
- if (children.size() != 1 || children.at(0)->type != AST_RANGE) {
- if (!must_succeed)
- return false;
- log_file_error(filename, location.first_line, "Memory access in constant function is not supported\n%s: ...called from here.\n",
- fcall->loc_string().c_str());
- }
- if (!children.at(0)->replace_variables(variables, fcall, must_succeed))
- return false;
- while (simplify(true, false, false, 1, -1, false, true)) { }
- if (!children.at(0)->range_valid) {
- if (!must_succeed)
- return false;
- log_file_error(filename, location.first_line, "Non-constant range\n%s: ... called from here.\n",
- fcall->loc_string().c_str());
- }
- offset = min(children.at(0)->range_left, children.at(0)->range_right);
- width = min(std::abs(children.at(0)->range_left - children.at(0)->range_right) + 1, width);
- }
- offset -= variables.at(str).offset;
- if (variables.at(str).range_swapped)
- offset = -offset;
- std::vector<RTLIL::State> &var_bits = variables.at(str).val.bits;
- std::vector<RTLIL::State> new_bits(var_bits.begin() + offset, var_bits.begin() + offset + width);
- AstNode *newNode = mkconst_bits(new_bits, variables.at(str).is_signed);
- newNode->cloneInto(this);
- delete newNode;
- return true;
- }
-
- for (auto &child : children)
- if (!child->replace_variables(variables, fcall, must_succeed))
- return false;
- return true;
-}
-
-// attempt to statically evaluate a functions with all-const arguments
-AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed)
-{
- std::map<std::string, AstNode*> backup_scope = current_scope;
- std::map<std::string, AstNode::varinfo_t> variables;
- std::vector<AstNode*> to_delete;
- AstNode *block = new AstNode(AST_BLOCK);
- AstNode *result = nullptr;
-
- size_t argidx = 0;
- for (auto child : children)
- {
- block->children.push_back(child->clone());
- }
-
- while (!block->children.empty())
- {
- AstNode *stmt = block->children.front();
-
-#if 0
- log("-----------------------------------\n");
- for (auto &it : variables)
- log("%20s %40s\n", it.first.c_str(), log_signal(it.second.val));
- stmt->dumpAst(NULL, "stmt> ");
-#endif
-
- if (stmt->type == AST_WIRE)
- {
- while (stmt->simplify(true, false, false, 1, -1, false, true)) { }
- if (!stmt->range_valid) {
- if (!must_succeed)
- goto finished;
- log_file_error(stmt->filename, stmt->location.first_line, "Can't determine size of variable %s\n%s: ... called from here.\n",
- stmt->str.c_str(), fcall->loc_string().c_str());
- }
- AstNode::varinfo_t &variable = variables[stmt->str];
- int width = abs(stmt->range_left - stmt->range_right) + 1;
- // if this variable has already been declared as an input, check the
- // sizes match if it already had an explicit size
- if (variable.arg && variable.explicitly_sized && variable.val.size() != width) {
- log_file_error(filename, location.first_line, "Incompatible re-declaration of constant function wire %s.\n", stmt->str.c_str());
- }
- variable.val = RTLIL::Const(RTLIL::State::Sx, width);
- variable.offset = stmt->range_swapped ? stmt->range_left : stmt->range_right;
- variable.range_swapped = stmt->range_swapped;
- variable.is_signed = stmt->is_signed;
- variable.explicitly_sized = stmt->children.size() &&
- stmt->children.back()->type == AST_RANGE;
- // identify the argument corresponding to this wire, if applicable
- if (stmt->is_input && argidx < fcall->children.size()) {
- variable.arg = fcall->children.at(argidx++);
- }
- // load the constant arg's value into this variable
- if (variable.arg) {
- if (variable.arg->type == AST_CONSTANT) {
- variable.val = variable.arg->bitsAsConst(width);
- } else {
- log_assert(variable.arg->type == AST_REALVALUE);
- variable.val = variable.arg->realAsConst(width);
- }
- }
- current_scope[stmt->str] = stmt;
-
- block->children.erase(block->children.begin());
- to_delete.push_back(stmt);
- continue;
- }
-
- log_assert(variables.count(str) != 0);
-
- if (stmt->type == AST_LOCALPARAM)
- {
- while (stmt->simplify(true, false, false, 1, -1, false, true)) { }
-
- current_scope[stmt->str] = stmt;
-
- block->children.erase(block->children.begin());
- to_delete.push_back(stmt);
- continue;
- }
-
- if (stmt->type == AST_ASSIGN_EQ)
- {
- if (stmt->children.at(0)->type == AST_IDENTIFIER && stmt->children.at(0)->children.size() != 0 &&
- stmt->children.at(0)->children.at(0)->type == AST_RANGE)
- if (!stmt->children.at(0)->children.at(0)->replace_variables(variables, fcall, must_succeed))
- goto finished;
- if (!stmt->children.at(1)->replace_variables(variables, fcall, must_succeed))
- goto finished;
- while (stmt->simplify(true, false, false, 1, -1, false, true)) { }
-
- if (stmt->type != AST_ASSIGN_EQ)
- continue;
-
- if (stmt->children.at(1)->type != AST_CONSTANT) {
- if (!must_succeed)
- goto finished;
- log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s: ... called from here. X\n",
- fcall->loc_string().c_str());
- }
-
- if (stmt->children.at(0)->type != AST_IDENTIFIER) {
- if (!must_succeed)
- goto finished;
- log_file_error(stmt->filename, stmt->location.first_line, "Unsupported composite left hand side in constant function\n%s: ... called from here.\n",
- fcall->loc_string().c_str());
- }
-
- if (!variables.count(stmt->children.at(0)->str)) {
- if (!must_succeed)
- goto finished;
- log_file_error(stmt->filename, stmt->location.first_line, "Assignment to non-local variable in constant function\n%s: ... called from here.\n",
- fcall->loc_string().c_str());
- }
-
- if (stmt->children.at(0)->children.empty()) {
- variables[stmt->children.at(0)->str].val = stmt->children.at(1)->bitsAsConst(variables[stmt->children.at(0)->str].val.bits.size());
- } else {
- AstNode *range = stmt->children.at(0)->children.at(0);
- if (!range->range_valid) {
- if (!must_succeed)
- goto finished;
- log_file_error(range->filename, range->location.first_line, "Non-constant range\n%s: ... called from here.\n",
- fcall->loc_string().c_str());
- }
- int offset = min(range->range_left, range->range_right);
- int width = std::abs(range->range_left - range->range_right) + 1;
- varinfo_t &v = variables[stmt->children.at(0)->str];
- RTLIL::Const r = stmt->children.at(1)->bitsAsConst(v.val.bits.size());
- for (int i = 0; i < width; i++) {
- int index = i + offset - v.offset;
- if (v.range_swapped)
- index = -index;
- v.val.bits.at(index) = r.bits.at(i);
- }
- }
-
- delete block->children.front();
- block->children.erase(block->children.begin());
- continue;
- }
-
- if (stmt->type == AST_FOR)
- {
- block->children.insert(block->children.begin(), stmt->children.at(0));
- stmt->children.at(3)->children.push_back(stmt->children.at(2));
- stmt->children.erase(stmt->children.begin() + 2);
- stmt->children.erase(stmt->children.begin());
- stmt->type = AST_WHILE;
- continue;
- }
-
- if (stmt->type == AST_WHILE)
- {
- AstNode *cond = stmt->children.at(0)->clone();
- if (!cond->replace_variables(variables, fcall, must_succeed))
- goto finished;
- while (cond->simplify(true, false, false, 1, -1, false, true)) { }
-
- if (cond->type != AST_CONSTANT) {
- if (!must_succeed)
- goto finished;
- log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s: ... called from here.\n",
- fcall->loc_string().c_str());
- }
-
- if (cond->asBool()) {
- block->children.insert(block->children.begin(), stmt->children.at(1)->clone());
- } else {
- delete block->children.front();
- block->children.erase(block->children.begin());
- }
-
- delete cond;
- continue;
- }
-
- if (stmt->type == AST_REPEAT)
- {
- AstNode *num = stmt->children.at(0)->clone();
- if (!num->replace_variables(variables, fcall, must_succeed))
- goto finished;
- while (num->simplify(true, false, false, 1, -1, false, true)) { }
-
- if (num->type != AST_CONSTANT) {
- if (!must_succeed)
- goto finished;
- log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s: ... called from here.\n",
- fcall->loc_string().c_str());
- }
-
- block->children.erase(block->children.begin());
- for (int i = 0; i < num->bitsAsConst().as_int(); i++)
- block->children.insert(block->children.begin(), stmt->children.at(1)->clone());
-
- delete stmt;
- delete num;
- continue;
- }
-
- if (stmt->type == AST_CASE)
- {
- AstNode *expr = stmt->children.at(0)->clone();
- if (!expr->replace_variables(variables, fcall, must_succeed))
- goto finished;
- while (expr->simplify(true, false, false, 1, -1, false, true)) { }
-
- AstNode *sel_case = NULL;
- for (size_t i = 1; i < stmt->children.size(); i++)
- {
- bool found_match = false;
- log_assert(stmt->children.at(i)->type == AST_COND || stmt->children.at(i)->type == AST_CONDX || stmt->children.at(i)->type == AST_CONDZ);
-
- if (stmt->children.at(i)->children.front()->type == AST_DEFAULT) {
- sel_case = stmt->children.at(i)->children.back();
- continue;
- }
-
- for (size_t j = 0; j+1 < stmt->children.at(i)->children.size() && !found_match; j++)
- {
- AstNode *cond = stmt->children.at(i)->children.at(j)->clone();
- if (!cond->replace_variables(variables, fcall, must_succeed))
- goto finished;
-
- cond = new AstNode(AST_EQ, expr->clone(), cond);
- while (cond->simplify(true, false, false, 1, -1, false, true)) { }
-
- if (cond->type != AST_CONSTANT) {
- if (!must_succeed)
- goto finished;
- log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s: ... called from here.\n",
- fcall->loc_string().c_str());
- }
-
- found_match = cond->asBool();
- delete cond;
- }
-
- if (found_match) {
- sel_case = stmt->children.at(i)->children.back();
- break;
- }
- }
-
- block->children.erase(block->children.begin());
- if (sel_case)
- block->children.insert(block->children.begin(), sel_case->clone());
- delete stmt;
- delete expr;
- continue;
- }
-
- if (stmt->type == AST_BLOCK)
- {
- if (!stmt->str.empty())
- stmt->expand_genblock(stmt->str + ".");
-
- block->children.erase(block->children.begin());
- block->children.insert(block->children.begin(), stmt->children.begin(), stmt->children.end());
- stmt->children.clear();
- delete stmt;
- continue;
- }
-
- if (!must_succeed)
- goto finished;
- log_file_error(stmt->filename, stmt->location.first_line, "Unsupported language construct in constant function\n%s: ... called from here.\n",
- fcall->loc_string().c_str());
- log_abort();
- }
-
- result = AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed);
-
-finished:
- delete block;
- current_scope = backup_scope;
-
- for (auto it : to_delete) {
- delete it;
- }
- to_delete.clear();
-
- return result;
-}
-
-void AstNode::allocateDefaultEnumValues()
-{
- log_assert(type==AST_ENUM);
- log_assert(children.size() > 0);
- if (children.front()->attributes.count(ID::enum_base_type))
- return; // already elaborated
- int last_enum_int = -1;
- for (auto node : children) {
- log_assert(node->type==AST_ENUM_ITEM);
- node->attributes[ID::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
- }
- }
-}
-
-bool AstNode::is_recursive_function() const
-{
- std::set<const AstNode *> visited;
- std::function<bool(const AstNode *node)> visit = [&](const AstNode *node) {
- if (visited.count(node))
- return node == this;
- visited.insert(node);
- if (node->type == AST_FCALL) {
- auto it = current_scope.find(node->str);
- if (it != current_scope.end() && visit(it->second))
- return true;
- }
- for (const AstNode *child : node->children) {
- if (visit(child))
- return true;
- }
- return false;
- };
-
- log_assert(type == AST_FUNCTION);
- return visit(this);
-}
-
-std::pair<AstNode*, AstNode*> AstNode::get_tern_choice()
-{
- if (!children[0]->isConst())
- return {};
-
- bool found_sure_true = false;
- bool found_maybe_true = false;
-
- if (children[0]->type == AST_CONSTANT)
- for (auto &bit : children[0]->bits) {
- if (bit == RTLIL::State::S1)
- found_sure_true = true;
- if (bit > RTLIL::State::S1)
- found_maybe_true = true;
- }
- else
- found_sure_true = children[0]->asReal(true) != 0;
-
- AstNode *choice = nullptr, *not_choice = nullptr;
- if (found_sure_true)
- choice = children[1], not_choice = children[2];
- else if (!found_maybe_true)
- choice = children[2], not_choice = children[1];
-
- return {choice, not_choice};
-}
-
-std::string AstNode::try_pop_module_prefix() const
-{
- AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod;
- size_t pos = str.find('.', 1);
- if (str[0] == '\\' && pos != std::string::npos) {
- std::string new_str = "\\" + str.substr(pos + 1);
- if (current_scope.count(new_str)) {
- std::string prefix = str.substr(0, pos);
- auto it = current_scope_ast->attributes.find(ID::hdlname);
- if ((it != current_scope_ast->attributes.end() && it->second->str == prefix)
- || prefix == current_scope_ast->str)
- return new_str;
- }
- }
- return str;
-}
-
-YOSYS_NAMESPACE_END
+} // namespace systemverilog_plugin