2013-01-05 11:13:26 +01:00
|
|
|
/*
|
|
|
|
|
* yosys -- Yosys Open SYnthesis Suite
|
|
|
|
|
*
|
2021-06-08 00:39:36 +02:00
|
|
|
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
2015-07-02 11:14:30 +02:00
|
|
|
*
|
2013-01-05 11:13:26 +01:00
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
2015-07-02 11:14:30 +02:00
|
|
|
*
|
2013-01-05 11:13:26 +01:00
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
*
|
|
|
|
|
* ---
|
|
|
|
|
*
|
|
|
|
|
* This is the AST frontend library.
|
|
|
|
|
*
|
|
|
|
|
* The AST frontend library is not a frontend on it's own but provides a
|
|
|
|
|
* generic abstract syntax tree (AST) abstraction for HDL code and can be
|
|
|
|
|
* used by HDL frontends. See "ast.h" for an overview of the API and the
|
|
|
|
|
* Verilog frontend for an usage example.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "kernel/log.h"
|
2013-02-27 09:32:19 +01:00
|
|
|
#include "libs/sha1/sha1.h"
|
2014-10-26 20:33:10 +01:00
|
|
|
#include "frontends/verilog/verilog_frontend.h"
|
2013-01-05 11:13:26 +01:00
|
|
|
#include "ast.h"
|
|
|
|
|
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#include <stdarg.h>
|
2014-10-26 20:33:10 +01:00
|
|
|
#include <stdlib.h>
|
2014-06-14 08:51:22 +02:00
|
|
|
#include <math.h>
|
2025-06-16 22:55:24 +02:00
|
|
|
#include <optional>
|
2023-08-04 23:45:47 +02:00
|
|
|
// For std::gcd in C++17
|
|
|
|
|
// #include <numeric>
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2014-07-31 13:19:47 +02:00
|
|
|
YOSYS_NAMESPACE_BEGIN
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
using namespace AST;
|
|
|
|
|
using namespace AST_INTERNAL;
|
|
|
|
|
|
2023-08-04 23:45:47 +02:00
|
|
|
// gcd computed by Euclidian division.
|
|
|
|
|
// To be replaced by C++17 std::gcd
|
|
|
|
|
template<class I> I gcd(I a, I b) {
|
|
|
|
|
while (b != 0) {
|
|
|
|
|
I tmp = b;
|
|
|
|
|
b = a%b;
|
|
|
|
|
a = tmp;
|
|
|
|
|
}
|
|
|
|
|
return std::abs(a);
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-05 11:00:07 +02:00
|
|
|
void AstNode::set_in_lvalue_flag(bool flag, bool no_descend)
|
|
|
|
|
{
|
|
|
|
|
if (flag != in_lvalue_from_above) {
|
|
|
|
|
in_lvalue_from_above = flag;
|
|
|
|
|
if (!no_descend)
|
|
|
|
|
fixup_hierarchy_flags();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AstNode::set_in_param_flag(bool flag, bool no_descend)
|
|
|
|
|
{
|
|
|
|
|
if (flag != in_param_from_above) {
|
|
|
|
|
in_param_from_above = flag;
|
|
|
|
|
if (!no_descend)
|
|
|
|
|
fixup_hierarchy_flags();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AstNode::fixup_hierarchy_flags(bool force_descend)
|
|
|
|
|
{
|
|
|
|
|
// With forced descend, we disable the implicit
|
|
|
|
|
// descend from within the set_* functions, instead
|
|
|
|
|
// we do an explicit descend at the end of this function
|
|
|
|
|
|
|
|
|
|
in_param = in_param_from_above;
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case AST_PARAMETER:
|
|
|
|
|
case AST_LOCALPARAM:
|
|
|
|
|
case AST_DEFPARAM:
|
|
|
|
|
case AST_PARASET:
|
|
|
|
|
case AST_PREFIX:
|
|
|
|
|
in_param = true;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children)
|
2023-04-05 11:00:07 +02:00
|
|
|
child->set_in_param_flag(true, force_descend);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case AST_REPLICATE:
|
|
|
|
|
case AST_WIRE:
|
|
|
|
|
case AST_GENIF:
|
|
|
|
|
case AST_GENCASE:
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children)
|
2023-04-05 11:00:07 +02:00
|
|
|
child->set_in_param_flag(in_param, force_descend);
|
|
|
|
|
if (children.size() >= 1)
|
|
|
|
|
children[0]->set_in_param_flag(true, force_descend);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case AST_GENFOR:
|
|
|
|
|
case AST_FOR:
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children) {
|
|
|
|
|
log_assert((bool)child);
|
2023-04-05 11:00:07 +02:00
|
|
|
child->set_in_param_flag(in_param, force_descend);
|
2025-06-16 22:55:24 +02:00
|
|
|
}
|
2023-04-05 11:00:07 +02:00
|
|
|
if (children.size() >= 2)
|
|
|
|
|
children[1]->set_in_param_flag(true, force_descend);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
in_param = in_param_from_above;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children)
|
2023-04-05 11:00:07 +02:00
|
|
|
child->set_in_param_flag(in_param, force_descend);
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& attr : attributes)
|
2023-04-05 11:00:07 +02:00
|
|
|
attr.second->set_in_param_flag(true, force_descend);
|
|
|
|
|
|
|
|
|
|
in_lvalue = in_lvalue_from_above;
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case AST_ASSIGN:
|
|
|
|
|
case AST_ASSIGN_EQ:
|
|
|
|
|
case AST_ASSIGN_LE:
|
|
|
|
|
if (children.size() >= 1)
|
|
|
|
|
children[0]->set_in_lvalue_flag(true, force_descend);
|
|
|
|
|
if (children.size() >= 2)
|
|
|
|
|
children[1]->set_in_lvalue_flag(in_lvalue, force_descend);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children)
|
2023-04-05 11:00:07 +02:00
|
|
|
child->set_in_lvalue_flag(in_lvalue, force_descend);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (force_descend) {
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children)
|
2023-04-05 11:00:07 +02:00
|
|
|
child->fixup_hierarchy_flags(true);
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& attr : attributes)
|
2023-04-05 11:00:07 +02:00
|
|
|
attr.second->fixup_hierarchy_flags(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-19 22:15:51 +01:00
|
|
|
// Process a format string and arguments for $display, $write, $sprintf, etc
|
|
|
|
|
|
2023-09-22 17:56:34 +02:00
|
|
|
Fmt AstNode::processFormat(int stage, bool sformat_like, int default_base, size_t first_arg_at, bool may_fail) {
|
2020-11-29 09:57:07 +01:00
|
|
|
std::vector<VerilogFmtArg> args;
|
|
|
|
|
for (size_t index = first_arg_at; index < children.size(); index++) {
|
2025-06-16 22:55:24 +02:00
|
|
|
AstNode *node_arg = children[index].get();
|
2023-04-04 22:59:44 +02:00
|
|
|
while (node_arg->simplify(true, stage, -1, false)) { }
|
2020-11-29 09:57:07 +01:00
|
|
|
|
|
|
|
|
VerilogFmtArg arg = {};
|
|
|
|
|
arg.filename = filename;
|
|
|
|
|
arg.first_line = location.first_line;
|
2023-06-28 03:51:22 +02:00
|
|
|
if (node_arg->type == AST_CONSTANT && node_arg->is_string) {
|
2020-11-29 09:57:07 +01:00
|
|
|
arg.type = VerilogFmtArg::STRING;
|
|
|
|
|
arg.str = node_arg->bitsAsConst().decode_string();
|
|
|
|
|
// and in case this will be used as an argument...
|
|
|
|
|
arg.sig = node_arg->bitsAsConst();
|
|
|
|
|
arg.signed_ = false;
|
2023-06-28 03:51:22 +02:00
|
|
|
} else if (node_arg->type == AST_IDENTIFIER && node_arg->str == "$time") {
|
|
|
|
|
arg.type = VerilogFmtArg::TIME;
|
|
|
|
|
} else if (node_arg->type == AST_IDENTIFIER && node_arg->str == "$realtime") {
|
|
|
|
|
arg.type = VerilogFmtArg::TIME;
|
|
|
|
|
arg.realtime = true;
|
|
|
|
|
} else if (node_arg->type == AST_CONSTANT) {
|
2020-11-29 09:57:07 +01:00
|
|
|
arg.type = VerilogFmtArg::INTEGER;
|
|
|
|
|
arg.sig = node_arg->bitsAsConst();
|
|
|
|
|
arg.signed_ = node_arg->is_signed;
|
2023-09-22 17:56:34 +02:00
|
|
|
} else if (may_fail) {
|
|
|
|
|
log_file_info(filename, location.first_line, "Skipping system task `%s' with non-constant argument at position %zu.\n", str.c_str(), index + 1);
|
|
|
|
|
return Fmt();
|
2023-06-28 03:51:22 +02:00
|
|
|
} else {
|
|
|
|
|
log_file_error(filename, location.first_line, "Failed to evaluate system task `%s' with non-constant argument at position %zu.\n", str.c_str(), index + 1);
|
2020-01-19 22:15:51 +01:00
|
|
|
}
|
2020-11-29 09:57:07 +01:00
|
|
|
args.push_back(arg);
|
2020-01-19 22:15:51 +01:00
|
|
|
}
|
|
|
|
|
|
2024-01-11 10:39:28 +01:00
|
|
|
Fmt fmt;
|
2020-11-29 09:57:07 +01:00
|
|
|
fmt.parse_verilog(args, sformat_like, default_base, /*task_name=*/str, current_module->name);
|
|
|
|
|
return fmt;
|
|
|
|
|
}
|
2020-01-19 22:15:51 +01:00
|
|
|
|
2020-05-08 15:40:49 +02:00
|
|
|
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);
|
2023-04-04 22:59:44 +02:00
|
|
|
while (enum_node->simplify(true, 1, -1, false)) { }
|
2020-05-08 15:40:49 +02:00
|
|
|
//get width from 1st enum item:
|
|
|
|
|
log_assert(enum_node->children.size() >= 1);
|
2025-06-16 22:55:24 +02:00
|
|
|
AstNode *enum_item0 = enum_node->children[0].get();
|
2020-05-08 15:40:49 +02:00
|
|
|
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:
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& enum_item : enum_node->children){
|
2020-05-08 15:40:49 +02:00
|
|
|
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 {
|
2023-11-11 15:29:43 +01:00
|
|
|
log_error("enum_item children size==%zu, expected 1 or 2 for %s (%s)\n",
|
|
|
|
|
(size_t) enum_item->children.size(),
|
2020-05-08 15:40:49 +02:00
|
|
|
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
|
2023-04-05 11:00:07 +02:00
|
|
|
set_attribute(enum_item_str.c_str(), mkconst_str(enum_item->str));
|
2020-05-08 15:40:49 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
static std::unique_ptr<AstNode> make_range(int left, int right, bool is_signed = false)
|
2020-05-08 15:40:49 +02:00
|
|
|
{
|
|
|
|
|
// generate a pre-validated range node for a fixed signal range.
|
2025-06-16 22:55:24 +02:00
|
|
|
auto range = std::make_unique<AstNode>(AST_RANGE);
|
2020-05-08 15:40:49 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-07 19:28:45 +02:00
|
|
|
static int range_width(AstNode *node, AstNode *rnode)
|
|
|
|
|
{
|
|
|
|
|
log_assert(rnode->type==AST_RANGE);
|
|
|
|
|
if (!rnode->range_valid) {
|
2024-01-25 07:28:15 +01:00
|
|
|
node->input_error("Non-constant range in declaration of %s\n", node->str.c_str());
|
2020-06-07 19:28:45 +02:00
|
|
|
}
|
|
|
|
|
// note: range swapping has already been checked for
|
|
|
|
|
return rnode->range_left - rnode->range_right + 1;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-25 07:28:15 +01:00
|
|
|
static int add_dimension(AstNode *node, AstNode *rnode)
|
2022-11-12 08:48:25 +01:00
|
|
|
{
|
2024-01-25 07:28:15 +01:00
|
|
|
int width = range_width(node, rnode);
|
|
|
|
|
node->dimensions.push_back({ rnode->range_right, width, rnode->range_swapped });
|
|
|
|
|
return width;
|
2023-02-05 11:01:37 +01:00
|
|
|
}
|
2022-11-12 08:48:25 +01:00
|
|
|
|
2024-01-25 07:28:15 +01:00
|
|
|
[[noreturn]] static void struct_array_packing_error(AstNode *node)
|
2023-02-05 11:01:37 +01:00
|
|
|
{
|
2024-01-25 07:28:15 +01:00
|
|
|
node->input_error("Unpacked array in packed struct/union member %s\n", node->str.c_str());
|
2022-11-12 08:48:25 +01:00
|
|
|
}
|
|
|
|
|
|
2020-06-07 19:28:45 +02:00
|
|
|
static int size_packed_struct(AstNode *snode, int base_offset)
|
2020-05-12 15:25:33 +02:00
|
|
|
{
|
|
|
|
|
// 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) {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto node = it->get();
|
2020-05-12 15:25:33 +02:00
|
|
|
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);
|
2020-06-07 19:28:45 +02:00
|
|
|
if (node->children.size() > 0 && node->children[0]->type == AST_RANGE) {
|
|
|
|
|
// member width e.g. bit [7:0] a
|
2025-06-16 22:55:24 +02:00
|
|
|
width = range_width(node, node->children[0].get());
|
2020-06-07 19:28:45 +02:00
|
|
|
if (node->children.size() == 2) {
|
2022-11-30 20:04:45 +01:00
|
|
|
// Unpacked array. Note that this is a Yosys extension; only packed data types
|
|
|
|
|
// and integer data types are allowed in packed structs / unions in SystemVerilog.
|
2020-06-07 19:28:45 +02:00
|
|
|
if (node->children[1]->type == AST_RANGE) {
|
2022-11-30 20:04:45 +01:00
|
|
|
// Unpacked array, e.g. bit [63:0] a [0:3]
|
2024-01-25 07:28:15 +01:00
|
|
|
// Pretend it's declared as a packed array, e.g. bit [0:3][63:0] a
|
2025-06-16 22:55:24 +02:00
|
|
|
auto rnode = node->children[1].get();
|
2023-02-05 11:01:37 +01:00
|
|
|
if (rnode->children.size() == 1) {
|
|
|
|
|
// C-style array size, e.g. bit [63:0] a [4]
|
2024-01-25 07:28:15 +01:00
|
|
|
node->dimensions.push_back({ 0, rnode->range_left, true });
|
2023-02-05 11:01:37 +01:00
|
|
|
width *= rnode->range_left;
|
|
|
|
|
} else {
|
2024-01-25 07:28:15 +01:00
|
|
|
width *= add_dimension(node, rnode);
|
2023-02-05 11:01:37 +01:00
|
|
|
}
|
2025-06-16 22:55:24 +02:00
|
|
|
add_dimension(node, node->children[0].get());
|
2020-06-07 19:28:45 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2022-12-03 19:54:47 +01:00
|
|
|
// The Yosys extension for unpacked arrays in packed structs / unions
|
|
|
|
|
// only supports memories, i.e. e.g. logic [7:0] a [256] - see above.
|
|
|
|
|
struct_array_packing_error(node);
|
2020-06-07 19:28:45 +02:00
|
|
|
}
|
2022-11-12 08:48:25 +01:00
|
|
|
} else {
|
2022-12-03 19:54:47 +01:00
|
|
|
// Vector
|
2025-06-16 22:55:24 +02:00
|
|
|
add_dimension(node, node->children[0].get());
|
2020-06-07 19:28:45 +02:00
|
|
|
}
|
|
|
|
|
// range nodes are now redundant
|
|
|
|
|
node->children.clear();
|
|
|
|
|
}
|
2022-11-30 20:04:45 +01:00
|
|
|
else if (node->children.size() > 0 && node->children[0]->type == AST_MULTIRANGE) {
|
2022-12-03 19:54:47 +01:00
|
|
|
// Packed array, e.g. bit [3:0][63:0] a
|
|
|
|
|
if (node->children.size() != 1) {
|
|
|
|
|
// The Yosys extension for unpacked arrays in packed structs / unions
|
|
|
|
|
// only supports memories, i.e. e.g. logic [7:0] a [256] - see above.
|
|
|
|
|
struct_array_packing_error(node);
|
|
|
|
|
}
|
|
|
|
|
width = 1;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& rnode : node->children[0]->children) {
|
|
|
|
|
width *= add_dimension(node, rnode.get());
|
2020-06-07 19:28:45 +02:00
|
|
|
}
|
2020-05-12 15:25:33 +02:00
|
|
|
// range nodes are now redundant
|
|
|
|
|
node->children.clear();
|
|
|
|
|
}
|
|
|
|
|
else if (node->range_left < 0) {
|
|
|
|
|
// 1 bit signal: bit, logic or reg
|
|
|
|
|
width = 1;
|
2024-01-25 07:28:15 +01:00
|
|
|
node->dimensions.push_back({ 0, width, false });
|
2020-05-12 15:25:33 +02:00
|
|
|
}
|
|
|
|
|
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 {
|
2023-04-04 11:53:50 +02:00
|
|
|
if (packed_width != width)
|
|
|
|
|
node->input_error("member %s of a packed union has %d bits, expecting %d\n", node->str.c_str(), width, packed_width);
|
2020-05-12 15:25:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
offset += width;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-25 07:28:15 +01:00
|
|
|
int width = is_union ? packed_width : offset;
|
|
|
|
|
|
|
|
|
|
snode->range_right = base_offset;
|
|
|
|
|
snode->range_left = base_offset + width - 1;
|
|
|
|
|
snode->range_valid = true;
|
2024-01-05 19:29:06 +01:00
|
|
|
snode->dimensions.push_back({ 0, width, false });
|
2024-01-25 07:28:15 +01:00
|
|
|
|
|
|
|
|
return width;
|
2020-06-07 19:28:45 +02:00
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
static std::unique_ptr<AstNode> node_int(int ival)
|
2020-06-07 19:28:45 +02:00
|
|
|
{
|
2020-06-08 21:34:52 +02:00
|
|
|
return AstNode::mkconst_int(ival, true);
|
2020-06-07 19:28:45 +02:00
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
static std::unique_ptr<AstNode> multiply_by_const(std::unique_ptr<AstNode> expr_node, int stride)
|
2020-06-08 21:34:52 +02:00
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
return std::make_unique<AstNode>(AST_MUL, std::move(expr_node), node_int(stride));
|
2020-06-08 21:34:52 +02:00
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
static std::unique_ptr<AstNode> normalize_index(AstNode *expr, AstNode *decl_node, int dimension)
|
2020-06-08 21:34:52 +02:00
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
auto new_expr = expr->clone();
|
2022-12-04 06:54:22 +01:00
|
|
|
|
2024-01-25 07:28:15 +01:00
|
|
|
int offset = decl_node->dimensions[dimension].range_right;
|
2023-02-05 11:01:37 +01:00
|
|
|
if (offset) {
|
2025-06-16 22:55:24 +02:00
|
|
|
new_expr = std::make_unique<AstNode>(AST_SUB, std::move(new_expr), node_int(offset));
|
2023-02-05 11:01:37 +01:00
|
|
|
}
|
|
|
|
|
|
2024-01-25 07:28:15 +01:00
|
|
|
// Packed dimensions are normally indexed by lsb, while unpacked dimensions are normally indexed by msb.
|
|
|
|
|
if ((dimension < decl_node->unpacked_dimensions) ^ decl_node->dimensions[dimension].range_swapped) {
|
|
|
|
|
// Swap the index if the dimension is declared the "wrong" way.
|
|
|
|
|
int left = decl_node->dimensions[dimension].range_width - 1;
|
2025-06-16 22:55:24 +02:00
|
|
|
new_expr = std::make_unique<AstNode>(AST_SUB, node_int(left), std::move(new_expr));
|
2022-11-12 08:48:25 +01:00
|
|
|
}
|
2022-12-04 06:54:22 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
return new_expr;
|
2022-12-03 19:54:47 +01:00
|
|
|
}
|
2022-11-12 08:48:25 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
static std::unique_ptr<AstNode> index_offset(std::unique_ptr<AstNode> offset, AstNode *rnode, AstNode *decl_node, int dimension, int &stride)
|
2022-12-03 19:54:47 +01:00
|
|
|
{
|
2024-01-25 07:28:15 +01:00
|
|
|
stride /= decl_node->dimensions[dimension].range_width;
|
2025-06-16 22:55:24 +02:00
|
|
|
auto right = normalize_index(rnode->children.back().get(), decl_node, dimension);
|
|
|
|
|
auto add_offset = stride > 1 ? multiply_by_const(std::move(right), stride) : std::move(right);
|
|
|
|
|
return offset ? std::make_unique<AstNode>(AST_ADD, std::move(offset), std::move(add_offset)) : std::move(add_offset);
|
2020-06-08 21:34:52 +02:00
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
static std::unique_ptr<AstNode> index_msb_offset(std::unique_ptr<AstNode> lsb_offset, AstNode *rnode, AstNode *decl_node, int dimension, int stride)
|
2020-06-08 21:34:52 +02:00
|
|
|
{
|
2022-12-03 19:54:47 +01:00
|
|
|
log_assert(rnode->children.size() <= 2);
|
|
|
|
|
|
|
|
|
|
// Offset to add to LSB
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> add_offset;
|
2022-12-03 19:54:47 +01:00
|
|
|
if (rnode->children.size() == 1) {
|
|
|
|
|
// Index, e.g. s.a[i]
|
2024-01-25 07:28:15 +01:00
|
|
|
add_offset = node_int(stride - 1);
|
2022-12-03 19:54:47 +01:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// rnode->children.size() == 2
|
|
|
|
|
// Slice, e.g. s.a[i:j]
|
2025-06-16 22:55:24 +02:00
|
|
|
auto left = normalize_index(rnode->children[0].get(), decl_node, dimension);
|
|
|
|
|
auto right = normalize_index(rnode->children[1].get(), decl_node, dimension);
|
|
|
|
|
add_offset = std::make_unique<AstNode>(AST_SUB, std::move(left), std::move(right));
|
2022-12-04 06:54:22 +01:00
|
|
|
if (stride > 1) {
|
2022-12-03 19:54:47 +01:00
|
|
|
// offset = (msb - lsb + 1)*stride - 1
|
2025-06-16 22:55:24 +02:00
|
|
|
auto slice_width = std::make_unique<AstNode>(AST_ADD, std::move(add_offset), node_int(1));
|
|
|
|
|
add_offset = std::make_unique<AstNode>(AST_SUB, multiply_by_const(std::move(slice_width), stride), node_int(1));
|
2022-11-23 16:31:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
return std::make_unique<AstNode>(AST_ADD, std::move(lsb_offset), std::move(add_offset));
|
2020-06-07 19:28:45 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-08 21:34:52 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> AstNode::make_index_range(AstNode *decl_node, bool unpacked_range)
|
2020-06-07 19:28:45 +02:00
|
|
|
{
|
|
|
|
|
// 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
|
2024-01-25 07:28:15 +01:00
|
|
|
if (children.empty()) {
|
2020-06-07 19:28:45 +02:00
|
|
|
// no range operations apply, return the whole width
|
2024-01-25 07:28:15 +01:00
|
|
|
return make_range(decl_node->range_left - decl_node->range_right, 0);
|
2020-06-07 19:28:45 +02:00
|
|
|
}
|
2022-12-03 19:54:47 +01:00
|
|
|
|
2024-01-25 07:28:15 +01:00
|
|
|
log_assert(children.size() == 1);
|
2022-12-03 19:54:47 +01:00
|
|
|
|
|
|
|
|
// Range operations
|
2025-06-16 22:55:24 +02:00
|
|
|
AstNode *rnode = children[0].get();
|
|
|
|
|
std::unique_ptr<AstNode> offset = nullptr;
|
2024-01-25 07:28:15 +01:00
|
|
|
int dim = unpacked_range ? 0 : decl_node->unpacked_dimensions;
|
|
|
|
|
int max_dim = unpacked_range ? decl_node->unpacked_dimensions : GetSize(decl_node->dimensions);
|
|
|
|
|
|
|
|
|
|
int stride = 1;
|
|
|
|
|
for (int i = dim; i < max_dim; i++) {
|
|
|
|
|
stride *= decl_node->dimensions[i].range_width;
|
|
|
|
|
}
|
2022-12-03 19:54:47 +01:00
|
|
|
|
|
|
|
|
// Calculate LSB offset for the final index / slice
|
|
|
|
|
if (rnode->type == AST_RANGE) {
|
2025-06-16 22:55:24 +02:00
|
|
|
offset = index_offset(std::move(offset), rnode, decl_node, dim, stride);
|
2020-06-08 21:34:52 +02:00
|
|
|
}
|
2022-12-03 19:54:47 +01:00
|
|
|
else if (rnode->type == AST_MULTIRANGE) {
|
|
|
|
|
// Add offset for each dimension
|
2024-01-25 07:28:15 +01:00
|
|
|
AstNode *mrnode = rnode;
|
|
|
|
|
int stop_dim = std::min(GetSize(mrnode->children), max_dim);
|
|
|
|
|
for (; dim < stop_dim; dim++) {
|
2025-06-16 22:55:24 +02:00
|
|
|
rnode = mrnode->children[dim].get();
|
|
|
|
|
offset = index_offset(std::move(offset), rnode, decl_node, dim, stride);
|
2022-12-03 19:54:47 +01:00
|
|
|
}
|
2024-01-25 07:28:15 +01:00
|
|
|
dim--; // Step back to the final index / slice
|
2020-06-07 19:28:45 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2024-01-25 07:28:15 +01:00
|
|
|
input_error("Unsupported range operation for %s\n", str.c_str());
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode>index_range = std::make_unique<AstNode>(AST_RANGE);
|
2024-01-25 07:28:15 +01:00
|
|
|
|
|
|
|
|
if (!unpacked_range && (stride > 1 || GetSize(rnode->children) == 2)) {
|
|
|
|
|
// Calculate MSB offset for the final index / slice of packed dimensions.
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode>msb_offset = index_msb_offset(offset->clone(), rnode, decl_node, dim, stride);
|
|
|
|
|
index_range->children.push_back(std::move(msb_offset));
|
2020-06-07 19:28:45 +02:00
|
|
|
}
|
2022-12-03 19:54:47 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
index_range->children.push_back(std::move(offset));
|
2022-12-03 19:54:47 +01:00
|
|
|
|
2024-01-25 07:28:15 +01:00
|
|
|
return index_range;
|
2020-06-07 19:28:45 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-25 07:28:15 +01:00
|
|
|
AstNode *AstNode::get_struct_member() const
|
2023-02-28 18:45:55 +01:00
|
|
|
{
|
2024-01-25 07:28:15 +01:00
|
|
|
AstNode *member_node;
|
2025-06-16 22:55:24 +02:00
|
|
|
if (attributes.count(ID::wiretype) && (member_node = attributes.at(ID::wiretype).get()) &&
|
|
|
|
|
(member_node->type == AST_STRUCT_ITEM || member_node->type == AST_STRUCT || member_node->type == AST_UNION))
|
2023-02-28 18:45:55 +01:00
|
|
|
{
|
|
|
|
|
return member_node;
|
|
|
|
|
}
|
2024-01-25 07:28:15 +01:00
|
|
|
return nullptr;
|
2023-02-28 18:45:55 +01:00
|
|
|
}
|
|
|
|
|
|
2020-05-12 15:25:33 +02:00
|
|
|
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);
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto &node : snode->children) {
|
2022-02-14 14:34:20 +01:00
|
|
|
auto member_name = name + "." + node->str;
|
2025-06-16 22:55:24 +02:00
|
|
|
current_scope[member_name] = node.get();
|
2020-05-12 15:25:33 +02:00
|
|
|
if (node->type != AST_STRUCT_ITEM) {
|
|
|
|
|
// embedded struct or union
|
2025-06-16 22:55:24 +02:00
|
|
|
add_members_to_scope(node.get(), name + "." + node->str);
|
2020-05-12 15:25:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> make_packed_struct(AstNode *template_node, std::string &name, decltype(AstNode::attributes) &attributes)
|
2020-05-08 15:40:49 +02:00
|
|
|
{
|
|
|
|
|
// create a wire for the packed struct
|
2025-06-16 22:55:24 +02:00
|
|
|
auto wnode = std::make_unique<AstNode>(AST_WIRE, make_range(template_node->range_left, 0));
|
2020-05-08 15:40:49 +02:00
|
|
|
wnode->str = name;
|
|
|
|
|
wnode->is_logic = true;
|
|
|
|
|
wnode->range_valid = true;
|
2020-05-12 15:25:33 +02:00
|
|
|
wnode->is_signed = template_node->is_signed;
|
Handling of attributes for struct / union variables
(* nowrshmsk *) on a struct / union variable now affects dynamic
bit slice assignments to members of the struct / union.
(* nowrshmsk *) can in some cases yield significant resource savings; the
combination of pipeline shifting and indexed writes is an example of this.
Constructs similar to the one below can benefit from (* nowrshmsk *), and
in addition it is no longer necessary to split out the shift assignments
on separate lines in order to avoid the error message "ERROR: incompatible
mix of lookahead and non-lookahead IDs in LHS expression."
always_ff @(posedge clk) begin
if (rotate) begin
{ v5, v4, v3, v2, v1, v0 } <= { v4, v3, v2, v1, v0, v5 };
if (res) begin
v0.bytes <= '0;
end else if (w) begin
v0.bytes[addr] <= data;
end
end
end
2023-03-02 19:02:30 +01:00
|
|
|
for (auto &pair : attributes) {
|
2023-04-05 11:00:07 +02:00
|
|
|
wnode->set_attribute(pair.first, pair.second->clone());
|
Handling of attributes for struct / union variables
(* nowrshmsk *) on a struct / union variable now affects dynamic
bit slice assignments to members of the struct / union.
(* nowrshmsk *) can in some cases yield significant resource savings; the
combination of pipeline shifting and indexed writes is an example of this.
Constructs similar to the one below can benefit from (* nowrshmsk *), and
in addition it is no longer necessary to split out the shift assignments
on separate lines in order to avoid the error message "ERROR: incompatible
mix of lookahead and non-lookahead IDs in LHS expression."
always_ff @(posedge clk) begin
if (rotate) begin
{ v5, v4, v3, v2, v1, v0 } <= { v4, v3, v2, v1, v0, v5 };
if (res) begin
v0.bytes <= '0;
end else if (w) begin
v0.bytes[addr] <= data;
end
end
end
2023-03-02 19:02:30 +01:00
|
|
|
}
|
2024-01-25 07:28:15 +01:00
|
|
|
// resolve packed dimension
|
|
|
|
|
while (wnode->simplify(true, 1, -1, false)) {}
|
2020-05-08 15:40:49 +02:00
|
|
|
// make sure this node is the one in scope for this name
|
2025-06-16 22:55:24 +02:00
|
|
|
current_scope[name] = wnode.get();
|
2020-05-12 15:25:33 +02:00
|
|
|
// add all the struct members to scope under the wire's name
|
|
|
|
|
add_members_to_scope(template_node, name);
|
2020-05-08 15:40:49 +02:00
|
|
|
return wnode;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
static void prepend_ranges(std::unique_ptr<AstNode> &range, AstNode *range_add)
|
2024-01-01 01:18:00 +01:00
|
|
|
{
|
|
|
|
|
// Convert range to multirange.
|
|
|
|
|
if (range->type == AST_RANGE)
|
2025-06-16 22:55:24 +02:00
|
|
|
range = std::make_unique<AstNode>(AST_MULTIRANGE, std::move(range));
|
2024-01-01 01:18:00 +01:00
|
|
|
|
|
|
|
|
// Add range or ranges.
|
|
|
|
|
if (range_add->type == AST_RANGE)
|
|
|
|
|
range->children.insert(range->children.begin(), range_add->clone());
|
|
|
|
|
else {
|
|
|
|
|
int i = 0;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : range_add->children)
|
2024-01-01 01:18:00 +01:00
|
|
|
range->children.insert(range->children.begin() + i++, child->clone());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-25 05:18:24 +02:00
|
|
|
// check if a node or its children contains an assignment to the given variable
|
|
|
|
|
static bool node_contains_assignment_to(const AstNode* node, const AstNode* var)
|
|
|
|
|
{
|
|
|
|
|
if (node->type == AST_ASSIGN_EQ || node->type == AST_ASSIGN_LE) {
|
|
|
|
|
// current node is iteslf an assignment
|
|
|
|
|
log_assert(node->children.size() >= 2);
|
2025-06-16 22:55:24 +02:00
|
|
|
const AstNode* lhs = node->children[0].get();
|
2020-07-25 05:18:24 +02:00
|
|
|
if (lhs->type == AST_IDENTIFIER && lhs->str == var->str)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : node->children) {
|
2020-07-25 05:18:24 +02:00
|
|
|
// if this child shadows the given variable
|
2025-06-16 22:55:24 +02:00
|
|
|
if (child.get() != var && child->str == var->str && child->type == AST_WIRE)
|
2020-07-25 05:18:24 +02:00
|
|
|
break; // skip the remainder of this block/scope
|
|
|
|
|
// depth-first short circuit
|
2025-06-16 22:55:24 +02:00
|
|
|
if (!node_contains_assignment_to(child.get(), var))
|
2020-07-25 05:18:24 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
static std::string prefix_id(const std::string &prefix, const std::string &str)
|
|
|
|
|
{
|
|
|
|
|
log_assert(!prefix.empty() && (prefix.front() == '$' || prefix.front() == '\\'));
|
|
|
|
|
log_assert(!str.empty() && (str.front() == '$' || str.front() == '\\'));
|
|
|
|
|
log_assert(prefix.back() == '.');
|
|
|
|
|
if (str.front() == '\\')
|
|
|
|
|
return prefix + str.substr(1);
|
|
|
|
|
return prefix + str;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-20 02:46:26 +02:00
|
|
|
// 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))
|
2023-04-05 11:00:07 +02:00
|
|
|
set_attribute(ID::reprocess_after, AstNode::mkconst_str(modname));
|
2021-10-20 02:46:26 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const AstNode *celltype = nullptr;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children)
|
2021-10-20 02:46:26 +02:00
|
|
|
if (child->type == AST_CELLTYPE) {
|
2025-06-16 22:55:24 +02:00
|
|
|
celltype = child.get();
|
2021-10-20 02:46:26 +02:00
|
|
|
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;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children) {
|
2021-10-20 02:46:26 +02:00
|
|
|
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;
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
const AstNode *value = child->children[0].get();
|
2021-10-20 02:46:26 +02:00
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
if (node->type == AST_CONSTANT)
|
|
|
|
|
return node->is_unsized;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : node->children)
|
|
|
|
|
if (contains_unbased_unsized(child.get()))
|
2021-10-20 02:46:26 +02:00
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// adds a wire to the current module with the given name that matches the
|
|
|
|
|
// dimensions of the given wire reference
|
|
|
|
|
void add_wire_for_ref(const RTLIL::Wire *ref, const std::string &str)
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> left = AstNode::mkconst_int(ref->width - 1 + ref->start_offset, true);
|
|
|
|
|
std::unique_ptr<AstNode> right = AstNode::mkconst_int(ref->start_offset, true);
|
2021-10-20 02:46:26 +02:00
|
|
|
if (ref->upto)
|
|
|
|
|
std::swap(left, right);
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> range = std::make_unique<AstNode>(AST_RANGE, std::move(left), std::move(right));
|
2021-10-20 02:46:26 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> wire = std::make_unique<AstNode>(AST_WIRE, std::move(range));
|
2021-10-20 02:46:26 +02:00
|
|
|
wire->is_signed = ref->is_signed;
|
|
|
|
|
wire->is_logic = true;
|
|
|
|
|
wire->str = str;
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
current_scope[str] = wire.get();
|
|
|
|
|
current_ast_mod->children.push_back(std::move(wire));
|
2021-10-20 02:46:26 +02:00
|
|
|
}
|
|
|
|
|
|
2022-01-07 06:04:00 +01:00
|
|
|
enum class IdentUsage {
|
|
|
|
|
NotReferenced, // target variable is neither read or written in the block
|
|
|
|
|
Assigned, // target variable is always assigned before use
|
|
|
|
|
SyncRequired, // target variable may be used before it has been assigned
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// determines whether a local variable a block is always assigned before it is
|
|
|
|
|
// used, meaning the nosync attribute can automatically be added to that
|
|
|
|
|
// variable
|
|
|
|
|
static IdentUsage always_asgn_before_use(const AstNode *node, const std::string &target)
|
|
|
|
|
{
|
|
|
|
|
// This variable has been referenced before it has necessarily been assigned
|
|
|
|
|
// a value in this procedure.
|
|
|
|
|
if (node->type == AST_IDENTIFIER && node->str == target)
|
|
|
|
|
return IdentUsage::SyncRequired;
|
|
|
|
|
|
|
|
|
|
// For case statements (which are also used for if/else), we check each
|
|
|
|
|
// possible branch. If the variable is assigned in all branches, then it is
|
|
|
|
|
// assigned, and a sync isn't required. If it used before assignment in any
|
|
|
|
|
// branch, then a sync is required.
|
|
|
|
|
if (node->type == AST_CASE) {
|
|
|
|
|
bool all_defined = true;
|
|
|
|
|
bool any_used = false;
|
|
|
|
|
bool has_default = false;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : node->children) {
|
2022-01-07 06:04:00 +01:00
|
|
|
if (child->type == AST_COND && child->children.at(0)->type == AST_DEFAULT)
|
|
|
|
|
has_default = true;
|
2025-06-16 22:55:24 +02:00
|
|
|
IdentUsage nested = always_asgn_before_use(child.get(), target);
|
2022-01-07 06:04:00 +01:00
|
|
|
if (nested != IdentUsage::Assigned && child->type == AST_COND)
|
|
|
|
|
all_defined = false;
|
|
|
|
|
if (nested == IdentUsage::SyncRequired)
|
|
|
|
|
any_used = true;
|
|
|
|
|
}
|
|
|
|
|
if (any_used)
|
|
|
|
|
return IdentUsage::SyncRequired;
|
|
|
|
|
else if (all_defined && has_default)
|
|
|
|
|
return IdentUsage::Assigned;
|
|
|
|
|
else
|
|
|
|
|
return IdentUsage::NotReferenced;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if this is an assignment to the target variable. For simplicity, we
|
|
|
|
|
// don't analyze sub-ranges of the variable.
|
|
|
|
|
if (node->type == AST_ASSIGN_EQ) {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto& ident = node->children.at(0);
|
2022-01-07 06:04:00 +01:00
|
|
|
if (ident->type == AST_IDENTIFIER && ident->str == target)
|
|
|
|
|
return IdentUsage::Assigned;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : node->children) {
|
|
|
|
|
IdentUsage nested = always_asgn_before_use(child.get(), target);
|
2022-01-07 06:04:00 +01:00
|
|
|
if (nested != IdentUsage::NotReferenced)
|
|
|
|
|
return nested;
|
|
|
|
|
}
|
|
|
|
|
return IdentUsage::NotReferenced;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> AstNode::clone_at_zero()
|
2023-06-21 13:45:42 +02:00
|
|
|
{
|
|
|
|
|
int width_hint;
|
|
|
|
|
bool sign_hint;
|
|
|
|
|
AstNode *pointee;
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case AST_IDENTIFIER:
|
|
|
|
|
if (id2ast)
|
|
|
|
|
pointee = id2ast;
|
|
|
|
|
else if (current_scope.count(str))
|
|
|
|
|
pointee = current_scope[str];
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (pointee->type != AST_WIRE &&
|
|
|
|
|
pointee->type != AST_AUTOWIRE &&
|
|
|
|
|
pointee->type != AST_MEMORY)
|
|
|
|
|
break;
|
|
|
|
|
|
2024-01-11 10:39:28 +01:00
|
|
|
YS_FALLTHROUGH
|
2023-06-21 13:45:42 +02:00
|
|
|
case AST_MEMRD:
|
|
|
|
|
detectSignWidth(width_hint, sign_hint);
|
|
|
|
|
return mkconst_int(0, sign_hint, width_hint);
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto that = clone();
|
2023-06-21 13:45:42 +02:00
|
|
|
for (auto &it : that->children)
|
|
|
|
|
it = it->clone_at_zero();
|
|
|
|
|
for (auto &it : that->attributes)
|
|
|
|
|
it.second = it.second->clone();
|
2023-04-05 11:00:07 +02:00
|
|
|
|
|
|
|
|
that->set_in_lvalue_flag(false);
|
|
|
|
|
that->set_in_param_flag(false);
|
|
|
|
|
that->fixup_hierarchy_flags();
|
|
|
|
|
|
2023-06-21 13:45:42 +02:00
|
|
|
return that;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-04 11:34:17 +02:00
|
|
|
static bool try_determine_range_width(AstNode *range, int &result_width)
|
|
|
|
|
{
|
|
|
|
|
log_assert(range->type == AST_RANGE);
|
|
|
|
|
|
|
|
|
|
if (range->children.size() == 1) {
|
|
|
|
|
result_width = 1;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> left_at_zero_ast = range->children[0]->clone_at_zero();
|
|
|
|
|
std::unique_ptr<AstNode> right_at_zero_ast = range->children[1]->clone_at_zero();
|
2023-04-04 11:34:17 +02:00
|
|
|
|
2023-04-04 22:59:44 +02:00
|
|
|
while (left_at_zero_ast->simplify(true, 1, -1, false)) {}
|
|
|
|
|
while (right_at_zero_ast->simplify(true, 1, -1, false)) {}
|
2023-04-04 11:34:17 +02:00
|
|
|
|
|
|
|
|
bool ok = false;
|
|
|
|
|
if (left_at_zero_ast->type == AST_CONSTANT
|
|
|
|
|
&& right_at_zero_ast->type == AST_CONSTANT) {
|
|
|
|
|
ok = true;
|
|
|
|
|
result_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-07 06:04:00 +01:00
|
|
|
static const std::string auto_nosync_prefix = "\\AutoNosync";
|
|
|
|
|
|
|
|
|
|
// mark a local variable in an always_comb block for automatic nosync
|
|
|
|
|
// consideration
|
|
|
|
|
static void mark_auto_nosync(AstNode *block, const AstNode *wire)
|
|
|
|
|
{
|
|
|
|
|
log_assert(block->type == AST_BLOCK);
|
|
|
|
|
log_assert(wire->type == AST_WIRE);
|
2023-04-05 11:00:07 +02:00
|
|
|
block->set_attribute(auto_nosync_prefix + wire->str, AstNode::mkconst_int(1, false));
|
2022-01-07 06:04:00 +01:00
|
|
|
}
|
|
|
|
|
|
2022-02-22 16:57:08 +01:00
|
|
|
// block names can be prefixed with an explicit scope during elaboration
|
|
|
|
|
static bool is_autonamed_block(const std::string &str) {
|
|
|
|
|
size_t last_dot = str.rfind('.');
|
|
|
|
|
// unprefixed names: autonamed if the first char is a dollar sign
|
|
|
|
|
if (last_dot == std::string::npos)
|
|
|
|
|
return str.at(0) == '$'; // e.g., `$fordecl_block$1`
|
|
|
|
|
// prefixed names: autonamed if the final chunk begins with a dollar sign
|
|
|
|
|
return str.rfind(".$") == last_dot; // e.g., `\foo.bar.$fordecl_block$1`
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-07 06:04:00 +01:00
|
|
|
// check a procedural block for auto-nosync markings, remove them, and add
|
|
|
|
|
// nosync to local variables as necessary
|
|
|
|
|
static void check_auto_nosync(AstNode *node)
|
|
|
|
|
{
|
|
|
|
|
std::vector<RTLIL::IdString> attrs_to_drop;
|
|
|
|
|
for (const auto& elem : node->attributes) {
|
|
|
|
|
// skip attributes that don't begin with the prefix
|
|
|
|
|
if (elem.first.compare(0, auto_nosync_prefix.size(),
|
|
|
|
|
auto_nosync_prefix.c_str()))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// delete and remove the attribute once we're done iterating
|
|
|
|
|
attrs_to_drop.push_back(elem.first);
|
|
|
|
|
|
|
|
|
|
// find the wire based on the attribute
|
|
|
|
|
std::string wire_name = elem.first.substr(auto_nosync_prefix.size());
|
|
|
|
|
auto it = current_scope.find(wire_name);
|
|
|
|
|
if (it == current_scope.end())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// analyze the usage of the local variable in this block
|
|
|
|
|
IdentUsage ident_usage = always_asgn_before_use(node, wire_name);
|
|
|
|
|
if (ident_usage != IdentUsage::Assigned)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// mark the wire with `nosync`
|
|
|
|
|
AstNode *wire = it->second;
|
|
|
|
|
log_assert(wire->type == AST_WIRE);
|
2023-04-05 11:00:07 +02:00
|
|
|
wire->set_attribute(ID::nosync, AstNode::mkconst_int(1, false));
|
2022-01-07 06:04:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// remove the attributes we've "consumed"
|
|
|
|
|
for (const RTLIL::IdString &str : attrs_to_drop) {
|
|
|
|
|
auto it = node->attributes.find(str);
|
|
|
|
|
node->attributes.erase(it);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check local variables in any nested blocks
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : node->children)
|
|
|
|
|
check_auto_nosync(child.get());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AstNode::null_check()
|
|
|
|
|
{
|
|
|
|
|
for (auto& child : children) {
|
|
|
|
|
// if (!child)
|
|
|
|
|
// VALGRIND_PRINTF_BACKTRACE("null child");
|
|
|
|
|
log_assert((bool) child);
|
|
|
|
|
child->null_check();
|
|
|
|
|
}
|
2022-01-07 06:04:00 +01:00
|
|
|
}
|
|
|
|
|
|
2015-08-14 10:56:05 +02:00
|
|
|
// convert the AST into a simpler AST that has all parameters substituted by their
|
2013-01-05 11:13:26 +01:00
|
|
|
// values, unrolled for-loops, expanded generate blocks, etc. when this function
|
|
|
|
|
// is done with an AST it can be converted into RTLIL using genRTLIL().
|
|
|
|
|
//
|
|
|
|
|
// this function also does all name resolving and sets the id2ast member of all
|
|
|
|
|
// nodes that link to a different node using names and lexical scoping.
|
2023-04-04 22:59:44 +02:00
|
|
|
bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hint)
|
2013-01-05 11:13:26 +01:00
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
// null_check();
|
2015-02-13 12:33:12 +01:00
|
|
|
static int recursion_counter = 0;
|
2015-02-20 10:33:20 +01:00
|
|
|
static bool deep_recursion_warning = false;
|
|
|
|
|
|
|
|
|
|
if (recursion_counter++ == 1000 && deep_recursion_warning) {
|
2021-02-25 22:02:55 +01:00
|
|
|
log_warning("Deep recursion in AST simplifier.\nDoes this design contain overly long or deeply nested expressions, or excessive recursion?\n");
|
2015-02-20 10:33:20 +01:00
|
|
|
deep_recursion_warning = false;
|
|
|
|
|
}
|
2015-02-13 12:33:12 +01:00
|
|
|
|
2021-02-12 20:25:34 +01:00
|
|
|
static bool unevaluated_tern_branch = false;
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> newNode = nullptr;
|
2013-01-05 11:13:26 +01:00
|
|
|
bool did_something = false;
|
|
|
|
|
|
2014-06-14 12:00:47 +02:00
|
|
|
#if 0
|
|
|
|
|
log("-------------\n");
|
2020-02-23 08:19:52 +01:00
|
|
|
log("AST simplify[%d] depth %d at %s:%d on %s %p:\n", stage, recursion_counter, filename.c_str(), location.first_line, type2str(type).c_str(), this);
|
2023-04-04 22:59:44 +02:00
|
|
|
log("const_fold=%d, stage=%d, width_hint=%d, sign_hint=%d\n",
|
|
|
|
|
int(const_fold), int(stage), int(width_hint), int(sign_hint));
|
2025-06-16 22:55:24 +02:00
|
|
|
// dumpAst(nullptr, "> ");
|
2014-06-14 12:00:47 +02:00
|
|
|
#endif
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
if (stage == 0)
|
|
|
|
|
{
|
2018-10-11 23:33:31 +02:00
|
|
|
log_assert(type == AST_MODULE || type == AST_INTERFACE);
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2015-02-20 10:33:20 +01:00
|
|
|
deep_recursion_warning = true;
|
2023-04-04 22:59:44 +02:00
|
|
|
while (simplify(const_fold, 1, width_hint, sign_hint)) { }
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2020-04-02 18:51:32 +02:00
|
|
|
if (!flag_nomem2reg && !get_bool_attribute(ID::nomem2reg))
|
2013-01-05 11:13:26 +01:00
|
|
|
{
|
2014-12-29 03:11:50 +01:00
|
|
|
dict<AstNode*, pool<std::string>> mem2reg_places;
|
|
|
|
|
dict<AstNode*, uint32_t> mem2reg_candidates, dummy_proc_flags;
|
2013-11-21 13:49:00 +01:00
|
|
|
uint32_t flags = flag_mem2reg ? AstNode::MEM2REG_FL_ALL : 0;
|
|
|
|
|
mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, dummy_proc_flags, flags);
|
|
|
|
|
|
2014-12-29 03:11:50 +01:00
|
|
|
pool<AstNode*> mem2reg_set;
|
2013-11-21 13:49:00 +01:00
|
|
|
for (auto &it : mem2reg_candidates)
|
|
|
|
|
{
|
|
|
|
|
AstNode *mem = it.first;
|
|
|
|
|
uint32_t memflags = it.second;
|
2015-02-14 11:21:12 +01:00
|
|
|
bool this_nomeminit = flag_nomeminit;
|
2014-07-28 11:08:55 +02:00
|
|
|
log_assert((memflags & ~0x00ffff00) == 0);
|
2013-11-21 13:49:00 +01:00
|
|
|
|
2020-04-02 18:51:32 +02:00
|
|
|
if (mem->get_bool_attribute(ID::nomem2reg))
|
2013-11-21 13:49:00 +01:00
|
|
|
continue;
|
|
|
|
|
|
2020-04-02 18:51:32 +02:00
|
|
|
if (mem->get_bool_attribute(ID::nomeminit) || get_bool_attribute(ID::nomeminit))
|
2015-02-14 11:21:12 +01:00
|
|
|
this_nomeminit = true;
|
|
|
|
|
|
2013-11-21 13:49:00 +01:00
|
|
|
if (memflags & AstNode::MEM2REG_FL_FORCED)
|
|
|
|
|
goto silent_activate;
|
|
|
|
|
|
|
|
|
|
if (memflags & AstNode::MEM2REG_FL_EQ2)
|
|
|
|
|
goto verbose_activate;
|
|
|
|
|
|
2013-11-21 21:26:56 +01:00
|
|
|
if (memflags & AstNode::MEM2REG_FL_SET_ASYNC)
|
|
|
|
|
goto verbose_activate;
|
|
|
|
|
|
2015-02-14 11:21:12 +01:00
|
|
|
if ((memflags & AstNode::MEM2REG_FL_SET_INIT) && (memflags & AstNode::MEM2REG_FL_SET_ELSE) && this_nomeminit)
|
2013-11-21 13:49:00 +01:00
|
|
|
goto verbose_activate;
|
|
|
|
|
|
2014-06-17 21:39:25 +02:00
|
|
|
if (memflags & AstNode::MEM2REG_FL_CMPLX_LHS)
|
|
|
|
|
goto verbose_activate;
|
|
|
|
|
|
2019-03-01 22:35:09 +01:00
|
|
|
if ((memflags & AstNode::MEM2REG_FL_CONST_LHS) && !(memflags & AstNode::MEM2REG_FL_VAR_LHS))
|
|
|
|
|
goto verbose_activate;
|
|
|
|
|
|
2013-11-21 21:26:56 +01:00
|
|
|
// log("Note: Not replacing memory %s with list of registers (flags=0x%08lx).\n", mem->str.c_str(), long(memflags));
|
2013-11-21 13:49:00 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
verbose_activate:
|
|
|
|
|
if (mem2reg_set.count(mem) == 0) {
|
2014-12-27 03:26:30 +01:00
|
|
|
std::string message = stringf("Replacing memory %s with list of registers.", mem->str.c_str());
|
2013-11-21 13:49:00 +01:00
|
|
|
bool first_element = true;
|
|
|
|
|
for (auto &place : mem2reg_places[it.first]) {
|
2014-12-27 03:26:30 +01:00
|
|
|
message += stringf("%s%s", first_element ? " See " : ", ", place.c_str());
|
2013-11-21 13:49:00 +01:00
|
|
|
first_element = false;
|
|
|
|
|
}
|
2014-12-27 03:26:30 +01:00
|
|
|
log_warning("%s\n", message.c_str());
|
2013-11-21 13:49:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
silent_activate:
|
2013-11-21 21:26:56 +01:00
|
|
|
// log("Note: Replacing memory %s with list of registers (flags=0x%08lx).\n", mem->str.c_str(), long(memflags));
|
2013-11-21 13:49:00 +01:00
|
|
|
mem2reg_set.insert(mem);
|
|
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
|
|
|
|
|
for (auto node : mem2reg_set)
|
|
|
|
|
{
|
|
|
|
|
int mem_width, mem_size, addr_bits;
|
|
|
|
|
node->meminfo(mem_width, mem_size, addr_bits);
|
|
|
|
|
|
2019-03-21 22:19:17 +01:00
|
|
|
int data_range_left = node->children[0]->range_left;
|
|
|
|
|
int data_range_right = node->children[0]->range_right;
|
|
|
|
|
|
|
|
|
|
if (node->children[0]->range_swapped)
|
|
|
|
|
std::swap(data_range_left, data_range_right);
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
for (int i = 0; i < mem_size; i++) {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto reg = std::make_unique<AstNode>(AST_WIRE, std::make_unique<AstNode>(AST_RANGE,
|
2019-03-21 22:19:17 +01:00
|
|
|
mkconst_int(data_range_left, true), mkconst_int(data_range_right, true)));
|
2013-01-05 11:13:26 +01:00
|
|
|
reg->str = stringf("%s[%d]", node->str.c_str(), i);
|
|
|
|
|
reg->is_reg = true;
|
|
|
|
|
reg->is_signed = node->is_signed;
|
2019-08-21 22:36:01 +02:00
|
|
|
for (auto &it : node->attributes)
|
2020-04-02 18:51:32 +02:00
|
|
|
if (it.first != ID::mem2reg)
|
2023-04-05 11:00:07 +02:00
|
|
|
reg->set_attribute(it.first, it.second->clone());
|
2019-08-21 22:36:01 +02:00
|
|
|
reg->filename = node->filename;
|
2020-02-23 08:19:52 +01:00
|
|
|
reg->location = node->location;
|
2023-04-04 22:59:44 +02:00
|
|
|
while (reg->simplify(true, 1, -1, false)) { }
|
2025-06-16 22:55:24 +02:00
|
|
|
children.push_back(std::move(reg));
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
AstNode* async_block = nullptr;
|
|
|
|
|
while (mem2reg_as_needed_pass2(mem2reg_set, this, nullptr, async_block)) { }
|
2016-05-27 17:25:33 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
mem2reg_remove(mem2reg_set);
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
|
2023-04-04 22:59:44 +02:00
|
|
|
while (simplify(const_fold, 2, width_hint, sign_hint)) { }
|
2015-02-13 12:33:12 +01:00
|
|
|
recursion_counter--;
|
2013-01-05 11:13:26 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
current_filename = filename;
|
|
|
|
|
|
|
|
|
|
// we do not look inside a task or function
|
2015-08-14 10:56:05 +02:00
|
|
|
// (but as soon as a task or function is instantiated we process the generated AST as usual)
|
2015-02-13 12:33:12 +01:00
|
|
|
if (type == AST_FUNCTION || type == AST_TASK) {
|
|
|
|
|
recursion_counter--;
|
2013-01-05 11:13:26 +01:00
|
|
|
return false;
|
2015-02-13 12:33:12 +01:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2015-08-14 10:56:05 +02:00
|
|
|
// deactivate all calls to non-synthesis system tasks
|
2016-03-21 16:19:51 +01:00
|
|
|
// note that $display, $finish, and $stop are used for synthesis-time DRC so they're not in this list
|
|
|
|
|
if ((type == AST_FCALL || type == AST_TCALL) && (str == "$strobe" || str == "$monitor" || str == "$time" ||
|
2015-01-15 13:08:19 +01:00
|
|
|
str == "$dumpfile" || str == "$dumpvars" || str == "$dumpon" || str == "$dumpoff" || str == "$dumpall")) {
|
2020-02-23 08:19:52 +01:00
|
|
|
log_file_warning(filename, location.first_line, "Ignoring call to system %s %s.\n", type == AST_FCALL ? "function" : "task", str.c_str());
|
2013-01-05 11:13:26 +01:00
|
|
|
delete_children();
|
|
|
|
|
str = std::string();
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-29 09:57:07 +01:00
|
|
|
if ((type == AST_TCALL) &&
|
2025-06-16 22:55:24 +02:00
|
|
|
(str == "$display" || str == "$displayb" || str == "$displayh" || str == "$displayo" ||
|
|
|
|
|
str == "$write" || str == "$writeb" || str == "$writeh" || str == "$writeo"))
|
2015-09-18 05:34:56 +02:00
|
|
|
{
|
2020-11-29 15:34:17 +01:00
|
|
|
if (!current_always) {
|
|
|
|
|
log_file_warning(filename, location.first_line, "System task `%s' outside initial or always block is unsupported.\n", str.c_str());
|
2024-01-11 10:32:44 +01:00
|
|
|
delete_children();
|
|
|
|
|
str = std::string();
|
2020-11-29 15:34:17 +01:00
|
|
|
} else {
|
2024-01-11 10:32:44 +01:00
|
|
|
// simplify the expressions and convert them to a special cell later in genrtlil
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& node : children)
|
2023-04-04 22:59:44 +02:00
|
|
|
while (node->simplify(true, stage, -1, false)) {}
|
2024-01-11 10:32:44 +01:00
|
|
|
|
|
|
|
|
if (current_always->type == AST_INITIAL && !flag_nodisplay && stage == 2) {
|
|
|
|
|
int default_base = 10;
|
|
|
|
|
if (str.back() == 'b')
|
|
|
|
|
default_base = 2;
|
|
|
|
|
else if (str.back() == 'o')
|
|
|
|
|
default_base = 8;
|
|
|
|
|
else if (str.back() == 'h')
|
|
|
|
|
default_base = 16;
|
|
|
|
|
|
|
|
|
|
// when $display()/$write() functions are used in an initial block, print them during synthesis
|
|
|
|
|
Fmt fmt = processFormat(stage, /*sformat_like=*/false, default_base, /*first_arg_at=*/0, /*may_fail=*/true);
|
|
|
|
|
if (str.substr(0, 8) == "$display")
|
2024-03-28 07:30:16 +01:00
|
|
|
fmt.append_literal("\n");
|
2024-01-11 10:32:44 +01:00
|
|
|
log("%s", fmt.render().c_str());
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-29 15:34:17 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
2015-09-18 05:34:56 +02:00
|
|
|
}
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
// activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.)
|
2018-03-09 13:47:11 +01:00
|
|
|
if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_ENUM_ITEM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX || type == AST_TYPEDEF)
|
2014-02-14 20:45:30 +01:00
|
|
|
const_fold = true;
|
2018-03-09 13:47:11 +01:00
|
|
|
if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM || current_scope[str]->type == AST_ENUM_ITEM))
|
2013-01-05 11:13:26 +01:00
|
|
|
const_fold = true;
|
|
|
|
|
|
|
|
|
|
std::map<std::string, AstNode*> backup_scope;
|
|
|
|
|
|
|
|
|
|
// create name resolution entries for all objects with names
|
|
|
|
|
// also merge multiple declarations for the same wire (e.g. "output foobar; reg foobar;")
|
2023-02-13 00:25:39 +01:00
|
|
|
if (type == AST_MODULE || type == AST_INTERFACE) {
|
2013-01-05 11:13:26 +01:00
|
|
|
current_scope.clear();
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
std::set<std::string> existing;
|
|
|
|
|
int counter = 0;
|
|
|
|
|
label_genblks(existing, counter);
|
2013-01-05 11:13:26 +01:00
|
|
|
std::map<std::string, AstNode*> this_wire_scope;
|
2025-08-06 21:32:36 +02:00
|
|
|
|
2025-08-04 05:31:54 +02:00
|
|
|
// Process package imports after clearing the scope but before processing module declarations
|
2025-08-05 02:57:43 +02:00
|
|
|
for (size_t i = 0; i < children.size(); i++) {
|
|
|
|
|
AstNode *child = children[i];
|
2025-08-04 05:31:54 +02:00
|
|
|
if (child->type == AST_IMPORT) {
|
|
|
|
|
// Find the package in the design
|
|
|
|
|
AstNode *package_node = nullptr;
|
2025-08-06 21:32:36 +02:00
|
|
|
|
2025-08-04 05:48:33 +02:00
|
|
|
// First look in current_ast->children (for packages in same file)
|
2025-08-05 02:57:43 +02:00
|
|
|
if (current_ast != nullptr) {
|
|
|
|
|
for (auto &design_child : current_ast->children) {
|
|
|
|
|
if (design_child->type == AST_PACKAGE) {
|
|
|
|
|
if (design_child->str == child->str) {
|
|
|
|
|
package_node = design_child;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-08-04 05:48:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-06 21:32:36 +02:00
|
|
|
|
2025-08-04 05:48:33 +02:00
|
|
|
// If not found, look in design->verilog_packages (for packages from other files)
|
|
|
|
|
if (!package_node && simplify_design_context != nullptr) {
|
|
|
|
|
for (auto &design_package : simplify_design_context->verilog_packages) {
|
2025-08-05 02:57:43 +02:00
|
|
|
// Handle both with and without leading backslash
|
|
|
|
|
std::string package_name = design_package->str;
|
|
|
|
|
if (package_name[0] == '\\') {
|
|
|
|
|
package_name = package_name.substr(1);
|
|
|
|
|
}
|
|
|
|
|
if (package_name == child->str || design_package->str == child->str) {
|
2025-08-04 05:48:33 +02:00
|
|
|
package_node = design_package;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-08-04 05:31:54 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-08-06 21:32:36 +02:00
|
|
|
|
2025-08-04 05:31:54 +02:00
|
|
|
if (package_node) {
|
|
|
|
|
// Import all names from the package into current scope
|
|
|
|
|
for (auto &pkg_child : package_node->children) {
|
2025-08-06 21:32:36 +02:00
|
|
|
if (pkg_child->type == AST_PARAMETER || pkg_child->type == AST_LOCALPARAM ||
|
|
|
|
|
pkg_child->type == AST_TYPEDEF || pkg_child->type == AST_FUNCTION ||
|
2025-08-04 05:31:54 +02:00
|
|
|
pkg_child->type == AST_TASK || pkg_child->type == AST_ENUM) {
|
|
|
|
|
current_scope[pkg_child->str] = pkg_child;
|
|
|
|
|
}
|
|
|
|
|
if (pkg_child->type == AST_ENUM) {
|
|
|
|
|
for (auto enode : pkg_child->children) {
|
|
|
|
|
log_assert(enode->type==AST_ENUM_ITEM);
|
|
|
|
|
if (current_scope.count(enode->str) == 0)
|
|
|
|
|
current_scope[enode->str] = enode;
|
|
|
|
|
else
|
|
|
|
|
input_error("enum item %s already exists in current scope\n", enode->str.c_str());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-05 02:57:43 +02:00
|
|
|
// Remove the import node since it's been processed
|
|
|
|
|
delete child;
|
|
|
|
|
children.erase(children.begin() + i);
|
|
|
|
|
i--; // Adjust index since we removed an element
|
2025-08-04 05:31:54 +02:00
|
|
|
} else {
|
2025-08-05 02:57:43 +02:00
|
|
|
// If we can't find the package, just remove the import node to avoid errors later
|
|
|
|
|
log_warning("Package `%s' not found for import, removing import statement\n", child->str.c_str());
|
|
|
|
|
delete child;
|
|
|
|
|
children.erase(children.begin() + i);
|
|
|
|
|
i--; // Adjust index since we removed an element
|
2025-08-04 05:31:54 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
for (size_t i = 0; i < children.size(); i++) {
|
2025-06-16 22:55:24 +02:00
|
|
|
AstNode* node = children[i].get();
|
2019-09-19 21:43:13 +02:00
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
if (node->type == AST_WIRE) {
|
2019-03-02 21:36:46 +01:00
|
|
|
if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) {
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& c : node->children[0]->children) {
|
|
|
|
|
if (!c->is_simple_const_expr())
|
2023-04-05 11:00:07 +02:00
|
|
|
set_attribute(ID::dynports, AstNode::mkconst_int(1, true));
|
2019-03-02 21:36:46 +01:00
|
|
|
}
|
|
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
if (this_wire_scope.count(node->str) > 0) {
|
|
|
|
|
AstNode *first_node = this_wire_scope[node->str];
|
2018-03-09 09:35:33 +01:00
|
|
|
if (first_node->is_input && node->is_reg)
|
|
|
|
|
goto wires_are_incompatible;
|
2013-03-23 18:54:31 +01:00
|
|
|
if (!node->is_input && !node->is_output && node->is_reg && node->children.size() == 0)
|
|
|
|
|
goto wires_are_compatible;
|
2015-01-15 12:53:12 +01:00
|
|
|
if (first_node->children.size() == 0 && node->children.size() == 1 && node->children[0]->type == AST_RANGE) {
|
2025-06-16 22:55:24 +02:00
|
|
|
AstNode* r = node->children[0].get();
|
2015-01-15 12:53:12 +01:00
|
|
|
if (r->range_valid && r->range_left == 0 && r->range_right == 0) {
|
|
|
|
|
node->children.pop_back();
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
if (first_node->children.size() != node->children.size())
|
|
|
|
|
goto wires_are_incompatible;
|
|
|
|
|
for (size_t j = 0; j < node->children.size(); j++) {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto &n1 = first_node->children[j], &n2 = node->children[j];
|
2013-01-05 11:13:26 +01:00
|
|
|
if (n1->type == AST_RANGE && n2->type == AST_RANGE && n1->range_valid && n2->range_valid) {
|
|
|
|
|
if (n1->range_left != n2->range_left)
|
|
|
|
|
goto wires_are_incompatible;
|
|
|
|
|
if (n1->range_right != n2->range_right)
|
|
|
|
|
goto wires_are_incompatible;
|
|
|
|
|
} else if (*n1 != *n2)
|
|
|
|
|
goto wires_are_incompatible;
|
|
|
|
|
}
|
|
|
|
|
if (first_node->range_left != node->range_left)
|
|
|
|
|
goto wires_are_incompatible;
|
|
|
|
|
if (first_node->range_right != node->range_right)
|
|
|
|
|
goto wires_are_incompatible;
|
|
|
|
|
if (first_node->port_id == 0 && (node->is_input || node->is_output))
|
|
|
|
|
goto wires_are_incompatible;
|
2013-03-23 18:54:31 +01:00
|
|
|
wires_are_compatible:
|
2013-01-05 11:13:26 +01:00
|
|
|
if (node->is_input)
|
|
|
|
|
first_node->is_input = true;
|
|
|
|
|
if (node->is_output)
|
|
|
|
|
first_node->is_output = true;
|
|
|
|
|
if (node->is_reg)
|
|
|
|
|
first_node->is_reg = true;
|
2018-03-09 09:35:33 +01:00
|
|
|
if (node->is_logic)
|
|
|
|
|
first_node->is_logic = true;
|
2013-01-05 11:13:26 +01:00
|
|
|
if (node->is_signed)
|
|
|
|
|
first_node->is_signed = true;
|
|
|
|
|
for (auto &it : node->attributes) {
|
2023-04-05 11:00:07 +02:00
|
|
|
first_node->set_attribute(it.first, it.second->clone());
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
children.erase(children.begin()+(i--));
|
|
|
|
|
did_something = true;
|
|
|
|
|
continue;
|
2014-03-05 19:55:58 +01:00
|
|
|
wires_are_incompatible:
|
|
|
|
|
if (stage > 1)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Incompatible re-declaration of wire %s.\n", node->str.c_str());
|
2014-03-05 19:55:58 +01:00
|
|
|
continue;
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
this_wire_scope[node->str] = node;
|
|
|
|
|
}
|
2018-03-09 13:47:11 +01:00
|
|
|
// these nodes appear at the top level in a module and can define names
|
2013-01-05 11:13:26 +01:00
|
|
|
if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR ||
|
2019-09-19 21:43:13 +02:00
|
|
|
node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION || node->type == AST_CELL ||
|
|
|
|
|
node->type == AST_TYPEDEF) {
|
2013-01-05 11:13:26 +01:00
|
|
|
backup_scope[node->str] = current_scope[node->str];
|
|
|
|
|
current_scope[node->str] = node;
|
|
|
|
|
}
|
2018-03-09 13:47:11 +01:00
|
|
|
if (node->type == AST_ENUM) {
|
2020-02-03 07:12:24 +01:00
|
|
|
current_scope[node->str] = node;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& enode : node->children) {
|
2018-03-09 13:47:11 +01:00
|
|
|
log_assert(enode->type==AST_ENUM_ITEM);
|
2020-04-07 08:30:11 +02:00
|
|
|
if (current_scope.count(enode->str) == 0)
|
2025-06-16 22:55:24 +02:00
|
|
|
current_scope[enode->str] = enode.get();
|
2020-04-07 08:30:11 +02:00
|
|
|
else
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("enum item %s already exists\n", enode->str.c_str());
|
2018-03-09 13:47:11 +01:00
|
|
|
}
|
|
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
2013-11-24 19:40:23 +01:00
|
|
|
for (size_t i = 0; i < children.size(); i++) {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto& node = children[i];
|
2019-09-19 21:43:13 +02:00
|
|
|
if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_MEMORY || node->type == AST_TYPEDEF)
|
2023-04-04 22:59:44 +02:00
|
|
|
while (node->simplify(true, 1, -1, false))
|
2014-03-05 19:45:33 +01:00
|
|
|
did_something = true;
|
2020-01-16 21:23:03 +01:00
|
|
|
if (node->type == AST_ENUM) {
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& enode : node->children){
|
2020-01-16 21:23:03 +01:00
|
|
|
log_assert(enode->type==AST_ENUM_ITEM);
|
2023-04-04 22:59:44 +02:00
|
|
|
while (node->simplify(true, 1, -1, false))
|
2020-01-16 21:23:03 +01:00
|
|
|
did_something = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-11-24 19:40:23 +01:00
|
|
|
}
|
2022-01-07 06:04:00 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children)
|
2022-01-07 06:04:00 +01:00
|
|
|
if (child->type == AST_ALWAYS &&
|
|
|
|
|
child->attributes.count(ID::always_comb))
|
2025-06-16 22:55:24 +02:00
|
|
|
check_auto_nosync(child.get());
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
|
2020-04-07 06:37:44 +02:00
|
|
|
// create name resolution entries for all objects with names
|
|
|
|
|
if (type == AST_PACKAGE) {
|
|
|
|
|
//add names to package scope
|
|
|
|
|
for (size_t i = 0; i < children.size(); i++) {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto& node = children[i];
|
2020-04-07 06:37:44 +02:00
|
|
|
// these nodes appear at the top level in a package and can define names
|
2021-05-27 22:47:02 +02:00
|
|
|
if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_TYPEDEF || node->type == AST_FUNCTION || node->type == AST_TASK) {
|
2025-06-16 22:55:24 +02:00
|
|
|
current_scope[node->str] = node.get();
|
2020-04-07 06:37:44 +02:00
|
|
|
}
|
|
|
|
|
if (node->type == AST_ENUM) {
|
2025-06-16 22:55:24 +02:00
|
|
|
current_scope[node->str] = node.get();
|
|
|
|
|
for (auto& enode : node->children) {
|
2020-04-07 06:37:44 +02:00
|
|
|
log_assert(enode->type==AST_ENUM_ITEM);
|
|
|
|
|
if (current_scope.count(enode->str) == 0)
|
2025-06-16 22:55:24 +02:00
|
|
|
current_scope[enode->str] = enode.get();
|
2020-04-07 06:37:44 +02:00
|
|
|
else
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("enum item %s already exists in package\n", enode->str.c_str());
|
2020-04-07 06:37:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
auto backup_current_block = current_block;
|
|
|
|
|
auto backup_current_block_child = current_block_child;
|
|
|
|
|
auto backup_current_top_block = current_top_block;
|
2015-02-14 10:49:30 +01:00
|
|
|
auto backup_current_always = current_always;
|
2016-09-06 17:34:42 +02:00
|
|
|
auto backup_current_always_clocked = current_always_clocked;
|
2015-02-14 10:49:30 +01:00
|
|
|
|
|
|
|
|
if (type == AST_ALWAYS || type == AST_INITIAL)
|
2016-09-06 17:34:42 +02:00
|
|
|
{
|
2017-12-02 18:52:05 +01:00
|
|
|
if (current_always != nullptr)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Invalid nesting of always blocks and/or initializations.\n");
|
2017-12-02 18:52:05 +01:00
|
|
|
|
2015-02-14 10:49:30 +01:00
|
|
|
current_always = this;
|
2016-09-06 17:34:42 +02:00
|
|
|
current_always_clocked = false;
|
|
|
|
|
|
|
|
|
|
if (type == AST_ALWAYS)
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children) {
|
2016-09-06 17:34:42 +02:00
|
|
|
if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE)
|
|
|
|
|
current_always_clocked = true;
|
2017-10-10 11:59:32 +02:00
|
|
|
if (child->type == AST_EDGE && GetSize(child->children) == 1 &&
|
|
|
|
|
child->children[0]->type == AST_IDENTIFIER && child->children[0]->str == "\\$global_clock")
|
|
|
|
|
current_always_clocked = true;
|
|
|
|
|
}
|
2016-09-06 17:34:42 +02:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2021-10-20 02:46:26 +02:00
|
|
|
if (type == AST_CELL) {
|
|
|
|
|
bool lookup_suggested = false;
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children) {
|
2021-10-20 02:46:26 +02:00
|
|
|
// simplify any parameters to constants
|
|
|
|
|
if (child->type == AST_PARASET)
|
2023-04-04 22:59:44 +02:00
|
|
|
while (child->simplify(true, 1, -1, false)) { }
|
2021-10-20 02:46:26 +02:00
|
|
|
|
|
|
|
|
// look for patterns which _may_ indicate ambiguity requiring
|
|
|
|
|
// resolution of the underlying module
|
|
|
|
|
if (child->type == AST_ARGUMENT) {
|
|
|
|
|
if (child->children.size() != 1)
|
|
|
|
|
continue;
|
2025-06-16 22:55:24 +02:00
|
|
|
const auto& value = child->children[0];
|
2021-10-20 02:46:26 +02:00
|
|
|
if (value->type == AST_IDENTIFIER) {
|
|
|
|
|
const AstNode *elem = value->id2ast;
|
|
|
|
|
if (elem == nullptr) {
|
|
|
|
|
if (current_scope.count(value->str))
|
|
|
|
|
elem = current_scope.at(value->str);
|
|
|
|
|
else
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (elem->type == AST_MEMORY)
|
|
|
|
|
// need to determine is the is a read or wire
|
|
|
|
|
lookup_suggested = true;
|
|
|
|
|
else if (elem->type == AST_WIRE && elem->is_signed && !value->children.empty())
|
|
|
|
|
// this may be a fully sliced signed wire which needs
|
|
|
|
|
// to be indirected to produce an unsigned connection
|
|
|
|
|
lookup_suggested = true;
|
|
|
|
|
}
|
2025-06-16 22:55:24 +02:00
|
|
|
else if (contains_unbased_unsized(value.get()))
|
2021-10-20 02:46:26 +02:00
|
|
|
// unbased unsized literals extend to width of the context
|
|
|
|
|
lookup_suggested = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const RTLIL::Module *module = nullptr;
|
|
|
|
|
if (lookup_suggested)
|
|
|
|
|
module = lookup_cell_module();
|
|
|
|
|
if (module) {
|
|
|
|
|
size_t port_counter = 0;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children) {
|
2021-10-20 02:46:26 +02:00
|
|
|
if (child->type != AST_ARGUMENT)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// determine the full name of port this argument is connected to
|
|
|
|
|
RTLIL::IdString port_name;
|
|
|
|
|
if (child->str.size())
|
|
|
|
|
port_name = child->str;
|
|
|
|
|
else {
|
|
|
|
|
if (port_counter >= module->ports.size())
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Cell instance has more ports than the module!\n");
|
2021-10-20 02:46:26 +02:00
|
|
|
port_name = module->ports[port_counter++];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// find the port's wire in the underlying module
|
|
|
|
|
const RTLIL::Wire *ref = module->wire(port_name);
|
|
|
|
|
if (ref == nullptr)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Cell instance refers to port %s which does not exist in module %s!.\n",
|
2021-10-20 02:46:26 +02:00
|
|
|
log_id(port_name), log_id(module->name));
|
|
|
|
|
|
|
|
|
|
// select the argument, if present
|
|
|
|
|
log_assert(child->children.size() <= 1);
|
|
|
|
|
if (child->children.empty())
|
|
|
|
|
continue;
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
{
|
|
|
|
|
auto arg_check = child->children[0].get();
|
2021-10-20 02:46:26 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
// plain identifiers never need indirection; this also prevents
|
|
|
|
|
// adding infinite levels of indirection
|
|
|
|
|
if (arg_check->type == AST_IDENTIFIER && arg_check->children.empty())
|
|
|
|
|
continue;
|
2021-10-20 02:46:26 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
// only add indirection for standard inputs or outputs
|
|
|
|
|
if (ref->port_input == ref->port_output)
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto arg = std::move(child->children[0]);
|
2021-10-20 02:46:26 +02:00
|
|
|
did_something = true;
|
|
|
|
|
|
|
|
|
|
// create the indirection wire
|
|
|
|
|
std::stringstream sstr;
|
2022-08-08 16:13:33 +02:00
|
|
|
sstr << "$indirect$" << ref->name.c_str() << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++);
|
2021-10-20 02:46:26 +02:00
|
|
|
std::string tmp_str = sstr.str();
|
|
|
|
|
add_wire_for_ref(ref, tmp_str);
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto asgn_owned = std::make_unique<AstNode>(AST_ASSIGN);
|
|
|
|
|
auto* asgn = asgn_owned.get();
|
|
|
|
|
current_ast_mod->children.push_back(std::move(asgn_owned));
|
2021-10-20 02:46:26 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto ident = std::make_unique<AstNode>(AST_IDENTIFIER);
|
2021-10-20 02:46:26 +02:00
|
|
|
ident->str = tmp_str;
|
|
|
|
|
child->children[0] = ident->clone();
|
|
|
|
|
|
|
|
|
|
if (ref->port_input && !ref->port_output) {
|
2025-06-16 22:55:24 +02:00
|
|
|
asgn->children.push_back(std::move(ident));
|
|
|
|
|
asgn->children.push_back(std::move(arg));
|
2021-10-20 02:46:26 +02:00
|
|
|
} else {
|
|
|
|
|
log_assert(!ref->port_input && ref->port_output);
|
2025-06-16 22:55:24 +02:00
|
|
|
asgn->children.push_back(std::move(arg));
|
|
|
|
|
asgn->children.push_back(std::move(ident));
|
2021-10-20 02:46:26 +02:00
|
|
|
}
|
2023-04-05 11:00:07 +02:00
|
|
|
asgn->fixup_hierarchy_flags();
|
2021-10-20 02:46:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-03-03 20:36:19 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-04 06:04:42 +01:00
|
|
|
int backup_width_hint = width_hint;
|
|
|
|
|
bool backup_sign_hint = sign_hint;
|
|
|
|
|
|
|
|
|
|
bool detect_width_simple = false;
|
|
|
|
|
bool child_0_is_self_determined = false;
|
|
|
|
|
bool child_1_is_self_determined = false;
|
2014-06-25 10:05:36 +02:00
|
|
|
bool child_2_is_self_determined = false;
|
2013-11-04 06:04:42 +01:00
|
|
|
bool children_are_self_determined = false;
|
|
|
|
|
bool reset_width_after_children = false;
|
|
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case AST_ASSIGN_EQ:
|
|
|
|
|
case AST_ASSIGN_LE:
|
|
|
|
|
case AST_ASSIGN:
|
2023-04-04 22:59:44 +02:00
|
|
|
while (!children[0]->basic_prep && children[0]->simplify(false, stage, -1, false) == true)
|
2014-03-05 19:45:33 +01:00
|
|
|
did_something = true;
|
2023-04-04 22:59:44 +02:00
|
|
|
while (!children[1]->basic_prep && children[1]->simplify(false, stage, -1, false) == true)
|
2014-03-05 19:45:33 +01:00
|
|
|
did_something = true;
|
2013-11-04 21:29:36 +01:00
|
|
|
children[0]->detectSignWidth(backup_width_hint, backup_sign_hint);
|
|
|
|
|
children[1]->detectSignWidth(width_hint, sign_hint);
|
2015-10-25 19:30:49 +01:00
|
|
|
width_hint = max(width_hint, backup_width_hint);
|
2013-11-04 06:04:42 +01:00
|
|
|
child_0_is_self_determined = true;
|
2018-06-05 16:44:24 +02:00
|
|
|
// test only once, before optimizations and memory mappings but after assignment LHS was mapped to an identifier
|
|
|
|
|
if (children[0]->id2ast && !children[0]->was_checked) {
|
|
|
|
|
if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && children[0]->id2ast->is_logic)
|
|
|
|
|
children[0]->id2ast->is_reg = true; // if logic type is used in a block asignment
|
|
|
|
|
if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && !children[0]->id2ast->is_reg)
|
2021-02-23 19:22:53 +01:00
|
|
|
log_warning("wire '%s' is assigned in a block at %s.\n", children[0]->str.c_str(), loc_string().c_str());
|
2018-11-01 15:25:24 +01:00
|
|
|
if (type == AST_ASSIGN && children[0]->id2ast->is_reg) {
|
|
|
|
|
bool is_rand_reg = false;
|
|
|
|
|
if (children[1]->type == AST_FCALL) {
|
|
|
|
|
if (children[1]->str == "\\$anyconst")
|
|
|
|
|
is_rand_reg = true;
|
|
|
|
|
if (children[1]->str == "\\$anyseq")
|
|
|
|
|
is_rand_reg = true;
|
|
|
|
|
if (children[1]->str == "\\$allconst")
|
|
|
|
|
is_rand_reg = true;
|
|
|
|
|
if (children[1]->str == "\\$allseq")
|
|
|
|
|
is_rand_reg = true;
|
|
|
|
|
}
|
|
|
|
|
if (!is_rand_reg)
|
2021-02-23 19:22:53 +01:00
|
|
|
log_warning("reg '%s' is assigned in a continuous assignment at %s.\n", children[0]->str.c_str(), loc_string().c_str());
|
2018-11-01 15:25:24 +01:00
|
|
|
}
|
2018-06-05 16:44:24 +02:00
|
|
|
children[0]->was_checked = true;
|
|
|
|
|
}
|
2013-11-04 06:04:42 +01:00
|
|
|
break;
|
|
|
|
|
|
2020-05-08 15:40:49 +02:00
|
|
|
case AST_STRUCT:
|
2020-05-12 15:25:33 +02:00
|
|
|
case AST_UNION:
|
2020-05-08 15:40:49 +02:00
|
|
|
if (!basic_prep) {
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& node : children) {
|
2020-05-08 15:40:49 +02:00
|
|
|
// resolve any ranges
|
2023-04-04 22:59:44 +02:00
|
|
|
while (!node->basic_prep && node->simplify(true, stage, -1, false)) {
|
2020-05-08 15:40:49 +02:00
|
|
|
did_something = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-12 15:25:33 +02:00
|
|
|
// 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
|
Handling of attributes for struct / union variables
(* nowrshmsk *) on a struct / union variable now affects dynamic
bit slice assignments to members of the struct / union.
(* nowrshmsk *) can in some cases yield significant resource savings; the
combination of pipeline shifting and indexed writes is an example of this.
Constructs similar to the one below can benefit from (* nowrshmsk *), and
in addition it is no longer necessary to split out the shift assignments
on separate lines in order to avoid the error message "ERROR: incompatible
mix of lookahead and non-lookahead IDs in LHS expression."
always_ff @(posedge clk) begin
if (rotate) begin
{ v5, v4, v3, v2, v1, v0 } <= { v4, v3, v2, v1, v0, v5 };
if (res) begin
v0.bytes <= '0;
end else if (w) begin
v0.bytes[addr] <= data;
end
end
end
2023-03-02 19:02:30 +01:00
|
|
|
auto wnode = make_packed_struct(this, str, attributes);
|
2020-05-12 15:25:33 +02:00
|
|
|
log_assert(current_ast_mod);
|
2025-06-16 22:55:24 +02:00
|
|
|
current_ast_mod->children.push_back(std::move(wnode));
|
2020-05-08 15:40:49 +02:00
|
|
|
}
|
2020-05-12 15:25:33 +02:00
|
|
|
basic_prep = true;
|
2025-05-26 02:19:33 +02:00
|
|
|
is_custom_type = false;
|
2020-05-08 15:40:49 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case AST_STRUCT_ITEM:
|
2023-01-29 20:22:00 +01:00
|
|
|
if (is_custom_type) {
|
2024-01-05 19:29:06 +01:00
|
|
|
log_assert(children.size() >= 1);
|
2023-01-29 20:22:00 +01:00
|
|
|
log_assert(children[0]->type == AST_WIRETYPE);
|
|
|
|
|
|
2024-01-05 19:29:06 +01:00
|
|
|
// Pretend it's just a wire in order to resolve the type.
|
|
|
|
|
type = AST_WIRE;
|
|
|
|
|
while (is_custom_type && simplify(const_fold, stage, width_hint, sign_hint)) {};
|
|
|
|
|
if (type == AST_WIRE)
|
2023-01-29 20:22:00 +01:00
|
|
|
type = AST_STRUCT_ITEM;
|
|
|
|
|
|
|
|
|
|
did_something = true;
|
|
|
|
|
}
|
|
|
|
|
log_assert(!is_custom_type);
|
2020-05-08 15:40:49 +02:00
|
|
|
break;
|
|
|
|
|
|
2018-03-09 13:47:11 +01:00
|
|
|
case AST_ENUM:
|
2020-02-03 07:08:16 +01:00
|
|
|
//log("\nENUM %s: %d child %d\n", str.c_str(), basic_prep, children[0]->basic_prep);
|
2018-03-09 13:47:11 +01:00
|
|
|
if (!basic_prep) {
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& item_node : children) {
|
2023-04-04 22:59:44 +02:00
|
|
|
while (!item_node->basic_prep && item_node->simplify(false, stage, -1, false))
|
2018-03-09 13:47:11 +01:00
|
|
|
did_something = true;
|
|
|
|
|
}
|
|
|
|
|
// allocate values (called more than once)
|
|
|
|
|
allocateDefaultEnumValues();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2013-11-07 09:58:15 +01:00
|
|
|
case AST_PARAMETER:
|
|
|
|
|
case AST_LOCALPARAM:
|
2022-02-14 14:34:20 +01:00
|
|
|
// if parameter is implicit type which is the typename of a struct or union,
|
|
|
|
|
// save information about struct in wiretype attribute
|
|
|
|
|
if (children[0]->type == AST_IDENTIFIER && current_scope.count(children[0]->str) > 0) {
|
|
|
|
|
auto item_node = current_scope[children[0]->str];
|
|
|
|
|
if (item_node->type == AST_STRUCT || item_node->type == AST_UNION) {
|
2023-04-05 11:00:07 +02:00
|
|
|
set_attribute(ID::wiretype, item_node->clone());
|
2025-06-16 22:55:24 +02:00
|
|
|
size_packed_struct(attributes[ID::wiretype].get(), 0);
|
|
|
|
|
add_members_to_scope(attributes[ID::wiretype].get(), str);
|
2022-02-14 14:34:20 +01:00
|
|
|
}
|
|
|
|
|
}
|
2023-04-04 22:59:44 +02:00
|
|
|
while (!children[0]->basic_prep && children[0]->simplify(false, stage, -1, false) == true)
|
2014-03-05 19:45:33 +01:00
|
|
|
did_something = true;
|
2013-11-07 09:58:15 +01:00
|
|
|
children[0]->detectSignWidth(width_hint, sign_hint);
|
2014-06-14 12:00:47 +02:00
|
|
|
if (children.size() > 1 && children[1]->type == AST_RANGE) {
|
2023-04-04 22:59:44 +02:00
|
|
|
while (!children[1]->basic_prep && children[1]->simplify(false, stage, -1, false) == true)
|
2014-03-05 19:45:33 +01:00
|
|
|
did_something = true;
|
2013-11-07 09:58:15 +01:00
|
|
|
if (!children[1]->range_valid)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Non-constant width range on parameter decl.\n");
|
2015-10-25 19:30:49 +01:00
|
|
|
width_hint = max(width_hint, children[1]->range_left - children[1]->range_right + 1);
|
2013-11-07 09:58:15 +01:00
|
|
|
}
|
|
|
|
|
break;
|
2020-02-03 07:08:16 +01:00
|
|
|
case AST_ENUM_ITEM:
|
2023-04-04 22:59:44 +02:00
|
|
|
while (!children[0]->basic_prep && children[0]->simplify(false, stage, -1, false))
|
2020-02-03 07:08:16 +01:00
|
|
|
did_something = true;
|
|
|
|
|
children[0]->detectSignWidth(width_hint, sign_hint);
|
|
|
|
|
if (children.size() > 1 && children[1]->type == AST_RANGE) {
|
2023-04-04 22:59:44 +02:00
|
|
|
while (!children[1]->basic_prep && children[1]->simplify(false, stage, -1, false))
|
2020-02-03 07:08:16 +01:00
|
|
|
did_something = true;
|
|
|
|
|
if (!children[1]->range_valid)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Non-constant width range on enum item decl.\n");
|
2020-02-03 07:08:16 +01:00
|
|
|
width_hint = max(width_hint, children[1]->range_left - children[1]->range_right + 1);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2013-11-07 09:58:15 +01:00
|
|
|
|
2024-09-29 23:03:01 +02:00
|
|
|
case AST_CAST_SIZE: {
|
2025-06-16 22:55:24 +02:00
|
|
|
if (children[0]->type == AST_WIRE) {
|
|
|
|
|
int width = 1;
|
|
|
|
|
std::unique_ptr<AstNode> node;
|
|
|
|
|
AstNode* child = children[0].release();
|
2024-09-29 23:03:01 +02:00
|
|
|
if (child->children.size() == 0) {
|
|
|
|
|
// Base type (e.g., int)
|
|
|
|
|
width = child->range_left - child->range_right +1;
|
|
|
|
|
node = mkconst_int(width, child->is_signed);
|
|
|
|
|
} else {
|
|
|
|
|
// User defined type
|
|
|
|
|
log_assert(child->children[0]->type == AST_WIRETYPE);
|
|
|
|
|
|
|
|
|
|
const std::string &type_name = child->children[0]->str;
|
|
|
|
|
if (!current_scope.count(type_name))
|
|
|
|
|
input_error("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)
|
|
|
|
|
input_error("`%s' does not name a type\n", type_name.c_str());
|
|
|
|
|
log_assert(resolved_type_node->children.size() == 1);
|
2025-06-16 22:55:24 +02:00
|
|
|
AstNode *template_node = resolved_type_node->children[0].get();
|
2024-09-29 23:03:01 +02:00
|
|
|
|
|
|
|
|
// Ensure typedef itself is fully simplified
|
|
|
|
|
while (template_node->simplify(const_fold, stage, width_hint, sign_hint)) {};
|
|
|
|
|
|
|
|
|
|
switch (template_node->type)
|
|
|
|
|
{
|
|
|
|
|
case AST_WIRE: {
|
|
|
|
|
if (template_node->children.size() > 0 && template_node->children[0]->type == AST_RANGE)
|
2025-06-16 22:55:24 +02:00
|
|
|
width = range_width(this, template_node->children[0].get());
|
2024-09-29 23:03:01 +02:00
|
|
|
child->delete_children();
|
|
|
|
|
node = mkconst_int(width, true);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case AST_STRUCT:
|
|
|
|
|
case AST_UNION: {
|
|
|
|
|
child->delete_children();
|
|
|
|
|
width = size_packed_struct(template_node, 0);
|
|
|
|
|
node = mkconst_int(width, false);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
log_error("Don't know how to translate static cast of type %s\n", type2str(template_node->type).c_str());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
children.erase(children.begin());
|
2025-06-16 22:55:24 +02:00
|
|
|
children.insert(children.begin(), std::move(node));
|
2024-09-29 23:03:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
detect_width_simple = true;
|
|
|
|
|
children_are_self_determined = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-01 13:50:23 +01:00
|
|
|
case AST_TO_BITS:
|
2013-11-04 06:04:42 +01:00
|
|
|
case AST_TO_SIGNED:
|
|
|
|
|
case AST_TO_UNSIGNED:
|
2020-04-29 14:28:04 +02:00
|
|
|
case AST_SELFSZ:
|
2013-11-04 06:04:42 +01:00
|
|
|
case AST_CONCAT:
|
|
|
|
|
case AST_REPLICATE:
|
|
|
|
|
case AST_REDUCE_AND:
|
|
|
|
|
case AST_REDUCE_OR:
|
|
|
|
|
case AST_REDUCE_XOR:
|
|
|
|
|
case AST_REDUCE_XNOR:
|
|
|
|
|
case AST_REDUCE_BOOL:
|
|
|
|
|
detect_width_simple = true;
|
|
|
|
|
children_are_self_determined = true;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case AST_NEG:
|
|
|
|
|
case AST_BIT_NOT:
|
|
|
|
|
case AST_POS:
|
|
|
|
|
case AST_BIT_AND:
|
|
|
|
|
case AST_BIT_OR:
|
|
|
|
|
case AST_BIT_XOR:
|
|
|
|
|
case AST_BIT_XNOR:
|
|
|
|
|
case AST_ADD:
|
|
|
|
|
case AST_SUB:
|
|
|
|
|
case AST_MUL:
|
|
|
|
|
case AST_DIV:
|
|
|
|
|
case AST_MOD:
|
|
|
|
|
detect_width_simple = true;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case AST_SHIFT_LEFT:
|
|
|
|
|
case AST_SHIFT_RIGHT:
|
|
|
|
|
case AST_SHIFT_SLEFT:
|
|
|
|
|
case AST_SHIFT_SRIGHT:
|
|
|
|
|
case AST_POW:
|
|
|
|
|
detect_width_simple = true;
|
|
|
|
|
child_1_is_self_determined = true;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case AST_LT:
|
|
|
|
|
case AST_LE:
|
|
|
|
|
case AST_EQ:
|
|
|
|
|
case AST_NE:
|
2013-12-27 13:50:08 +01:00
|
|
|
case AST_EQX:
|
|
|
|
|
case AST_NEX:
|
2013-11-04 06:04:42 +01:00
|
|
|
case AST_GE:
|
|
|
|
|
case AST_GT:
|
|
|
|
|
width_hint = -1;
|
|
|
|
|
sign_hint = true;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children) {
|
2023-04-04 22:59:44 +02:00
|
|
|
while (!child->basic_prep && child->simplify(false, stage, -1, false) == true)
|
2014-03-05 19:45:33 +01:00
|
|
|
did_something = true;
|
2013-11-04 06:04:42 +01:00
|
|
|
child->detectSignWidthWorker(width_hint, sign_hint);
|
|
|
|
|
}
|
|
|
|
|
reset_width_after_children = true;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case AST_LOGIC_AND:
|
|
|
|
|
case AST_LOGIC_OR:
|
|
|
|
|
case AST_LOGIC_NOT:
|
|
|
|
|
detect_width_simple = true;
|
|
|
|
|
children_are_self_determined = true;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case AST_TERNARY:
|
|
|
|
|
child_0_is_self_determined = true;
|
|
|
|
|
break;
|
2015-07-02 11:14:30 +02:00
|
|
|
|
2013-11-04 06:04:42 +01:00
|
|
|
case AST_MEMRD:
|
|
|
|
|
detect_width_simple = true;
|
|
|
|
|
children_are_self_determined = true;
|
|
|
|
|
break;
|
|
|
|
|
|
2016-09-19 01:30:07 +02:00
|
|
|
case AST_FCALL:
|
|
|
|
|
case AST_TCALL:
|
|
|
|
|
children_are_self_determined = true;
|
|
|
|
|
break;
|
|
|
|
|
|
2013-11-04 06:04:42 +01:00
|
|
|
default:
|
2013-11-02 13:00:17 +01:00
|
|
|
width_hint = -1;
|
|
|
|
|
sign_hint = false;
|
|
|
|
|
}
|
2013-11-04 06:04:42 +01:00
|
|
|
|
|
|
|
|
if (detect_width_simple && width_hint < 0) {
|
2014-06-07 00:02:05 +02:00
|
|
|
if (type == AST_REPLICATE)
|
2023-04-04 22:59:44 +02:00
|
|
|
while (children[0]->simplify(true, stage, -1, false) == true)
|
2014-06-07 00:02:05 +02:00
|
|
|
did_something = true;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children)
|
2023-04-04 22:59:44 +02:00
|
|
|
while (!child->basic_prep && child->simplify(false, stage, -1, false) == true)
|
2014-03-05 19:45:33 +01:00
|
|
|
did_something = true;
|
2013-11-04 06:04:42 +01:00
|
|
|
detectSignWidth(width_hint, sign_hint);
|
2013-11-02 13:00:17 +01:00
|
|
|
}
|
|
|
|
|
|
2016-09-19 01:30:07 +02:00
|
|
|
if (type == AST_FCALL && str == "\\$past")
|
|
|
|
|
detectSignWidth(width_hint, sign_hint);
|
|
|
|
|
|
2014-06-25 10:05:36 +02:00
|
|
|
if (type == AST_TERNARY) {
|
2021-02-12 20:25:34 +01:00
|
|
|
if (width_hint < 0) {
|
2023-04-04 22:59:44 +02:00
|
|
|
while (!children[0]->basic_prep && children[0]->simplify(true, stage, -1, false))
|
2021-02-12 20:25:34 +01:00
|
|
|
did_something = true;
|
|
|
|
|
|
|
|
|
|
bool backup_unevaluated_tern_branch = unevaluated_tern_branch;
|
|
|
|
|
AstNode *chosen = get_tern_choice().first;
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == children[2].get();
|
2023-04-04 22:59:44 +02:00
|
|
|
while (!children[1]->basic_prep && children[1]->simplify(false, stage, -1, false))
|
2021-02-12 20:25:34 +01:00
|
|
|
did_something = true;
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == children[1].get();
|
2023-04-04 22:59:44 +02:00
|
|
|
while (!children[2]->basic_prep && children[2]->simplify(false, stage, -1, false))
|
2021-02-12 20:25:34 +01:00
|
|
|
did_something = true;
|
|
|
|
|
|
|
|
|
|
unevaluated_tern_branch = backup_unevaluated_tern_branch;
|
|
|
|
|
detectSignWidth(width_hint, sign_hint);
|
|
|
|
|
}
|
2014-06-25 10:05:36 +02:00
|
|
|
int width_hint_left, width_hint_right;
|
|
|
|
|
bool sign_hint_left, sign_hint_right;
|
|
|
|
|
bool found_real_left, found_real_right;
|
|
|
|
|
children[1]->detectSignWidth(width_hint_left, sign_hint_left, &found_real_left);
|
|
|
|
|
children[2]->detectSignWidth(width_hint_right, sign_hint_right, &found_real_right);
|
|
|
|
|
if (found_real_left || found_real_right) {
|
|
|
|
|
child_1_is_self_determined = true;
|
|
|
|
|
child_2_is_self_determined = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-21 15:31:54 +02:00
|
|
|
if (type == AST_CONDX && children.size() > 0 && children.at(0)->type == AST_CONSTANT) {
|
|
|
|
|
for (auto &bit : children.at(0)->bits)
|
|
|
|
|
if (bit == State::Sz || bit == State::Sx)
|
|
|
|
|
bit = State::Sa;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (type == AST_CONDZ && children.size() > 0 && children.at(0)->type == AST_CONSTANT) {
|
|
|
|
|
for (auto &bit : children.at(0)->bits)
|
|
|
|
|
if (bit == State::Sz)
|
|
|
|
|
bit = State::Sa;
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-29 08:29:51 +01:00
|
|
|
if (const_fold && type == AST_CASE)
|
|
|
|
|
{
|
2021-03-25 19:06:05 +01:00
|
|
|
detectSignWidth(width_hint, sign_hint);
|
2023-04-04 22:59:44 +02:00
|
|
|
while (children[0]->simplify(const_fold, stage, width_hint, sign_hint)) { }
|
2014-10-29 08:29:51 +01:00
|
|
|
if (children[0]->type == AST_CONSTANT && children[0]->bits_only_01()) {
|
2022-05-24 14:32:14 +02:00
|
|
|
children[0]->is_signed = sign_hint;
|
2021-03-25 19:06:05 +01:00
|
|
|
RTLIL::Const case_expr = children[0]->bitsAsConst(width_hint, sign_hint);
|
2025-06-16 22:55:24 +02:00
|
|
|
std::vector<std::unique_ptr<AstNode>> new_children;
|
|
|
|
|
new_children.push_back(std::move(children[0]));
|
2014-10-29 08:29:51 +01:00
|
|
|
for (int i = 1; i < GetSize(children); i++) {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto& child = children[i];
|
2016-04-21 15:31:54 +02:00
|
|
|
log_assert(child->type == AST_COND || child->type == AST_CONDX || child->type == AST_CONDZ);
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& v : child->children) {
|
2014-10-29 08:29:51 +01:00
|
|
|
if (v->type == AST_DEFAULT)
|
|
|
|
|
goto keep_const_cond;
|
|
|
|
|
if (v->type == AST_BLOCK)
|
|
|
|
|
continue;
|
2023-04-04 22:59:44 +02:00
|
|
|
while (v->simplify(const_fold, stage, width_hint, sign_hint)) { }
|
2014-10-29 08:29:51 +01:00
|
|
|
if (v->type == AST_CONSTANT && v->bits_only_01()) {
|
2021-03-25 19:06:05 +01:00
|
|
|
RTLIL::Const case_item_expr = v->bitsAsConst(width_hint, sign_hint);
|
|
|
|
|
RTLIL::Const match = const_eq(case_expr, case_item_expr, sign_hint, sign_hint, 1);
|
2024-10-09 19:39:45 +02:00
|
|
|
log_assert(match.size() == 1);
|
|
|
|
|
if (match.front() == RTLIL::State::S1) {
|
2025-06-16 22:55:24 +02:00
|
|
|
// This is the only reachable case. Skip to the end
|
|
|
|
|
i = GetSize(children);
|
2014-10-29 08:29:51 +01:00
|
|
|
goto keep_const_cond;
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
goto keep_const_cond;
|
|
|
|
|
}
|
|
|
|
|
if (0)
|
|
|
|
|
keep_const_cond:
|
2025-06-16 22:55:24 +02:00
|
|
|
new_children.push_back(std::move(child));
|
2014-10-29 08:29:51 +01:00
|
|
|
}
|
|
|
|
|
new_children.swap(children);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-23 16:48:29 +01:00
|
|
|
dict<std::string, pool<int>> backup_memwr_visible;
|
|
|
|
|
dict<std::string, pool<int>> final_memwr_visible;
|
|
|
|
|
|
|
|
|
|
if (type == AST_CASE && stage == 2) {
|
|
|
|
|
backup_memwr_visible = current_memwr_visible;
|
|
|
|
|
final_memwr_visible = current_memwr_visible;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
// simplify all children first
|
|
|
|
|
// (iterate by index as e.g. auto wires can add new children in the process)
|
|
|
|
|
for (size_t i = 0; i < children.size(); i++) {
|
|
|
|
|
bool did_something_here = true;
|
2019-02-24 20:09:41 +01:00
|
|
|
bool backup_flag_autowire = flag_autowire;
|
2021-02-12 20:25:34 +01:00
|
|
|
bool backup_unevaluated_tern_branch = unevaluated_tern_branch;
|
2013-01-05 11:13:26 +01:00
|
|
|
if ((type == AST_GENFOR || type == AST_FOR) && i >= 3)
|
|
|
|
|
break;
|
2013-12-04 21:06:54 +01:00
|
|
|
if ((type == AST_GENIF || type == AST_GENCASE) && i >= 1)
|
2013-01-05 11:13:26 +01:00
|
|
|
break;
|
2013-03-26 09:44:54 +01:00
|
|
|
if (type == AST_GENBLOCK)
|
|
|
|
|
break;
|
2025-03-25 00:15:54 +01:00
|
|
|
if (type == AST_CELLARRAY && (children[i]->type == AST_CELL || children[i]->type == AST_PRIMITIVE))
|
2022-08-30 13:58:09 +02:00
|
|
|
continue;
|
2013-12-04 09:10:16 +01:00
|
|
|
if (type == AST_BLOCK && !str.empty())
|
|
|
|
|
break;
|
2013-02-26 13:18:22 +01:00
|
|
|
if (type == AST_PREFIX && i >= 1)
|
|
|
|
|
break;
|
2019-02-24 20:09:41 +01:00
|
|
|
if (type == AST_DEFPARAM && i == 0)
|
|
|
|
|
flag_autowire = true;
|
2021-02-12 20:25:34 +01:00
|
|
|
if (type == AST_TERNARY && i > 0 && !unevaluated_tern_branch) {
|
|
|
|
|
AstNode *chosen = get_tern_choice().first;
|
2025-06-16 22:55:24 +02:00
|
|
|
unevaluated_tern_branch = chosen && chosen != children[i].get();
|
2021-02-12 20:25:34 +01:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
while (did_something_here && i < children.size()) {
|
2023-04-04 22:59:44 +02:00
|
|
|
bool const_fold_here = const_fold;
|
2013-11-02 13:00:17 +01:00
|
|
|
int width_hint_here = width_hint;
|
|
|
|
|
bool sign_hint_here = sign_hint;
|
2014-06-07 00:02:05 +02:00
|
|
|
if (i == 0 && (type == AST_REPLICATE || type == AST_WIRE))
|
2023-04-04 22:59:44 +02:00
|
|
|
const_fold_here = true;
|
2013-11-07 14:08:53 +01:00
|
|
|
if (type == AST_PARAMETER || type == AST_LOCALPARAM)
|
|
|
|
|
const_fold_here = true;
|
2013-01-05 11:13:26 +01:00
|
|
|
if (type == AST_BLOCK) {
|
|
|
|
|
current_block = this;
|
2025-06-16 22:55:24 +02:00
|
|
|
current_block_child = children[i].get();
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
2013-03-31 11:19:11 +02:00
|
|
|
if ((type == AST_ALWAYS || type == AST_INITIAL) && children[i]->type == AST_BLOCK)
|
2025-06-16 22:55:24 +02:00
|
|
|
current_top_block = children[i].get();
|
2013-11-04 06:04:42 +01:00
|
|
|
if (i == 0 && child_0_is_self_determined)
|
|
|
|
|
width_hint_here = -1, sign_hint_here = false;
|
|
|
|
|
if (i == 1 && child_1_is_self_determined)
|
|
|
|
|
width_hint_here = -1, sign_hint_here = false;
|
2014-06-25 10:05:36 +02:00
|
|
|
if (i == 2 && child_2_is_self_determined)
|
|
|
|
|
width_hint_here = -1, sign_hint_here = false;
|
2013-11-04 06:04:42 +01:00
|
|
|
if (children_are_self_determined)
|
|
|
|
|
width_hint_here = -1, sign_hint_here = false;
|
2023-04-04 22:59:44 +02:00
|
|
|
did_something_here = children[i]->simplify(const_fold_here, stage, width_hint_here, sign_hint_here);
|
2013-01-05 11:13:26 +01:00
|
|
|
if (did_something_here)
|
|
|
|
|
did_something = true;
|
2014-07-11 13:05:53 +02:00
|
|
|
}
|
|
|
|
|
if (stage == 2 && children[i]->type == AST_INITIAL && current_ast_mod != this) {
|
2025-06-16 22:55:24 +02:00
|
|
|
current_ast_mod->children.push_back(std::move(children[i]));
|
2014-07-11 13:05:53 +02:00
|
|
|
children.erase(children.begin() + (i--));
|
|
|
|
|
did_something = true;
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
2019-02-24 20:09:41 +01:00
|
|
|
flag_autowire = backup_flag_autowire;
|
2021-02-12 20:25:34 +01:00
|
|
|
unevaluated_tern_branch = backup_unevaluated_tern_branch;
|
2021-02-23 16:48:29 +01:00
|
|
|
if (stage == 2 && type == AST_CASE) {
|
|
|
|
|
for (auto &x : current_memwr_visible) {
|
|
|
|
|
for (int y : x.second)
|
|
|
|
|
final_memwr_visible[x.first].insert(y);
|
|
|
|
|
}
|
|
|
|
|
current_memwr_visible = backup_memwr_visible;
|
|
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
for (auto &attr : attributes) {
|
2023-04-04 22:59:44 +02:00
|
|
|
while (attr.second->simplify(true, stage, -1, false))
|
2014-03-05 19:45:33 +01:00
|
|
|
did_something = true;
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
2021-02-23 16:48:29 +01:00
|
|
|
if (type == AST_CASE && stage == 2) {
|
|
|
|
|
current_memwr_visible = final_memwr_visible;
|
|
|
|
|
}
|
|
|
|
|
if (type == AST_ALWAYS && stage == 2) {
|
|
|
|
|
current_memwr_visible.clear();
|
|
|
|
|
current_memwr_count.clear();
|
|
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2013-11-04 06:04:42 +01:00
|
|
|
if (reset_width_after_children) {
|
|
|
|
|
width_hint = backup_width_hint;
|
|
|
|
|
sign_hint = backup_sign_hint;
|
|
|
|
|
if (width_hint < 0)
|
|
|
|
|
detectSignWidth(width_hint, sign_hint);
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
current_block = backup_current_block;
|
|
|
|
|
current_block_child = backup_current_block_child;
|
|
|
|
|
current_top_block = backup_current_top_block;
|
2015-02-14 10:49:30 +01:00
|
|
|
current_always = backup_current_always;
|
2016-09-06 17:34:42 +02:00
|
|
|
current_always_clocked = backup_current_always_clocked;
|
2013-01-05 11:13:26 +01:00
|
|
|
|
|
|
|
|
for (auto it = backup_scope.begin(); it != backup_scope.end(); it++) {
|
2025-06-16 22:55:24 +02:00
|
|
|
if (it->second == nullptr)
|
2013-01-05 11:13:26 +01:00
|
|
|
current_scope.erase(it->first);
|
|
|
|
|
else
|
|
|
|
|
current_scope[it->first] = it->second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
current_filename = filename;
|
|
|
|
|
|
2023-02-13 00:25:39 +01:00
|
|
|
if (type == AST_MODULE || type == AST_INTERFACE)
|
2013-01-05 11:13:26 +01:00
|
|
|
current_scope.clear();
|
|
|
|
|
|
2013-07-04 14:12:33 +02:00
|
|
|
// convert defparam nodes to cell parameters
|
2016-11-15 13:35:19 +01:00
|
|
|
if (type == AST_DEFPARAM && !children.empty())
|
|
|
|
|
{
|
|
|
|
|
if (children[0]->type != AST_IDENTIFIER)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Module name in defparam contains non-constant expressions!\n");
|
2016-11-15 13:35:19 +01:00
|
|
|
|
|
|
|
|
string modname, paramname = children[0]->str;
|
|
|
|
|
|
|
|
|
|
size_t pos = paramname.rfind('.');
|
|
|
|
|
|
|
|
|
|
while (pos != 0 && pos != std::string::npos)
|
|
|
|
|
{
|
|
|
|
|
modname = paramname.substr(0, pos);
|
|
|
|
|
|
|
|
|
|
if (current_scope.count(modname))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
pos = paramname.rfind('.', pos - 1);
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-04 14:12:33 +02:00
|
|
|
if (pos == std::string::npos)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Can't find object for defparam `%s`!\n", RTLIL::unescape_id(paramname).c_str());
|
2016-11-15 13:35:19 +01:00
|
|
|
|
|
|
|
|
paramname = "\\" + paramname.substr(pos+1);
|
|
|
|
|
|
|
|
|
|
if (current_scope.at(modname)->type != AST_CELL)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Defparam argument `%s . %s` does not match a cell!\n",
|
2018-11-04 10:19:32 +01:00
|
|
|
RTLIL::unescape_id(modname).c_str(), RTLIL::unescape_id(paramname).c_str());
|
2016-11-15 13:35:19 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto paraset = std::make_unique<AstNode>(AST_PARASET, children[1]->clone(), GetSize(children) > 2 ? children[2]->clone() : nullptr);
|
2016-11-15 13:35:19 +01:00
|
|
|
paraset->str = paramname;
|
|
|
|
|
|
|
|
|
|
AstNode *cell = current_scope.at(modname);
|
2025-06-16 22:55:24 +02:00
|
|
|
cell->children.insert(cell->children.begin() + 1, std::move(paraset));
|
2016-11-15 13:35:19 +01:00
|
|
|
delete_children();
|
2013-07-04 14:12:33 +02:00
|
|
|
}
|
|
|
|
|
|
2019-09-19 21:43:13 +02:00
|
|
|
// resolve typedefs
|
|
|
|
|
if (type == AST_TYPEDEF) {
|
|
|
|
|
log_assert(children.size() == 1);
|
2025-06-16 22:55:24 +02:00
|
|
|
auto& type_node = children[0];
|
2020-05-12 15:25:33 +02:00
|
|
|
log_assert(type_node->type == AST_WIRE || type_node->type == AST_MEMORY || type_node->type == AST_STRUCT || type_node->type == AST_UNION);
|
2023-04-04 22:59:44 +02:00
|
|
|
while (type_node->simplify(const_fold, stage, width_hint, sign_hint)) {
|
2019-09-20 12:39:15 +02:00
|
|
|
did_something = true;
|
2020-05-08 15:40:49 +02:00
|
|
|
}
|
|
|
|
|
log_assert(!type_node->is_custom_type);
|
2019-09-19 21:43:13 +02:00
|
|
|
}
|
|
|
|
|
|
2019-09-19 22:07:20 +02:00
|
|
|
// resolve types of wires
|
2019-09-20 12:46:37 +02:00
|
|
|
if (type == AST_WIRE || type == AST_MEMORY) {
|
2019-09-19 21:43:13 +02:00
|
|
|
if (is_custom_type) {
|
2019-09-20 12:46:37 +02:00
|
|
|
log_assert(children.size() >= 1);
|
2019-09-19 21:43:13 +02:00
|
|
|
log_assert(children[0]->type == AST_WIRETYPE);
|
2020-05-08 15:40:49 +02:00
|
|
|
auto type_name = children[0]->str;
|
|
|
|
|
if (!current_scope.count(type_name)) {
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Unknown identifier `%s' used as type name\n", type_name.c_str());
|
2020-05-08 15:40:49 +02:00
|
|
|
}
|
|
|
|
|
AstNode *resolved_type_node = current_scope.at(type_name);
|
|
|
|
|
if (resolved_type_node->type != AST_TYPEDEF)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("`%s' does not name a type\n", type_name.c_str());
|
2020-05-08 15:40:49 +02:00
|
|
|
log_assert(resolved_type_node->children.size() == 1);
|
2025-06-16 22:55:24 +02:00
|
|
|
auto& template_node = resolved_type_node->children[0];
|
2020-05-08 15:40:49 +02:00
|
|
|
|
2024-01-01 01:18:00 +01:00
|
|
|
// Resolve the typedef from the bottom up, recursing within the current
|
|
|
|
|
// block of code. Defer further simplification until the complete type is
|
|
|
|
|
// resolved.
|
|
|
|
|
while (template_node->is_custom_type && template_node->simplify(const_fold, stage, width_hint, sign_hint)) {};
|
2020-05-08 15:40:49 +02:00
|
|
|
|
2023-01-29 20:22:00 +01:00
|
|
|
if (!str.empty() && str[0] == '\\' && (template_node->type == AST_STRUCT || template_node->type == AST_UNION)) {
|
|
|
|
|
// replace instance with wire representing the packed structure
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = make_packed_struct(template_node.get(), str, attributes);
|
2023-04-05 11:00:07 +02:00
|
|
|
newNode->set_attribute(ID::wiretype, mkconst_str(resolved_type_node->str));
|
2020-06-09 09:53:00 +02:00
|
|
|
// add original input/output attribute to resolved wire
|
|
|
|
|
newNode->is_input = this->is_input;
|
|
|
|
|
newNode->is_output = this->is_output;
|
2020-05-08 15:40:49 +02:00
|
|
|
current_scope[str] = this;
|
|
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-01 01:18:00 +01:00
|
|
|
// Prepare replacement node.
|
|
|
|
|
newNode = template_node->clone();
|
|
|
|
|
newNode->str = str;
|
|
|
|
|
newNode->set_attribute(ID::wiretype, mkconst_str(resolved_type_node->str));
|
|
|
|
|
newNode->is_input = is_input;
|
|
|
|
|
newNode->is_output = is_output;
|
|
|
|
|
newNode->is_wand = is_wand;
|
|
|
|
|
newNode->is_wor = is_wor;
|
|
|
|
|
for (auto &pair : attributes)
|
|
|
|
|
newNode->set_attribute(pair.first, pair.second->clone());
|
2019-09-19 21:43:13 +02:00
|
|
|
|
2024-01-01 01:18:00 +01:00
|
|
|
// if an enum then add attributes to support simulator tracing
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode->annotateTypedEnums(template_node.get());
|
2020-05-08 15:40:49 +02:00
|
|
|
|
2024-01-01 01:18:00 +01:00
|
|
|
bool add_packed_dimensions = (type == AST_WIRE && GetSize(children) > 1) || (type == AST_MEMORY && GetSize(children) > 2);
|
2020-05-08 15:40:49 +02:00
|
|
|
|
2024-01-01 01:18:00 +01:00
|
|
|
// Cannot add packed dimensions if unpacked dimensions are already specified.
|
|
|
|
|
if (add_packed_dimensions && newNode->type == AST_MEMORY)
|
|
|
|
|
input_error("Cannot extend unpacked type `%s' with packed dimensions\n", type_name.c_str());
|
2019-09-20 12:46:37 +02:00
|
|
|
|
2024-01-01 01:18:00 +01:00
|
|
|
// Add packed dimensions.
|
|
|
|
|
if (add_packed_dimensions) {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto& packed = children[1];
|
2024-01-01 01:18:00 +01:00
|
|
|
if (newNode->children.empty())
|
|
|
|
|
newNode->children.insert(newNode->children.begin(), packed->clone());
|
|
|
|
|
else
|
2025-06-16 22:55:24 +02:00
|
|
|
prepend_ranges(newNode->children[0], packed.get());
|
2024-01-01 01:18:00 +01:00
|
|
|
}
|
2018-03-09 13:47:11 +01:00
|
|
|
|
2024-01-01 01:18:00 +01:00
|
|
|
// Add unpacked dimensions.
|
|
|
|
|
if (type == AST_MEMORY) {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto& unpacked = children.back();
|
2024-01-01 01:18:00 +01:00
|
|
|
if (GetSize(newNode->children) < 2)
|
|
|
|
|
newNode->children.push_back(unpacked->clone());
|
|
|
|
|
else
|
2025-06-16 22:55:24 +02:00
|
|
|
prepend_ranges(newNode->children[1], unpacked.get());
|
2024-01-01 01:18:00 +01:00
|
|
|
newNode->type = type;
|
2019-09-20 12:46:37 +02:00
|
|
|
}
|
2024-01-01 01:18:00 +01:00
|
|
|
|
|
|
|
|
// Prepare to generate dimensions metadata for the resolved type.
|
|
|
|
|
newNode->dimensions.clear();
|
|
|
|
|
newNode->unpacked_dimensions = 0;
|
|
|
|
|
|
|
|
|
|
goto apply_newNode;
|
2019-09-19 21:43:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-19 22:07:20 +02:00
|
|
|
// resolve types of parameters
|
|
|
|
|
if (type == AST_LOCALPARAM || type == AST_PARAMETER) {
|
|
|
|
|
if (is_custom_type) {
|
2024-01-01 01:18:00 +01:00
|
|
|
log_assert(children.size() >= 2);
|
2019-09-19 22:07:20 +02:00
|
|
|
log_assert(children[1]->type == AST_WIRETYPE);
|
2024-01-05 19:29:06 +01:00
|
|
|
|
|
|
|
|
// Pretend it's just a wire in order to resolve the type in the code block above.
|
2024-01-01 01:18:00 +01:00
|
|
|
AstNodeType param_type = type;
|
|
|
|
|
type = AST_WIRE;
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> expr = std::move(children.front());
|
2024-01-01 01:18:00 +01:00
|
|
|
children.erase(children.begin());
|
2024-01-05 19:29:06 +01:00
|
|
|
while (is_custom_type && simplify(const_fold, stage, width_hint, sign_hint)) {};
|
2024-01-01 01:18:00 +01:00
|
|
|
type = param_type;
|
2025-06-16 22:55:24 +02:00
|
|
|
children.insert(children.begin(), std::move(expr));
|
2024-01-05 19:29:06 +01:00
|
|
|
|
2024-01-01 01:18:00 +01:00
|
|
|
if (children[1]->type == AST_MEMORY)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("unpacked array type `%s' cannot be used for a parameter\n", children[1]->str.c_str());
|
2023-04-05 11:00:07 +02:00
|
|
|
fixup_hierarchy_flags();
|
2019-09-20 12:39:15 +02:00
|
|
|
did_something = true;
|
2019-09-19 22:07:20 +02:00
|
|
|
}
|
|
|
|
|
log_assert(!is_custom_type);
|
2018-03-09 13:47:11 +01:00
|
|
|
}
|
2019-09-19 22:07:20 +02:00
|
|
|
|
2013-02-26 13:18:22 +01:00
|
|
|
// resolve constant prefixes
|
|
|
|
|
if (type == AST_PREFIX) {
|
|
|
|
|
if (children[0]->type != AST_CONSTANT) {
|
2025-06-16 22:55:24 +02:00
|
|
|
// dumpAst(nullptr, "> ");
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Index in generate block prefix syntax is not constant!\n");
|
2013-02-26 13:18:22 +01:00
|
|
|
}
|
2015-09-22 20:52:02 +02:00
|
|
|
if (children[1]->type == AST_PREFIX)
|
2023-04-04 22:59:44 +02:00
|
|
|
children[1]->simplify(const_fold, stage, width_hint, sign_hint);
|
2014-07-28 11:08:55 +02:00
|
|
|
log_assert(children[1]->type == AST_IDENTIFIER);
|
2013-02-26 13:18:22 +01:00
|
|
|
newNode = children[1]->clone();
|
2013-11-20 13:57:40 +01:00
|
|
|
const char *second_part = children[1]->str.c_str();
|
|
|
|
|
if (second_part[0] == '\\')
|
|
|
|
|
second_part++;
|
|
|
|
|
newNode->str = stringf("%s[%d].%s", str.c_str(), children[0]->integer, second_part);
|
2013-02-26 13:18:22 +01:00
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-01 13:50:23 +01:00
|
|
|
// evaluate TO_BITS nodes
|
|
|
|
|
if (type == AST_TO_BITS) {
|
|
|
|
|
if (children[0]->type != AST_CONSTANT)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Left operand of to_bits expression is not constant!\n");
|
2014-02-01 13:50:23 +01:00
|
|
|
if (children[1]->type != AST_CONSTANT)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Right operand of to_bits expression is not constant!\n");
|
2014-02-01 13:50:23 +01:00
|
|
|
RTLIL::Const new_value = children[1]->bitsAsConst(children[0]->bitsAsConst().as_int(), children[1]->is_signed);
|
2024-10-09 19:39:45 +02:00
|
|
|
newNode = mkconst_bits(new_value.to_bits(), children[1]->is_signed);
|
2014-02-01 13:50:23 +01:00
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
// annotate constant ranges
|
|
|
|
|
if (type == AST_RANGE) {
|
|
|
|
|
bool old_range_valid = range_valid;
|
|
|
|
|
range_valid = false;
|
2014-07-28 14:25:03 +02:00
|
|
|
range_swapped = false;
|
2013-01-05 11:13:26 +01:00
|
|
|
range_left = -1;
|
|
|
|
|
range_right = 0;
|
2014-07-28 11:08:55 +02:00
|
|
|
log_assert(children.size() >= 1);
|
2013-01-05 11:13:26 +01:00
|
|
|
if (children[0]->type == AST_CONSTANT) {
|
|
|
|
|
range_valid = true;
|
|
|
|
|
range_left = children[0]->integer;
|
|
|
|
|
if (children.size() == 1)
|
|
|
|
|
range_right = range_left;
|
|
|
|
|
}
|
|
|
|
|
if (children.size() >= 2) {
|
|
|
|
|
if (children[1]->type == AST_CONSTANT)
|
|
|
|
|
range_right = children[1]->integer;
|
|
|
|
|
else
|
|
|
|
|
range_valid = false;
|
|
|
|
|
}
|
|
|
|
|
if (old_range_valid != range_valid)
|
|
|
|
|
did_something = true;
|
2020-05-04 21:18:20 +02:00
|
|
|
if (range_valid && range_right > range_left) {
|
2024-01-25 07:28:15 +01:00
|
|
|
std::swap(range_left, range_right);
|
2014-07-28 14:25:03 +02:00
|
|
|
range_swapped = true;
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// annotate wires with their ranges
|
|
|
|
|
if (type == AST_WIRE) {
|
|
|
|
|
if (children.size() > 0) {
|
|
|
|
|
if (children[0]->range_valid) {
|
|
|
|
|
if (!range_valid)
|
|
|
|
|
did_something = true;
|
|
|
|
|
range_valid = true;
|
2014-07-28 14:25:03 +02:00
|
|
|
range_swapped = children[0]->range_swapped;
|
2013-01-05 11:13:26 +01:00
|
|
|
range_left = children[0]->range_left;
|
|
|
|
|
range_right = children[0]->range_right;
|
2020-05-18 18:15:03 +02:00
|
|
|
bool force_upto = false, force_downto = false;
|
|
|
|
|
if (attributes.count(ID::force_upto)) {
|
2025-06-16 22:55:24 +02:00
|
|
|
AstNode *val = attributes[ID::force_upto].get();
|
2020-05-18 18:15:03 +02:00
|
|
|
if (val->type != AST_CONSTANT)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Attribute `force_upto' with non-constant value!\n");
|
2020-05-18 18:15:03 +02:00
|
|
|
force_upto = val->asAttrConst().as_bool();
|
|
|
|
|
}
|
|
|
|
|
if (attributes.count(ID::force_downto)) {
|
2025-06-16 22:55:24 +02:00
|
|
|
AstNode *val = attributes[ID::force_downto].get();
|
2020-05-18 18:15:03 +02:00
|
|
|
if (val->type != AST_CONSTANT)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Attribute `force_downto' with non-constant value!\n");
|
2020-05-18 18:15:03 +02:00
|
|
|
force_downto = val->asAttrConst().as_bool();
|
|
|
|
|
}
|
|
|
|
|
if (force_upto && force_downto)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Attributes `force_downto' and `force_upto' cannot be both set!\n");
|
2020-05-18 18:15:03 +02:00
|
|
|
if ((force_upto && !range_swapped) || (force_downto && range_swapped)) {
|
|
|
|
|
std::swap(range_left, range_right);
|
|
|
|
|
range_swapped = force_upto;
|
|
|
|
|
}
|
2025-06-04 10:32:03 +02:00
|
|
|
if (range_left == range_right && !attributes.count(ID::single_bit_vector))
|
2025-05-06 12:02:00 +02:00
|
|
|
set_attribute(ID::single_bit_vector, mkconst_int(1, false));
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (!range_valid)
|
|
|
|
|
did_something = true;
|
|
|
|
|
range_valid = true;
|
2014-07-28 14:25:03 +02:00
|
|
|
range_swapped = false;
|
2013-01-05 11:13:26 +01:00
|
|
|
range_left = 0;
|
|
|
|
|
range_right = 0;
|
2025-06-16 22:55:24 +02:00
|
|
|
attributes.erase(ID::single_bit_vector);
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-25 07:28:15 +01:00
|
|
|
// Resolve packed and unpacked ranges in declarations.
|
|
|
|
|
if ((type == AST_WIRE || type == AST_MEMORY) && dimensions.empty()) {
|
|
|
|
|
if (!children.empty()) {
|
|
|
|
|
// Unpacked ranges first, then packed ranges.
|
|
|
|
|
for (int i = std::min(GetSize(children), 2) - 1; i >= 0; i--) {
|
|
|
|
|
if (children[i]->type == AST_MULTIRANGE) {
|
|
|
|
|
int width = 1;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& range : children[i]->children) {
|
|
|
|
|
width *= add_dimension(this, range.get());
|
2024-01-25 07:28:15 +01:00
|
|
|
if (i) unpacked_dimensions++;
|
|
|
|
|
}
|
|
|
|
|
int left = width - 1, right = 0;
|
|
|
|
|
if (i)
|
|
|
|
|
std::swap(left, right);
|
2025-06-16 22:55:24 +02:00
|
|
|
children[i] = std::make_unique<AstNode>(AST_RANGE, mkconst_int(left, true), mkconst_int(right, true));
|
2024-01-25 07:28:15 +01:00
|
|
|
fixup_hierarchy_flags();
|
|
|
|
|
did_something = true;
|
|
|
|
|
} else if (children[i]->type == AST_RANGE) {
|
2025-06-16 22:55:24 +02:00
|
|
|
add_dimension(this, children[i].get());
|
2024-01-25 07:28:15 +01:00
|
|
|
if (i) unpacked_dimensions++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 1 bit signal: bit, logic or reg
|
|
|
|
|
dimensions.push_back({ 0, 1, false });
|
2014-08-06 15:43:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-25 07:28:15 +01:00
|
|
|
// Resolve multidimensional array access.
|
|
|
|
|
if (type == AST_IDENTIFIER && !basic_prep && id2ast && (id2ast->type == AST_WIRE || id2ast->type == AST_MEMORY) &&
|
2025-06-16 22:55:24 +02:00
|
|
|
children.size() > 0 && (children[0]->type == AST_RANGE || children[0]->type == AST_MULTIRANGE))
|
2014-08-06 15:43:46 +02:00
|
|
|
{
|
2024-01-25 07:28:15 +01:00
|
|
|
int dims_sel = children[0]->type == AST_MULTIRANGE ? children[0]->children.size() : 1;
|
|
|
|
|
// Save original number of dimensions for $size() etc.
|
|
|
|
|
integer = dims_sel;
|
|
|
|
|
|
|
|
|
|
// Split access into unpacked and packed parts.
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> unpacked_range = nullptr;
|
|
|
|
|
std::unique_ptr<AstNode> packed_range = nullptr;
|
2024-01-25 07:28:15 +01:00
|
|
|
|
|
|
|
|
if (id2ast->unpacked_dimensions) {
|
|
|
|
|
if (id2ast->unpacked_dimensions > 1) {
|
|
|
|
|
// Flattened range for access to unpacked dimensions.
|
|
|
|
|
unpacked_range = make_index_range(id2ast, true);
|
|
|
|
|
} else {
|
|
|
|
|
// Index into one-dimensional unpacked part; unlink simple range node.
|
2025-06-16 22:55:24 +02:00
|
|
|
auto& range = children[0]->type == AST_MULTIRANGE ? children[0]->children[0] : children[0];
|
|
|
|
|
unpacked_range = std::move(range);
|
2024-01-25 07:28:15 +01:00
|
|
|
range = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-08-06 15:43:46 +02:00
|
|
|
|
2024-01-25 07:28:15 +01:00
|
|
|
if (dims_sel > id2ast->unpacked_dimensions) {
|
|
|
|
|
if (GetSize(id2ast->dimensions) - id2ast->unpacked_dimensions > 1) {
|
|
|
|
|
// Flattened range for access to packed dimensions.
|
|
|
|
|
packed_range = make_index_range(id2ast, false);
|
|
|
|
|
} else {
|
|
|
|
|
// Index into one-dimensional packed part; unlink simple range node.
|
2025-06-16 22:55:24 +02:00
|
|
|
auto& range = children[0]->type == AST_MULTIRANGE ? children[0]->children[dims_sel - 1] : children[0];
|
|
|
|
|
packed_range = std::move(range);
|
2024-01-25 07:28:15 +01:00
|
|
|
range = nullptr;
|
|
|
|
|
}
|
2014-08-06 15:43:46 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-25 07:28:15 +01:00
|
|
|
children.clear();
|
2014-08-06 15:43:46 +02:00
|
|
|
|
2024-01-25 07:28:15 +01:00
|
|
|
if (unpacked_range)
|
2025-06-16 22:55:24 +02:00
|
|
|
children.push_back(std::move(unpacked_range));
|
2024-01-25 07:28:15 +01:00
|
|
|
|
|
|
|
|
if (packed_range)
|
2025-06-16 22:55:24 +02:00
|
|
|
children.push_back(std::move(packed_range));
|
2014-08-06 15:43:46 +02:00
|
|
|
|
2023-04-05 11:00:07 +02:00
|
|
|
fixup_hierarchy_flags();
|
2024-01-25 07:28:15 +01:00
|
|
|
basic_prep = true;
|
2014-08-06 15:43:46 +02:00
|
|
|
did_something = true;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-07 09:58:15 +01:00
|
|
|
// trim/extend parameters
|
2018-03-09 13:47:11 +01:00
|
|
|
if (type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_ENUM_ITEM) {
|
2014-06-14 12:00:47 +02:00
|
|
|
if (children.size() > 1 && children[1]->type == AST_RANGE) {
|
2014-06-14 20:38:05 +02:00
|
|
|
if (!children[1]->range_valid)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Non-constant width range on parameter decl.\n");
|
2015-06-08 14:03:06 +02:00
|
|
|
int width = std::abs(children[1]->range_left - children[1]->range_right) + 1;
|
2014-06-14 12:00:47 +02:00
|
|
|
if (children[0]->type == AST_REALVALUE) {
|
2014-06-14 20:38:05 +02:00
|
|
|
RTLIL::Const constvalue = children[0]->realAsConst(width);
|
2020-02-23 08:19:52 +01:00
|
|
|
log_file_warning(filename, location.first_line, "converting real value %e to binary %s.\n",
|
2018-11-04 10:19:32 +01:00
|
|
|
children[0]->realvalue, log_signal(constvalue));
|
2024-10-09 19:39:45 +02:00
|
|
|
children[0] = mkconst_bits(constvalue.to_bits(), sign_hint);
|
2023-04-05 11:00:07 +02:00
|
|
|
fixup_hierarchy_flags();
|
2014-06-14 12:00:47 +02:00
|
|
|
did_something = true;
|
|
|
|
|
}
|
|
|
|
|
if (children[0]->type == AST_CONSTANT) {
|
|
|
|
|
if (width != int(children[0]->bits.size())) {
|
|
|
|
|
RTLIL::SigSpec sig(children[0]->bits);
|
|
|
|
|
sig.extend_u0(width, children[0]->is_signed);
|
2024-10-09 19:39:45 +02:00
|
|
|
children[0] = mkconst_bits(sig.as_const().to_bits(), is_signed);
|
2023-04-05 11:00:07 +02:00
|
|
|
fixup_hierarchy_flags();
|
2014-06-14 12:00:47 +02:00
|
|
|
}
|
|
|
|
|
children[0]->is_signed = is_signed;
|
|
|
|
|
}
|
2014-07-28 16:45:26 +02:00
|
|
|
range_valid = true;
|
|
|
|
|
range_swapped = children[1]->range_swapped;
|
|
|
|
|
range_left = children[1]->range_left;
|
|
|
|
|
range_right = children[1]->range_right;
|
2014-06-14 12:00:47 +02:00
|
|
|
} else
|
|
|
|
|
if (children.size() > 1 && children[1]->type == AST_REALVALUE && children[0]->type == AST_CONSTANT) {
|
|
|
|
|
double as_realvalue = children[0]->asReal(sign_hint);
|
2025-06-16 22:55:24 +02:00
|
|
|
children[0] = std::make_unique<AstNode>(AST_REALVALUE);
|
2014-06-14 12:00:47 +02:00
|
|
|
children[0]->realvalue = as_realvalue;
|
2023-04-05 11:00:07 +02:00
|
|
|
fixup_hierarchy_flags();
|
2014-06-14 12:00:47 +02:00
|
|
|
did_something = true;
|
2013-11-07 09:58:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-08 15:40:49 +02:00
|
|
|
if (type == AST_IDENTIFIER && !basic_prep) {
|
|
|
|
|
// check if a plausible struct member sss.mmmm
|
2024-01-04 17:22:07 +01:00
|
|
|
if (!str.empty() && str[0] == '\\' && current_scope.count(str)) {
|
|
|
|
|
auto item_node = current_scope[str];
|
|
|
|
|
if (item_node->type == AST_STRUCT_ITEM || item_node->type == AST_STRUCT || item_node->type == AST_UNION) {
|
|
|
|
|
// Traverse any hierarchical path until the full name for the referenced struct/union is found.
|
|
|
|
|
std::string sname;
|
|
|
|
|
bool found_sname = false;
|
|
|
|
|
for (std::string::size_type pos = 0; (pos = str.find('.', pos)) != std::string::npos; pos++) {
|
|
|
|
|
sname = str.substr(0, pos);
|
|
|
|
|
if (current_scope.count(sname)) {
|
|
|
|
|
auto stype = current_scope[sname]->type;
|
|
|
|
|
if (stype == AST_WIRE || stype == AST_PARAMETER || stype == AST_LOCALPARAM) {
|
|
|
|
|
found_sname = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (found_sname) {
|
2020-05-08 15:40:49 +02:00
|
|
|
// structure member, rewrite this node to reference the packed struct wire
|
2024-01-25 07:28:15 +01:00
|
|
|
auto range = make_index_range(item_node);
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_IDENTIFIER, std::move(range));
|
2020-05-08 15:40:49 +02:00
|
|
|
newNode->str = sname;
|
2023-02-09 19:27:51 +01:00
|
|
|
// save type and original number of dimensions for $size() etc.
|
2023-04-05 11:00:07 +02:00
|
|
|
newNode->set_attribute(ID::wiretype, item_node->clone());
|
2024-01-25 07:28:15 +01:00
|
|
|
if (!item_node->dimensions.empty() && children.size() > 0) {
|
2023-02-09 19:27:51 +01:00
|
|
|
if (children[0]->type == AST_RANGE)
|
|
|
|
|
newNode->integer = 1;
|
|
|
|
|
else if (children[0]->type == AST_MULTIRANGE)
|
|
|
|
|
newNode->integer = children[0]->children.size();
|
|
|
|
|
}
|
2020-05-08 15:40:49 +02:00
|
|
|
newNode->basic_prep = true;
|
2021-06-22 16:39:57 +02:00
|
|
|
if (item_node->is_signed)
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_TO_SIGNED, std::move(newNode));
|
2020-05-08 15:40:49 +02:00
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-07 19:28:45 +02:00
|
|
|
// annotate identifiers using scope resolution and create auto-wires as needed
|
2013-01-05 11:13:26 +01:00
|
|
|
if (type == AST_IDENTIFIER) {
|
|
|
|
|
if (current_scope.count(str) == 0) {
|
2020-04-16 11:27:59 +02:00
|
|
|
AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod;
|
2021-06-05 22:21:09 +02:00
|
|
|
str = try_pop_module_prefix();
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& node : current_scope_ast->children) {
|
2018-03-09 13:47:11 +01:00
|
|
|
//log("looking at mod scope child %s\n", type2str(node->type).c_str());
|
|
|
|
|
switch (node->type) {
|
|
|
|
|
case AST_PARAMETER:
|
|
|
|
|
case AST_LOCALPARAM:
|
|
|
|
|
case AST_WIRE:
|
|
|
|
|
case AST_AUTOWIRE:
|
|
|
|
|
case AST_GENVAR:
|
|
|
|
|
case AST_MEMORY:
|
|
|
|
|
case AST_FUNCTION:
|
|
|
|
|
case AST_TASK:
|
|
|
|
|
case AST_DPI_FUNCTION:
|
|
|
|
|
//log("found child %s, %s\n", type2str(node->type).c_str(), node->str.c_str());
|
2020-01-17 07:21:09 +01:00
|
|
|
if (str == node->str) {
|
2020-02-28 01:53:49 +01:00
|
|
|
//log("add %s, type %s to scope\n", str.c_str(), type2str(node->type).c_str());
|
2025-06-16 22:55:24 +02:00
|
|
|
current_scope[node->str] = node.get();
|
2020-01-17 07:21:09 +01:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
break;
|
2018-03-09 13:47:11 +01:00
|
|
|
case AST_ENUM:
|
2025-06-16 22:55:24 +02:00
|
|
|
current_scope[node->str] = node.get();
|
|
|
|
|
for (auto& enum_node : node->children) {
|
2018-03-09 13:47:11 +01:00
|
|
|
log_assert(enum_node->type==AST_ENUM_ITEM);
|
|
|
|
|
if (str == enum_node->str) {
|
2020-02-03 07:12:24 +01:00
|
|
|
//log("\nadding enum item %s to scope\n", str.c_str());
|
2025-06-16 22:55:24 +02:00
|
|
|
current_scope[str] = enum_node.get();
|
2018-03-09 13:47:11 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2013-01-05 11:13:26 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (current_scope.count(str) == 0) {
|
2020-04-16 11:27:59 +02:00
|
|
|
if (current_ast_mod == nullptr) {
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Identifier `%s' is implicitly declared outside of a module.\n", str.c_str());
|
2020-04-16 11:27:59 +02:00
|
|
|
} else if (flag_autowire || str == "\\$global_clock") {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto auto_wire = std::make_unique<AstNode>(AST_AUTOWIRE);
|
2019-02-21 18:40:11 +01:00
|
|
|
auto_wire->str = str;
|
2025-06-16 22:55:24 +02:00
|
|
|
current_scope[str] = auto_wire.get();
|
|
|
|
|
current_ast_mod->children.push_back(std::move(auto_wire));
|
2019-02-21 18:40:11 +01:00
|
|
|
did_something = true;
|
|
|
|
|
} else {
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str());
|
2019-02-21 18:40:11 +01:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
2014-03-05 19:45:33 +01:00
|
|
|
if (id2ast != current_scope[str]) {
|
|
|
|
|
id2ast = current_scope[str];
|
|
|
|
|
did_something = true;
|
|
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-20 10:51:32 +01:00
|
|
|
// split memory access with bit select to individual statements
|
2021-05-27 00:22:31 +02:00
|
|
|
if (type == AST_IDENTIFIER && children.size() == 2 && children[0]->type == AST_RANGE && children[1]->type == AST_RANGE && !in_lvalue && stage == 2)
|
2013-11-20 10:51:32 +01:00
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
if (id2ast == nullptr || id2ast->type != AST_MEMORY || children[0]->children.size() != 1)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Invalid bit-select on memory access!\n");
|
2013-11-20 10:51:32 +01:00
|
|
|
|
|
|
|
|
int mem_width, mem_size, addr_bits;
|
|
|
|
|
id2ast->meminfo(mem_width, mem_size, addr_bits);
|
|
|
|
|
|
2016-08-22 14:27:46 +02:00
|
|
|
int data_range_left = id2ast->children[0]->range_left;
|
|
|
|
|
int data_range_right = id2ast->children[0]->range_right;
|
|
|
|
|
|
2019-03-21 22:19:17 +01:00
|
|
|
if (id2ast->children[0]->range_swapped)
|
|
|
|
|
std::swap(data_range_left, data_range_right);
|
|
|
|
|
|
2013-11-20 10:51:32 +01:00
|
|
|
std::stringstream sstr;
|
2022-08-08 16:13:33 +02:00
|
|
|
sstr << "$mem2bits$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++);
|
2013-11-20 10:51:32 +01:00
|
|
|
std::string wire_id = sstr.str();
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto wire_owned = std::make_unique<AstNode>(AST_WIRE, std::make_unique<AstNode>(AST_RANGE, mkconst_int(data_range_left, true), mkconst_int(data_range_right, true)));
|
|
|
|
|
auto* wire = wire_owned.get();
|
|
|
|
|
current_ast_mod->children.push_back(std::move(wire_owned));
|
2013-11-20 10:51:32 +01:00
|
|
|
wire->str = wire_id;
|
|
|
|
|
if (current_block)
|
2023-04-05 11:00:07 +02:00
|
|
|
wire->set_attribute(ID::nosync, AstNode::mkconst_int(1, false));
|
2023-04-04 22:59:44 +02:00
|
|
|
while (wire->simplify(true, 1, -1, false)) { }
|
2013-11-20 10:51:32 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto data = clone();
|
2013-11-20 10:51:32 +01:00
|
|
|
data->children.pop_back();
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto assign = std::make_unique<AstNode>(AST_ASSIGN_EQ, std::make_unique<AstNode>(AST_IDENTIFIER), std::move(data));
|
2013-11-20 10:51:32 +01:00
|
|
|
assign->children[0]->str = wire_id;
|
2018-06-05 16:44:24 +02:00
|
|
|
assign->children[0]->was_checked = true;
|
2013-11-20 10:51:32 +01:00
|
|
|
|
|
|
|
|
if (current_block)
|
|
|
|
|
{
|
|
|
|
|
size_t assign_idx = 0;
|
2025-06-16 22:55:24 +02:00
|
|
|
while (assign_idx < current_block->children.size() && current_block->children[assign_idx].get() != current_block_child)
|
2013-11-20 10:51:32 +01:00
|
|
|
assign_idx++;
|
|
|
|
|
log_assert(assign_idx < current_block->children.size());
|
2025-06-16 22:55:24 +02:00
|
|
|
current_block->children.insert(current_block->children.begin()+assign_idx, std::move(assign));
|
2013-11-20 10:51:32 +01:00
|
|
|
wire->is_reg = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
auto proc = std::make_unique<AstNode>(AST_ALWAYS, std::make_unique<AstNode>(AST_BLOCK));
|
|
|
|
|
proc->children[0]->children.push_back(std::move(assign));
|
|
|
|
|
current_ast_mod->children.push_back(std::move(proc));
|
2013-11-20 10:51:32 +01:00
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_IDENTIFIER, children[1]->clone());
|
2013-11-20 10:51:32 +01:00
|
|
|
newNode->str = wire_id;
|
2020-09-16 23:55:17 +02:00
|
|
|
newNode->integer = integer; // save original number of dimensions for $size() etc.
|
2013-11-20 10:51:32 +01:00
|
|
|
newNode->id2ast = wire;
|
|
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-06 22:55:02 +02:00
|
|
|
if (type == AST_WHILE)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("While loops are only allowed in constant functions!\n");
|
2014-06-06 22:55:02 +02:00
|
|
|
|
|
|
|
|
if (type == AST_REPEAT)
|
2019-04-09 18:28:32 +02:00
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
auto count = std::move(children[0]);
|
|
|
|
|
auto body = std::move(children[1]);
|
2019-04-09 18:28:32 +02:00
|
|
|
|
|
|
|
|
// eval count expression
|
2023-04-04 22:59:44 +02:00
|
|
|
while (count->simplify(true, stage, 32, true)) { }
|
2019-04-09 18:28:32 +02:00
|
|
|
|
|
|
|
|
if (count->type != AST_CONSTANT)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Repeat loops outside must have constant repeat counts!\n");
|
2019-04-09 18:28:32 +02:00
|
|
|
|
|
|
|
|
// convert to a block with the body repeated n times
|
|
|
|
|
type = AST_BLOCK;
|
|
|
|
|
children.clear();
|
|
|
|
|
for (int i = 0; i < count->bitsAsConst().as_int(); i++)
|
|
|
|
|
children.insert(children.begin(), body->clone());
|
|
|
|
|
|
|
|
|
|
did_something = true;
|
|
|
|
|
}
|
2014-06-06 22:55:02 +02:00
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
// unroll for loops and generate-for blocks
|
|
|
|
|
if ((type == AST_GENFOR || type == AST_FOR) && children.size() != 0)
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
auto& init_ast = children[0];
|
|
|
|
|
auto& while_ast = children[1];
|
|
|
|
|
auto& next_ast = children[2];
|
|
|
|
|
auto* body_ast = children[3].get();
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2013-12-04 21:33:00 +01:00
|
|
|
while (body_ast->type == AST_GENBLOCK && body_ast->str.empty() &&
|
|
|
|
|
body_ast->children.size() == 1 && body_ast->children.at(0)->type == AST_GENBLOCK)
|
2025-06-16 22:55:24 +02:00
|
|
|
body_ast = body_ast->children.at(0).get();
|
2013-12-04 21:33:00 +01:00
|
|
|
|
2020-07-25 18:35:03 +02:00
|
|
|
const char* loop_type_str = "procedural";
|
|
|
|
|
const char* var_type_str = "register";
|
|
|
|
|
AstNodeType var_type = AST_WIRE;
|
|
|
|
|
if (type == AST_GENFOR) {
|
|
|
|
|
loop_type_str = "generate";
|
|
|
|
|
var_type_str = "genvar";
|
|
|
|
|
var_type = AST_GENVAR;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
if (init_ast->type != AST_ASSIGN_EQ)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Unsupported 1st expression of %s for-loop!\n", loop_type_str);
|
2013-01-05 11:13:26 +01:00
|
|
|
if (next_ast->type != AST_ASSIGN_EQ)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Unsupported 3rd expression of %s for-loop!\n", loop_type_str);
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
if (init_ast->children[0]->id2ast == nullptr || init_ast->children[0]->id2ast->type != var_type)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Left hand side of 1st expression of %s for-loop is not a %s!\n", loop_type_str, var_type_str);
|
2025-06-16 22:55:24 +02:00
|
|
|
if (next_ast->children[0]->id2ast == nullptr || next_ast->children[0]->id2ast->type != var_type)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Left hand side of 3rd expression of %s for-loop is not a %s!\n", loop_type_str, var_type_str);
|
2013-01-05 11:13:26 +01:00
|
|
|
|
|
|
|
|
if (init_ast->children[0]->id2ast != next_ast->children[0]->id2ast)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Incompatible left-hand sides in 1st and 3rd expression of %s for-loop!\n", loop_type_str);
|
2013-01-05 11:13:26 +01:00
|
|
|
|
|
|
|
|
// eval 1st expression
|
2025-06-16 22:55:24 +02:00
|
|
|
auto varbuf = init_ast->children[1]->clone();
|
2019-04-22 18:19:02 +02:00
|
|
|
{
|
|
|
|
|
int expr_width_hint = -1;
|
|
|
|
|
bool expr_sign_hint = true;
|
|
|
|
|
varbuf->detectSignWidth(expr_width_hint, expr_sign_hint);
|
2023-04-04 22:59:44 +02:00
|
|
|
while (varbuf->simplify(true, stage, 32, true)) { }
|
2019-04-22 18:19:02 +02:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
|
|
|
|
|
if (varbuf->type != AST_CONSTANT)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Right hand side of 1st expression of %s for-loop is not constant!\n", loop_type_str);
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2019-12-12 01:26:26 +01:00
|
|
|
auto resolved = current_scope.at(init_ast->children[0]->str);
|
|
|
|
|
if (resolved->range_valid) {
|
2020-02-06 02:19:42 +01:00
|
|
|
int const_size = varbuf->range_left - varbuf->range_right;
|
|
|
|
|
int resolved_size = resolved->range_left - resolved->range_right;
|
|
|
|
|
if (const_size < resolved_size) {
|
|
|
|
|
for (int i = const_size; i < resolved_size; i++)
|
|
|
|
|
varbuf->bits.push_back(resolved->is_signed ? varbuf->bits.back() : State::S0);
|
|
|
|
|
varbuf->range_left = resolved->range_left;
|
|
|
|
|
varbuf->range_right = resolved->range_right;
|
|
|
|
|
varbuf->range_swapped = resolved->range_swapped;
|
|
|
|
|
varbuf->range_valid = resolved->range_valid;
|
|
|
|
|
}
|
2019-12-12 01:26:26 +01:00
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
varbuf = std::make_unique<AstNode>(AST_LOCALPARAM, std::move(varbuf));
|
2020-02-06 02:19:42 +01:00
|
|
|
varbuf->str = init_ast->children[0]->str;
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
AstNode *backup_scope_varbuf = current_scope[varbuf->str];
|
2025-06-16 22:55:24 +02:00
|
|
|
current_scope[varbuf->str] = varbuf.get();
|
2013-01-05 11:13:26 +01:00
|
|
|
|
|
|
|
|
size_t current_block_idx = 0;
|
|
|
|
|
if (type == AST_FOR) {
|
|
|
|
|
while (current_block_idx < current_block->children.size() &&
|
2025-06-16 22:55:24 +02:00
|
|
|
current_block->children[current_block_idx].get() != current_block_child)
|
2013-01-05 11:13:26 +01:00
|
|
|
current_block_idx++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
// eval 2nd expression
|
2025-06-16 22:55:24 +02:00
|
|
|
auto buf = while_ast->clone();
|
2019-04-22 18:19:02 +02:00
|
|
|
{
|
|
|
|
|
int expr_width_hint = -1;
|
|
|
|
|
bool expr_sign_hint = true;
|
|
|
|
|
buf->detectSignWidth(expr_width_hint, expr_sign_hint);
|
2023-04-04 22:59:44 +02:00
|
|
|
while (buf->simplify(true, stage, expr_width_hint, expr_sign_hint)) { }
|
2019-04-22 18:19:02 +02:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
|
|
|
|
|
if (buf->type != AST_CONSTANT)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("2nd expression of %s for-loop is not constant!\n", loop_type_str);
|
2013-01-05 11:13:26 +01:00
|
|
|
|
|
|
|
|
if (buf->integer == 0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// expand body
|
|
|
|
|
int index = varbuf->children[0]->integer;
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
log_assert(body_ast->type == AST_GENBLOCK || body_ast->type == AST_BLOCK);
|
|
|
|
|
log_assert(!body_ast->str.empty());
|
|
|
|
|
buf = body_ast->clone();
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
std::stringstream sstr;
|
|
|
|
|
sstr << buf->str << "[" << index << "].";
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
std::string prefix = sstr.str();
|
|
|
|
|
|
|
|
|
|
// create a scoped localparam for the current value of the loop variable
|
2025-06-16 22:55:24 +02:00
|
|
|
auto local_index = varbuf->clone();
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
size_t pos = local_index->str.rfind('.');
|
|
|
|
|
if (pos != std::string::npos) // remove outer prefix
|
|
|
|
|
local_index->str = "\\" + local_index->str.substr(pos + 1);
|
|
|
|
|
local_index->str = prefix_id(prefix, local_index->str);
|
2025-06-16 22:55:24 +02:00
|
|
|
current_scope[local_index->str] = local_index.get();
|
|
|
|
|
current_ast_mod->children.push_back(std::move(local_index));
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
|
|
|
|
|
buf->expand_genblock(prefix);
|
2013-01-05 11:13:26 +01:00
|
|
|
|
|
|
|
|
if (type == AST_GENFOR) {
|
2013-11-20 13:57:40 +01:00
|
|
|
for (size_t i = 0; i < buf->children.size(); i++) {
|
2023-04-04 22:59:44 +02:00
|
|
|
buf->children[i]->simplify(const_fold, stage, -1, false);
|
2025-06-16 22:55:24 +02:00
|
|
|
current_ast_mod->children.push_back(std::move(buf->children[i]));
|
2013-11-20 13:57:40 +01:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
} else {
|
|
|
|
|
for (size_t i = 0; i < buf->children.size(); i++)
|
2025-06-16 22:55:24 +02:00
|
|
|
current_block->children.insert(current_block->children.begin() + current_block_idx++, std::move(buf->children[i]));
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
buf->children.clear();
|
|
|
|
|
|
|
|
|
|
// eval 3rd expression
|
|
|
|
|
buf = next_ast->children[1]->clone();
|
2023-04-05 11:00:07 +02:00
|
|
|
buf->set_in_param_flag(true);
|
2019-04-22 18:19:02 +02:00
|
|
|
{
|
|
|
|
|
int expr_width_hint = -1;
|
|
|
|
|
bool expr_sign_hint = true;
|
|
|
|
|
buf->detectSignWidth(expr_width_hint, expr_sign_hint);
|
2023-04-04 22:59:44 +02:00
|
|
|
while (buf->simplify(true, stage, expr_width_hint, expr_sign_hint)) { }
|
2019-04-22 18:19:02 +02:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
|
|
|
|
|
if (buf->type != AST_CONSTANT)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Right hand side of 3rd expression of %s for-loop is not constant (%s)!\n", loop_type_str, type2str(buf->type).c_str());
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
varbuf->children[0] = std::move(buf);
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
|
2019-04-30 15:03:32 +02:00
|
|
|
if (type == AST_FOR) {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto buf = next_ast->clone();
|
2019-04-30 15:03:32 +02:00
|
|
|
buf->children[1] = varbuf->children[0]->clone();
|
2025-06-16 22:55:24 +02:00
|
|
|
current_block->children.insert(current_block->children.begin() + current_block_idx++, std::move(buf));
|
2019-04-30 15:03:32 +02:00
|
|
|
}
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
current_scope[varbuf->str] = backup_scope_varbuf;
|
|
|
|
|
delete_children();
|
|
|
|
|
did_something = true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-04 16:03:04 +01:00
|
|
|
// check for local objects in unnamed block
|
|
|
|
|
if (type == AST_BLOCK && str.empty())
|
|
|
|
|
{
|
|
|
|
|
for (size_t i = 0; i < children.size(); i++)
|
2019-09-19 21:43:13 +02:00
|
|
|
if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF)
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
log_assert(!sv_mode);
|
2023-04-04 11:53:50 +02:00
|
|
|
children[i]->input_error("Local declaration in unnamed block is only supported in SystemVerilog mode!\n");
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
}
|
2017-01-04 16:03:04 +01:00
|
|
|
}
|
|
|
|
|
|
2013-12-04 09:10:16 +01:00
|
|
|
// transform block with name
|
|
|
|
|
if (type == AST_BLOCK && !str.empty())
|
|
|
|
|
{
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
expand_genblock(str + ".");
|
2013-12-04 09:10:16 +01:00
|
|
|
|
2022-01-07 06:04:00 +01:00
|
|
|
// if this is an autonamed block is in an always_comb
|
|
|
|
|
if (current_always && current_always->attributes.count(ID::always_comb)
|
2022-02-22 16:57:08 +01:00
|
|
|
&& is_autonamed_block(str))
|
2022-01-07 06:04:00 +01:00
|
|
|
// track local variables in this block so we can consider adding
|
|
|
|
|
// nosync once the block has been fully elaborated
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children)
|
2022-01-07 06:04:00 +01:00
|
|
|
if (child->type == AST_WIRE &&
|
|
|
|
|
!child->attributes.count(ID::nosync))
|
2025-06-16 22:55:24 +02:00
|
|
|
mark_auto_nosync(this, child.get());
|
2022-01-07 06:04:00 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
std::vector<std::unique_ptr<AstNode>> new_children;
|
2013-12-04 09:10:16 +01:00
|
|
|
for (size_t i = 0; i < children.size(); i++)
|
2019-09-19 21:43:13 +02:00
|
|
|
if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF) {
|
2023-04-04 22:59:44 +02:00
|
|
|
children[i]->simplify(false, stage, -1, false);
|
2025-06-16 22:55:24 +02:00
|
|
|
current_scope[children[i]->str] = children[i].get();
|
|
|
|
|
current_ast_mod->children.push_back(std::move(children[i]));
|
2013-12-04 09:10:16 +01:00
|
|
|
} else
|
2025-06-16 22:55:24 +02:00
|
|
|
new_children.push_back(std::move(children[i]));
|
2013-12-04 09:10:16 +01:00
|
|
|
|
|
|
|
|
children.swap(new_children);
|
|
|
|
|
did_something = true;
|
|
|
|
|
str.clear();
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-26 09:44:54 +01:00
|
|
|
// simplify unconditional generate block
|
|
|
|
|
if (type == AST_GENBLOCK && children.size() != 0)
|
|
|
|
|
{
|
|
|
|
|
if (!str.empty()) {
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
expand_genblock(str + ".");
|
2013-03-26 09:44:54 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-20 13:57:40 +01:00
|
|
|
for (size_t i = 0; i < children.size(); i++) {
|
2023-04-04 22:59:44 +02:00
|
|
|
children[i]->simplify(const_fold, stage, -1, false);
|
2025-06-16 22:55:24 +02:00
|
|
|
current_ast_mod->children.push_back(std::move(children[i]));
|
2013-11-20 13:57:40 +01:00
|
|
|
}
|
2013-03-26 09:44:54 +01:00
|
|
|
|
|
|
|
|
children.clear();
|
|
|
|
|
did_something = true;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
// simplify generate-if blocks
|
|
|
|
|
if (type == AST_GENIF && children.size() != 0)
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
auto buf = children[0]->clone();
|
2023-04-04 22:59:44 +02:00
|
|
|
while (buf->simplify(true, stage, width_hint, sign_hint)) { }
|
2013-01-05 11:13:26 +01:00
|
|
|
if (buf->type != AST_CONSTANT) {
|
2013-11-07 14:08:53 +01:00
|
|
|
// for (auto f : log_files)
|
|
|
|
|
// dumpAst(f, "verilog-ast> ");
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Condition for generate if is not constant!\n");
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
2013-12-04 21:06:54 +01:00
|
|
|
if (buf->asBool() != 0) {
|
2013-01-05 11:13:26 +01:00
|
|
|
buf = children[1]->clone();
|
|
|
|
|
} else {
|
2025-06-16 22:55:24 +02:00
|
|
|
buf = children.size() > 2 ? children[2]->clone() : nullptr;
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (buf)
|
|
|
|
|
{
|
|
|
|
|
if (buf->type != AST_GENBLOCK)
|
2025-06-16 22:55:24 +02:00
|
|
|
buf = std::make_unique<AstNode>(AST_GENBLOCK, std::move(buf));
|
2013-01-05 11:13:26 +01:00
|
|
|
|
|
|
|
|
if (!buf->str.empty()) {
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
buf->expand_genblock(buf->str + ".");
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-20 13:57:40 +01:00
|
|
|
for (size_t i = 0; i < buf->children.size(); i++) {
|
2023-04-04 22:59:44 +02:00
|
|
|
buf->children[i]->simplify(const_fold, stage, -1, false);
|
2025-06-16 22:55:24 +02:00
|
|
|
current_ast_mod->children.push_back(std::move(buf->children[i]));
|
2013-11-20 13:57:40 +01:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
|
|
|
|
|
buf->children.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete_children();
|
|
|
|
|
did_something = true;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-04 21:06:54 +01:00
|
|
|
// simplify generate-case blocks
|
|
|
|
|
if (type == AST_GENCASE && children.size() != 0)
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
auto buf = children[0]->clone();
|
2023-04-04 22:59:44 +02:00
|
|
|
while (buf->simplify(true, stage, width_hint, sign_hint)) { }
|
2013-12-04 21:06:54 +01:00
|
|
|
if (buf->type != AST_CONSTANT) {
|
|
|
|
|
// for (auto f : log_files)
|
|
|
|
|
// dumpAst(f, "verilog-ast> ");
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Condition for generate case is not constant!\n");
|
2013-12-04 21:06:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ref_signed = buf->is_signed;
|
|
|
|
|
RTLIL::Const ref_value = buf->bitsAsConst();
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
AstNode *selected_case = nullptr;
|
2013-12-04 21:06:54 +01:00
|
|
|
for (size_t i = 1; i < children.size(); i++)
|
|
|
|
|
{
|
2016-04-21 15:31:54 +02:00
|
|
|
log_assert(children.at(i)->type == AST_COND || children.at(i)->type == AST_CONDX || children.at(i)->type == AST_CONDZ);
|
2013-12-04 21:06:54 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
AstNode *this_genblock = nullptr;
|
|
|
|
|
for (auto& child : children.at(i)->children) {
|
|
|
|
|
log_assert(this_genblock == nullptr);
|
2013-12-04 21:06:54 +01:00
|
|
|
if (child->type == AST_GENBLOCK)
|
2025-06-16 22:55:24 +02:00
|
|
|
this_genblock = child.get();
|
2013-12-04 21:06:54 +01:00
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children.at(i)->children)
|
2013-12-04 21:06:54 +01:00
|
|
|
{
|
|
|
|
|
if (child->type == AST_DEFAULT) {
|
2025-06-16 22:55:24 +02:00
|
|
|
if (selected_case == nullptr)
|
2013-12-04 21:06:54 +01:00
|
|
|
selected_case = this_genblock;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (child->type == AST_GENBLOCK)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
buf = child->clone();
|
2023-04-05 11:00:07 +02:00
|
|
|
buf->set_in_param_flag(true);
|
2023-04-04 22:59:44 +02:00
|
|
|
while (buf->simplify(true, stage, width_hint, sign_hint)) { }
|
2013-12-04 21:06:54 +01:00
|
|
|
if (buf->type != AST_CONSTANT) {
|
|
|
|
|
// for (auto f : log_files)
|
|
|
|
|
// dumpAst(f, "verilog-ast> ");
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Expression in generate case is not constant!\n");
|
2013-12-04 21:06:54 +01:00
|
|
|
}
|
|
|
|
|
|
2014-07-25 13:07:31 +02:00
|
|
|
bool is_selected = RTLIL::const_eq(ref_value, buf->bitsAsConst(), ref_signed && buf->is_signed, ref_signed && buf->is_signed, 1).as_bool();
|
|
|
|
|
|
|
|
|
|
if (is_selected) {
|
2013-12-04 21:06:54 +01:00
|
|
|
selected_case = this_genblock;
|
|
|
|
|
i = children.size();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
if (selected_case != nullptr)
|
2013-12-04 21:06:54 +01:00
|
|
|
{
|
|
|
|
|
log_assert(selected_case->type == AST_GENBLOCK);
|
|
|
|
|
buf = selected_case->clone();
|
|
|
|
|
|
|
|
|
|
if (!buf->str.empty()) {
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
buf->expand_genblock(buf->str + ".");
|
2013-12-04 21:06:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < buf->children.size(); i++) {
|
2023-04-04 22:59:44 +02:00
|
|
|
buf->children[i]->simplify(const_fold, stage, -1, false);
|
2025-06-16 22:55:24 +02:00
|
|
|
current_ast_mod->children.push_back(std::move(buf->children[i]));
|
2013-12-04 21:06:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buf->children.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete_children();
|
|
|
|
|
did_something = true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-07 11:48:50 +02:00
|
|
|
// unroll cell arrays
|
|
|
|
|
if (type == AST_CELLARRAY)
|
|
|
|
|
{
|
|
|
|
|
if (!children.at(0)->range_valid)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Non-constant array range on cell array.\n");
|
2014-06-07 11:48:50 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_GENBLOCK);
|
2015-10-25 19:30:49 +01:00
|
|
|
int num = max(children.at(0)->range_left, children.at(0)->range_right) - min(children.at(0)->range_left, children.at(0)->range_right) + 1;
|
2014-06-07 11:48:50 +02:00
|
|
|
|
|
|
|
|
for (int i = 0; i < num; i++) {
|
|
|
|
|
int idx = children.at(0)->range_left > children.at(0)->range_right ? children.at(0)->range_right + i : children.at(0)->range_right - i;
|
2025-06-16 22:55:24 +02:00
|
|
|
auto new_cell_owned = children.at(1)->clone();
|
|
|
|
|
auto* new_cell = new_cell_owned.get();
|
|
|
|
|
newNode->children.push_back(std::move(new_cell_owned));
|
2014-06-07 11:48:50 +02:00
|
|
|
new_cell->str += stringf("[%d]", idx);
|
|
|
|
|
if (new_cell->type == AST_PRIMITIVE) {
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Cell arrays of primitives are currently not supported.\n");
|
2014-06-07 11:48:50 +02:00
|
|
|
} else {
|
2025-03-25 00:15:54 +01:00
|
|
|
this->dumpAst(NULL, " ");
|
2014-06-07 11:48:50 +02:00
|
|
|
log_assert(new_cell->children.at(0)->type == AST_CELLTYPE);
|
|
|
|
|
new_cell->children.at(0)->str = stringf("$array:%d:%d:%s", i, num, new_cell->children.at(0)->str.c_str());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-14 10:56:05 +02:00
|
|
|
// replace primitives with assignments
|
2013-01-05 11:13:26 +01:00
|
|
|
if (type == AST_PRIMITIVE)
|
|
|
|
|
{
|
|
|
|
|
if (children.size() < 2)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Insufficient number of arguments for primitive `%s'!\n", str.c_str());
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
std::vector<std::unique_ptr<AstNode>> children_list;
|
|
|
|
|
for (auto& child : children) {
|
2014-07-28 11:08:55 +02:00
|
|
|
log_assert(child->type == AST_ARGUMENT);
|
|
|
|
|
log_assert(child->children.size() == 1);
|
2025-06-16 22:55:24 +02:00
|
|
|
children_list.push_back(std::move(child->children[0]));
|
2013-01-05 11:13:26 +01:00
|
|
|
child->children.clear();
|
|
|
|
|
}
|
|
|
|
|
children.clear();
|
|
|
|
|
|
2013-08-20 11:23:59 +02:00
|
|
|
if (str == "bufif0" || str == "bufif1" || str == "notif0" || str == "notif1")
|
2013-08-19 19:50:04 +02:00
|
|
|
{
|
|
|
|
|
if (children_list.size() != 3)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Invalid number of arguments for primitive `%s'!\n", str.c_str());
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2013-08-19 19:50:04 +02:00
|
|
|
std::vector<RTLIL::State> z_const(1, RTLIL::State::Sz);
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto& mux_input = children_list.at(1);
|
2013-08-20 11:23:59 +02:00
|
|
|
if (str == "notif0" || str == "notif1") {
|
2025-06-16 22:55:24 +02:00
|
|
|
mux_input = std::make_unique<AstNode>(AST_BIT_NOT, std::move(mux_input));
|
2013-08-20 11:23:59 +02:00
|
|
|
}
|
2025-06-16 22:55:24 +02:00
|
|
|
auto node = std::make_unique<AstNode>(AST_TERNARY, std::move(children_list.at(2)));
|
2013-08-19 19:50:04 +02:00
|
|
|
if (str == "bufif0") {
|
|
|
|
|
node->children.push_back(AstNode::mkconst_bits(z_const, false));
|
2025-06-16 22:55:24 +02:00
|
|
|
node->children.push_back(std::move(mux_input));
|
2013-08-19 19:50:04 +02:00
|
|
|
} else {
|
2025-06-16 22:55:24 +02:00
|
|
|
node->children.push_back(std::move(mux_input));
|
2013-08-19 19:50:04 +02:00
|
|
|
node->children.push_back(AstNode::mkconst_bits(z_const, false));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
str.clear();
|
|
|
|
|
type = AST_ASSIGN;
|
2025-06-16 22:55:24 +02:00
|
|
|
children.push_back(std::move(children_list.at(0)));
|
2018-09-18 00:23:40 +02:00
|
|
|
children.back()->was_checked = true;
|
2025-06-16 22:55:24 +02:00
|
|
|
children.push_back(std::move(node));
|
2023-04-05 11:00:07 +02:00
|
|
|
fixup_hierarchy_flags();
|
2013-08-19 19:50:04 +02:00
|
|
|
did_something = true;
|
|
|
|
|
}
|
2021-03-17 00:18:36 +01:00
|
|
|
else if (str == "buf" || str == "not")
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
auto& input = children_list.back();
|
2021-03-17 00:18:36 +01:00
|
|
|
if (str == "not")
|
2025-06-16 22:55:24 +02:00
|
|
|
input = std::make_unique<AstNode>(AST_BIT_NOT, std::move(input));
|
2021-03-17 00:18:36 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_GENBLOCK);
|
2021-03-17 00:18:36 +01:00
|
|
|
for (auto it = children_list.begin(); it != std::prev(children_list.end()); it++) {
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode->children.push_back(std::make_unique<AstNode>(AST_ASSIGN, std::move(*it), input->clone()));
|
2021-03-17 00:18:36 +01:00
|
|
|
newNode->children.back()->was_checked = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
did_something = true;
|
|
|
|
|
}
|
2013-08-19 19:50:04 +02:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
AstNodeType op_type = AST_NONE;
|
|
|
|
|
bool invert_results = false;
|
|
|
|
|
|
|
|
|
|
if (str == "and")
|
|
|
|
|
op_type = AST_BIT_AND;
|
|
|
|
|
if (str == "nand")
|
|
|
|
|
op_type = AST_BIT_AND, invert_results = true;
|
|
|
|
|
if (str == "or")
|
|
|
|
|
op_type = AST_BIT_OR;
|
|
|
|
|
if (str == "nor")
|
|
|
|
|
op_type = AST_BIT_OR, invert_results = true;
|
|
|
|
|
if (str == "xor")
|
|
|
|
|
op_type = AST_BIT_XOR;
|
|
|
|
|
if (str == "xnor")
|
|
|
|
|
op_type = AST_BIT_XOR, invert_results = true;
|
2014-07-28 11:08:55 +02:00
|
|
|
log_assert(op_type != AST_NONE);
|
2013-08-19 19:50:04 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto& node = children_list[1];
|
2013-08-19 19:50:04 +02:00
|
|
|
if (op_type != AST_POS)
|
2020-05-04 19:22:05 +02:00
|
|
|
for (size_t i = 2; i < children_list.size(); i++) {
|
2025-06-16 22:55:24 +02:00
|
|
|
node = std::make_unique<AstNode>(op_type, std::move(node), std::move(children_list[i]));
|
2020-05-04 19:22:05 +02:00
|
|
|
node->location = location;
|
|
|
|
|
}
|
2013-08-19 19:50:04 +02:00
|
|
|
if (invert_results)
|
2025-06-16 22:55:24 +02:00
|
|
|
node = std::make_unique<AstNode>(AST_BIT_NOT, std::move(node));
|
2013-08-19 19:50:04 +02:00
|
|
|
|
|
|
|
|
str.clear();
|
|
|
|
|
type = AST_ASSIGN;
|
2025-06-16 22:55:24 +02:00
|
|
|
children.push_back(std::move(children_list[0]));
|
2018-09-18 00:23:40 +02:00
|
|
|
children.back()->was_checked = true;
|
2025-06-16 22:55:24 +02:00
|
|
|
children.push_back(std::move(node));
|
2023-04-05 11:00:07 +02:00
|
|
|
fixup_hierarchy_flags();
|
2013-08-19 19:50:04 +02:00
|
|
|
did_something = true;
|
|
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// replace dynamic ranges in left-hand side expressions (e.g. "foo[bar] <= 1'b1;") with
|
2020-04-01 12:43:42 +02:00
|
|
|
// either a big case block that selects the correct single-bit assignment, or mask and
|
|
|
|
|
// shift operations.
|
|
|
|
|
if (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE)
|
|
|
|
|
{
|
2013-01-05 11:13:26 +01:00
|
|
|
if (children[0]->type != AST_IDENTIFIER || children[0]->children.size() == 0)
|
|
|
|
|
goto skip_dynamic_range_lvalue_expansion;
|
|
|
|
|
if (children[0]->children[0]->range_valid || did_something)
|
|
|
|
|
goto skip_dynamic_range_lvalue_expansion;
|
2025-06-16 22:55:24 +02:00
|
|
|
if (children[0]->id2ast == nullptr || children[0]->id2ast->type != AST_WIRE)
|
2013-01-05 11:13:26 +01:00
|
|
|
goto skip_dynamic_range_lvalue_expansion;
|
|
|
|
|
if (!children[0]->id2ast->range_valid)
|
|
|
|
|
goto skip_dynamic_range_lvalue_expansion;
|
2020-04-01 12:43:42 +02:00
|
|
|
|
2024-01-25 07:28:15 +01:00
|
|
|
AST::AstNode *member_node = children[0]->get_struct_member();
|
2023-08-04 23:45:47 +02:00
|
|
|
int wire_width = member_node ?
|
|
|
|
|
member_node->range_left - member_node->range_right + 1 :
|
|
|
|
|
children[0]->id2ast->range_left - children[0]->id2ast->range_right + 1;
|
|
|
|
|
int wire_offset = children[0]->id2ast->range_right;
|
|
|
|
|
int result_width = 1;
|
2020-04-01 12:43:42 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> shift_expr = nullptr;
|
|
|
|
|
auto& range = children[0]->children[0];
|
2020-04-01 12:43:42 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
if (!try_determine_range_width(range.get(), result_width))
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
|
2023-04-04 11:34:17 +02:00
|
|
|
|
|
|
|
|
if (range->children.size() >= 2)
|
2013-01-05 11:13:26 +01:00
|
|
|
shift_expr = range->children[1]->clone();
|
2023-04-04 11:34:17 +02:00
|
|
|
else
|
|
|
|
|
shift_expr = range->children[0]->clone();
|
2020-04-01 12:43:42 +02:00
|
|
|
|
2023-08-04 23:45:47 +02:00
|
|
|
bool use_case_method = children[0]->id2ast->get_bool_attribute(ID::nowrshmsk);
|
2020-04-27 17:04:47 +02:00
|
|
|
|
2020-06-04 23:10:03 +02:00
|
|
|
if (!use_case_method && current_always->detect_latch(children[0]->str))
|
|
|
|
|
use_case_method = true;
|
|
|
|
|
|
2023-08-04 23:45:47 +02:00
|
|
|
if (use_case_method) {
|
2020-04-01 12:43:42 +02:00
|
|
|
// big case block
|
|
|
|
|
|
2023-08-04 23:45:47 +02:00
|
|
|
int stride = 1;
|
|
|
|
|
long long bitno_div = stride;
|
|
|
|
|
|
|
|
|
|
int case_width_hint;
|
|
|
|
|
bool case_sign_hint;
|
|
|
|
|
shift_expr->detectSignWidth(case_width_hint, case_sign_hint);
|
|
|
|
|
int max_width = case_width_hint;
|
|
|
|
|
|
|
|
|
|
if (member_node) { // Member in packed struct/union
|
|
|
|
|
// Clamp chunk to range of member within struct/union.
|
|
|
|
|
log_assert(!wire_offset && !children[0]->id2ast->range_swapped);
|
|
|
|
|
|
|
|
|
|
// When the (* nowrshmsk *) attribute is set, a CASE block is generated below
|
|
|
|
|
// to select the indexed bit slice. When a multirange array is indexed, the
|
|
|
|
|
// start of each possible slice is separated by the bit stride of the last
|
|
|
|
|
// index dimension, and we can optimize the CASE block accordingly.
|
|
|
|
|
// The dimension of the original array expression is saved in the 'integer' field.
|
|
|
|
|
int dims = children[0]->integer;
|
|
|
|
|
stride = wire_width;
|
|
|
|
|
for (int dim = 0; dim < dims; dim++) {
|
2024-01-25 07:28:15 +01:00
|
|
|
stride /= member_node->dimensions[dim].range_width;
|
2023-08-04 23:45:47 +02:00
|
|
|
}
|
|
|
|
|
bitno_div = stride;
|
|
|
|
|
} else {
|
|
|
|
|
// Extract (index)*(width) from non_opt_range pattern ((@selfsz@((index)*(width)))+(0)).
|
|
|
|
|
AstNode *lsb_expr =
|
|
|
|
|
shift_expr->type == AST_ADD && shift_expr->children[0]->type == AST_SELFSZ &&
|
|
|
|
|
shift_expr->children[1]->type == AST_CONSTANT && shift_expr->children[1]->integer == 0 ?
|
2025-06-16 22:55:24 +02:00
|
|
|
shift_expr->children[0]->children[0].get() :
|
|
|
|
|
shift_expr.get();
|
2023-08-04 23:45:47 +02:00
|
|
|
|
|
|
|
|
// Extract stride from indexing of two-dimensional packed arrays and
|
|
|
|
|
// variable slices on the form dst[i*stride +: width] = src.
|
|
|
|
|
if (lsb_expr->type == AST_MUL &&
|
2025-06-16 22:55:24 +02:00
|
|
|
(lsb_expr->children[0]->type == AST_CONSTANT ||
|
|
|
|
|
lsb_expr->children[1]->type == AST_CONSTANT))
|
2023-08-04 23:45:47 +02:00
|
|
|
{
|
|
|
|
|
int stride_ix = lsb_expr->children[1]->type == AST_CONSTANT;
|
|
|
|
|
stride = (int)lsb_expr->children[stride_ix]->integer;
|
|
|
|
|
bitno_div = stride != 0 ? stride : 1;
|
|
|
|
|
|
|
|
|
|
// Check whether i*stride can overflow.
|
|
|
|
|
int i_width;
|
|
|
|
|
bool i_sign;
|
|
|
|
|
lsb_expr->children[1 - stride_ix]->detectSignWidth(i_width, i_sign);
|
|
|
|
|
int stride_width;
|
|
|
|
|
bool stride_sign;
|
|
|
|
|
lsb_expr->children[stride_ix]->detectSignWidth(stride_width, stride_sign);
|
|
|
|
|
max_width = std::max(i_width, stride_width);
|
|
|
|
|
// Stride width calculated from actual stride value.
|
2025-01-31 00:15:53 +01:00
|
|
|
if (stride == 0)
|
|
|
|
|
stride_width = 0;
|
|
|
|
|
else
|
|
|
|
|
stride_width = std::ceil(std::log2(std::abs(stride)));
|
2023-08-04 23:45:47 +02:00
|
|
|
|
|
|
|
|
if (i_width + stride_width > max_width) {
|
|
|
|
|
// For (truncated) i*stride to be within the range of dst, the following must hold:
|
|
|
|
|
// i*stride ≡ bitno (mod shift_mod), i.e.
|
|
|
|
|
// i*stride = k*shift_mod + bitno
|
|
|
|
|
//
|
|
|
|
|
// The Diophantine equation on the form ax + by = c:
|
|
|
|
|
// stride*i - shift_mod*k = bitno
|
|
|
|
|
// has solutions iff c is a multiple of d = gcd(a, b), i.e.
|
|
|
|
|
// bitno mod gcd(stride, shift_mod) = 0
|
|
|
|
|
//
|
|
|
|
|
// long long is at least 64 bits in C++11
|
|
|
|
|
long long shift_mod = 1ll << (max_width - case_sign_hint);
|
|
|
|
|
// std::gcd requires C++17
|
|
|
|
|
// bitno_div = std::gcd(stride, shift_mod);
|
|
|
|
|
bitno_div = gcd((long long)stride, shift_mod);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// long long is at least 64 bits in C++11
|
|
|
|
|
long long max_offset = (1ll << (max_width - case_sign_hint)) - 1;
|
|
|
|
|
long long min_offset = case_sign_hint ? -(1ll << (max_width - 1)) : 0;
|
|
|
|
|
|
2023-12-08 20:47:43 +01:00
|
|
|
// A temporary register holds the result of the (possibly complex) rvalue expression,
|
|
|
|
|
// avoiding repetition in each AST_COND below.
|
|
|
|
|
int rvalue_width;
|
|
|
|
|
bool rvalue_sign;
|
|
|
|
|
children[1]->detectSignWidth(rvalue_width, rvalue_sign);
|
2025-06-16 22:55:24 +02:00
|
|
|
auto rvalue = mktemp_logic("$bitselwrite$rvalue$", current_ast_mod, true, rvalue_width - 1, 0, rvalue_sign);
|
|
|
|
|
auto* rvalue_leaky = rvalue.get();
|
|
|
|
|
log("make 1\n");
|
|
|
|
|
auto case_node_owned = std::make_unique<AstNode>(AST_CASE, std::move(shift_expr));
|
|
|
|
|
auto* case_node = case_node_owned.get();
|
|
|
|
|
newNode = std::make_unique<AstNode>(AST_BLOCK,
|
|
|
|
|
std::make_unique<AstNode>(AST_ASSIGN_EQ, std::move(rvalue), children[1]->clone()),
|
|
|
|
|
std::move(case_node_owned));
|
2023-12-08 20:47:43 +01:00
|
|
|
|
2020-04-01 12:43:42 +02:00
|
|
|
did_something = true;
|
2023-08-04 23:45:47 +02:00
|
|
|
for (int i = 1 - result_width; i < wire_width; i++) {
|
|
|
|
|
// Out of range indexes are handled in genrtlil.cc
|
|
|
|
|
int start_bit = wire_offset + i;
|
|
|
|
|
int end_bit = start_bit + result_width - 1;
|
|
|
|
|
// Check whether the current index can be generated by shift_expr.
|
|
|
|
|
if (start_bit < min_offset || start_bit > max_offset)
|
|
|
|
|
continue;
|
|
|
|
|
if (start_bit%bitno_div != 0 || (stride == 0 && start_bit != 0))
|
|
|
|
|
continue;
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto cond = std::make_unique<AstNode>(AST_COND, mkconst_int(start_bit, case_sign_hint, max_width));
|
|
|
|
|
auto lvalue = children[0]->clone();
|
2020-04-01 12:43:42 +02:00
|
|
|
lvalue->delete_children();
|
2023-02-28 18:45:55 +01:00
|
|
|
if (member_node)
|
2023-04-05 11:00:07 +02:00
|
|
|
lvalue->set_attribute(ID::wiretype, member_node->clone());
|
2025-06-16 22:55:24 +02:00
|
|
|
lvalue->children.push_back(std::make_unique<AstNode>(AST_RANGE,
|
2020-04-01 12:43:42 +02:00
|
|
|
mkconst_int(end_bit, true), mkconst_int(start_bit, true)));
|
2025-06-16 22:55:24 +02:00
|
|
|
cond->children.push_back(std::make_unique<AstNode>(AST_BLOCK, std::make_unique<AstNode>(std::move(type), std::move(lvalue), rvalue_leaky->clone())));
|
|
|
|
|
case_node->children.push_back(std::move(cond));
|
2020-04-01 12:43:42 +02:00
|
|
|
}
|
2023-08-04 23:45:47 +02:00
|
|
|
} else {
|
|
|
|
|
// mask and shift operations
|
2023-12-12 13:37:34 +01:00
|
|
|
// dst = (dst & ~(width'1 << lsb)) | unsigned'(width'(src)) << lsb)
|
2020-04-01 12:43:42 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto lvalue = children[0]->clone();
|
2013-01-05 11:13:26 +01:00
|
|
|
lvalue->delete_children();
|
2023-02-28 18:45:55 +01:00
|
|
|
if (member_node)
|
2023-04-05 11:00:07 +02:00
|
|
|
lvalue->set_attribute(ID::wiretype, member_node->clone());
|
2020-04-01 12:43:42 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto old_data = lvalue->clone();
|
2020-04-15 20:36:40 +02:00
|
|
|
if (type == AST_ASSIGN_LE)
|
|
|
|
|
old_data->lookahead = true;
|
|
|
|
|
|
2023-12-12 13:37:34 +01:00
|
|
|
int shift_width_hint;
|
|
|
|
|
bool shift_sign_hint;
|
|
|
|
|
shift_expr->detectSignWidth(shift_width_hint, shift_sign_hint);
|
2020-04-01 12:43:42 +02:00
|
|
|
|
2023-12-12 13:37:34 +01:00
|
|
|
// All operations are carried out in a new block.
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_BLOCK);
|
2023-12-12 13:37:34 +01:00
|
|
|
|
|
|
|
|
// Temporary register holding the result of the bit- or part-select position expression.
|
2025-06-16 22:55:24 +02:00
|
|
|
auto pos = mktemp_logic("$bitselwrite$pos$", current_ast_mod, true, shift_width_hint - 1, 0, shift_sign_hint);
|
2023-12-12 13:37:34 +01:00
|
|
|
// Calculate lsb from position.
|
2025-06-16 22:55:24 +02:00
|
|
|
auto shift_val = pos->clone();
|
|
|
|
|
|
|
|
|
|
newNode->children.push_back(std::make_unique<AstNode>(AST_ASSIGN_EQ, std::move(pos), std::move(shift_expr)));
|
2023-12-12 13:37:34 +01:00
|
|
|
|
|
|
|
|
// If the expression is signed, we must add an extra bit for possible negation of the most negative number.
|
|
|
|
|
// If the expression is unsigned, we must add an extra bit for sign.
|
2025-06-16 22:55:24 +02:00
|
|
|
shift_val = std::make_unique<AstNode>(AST_CAST_SIZE, mkconst_int(shift_width_hint + 1, true), std::move(shift_val));
|
2023-12-12 13:37:34 +01:00
|
|
|
if (!shift_sign_hint)
|
2025-06-16 22:55:24 +02:00
|
|
|
shift_val = std::make_unique<AstNode>(AST_TO_SIGNED, std::move(shift_val));
|
2022-01-18 07:18:12 +01:00
|
|
|
|
|
|
|
|
// offset the shift amount by the lower bound of the dimension
|
2023-12-12 13:37:34 +01:00
|
|
|
if (wire_offset != 0)
|
2025-06-16 22:55:24 +02:00
|
|
|
shift_val = std::make_unique<AstNode>(AST_SUB, std::move(shift_val), mkconst_int(wire_offset, true));
|
2020-04-27 17:04:47 +02:00
|
|
|
|
2022-01-18 07:18:12 +01:00
|
|
|
// reflect the shift amount if the dimension is swapped
|
|
|
|
|
if (children[0]->id2ast->range_swapped)
|
2025-06-16 22:55:24 +02:00
|
|
|
shift_val = std::make_unique<AstNode>(AST_SUB, mkconst_int(wire_width - result_width, true), std::move(shift_val));
|
2022-01-18 07:18:12 +01:00
|
|
|
|
|
|
|
|
// AST_SHIFT uses negative amounts for shifting left
|
2025-06-16 22:55:24 +02:00
|
|
|
shift_val = std::make_unique<AstNode>(AST_NEG, std::move(shift_val));
|
|
|
|
|
auto also_shift_val = shift_val->clone();
|
2020-04-27 17:04:47 +02:00
|
|
|
|
2023-12-12 13:37:34 +01:00
|
|
|
// dst = (dst & ~(width'1 << lsb)) | unsigned'(width'(src)) << lsb)
|
|
|
|
|
did_something = true;
|
2025-06-16 22:55:24 +02:00
|
|
|
auto bitmask = mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false);
|
2023-12-12 13:37:34 +01:00
|
|
|
newNode->children.push_back(
|
2025-06-16 22:55:24 +02:00
|
|
|
std::make_unique<AstNode>(std::move(type),
|
|
|
|
|
std::move(lvalue),
|
|
|
|
|
std::make_unique<AstNode>(AST_BIT_OR,
|
|
|
|
|
std::make_unique<AstNode>(AST_BIT_AND,
|
|
|
|
|
std::move(old_data),
|
|
|
|
|
std::make_unique<AstNode>(AST_BIT_NOT,
|
|
|
|
|
std::make_unique<AstNode>(AST_SHIFT,
|
|
|
|
|
std::move(bitmask),
|
|
|
|
|
std::move(shift_val)))),
|
|
|
|
|
std::make_unique<AstNode>(AST_SHIFT,
|
|
|
|
|
std::make_unique<AstNode>(AST_TO_UNSIGNED,
|
|
|
|
|
std::make_unique<AstNode>(AST_CAST_SIZE,
|
|
|
|
|
mkconst_int(result_width, true),
|
|
|
|
|
children[1]->clone())),
|
|
|
|
|
std::move(also_shift_val)))));
|
2023-04-05 11:00:07 +02:00
|
|
|
|
|
|
|
|
newNode->fixup_hierarchy_flags(true);
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
2020-04-01 12:43:42 +02:00
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
|
|
|
|
skip_dynamic_range_lvalue_expansion:;
|
|
|
|
|
|
|
|
|
|
// found right-hand side identifier for memory -> replace with memory read port
|
2025-06-16 22:55:24 +02:00
|
|
|
if (stage > 1 && type == AST_IDENTIFIER && id2ast != nullptr && id2ast->type == AST_MEMORY && !in_lvalue &&
|
2015-08-22 14:46:42 +02:00
|
|
|
children.size() == 1 && children[0]->type == AST_RANGE && children[0]->children.size() == 1) {
|
2024-01-25 07:28:15 +01:00
|
|
|
if (integer < (unsigned)id2ast->unpacked_dimensions)
|
|
|
|
|
input_error("Insufficient number of array indices for %s.\n", log_id(str));
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_MEMRD, children[0]->children[0]->clone());
|
2013-01-05 11:13:26 +01:00
|
|
|
newNode->str = str;
|
2013-11-04 06:04:42 +01:00
|
|
|
newNode->id2ast = id2ast;
|
2013-01-05 11:13:26 +01:00
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-08 14:31:06 +02:00
|
|
|
// assignment with nontrivial member in left-hand concat expression -> split assignment
|
|
|
|
|
if ((type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) && children[0]->type == AST_CONCAT && width_hint > 0)
|
|
|
|
|
{
|
|
|
|
|
bool found_nontrivial_member = false;
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children[0]->children) {
|
|
|
|
|
if (child->type == AST_IDENTIFIER && child->id2ast != nullptr && child->id2ast->type == AST_MEMORY)
|
2016-07-08 14:31:06 +02:00
|
|
|
found_nontrivial_member = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (found_nontrivial_member)
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_BLOCK);
|
2016-07-08 14:31:06 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto wire_tmp_owned = std::make_unique<AstNode>(AST_WIRE, std::make_unique<AstNode>(AST_RANGE, mkconst_int(width_hint-1, true), mkconst_int(0, true)));
|
|
|
|
|
auto wire_tmp = wire_tmp_owned.get();
|
2022-08-08 16:13:33 +02:00
|
|
|
wire_tmp->str = stringf("$splitcmplxassign$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
|
2016-07-08 14:31:06 +02:00
|
|
|
current_scope[wire_tmp->str] = wire_tmp;
|
2025-06-16 22:55:24 +02:00
|
|
|
current_ast_mod->children.push_back(std::move(wire_tmp_owned));
|
2023-04-05 11:00:07 +02:00
|
|
|
wire_tmp->set_attribute(ID::nosync, AstNode::mkconst_int(1, false));
|
2023-04-04 22:59:44 +02:00
|
|
|
while (wire_tmp->simplify(true, 1, -1, false)) { }
|
2019-05-01 10:01:54 +02:00
|
|
|
wire_tmp->is_logic = true;
|
2016-07-08 14:31:06 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto wire_tmp_id_owned = std::make_unique<AstNode>(AST_IDENTIFIER);
|
|
|
|
|
auto* wire_tmp_id = wire_tmp_id_owned.get();
|
2016-07-08 14:31:06 +02:00
|
|
|
wire_tmp_id->str = wire_tmp->str;
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode->children.push_back(std::make_unique<AstNode>(AST_ASSIGN_EQ, std::move(wire_tmp_id_owned), children[1]->clone()));
|
2018-09-18 00:23:40 +02:00
|
|
|
newNode->children.back()->was_checked = true;
|
2016-07-08 14:31:06 +02:00
|
|
|
|
|
|
|
|
int cursor = 0;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children[0]->children)
|
2016-07-08 14:31:06 +02:00
|
|
|
{
|
|
|
|
|
int child_width_hint = -1;
|
|
|
|
|
bool child_sign_hint = true;
|
|
|
|
|
child->detectSignWidth(child_width_hint, child_sign_hint);
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto rhs = wire_tmp_id->clone();
|
|
|
|
|
rhs->children.push_back(std::make_unique<AstNode>(AST_RANGE, AstNode::mkconst_int(cursor+child_width_hint-1, true), AstNode::mkconst_int(cursor, true)));
|
|
|
|
|
newNode->children.push_back(std::make_unique<AstNode>(type, child->clone(), std::move(rhs)));
|
2016-07-08 14:31:06 +02:00
|
|
|
|
|
|
|
|
cursor += child_width_hint;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
// assignment with memory in left-hand side expression -> replace with memory write port
|
|
|
|
|
if (stage > 1 && (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) && children[0]->type == AST_IDENTIFIER &&
|
2014-07-17 13:13:21 +02:00
|
|
|
children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY && children[0]->id2ast->children.size() >= 2 &&
|
|
|
|
|
children[0]->id2ast->children[0]->range_valid && children[0]->id2ast->children[1]->range_valid &&
|
2014-08-06 15:43:46 +02:00
|
|
|
(children[0]->children.size() == 1 || children[0]->children.size() == 2) && children[0]->children[0]->type == AST_RANGE)
|
2013-01-05 11:13:26 +01:00
|
|
|
{
|
2024-01-25 07:28:15 +01:00
|
|
|
if (children[0]->integer < (unsigned)children[0]->id2ast->unpacked_dimensions)
|
|
|
|
|
input_error("Insufficient number of array indices for %s.\n", log_id(str));
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
std::stringstream sstr;
|
2022-08-08 16:13:33 +02:00
|
|
|
sstr << "$memwr$" << children[0]->str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++);
|
2013-01-05 11:13:26 +01:00
|
|
|
std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA", id_en = sstr.str() + "_EN";
|
|
|
|
|
|
|
|
|
|
int mem_width, mem_size, addr_bits;
|
2016-11-01 23:17:43 +01:00
|
|
|
bool mem_signed = children[0]->id2ast->is_signed;
|
2013-01-05 11:13:26 +01:00
|
|
|
children[0]->id2ast->meminfo(mem_width, mem_size, addr_bits);
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_BLOCK);
|
|
|
|
|
auto defNode = std::make_unique<AstNode>(AST_BLOCK);
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
|
2016-08-22 14:27:46 +02:00
|
|
|
int data_range_left = children[0]->id2ast->children[0]->range_left;
|
|
|
|
|
int data_range_right = children[0]->id2ast->children[0]->range_right;
|
|
|
|
|
int mem_data_range_offset = std::min(data_range_left, data_range_right);
|
|
|
|
|
|
2016-08-19 18:38:25 +02:00
|
|
|
int addr_width_hint = -1;
|
|
|
|
|
bool addr_sign_hint = true;
|
|
|
|
|
children[0]->children[0]->children[0]->detectSignWidthWorker(addr_width_hint, addr_sign_hint);
|
|
|
|
|
addr_bits = std::max(addr_bits, addr_width_hint);
|
|
|
|
|
|
2014-07-16 12:23:47 +02:00
|
|
|
std::vector<RTLIL::State> x_bits_addr, x_bits_data, set_bits_en;
|
2014-02-22 17:08:00 +01:00
|
|
|
for (int i = 0; i < addr_bits; i++)
|
|
|
|
|
x_bits_addr.push_back(RTLIL::State::Sx);
|
2013-11-18 19:55:12 +01:00
|
|
|
for (int i = 0; i < mem_width; i++)
|
2014-02-22 17:08:00 +01:00
|
|
|
x_bits_data.push_back(RTLIL::State::Sx);
|
2014-07-16 12:23:47 +02:00
|
|
|
for (int i = 0; i < mem_width; i++)
|
|
|
|
|
set_bits_en.push_back(RTLIL::State::S1);
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> node_addr = nullptr;
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
if (children[0]->children[0]->children[0]->isConst()) {
|
|
|
|
|
node_addr = children[0]->children[0]->children[0]->clone();
|
|
|
|
|
} else {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto wire_addr_owned = std::make_unique<AstNode>(AST_WIRE, std::make_unique<AstNode>(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true)));
|
|
|
|
|
auto* wire_addr = wire_addr_owned.get();
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
wire_addr->str = id_addr;
|
|
|
|
|
wire_addr->was_checked = true;
|
2025-06-16 22:55:24 +02:00
|
|
|
current_ast_mod->children.push_back(std::move(wire_addr_owned));
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
current_scope[wire_addr->str] = wire_addr;
|
2023-04-04 22:59:44 +02:00
|
|
|
while (wire_addr->simplify(true, 1, -1, false)) { }
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto assign_addr = std::make_unique<AstNode>(AST_ASSIGN_EQ, std::make_unique<AstNode>(AST_IDENTIFIER), mkconst_bits(x_bits_addr, false));
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
assign_addr->children[0]->str = id_addr;
|
|
|
|
|
assign_addr->children[0]->was_checked = true;
|
2025-06-16 22:55:24 +02:00
|
|
|
defNode->children.push_back(std::move(assign_addr));
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
assign_addr = std::make_unique<AstNode>(AST_ASSIGN_EQ, std::make_unique<AstNode>(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone());
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
assign_addr->children[0]->str = id_addr;
|
|
|
|
|
assign_addr->children[0]->was_checked = true;
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode->children.push_back(std::move(assign_addr));
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
node_addr = std::make_unique<AstNode>(AST_IDENTIFIER);
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
node_addr->str = id_addr;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> node_data = nullptr;
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
if (children[0]->children.size() == 1 && children[1]->isConst()) {
|
|
|
|
|
node_data = children[1]->clone();
|
|
|
|
|
} else {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto wire_data_owned = std::make_unique<AstNode>(AST_WIRE, std::make_unique<AstNode>(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
|
|
|
|
|
auto* wire_data = wire_data_owned.get();
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
wire_data->str = id_data;
|
|
|
|
|
wire_data->was_checked = true;
|
|
|
|
|
wire_data->is_signed = mem_signed;
|
|
|
|
|
current_scope[wire_data->str] = wire_data;
|
2025-06-16 22:55:24 +02:00
|
|
|
current_ast_mod->children.push_back(std::move(wire_data_owned));
|
2023-04-04 22:59:44 +02:00
|
|
|
while (wire_data->simplify(true, 1, -1, false)) { }
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto assign_data = std::make_unique<AstNode>(AST_ASSIGN_EQ, std::make_unique<AstNode>(AST_IDENTIFIER), mkconst_bits(x_bits_data, false));
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
assign_data->children[0]->str = id_data;
|
|
|
|
|
assign_data->children[0]->was_checked = true;
|
2025-06-16 22:55:24 +02:00
|
|
|
defNode->children.push_back(std::move(assign_data));
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
node_data = std::make_unique<AstNode>(AST_IDENTIFIER);
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
node_data->str = id_data;
|
|
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto wire_en_owned = std::make_unique<AstNode>(AST_WIRE, std::make_unique<AstNode>(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
|
|
|
|
|
auto* wire_en = wire_en_owned.get();
|
2021-05-21 02:27:06 +02:00
|
|
|
wire_en->str = id_en;
|
|
|
|
|
wire_en->was_checked = true;
|
|
|
|
|
current_scope[wire_en->str] = wire_en;
|
2025-06-16 22:55:24 +02:00
|
|
|
current_ast_mod->children.push_back(std::move(wire_en_owned));
|
2023-04-04 22:59:44 +02:00
|
|
|
while (wire_en->simplify(true, 1, -1, false)) { }
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto assign_en_first = std::make_unique<AstNode>(AST_ASSIGN_EQ, std::make_unique<AstNode>(AST_IDENTIFIER), mkconst_int(0, false, mem_width));
|
2021-05-21 02:27:06 +02:00
|
|
|
assign_en_first->children[0]->str = id_en;
|
|
|
|
|
assign_en_first->children[0]->was_checked = true;
|
2025-06-16 22:55:24 +02:00
|
|
|
defNode->children.push_back(std::move(assign_en_first));
|
2021-05-21 02:27:06 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto node_en = std::make_unique<AstNode>(AST_IDENTIFIER);
|
2021-05-21 02:27:06 +02:00
|
|
|
node_en->str = id_en;
|
2013-01-05 11:13:26 +01:00
|
|
|
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
if (!defNode->children.empty())
|
2025-06-16 22:55:24 +02:00
|
|
|
current_top_block->children.insert(current_top_block->children.begin(), std::move(defNode));
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> assign_data = nullptr;
|
|
|
|
|
std::unique_ptr<AstNode> assign_en = nullptr;
|
2014-07-17 13:13:21 +02:00
|
|
|
if (children[0]->children.size() == 2)
|
|
|
|
|
{
|
|
|
|
|
if (children[0]->children[1]->range_valid)
|
|
|
|
|
{
|
|
|
|
|
int offset = children[0]->children[1]->range_right;
|
|
|
|
|
int width = children[0]->children[1]->range_left - offset + 1;
|
2016-08-22 14:27:46 +02:00
|
|
|
offset -= mem_data_range_offset;
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2014-07-17 13:13:21 +02:00
|
|
|
std::vector<RTLIL::State> padding_x(offset, RTLIL::State::Sx);
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
assign_data = std::make_unique<AstNode>(AST_ASSIGN_EQ, std::make_unique<AstNode>(AST_IDENTIFIER),
|
|
|
|
|
std::make_unique<AstNode>(AST_CONCAT, mkconst_bits(padding_x, false), children[1]->clone()));
|
2014-07-17 13:13:21 +02:00
|
|
|
assign_data->children[0]->str = id_data;
|
2018-06-05 16:44:24 +02:00
|
|
|
assign_data->children[0]->was_checked = true;
|
2014-07-17 13:13:21 +02:00
|
|
|
|
2021-05-21 02:27:06 +02:00
|
|
|
for (int i = 0; i < mem_width; i++)
|
|
|
|
|
set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0;
|
2025-06-16 22:55:24 +02:00
|
|
|
assign_en = std::make_unique<AstNode>(AST_ASSIGN_EQ, std::make_unique<AstNode>(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
|
2021-05-21 02:27:06 +02:00
|
|
|
assign_en->children[0]->str = id_en;
|
|
|
|
|
assign_en->children[0]->was_checked = true;
|
2014-07-17 13:13:21 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode>& the_range = children[0]->children[1];
|
|
|
|
|
std::unique_ptr<AstNode> offset_ast;
|
2023-04-04 11:34:17 +02:00
|
|
|
int width;
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
if (!try_determine_range_width(the_range.get(), width))
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
|
2023-04-04 11:34:17 +02:00
|
|
|
|
|
|
|
|
if (the_range->children.size() >= 2)
|
|
|
|
|
offset_ast = the_range->children[1]->clone();
|
|
|
|
|
else
|
|
|
|
|
offset_ast = the_range->children[0]->clone();
|
2014-07-17 16:49:23 +02:00
|
|
|
|
2016-08-22 14:27:46 +02:00
|
|
|
if (mem_data_range_offset)
|
2025-06-16 22:55:24 +02:00
|
|
|
offset_ast = std::make_unique<AstNode>(AST_SUB, std::move(offset_ast), mkconst_int(mem_data_range_offset, true));
|
2016-08-22 14:27:46 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
assign_data = std::make_unique<AstNode>(AST_ASSIGN_EQ, std::make_unique<AstNode>(AST_IDENTIFIER),
|
|
|
|
|
std::make_unique<AstNode>(AST_SHIFT_LEFT, children[1]->clone(), offset_ast->clone()));
|
2014-07-17 16:49:23 +02:00
|
|
|
assign_data->children[0]->str = id_data;
|
2018-06-05 16:44:24 +02:00
|
|
|
assign_data->children[0]->was_checked = true;
|
2014-07-17 16:49:23 +02:00
|
|
|
|
2021-05-21 02:27:06 +02:00
|
|
|
for (int i = 0; i < mem_width; i++)
|
|
|
|
|
set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0;
|
2025-06-16 22:55:24 +02:00
|
|
|
assign_en = std::make_unique<AstNode>(AST_ASSIGN_EQ, std::make_unique<AstNode>(AST_IDENTIFIER),
|
|
|
|
|
std::make_unique<AstNode>(AST_SHIFT_LEFT, mkconst_bits(set_bits_en, false), offset_ast->clone()));
|
2021-05-21 02:27:06 +02:00
|
|
|
assign_en->children[0]->str = id_en;
|
|
|
|
|
assign_en->children[0]->was_checked = true;
|
2014-07-17 13:13:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
if (!(children[0]->children.size() == 1 && children[1]->isConst())) {
|
2025-06-16 22:55:24 +02:00
|
|
|
assign_data = std::make_unique<AstNode>(AST_ASSIGN_EQ, std::make_unique<AstNode>(AST_IDENTIFIER), children[1]->clone());
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
assign_data->children[0]->str = id_data;
|
|
|
|
|
assign_data->children[0]->was_checked = true;
|
|
|
|
|
}
|
2014-07-17 13:13:21 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
assign_en = std::make_unique<AstNode>(AST_ASSIGN_EQ, std::make_unique<AstNode>(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
|
2021-05-21 02:27:06 +02:00
|
|
|
assign_en->children[0]->str = id_en;
|
|
|
|
|
assign_en->children[0]->was_checked = true;
|
2014-07-17 13:13:21 +02:00
|
|
|
}
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
if (assign_data)
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode->children.push_back(std::move(assign_data));
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
if (assign_en)
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode->children.push_back(std::move(assign_en));
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> wrnode;
|
2021-05-21 02:27:06 +02:00
|
|
|
if (current_always->type == AST_INITIAL)
|
2025-06-16 22:55:24 +02:00
|
|
|
wrnode = std::make_unique<AstNode>(AST_MEMINIT, std::move(node_addr), std::move(node_data), std::move(node_en), mkconst_int(1, false));
|
2021-05-21 02:27:06 +02:00
|
|
|
else
|
2025-06-16 22:55:24 +02:00
|
|
|
wrnode = std::make_unique<AstNode>(AST_MEMWR, std::move(node_addr), std::move(node_data), std::move(node_en));
|
2013-01-05 11:13:26 +01:00
|
|
|
wrnode->str = children[0]->str;
|
2015-02-14 14:21:15 +01:00
|
|
|
wrnode->id2ast = children[0]->id2ast;
|
2021-02-23 16:48:29 +01:00
|
|
|
wrnode->location = location;
|
|
|
|
|
if (wrnode->type == AST_MEMWR) {
|
|
|
|
|
int portid = current_memwr_count[wrnode->str]++;
|
|
|
|
|
wrnode->children.push_back(mkconst_int(portid, false));
|
|
|
|
|
std::vector<RTLIL::State> priority_mask;
|
|
|
|
|
for (int i = 0; i < portid; i++) {
|
|
|
|
|
bool has_prio = current_memwr_visible[wrnode->str].count(i);
|
|
|
|
|
priority_mask.push_back(State(has_prio));
|
|
|
|
|
}
|
|
|
|
|
wrnode->children.push_back(mkconst_bits(priority_mask, false));
|
|
|
|
|
current_memwr_visible[wrnode->str].insert(portid);
|
2025-06-16 22:55:24 +02:00
|
|
|
current_always->children.push_back(std::move(wrnode));
|
2021-02-23 16:48:29 +01:00
|
|
|
} else {
|
2025-06-16 22:55:24 +02:00
|
|
|
current_ast_mod->children.push_back(std::move(wrnode));
|
2021-02-23 16:48:29 +01:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
if (newNode->children.empty()) {
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>();
|
ast: avoid intermediate wires/assigns when lowering to AST_MEMINIT.
Before this commit, every initial assignment to a memory generated
two wires and four assigns in a process. For unknown reasons (I did
not investigate), large amounts of assigns cause quadratic slowdown
later in the AST frontend, in processAst/removeSignalFromCaseTree.
As a consequence, common and reasonable Verilog code, such as:
reg [`WIDTH:0] mem [0:`DEPTH];
integer i; initial for (i = 0; i <= `DEPTH; i++) mem[i] = 0;
took extremely long time to be processed; around 80 s for a 8-wide,
8192-deep memory.
After this commit, initial assignments where address and/or data are
constant (after `generate`) do not incur the cost of intermediate
wires; expressions like `mem[i+1]=i^(i<<1)` are considered constant.
This results in speedups of orders of magnitude for common memory
sizes; it now takes merely 0.4 s to process a 8-wide, 8192-deep
memory, and only 5.8 s to process a 8-wide, 131072-deep one.
As a bonus, this change also results in nontrivial speedups later
in the synthesis pipeline, since pass sequencing issues meant that
all of these intermediate wires were subject to transformations such
as width reduction, even though they existed solely to be constant
folded away in `memory_collect`.
2020-01-01 21:18:28 +01:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// replace function and task calls with the code from the function or task
|
|
|
|
|
if ((type == AST_FCALL || type == AST_TCALL) && !str.empty())
|
|
|
|
|
{
|
2013-12-04 21:19:54 +01:00
|
|
|
if (type == AST_FCALL)
|
|
|
|
|
{
|
2016-07-21 14:23:22 +02:00
|
|
|
if (str == "\\$initstate")
|
|
|
|
|
{
|
|
|
|
|
int myidx = autoidx++;
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto wire_owned = std::make_unique<AstNode>(AST_WIRE);
|
|
|
|
|
auto* wire = wire_owned.get();
|
|
|
|
|
current_ast_mod->children.push_back(std::move(wire_owned));
|
2016-07-21 14:23:22 +02:00
|
|
|
wire->str = stringf("$initstate$%d_wire", myidx);
|
2023-04-04 22:59:44 +02:00
|
|
|
while (wire->simplify(true, 1, -1, false)) { }
|
2016-07-21 14:23:22 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto cell = std::make_unique<AstNode>(AST_CELL, std::make_unique<AstNode>(AST_CELLTYPE), std::make_unique<AstNode>(AST_ARGUMENT, std::make_unique<AstNode>(AST_IDENTIFIER)));
|
2016-07-21 14:23:22 +02:00
|
|
|
cell->str = stringf("$initstate$%d", myidx);
|
|
|
|
|
cell->children[0]->str = "$initstate";
|
|
|
|
|
cell->children[1]->str = "\\Y";
|
|
|
|
|
cell->children[1]->children[0]->str = wire->str;
|
|
|
|
|
cell->children[1]->children[0]->id2ast = wire;
|
2025-06-16 22:55:24 +02:00
|
|
|
current_ast_mod->children.push_back(std::move(cell));
|
2023-04-04 22:59:44 +02:00
|
|
|
while (cell->simplify(true, 1, -1, false)) { }
|
2016-07-21 14:23:22 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_IDENTIFIER);
|
2016-07-21 14:23:22 +02:00
|
|
|
newNode->str = wire->str;
|
|
|
|
|
newNode->id2ast = wire;
|
|
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-19 01:30:07 +02:00
|
|
|
if (str == "\\$past")
|
|
|
|
|
{
|
2019-02-21 17:55:33 +01:00
|
|
|
if (width_hint < 0)
|
2016-09-19 01:30:07 +02:00
|
|
|
goto replace_fcall_later;
|
|
|
|
|
|
|
|
|
|
int num_steps = 1;
|
|
|
|
|
|
|
|
|
|
if (GetSize(children) != 1 && GetSize(children) != 2)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("System function %s got %d arguments, expected 1 or 2.\n",
|
2018-11-04 10:19:32 +01:00
|
|
|
RTLIL::unescape_id(str).c_str(), int(children.size()));
|
2016-09-19 01:30:07 +02:00
|
|
|
|
|
|
|
|
if (!current_always_clocked)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("System function %s is only allowed in clocked blocks.\n",
|
2018-11-04 10:19:32 +01:00
|
|
|
RTLIL::unescape_id(str).c_str());
|
2016-09-19 01:30:07 +02:00
|
|
|
|
|
|
|
|
if (GetSize(children) == 2)
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
auto buf = children[1]->clone();
|
2023-04-04 22:59:44 +02:00
|
|
|
while (buf->simplify(true, stage, -1, false)) { }
|
2016-09-19 01:30:07 +02:00
|
|
|
if (buf->type != AST_CONSTANT)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Failed to evaluate system function `%s' with non-constant value.\n", str.c_str());
|
2016-09-19 01:30:07 +02:00
|
|
|
|
|
|
|
|
num_steps = buf->asInt(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AstNode *block = nullptr;
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : current_always->children)
|
2016-09-19 01:30:07 +02:00
|
|
|
if (child->type == AST_BLOCK)
|
2025-06-16 22:55:24 +02:00
|
|
|
block = child.get();
|
2016-09-19 01:30:07 +02:00
|
|
|
|
|
|
|
|
log_assert(block != nullptr);
|
|
|
|
|
|
2018-12-18 17:49:38 +01:00
|
|
|
if (num_steps == 0) {
|
|
|
|
|
newNode = children[0]->clone();
|
|
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-19 01:30:07 +02:00
|
|
|
int myidx = autoidx++;
|
2025-06-16 22:55:24 +02:00
|
|
|
AstNode* outreg = nullptr;
|
2016-09-19 01:30:07 +02:00
|
|
|
|
|
|
|
|
for (int i = 0; i < num_steps; i++)
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
auto reg_owned = std::make_unique<AstNode>(AST_WIRE, std::make_unique<AstNode>(AST_RANGE,
|
2016-09-19 01:30:07 +02:00
|
|
|
mkconst_int(width_hint-1, true), mkconst_int(0, true)));
|
2025-06-16 22:55:24 +02:00
|
|
|
auto* reg = reg_owned.get();
|
|
|
|
|
current_ast_mod->children.push_back(std::move(reg_owned));
|
2016-09-19 01:30:07 +02:00
|
|
|
|
2022-08-08 16:13:33 +02:00
|
|
|
reg->str = stringf("$past$%s:%d$%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, myidx, i);
|
2016-09-19 01:30:07 +02:00
|
|
|
reg->is_reg = true;
|
2022-05-24 17:18:53 +02:00
|
|
|
reg->is_signed = sign_hint;
|
2016-09-19 01:30:07 +02:00
|
|
|
|
|
|
|
|
|
2023-04-04 22:59:44 +02:00
|
|
|
while (reg->simplify(true, 1, -1, false)) { }
|
2016-09-19 01:30:07 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto regid = std::make_unique<AstNode>(AST_IDENTIFIER);
|
2016-09-19 01:30:07 +02:00
|
|
|
regid->str = reg->str;
|
|
|
|
|
regid->id2ast = reg;
|
2018-09-18 00:23:40 +02:00
|
|
|
regid->was_checked = true;
|
2016-09-19 01:30:07 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> rhs = nullptr;
|
2016-09-19 01:30:07 +02:00
|
|
|
|
|
|
|
|
if (outreg == nullptr) {
|
|
|
|
|
rhs = children.at(0)->clone();
|
|
|
|
|
} else {
|
2025-06-16 22:55:24 +02:00
|
|
|
rhs = std::make_unique<AstNode>(AST_IDENTIFIER);
|
2016-09-19 01:30:07 +02:00
|
|
|
rhs->str = outreg->str;
|
|
|
|
|
rhs->id2ast = outreg;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
block->children.push_back(std::make_unique<AstNode>(AST_ASSIGN_LE, std::move(regid), std::move(rhs)));
|
2016-09-19 01:30:07 +02:00
|
|
|
outreg = reg;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_IDENTIFIER);
|
2016-09-19 01:30:07 +02:00
|
|
|
newNode->str = outreg->str;
|
|
|
|
|
newNode->id2ast = outreg;
|
|
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-01 19:41:35 +02:00
|
|
|
if (str == "\\$stable" || str == "\\$rose" || str == "\\$fell" || str == "\\$changed")
|
2016-09-19 01:30:07 +02:00
|
|
|
{
|
|
|
|
|
if (GetSize(children) != 1)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("System function %s got %d arguments, expected 1.\n",
|
2018-11-04 10:19:32 +01:00
|
|
|
RTLIL::unescape_id(str).c_str(), int(children.size()));
|
2016-09-19 01:30:07 +02:00
|
|
|
|
|
|
|
|
if (!current_always_clocked)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("System function %s is only allowed in clocked blocks.\n",
|
2018-11-04 10:19:32 +01:00
|
|
|
RTLIL::unescape_id(str).c_str());
|
2016-09-19 01:30:07 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto present = children.at(0)->clone();
|
|
|
|
|
auto past = clone();
|
2016-09-19 01:30:07 +02:00
|
|
|
past->str = "\\$past";
|
|
|
|
|
|
|
|
|
|
if (str == "\\$stable")
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_EQ, std::move(past), std::move(present));
|
2016-09-19 01:30:07 +02:00
|
|
|
|
2018-10-01 19:41:35 +02:00
|
|
|
else if (str == "\\$changed")
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_NE, std::move(past), std::move(present));
|
2018-10-01 19:41:35 +02:00
|
|
|
|
2016-09-19 01:30:07 +02:00
|
|
|
else if (str == "\\$rose")
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_LOGIC_AND,
|
|
|
|
|
std::make_unique<AstNode>(AST_LOGIC_NOT, std::make_unique<AstNode>(AST_BIT_AND, std::move(past), mkconst_int(1,false))),
|
|
|
|
|
std::make_unique<AstNode>(AST_BIT_AND, std::move(present), mkconst_int(1,false)));
|
2016-09-19 01:30:07 +02:00
|
|
|
|
|
|
|
|
else if (str == "\\$fell")
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_LOGIC_AND,
|
|
|
|
|
std::make_unique<AstNode>(AST_BIT_AND, std::move(past), mkconst_int(1,false)),
|
|
|
|
|
std::make_unique<AstNode>(AST_LOGIC_NOT, std::make_unique<AstNode>(AST_BIT_AND, std::move(present), mkconst_int(1,false))));
|
2016-09-19 01:30:07 +02:00
|
|
|
|
|
|
|
|
else
|
|
|
|
|
log_abort();
|
|
|
|
|
|
|
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-14 15:24:03 +02:00
|
|
|
// $anyconst and $anyseq are mapped in AstNode::genRTLIL()
|
2018-02-23 13:14:47 +01:00
|
|
|
if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") {
|
2016-09-19 01:30:07 +02:00
|
|
|
recursion_counter--;
|
2016-07-27 15:41:22 +02:00
|
|
|
return false;
|
2016-09-19 01:30:07 +02:00
|
|
|
}
|
2016-07-27 15:41:22 +02:00
|
|
|
|
2013-12-04 21:19:54 +01:00
|
|
|
if (str == "\\$clog2")
|
|
|
|
|
{
|
2014-06-14 13:36:23 +02:00
|
|
|
if (children.size() != 1)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("System function %s got %d arguments, expected 1.\n",
|
2018-11-04 10:19:32 +01:00
|
|
|
RTLIL::unescape_id(str).c_str(), int(children.size()));
|
2014-06-14 13:36:23 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto buf = children[0]->clone();
|
2023-04-04 22:59:44 +02:00
|
|
|
while (buf->simplify(true, stage, width_hint, sign_hint)) { }
|
2014-01-12 21:04:42 +01:00
|
|
|
if (buf->type != AST_CONSTANT)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Failed to evaluate system function `%s' with non-constant value.\n", str.c_str());
|
2013-12-04 21:19:54 +01:00
|
|
|
|
|
|
|
|
RTLIL::Const arg_value = buf->bitsAsConst();
|
2014-09-08 12:25:23 +02:00
|
|
|
if (arg_value.as_bool())
|
2014-10-10 16:59:44 +02:00
|
|
|
arg_value = const_sub(arg_value, 1, false, false, GetSize(arg_value));
|
2014-07-25 13:07:31 +02:00
|
|
|
|
2013-12-04 21:19:54 +01:00
|
|
|
uint32_t result = 0;
|
2024-11-29 00:31:34 +01:00
|
|
|
for (auto i = 0; i < arg_value.size(); i++)
|
2024-10-09 19:39:45 +02:00
|
|
|
if (arg_value.at(i) == RTLIL::State::S1)
|
2014-09-06 19:31:04 +02:00
|
|
|
result = i + 1;
|
2013-12-04 21:19:54 +01:00
|
|
|
|
2018-11-24 18:49:23 +01:00
|
|
|
newNode = mkconst_int(result, true);
|
2013-12-04 21:19:54 +01:00
|
|
|
goto apply_newNode;
|
2013-11-20 11:05:58 +01:00
|
|
|
}
|
2017-09-26 05:25:42 +02:00
|
|
|
|
2023-12-28 00:23:59 +01:00
|
|
|
if (str == "\\$dimensions" || str == "\\$unpacked_dimensions" ||
|
2025-06-16 22:55:24 +02:00
|
|
|
str == "\\$increment" || str == "\\$size" || str == "\\$bits" || str == "\\$high" || str == "\\$low" || str == "\\$left" || str == "\\$right")
|
2017-09-26 05:25:42 +02:00
|
|
|
{
|
2017-09-26 18:18:25 +02:00
|
|
|
int dim = 1;
|
2023-12-28 00:23:59 +01:00
|
|
|
if (str == "\\$dimensions" || str == "\\$unpacked_dimensions" || str == "\\$bits") {
|
2020-09-15 19:49:52 +02:00
|
|
|
if (children.size() != 1)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("System function %s got %d arguments, expected 1.\n",
|
2020-09-15 19:49:52 +02:00
|
|
|
RTLIL::unescape_id(str).c_str(), int(children.size()));
|
|
|
|
|
} else {
|
|
|
|
|
if (children.size() != 1 && children.size() != 2)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("System function %s got %d arguments, expected 1 or 2.\n",
|
2020-09-15 19:49:52 +02:00
|
|
|
RTLIL::unescape_id(str).c_str(), int(children.size()));
|
|
|
|
|
if (children.size() == 2) {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto buf = children[1]->clone();
|
2020-09-15 19:49:52 +02:00
|
|
|
// Evaluate constant expression
|
2023-04-04 22:59:44 +02:00
|
|
|
while (buf->simplify(true, stage, width_hint, sign_hint)) { }
|
2020-09-15 19:49:52 +02:00
|
|
|
dim = buf->asInt(false);
|
|
|
|
|
}
|
2017-09-26 18:18:25 +02:00
|
|
|
}
|
2025-06-16 22:55:24 +02:00
|
|
|
auto buf = children[0]->clone();
|
2017-09-26 08:11:25 +02:00
|
|
|
int mem_depth = 1;
|
2020-09-15 19:49:52 +02:00
|
|
|
int result, high = 0, low = 0, left = 0, right = 0, width = 1; // defaults for a simple wire
|
2023-12-28 00:23:59 +01:00
|
|
|
int expr_dimensions = 0, expr_unpacked_dimensions = 0;
|
2025-06-16 22:55:24 +02:00
|
|
|
AstNode *id_ast = nullptr;
|
2017-09-26 18:18:25 +02:00
|
|
|
|
2017-09-26 05:25:42 +02:00
|
|
|
buf->detectSignWidth(width_hint, sign_hint);
|
2017-09-26 19:34:24 +02:00
|
|
|
|
2017-09-26 18:18:25 +02:00
|
|
|
if (buf->type == AST_IDENTIFIER) {
|
|
|
|
|
id_ast = buf->id2ast;
|
2025-06-16 22:55:24 +02:00
|
|
|
if (id_ast == nullptr && current_scope.count(buf->str))
|
2017-09-26 18:18:25 +02:00
|
|
|
id_ast = current_scope.at(buf->str);
|
|
|
|
|
if (!id_ast)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Failed to resolve identifier %s for width detection!\n", buf->str.c_str());
|
2023-02-09 19:27:51 +01:00
|
|
|
|
2024-01-25 07:28:15 +01:00
|
|
|
if (id_ast->type == AST_WIRE || id_ast->type == AST_MEMORY) {
|
|
|
|
|
// Check for item in packed struct / union
|
|
|
|
|
AstNode *item_node = buf->get_struct_member();
|
|
|
|
|
if (item_node)
|
|
|
|
|
id_ast = item_node;
|
|
|
|
|
|
2023-02-09 19:27:51 +01:00
|
|
|
// The dimension of the original array expression is saved in the 'integer' field
|
|
|
|
|
dim += buf->integer;
|
|
|
|
|
|
2024-01-25 07:28:15 +01:00
|
|
|
int dims = GetSize(id_ast->dimensions);
|
|
|
|
|
// TODO: IEEE Std 1800-2017 20.7: "If the first argument to an array query function would cause $dimensions to return 0
|
|
|
|
|
// or if the second argument is out of range, then 'x shall be returned."
|
|
|
|
|
if (dim < 1 || dim > dims)
|
|
|
|
|
input_error("Dimension %d out of range in `%s', as it only has %d dimensions!\n", dim, id_ast->str.c_str(), dims);
|
2023-12-28 00:23:59 +01:00
|
|
|
|
|
|
|
|
expr_dimensions = dims - dim + 1;
|
|
|
|
|
expr_unpacked_dimensions = std::max(id_ast->unpacked_dimensions - dim + 1, 0);
|
|
|
|
|
|
2024-01-25 07:28:15 +01:00
|
|
|
right = low = id_ast->dimensions[dim - 1].range_right;
|
|
|
|
|
left = high = low + id_ast->dimensions[dim - 1].range_width - 1;
|
|
|
|
|
if (id_ast->dimensions[dim - 1].range_swapped) {
|
|
|
|
|
std::swap(left, right);
|
|
|
|
|
}
|
|
|
|
|
for (int i = dim; i < dims; i++) {
|
|
|
|
|
mem_depth *= id_ast->dimensions[i].range_width;
|
2017-09-26 08:11:25 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-09-15 19:49:52 +02:00
|
|
|
width = high - low + 1;
|
|
|
|
|
} else {
|
|
|
|
|
width = width_hint;
|
2023-12-28 00:23:59 +01:00
|
|
|
right = low = 0;
|
|
|
|
|
left = high = width - 1;
|
|
|
|
|
expr_dimensions = 1;
|
2017-09-26 08:11:25 +02:00
|
|
|
}
|
2023-12-28 00:23:59 +01:00
|
|
|
if (str == "\\$dimensions")
|
|
|
|
|
result = expr_dimensions;
|
|
|
|
|
else if (str == "\\$unpacked_dimensions")
|
|
|
|
|
result = expr_unpacked_dimensions;
|
|
|
|
|
else if (str == "\\$high")
|
2020-09-15 19:49:52 +02:00
|
|
|
result = high;
|
|
|
|
|
else if (str == "\\$low")
|
|
|
|
|
result = low;
|
|
|
|
|
else if (str == "\\$left")
|
|
|
|
|
result = left;
|
|
|
|
|
else if (str == "\\$right")
|
|
|
|
|
result = right;
|
2023-12-27 22:00:13 +01:00
|
|
|
else if (str == "\\$increment")
|
|
|
|
|
result = left >= right ? 1 : -1;
|
2020-09-15 19:49:52 +02:00
|
|
|
else if (str == "\\$size")
|
|
|
|
|
result = width;
|
2023-02-09 19:27:51 +01:00
|
|
|
else { // str == "\\$bits"
|
2020-09-15 19:49:52 +02:00
|
|
|
result = width * mem_depth;
|
|
|
|
|
}
|
2022-05-20 21:46:39 +02:00
|
|
|
newNode = mkconst_int(result, true);
|
2017-09-26 05:25:42 +02:00
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
2013-12-04 21:19:54 +01:00
|
|
|
|
2014-06-14 13:36:23 +02:00
|
|
|
if (str == "\\$ln" || str == "\\$log10" || str == "\\$exp" || str == "\\$sqrt" || str == "\\$pow" ||
|
|
|
|
|
str == "\\$floor" || str == "\\$ceil" || str == "\\$sin" || str == "\\$cos" || str == "\\$tan" ||
|
|
|
|
|
str == "\\$asin" || str == "\\$acos" || str == "\\$atan" || str == "\\$atan2" || str == "\\$hypot" ||
|
2017-01-03 17:40:58 +01:00
|
|
|
str == "\\$sinh" || str == "\\$cosh" || str == "\\$tanh" || str == "\\$asinh" || str == "\\$acosh" || str == "\\$atanh" ||
|
|
|
|
|
str == "\\$rtoi" || str == "\\$itor")
|
2014-06-14 13:36:23 +02:00
|
|
|
{
|
|
|
|
|
bool func_with_two_arguments = str == "\\$pow" || str == "\\$atan2" || str == "\\$hypot";
|
|
|
|
|
double x = 0, y = 0;
|
|
|
|
|
|
|
|
|
|
if (func_with_two_arguments) {
|
|
|
|
|
if (children.size() != 2)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("System function %s got %d arguments, expected 2.\n",
|
2018-11-04 10:19:32 +01:00
|
|
|
RTLIL::unescape_id(str).c_str(), int(children.size()));
|
2014-06-14 13:36:23 +02:00
|
|
|
} else {
|
|
|
|
|
if (children.size() != 1)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("System function %s got %d arguments, expected 1.\n",
|
2018-11-04 10:19:32 +01:00
|
|
|
RTLIL::unescape_id(str).c_str(), int(children.size()));
|
2014-06-14 13:36:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (children.size() >= 1) {
|
2023-04-04 22:59:44 +02:00
|
|
|
while (children[0]->simplify(true, stage, width_hint, sign_hint)) { }
|
2014-06-14 13:36:23 +02:00
|
|
|
if (!children[0]->isConst())
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Failed to evaluate system function `%s' with non-constant argument.\n",
|
2018-11-04 10:19:32 +01:00
|
|
|
RTLIL::unescape_id(str).c_str());
|
2014-06-14 13:36:23 +02:00
|
|
|
int child_width_hint = width_hint;
|
|
|
|
|
bool child_sign_hint = sign_hint;
|
|
|
|
|
children[0]->detectSignWidth(child_width_hint, child_sign_hint);
|
|
|
|
|
x = children[0]->asReal(child_sign_hint);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (children.size() >= 2) {
|
2023-04-04 22:59:44 +02:00
|
|
|
while (children[1]->simplify(true, stage, width_hint, sign_hint)) { }
|
2014-06-14 13:36:23 +02:00
|
|
|
if (!children[1]->isConst())
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Failed to evaluate system function `%s' with non-constant argument.\n",
|
2018-11-04 10:19:32 +01:00
|
|
|
RTLIL::unescape_id(str).c_str());
|
2014-06-14 13:36:23 +02:00
|
|
|
int child_width_hint = width_hint;
|
|
|
|
|
bool child_sign_hint = sign_hint;
|
|
|
|
|
children[1]->detectSignWidth(child_width_hint, child_sign_hint);
|
|
|
|
|
y = children[1]->asReal(child_sign_hint);
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-03 17:40:58 +01:00
|
|
|
if (str == "\\$rtoi") {
|
|
|
|
|
newNode = AstNode::mkconst_int(x, true);
|
|
|
|
|
} else {
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_REALVALUE);
|
|
|
|
|
if (str == "\\$ln") newNode->realvalue = ::log(x);
|
2017-01-03 17:40:58 +01:00
|
|
|
else if (str == "\\$log10") newNode->realvalue = ::log10(x);
|
|
|
|
|
else if (str == "\\$exp") newNode->realvalue = ::exp(x);
|
|
|
|
|
else if (str == "\\$sqrt") newNode->realvalue = ::sqrt(x);
|
|
|
|
|
else if (str == "\\$pow") newNode->realvalue = ::pow(x, y);
|
|
|
|
|
else if (str == "\\$floor") newNode->realvalue = ::floor(x);
|
|
|
|
|
else if (str == "\\$ceil") newNode->realvalue = ::ceil(x);
|
|
|
|
|
else if (str == "\\$sin") newNode->realvalue = ::sin(x);
|
|
|
|
|
else if (str == "\\$cos") newNode->realvalue = ::cos(x);
|
|
|
|
|
else if (str == "\\$tan") newNode->realvalue = ::tan(x);
|
|
|
|
|
else if (str == "\\$asin") newNode->realvalue = ::asin(x);
|
|
|
|
|
else if (str == "\\$acos") newNode->realvalue = ::acos(x);
|
|
|
|
|
else if (str == "\\$atan") newNode->realvalue = ::atan(x);
|
|
|
|
|
else if (str == "\\$atan2") newNode->realvalue = ::atan2(x, y);
|
|
|
|
|
else if (str == "\\$hypot") newNode->realvalue = ::hypot(x, y);
|
|
|
|
|
else if (str == "\\$sinh") newNode->realvalue = ::sinh(x);
|
|
|
|
|
else if (str == "\\$cosh") newNode->realvalue = ::cosh(x);
|
|
|
|
|
else if (str == "\\$tanh") newNode->realvalue = ::tanh(x);
|
|
|
|
|
else if (str == "\\$asinh") newNode->realvalue = ::asinh(x);
|
|
|
|
|
else if (str == "\\$acosh") newNode->realvalue = ::acosh(x);
|
|
|
|
|
else if (str == "\\$atanh") newNode->realvalue = ::atanh(x);
|
|
|
|
|
else if (str == "\\$itor") newNode->realvalue = x;
|
|
|
|
|
else log_abort();
|
|
|
|
|
}
|
2014-06-14 13:36:23 +02:00
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-19 22:15:51 +01:00
|
|
|
if (str == "\\$sformatf") {
|
2020-11-29 09:57:07 +01:00
|
|
|
Fmt fmt = processFormat(stage, /*sformat_like=*/true);
|
|
|
|
|
newNode = AstNode::mkconst_str(fmt.render());
|
2020-01-19 22:15:51 +01:00
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-23 00:55:55 +01:00
|
|
|
if (str == "\\$countbits") {
|
|
|
|
|
if (children.size() < 2)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("System function %s got %d arguments, expected at least 2.\n",
|
2021-02-23 00:55:55 +01:00
|
|
|
RTLIL::unescape_id(str).c_str(), int(children.size()));
|
|
|
|
|
|
|
|
|
|
std::vector<RTLIL::State> control_bits;
|
|
|
|
|
|
|
|
|
|
// Determine which bits to count
|
|
|
|
|
for (size_t i = 1; i < children.size(); i++) {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto& node = children[i];
|
2023-04-04 22:59:44 +02:00
|
|
|
while (node->simplify(true, stage, -1, false)) { }
|
2021-02-23 00:55:55 +01:00
|
|
|
if (node->type != AST_CONSTANT)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Failed to evaluate system function `%s' with non-constant control bit argument.\n", str.c_str());
|
2021-02-23 00:55:55 +01:00
|
|
|
if (node->bits.size() != 1)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Failed to evaluate system function `%s' with control bit width != 1.\n", str.c_str());
|
2021-02-23 00:55:55 +01:00
|
|
|
control_bits.push_back(node->bits[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Detect width of exp (first argument of $countbits)
|
|
|
|
|
int exp_width = -1;
|
|
|
|
|
bool exp_sign = false;
|
2025-06-16 22:55:24 +02:00
|
|
|
auto& exp = children[0];
|
|
|
|
|
exp->detectSignWidth(exp_width, exp_sign, nullptr);
|
2021-02-23 00:55:55 +01:00
|
|
|
|
|
|
|
|
newNode = mkconst_int(0, false);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < exp_width; i++) {
|
|
|
|
|
// Generate nodes for: exp << i >> ($size(exp) - 1)
|
2025-06-16 22:55:24 +02:00
|
|
|
// ^^ ^^
|
|
|
|
|
auto lsh_node = std::make_unique<AstNode>(AST_SHIFT_LEFT, exp->clone(), mkconst_int(i, false));
|
|
|
|
|
auto rsh_node = std::make_unique<AstNode>(AST_SHIFT_RIGHT, std::move(lsh_node), mkconst_int(exp_width - 1, false));
|
2021-02-23 00:55:55 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> or_node = nullptr;
|
2021-02-23 00:55:55 +01:00
|
|
|
|
|
|
|
|
for (RTLIL::State control_bit : control_bits) {
|
|
|
|
|
// Generate node for: (exp << i >> ($size(exp) - 1)) === control_bit
|
2025-06-16 22:55:24 +02:00
|
|
|
// ^^^
|
|
|
|
|
auto eq_node = std::make_unique<AstNode>(AST_EQX, rsh_node->clone(), mkconst_bits({control_bit}, false));
|
2021-02-23 00:55:55 +01:00
|
|
|
|
|
|
|
|
// Or the result for each checked bit value
|
|
|
|
|
if (or_node)
|
2025-06-16 22:55:24 +02:00
|
|
|
or_node = std::make_unique<AstNode>(AST_LOGIC_OR, std::move(or_node), std::move(eq_node));
|
2021-02-23 00:55:55 +01:00
|
|
|
else
|
2025-06-16 22:55:24 +02:00
|
|
|
or_node = std::move(eq_node);
|
2021-02-23 00:55:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We should have at least one element in control_bits,
|
|
|
|
|
// because we checked for the number of arguments above
|
|
|
|
|
log_assert(or_node != nullptr);
|
|
|
|
|
|
|
|
|
|
// Generate node for adding with result of previous bit
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_ADD, std::move(newNode), std::move(or_node));
|
2021-02-23 00:55:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-23 01:19:06 +01:00
|
|
|
if (str == "\\$countones" || str == "\\$isunknown" || str == "\\$onehot" || str == "\\$onehot0") {
|
|
|
|
|
if (children.size() != 1)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("System function %s got %d arguments, expected 1.\n",
|
2021-02-23 01:19:06 +01:00
|
|
|
RTLIL::unescape_id(str).c_str(), int(children.size()));
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto countbits = clone();
|
2021-02-23 01:19:06 +01:00
|
|
|
countbits->str = "\\$countbits";
|
|
|
|
|
|
|
|
|
|
if (str == "\\$countones") {
|
|
|
|
|
countbits->children.push_back(mkconst_bits({RTLIL::State::S1}, false));
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::move(countbits);
|
2021-02-23 01:19:06 +01:00
|
|
|
} else if (str == "\\$isunknown") {
|
|
|
|
|
countbits->children.push_back(mkconst_bits({RTLIL::Sx}, false));
|
|
|
|
|
countbits->children.push_back(mkconst_bits({RTLIL::Sz}, false));
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_GT, std::move(countbits), mkconst_int(0, false));
|
2021-02-23 01:19:06 +01:00
|
|
|
} else if (str == "\\$onehot") {
|
|
|
|
|
countbits->children.push_back(mkconst_bits({RTLIL::State::S1}, false));
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_EQ, std::move(countbits), mkconst_int(1, false));
|
2021-02-23 01:19:06 +01:00
|
|
|
} else if (str == "\\$onehot0") {
|
|
|
|
|
countbits->children.push_back(mkconst_bits({RTLIL::State::S1}, false));
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_LE, std::move(countbits), mkconst_int(1, false));
|
2021-02-23 01:19:06 +01:00
|
|
|
} else {
|
|
|
|
|
log_abort();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-21 12:43:51 +02:00
|
|
|
if (current_scope.count(str) != 0 && current_scope[str]->type == AST_DPI_FUNCTION)
|
|
|
|
|
{
|
|
|
|
|
AstNode *dpi_decl = current_scope[str];
|
|
|
|
|
|
|
|
|
|
std::string rtype, fname;
|
|
|
|
|
std::vector<std::string> argtypes;
|
2025-06-16 22:55:24 +02:00
|
|
|
std::vector<std::unique_ptr<AstNode>> args;
|
2014-08-21 12:43:51 +02:00
|
|
|
|
|
|
|
|
rtype = RTLIL::unescape_id(dpi_decl->children.at(0)->str);
|
2014-08-21 17:22:04 +02:00
|
|
|
fname = RTLIL::unescape_id(dpi_decl->children.at(1)->str);
|
2014-08-21 12:43:51 +02:00
|
|
|
|
2014-10-10 16:59:44 +02:00
|
|
|
for (int i = 2; i < GetSize(dpi_decl->children); i++)
|
2014-08-21 12:43:51 +02:00
|
|
|
{
|
2014-10-10 16:59:44 +02:00
|
|
|
if (i-2 >= GetSize(children))
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Insufficient number of arguments in DPI function call.\n");
|
2014-08-21 12:43:51 +02:00
|
|
|
|
|
|
|
|
argtypes.push_back(RTLIL::unescape_id(dpi_decl->children.at(i)->str));
|
2014-08-21 17:22:04 +02:00
|
|
|
args.push_back(children.at(i-2)->clone());
|
2023-04-04 22:59:44 +02:00
|
|
|
while (args.back()->simplify(true, stage, -1, false)) { }
|
2014-08-21 12:43:51 +02:00
|
|
|
|
|
|
|
|
if (args.back()->type != AST_CONSTANT && args.back()->type != AST_REALVALUE)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Failed to evaluate DPI function with non-constant argument.\n");
|
2014-08-21 12:43:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
newNode = dpi_call(rtype, fname, argtypes, args);
|
2014-08-21 13:09:47 +02:00
|
|
|
|
2014-08-21 12:43:51 +02:00
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-29 21:14:11 +02:00
|
|
|
if (current_scope.count(str) == 0)
|
|
|
|
|
str = try_pop_module_prefix();
|
2013-12-04 21:19:54 +01:00
|
|
|
if (current_scope.count(str) == 0 || current_scope[str]->type != AST_FUNCTION)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Can't resolve function name `%s'.\n", str.c_str());
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
2015-09-18 09:50:53 +02:00
|
|
|
|
2015-09-18 05:34:56 +02:00
|
|
|
if (type == AST_TCALL)
|
|
|
|
|
{
|
2016-03-21 16:19:51 +01:00
|
|
|
if (str == "$finish" || str == "$stop")
|
2015-09-18 05:34:56 +02:00
|
|
|
{
|
2015-09-18 09:50:53 +02:00
|
|
|
if (!current_always || current_always->type != AST_INITIAL)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("System task `%s' outside initial block is unsupported.\n", str.c_str());
|
2015-09-18 09:50:53 +02:00
|
|
|
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("System task `%s' executed.\n", str.c_str());
|
2015-09-18 05:34:56 +02:00
|
|
|
}
|
|
|
|
|
|
2014-10-26 20:33:10 +01:00
|
|
|
if (str == "\\$readmemh" || str == "\\$readmemb")
|
|
|
|
|
{
|
|
|
|
|
if (GetSize(children) < 2 || GetSize(children) > 4)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("System function %s got %d arguments, expected 2-4.\n",
|
2018-11-04 10:19:32 +01:00
|
|
|
RTLIL::unescape_id(str).c_str(), int(children.size()));
|
2014-10-26 20:33:10 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto node_filename = children[0]->clone();
|
2023-04-04 22:59:44 +02:00
|
|
|
while (node_filename->simplify(true, stage, width_hint, sign_hint)) { }
|
2014-10-26 20:33:10 +01:00
|
|
|
if (node_filename->type != AST_CONSTANT)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Failed to evaluate system function `%s' with non-constant 1st argument.\n", str.c_str());
|
2014-10-26 20:33:10 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto node_memory = children[1]->clone();
|
2023-04-04 22:59:44 +02:00
|
|
|
while (node_memory->simplify(true, stage, width_hint, sign_hint)) { }
|
2014-10-26 20:33:10 +01:00
|
|
|
if (node_memory->type != AST_IDENTIFIER || node_memory->id2ast == nullptr || node_memory->id2ast->type != AST_MEMORY)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Failed to evaluate system function `%s' with non-memory 2nd argument.\n", str.c_str());
|
2014-10-26 20:33:10 +01:00
|
|
|
|
|
|
|
|
int start_addr = -1, finish_addr = -1;
|
|
|
|
|
|
|
|
|
|
if (GetSize(children) > 2) {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto node_addr = children[2]->clone();
|
2023-04-04 22:59:44 +02:00
|
|
|
while (node_addr->simplify(true, stage, width_hint, sign_hint)) { }
|
2014-10-26 20:33:10 +01:00
|
|
|
if (node_addr->type != AST_CONSTANT)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Failed to evaluate system function `%s' with non-constant 3rd argument.\n", str.c_str());
|
2016-02-13 17:31:24 +01:00
|
|
|
start_addr = int(node_addr->asInt(false));
|
2014-10-26 20:33:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (GetSize(children) > 3) {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto node_addr = children[3]->clone();
|
2023-04-04 22:59:44 +02:00
|
|
|
while (node_addr->simplify(true, stage, width_hint, sign_hint)) { }
|
2014-10-26 20:33:10 +01:00
|
|
|
if (node_addr->type != AST_CONSTANT)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Failed to evaluate system function `%s' with non-constant 4th argument.\n", str.c_str());
|
2016-02-13 17:31:24 +01:00
|
|
|
finish_addr = int(node_addr->asInt(false));
|
2014-10-26 20:33:10 +01:00
|
|
|
}
|
|
|
|
|
|
2015-07-31 10:40:09 +02:00
|
|
|
bool unconditional_init = false;
|
|
|
|
|
if (current_always->type == AST_INITIAL) {
|
2015-09-30 15:46:51 +02:00
|
|
|
pool<AstNode*> queue;
|
2015-07-31 10:40:09 +02:00
|
|
|
log_assert(current_always->children[0]->type == AST_BLOCK);
|
2025-06-16 22:55:24 +02:00
|
|
|
queue.insert(current_always->children[0].get());
|
2015-09-30 15:46:51 +02:00
|
|
|
while (!unconditional_init && !queue.empty()) {
|
|
|
|
|
pool<AstNode*> next_queue;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& n : queue)
|
|
|
|
|
for (auto& c : n->children) {
|
|
|
|
|
if (c.get() == this)
|
2015-09-30 15:46:51 +02:00
|
|
|
unconditional_init = true;
|
2025-06-16 22:55:24 +02:00
|
|
|
next_queue.insert(c.get());
|
2015-07-31 10:40:09 +02:00
|
|
|
}
|
2015-09-30 15:46:51 +02:00
|
|
|
next_queue.swap(queue);
|
|
|
|
|
}
|
2015-07-31 10:40:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
newNode = readmem(str == "\\$readmemh", node_filename->bitsAsConst().decode_string(), node_memory->id2ast, start_addr, finish_addr, unconditional_init);
|
2014-10-26 20:33:10 +01:00
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-29 21:14:11 +02:00
|
|
|
if (current_scope.count(str) == 0)
|
|
|
|
|
str = try_pop_module_prefix();
|
2013-01-05 11:13:26 +01:00
|
|
|
if (current_scope.count(str) == 0 || current_scope[str]->type != AST_TASK)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Can't resolve task name `%s'.\n", str.c_str());
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
|
2014-08-18 14:29:30 +02:00
|
|
|
|
|
|
|
|
std::stringstream sstr;
|
2022-08-08 16:13:33 +02:00
|
|
|
sstr << str << "$func$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++) << '.';
|
2014-08-18 14:29:30 +02:00
|
|
|
std::string prefix = sstr.str();
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto* decl = current_scope[str];
|
2021-02-12 20:25:34 +01:00
|
|
|
if (unevaluated_tern_branch && decl->is_recursive_function())
|
|
|
|
|
goto replace_fcall_later;
|
2025-06-16 22:55:24 +02:00
|
|
|
auto decl_clone = decl->clone();
|
|
|
|
|
decl = decl_clone.get(); // sketchy?
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
decl->replace_result_wire_name_in_function(str, "$result"); // enables recursion
|
|
|
|
|
decl->expand_genblock(prefix);
|
|
|
|
|
|
2021-01-27 19:21:13 +01:00
|
|
|
if (decl->type == AST_FUNCTION && !decl->attributes.count(ID::via_celltype))
|
2014-02-14 19:56:44 +01:00
|
|
|
{
|
2021-01-27 19:21:13 +01:00
|
|
|
bool require_const_eval = decl->has_const_only_constructs();
|
2014-02-14 19:56:44 +01:00
|
|
|
bool all_args_const = true;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children) {
|
2023-04-04 22:59:44 +02:00
|
|
|
while (child->simplify(true, 1, -1, false)) { }
|
2020-07-20 04:27:09 +02:00
|
|
|
if (child->type != AST_CONSTANT && child->type != AST_REALVALUE)
|
2014-02-14 19:56:44 +01:00
|
|
|
all_args_const = false;
|
2014-02-16 13:16:38 +01:00
|
|
|
}
|
2014-02-14 19:56:44 +01:00
|
|
|
|
|
|
|
|
if (all_args_const) {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto func_workspace = decl->clone();
|
2023-04-05 11:00:07 +02:00
|
|
|
func_workspace->set_in_param_flag(true);
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
func_workspace->str = prefix_id(prefix, "$result");
|
2025-06-16 22:55:24 +02:00
|
|
|
// func_workspace->dumpAst(stdout, "func_workspace ");
|
2021-01-27 19:21:13 +01:00
|
|
|
newNode = func_workspace->eval_const_function(this, in_param || require_const_eval);
|
|
|
|
|
if (newNode) {
|
|
|
|
|
goto apply_newNode;
|
|
|
|
|
}
|
2014-02-14 19:56:44 +01:00
|
|
|
}
|
|
|
|
|
|
2014-06-06 22:55:02 +02:00
|
|
|
if (in_param)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Non-constant function call in constant expression.\n");
|
2014-06-07 00:02:05 +02:00
|
|
|
if (require_const_eval)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Function %s can only be called with constant arguments.\n", str.c_str());
|
2014-02-14 19:56:44 +01:00
|
|
|
}
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
size_t arg_count = 0;
|
2015-11-23 17:09:57 +01:00
|
|
|
dict<std::string, AstNode*> wire_cache;
|
2025-06-16 22:55:24 +02:00
|
|
|
vector<std::unique_ptr<AstNode>> new_stmts;
|
|
|
|
|
vector<std::unique_ptr<AstNode>> output_assignments;
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
if (current_block == nullptr)
|
2013-01-05 11:13:26 +01:00
|
|
|
{
|
2014-07-28 11:08:55 +02:00
|
|
|
log_assert(type == AST_FCALL);
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> wire = nullptr;
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
std::string res_name = prefix_id(prefix, "$result");
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : decl->children)
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
if (child->type == AST_WIRE && child->str == res_name)
|
2013-01-05 11:13:26 +01:00
|
|
|
wire = child->clone();
|
2025-06-16 22:55:24 +02:00
|
|
|
log_assert(wire != nullptr);
|
2013-01-05 11:13:26 +01:00
|
|
|
|
|
|
|
|
wire->port_id = 0;
|
|
|
|
|
wire->is_input = false;
|
|
|
|
|
wire->is_output = false;
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto* wire_leaky = wire.get();
|
|
|
|
|
current_scope[wire->str] = wire_leaky;
|
|
|
|
|
current_ast_mod->children.push_back(std::move(wire));
|
|
|
|
|
while (wire_leaky->simplify(true, 1, -1, false)) { }
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto lvalue = std::make_unique<AstNode>(AST_IDENTIFIER);
|
|
|
|
|
lvalue->str = wire_leaky->str;
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto always = std::make_unique<AstNode>(AST_ALWAYS, std::make_unique<AstNode>(AST_BLOCK,
|
|
|
|
|
std::make_unique<AstNode>(AST_ASSIGN_EQ, std::move(lvalue), clone())));
|
2018-09-18 00:23:40 +02:00
|
|
|
always->children[0]->children[0]->was_checked = true;
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
current_ast_mod->children.push_back(std::move(always));
|
2013-01-05 11:13:26 +01:00
|
|
|
|
|
|
|
|
goto replace_fcall_with_id;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-04 19:48:37 +02:00
|
|
|
if (decl->attributes.count(ID::via_celltype))
|
2014-08-18 14:29:30 +02:00
|
|
|
{
|
2020-05-04 19:48:37 +02:00
|
|
|
std::string celltype = decl->attributes.at(ID::via_celltype)->asAttrConst().decode_string();
|
2014-08-18 14:29:30 +02:00
|
|
|
std::string outport = str;
|
|
|
|
|
|
|
|
|
|
if (celltype.find(' ') != std::string::npos) {
|
|
|
|
|
int pos = celltype.find(' ');
|
|
|
|
|
outport = RTLIL::escape_id(celltype.substr(pos+1));
|
|
|
|
|
celltype = RTLIL::escape_id(celltype.substr(0, pos));
|
|
|
|
|
} else
|
|
|
|
|
celltype = RTLIL::escape_id(celltype);
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto cell = std::make_unique<AstNode>(AST_CELL, std::make_unique<AstNode>(AST_CELLTYPE));
|
2014-10-10 16:59:44 +02:00
|
|
|
cell->str = prefix.substr(0, GetSize(prefix)-1);
|
2014-08-18 14:29:30 +02:00
|
|
|
cell->children[0]->str = celltype;
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& attr : decl->attributes)
|
2014-08-18 14:29:30 +02:00
|
|
|
if (attr.first.str().rfind("\\via_celltype_defparam_", 0) == 0)
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
auto cell_arg = std::make_unique<AstNode>(AST_PARASET, attr.second->clone());
|
2019-08-07 04:08:33 +02:00
|
|
|
cell_arg->str = RTLIL::escape_id(attr.first.substr(strlen("\\via_celltype_defparam_")));
|
2025-06-16 22:55:24 +02:00
|
|
|
cell->children.push_back(std::move(cell_arg));
|
2014-08-18 14:29:30 +02:00
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : decl->children)
|
2014-08-18 14:29:30 +02:00
|
|
|
if (child->type == AST_WIRE && (child->is_input || child->is_output || (type == AST_FCALL && child->str == str)))
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
auto wire = child->clone();
|
2014-08-18 14:29:30 +02:00
|
|
|
wire->port_id = 0;
|
|
|
|
|
wire->is_input = false;
|
|
|
|
|
wire->is_output = false;
|
2025-06-16 22:55:24 +02:00
|
|
|
current_ast_mod->children.push_back(std::move(wire));
|
2023-04-04 22:59:44 +02:00
|
|
|
while (wire->simplify(true, 1, -1, false)) { }
|
2014-08-18 14:29:30 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto wire_id = std::make_unique<AstNode>(AST_IDENTIFIER);
|
2014-08-18 14:29:30 +02:00
|
|
|
wire_id->str = wire->str;
|
|
|
|
|
|
|
|
|
|
if ((child->is_input || child->is_output) && arg_count < children.size())
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
auto arg = children[arg_count++]->clone();
|
|
|
|
|
auto assign = child->is_input ?
|
|
|
|
|
std::make_unique<AstNode>(AST_ASSIGN_EQ, wire_id->clone(), std::move(arg)) :
|
|
|
|
|
std::make_unique<AstNode>(AST_ASSIGN_EQ, std::move(arg), wire_id->clone());
|
2018-09-18 00:23:40 +02:00
|
|
|
assign->children[0]->was_checked = true;
|
2014-08-18 14:29:30 +02:00
|
|
|
|
|
|
|
|
for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) {
|
2025-06-16 22:55:24 +02:00
|
|
|
if (it->get() != current_block_child)
|
2014-08-18 14:29:30 +02:00
|
|
|
continue;
|
2025-06-16 22:55:24 +02:00
|
|
|
current_block->children.insert(it, std::move(assign));
|
2014-08-18 14:29:30 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto cell_arg = std::make_unique<AstNode>(AST_ARGUMENT, std::move(wire_id));
|
2014-08-18 14:29:30 +02:00
|
|
|
cell_arg->str = child->str == str ? outport : child->str;
|
2025-06-16 22:55:24 +02:00
|
|
|
cell->children.push_back(std::move(cell_arg));
|
2014-08-18 14:29:30 +02:00
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
current_ast_mod->children.push_back(std::move(cell));
|
2014-08-18 14:29:30 +02:00
|
|
|
goto replace_fcall_with_id;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : decl->children)
|
2018-03-09 13:47:11 +01:00
|
|
|
if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM || child->type == AST_ENUM_ITEM)
|
2013-01-05 11:13:26 +01:00
|
|
|
{
|
2015-11-23 17:09:57 +01:00
|
|
|
AstNode *wire = nullptr;
|
|
|
|
|
|
|
|
|
|
if (wire_cache.count(child->str))
|
|
|
|
|
{
|
|
|
|
|
wire = wire_cache.at(child->str);
|
2020-08-29 19:31:02 +02:00
|
|
|
bool contains_value = wire->type == AST_LOCALPARAM;
|
|
|
|
|
if (wire->children.size() == contains_value) {
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& c : child->children)
|
2015-11-23 17:09:57 +01:00
|
|
|
wire->children.push_back(c->clone());
|
2017-02-14 15:10:59 +01:00
|
|
|
} else if (!child->children.empty()) {
|
2023-04-04 22:59:44 +02:00
|
|
|
while (child->simplify(true, stage, -1, false)) { }
|
2020-08-29 19:31:02 +02:00
|
|
|
if (GetSize(child->children) == GetSize(wire->children) - contains_value) {
|
2017-02-14 15:10:59 +01:00
|
|
|
for (int i = 0; i < GetSize(child->children); i++)
|
2020-08-29 19:31:02 +02:00
|
|
|
if (*child->children.at(i) != *wire->children.at(i + contains_value))
|
2017-02-14 15:10:59 +01:00
|
|
|
goto tcall_incompatible_wires;
|
|
|
|
|
} else {
|
|
|
|
|
tcall_incompatible_wires:
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Incompatible re-declaration of wire %s.\n", child->str.c_str());
|
2017-02-14 15:10:59 +01:00
|
|
|
}
|
2015-11-23 17:09:57 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
current_ast_mod->children.push_back(child->clone());
|
|
|
|
|
wire = current_ast_mod->children.back().get();
|
2015-11-23 17:09:57 +01:00
|
|
|
wire->port_id = 0;
|
|
|
|
|
wire->is_input = false;
|
|
|
|
|
wire->is_output = false;
|
2019-03-08 07:44:37 +01:00
|
|
|
wire->is_reg = true;
|
2023-04-05 11:00:07 +02:00
|
|
|
wire->set_attribute(ID::nosync, AstNode::mkconst_int(1, false));
|
2020-02-17 10:40:02 +01:00
|
|
|
if (child->type == AST_ENUM_ITEM)
|
2025-06-16 22:55:24 +02:00
|
|
|
wire->set_attribute(ID::enum_base_type, std::move(child->attributes[ID::enum_base_type]));
|
2020-02-03 07:12:24 +01:00
|
|
|
|
2015-11-23 17:09:57 +01:00
|
|
|
wire_cache[child->str] = wire;
|
|
|
|
|
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
current_scope[wire->str] = wire;
|
2015-11-23 17:09:57 +01:00
|
|
|
}
|
2015-11-12 13:02:36 +01:00
|
|
|
|
2023-04-04 22:59:44 +02:00
|
|
|
while (wire->simplify(true, 1, -1, false)) { }
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2014-08-14 22:26:10 +02:00
|
|
|
if ((child->is_input || child->is_output) && arg_count < children.size())
|
2013-01-05 11:13:26 +01:00
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
auto arg = children[arg_count++]->clone();
|
2020-07-25 05:18:24 +02:00
|
|
|
// convert purely constant arguments into localparams
|
2025-06-16 22:55:24 +02:00
|
|
|
if (child->is_input && child->type == AST_WIRE && arg->type == AST_CONSTANT && node_contains_assignment_to(decl, child.get())) {
|
2020-07-25 05:18:24 +02:00
|
|
|
wire->type = AST_LOCALPARAM;
|
|
|
|
|
wire->attributes.erase(ID::nosync);
|
|
|
|
|
wire->children.insert(wire->children.begin(), arg->clone());
|
2020-12-06 02:56:18 +01:00
|
|
|
// args without a range implicitly have width 1
|
|
|
|
|
if (wire->children.back()->type != AST_RANGE) {
|
2020-12-26 16:39:57 +01:00
|
|
|
// check if this wire is redeclared with an explicit size
|
|
|
|
|
bool uses_explicit_size = false;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& other_child : decl->children)
|
2020-12-26 16:39:57 +01:00
|
|
|
if (other_child->type == AST_WIRE && child->str == other_child->str
|
|
|
|
|
&& !other_child->children.empty()
|
|
|
|
|
&& other_child->children.back()->type == AST_RANGE) {
|
|
|
|
|
uses_explicit_size = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!uses_explicit_size) {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto range = std::make_unique<AstNode>();
|
2020-12-26 16:39:57 +01:00
|
|
|
range->type = AST_RANGE;
|
|
|
|
|
range->children.push_back(mkconst_int(0, true));
|
|
|
|
|
range->children.push_back(mkconst_int(0, true));
|
2025-06-16 22:55:24 +02:00
|
|
|
wire->children.push_back(std::move(range));
|
2020-12-26 16:39:57 +01:00
|
|
|
}
|
2020-12-06 02:56:18 +01:00
|
|
|
}
|
2023-04-05 11:00:07 +02:00
|
|
|
wire->fixup_hierarchy_flags();
|
2021-02-21 20:45:21 +01:00
|
|
|
// updates the sizing
|
2023-04-04 22:59:44 +02:00
|
|
|
while (wire->simplify(true, 1, -1, false)) { }
|
2020-07-25 05:18:24 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2025-05-31 02:08:15 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto wire_id = std::make_unique<AstNode>(AST_IDENTIFIER);
|
2013-01-05 11:13:26 +01:00
|
|
|
wire_id->str = wire->str;
|
2025-05-31 02:08:15 +02:00
|
|
|
if (child->is_input) {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto assign = std::make_unique<AstNode>(AST_ASSIGN_EQ, wire_id->clone(), arg->clone());
|
2025-05-31 02:08:15 +02:00
|
|
|
assign->children[0]->was_checked = true;
|
2025-06-16 22:55:24 +02:00
|
|
|
new_stmts.push_back(std::move(assign));
|
2025-05-31 02:08:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (child->is_output) {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto assign = std::make_unique<AstNode>(AST_ASSIGN_EQ, arg->clone(), wire_id->clone());
|
2025-05-31 02:08:15 +02:00
|
|
|
assign->children[0]->was_checked = true;
|
2025-06-16 22:55:24 +02:00
|
|
|
output_assignments.push_back(std::move(assign));
|
2025-05-31 02:08:15 +02:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
}
|
2014-08-05 08:35:51 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : decl->children)
|
2017-01-05 13:18:58 +01:00
|
|
|
if (child->type != AST_WIRE && child->type != AST_MEMORY && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM)
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
new_stmts.push_back(child->clone());
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
new_stmts.reserve(new_stmts.size() + output_assignments.size());
|
|
|
|
|
std::move(output_assignments.begin(), output_assignments.end(), std::back_inserter(new_stmts));
|
2019-03-08 07:44:37 +01:00
|
|
|
|
|
|
|
|
for (auto it = current_block->children.begin(); ; it++) {
|
|
|
|
|
log_assert(it != current_block->children.end());
|
2025-06-16 22:55:24 +02:00
|
|
|
if (it->get() == current_block_child) {
|
|
|
|
|
current_block->children.insert(it,
|
|
|
|
|
std::make_move_iterator(new_stmts.begin()),
|
|
|
|
|
std::make_move_iterator(new_stmts.end()));
|
2019-03-08 07:44:37 +01:00
|
|
|
break;
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
2019-03-08 07:44:37 +01:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
|
|
|
|
|
replace_fcall_with_id:
|
|
|
|
|
if (type == AST_FCALL) {
|
|
|
|
|
delete_children();
|
|
|
|
|
type = AST_IDENTIFIER;
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
str = prefix_id(prefix, "$result");
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
if (type == AST_TCALL)
|
|
|
|
|
str = "";
|
|
|
|
|
did_something = true;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-19 01:30:07 +02:00
|
|
|
replace_fcall_later:;
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
// perform const folding when activated
|
2014-08-18 00:02:30 +02:00
|
|
|
if (const_fold)
|
2013-01-05 11:13:26 +01:00
|
|
|
{
|
2013-12-05 13:26:17 +01:00
|
|
|
bool string_op;
|
2013-06-10 13:56:03 +02:00
|
|
|
std::vector<RTLIL::State> tmp_bits;
|
2013-01-05 11:13:26 +01:00
|
|
|
RTLIL::Const (*const_func)(const RTLIL::Const&, const RTLIL::Const&, bool, bool, int);
|
|
|
|
|
RTLIL::Const dummy_arg;
|
|
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case AST_IDENTIFIER:
|
2018-03-09 13:47:11 +01:00
|
|
|
if (current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM || current_scope[str]->type == AST_ENUM_ITEM)) {
|
2013-11-07 14:08:53 +01:00
|
|
|
if (current_scope[str]->children[0]->type == AST_CONSTANT) {
|
|
|
|
|
if (children.size() != 0 && children[0]->type == AST_RANGE && children[0]->range_valid) {
|
2013-01-05 11:13:26 +01:00
|
|
|
std::vector<RTLIL::State> data;
|
2014-07-28 16:45:26 +02:00
|
|
|
bool param_upto = current_scope[str]->range_valid && current_scope[str]->range_swapped;
|
|
|
|
|
int param_offset = current_scope[str]->range_valid ? current_scope[str]->range_right : 0;
|
|
|
|
|
int param_width = current_scope[str]->range_valid ? current_scope[str]->range_left - current_scope[str]->range_right + 1 :
|
2014-10-10 16:59:44 +02:00
|
|
|
GetSize(current_scope[str]->children[0]->bits);
|
2014-07-28 16:45:26 +02:00
|
|
|
int tmp_range_left = children[0]->range_left, tmp_range_right = children[0]->range_right;
|
|
|
|
|
if (param_upto) {
|
|
|
|
|
tmp_range_left = (param_width + 2*param_offset) - children[0]->range_right - 1;
|
|
|
|
|
tmp_range_right = (param_width + 2*param_offset) - children[0]->range_left - 1;
|
|
|
|
|
}
|
2024-01-25 07:28:15 +01:00
|
|
|
AstNode *member_node = get_struct_member();
|
2023-02-28 18:45:55 +01:00
|
|
|
int chunk_offset = member_node ? member_node->range_right : 0;
|
|
|
|
|
log_assert(!(chunk_offset && param_upto));
|
2014-07-28 16:45:26 +02:00
|
|
|
for (int i = tmp_range_right; i <= tmp_range_left; i++) {
|
|
|
|
|
int index = i - param_offset;
|
|
|
|
|
if (0 <= index && index < param_width)
|
2023-02-28 18:45:55 +01:00
|
|
|
data.push_back(current_scope[str]->children[0]->bits[chunk_offset + index]);
|
2014-07-28 16:45:26 +02:00
|
|
|
else
|
|
|
|
|
data.push_back(RTLIL::State::Sx);
|
|
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
newNode = mkconst_bits(data, false);
|
2013-11-07 14:08:53 +01:00
|
|
|
} else
|
|
|
|
|
if (children.size() == 0)
|
|
|
|
|
newNode = current_scope[str]->children[0]->clone();
|
2014-06-14 11:27:05 +02:00
|
|
|
} else
|
|
|
|
|
if (current_scope[str]->children[0]->isConst())
|
|
|
|
|
newNode = current_scope[str]->children[0]->clone();
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case AST_BIT_NOT:
|
|
|
|
|
if (children[0]->type == AST_CONSTANT) {
|
2013-11-04 15:37:09 +01:00
|
|
|
RTLIL::Const y = RTLIL::const_not(children[0]->bitsAsConst(width_hint, sign_hint), dummy_arg, sign_hint, false, width_hint);
|
2024-10-09 19:39:45 +02:00
|
|
|
newNode = mkconst_bits(y.to_bits(), sign_hint);
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
break;
|
2013-12-05 13:09:41 +01:00
|
|
|
case AST_TO_SIGNED:
|
|
|
|
|
case AST_TO_UNSIGNED:
|
|
|
|
|
if (children[0]->type == AST_CONSTANT) {
|
|
|
|
|
RTLIL::Const y = children[0]->bitsAsConst(width_hint, sign_hint);
|
2024-10-09 19:39:45 +02:00
|
|
|
newNode = mkconst_bits(y.to_bits(), type == AST_TO_SIGNED);
|
2013-12-05 13:09:41 +01:00
|
|
|
}
|
|
|
|
|
break;
|
2013-01-05 11:13:26 +01:00
|
|
|
if (0) { case AST_BIT_AND: const_func = RTLIL::const_and; }
|
|
|
|
|
if (0) { case AST_BIT_OR: const_func = RTLIL::const_or; }
|
|
|
|
|
if (0) { case AST_BIT_XOR: const_func = RTLIL::const_xor; }
|
|
|
|
|
if (0) { case AST_BIT_XNOR: const_func = RTLIL::const_xnor; }
|
|
|
|
|
if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
|
2013-11-04 15:37:09 +01:00
|
|
|
RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint),
|
|
|
|
|
children[1]->bitsAsConst(width_hint, sign_hint), sign_hint, sign_hint, width_hint);
|
2024-10-09 19:39:45 +02:00
|
|
|
newNode = mkconst_bits(y.to_bits(), sign_hint);
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
if (0) { case AST_REDUCE_AND: const_func = RTLIL::const_reduce_and; }
|
|
|
|
|
if (0) { case AST_REDUCE_OR: const_func = RTLIL::const_reduce_or; }
|
|
|
|
|
if (0) { case AST_REDUCE_XOR: const_func = RTLIL::const_reduce_xor; }
|
|
|
|
|
if (0) { case AST_REDUCE_XNOR: const_func = RTLIL::const_reduce_xnor; }
|
|
|
|
|
if (0) { case AST_REDUCE_BOOL: const_func = RTLIL::const_reduce_bool; }
|
2013-06-10 13:19:04 +02:00
|
|
|
if (children[0]->type == AST_CONSTANT) {
|
2013-11-04 15:37:09 +01:00
|
|
|
RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), dummy_arg, false, false, -1);
|
2024-10-09 19:39:45 +02:00
|
|
|
newNode = mkconst_bits(y.to_bits(), false);
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case AST_LOGIC_NOT:
|
|
|
|
|
if (children[0]->type == AST_CONSTANT) {
|
|
|
|
|
RTLIL::Const y = RTLIL::const_logic_not(RTLIL::Const(children[0]->bits), dummy_arg, children[0]->is_signed, false, -1);
|
2024-10-09 19:39:45 +02:00
|
|
|
newNode = mkconst_bits(y.to_bits(), false);
|
2014-06-14 11:27:05 +02:00
|
|
|
} else
|
|
|
|
|
if (children[0]->isConst()) {
|
|
|
|
|
newNode = mkconst_int(children[0]->asReal(sign_hint) == 0, false, 1);
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
if (0) { case AST_LOGIC_AND: const_func = RTLIL::const_logic_and; }
|
|
|
|
|
if (0) { case AST_LOGIC_OR: const_func = RTLIL::const_logic_or; }
|
|
|
|
|
if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
|
|
|
|
|
RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), RTLIL::Const(children[1]->bits),
|
|
|
|
|
children[0]->is_signed, children[1]->is_signed, -1);
|
2024-10-09 19:39:45 +02:00
|
|
|
newNode = mkconst_bits(y.to_bits(), false);
|
2014-06-14 11:27:05 +02:00
|
|
|
} else
|
|
|
|
|
if (children[0]->isConst() && children[1]->isConst()) {
|
|
|
|
|
if (type == AST_LOGIC_AND)
|
|
|
|
|
newNode = mkconst_int((children[0]->asReal(sign_hint) != 0) && (children[1]->asReal(sign_hint) != 0), false, 1);
|
|
|
|
|
else
|
|
|
|
|
newNode = mkconst_int((children[0]->asReal(sign_hint) != 0) || (children[1]->asReal(sign_hint) != 0), false, 1);
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
if (0) { case AST_SHIFT_LEFT: const_func = RTLIL::const_shl; }
|
|
|
|
|
if (0) { case AST_SHIFT_RIGHT: const_func = RTLIL::const_shr; }
|
|
|
|
|
if (0) { case AST_SHIFT_SLEFT: const_func = RTLIL::const_sshl; }
|
|
|
|
|
if (0) { case AST_SHIFT_SRIGHT: const_func = RTLIL::const_sshr; }
|
2025-06-16 22:55:24 +02:00
|
|
|
if (0) { case AST_POW: const_func = RTLIL::const_pow; }
|
2013-01-05 11:13:26 +01:00
|
|
|
if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
|
2013-11-04 15:37:09 +01:00
|
|
|
RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint),
|
2013-11-08 11:06:11 +01:00
|
|
|
RTLIL::Const(children[1]->bits), sign_hint, type == AST_POW ? children[1]->is_signed : false, width_hint);
|
2024-10-09 19:39:45 +02:00
|
|
|
newNode = mkconst_bits(y.to_bits(), sign_hint);
|
2014-06-14 11:27:05 +02:00
|
|
|
} else
|
|
|
|
|
if (type == AST_POW && children[0]->isConst() && children[1]->isConst()) {
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_REALVALUE);
|
2014-06-14 11:27:05 +02:00
|
|
|
newNode->realvalue = pow(children[0]->asReal(sign_hint), children[1]->asReal(sign_hint));
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
break;
|
2013-12-27 13:50:08 +01:00
|
|
|
if (0) { case AST_LT: const_func = RTLIL::const_lt; }
|
|
|
|
|
if (0) { case AST_LE: const_func = RTLIL::const_le; }
|
|
|
|
|
if (0) { case AST_EQ: const_func = RTLIL::const_eq; }
|
|
|
|
|
if (0) { case AST_NE: const_func = RTLIL::const_ne; }
|
|
|
|
|
if (0) { case AST_EQX: const_func = RTLIL::const_eqx; }
|
|
|
|
|
if (0) { case AST_NEX: const_func = RTLIL::const_nex; }
|
|
|
|
|
if (0) { case AST_GE: const_func = RTLIL::const_ge; }
|
|
|
|
|
if (0) { case AST_GT: const_func = RTLIL::const_gt; }
|
2013-01-05 11:13:26 +01:00
|
|
|
if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
|
2015-10-25 19:30:49 +01:00
|
|
|
int cmp_width = max(children[0]->bits.size(), children[1]->bits.size());
|
2013-11-04 15:37:09 +01:00
|
|
|
bool cmp_signed = children[0]->is_signed && children[1]->is_signed;
|
|
|
|
|
RTLIL::Const y = const_func(children[0]->bitsAsConst(cmp_width, cmp_signed),
|
|
|
|
|
children[1]->bitsAsConst(cmp_width, cmp_signed), cmp_signed, cmp_signed, 1);
|
2024-10-09 19:39:45 +02:00
|
|
|
newNode = mkconst_bits(y.to_bits(), false);
|
2014-06-14 11:27:05 +02:00
|
|
|
} else
|
|
|
|
|
if (children[0]->isConst() && children[1]->isConst()) {
|
2014-06-17 12:47:51 +02:00
|
|
|
bool cmp_signed = (children[0]->type == AST_REALVALUE || children[0]->is_signed) && (children[1]->type == AST_REALVALUE || children[1]->is_signed);
|
2014-06-14 11:27:05 +02:00
|
|
|
switch (type) {
|
2014-06-17 12:47:51 +02:00
|
|
|
case AST_LT: newNode = mkconst_int(children[0]->asReal(cmp_signed) < children[1]->asReal(cmp_signed), false, 1); break;
|
|
|
|
|
case AST_LE: newNode = mkconst_int(children[0]->asReal(cmp_signed) <= children[1]->asReal(cmp_signed), false, 1); break;
|
|
|
|
|
case AST_EQ: newNode = mkconst_int(children[0]->asReal(cmp_signed) == children[1]->asReal(cmp_signed), false, 1); break;
|
|
|
|
|
case AST_NE: newNode = mkconst_int(children[0]->asReal(cmp_signed) != children[1]->asReal(cmp_signed), false, 1); break;
|
|
|
|
|
case AST_EQX: newNode = mkconst_int(children[0]->asReal(cmp_signed) == children[1]->asReal(cmp_signed), false, 1); break;
|
|
|
|
|
case AST_NEX: newNode = mkconst_int(children[0]->asReal(cmp_signed) != children[1]->asReal(cmp_signed), false, 1); break;
|
|
|
|
|
case AST_GE: newNode = mkconst_int(children[0]->asReal(cmp_signed) >= children[1]->asReal(cmp_signed), false, 1); break;
|
|
|
|
|
case AST_GT: newNode = mkconst_int(children[0]->asReal(cmp_signed) > children[1]->asReal(cmp_signed), false, 1); break;
|
2014-06-14 11:27:05 +02:00
|
|
|
default: log_abort();
|
|
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
if (0) { case AST_ADD: const_func = RTLIL::const_add; }
|
|
|
|
|
if (0) { case AST_SUB: const_func = RTLIL::const_sub; }
|
|
|
|
|
if (0) { case AST_MUL: const_func = RTLIL::const_mul; }
|
|
|
|
|
if (0) { case AST_DIV: const_func = RTLIL::const_div; }
|
|
|
|
|
if (0) { case AST_MOD: const_func = RTLIL::const_mod; }
|
2014-06-14 11:27:05 +02:00
|
|
|
if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
|
|
|
|
|
RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint),
|
|
|
|
|
children[1]->bitsAsConst(width_hint, sign_hint), sign_hint, sign_hint, width_hint);
|
2024-10-09 19:39:45 +02:00
|
|
|
newNode = mkconst_bits(y.to_bits(), sign_hint);
|
2014-06-14 11:27:05 +02:00
|
|
|
} else
|
2014-06-14 08:51:22 +02:00
|
|
|
if (children[0]->isConst() && children[1]->isConst()) {
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_REALVALUE);
|
2014-06-14 11:27:05 +02:00
|
|
|
switch (type) {
|
|
|
|
|
case AST_ADD: newNode->realvalue = children[0]->asReal(sign_hint) + children[1]->asReal(sign_hint); break;
|
|
|
|
|
case AST_SUB: newNode->realvalue = children[0]->asReal(sign_hint) - children[1]->asReal(sign_hint); break;
|
|
|
|
|
case AST_MUL: newNode->realvalue = children[0]->asReal(sign_hint) * children[1]->asReal(sign_hint); break;
|
|
|
|
|
case AST_DIV: newNode->realvalue = children[0]->asReal(sign_hint) / children[1]->asReal(sign_hint); break;
|
|
|
|
|
case AST_MOD: newNode->realvalue = fmod(children[0]->asReal(sign_hint), children[1]->asReal(sign_hint)); break;
|
|
|
|
|
default: log_abort();
|
2014-06-14 08:51:22 +02:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
break;
|
2020-04-29 14:28:04 +02:00
|
|
|
if (0) { case AST_SELFSZ: const_func = RTLIL::const_pos; }
|
2013-01-05 11:13:26 +01:00
|
|
|
if (0) { case AST_POS: const_func = RTLIL::const_pos; }
|
|
|
|
|
if (0) { case AST_NEG: const_func = RTLIL::const_neg; }
|
|
|
|
|
if (children[0]->type == AST_CONSTANT) {
|
2013-11-04 15:37:09 +01:00
|
|
|
RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint), dummy_arg, sign_hint, false, width_hint);
|
2024-10-09 19:39:45 +02:00
|
|
|
newNode = mkconst_bits(y.to_bits(), sign_hint);
|
2014-06-14 11:27:05 +02:00
|
|
|
} else
|
|
|
|
|
if (children[0]->isConst()) {
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_REALVALUE);
|
2020-04-29 14:28:04 +02:00
|
|
|
if (type == AST_NEG)
|
2014-06-14 11:27:05 +02:00
|
|
|
newNode->realvalue = -children[0]->asReal(sign_hint);
|
2020-04-29 14:28:04 +02:00
|
|
|
else
|
|
|
|
|
newNode->realvalue = +children[0]->asReal(sign_hint);
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case AST_TERNARY:
|
2014-06-16 15:12:24 +02:00
|
|
|
if (children[0]->isConst())
|
|
|
|
|
{
|
2021-02-12 20:25:34 +01:00
|
|
|
auto pair = get_tern_choice();
|
|
|
|
|
AstNode *choice = pair.first;
|
|
|
|
|
AstNode *not_choice = pair.second;
|
2014-06-16 15:12:24 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
if (choice != nullptr) {
|
2014-06-14 11:27:05 +02:00
|
|
|
if (choice->type == AST_CONSTANT) {
|
2014-06-16 15:12:24 +02:00
|
|
|
int other_width_hint = width_hint;
|
|
|
|
|
bool other_sign_hint = sign_hint, other_real = false;
|
|
|
|
|
not_choice->detectSignWidth(other_width_hint, other_sign_hint, &other_real);
|
|
|
|
|
if (other_real) {
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_REALVALUE);
|
2014-06-25 10:05:36 +02:00
|
|
|
choice->detectSignWidth(width_hint, sign_hint);
|
2014-06-16 15:12:24 +02:00
|
|
|
newNode->realvalue = choice->asReal(sign_hint);
|
|
|
|
|
} else {
|
|
|
|
|
RTLIL::Const y = choice->bitsAsConst(width_hint, sign_hint);
|
2024-10-09 19:39:45 +02:00
|
|
|
if (choice->is_string && y.size() % 8 == 0 && sign_hint == false)
|
|
|
|
|
newNode = mkconst_str(y.to_bits());
|
2014-06-16 15:12:24 +02:00
|
|
|
else
|
2024-10-09 19:39:45 +02:00
|
|
|
newNode = mkconst_bits(y.to_bits(), sign_hint);
|
2014-06-16 15:12:24 +02:00
|
|
|
}
|
2014-06-14 11:27:05 +02:00
|
|
|
} else
|
|
|
|
|
if (choice->isConst()) {
|
|
|
|
|
newNode = choice->clone();
|
|
|
|
|
}
|
2013-11-08 04:44:09 +01:00
|
|
|
} else if (children[1]->type == AST_CONSTANT && children[2]->type == AST_CONSTANT) {
|
|
|
|
|
RTLIL::Const a = children[1]->bitsAsConst(width_hint, sign_hint);
|
|
|
|
|
RTLIL::Const b = children[2]->bitsAsConst(width_hint, sign_hint);
|
2024-10-09 19:39:45 +02:00
|
|
|
log_assert(a.size() == b.size());
|
2024-11-29 00:31:34 +01:00
|
|
|
for (auto i = 0; i < a.size(); i++)
|
2024-10-09 19:39:45 +02:00
|
|
|
if (a[i] != b[i])
|
|
|
|
|
a.bits()[i] = RTLIL::State::Sx;
|
|
|
|
|
newNode = mkconst_bits(a.to_bits(), sign_hint);
|
2014-06-16 15:05:37 +02:00
|
|
|
} else if (children[1]->isConst() && children[2]->isConst()) {
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_REALVALUE);
|
2014-06-16 15:05:37 +02:00
|
|
|
if (children[1]->asReal(sign_hint) == children[2]->asReal(sign_hint))
|
|
|
|
|
newNode->realvalue = children[1]->asReal(sign_hint);
|
|
|
|
|
else
|
|
|
|
|
// IEEE Std 1800-2012 Sec. 11.4.11 states that the entry in Table 7-1 for
|
|
|
|
|
// the data type in question should be returned if the ?: is ambiguous. The
|
|
|
|
|
// value in Table 7-1 for the 'real' type is 0.0.
|
|
|
|
|
newNode->realvalue = 0.0;
|
2013-11-04 16:46:14 +01:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
break;
|
2020-06-20 04:09:43 +02:00
|
|
|
case AST_CAST_SIZE:
|
|
|
|
|
if (children.at(0)->type == AST_CONSTANT && children.at(1)->type == AST_CONSTANT) {
|
|
|
|
|
int width = children[0]->bitsAsConst().as_int();
|
2021-03-03 20:36:19 +01:00
|
|
|
RTLIL::Const val;
|
|
|
|
|
if (children[1]->is_unsized)
|
|
|
|
|
val = children[1]->bitsAsUnsizedConst(width);
|
|
|
|
|
else
|
|
|
|
|
val = children[1]->bitsAsConst(width);
|
2024-10-09 19:39:45 +02:00
|
|
|
newNode = mkconst_bits(val.to_bits(), children[1]->is_signed);
|
2020-06-20 04:09:43 +02:00
|
|
|
}
|
|
|
|
|
break;
|
2013-06-10 13:56:03 +02:00
|
|
|
case AST_CONCAT:
|
2013-12-05 13:26:17 +01:00
|
|
|
string_op = !children.empty();
|
2013-06-10 13:56:03 +02:00
|
|
|
for (auto it = children.begin(); it != children.end(); it++) {
|
|
|
|
|
if ((*it)->type != AST_CONSTANT)
|
|
|
|
|
goto not_const;
|
2013-12-05 13:26:17 +01:00
|
|
|
if (!(*it)->is_string)
|
|
|
|
|
string_op = false;
|
2013-06-10 13:56:03 +02:00
|
|
|
tmp_bits.insert(tmp_bits.end(), (*it)->bits.begin(), (*it)->bits.end());
|
|
|
|
|
}
|
2013-12-05 13:26:17 +01:00
|
|
|
newNode = string_op ? mkconst_str(tmp_bits) : mkconst_bits(tmp_bits, false);
|
2013-11-07 14:08:53 +01:00
|
|
|
break;
|
|
|
|
|
case AST_REPLICATE:
|
|
|
|
|
if (children.at(0)->type != AST_CONSTANT || children.at(1)->type != AST_CONSTANT)
|
|
|
|
|
goto not_const;
|
|
|
|
|
for (int i = 0; i < children[0]->bitsAsConst().as_int(); i++)
|
|
|
|
|
tmp_bits.insert(tmp_bits.end(), children.at(1)->bits.begin(), children.at(1)->bits.end());
|
2013-12-05 13:26:17 +01:00
|
|
|
newNode = children.at(1)->is_string ? mkconst_str(tmp_bits) : mkconst_bits(tmp_bits, false);
|
2013-06-10 13:56:03 +02:00
|
|
|
break;
|
2013-01-05 11:13:26 +01:00
|
|
|
default:
|
2013-06-10 13:56:03 +02:00
|
|
|
not_const:
|
2013-01-05 11:13:26 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if any of the above set 'newNode' -> use 'newNode' as template to update 'this'
|
|
|
|
|
if (newNode) {
|
|
|
|
|
apply_newNode:
|
|
|
|
|
// fprintf(stderr, "----\n");
|
|
|
|
|
// dumpAst(stderr, "- ");
|
|
|
|
|
// newNode->dumpAst(stderr, "+ ");
|
2025-06-16 22:55:24 +02:00
|
|
|
log_assert(newNode != nullptr);
|
|
|
|
|
// newNode->null_check();
|
2013-01-05 11:13:26 +01:00
|
|
|
newNode->filename = filename;
|
2020-02-23 08:19:52 +01:00
|
|
|
newNode->location = location;
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode->cloneInto(*this);
|
2023-04-05 11:00:07 +02:00
|
|
|
fixup_hierarchy_flags();
|
2013-01-05 11:13:26 +01:00
|
|
|
did_something = true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-20 20:25:20 +01:00
|
|
|
if (!did_something)
|
|
|
|
|
basic_prep = true;
|
|
|
|
|
|
2015-02-13 12:33:12 +01:00
|
|
|
recursion_counter--;
|
2013-01-05 11:13:26 +01:00
|
|
|
return did_something;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-01 01:23:36 +01:00
|
|
|
void AstNode::replace_result_wire_name_in_function(const std::string &from, const std::string &to)
|
2013-11-20 11:05:58 +01:00
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children)
|
2021-01-01 01:23:36 +01:00
|
|
|
child->replace_result_wire_name_in_function(from, to);
|
|
|
|
|
if (str == from && type != AST_FCALL && type != AST_TCALL)
|
|
|
|
|
str = to;
|
2013-11-20 11:05:58 +01:00
|
|
|
}
|
|
|
|
|
|
2014-10-26 20:33:10 +01:00
|
|
|
// replace a readmem[bh] TCALL ast node with a block of memory assignments
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init)
|
2014-10-26 20:33:10 +01:00
|
|
|
{
|
2015-09-25 13:49:48 +02:00
|
|
|
int mem_width, mem_size, addr_bits;
|
|
|
|
|
memory->meminfo(mem_width, mem_size, addr_bits);
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto block = std::make_unique<AstNode>(AST_BLOCK);
|
2014-10-26 20:33:10 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
AstNode* meminit = nullptr;
|
2015-08-14 22:22:17 +02:00
|
|
|
int next_meminit_cursor=0;
|
2015-07-31 10:40:09 +02:00
|
|
|
vector<State> meminit_bits;
|
2021-05-21 02:27:06 +02:00
|
|
|
vector<State> en_bits;
|
2015-08-14 22:22:17 +02:00
|
|
|
int meminit_size=0;
|
2015-07-31 10:40:09 +02:00
|
|
|
|
2021-05-21 02:27:06 +02:00
|
|
|
for (int i = 0; i < mem_width; i++)
|
|
|
|
|
en_bits.push_back(State::S1);
|
|
|
|
|
|
2014-10-26 20:33:10 +01:00
|
|
|
std::ifstream f;
|
2020-02-01 02:10:51 +01:00
|
|
|
f.open(mem_filename.c_str());
|
|
|
|
|
if (f.fail()) {
|
2020-02-06 14:10:29 +01:00
|
|
|
#ifdef _WIN32
|
|
|
|
|
char slash = '\\';
|
|
|
|
|
#else
|
|
|
|
|
char slash = '/';
|
|
|
|
|
#endif
|
|
|
|
|
std::string path = filename.substr(0, filename.find_last_of(slash)+1);
|
2020-02-01 02:10:51 +01:00
|
|
|
f.open(path + mem_filename.c_str());
|
|
|
|
|
yosys_input_files.insert(path + mem_filename);
|
|
|
|
|
} else {
|
|
|
|
|
yosys_input_files.insert(mem_filename);
|
|
|
|
|
}
|
2020-02-03 14:30:33 +01:00
|
|
|
if (f.fail() || GetSize(mem_filename) == 0)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Can not open file `%s` for %s.\n", mem_filename.c_str(), str.c_str());
|
2014-10-26 20:33:10 +01:00
|
|
|
|
2014-10-26 23:29:36 +01:00
|
|
|
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;
|
2015-10-25 19:30:49 +01:00
|
|
|
int range_min = min(range_left, range_right), range_max = max(range_left, range_right);
|
2014-10-26 23:29:36 +01:00
|
|
|
|
|
|
|
|
if (start_addr < 0)
|
|
|
|
|
start_addr = range_min;
|
|
|
|
|
|
|
|
|
|
if (finish_addr < 0)
|
2015-09-25 13:49:48 +02:00
|
|
|
finish_addr = range_max + 1;
|
2014-10-26 20:33:10 +01:00
|
|
|
|
|
|
|
|
bool in_comment = false;
|
2014-10-26 23:29:36 +01:00
|
|
|
int increment = start_addr <= finish_addr ? +1 : -1;
|
|
|
|
|
int cursor = start_addr;
|
2014-10-26 20:33:10 +01:00
|
|
|
|
|
|
|
|
while (!f.eof())
|
|
|
|
|
{
|
|
|
|
|
std::string line, token;
|
|
|
|
|
std::getline(f, line);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < GetSize(line); i++) {
|
2019-08-07 21:20:08 +02:00
|
|
|
if (in_comment && line.compare(i, 2, "*/") == 0) {
|
2014-10-26 20:33:10 +01:00
|
|
|
line[i] = ' ';
|
|
|
|
|
line[i+1] = ' ';
|
|
|
|
|
in_comment = false;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2019-08-07 21:20:08 +02:00
|
|
|
if (!in_comment && line.compare(i, 2, "/*") == 0)
|
2014-10-26 20:33:10 +01:00
|
|
|
in_comment = true;
|
|
|
|
|
if (in_comment)
|
|
|
|
|
line[i] = ' ';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
token = next_token(line, " \t\r\n");
|
2019-08-07 21:20:08 +02:00
|
|
|
if (token.empty() || token.compare(0, 2, "//") == 0)
|
2014-10-26 20:33:10 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (token[0] == '@') {
|
|
|
|
|
token = token.substr(1);
|
|
|
|
|
const char *nptr = token.c_str();
|
|
|
|
|
char *endptr;
|
|
|
|
|
cursor = strtol(nptr, &endptr, 16);
|
|
|
|
|
if (!*nptr || *endptr)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Can not parse address `%s` for %s.\n", nptr, str.c_str());
|
2014-10-26 20:33:10 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
VERILOG_FRONTEND::ConstParser p{mem_filename, std::nullopt};
|
|
|
|
|
auto value = p.const2ast(stringf("%d'%c", mem_width, is_readmemh ? 'h' : 'b') + token);
|
2014-10-26 20:33:10 +01:00
|
|
|
|
2015-07-31 10:40:09 +02:00
|
|
|
if (unconditional_init)
|
|
|
|
|
{
|
|
|
|
|
if (meminit == nullptr || cursor != next_meminit_cursor)
|
|
|
|
|
{
|
|
|
|
|
if (meminit != nullptr) {
|
|
|
|
|
meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false);
|
2021-05-21 02:27:06 +02:00
|
|
|
meminit->children[3] = AstNode::mkconst_int(meminit_size, false);
|
2015-07-31 10:40:09 +02:00
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto meminit_owned = std::make_unique<AstNode>(AST_MEMINIT);
|
|
|
|
|
meminit = meminit_owned.get();
|
2015-07-31 10:40:09 +02:00
|
|
|
meminit->children.push_back(AstNode::mkconst_int(cursor, false));
|
|
|
|
|
meminit->children.push_back(nullptr);
|
2021-05-21 02:27:06 +02:00
|
|
|
meminit->children.push_back(AstNode::mkconst_bits(en_bits, false));
|
2015-07-31 10:40:09 +02:00
|
|
|
meminit->children.push_back(nullptr);
|
|
|
|
|
meminit->str = memory->str;
|
|
|
|
|
meminit->id2ast = memory;
|
|
|
|
|
meminit_bits.clear();
|
|
|
|
|
meminit_size = 0;
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
current_ast_mod->children.push_back(std::move(meminit_owned));
|
2015-07-31 10:40:09 +02:00
|
|
|
next_meminit_cursor = cursor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
meminit_size++;
|
|
|
|
|
next_meminit_cursor++;
|
|
|
|
|
meminit_bits.insert(meminit_bits.end(), value->bits.begin(), value->bits.end());
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
block->children.push_back(std::make_unique<AstNode>(AST_ASSIGN_EQ, std::make_unique<AstNode>(AST_IDENTIFIER, std::make_unique<AstNode>(AST_RANGE, AstNode::mkconst_int(cursor, false))), std::move(value)));
|
2015-07-31 10:40:09 +02:00
|
|
|
block->children.back()->children[0]->str = memory->str;
|
|
|
|
|
block->children.back()->children[0]->id2ast = memory;
|
2018-09-18 00:23:40 +02:00
|
|
|
block->children.back()->children[0]->was_checked = true;
|
2015-07-31 10:40:09 +02:00
|
|
|
}
|
2014-10-26 20:33:10 +01:00
|
|
|
|
|
|
|
|
cursor += increment;
|
2016-08-20 13:47:46 +02:00
|
|
|
if ((cursor == finish_addr+increment) || (increment > 0 && cursor > range_max) || (increment < 0 && cursor < range_min))
|
|
|
|
|
break;
|
2014-10-26 20:33:10 +01:00
|
|
|
}
|
|
|
|
|
|
2016-08-20 13:47:46 +02:00
|
|
|
if ((cursor == finish_addr+increment) || (increment > 0 && cursor > range_max) || (increment < 0 && cursor < range_min))
|
2014-10-26 20:33:10 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-31 10:40:09 +02:00
|
|
|
if (meminit != nullptr) {
|
|
|
|
|
meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false);
|
2021-05-21 02:27:06 +02:00
|
|
|
meminit->children[3] = AstNode::mkconst_int(meminit_size, false);
|
2015-07-31 10:40:09 +02:00
|
|
|
}
|
|
|
|
|
|
2014-10-26 20:33:10 +01:00
|
|
|
return block;
|
|
|
|
|
}
|
|
|
|
|
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
// 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)
|
2013-01-05 11:13:26 +01:00
|
|
|
{
|
2021-08-03 02:42:34 +02:00
|
|
|
if (type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE || type == AST_PREFIX) {
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
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;
|
2020-08-01 04:13:05 +02:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-01 04:13:05 +02:00
|
|
|
|
|
|
|
|
}
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
}
|
2020-08-01 04:13:05 +02:00
|
|
|
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
auto prefix_node = [&prefix](AstNode* child) {
|
|
|
|
|
if (child->str.empty()) return;
|
|
|
|
|
std::string new_name = prefix_id(prefix, child->str);
|
2020-08-01 04:13:05 +02:00
|
|
|
if (child->type == AST_FUNCTION)
|
2021-01-01 01:23:36 +01:00
|
|
|
child->replace_result_wire_name_in_function(child->str, new_name);
|
2020-08-01 04:13:05 +02:00
|
|
|
else
|
|
|
|
|
child->str = new_name;
|
|
|
|
|
current_scope[new_name] = child;
|
|
|
|
|
};
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
for (size_t i = 0; i < children.size(); i++) {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto* child = children[i].get();
|
2020-08-01 04:13:05 +02:00
|
|
|
|
|
|
|
|
switch (child->type) {
|
|
|
|
|
case AST_WIRE:
|
|
|
|
|
case AST_MEMORY:
|
2024-01-04 17:22:07 +01:00
|
|
|
case AST_STRUCT:
|
|
|
|
|
case AST_UNION:
|
2020-08-01 04:13:05 +02:00
|
|
|
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:
|
2020-02-03 07:12:24 +01:00
|
|
|
current_scope[child->str] = child;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& enode : child->children){
|
2020-01-16 23:05:45 +01:00
|
|
|
log_assert(enode->type == AST_ENUM_ITEM);
|
2025-06-16 22:55:24 +02:00
|
|
|
prefix_node(enode.get());
|
2020-01-16 23:05:45 +01:00
|
|
|
}
|
2020-08-01 04:13:05 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2020-01-16 23:05:45 +01:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < children.size(); i++) {
|
2025-06-16 22:55:24 +02:00
|
|
|
auto& child = children[i];
|
2021-08-03 02:42:34 +02:00
|
|
|
// 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;
|
2019-03-19 01:34:21 +01:00
|
|
|
continue;
|
2021-08-03 02:42:34 +02:00
|
|
|
}
|
2020-08-01 04:13:05 +02:00
|
|
|
// functions/tasks may reference wires, constants, etc. in this scope
|
|
|
|
|
if (child->type == AST_FUNCTION || child->type == AST_TASK)
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
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;
|
2019-09-19 21:43:13 +02:00
|
|
|
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
child->expand_genblock(prefix);
|
|
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
// 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)
|
2013-01-05 11:13:26 +01:00
|
|
|
{
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
switch (type) {
|
|
|
|
|
case AST_GENIF:
|
|
|
|
|
case AST_GENFOR:
|
|
|
|
|
case AST_GENCASE:
|
|
|
|
|
// seeing a proper generate control flow construct increments the
|
|
|
|
|
// counter once
|
|
|
|
|
++counter;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children)
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
child->label_genblks(existing, counter);
|
|
|
|
|
break;
|
2014-08-05 12:15:53 +02:00
|
|
|
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
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;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children)
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
child->label_genblks(existing_local, counter_local);
|
|
|
|
|
break;
|
2014-08-05 12:15:53 +02:00
|
|
|
}
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
// track names which could conflict with implicit genblk names
|
|
|
|
|
if (str.rfind("\\genblk", 0) == 0)
|
|
|
|
|
existing.insert(str);
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children)
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
child->label_genblks(existing, counter);
|
|
|
|
|
break;
|
2014-08-05 12:15:53 +02:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
|
2014-06-17 21:39:25 +02:00
|
|
|
// helper function for mem2reg_as_needed_pass1
|
2014-12-29 03:11:50 +01:00
|
|
|
static void mark_memories_assign_lhs_complex(dict<AstNode*, pool<std::string>> &mem2reg_places,
|
|
|
|
|
dict<AstNode*, uint32_t> &mem2reg_candidates, AstNode *that)
|
2014-06-17 21:39:25 +02:00
|
|
|
{
|
|
|
|
|
for (auto &child : that->children)
|
2025-06-16 22:55:24 +02:00
|
|
|
mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, child.get());
|
2014-06-17 21:39:25 +02:00
|
|
|
|
|
|
|
|
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))
|
2022-08-08 16:13:33 +02:00
|
|
|
mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(that->filename).c_str(), that->location.first_line));
|
2014-06-17 21:39:25 +02:00
|
|
|
mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_CMPLX_LHS;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
// find memories that should be replaced by registers
|
2014-12-29 03:11:50 +01:00
|
|
|
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)
|
2013-01-05 11:13:26 +01:00
|
|
|
{
|
2013-11-21 13:49:00 +01:00
|
|
|
uint32_t children_flags = 0;
|
2019-03-12 20:12:02 +01:00
|
|
|
int lhs_children_counter = 0;
|
2013-11-21 13:49:00 +01:00
|
|
|
|
2019-09-20 12:39:15 +02:00
|
|
|
if (type == AST_TYPEDEF)
|
|
|
|
|
return; // don't touch content of typedefs
|
|
|
|
|
|
2013-11-21 13:49:00 +01:00
|
|
|
if (type == AST_ASSIGN || type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ)
|
|
|
|
|
{
|
2014-06-17 21:39:25 +02:00
|
|
|
// mark all memories that are used in a complex expression on the left side of an assignment
|
|
|
|
|
for (auto &lhs_child : children[0]->children)
|
2025-06-16 22:55:24 +02:00
|
|
|
mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, lhs_child.get());
|
2014-06-17 21:39:25 +02:00
|
|
|
|
2013-11-21 13:49:00 +01:00
|
|
|
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))
|
2022-08-08 16:13:33 +02:00
|
|
|
mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line));
|
2013-11-21 13:49:00 +01:00
|
|
|
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))
|
2022-08-08 16:13:33 +02:00
|
|
|
mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line));
|
2013-11-21 13:49:00 +01:00
|
|
|
proc_flags[mem] |= AstNode::MEM2REG_FL_EQ1;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-12 20:09:47 +01:00
|
|
|
// 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;
|
|
|
|
|
}
|
2019-03-01 22:35:09 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-21 13:49:00 +01:00
|
|
|
// remember where this is
|
|
|
|
|
if (flags & MEM2REG_FL_INIT) {
|
|
|
|
|
if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_INIT))
|
2022-08-08 16:13:33 +02:00
|
|
|
mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line));
|
2013-11-21 13:49:00 +01:00
|
|
|
mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_INIT;
|
|
|
|
|
} else {
|
|
|
|
|
if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_ELSE))
|
2022-08-08 16:13:33 +02:00
|
|
|
mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line));
|
2013-11-21 13:49:00 +01:00
|
|
|
mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_ELSE;
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
}
|
2013-11-21 13:49:00 +01:00
|
|
|
|
2019-03-12 20:12:02 +01:00
|
|
|
lhs_children_counter = 1;
|
2013-11-21 13:49:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (type == AST_IDENTIFIER && id2ast && id2ast->type == AST_MEMORY)
|
|
|
|
|
{
|
|
|
|
|
AstNode *mem = id2ast;
|
|
|
|
|
|
2024-01-25 07:28:15 +01:00
|
|
|
if (integer < (unsigned)mem->unpacked_dimensions)
|
|
|
|
|
input_error("Insufficient number of array indices for %s.\n", log_id(str));
|
|
|
|
|
|
2013-11-21 13:49:00 +01:00
|
|
|
// flag if used after blocking assignment (in same proc)
|
|
|
|
|
if ((proc_flags[mem] & AstNode::MEM2REG_FL_EQ1) && !(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_EQ2)) {
|
2022-08-08 16:13:33 +02:00
|
|
|
mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line));
|
2013-11-21 13:49:00 +01:00
|
|
|
mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_EQ2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-05 06:11:16 +02:00
|
|
|
// 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)))
|
2013-11-21 13:49:00 +01:00
|
|
|
mem2reg_candidates[this] |= AstNode::MEM2REG_FL_FORCED;
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2023-02-13 00:25:39 +01:00
|
|
|
if ((type == AST_MODULE || type == AST_INTERFACE) && get_bool_attribute(ID::mem2reg))
|
2013-11-21 13:49:00 +01:00
|
|
|
children_flags |= AstNode::MEM2REG_FL_ALL;
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
dict<AstNode*, uint32_t> *proc_flags_p = nullptr;
|
2013-03-24 11:13:32 +01:00
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
if (type == AST_ALWAYS) {
|
2013-11-21 21:26:56 +01:00
|
|
|
int count_edge_events = 0;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children)
|
2013-01-05 11:13:26 +01:00
|
|
|
if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE)
|
2013-11-21 21:26:56 +01:00
|
|
|
count_edge_events++;
|
|
|
|
|
if (count_edge_events != 1)
|
2013-11-21 13:49:00 +01:00
|
|
|
children_flags |= AstNode::MEM2REG_FL_ASYNC;
|
2014-12-29 03:11:50 +01:00
|
|
|
proc_flags_p = new dict<AstNode*, uint32_t>;
|
2013-11-21 13:49:00 +01:00
|
|
|
}
|
2023-01-17 12:58:08 +01:00
|
|
|
else if (type == AST_INITIAL) {
|
2013-11-21 13:49:00 +01:00
|
|
|
children_flags |= AstNode::MEM2REG_FL_INIT;
|
2014-12-29 03:11:50 +01:00
|
|
|
proc_flags_p = new dict<AstNode*, uint32_t>;
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-21 13:49:00 +01:00
|
|
|
uint32_t backup_flags = flags;
|
|
|
|
|
flags |= children_flags;
|
2014-07-28 11:08:55 +02:00
|
|
|
log_assert((flags & ~0x000000ff) == 0);
|
2013-11-21 13:49:00 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children)
|
2019-03-12 20:12:02 +01:00
|
|
|
{
|
|
|
|
|
if (lhs_children_counter > 0) {
|
|
|
|
|
lhs_children_counter--;
|
|
|
|
|
if (child->children.size() && child->children[0]->type == AST_RANGE && child->children[0]->children.size()) {
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& c : child->children[0]->children) {
|
2019-03-12 20:12:02 +01:00
|
|
|
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)
|
2013-11-21 13:49:00 +01:00
|
|
|
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);
|
2019-03-12 20:12:02 +01:00
|
|
|
}
|
2013-11-21 13:49:00 +01:00
|
|
|
|
|
|
|
|
flags &= ~children_flags | backup_flags;
|
|
|
|
|
|
|
|
|
|
if (proc_flags_p) {
|
2015-01-24 12:16:46 +01:00
|
|
|
#ifndef NDEBUG
|
2013-11-21 13:49:00 +01:00
|
|
|
for (auto it : *proc_flags_p)
|
2014-02-06 22:49:14 +01:00
|
|
|
log_assert((it.second & ~0xff000000) == 0);
|
2015-01-24 12:16:46 +01:00
|
|
|
#endif
|
2013-11-21 13:49:00 +01:00
|
|
|
delete proc_flags_p;
|
|
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
|
2014-12-29 03:11:50 +01:00
|
|
|
bool AstNode::mem2reg_check(pool<AstNode*> &mem2reg_set)
|
2014-10-16 00:44:23 +02:00
|
|
|
{
|
|
|
|
|
if (type != AST_IDENTIFIER || !id2ast || !mem2reg_set.count(id2ast))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (children.empty() || children[0]->type != AST_RANGE || GetSize(children[0]->children) != 1)
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Invalid array access.\n");
|
2014-10-16 00:44:23 +02:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
void AstNode::mem2reg_remove(pool<AstNode*> &mem2reg_set)
|
2016-05-27 17:25:33 +02:00
|
|
|
{
|
|
|
|
|
log_assert(mem2reg_set.count(this) == 0);
|
|
|
|
|
|
|
|
|
|
if (mem2reg_set.count(id2ast))
|
|
|
|
|
id2ast = nullptr;
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < children.size(); i++) {
|
2025-06-16 22:55:24 +02:00
|
|
|
if (mem2reg_set.count(children[i].get()) > 0) {
|
2016-05-27 17:25:33 +02:00
|
|
|
children.erase(children.begin() + (i--));
|
|
|
|
|
} else {
|
2025-06-16 22:55:24 +02:00
|
|
|
children[i]->mem2reg_remove(mem2reg_set);
|
2016-05-27 17:25:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
// actually replace memories with registers
|
2025-06-16 22:55:24 +02:00
|
|
|
bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block, AstNode* async_block)
|
2013-01-05 11:13:26 +01:00
|
|
|
{
|
2015-07-29 16:37:08 +02:00
|
|
|
bool did_something = false;
|
|
|
|
|
|
2013-01-05 11:13:26 +01:00
|
|
|
if (type == AST_BLOCK)
|
|
|
|
|
block = this;
|
|
|
|
|
|
2016-08-21 13:23:58 +02:00
|
|
|
if (type == AST_FUNCTION || type == AST_TASK)
|
|
|
|
|
return false;
|
|
|
|
|
|
2019-09-20 12:39:15 +02:00
|
|
|
if (type == AST_TYPEDEF)
|
|
|
|
|
return false;
|
|
|
|
|
|
2019-03-02 18:58:20 +01:00
|
|
|
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);
|
2021-05-21 02:27:06 +02:00
|
|
|
log_assert(children[3]->type == AST_CONSTANT);
|
2019-03-02 18:58:20 +01:00
|
|
|
|
|
|
|
|
int cursor = children[0]->asInt(false);
|
|
|
|
|
Const data = children[1]->bitsAsConst();
|
2021-05-21 02:27:06 +02:00
|
|
|
Const en = children[2]->bitsAsConst();
|
|
|
|
|
int length = children[3]->asInt(false);
|
2019-03-02 18:58:20 +01:00
|
|
|
|
|
|
|
|
if (length != 0)
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
auto block_owned = std::make_unique<AstNode>(AST_INITIAL, std::make_unique<AstNode>(AST_BLOCK));
|
|
|
|
|
auto block = block_owned.get();
|
|
|
|
|
mod->children.push_back(std::move(block_owned));
|
|
|
|
|
block = block->children[0].get();
|
2019-03-02 18:58:20 +01:00
|
|
|
|
|
|
|
|
int wordsz = GetSize(data) / length;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < length; i++) {
|
2021-05-21 02:27:06 +02:00
|
|
|
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;
|
2025-06-16 22:55:24 +02:00
|
|
|
auto range = std::make_unique<AstNode>(AST_RANGE, AstNode::mkconst_int(cursor+i, false));
|
2021-05-21 02:27:06 +02:00
|
|
|
if (pos != 0 || epos != wordsz) {
|
|
|
|
|
int left;
|
|
|
|
|
int right;
|
2025-06-16 22:55:24 +02:00
|
|
|
auto& mrange = id2ast->children[0];
|
2021-05-21 02:27:06 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2025-06-16 22:55:24 +02:00
|
|
|
range = std::make_unique<AstNode>(AST_MULTIRANGE, std::move(range), std::make_unique<AstNode>(AST_RANGE, AstNode::mkconst_int(left, true), AstNode::mkconst_int(right, true)));
|
2021-05-21 02:27:06 +02:00
|
|
|
}
|
2025-06-16 22:55:24 +02:00
|
|
|
auto target = std::make_unique<AstNode>(AST_IDENTIFIER, std::move(range));
|
2021-05-21 02:27:06 +02:00
|
|
|
target->str = str;
|
|
|
|
|
target->id2ast = id2ast;
|
|
|
|
|
target->was_checked = true;
|
2025-06-16 22:55:24 +02:00
|
|
|
block->children.push_back(std::make_unique<AstNode>(AST_ASSIGN_EQ, std::move(target), mkconst_bits(data.extract(i*wordsz + pos, clen).to_bits(), false)));
|
2021-05-21 02:27:06 +02:00
|
|
|
pos = epos;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-02 18:58:20 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto newNode = std::make_unique<AstNode>(AST_NONE);
|
|
|
|
|
newNode->cloneInto(*this);
|
2019-03-02 18:58:20 +01:00
|
|
|
did_something = true;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
if (type == AST_ASSIGN && block == nullptr && children[0]->mem2reg_check(mem2reg_set))
|
2016-08-21 13:23:58 +02:00
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
if (async_block == nullptr) {
|
|
|
|
|
auto async_block_owned = std::make_unique<AstNode>(AST_ALWAYS, std::make_unique<AstNode>(AST_BLOCK));
|
|
|
|
|
async_block = async_block_owned.get();
|
|
|
|
|
mod->children.push_back(std::move(async_block_owned));
|
2016-08-21 13:23:58 +02:00
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto newNode = clone();
|
2016-08-21 13:23:58 +02:00
|
|
|
newNode->type = AST_ASSIGN_EQ;
|
2018-09-24 23:32:57 +02:00
|
|
|
newNode->children[0]->was_checked = true;
|
2025-06-16 22:55:24 +02:00
|
|
|
async_block->children[0]->children.push_back(std::move(newNode));
|
2016-08-21 13:23:58 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
newNode = std::make_unique<AstNode>(AST_NONE);
|
|
|
|
|
newNode->cloneInto(*this);
|
2016-08-21 13:23:58 +02:00
|
|
|
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)
|
2013-01-05 11:13:26 +01:00
|
|
|
{
|
|
|
|
|
std::stringstream sstr;
|
2022-08-08 16:13:33 +02:00
|
|
|
sstr << "$mem2reg_wr$" << children[0]->str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++);
|
2013-01-05 11:13:26 +01:00
|
|
|
std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA";
|
|
|
|
|
|
|
|
|
|
int mem_width, mem_size, addr_bits;
|
2016-11-01 23:17:43 +01:00
|
|
|
bool mem_signed = children[0]->id2ast->is_signed;
|
2013-01-05 11:13:26 +01:00
|
|
|
children[0]->id2ast->meminfo(mem_width, mem_size, addr_bits);
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto wire_addr = std::make_unique<AstNode>(AST_WIRE, std::make_unique<AstNode>(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true)));
|
2013-01-05 11:13:26 +01:00
|
|
|
wire_addr->str = id_addr;
|
|
|
|
|
wire_addr->is_reg = true;
|
2018-06-05 16:44:24 +02:00
|
|
|
wire_addr->was_checked = true;
|
2023-04-05 11:00:07 +02:00
|
|
|
wire_addr->set_attribute(ID::nosync, AstNode::mkconst_int(1, false));
|
2023-04-04 22:59:44 +02:00
|
|
|
while (wire_addr->simplify(true, 1, -1, false)) { }
|
2025-06-16 22:55:24 +02:00
|
|
|
mod->children.push_back(std::move(wire_addr));
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto wire_data = std::make_unique<AstNode>(AST_WIRE, std::make_unique<AstNode>(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
|
2013-01-05 11:13:26 +01:00
|
|
|
wire_data->str = id_data;
|
|
|
|
|
wire_data->is_reg = true;
|
2018-06-05 16:44:24 +02:00
|
|
|
wire_data->was_checked = true;
|
2016-11-01 23:17:43 +01:00
|
|
|
wire_data->is_signed = mem_signed;
|
2023-04-05 11:00:07 +02:00
|
|
|
wire_data->set_attribute(ID::nosync, AstNode::mkconst_int(1, false));
|
2023-04-04 22:59:44 +02:00
|
|
|
while (wire_data->simplify(true, 1, -1, false)) { }
|
2025-06-16 22:55:24 +02:00
|
|
|
mod->children.push_back(std::move(wire_data));
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
log_assert(block != nullptr);
|
2013-01-05 11:13:26 +01:00
|
|
|
size_t assign_idx = 0;
|
2025-06-16 22:55:24 +02:00
|
|
|
while (assign_idx < block->children.size() && block->children[assign_idx].get() != this)
|
2013-01-05 11:13:26 +01:00
|
|
|
assign_idx++;
|
2014-07-28 11:08:55 +02:00
|
|
|
log_assert(assign_idx < block->children.size());
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto assign_addr = std::make_unique<AstNode>(AST_ASSIGN_EQ, std::make_unique<AstNode>(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone());
|
2013-01-05 11:13:26 +01:00
|
|
|
assign_addr->children[0]->str = id_addr;
|
2018-09-24 23:32:57 +02:00
|
|
|
assign_addr->children[0]->was_checked = true;
|
2025-06-16 22:55:24 +02:00
|
|
|
block->children.insert(block->children.begin()+assign_idx+1, std::move(assign_addr));
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto case_node = std::make_unique<AstNode>(AST_CASE, std::make_unique<AstNode>(AST_IDENTIFIER));
|
2013-01-05 11:13:26 +01:00
|
|
|
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;
|
2025-06-16 22:55:24 +02:00
|
|
|
auto cond_node = std::make_unique<AstNode>(AST_COND, AstNode::mkconst_int(i, false, addr_bits), std::make_unique<AstNode>(AST_BLOCK));
|
|
|
|
|
auto assign_reg = std::make_unique<AstNode>(type, std::make_unique<AstNode>(AST_IDENTIFIER), std::make_unique<AstNode>(AST_IDENTIFIER));
|
2014-07-17 13:49:32 +02:00
|
|
|
if (children[0]->children.size() == 2)
|
|
|
|
|
assign_reg->children[0]->children.push_back(children[0]->children[1]->clone());
|
2013-01-05 11:13:26 +01:00
|
|
|
assign_reg->children[0]->str = stringf("%s[%d]", children[0]->str.c_str(), i);
|
|
|
|
|
assign_reg->children[1]->str = id_data;
|
2025-06-16 22:55:24 +02:00
|
|
|
cond_node->children[1]->children.push_back(std::move(assign_reg));
|
|
|
|
|
case_node->children.push_back(std::move(cond_node));
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
2023-04-05 11:00:07 +02:00
|
|
|
|
|
|
|
|
// fixup on the full hierarchy below case_node
|
|
|
|
|
case_node->fixup_hierarchy_flags(true);
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
block->children.insert(block->children.begin()+assign_idx+2, std::move(case_node));
|
2013-01-05 11:13:26 +01:00
|
|
|
|
|
|
|
|
children[0]->delete_children();
|
|
|
|
|
children[0]->range_valid = false;
|
2025-06-16 22:55:24 +02:00
|
|
|
children[0]->id2ast = nullptr;
|
2013-01-05 11:13:26 +01:00
|
|
|
children[0]->str = id_data;
|
2013-03-24 10:42:08 +01:00
|
|
|
type = AST_ASSIGN_EQ;
|
2018-09-18 00:23:40 +02:00
|
|
|
children[0]->was_checked = true;
|
2015-07-29 16:37:08 +02:00
|
|
|
|
2023-04-05 11:00:07 +02:00
|
|
|
fixup_hierarchy_flags();
|
2015-07-29 16:37:08 +02:00
|
|
|
did_something = true;
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
|
2014-10-16 00:44:23 +02:00
|
|
|
if (mem2reg_check(mem2reg_set))
|
2013-01-05 11:13:26 +01:00
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> bit_part_sel = nullptr;
|
2014-07-17 13:49:32 +02:00
|
|
|
if (children.size() == 2)
|
|
|
|
|
bit_part_sel = children[1]->clone();
|
|
|
|
|
|
2013-11-21 13:49:00 +01:00
|
|
|
if (children[0]->children[0]->type == AST_CONSTANT)
|
|
|
|
|
{
|
|
|
|
|
int id = children[0]->children[0]->integer;
|
2021-05-27 00:22:31 +02:00
|
|
|
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;
|
2025-06-16 22:55:24 +02:00
|
|
|
id2ast = nullptr;
|
2021-05-27 00:22:31 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int width;
|
|
|
|
|
if (bit_part_sel)
|
|
|
|
|
{
|
2024-01-25 07:28:15 +01:00
|
|
|
// bit_part_sel->dumpAst(nullptr, "? ");
|
2021-05-27 00:22:31 +02:00
|
|
|
if (bit_part_sel->children.size() == 1)
|
|
|
|
|
width = 0;
|
|
|
|
|
else
|
|
|
|
|
width = bit_part_sel->children[0]->integer -
|
|
|
|
|
bit_part_sel->children[1]->integer;
|
|
|
|
|
bit_part_sel = nullptr;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
width = id2ast->children[0]->children[0]->integer -
|
|
|
|
|
id2ast->children[0]->children[1]->integer;
|
|
|
|
|
}
|
|
|
|
|
width = abs(width) + 1;
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2021-05-27 00:22:31 +02:00
|
|
|
delete_children();
|
|
|
|
|
|
|
|
|
|
std::vector<RTLIL::State> x_bits;
|
|
|
|
|
for (int i = 0; i < width; i++)
|
|
|
|
|
x_bits.push_back(RTLIL::State::Sx);
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> constant = AstNode::mkconst_bits(x_bits, false);
|
|
|
|
|
constant->cloneInto(*this);
|
2021-05-27 00:22:31 +02:00
|
|
|
}
|
2013-11-21 13:49:00 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
std::stringstream sstr;
|
2022-08-08 16:13:33 +02:00
|
|
|
sstr << "$mem2reg_rd$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++);
|
2013-11-21 13:49:00 +01:00
|
|
|
std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA";
|
|
|
|
|
|
|
|
|
|
int mem_width, mem_size, addr_bits;
|
2016-11-01 23:17:43 +01:00
|
|
|
bool mem_signed = id2ast->is_signed;
|
2013-11-21 13:49:00 +01:00
|
|
|
id2ast->meminfo(mem_width, mem_size, addr_bits);
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto wire_addr = std::make_unique<AstNode>(AST_WIRE, std::make_unique<AstNode>(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true)));
|
2013-11-21 13:49:00 +01:00
|
|
|
wire_addr->str = id_addr;
|
|
|
|
|
wire_addr->is_reg = true;
|
2018-06-05 16:44:24 +02:00
|
|
|
wire_addr->was_checked = true;
|
2013-11-21 13:49:00 +01:00
|
|
|
if (block)
|
2023-04-05 11:00:07 +02:00
|
|
|
wire_addr->set_attribute(ID::nosync, AstNode::mkconst_int(1, false));
|
2023-04-04 22:59:44 +02:00
|
|
|
while (wire_addr->simplify(true, 1, -1, false)) { }
|
2025-06-16 22:55:24 +02:00
|
|
|
mod->children.push_back(std::move(wire_addr));
|
2013-11-21 13:49:00 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto wire_data = std::make_unique<AstNode>(AST_WIRE, std::make_unique<AstNode>(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
|
2013-11-21 13:49:00 +01:00
|
|
|
wire_data->str = id_data;
|
|
|
|
|
wire_data->is_reg = true;
|
2018-06-05 16:44:24 +02:00
|
|
|
wire_data->was_checked = true;
|
2016-11-01 23:17:43 +01:00
|
|
|
wire_data->is_signed = mem_signed;
|
2013-11-21 13:49:00 +01:00
|
|
|
if (block)
|
2023-04-05 11:00:07 +02:00
|
|
|
wire_data->set_attribute(ID::nosync, AstNode::mkconst_int(1, false));
|
2023-04-04 22:59:44 +02:00
|
|
|
while (wire_data->simplify(true, 1, -1, false)) { }
|
2025-06-16 22:55:24 +02:00
|
|
|
mod->children.push_back(std::move(wire_data));
|
2013-11-21 13:49:00 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto assign_addr = std::make_unique<AstNode>(block ? AST_ASSIGN_EQ : AST_ASSIGN, std::make_unique<AstNode>(AST_IDENTIFIER), children[0]->children[0]->clone());
|
2013-11-21 13:49:00 +01:00
|
|
|
assign_addr->children[0]->str = id_addr;
|
2018-06-05 16:44:24 +02:00
|
|
|
assign_addr->children[0]->was_checked = true;
|
2013-11-21 13:49:00 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto case_node = std::make_unique<AstNode>(AST_CASE, std::make_unique<AstNode>(AST_IDENTIFIER));
|
2013-11-21 13:49:00 +01:00
|
|
|
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;
|
2025-06-16 22:55:24 +02:00
|
|
|
auto cond_node = std::make_unique<AstNode>(AST_COND, AstNode::mkconst_int(i, false, addr_bits), std::make_unique<AstNode>(AST_BLOCK));
|
|
|
|
|
auto assign_reg = std::make_unique<AstNode>(AST_ASSIGN_EQ, std::make_unique<AstNode>(AST_IDENTIFIER), std::make_unique<AstNode>(AST_IDENTIFIER));
|
2013-11-21 13:49:00 +01:00
|
|
|
assign_reg->children[0]->str = id_data;
|
2018-06-05 16:44:24 +02:00
|
|
|
assign_reg->children[0]->was_checked = true;
|
2013-11-21 13:49:00 +01:00
|
|
|
assign_reg->children[1]->str = stringf("%s[%d]", str.c_str(), i);
|
2025-06-16 22:55:24 +02:00
|
|
|
cond_node->children[1]->children.push_back(std::move(assign_reg));
|
|
|
|
|
case_node->children.push_back(std::move(cond_node));
|
2013-11-21 13:49:00 +01:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2013-11-21 13:49:00 +01:00
|
|
|
std::vector<RTLIL::State> x_bits;
|
|
|
|
|
for (int i = 0; i < mem_width; i++)
|
|
|
|
|
x_bits.push_back(RTLIL::State::Sx);
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
auto cond_node = std::make_unique<AstNode>(AST_COND, std::make_unique<AstNode>(AST_DEFAULT), std::make_unique<AstNode>(AST_BLOCK));
|
|
|
|
|
auto assign_reg = std::make_unique<AstNode>(AST_ASSIGN_EQ, std::make_unique<AstNode>(AST_IDENTIFIER), AstNode::mkconst_bits(x_bits, false));
|
2013-01-05 11:13:26 +01:00
|
|
|
assign_reg->children[0]->str = id_data;
|
2018-06-05 16:44:24 +02:00
|
|
|
assign_reg->children[0]->was_checked = true;
|
2025-06-16 22:55:24 +02:00
|
|
|
cond_node->children[1]->children.push_back(std::move(assign_reg));
|
|
|
|
|
case_node->children.push_back(std::move(cond_node));
|
2013-03-24 10:42:08 +01:00
|
|
|
|
2023-04-05 11:00:07 +02:00
|
|
|
// fixup on the full hierarchy below case_node
|
|
|
|
|
case_node->fixup_hierarchy_flags(true);
|
|
|
|
|
|
2013-11-21 13:49:00 +01:00
|
|
|
if (block)
|
|
|
|
|
{
|
|
|
|
|
size_t assign_idx = 0;
|
|
|
|
|
while (assign_idx < block->children.size() && !block->children[assign_idx]->contains(this))
|
|
|
|
|
assign_idx++;
|
2014-07-28 11:08:55 +02:00
|
|
|
log_assert(assign_idx < block->children.size());
|
2025-06-16 22:55:24 +02:00
|
|
|
block->children.insert(block->children.begin()+assign_idx, std::move(case_node));
|
|
|
|
|
block->children.insert(block->children.begin()+assign_idx, std::move(assign_addr));
|
2013-11-21 13:49:00 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
auto proc = std::make_unique<AstNode>(AST_ALWAYS, std::make_unique<AstNode>(AST_BLOCK, std::move(case_node)));
|
|
|
|
|
mod->children.push_back(std::move(proc));
|
|
|
|
|
mod->children.push_back(std::move(assign_addr));
|
2023-04-05 11:00:07 +02:00
|
|
|
mod->fixup_hierarchy_flags();
|
2013-11-21 13:49:00 +01:00
|
|
|
}
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2013-11-21 13:49:00 +01:00
|
|
|
delete_children();
|
|
|
|
|
range_valid = false;
|
2025-06-16 22:55:24 +02:00
|
|
|
id2ast = nullptr;
|
2013-11-21 13:49:00 +01:00
|
|
|
str = id_data;
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
2014-07-17 13:49:32 +02:00
|
|
|
|
2023-04-05 11:00:07 +02:00
|
|
|
if (bit_part_sel) {
|
2025-06-16 22:55:24 +02:00
|
|
|
children.push_back(std::move(bit_part_sel));
|
2023-04-05 11:00:07 +02:00
|
|
|
fixup_hierarchy_flags();
|
|
|
|
|
}
|
2017-01-15 13:52:50 +01:00
|
|
|
|
|
|
|
|
did_something = true;
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
log_assert(id2ast == nullptr || mem2reg_set.count(id2ast) == 0);
|
|
|
|
|
|
|
|
|
|
std::vector<AstNode*> children_list;
|
|
|
|
|
for (auto& child : children)
|
|
|
|
|
children_list.push_back(child.get());
|
2013-01-05 11:13:26 +01:00
|
|
|
|
2013-03-25 17:13:14 +01:00
|
|
|
for (size_t i = 0; i < children_list.size(); i++)
|
2016-08-21 13:23:58 +02:00
|
|
|
if (children_list[i]->mem2reg_as_needed_pass2(mem2reg_set, mod, block, async_block))
|
2015-07-29 16:37:08 +02:00
|
|
|
did_something = true;
|
|
|
|
|
|
|
|
|
|
return did_something;
|
2013-01-05 11:13:26 +01:00
|
|
|
}
|
|
|
|
|
|
2015-08-14 10:56:05 +02:00
|
|
|
// calculate memory dimensions
|
2013-01-05 11:13:26 +01:00
|
|
|
void AstNode::meminfo(int &mem_width, int &mem_size, int &addr_bits)
|
|
|
|
|
{
|
2014-07-28 11:08:55 +02:00
|
|
|
log_assert(type == AST_MEMORY);
|
2013-01-05 11:13:26 +01:00
|
|
|
|
|
|
|
|
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;
|
2015-10-25 19:30:49 +01:00
|
|
|
mem_size += min(children[1]->range_left, children[1]->range_right) + 1;
|
2013-01-05 11:13:26 +01:00
|
|
|
|
|
|
|
|
addr_bits = 1;
|
|
|
|
|
while ((1 << addr_bits) < mem_size)
|
|
|
|
|
addr_bits++;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-04 23:10:03 +02:00
|
|
|
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;
|
2020-07-10 18:41:13 +02:00
|
|
|
case AST_EDGE:
|
|
|
|
|
break;
|
2020-06-04 23:10:03 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-27 19:21:13 +01:00
|
|
|
bool AstNode::has_const_only_constructs()
|
2014-06-06 22:55:02 +02:00
|
|
|
{
|
|
|
|
|
if (type == AST_WHILE || type == AST_REPEAT)
|
|
|
|
|
return true;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children)
|
2021-01-27 19:21:13 +01:00
|
|
|
if (child->has_const_only_constructs())
|
2014-06-06 22:55:02 +02:00
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-02 21:36:46 +01:00
|
|
|
bool AstNode::is_simple_const_expr()
|
|
|
|
|
{
|
|
|
|
|
if (type == AST_IDENTIFIER)
|
|
|
|
|
return false;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children)
|
2019-03-02 21:36:46 +01:00
|
|
|
if (!child->is_simple_const_expr())
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-14 19:56:44 +01:00
|
|
|
// helper function for AstNode::eval_const_function()
|
2021-01-27 19:21:13 +01:00
|
|
|
bool AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall, bool must_succeed)
|
2014-02-14 19:56:44 +01:00
|
|
|
{
|
|
|
|
|
if (type == AST_IDENTIFIER && variables.count(str)) {
|
2024-10-09 19:39:45 +02:00
|
|
|
int offset = variables.at(str).offset, width = variables.at(str).val.size();
|
2014-02-14 19:56:44 +01:00
|
|
|
if (!children.empty()) {
|
2021-01-27 19:21:13 +01:00
|
|
|
if (children.size() != 1 || children.at(0)->type != AST_RANGE) {
|
|
|
|
|
if (!must_succeed)
|
|
|
|
|
return false;
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Memory access in constant function is not supported\n%s: ...called from here.\n",
|
2021-02-23 19:22:53 +01:00
|
|
|
fcall->loc_string().c_str());
|
2021-01-27 19:21:13 +01:00
|
|
|
}
|
|
|
|
|
if (!children.at(0)->replace_variables(variables, fcall, must_succeed))
|
|
|
|
|
return false;
|
2023-04-04 22:59:44 +02:00
|
|
|
while (simplify(true, 1, -1, false)) { }
|
2021-01-27 19:21:13 +01:00
|
|
|
if (!children.at(0)->range_valid) {
|
|
|
|
|
if (!must_succeed)
|
|
|
|
|
return false;
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Non-constant range\n%s: ... called from here.\n",
|
2021-02-23 19:22:53 +01:00
|
|
|
fcall->loc_string().c_str());
|
2021-01-27 19:21:13 +01:00
|
|
|
}
|
2015-10-25 19:30:49 +01:00
|
|
|
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);
|
2014-02-14 19:56:44 +01:00
|
|
|
}
|
|
|
|
|
offset -= variables.at(str).offset;
|
2022-01-12 07:51:08 +01:00
|
|
|
if (variables.at(str).range_swapped)
|
|
|
|
|
offset = -offset;
|
2024-10-09 19:39:45 +02:00
|
|
|
std::vector<RTLIL::State> &var_bits = variables.at(str).val.bits();
|
2014-02-14 19:56:44 +01:00
|
|
|
std::vector<RTLIL::State> new_bits(var_bits.begin() + offset, var_bits.begin() + offset + width);
|
2025-06-16 22:55:24 +02:00
|
|
|
auto newNode = mkconst_bits(new_bits, variables.at(str).is_signed);
|
|
|
|
|
newNode->cloneInto(*this);
|
2021-01-27 19:21:13 +01:00
|
|
|
return true;
|
2014-02-14 19:56:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto &child : children)
|
2021-01-27 19:21:13 +01:00
|
|
|
if (!child->replace_variables(variables, fcall, must_succeed))
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
2014-02-14 19:56:44 +01:00
|
|
|
}
|
|
|
|
|
|
2021-01-27 19:21:13 +01:00
|
|
|
// attempt to statically evaluate a functions with all-const arguments
|
2025-06-16 22:55:24 +02:00
|
|
|
std::unique_ptr<AstNode> AstNode::eval_const_function(AstNode *fcall, bool must_succeed)
|
2014-02-14 19:56:44 +01:00
|
|
|
{
|
2021-01-27 19:21:13 +01:00
|
|
|
std::map<std::string, AstNode*> backup_scope = current_scope;
|
2014-02-14 19:56:44 +01:00
|
|
|
std::map<std::string, AstNode::varinfo_t> variables;
|
2025-06-16 22:55:24 +02:00
|
|
|
auto block = std::make_unique<AstNode>(AST_BLOCK);
|
|
|
|
|
std::unique_ptr<AstNode> result = nullptr;
|
2014-02-14 19:56:44 +01:00
|
|
|
|
|
|
|
|
size_t argidx = 0;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : children)
|
2014-02-14 19:56:44 +01:00
|
|
|
{
|
2014-06-06 21:29:23 +02:00
|
|
|
block->children.push_back(child->clone());
|
2014-02-14 19:56:44 +01:00
|
|
|
}
|
2023-04-05 11:00:07 +02:00
|
|
|
block->set_in_param_flag(true);
|
2025-06-16 22:55:24 +02:00
|
|
|
std::vector<std::unique_ptr<AstNode>> temporary_nodes;
|
2014-02-14 19:56:44 +01:00
|
|
|
|
|
|
|
|
while (!block->children.empty())
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
// log("%zu left in block %p\n", block->children.size(), block.get());
|
|
|
|
|
std::unique_ptr<AstNode>& stmt = block->children.front();
|
2014-02-14 19:56:44 +01:00
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
log("-----------------------------------\n");
|
|
|
|
|
for (auto &it : variables)
|
|
|
|
|
log("%20s %40s\n", it.first.c_str(), log_signal(it.second.val));
|
2025-06-16 22:55:24 +02:00
|
|
|
stmt->dumpAst(nullptr, "stmt> ");
|
2014-02-14 19:56:44 +01:00
|
|
|
#endif
|
2025-06-16 22:55:24 +02:00
|
|
|
// log("A\n");
|
|
|
|
|
// log("%s\n", type2str(stmt->type).c_str());
|
2020-07-25 18:16:12 +02:00
|
|
|
if (stmt->type == AST_WIRE)
|
|
|
|
|
{
|
2023-04-04 22:59:44 +02:00
|
|
|
while (stmt->simplify(true, 1, -1, false)) { }
|
2021-01-27 19:21:13 +01:00
|
|
|
if (!stmt->range_valid) {
|
|
|
|
|
if (!must_succeed)
|
|
|
|
|
goto finished;
|
2023-04-04 11:53:50 +02:00
|
|
|
stmt->input_error("Can't determine size of variable %s\n%s: ... called from here.\n",
|
2021-02-23 19:22:53 +01:00
|
|
|
stmt->str.c_str(), fcall->loc_string().c_str());
|
2021-01-27 19:21:13 +01:00
|
|
|
}
|
2021-02-21 20:45:21 +01:00
|
|
|
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) {
|
2023-04-04 11:53:50 +02:00
|
|
|
input_error("Incompatible re-declaration of constant function wire %s.\n", stmt->str.c_str());
|
2021-02-21 20:45:21 +01:00
|
|
|
}
|
|
|
|
|
variable.val = RTLIL::Const(RTLIL::State::Sx, width);
|
2022-01-12 07:51:08 +01:00
|
|
|
variable.offset = stmt->range_swapped ? stmt->range_left : stmt->range_right;
|
|
|
|
|
variable.range_swapped = stmt->range_swapped;
|
2021-02-21 20:45:21 +01:00
|
|
|
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
|
2020-08-18 17:27:51 +02:00
|
|
|
if (stmt->is_input && argidx < fcall->children.size()) {
|
2025-06-16 22:55:24 +02:00
|
|
|
variable.arg = fcall->children.at(argidx++).get();
|
2021-02-21 20:45:21 +01:00
|
|
|
}
|
|
|
|
|
// load the constant arg's value into this variable
|
|
|
|
|
if (variable.arg) {
|
|
|
|
|
if (variable.arg->type == AST_CONSTANT) {
|
|
|
|
|
variable.val = variable.arg->bitsAsConst(width);
|
2020-08-18 17:27:51 +02:00
|
|
|
} else {
|
2021-02-21 20:45:21 +01:00
|
|
|
log_assert(variable.arg->type == AST_REALVALUE);
|
|
|
|
|
variable.val = variable.arg->realAsConst(width);
|
2020-08-18 17:27:51 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-06-16 22:55:24 +02:00
|
|
|
current_scope[stmt->str] = stmt.get();
|
|
|
|
|
temporary_nodes.push_back(std::move(stmt));
|
2020-07-25 18:16:12 +02:00
|
|
|
|
|
|
|
|
block->children.erase(block->children.begin());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log_assert(variables.count(str) != 0);
|
|
|
|
|
|
2020-08-21 02:09:54 +02:00
|
|
|
if (stmt->type == AST_LOCALPARAM)
|
|
|
|
|
{
|
2023-04-04 22:59:44 +02:00
|
|
|
while (stmt->simplify(true, 1, -1, false)) { }
|
2020-08-21 02:09:54 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
current_scope[stmt->str] = stmt.get();
|
|
|
|
|
temporary_nodes.push_back(std::move(stmt));
|
2020-08-21 02:09:54 +02:00
|
|
|
|
|
|
|
|
block->children.erase(block->children.begin());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-14 19:56:44 +01:00
|
|
|
if (stmt->type == AST_ASSIGN_EQ)
|
|
|
|
|
{
|
2015-10-01 12:15:35 +02:00
|
|
|
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)
|
2021-01-27 19:21:13 +01:00
|
|
|
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;
|
2023-04-04 22:59:44 +02:00
|
|
|
while (stmt->simplify(true, 1, -1, false)) { }
|
2014-02-14 19:56:44 +01:00
|
|
|
|
2014-06-06 17:40:45 +02:00
|
|
|
if (stmt->type != AST_ASSIGN_EQ)
|
|
|
|
|
continue;
|
|
|
|
|
|
2021-01-27 19:21:13 +01:00
|
|
|
if (stmt->children.at(1)->type != AST_CONSTANT) {
|
|
|
|
|
if (!must_succeed)
|
|
|
|
|
goto finished;
|
2023-04-04 11:53:50 +02:00
|
|
|
stmt->input_error("Non-constant expression in constant function\n%s: ... called from here. X\n",
|
2021-02-23 19:22:53 +01:00
|
|
|
fcall->loc_string().c_str());
|
2021-01-27 19:21:13 +01:00
|
|
|
}
|
2014-02-14 19:56:44 +01:00
|
|
|
|
2021-01-27 19:21:13 +01:00
|
|
|
if (stmt->children.at(0)->type != AST_IDENTIFIER) {
|
|
|
|
|
if (!must_succeed)
|
|
|
|
|
goto finished;
|
2023-04-04 11:53:50 +02:00
|
|
|
stmt->input_error("Unsupported composite left hand side in constant function\n%s: ... called from here.\n",
|
2021-02-23 19:22:53 +01:00
|
|
|
fcall->loc_string().c_str());
|
2021-01-27 19:21:13 +01:00
|
|
|
}
|
2014-02-14 19:56:44 +01:00
|
|
|
|
2021-01-27 19:21:13 +01:00
|
|
|
if (!variables.count(stmt->children.at(0)->str)) {
|
|
|
|
|
if (!must_succeed)
|
|
|
|
|
goto finished;
|
2023-04-04 11:53:50 +02:00
|
|
|
stmt->input_error("Assignment to non-local variable in constant function\n%s: ... called from here.\n",
|
2021-02-23 19:22:53 +01:00
|
|
|
fcall->loc_string().c_str());
|
2021-01-27 19:21:13 +01:00
|
|
|
}
|
2014-02-14 19:56:44 +01:00
|
|
|
|
2014-06-06 22:55:02 +02:00
|
|
|
if (stmt->children.at(0)->children.empty()) {
|
2024-10-09 19:39:45 +02:00
|
|
|
variables[stmt->children.at(0)->str].val = stmt->children.at(1)->bitsAsConst(variables[stmt->children.at(0)->str].val.size());
|
2014-06-06 22:55:02 +02:00
|
|
|
} else {
|
2025-06-16 22:55:24 +02:00
|
|
|
AstNode *range = stmt->children.at(0)->children.at(0).get();
|
2021-01-27 19:21:13 +01:00
|
|
|
if (!range->range_valid) {
|
|
|
|
|
if (!must_succeed)
|
|
|
|
|
goto finished;
|
2023-04-04 11:53:50 +02:00
|
|
|
range->input_error("Non-constant range\n%s: ... called from here.\n", fcall->loc_string().c_str());
|
2021-01-27 19:21:13 +01:00
|
|
|
}
|
2015-10-25 19:30:49 +01:00
|
|
|
int offset = min(range->range_left, range->range_right);
|
2014-07-23 20:45:27 +02:00
|
|
|
int width = std::abs(range->range_left - range->range_right) + 1;
|
2014-06-06 22:55:02 +02:00
|
|
|
varinfo_t &v = variables[stmt->children.at(0)->str];
|
2024-10-09 19:39:45 +02:00
|
|
|
RTLIL::Const r = stmt->children.at(1)->bitsAsConst(v.val.size());
|
2022-01-12 07:51:08 +01:00
|
|
|
for (int i = 0; i < width; i++) {
|
|
|
|
|
int index = i + offset - v.offset;
|
|
|
|
|
if (v.range_swapped)
|
|
|
|
|
index = -index;
|
2024-10-09 19:39:45 +02:00
|
|
|
v.val.bits().at(index) = r.at(i);
|
2022-01-12 07:51:08 +01:00
|
|
|
}
|
2014-06-06 22:55:02 +02:00
|
|
|
}
|
2014-02-14 19:56:44 +01:00
|
|
|
|
|
|
|
|
block->children.erase(block->children.begin());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-14 20:33:22 +01:00
|
|
|
if (stmt->type == AST_FOR)
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
stmt->type = AST_WHILE;
|
|
|
|
|
log_assert(stmt->children.size() > 2);
|
|
|
|
|
auto yoink0 = std::move(stmt->children.at(0));
|
|
|
|
|
log_assert(stmt->children.size() > 2);
|
|
|
|
|
auto yoink2 = std::move(stmt->children.at(2));
|
|
|
|
|
stmt->children.at(3)->children.push_back(std::move(yoink2));
|
2014-02-14 20:33:22 +01:00
|
|
|
stmt->children.erase(stmt->children.begin() + 2);
|
|
|
|
|
stmt->children.erase(stmt->children.begin());
|
2025-06-16 22:55:24 +02:00
|
|
|
block->children.insert(block->children.begin(), std::move(yoink0));
|
|
|
|
|
log_assert(stmt->children.size() == 2);
|
2014-02-14 20:33:22 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (stmt->type == AST_WHILE)
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
auto cond = stmt->children.at(0)->clone();
|
2021-01-27 19:21:13 +01:00
|
|
|
if (!cond->replace_variables(variables, fcall, must_succeed))
|
|
|
|
|
goto finished;
|
2023-04-05 11:00:07 +02:00
|
|
|
cond->set_in_param_flag(true);
|
2023-04-04 22:59:44 +02:00
|
|
|
while (cond->simplify(true, 1, -1, false)) { }
|
2014-02-14 20:33:22 +01:00
|
|
|
|
2021-01-27 19:21:13 +01:00
|
|
|
if (cond->type != AST_CONSTANT) {
|
|
|
|
|
if (!must_succeed)
|
|
|
|
|
goto finished;
|
2023-04-04 11:53:50 +02:00
|
|
|
stmt->input_error("Non-constant expression in constant function\n%s: ... called from here.\n",
|
2021-02-23 19:22:53 +01:00
|
|
|
fcall->loc_string().c_str());
|
2021-01-27 19:21:13 +01:00
|
|
|
}
|
2014-02-14 20:33:22 +01:00
|
|
|
|
|
|
|
|
if (cond->asBool()) {
|
|
|
|
|
block->children.insert(block->children.begin(), stmt->children.at(1)->clone());
|
|
|
|
|
} else {
|
|
|
|
|
block->children.erase(block->children.begin());
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-07 10:47:53 +02:00
|
|
|
if (stmt->type == AST_REPEAT)
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
auto num = stmt->children.at(0)->clone();
|
2021-01-27 19:21:13 +01:00
|
|
|
if (!num->replace_variables(variables, fcall, must_succeed))
|
|
|
|
|
goto finished;
|
2023-04-05 11:00:07 +02:00
|
|
|
num->set_in_param_flag(true);
|
2023-04-04 22:59:44 +02:00
|
|
|
while (num->simplify(true, 1, -1, false)) { }
|
2014-06-07 10:47:53 +02:00
|
|
|
|
2021-01-27 19:21:13 +01:00
|
|
|
if (num->type != AST_CONSTANT) {
|
|
|
|
|
if (!must_succeed)
|
|
|
|
|
goto finished;
|
2023-04-04 11:53:50 +02:00
|
|
|
stmt->input_error("Non-constant expression in constant function\n%s: ... called from here.\n",
|
2021-02-23 19:22:53 +01:00
|
|
|
fcall->loc_string().c_str());
|
2021-01-27 19:21:13 +01:00
|
|
|
}
|
2014-06-07 10:47:53 +02:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
temporary_nodes.push_back(std::move(stmt));
|
2014-06-07 10:47:53 +02:00
|
|
|
block->children.erase(block->children.begin());
|
|
|
|
|
for (int i = 0; i < num->bitsAsConst().as_int(); i++)
|
2025-06-16 22:55:24 +02:00
|
|
|
block->children.insert(block->children.begin(), temporary_nodes.back()->children.at(1)->clone());
|
2014-06-07 10:47:53 +02:00
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-16 13:16:38 +01:00
|
|
|
if (stmt->type == AST_CASE)
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
auto expr = stmt->children.at(0)->clone();
|
2021-01-27 19:21:13 +01:00
|
|
|
if (!expr->replace_variables(variables, fcall, must_succeed))
|
|
|
|
|
goto finished;
|
2023-04-05 11:00:07 +02:00
|
|
|
expr->set_in_param_flag(true);
|
2023-04-04 22:59:44 +02:00
|
|
|
while (expr->simplify(true, 1, -1, false)) { }
|
2014-02-16 13:16:38 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
AstNode *sel_case = nullptr;
|
|
|
|
|
std::unique_ptr<AstNode> sel_case_copy = nullptr;
|
2014-02-16 13:16:38 +01:00
|
|
|
for (size_t i = 1; i < stmt->children.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
bool found_match = false;
|
2016-04-21 15:31:54 +02:00
|
|
|
log_assert(stmt->children.at(i)->type == AST_COND || stmt->children.at(i)->type == AST_CONDX || stmt->children.at(i)->type == AST_CONDZ);
|
2014-02-16 13:16:38 +01:00
|
|
|
|
|
|
|
|
if (stmt->children.at(i)->children.front()->type == AST_DEFAULT) {
|
2025-06-16 22:55:24 +02:00
|
|
|
sel_case = stmt->children.at(i)->children.back().get();
|
2014-02-16 13:16:38 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t j = 0; j+1 < stmt->children.at(i)->children.size() && !found_match; j++)
|
|
|
|
|
{
|
2025-06-16 22:55:24 +02:00
|
|
|
auto cond = stmt->children.at(i)->children.at(j)->clone();
|
2021-01-27 19:21:13 +01:00
|
|
|
if (!cond->replace_variables(variables, fcall, must_succeed))
|
|
|
|
|
goto finished;
|
2014-02-16 13:16:38 +01:00
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
cond = std::make_unique<AstNode>(AST_EQ, expr->clone(), std::move(cond));
|
2023-04-05 11:00:07 +02:00
|
|
|
cond->set_in_param_flag(true);
|
2023-04-04 22:59:44 +02:00
|
|
|
while (cond->simplify(true, 1, -1, false)) { }
|
2014-02-16 13:16:38 +01:00
|
|
|
|
2021-01-27 19:21:13 +01:00
|
|
|
if (cond->type != AST_CONSTANT) {
|
|
|
|
|
if (!must_succeed)
|
|
|
|
|
goto finished;
|
2023-04-04 11:53:50 +02:00
|
|
|
stmt->input_error("Non-constant expression in constant function\n%s: ... called from here.\n",
|
2021-02-23 19:22:53 +01:00
|
|
|
fcall->loc_string().c_str());
|
2021-01-27 19:21:13 +01:00
|
|
|
}
|
2014-02-16 13:16:38 +01:00
|
|
|
|
|
|
|
|
found_match = cond->asBool();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (found_match) {
|
2025-06-16 22:55:24 +02:00
|
|
|
sel_case = stmt->children.at(i)->children.back().get();
|
2014-02-16 13:16:38 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-16 22:55:24 +02:00
|
|
|
if (sel_case)
|
|
|
|
|
sel_case_copy = sel_case->clone();
|
2014-02-16 13:16:38 +01:00
|
|
|
|
|
|
|
|
block->children.erase(block->children.begin());
|
2025-06-16 22:55:24 +02:00
|
|
|
if (sel_case_copy)
|
|
|
|
|
block->children.insert(block->children.begin(), std::move(sel_case_copy));
|
2014-02-16 13:16:38 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-14 20:33:22 +01:00
|
|
|
if (stmt->type == AST_BLOCK)
|
|
|
|
|
{
|
verilog: significant block scoping improvements
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.
Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.
1. Unlabled generate blocks are now implicitly named according to the LRM in
`label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
synthetic unnamed generate block to avoid creating extra hierarchy levels
where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
of the topmost scope, which is necessary because such wires and cells often
appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
scope, completely deferring the inspection and elaboration of nested scopes;
names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
in largely the same manner as other blocks
before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode
Addresses the following issues: 656, 2423, 2493
2021-01-27 19:30:22 +01:00
|
|
|
if (!stmt->str.empty())
|
|
|
|
|
stmt->expand_genblock(stmt->str + ".");
|
2025-06-16 22:55:24 +02:00
|
|
|
auto* stmt_leaky = stmt.get();
|
|
|
|
|
temporary_nodes.push_back(std::move(stmt));
|
2014-02-14 20:33:22 +01:00
|
|
|
block->children.erase(block->children.begin());
|
2025-06-16 22:55:24 +02:00
|
|
|
block->children.reserve(block->children.size() + stmt_leaky->children.size());
|
|
|
|
|
block->children.insert(block->children.begin(),
|
|
|
|
|
std::make_move_iterator(stmt_leaky->children.begin()),
|
|
|
|
|
std::make_move_iterator(stmt_leaky->children.end()));
|
|
|
|
|
stmt_leaky->children.clear();
|
2023-04-05 11:00:07 +02:00
|
|
|
block->fixup_hierarchy_flags();
|
2014-02-14 20:33:22 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 22:55:24 +02:00
|
|
|
// log("C\n");
|
2021-01-27 19:21:13 +01:00
|
|
|
if (!must_succeed)
|
|
|
|
|
goto finished;
|
2023-04-04 11:53:50 +02:00
|
|
|
stmt->input_error("Unsupported language construct in constant function\n%s: ... called from here.\n",
|
2021-02-23 19:22:53 +01:00
|
|
|
fcall->loc_string().c_str());
|
2014-02-14 19:56:44 +01:00
|
|
|
log_abort();
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-09 19:39:45 +02:00
|
|
|
result = AstNode::mkconst_bits(variables.at(str).val.to_bits(), variables.at(str).is_signed);
|
2014-06-06 17:40:45 +02:00
|
|
|
|
2021-01-27 19:21:13 +01:00
|
|
|
finished:
|
|
|
|
|
current_scope = backup_scope;
|
|
|
|
|
return result;
|
2014-02-14 19:56:44 +01:00
|
|
|
}
|
|
|
|
|
|
2018-03-09 13:47:11 +01:00
|
|
|
void AstNode::allocateDefaultEnumValues()
|
|
|
|
|
{
|
|
|
|
|
log_assert(type==AST_ENUM);
|
2021-06-17 21:59:59 +02:00
|
|
|
log_assert(children.size() > 0);
|
|
|
|
|
if (children.front()->attributes.count(ID::enum_base_type))
|
|
|
|
|
return; // already elaborated
|
2018-03-09 13:47:11 +01:00
|
|
|
int last_enum_int = -1;
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& node : children) {
|
2018-03-09 13:47:11 +01:00
|
|
|
log_assert(node->type==AST_ENUM_ITEM);
|
2023-04-05 11:00:07 +02:00
|
|
|
node->set_attribute(ID::enum_base_type, mkconst_str(str));
|
2018-03-09 13:47:11 +01:00
|
|
|
for (size_t i = 0; i < node->children.size(); i++) {
|
|
|
|
|
switch (node->children[i]->type) {
|
|
|
|
|
case AST_NONE:
|
|
|
|
|
// replace with auto-incremented constant
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-12 20:25:34 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2025-06-16 22:55:24 +02:00
|
|
|
for (auto& child : node->children) {
|
|
|
|
|
if (visit(child.get()))
|
2021-02-12 20:25:34 +01:00
|
|
|
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)
|
2025-06-16 22:55:24 +02:00
|
|
|
choice = children[1].get(), not_choice = children[2].get();
|
2021-02-12 20:25:34 +01:00
|
|
|
else if (!found_maybe_true)
|
2025-06-16 22:55:24 +02:00
|
|
|
choice = children[2].get(), not_choice = children[1].get();
|
2021-02-12 20:25:34 +01:00
|
|
|
|
|
|
|
|
return {choice, not_choice};
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-05 22:21:09 +02:00
|
|
|
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);
|
2024-02-13 21:38:41 +01:00
|
|
|
if ((it != current_scope_ast->attributes.end() && it->second->str == prefix.substr(1))
|
2021-06-05 22:21:09 +02:00
|
|
|
|| prefix == current_scope_ast->str)
|
|
|
|
|
return new_str;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return str;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-31 13:19:47 +02:00
|
|
|
YOSYS_NAMESPACE_END
|