Add support for packed multidimensional arrays

* Generalization of dimensions metadata (also simplifies $size et al.)
* Parsing and elaboration of multidimensional packed ranges
This commit is contained in:
Dag Lem 2024-01-25 07:28:15 +01:00 committed by Zachary Snow
parent ac0fb2e301
commit 39fea32c6e
7 changed files with 247 additions and 267 deletions

View File

@ -224,6 +224,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
port_id = 0;
range_left = -1;
range_right = 0;
unpacked_dimensions = 0;
integer = 0;
realvalue = 0;
id2ast = NULL;
@ -349,17 +350,15 @@ void AstNode::dumpAst(FILE *f, std::string indent) const
fprintf(f, " int=%u", (int)integer);
if (realvalue != 0)
fprintf(f, " real=%e", realvalue);
if (!multirange_dimensions.empty()) {
fprintf(f, " multirange=[");
for (int v : multirange_dimensions)
fprintf(f, " %d", v);
fprintf(f, " ]");
}
if (!multirange_swapped.empty()) {
fprintf(f, " multirange_swapped=[");
for (bool v : multirange_swapped)
fprintf(f, " %d", v);
fprintf(f, " ]");
if (!dimensions.empty()) {
fprintf(f, " dimensions=");
for (auto &dim : dimensions) {
int left = dim.range_right + dim.range_width - 1;
int right = dim.range_right;
if (dim.range_swapped)
std::swap(left, right);
fprintf(f, "[%d:%d]", left, right);
}
}
if (is_enum) {
fprintf(f, " type=enum");
@ -505,6 +504,11 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
}
break;
case AST_MULTIRANGE:
for (auto child : children)
child->dumpVlog(f, "");
break;
case AST_ALWAYS:
fprintf(f, "%s" "always @", indent.c_str());
for (auto child : children) {
@ -542,7 +546,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
case AST_IDENTIFIER:
{
AST::AstNode *member_node = AST::get_struct_member(this);
AstNode *member_node = get_struct_member();
if (member_node)
fprintf(f, "%s[%d:%d]", id2vl(str).c_str(), member_node->range_left, member_node->range_right);
else

View File

@ -202,9 +202,17 @@ namespace AST
// set for IDs typed to an enumeration, not used
bool is_enum;
// if this is a multirange memory then this vector contains offset and length of each dimension
std::vector<int> multirange_dimensions;
std::vector<bool> multirange_swapped; // true if range is swapped
// Declared range for array dimension.
struct dimension_t {
int range_right; // lsb in [msb:lsb]
int range_width; // msb - lsb + 1
bool range_swapped; // if the declared msb < lsb, msb and lsb above are swapped
};
// Packed and unpacked dimensions for arrays.
// Unpacked dimensions go first, to follow the order of indexing.
std::vector<dimension_t> dimensions;
// Number of unpacked dimensions.
int unpacked_dimensions;
// this is set by simplify and used during RTLIL generation
AstNode *id2ast;
@ -371,6 +379,10 @@ namespace AST
// localized fixups after modifying children/attributes of a particular node
void fixup_hierarchy_flags(bool force_descend = false);
// helpers for indexing
AstNode *make_index_range(AstNode *node, bool unpacked_range = false);
AstNode *get_struct_member() const;
// helper to print errors from simplify/genrtlil code
[[noreturn]] void input_error(const char *format, ...) const YS_ATTRIBUTE(format(printf, 2, 3));
};
@ -416,10 +428,6 @@ namespace AST
// Helper for setting the src attribute.
void set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast);
// struct helper exposed from simplify for genrtlil
AstNode *make_struct_member_range(AstNode *node, AstNode *member_node);
AstNode *get_struct_member(const AstNode *node);
// generate standard $paramod... derived module name; parameters should be
// in the order they are declared in the instantiated module
std::string derived_module_name(std::string stripped_name, const std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> &parameters);

View File

@ -1045,7 +1045,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
if (children.size() > 1)
range = children[1];
} else if (id_ast->type == AST_STRUCT_ITEM || id_ast->type == AST_STRUCT || id_ast->type == AST_UNION) {
AstNode *tmp_range = make_struct_member_range(this, id_ast);
AstNode *tmp_range = make_index_range(id_ast);
this_width = tmp_range->range_left - tmp_range->range_right + 1;
delete tmp_range;
} else
@ -1584,7 +1584,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
chunk.width = wire->width;
chunk.offset = 0;
if ((member_node = get_struct_member(this))) {
if ((member_node = get_struct_member())) {
// Clamp wire chunk to range of member within struct/union.
chunk.width = member_node->range_left - member_node->range_right + 1;
chunk.offset = member_node->range_right;

View File

@ -259,35 +259,24 @@ static int range_width(AstNode *node, AstNode *rnode)
{
log_assert(rnode->type==AST_RANGE);
if (!rnode->range_valid) {
node->input_error("Size must be constant in packed struct/union member %s\n", node->str.c_str());
node->input_error("Non-constant range in declaration of %s\n", node->str.c_str());
}
// note: range swapping has already been checked for
return rnode->range_left - rnode->range_right + 1;
}
static int add_dimension(AstNode *node, AstNode *rnode)
{
int width = range_width(node, rnode);
node->dimensions.push_back({ rnode->range_right, width, rnode->range_swapped });
return width;
}
[[noreturn]] static void struct_array_packing_error(AstNode *node)
{
node->input_error("Unpacked array in packed struct/union member %s\n", node->str.c_str());
}
static void save_struct_range_dimensions(AstNode *node, AstNode *rnode)
{
node->multirange_dimensions.push_back(rnode->range_right);
node->multirange_dimensions.push_back(range_width(node, rnode));
node->multirange_swapped.push_back(rnode->range_swapped);
}
static int get_struct_range_offset(AstNode *node, int dimension)
{
return node->multirange_dimensions[2*dimension];
}
static int get_struct_range_width(AstNode *node, int dimension)
{
return node->multirange_dimensions[2*dimension + 1];
}
static int size_packed_struct(AstNode *snode, int base_offset)
{
// Struct members will be laid out in the structure contiguously from left to right.
@ -303,10 +292,6 @@ static int size_packed_struct(AstNode *snode, int base_offset)
if (node->type == AST_STRUCT || node->type == AST_UNION) {
// embedded struct or union
width = size_packed_struct(node, base_offset + offset);
// set range of struct
node->range_right = base_offset + offset;
node->range_left = base_offset + offset + width - 1;
node->range_valid = true;
}
else {
log_assert(node->type == AST_STRUCT_ITEM);
@ -318,18 +303,16 @@ static int size_packed_struct(AstNode *snode, int base_offset)
// and integer data types are allowed in packed structs / unions in SystemVerilog.
if (node->children[1]->type == AST_RANGE) {
// Unpacked array, e.g. bit [63:0] a [0:3]
// Pretend it's declared as a packed array, e.g. bit [0:3][63:0] a
auto rnode = node->children[1];
if (rnode->children.size() == 1) {
// C-style array size, e.g. bit [63:0] a [4]
node->multirange_dimensions.push_back(0);
node->multirange_dimensions.push_back(rnode->range_left);
node->multirange_swapped.push_back(true);
node->dimensions.push_back({ 0, rnode->range_left, true });
width *= rnode->range_left;
} else {
save_struct_range_dimensions(node, rnode);
width *= range_width(node, rnode);
width *= add_dimension(node, rnode);
}
save_struct_range_dimensions(node, node->children[0]);
add_dimension(node, node->children[0]);
}
else {
// The Yosys extension for unpacked arrays in packed structs / unions
@ -338,7 +321,7 @@ static int size_packed_struct(AstNode *snode, int base_offset)
}
} else {
// Vector
save_struct_range_dimensions(node, node->children[0]);
add_dimension(node, node->children[0]);
}
// range nodes are now redundant
for (AstNode *child : node->children)
@ -354,8 +337,7 @@ static int size_packed_struct(AstNode *snode, int base_offset)
}
width = 1;
for (auto rnode : node->children[0]->children) {
save_struct_range_dimensions(node, rnode);
width *= range_width(node, rnode);
width *= add_dimension(node, rnode);
}
// range nodes are now redundant
for (AstNode *child : node->children)
@ -365,6 +347,7 @@ static int size_packed_struct(AstNode *snode, int base_offset)
else if (node->range_left < 0) {
// 1 bit signal: bit, logic or reg
width = 1;
node->dimensions.push_back({ 0, width, false });
}
else {
// already resolved and compacted
@ -395,12 +378,16 @@ static int size_packed_struct(AstNode *snode, int base_offset)
offset += width;
}
}
return (is_union ? packed_width : offset);
}
[[noreturn]] static void struct_op_error(AstNode *node)
{
node->input_error("Unsupported operation for struct/union member %s\n", node->str.c_str()+1);
int width = is_union ? packed_width : offset;
snode->range_right = base_offset;
snode->range_left = base_offset + width - 1;
snode->range_valid = true;
if (snode->dimensions.empty())
snode->dimensions.push_back({ 0, width, false });
return width;
}
static AstNode *node_int(int ival)
@ -413,113 +400,123 @@ static AstNode *multiply_by_const(AstNode *expr_node, int stride)
return new AstNode(AST_MUL, expr_node, node_int(stride));
}
static AstNode *normalize_struct_index(AstNode *expr, AstNode *member_node, int dimension)
static AstNode *normalize_index(AstNode *expr, AstNode *decl_node, int dimension)
{
expr = expr->clone();
int offset = get_struct_range_offset(member_node, dimension);
int offset = decl_node->dimensions[dimension].range_right;
if (offset) {
expr = new AstNode(AST_SUB, expr, node_int(offset));
}
if (member_node->multirange_swapped[dimension]) {
// The dimension has swapped range; swap index into the struct accordingly.
int msb = get_struct_range_width(member_node, dimension) - 1;
expr = new AstNode(AST_SUB, node_int(msb), expr);
// 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;
expr = new AstNode(AST_SUB, node_int(left), expr);
}
return expr;
}
static AstNode *struct_index_lsb_offset(AstNode *lsb_offset, AstNode *rnode, AstNode *member_node, int dimension, int &stride)
static AstNode *index_offset(AstNode *offset, AstNode *rnode, AstNode *decl_node, int dimension, int &stride)
{
stride /= get_struct_range_width(member_node, dimension);
auto right = normalize_struct_index(rnode->children.back(), member_node, dimension);
auto offset = stride > 1 ? multiply_by_const(right, stride) : right;
return lsb_offset ? new AstNode(AST_ADD, lsb_offset, offset) : offset;
stride /= decl_node->dimensions[dimension].range_width;
auto right = normalize_index(rnode->children.back(), decl_node, dimension);
auto add_offset = stride > 1 ? multiply_by_const(right, stride) : right;
return offset ? new AstNode(AST_ADD, offset, add_offset) : add_offset;
}
static AstNode *struct_index_msb_offset(AstNode *lsb_offset, AstNode *rnode, AstNode *member_node, int dimension, int stride)
static AstNode *index_msb_offset(AstNode *lsb_offset, AstNode *rnode, AstNode *decl_node, int dimension, int stride)
{
log_assert(rnode->children.size() <= 2);
// Offset to add to LSB
AstNode *offset;
AstNode *add_offset;
if (rnode->children.size() == 1) {
// Index, e.g. s.a[i]
offset = node_int(stride - 1);
add_offset = node_int(stride - 1);
}
else {
// rnode->children.size() == 2
// Slice, e.g. s.a[i:j]
auto left = normalize_struct_index(rnode->children[0], member_node, dimension);
auto right = normalize_struct_index(rnode->children[1], member_node, dimension);
offset = new AstNode(AST_SUB, left, right);
auto left = normalize_index(rnode->children[0], decl_node, dimension);
auto right = normalize_index(rnode->children[1], decl_node, dimension);
add_offset = new AstNode(AST_SUB, left, right);
if (stride > 1) {
// offset = (msb - lsb + 1)*stride - 1
auto slice_width = new AstNode(AST_ADD, offset, node_int(1));
offset = new AstNode(AST_SUB, multiply_by_const(slice_width, stride), node_int(1));
auto slice_width = new AstNode(AST_ADD, add_offset, node_int(1));
add_offset = new AstNode(AST_SUB, multiply_by_const(slice_width, stride), node_int(1));
}
}
return new AstNode(AST_ADD, lsb_offset, offset);
return new AstNode(AST_ADD, lsb_offset, add_offset);
}
AstNode *AST::make_struct_member_range(AstNode *node, AstNode *member_node)
AstNode *AstNode::make_index_range(AstNode *decl_node, bool unpacked_range)
{
// Work out the range in the packed array that corresponds to a struct member
// taking into account any range operations applicable to the current node
// such as array indexing or slicing
int range_left = member_node->range_left;
int range_right = member_node->range_right;
if (node->children.empty()) {
if (children.empty()) {
// no range operations apply, return the whole width
return make_range(range_left - range_right, 0);
return make_range(decl_node->range_left - decl_node->range_right, 0);
}
if (node->children.size() != 1) {
struct_op_error(node);
}
log_assert(children.size() == 1);
// Range operations
auto rnode = node->children[0];
AstNode *lsb_offset = NULL;
int stride = range_left - range_right + 1;
size_t i = 0;
AstNode *rnode = children[0];
AstNode *offset = NULL;
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;
}
// Calculate LSB offset for the final index / slice
if (rnode->type == AST_RANGE) {
lsb_offset = struct_index_lsb_offset(lsb_offset, rnode, member_node, i, stride);
offset = index_offset(offset, rnode, decl_node, dim, stride);
}
else if (rnode->type == AST_MULTIRANGE) {
// Add offset for each dimension
auto mrnode = rnode;
for (i = 0; i < mrnode->children.size(); i++) {
rnode = mrnode->children[i];
lsb_offset = struct_index_lsb_offset(lsb_offset, rnode, member_node, i, stride);
AstNode *mrnode = rnode;
int stop_dim = std::min(GetSize(mrnode->children), max_dim);
for (; dim < stop_dim; dim++) {
rnode = mrnode->children[dim];
offset = index_offset(offset, rnode, decl_node, dim, stride);
}
i--; // Step back to the final index / slice
dim--; // Step back to the final index / slice
}
else {
struct_op_error(node);
input_error("Unsupported range operation for %s\n", str.c_str());
}
// Calculate MSB offset for the final index / slice
auto msb_offset = struct_index_msb_offset(lsb_offset->clone(), rnode, member_node, i, stride);
AstNode *index_range = new AstNode(AST_RANGE);
return new AstNode(AST_RANGE, msb_offset, lsb_offset);
if (!unpacked_range && (stride > 1 || GetSize(rnode->children) == 2)) {
// Calculate MSB offset for the final index / slice of packed dimensions.
AstNode *msb_offset = index_msb_offset(offset->clone(), rnode, decl_node, dim, stride);
index_range->children.push_back(msb_offset);
}
index_range->children.push_back(offset);
return index_range;
}
AstNode *AST::get_struct_member(const AstNode *node)
AstNode *AstNode::get_struct_member() const
{
AST::AstNode *member_node;
if (node->attributes.count(ID::wiretype) && (member_node = node->attributes.at(ID::wiretype)) &&
AstNode *member_node;
if (attributes.count(ID::wiretype) && (member_node = attributes.at(ID::wiretype)) &&
(member_node->type == AST_STRUCT_ITEM || member_node->type == AST_STRUCT || member_node->type == AST_UNION))
{
return member_node;
}
return NULL;
return nullptr;
}
static void add_members_to_scope(AstNode *snode, std::string name)
@ -537,22 +534,10 @@ static void add_members_to_scope(AstNode *snode, std::string name)
}
}
static int get_max_offset(AstNode *node)
{
// get the width from the MS member in the struct
// as members are laid out from left to right in the packed wire
log_assert(node->type==AST_STRUCT || node->type==AST_UNION);
while (node->type != AST_STRUCT_ITEM) {
node = node->children[0];
}
return node->range_left;
}
static AstNode *make_packed_struct(AstNode *template_node, std::string &name, decltype(AstNode::attributes) &attributes)
{
// create a wire for the packed struct
int offset = get_max_offset(template_node);
auto wnode = new AstNode(AST_WIRE, make_range(offset, 0));
auto wnode = new AstNode(AST_WIRE, make_range(template_node->range_left, 0));
wnode->str = name;
wnode->is_logic = true;
wnode->range_valid = true;
@ -560,6 +545,8 @@ static AstNode *make_packed_struct(AstNode *template_node, std::string &name, de
for (auto &pair : attributes) {
wnode->set_attribute(pair.first, pair.second->clone());
}
// resolve packed dimension
while (wnode->simplify(true, 1, -1, false)) {}
// make sure this node is the one in scope for this name
current_scope[name] = wnode;
// add all the struct members to scope under the wire's name
@ -1986,6 +1973,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
range_swapped = template_node->range_swapped;
range_left = template_node->range_left;
range_right = template_node->range_right;
set_attribute(ID::wiretype, mkconst_str(resolved_type_node->str));
for (auto template_child : template_node->children)
children.push_back(template_child->clone());
@ -2046,9 +2034,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
if (old_range_valid != range_valid)
did_something = true;
if (range_valid && range_right > range_left) {
int tmp = range_right;
range_right = range_left;
range_left = tmp;
std::swap(range_left, range_right);
range_swapped = true;
}
}
@ -2093,58 +2079,83 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
}
}
// resolve multiranges on memory decl
if (type == AST_MEMORY && children.size() > 1 && children[1]->type == AST_MULTIRANGE)
{
int total_size = 1;
multirange_dimensions.clear();
multirange_swapped.clear();
for (auto range : children[1]->children) {
if (!range->range_valid)
input_error("Non-constant range on memory decl.\n");
multirange_dimensions.push_back(min(range->range_left, range->range_right));
multirange_dimensions.push_back(max(range->range_left, range->range_right) - min(range->range_left, range->range_right) + 1);
multirange_swapped.push_back(range->range_swapped);
total_size *= multirange_dimensions.back();
// 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;
for (auto range : children[i]->children) {
width *= add_dimension(this, range);
if (i) unpacked_dimensions++;
}
delete children[i];
int left = width - 1, right = 0;
if (i)
std::swap(left, right);
children[i] = new AstNode(AST_RANGE, mkconst_int(left, true), mkconst_int(right, true));
fixup_hierarchy_flags();
did_something = true;
} else if (children[i]->type == AST_RANGE) {
add_dimension(this, children[i]);
if (i) unpacked_dimensions++;
}
}
} else {
// 1 bit signal: bit, logic or reg
dimensions.push_back({ 0, 1, false });
}
delete children[1];
children[1] = new AstNode(AST_RANGE, AstNode::mkconst_int(0, true), AstNode::mkconst_int(total_size-1, true));
fixup_hierarchy_flags();
did_something = true;
}
// resolve multiranges on memory access
if (type == AST_IDENTIFIER && id2ast && id2ast->type == AST_MEMORY && children.size() > 0 && children[0]->type == AST_MULTIRANGE)
// Resolve multidimensional array access.
if (type == AST_IDENTIFIER && !basic_prep && id2ast && (id2ast->type == AST_WIRE || id2ast->type == AST_MEMORY) &&
children.size() > 0 && (children[0]->type == AST_RANGE || children[0]->type == AST_MULTIRANGE))
{
AstNode *index_expr = nullptr;
int dims_sel = children[0]->type == AST_MULTIRANGE ? children[0]->children.size() : 1;
// Save original number of dimensions for $size() etc.
integer = dims_sel;
integer = children[0]->children.size(); // save original number of dimensions for $size() etc.
for (int i = 0; 2*i < GetSize(id2ast->multirange_dimensions); i++)
{
if (GetSize(children[0]->children) <= i)
input_error("Insufficient number of array indices for %s.\n", log_id(str));
// Split access into unpacked and packed parts.
AstNode *unpacked_range = nullptr;
AstNode *packed_range = nullptr;
AstNode *new_index_expr = children[0]->children[i]->children.at(0)->clone();
if (id2ast->multirange_dimensions[2*i])
new_index_expr = new AstNode(AST_SUB, new_index_expr, AstNode::mkconst_int(id2ast->multirange_dimensions[2*i], true));
if (i == 0)
index_expr = new_index_expr;
else
index_expr = new AstNode(AST_ADD, new AstNode(AST_MUL, index_expr, AstNode::mkconst_int(id2ast->multirange_dimensions[2*i+1], true)), new_index_expr);
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.
AstNode *&range = children[0]->type == AST_MULTIRANGE ? children[0]->children[0] : children[0];
unpacked_range = range;
range = nullptr;
}
}
for (int i = GetSize(id2ast->multirange_dimensions)/2; i < GetSize(children[0]->children); i++)
children.push_back(children[0]->children[i]->clone());
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.
AstNode *&range = children[0]->type == AST_MULTIRANGE ? children[0]->children[dims_sel - 1] : children[0];
packed_range = range;
range = nullptr;
}
}
delete children[0];
if (index_expr == nullptr)
children.erase(children.begin());
else
children[0] = new AstNode(AST_RANGE, index_expr);
for (auto &it : children)
delete it;
children.clear();
if (unpacked_range)
children.push_back(unpacked_range);
if (packed_range)
children.push_back(packed_range);
fixup_hierarchy_flags();
basic_prep = true;
did_something = true;
}
@ -2210,12 +2221,12 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
if (found_sname) {
// structure member, rewrite this node to reference the packed struct wire
auto range = make_struct_member_range(this, item_node);
auto range = make_index_range(item_node);
newNode = new AstNode(AST_IDENTIFIER, range);
newNode->str = sname;
// save type and original number of dimensions for $size() etc.
newNode->set_attribute(ID::wiretype, item_node->clone());
if (!item_node->multirange_dimensions.empty() && children.size() > 0) {
if (!item_node->dimensions.empty() && children.size() > 0) {
if (children[0]->type == AST_RANGE)
newNode->integer = 1;
else if (children[0]->type == AST_MULTIRANGE)
@ -2835,7 +2846,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
if (!children[0]->id2ast->range_valid)
goto skip_dynamic_range_lvalue_expansion;
AST::AstNode *member_node = get_struct_member(children[0]);
AST::AstNode *member_node = children[0]->get_struct_member();
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;
@ -2881,7 +2892,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
int dims = children[0]->integer;
stride = wire_width;
for (int dim = 0; dim < dims; dim++) {
stride /= get_struct_range_width(member_node, dim);
stride /= member_node->dimensions[dim].range_width;
}
bitno_div = stride;
} else {
@ -3042,6 +3053,8 @@ skip_dynamic_range_lvalue_expansion:;
// found right-hand side identifier for memory -> replace with memory read port
if (stage > 1 && type == AST_IDENTIFIER && id2ast != NULL && id2ast->type == AST_MEMORY && !in_lvalue &&
children.size() == 1 && children[0]->type == AST_RANGE && children[0]->children.size() == 1) {
if (integer < (unsigned)id2ast->unpacked_dimensions)
input_error("Insufficient number of array indices for %s.\n", log_id(str));
newNode = new AstNode(AST_MEMRD, children[0]->children[0]->clone());
newNode->str = str;
newNode->id2ast = id2ast;
@ -3100,6 +3113,9 @@ skip_dynamic_range_lvalue_expansion:;
children[0]->id2ast->children[0]->range_valid && children[0]->id2ast->children[1]->range_valid &&
(children[0]->children.size() == 1 || children[0]->children.size() == 2) && children[0]->children[0]->type == AST_RANGE)
{
if (children[0]->integer < (unsigned)children[0]->id2ast->unpacked_dimensions)
input_error("Insufficient number of array indices for %s.\n", log_id(str));
std::stringstream sstr;
sstr << "$memwr$" << children[0]->str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++);
std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA", id_en = sstr.str() + "_EN";
@ -3495,8 +3511,6 @@ skip_dynamic_range_lvalue_expansion:;
int result, high = 0, low = 0, left = 0, right = 0, width = 1; // defaults for a simple wire
AstNode *id_ast = NULL;
// Is this needed?
//while (buf->simplify(true, false, stage, width_hint, sign_hint, false)) { }
buf->detectSignWidth(width_hint, sign_hint);
if (buf->type == AST_IDENTIFIER) {
@ -3506,99 +3520,27 @@ skip_dynamic_range_lvalue_expansion:;
if (!id_ast)
input_error("Failed to resolve identifier %s for width detection!\n", buf->str.c_str());
// Check for item in packed struct / union
AST::AstNode *item_node = get_struct_member(buf);
if (id_ast->type == AST_WIRE && item_node) {
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;
// The dimension of the original array expression is saved in the 'integer' field
dim += buf->integer;
if (item_node->multirange_dimensions.empty()) {
if (dim != 1)
input_error("Dimension %d out of range in `%s', as it only has one dimension!\n", dim, item_node->str.c_str());
left = high = item_node->range_left;
right = low = item_node->range_right;
} else {
int dims = GetSize(item_node->multirange_dimensions)/2;
if (dim < 1 || dim > dims)
input_error("Dimension %d out of range in `%s', as it only has dimensions 1..%d!\n", dim, item_node->str.c_str(), dims);
right = low = get_struct_range_offset(item_node, dim - 1);
left = high = low + get_struct_range_width(item_node, dim - 1) - 1;
if (item_node->multirange_swapped[dim - 1]) {
std::swap(left, right);
}
for (int i = dim; i < dims; i++) {
mem_depth *= get_struct_range_width(item_node, i);
}
}
}
// Otherwise, we have 4 cases:
// wire x; ==> AST_WIRE, no AST_RANGE children
// wire [1:0]x; ==> AST_WIRE, AST_RANGE children
// wire [1:0]x[1:0]; ==> AST_MEMORY, two AST_RANGE children (1st for packed, 2nd for unpacked)
// wire [1:0]x[1:0][1:0]; ==> AST_MEMORY, one AST_RANGE child (0) for packed, then AST_MULTIRANGE child (1) for unpacked
// (updated: actually by the time we are here, AST_MULTIRANGE is converted into one big AST_RANGE)
// case 0 handled by default
else if ((id_ast->type == AST_WIRE || id_ast->type == AST_MEMORY) && id_ast->children.size() > 0) {
// handle packed array left/right for case 1, and cases 2/3 when requesting the last dimension (packed side)
AstNode *wire_range = id_ast->children[0];
left = wire_range->children[0]->integer;
right = wire_range->children[1]->integer;
high = max(left, right);
low = min(left, right);
}
if (id_ast->type == AST_MEMORY) {
// a slice of our identifier means we advance to the next dimension, e.g. $size(a[3])
if (buf->children.size() > 0) {
// something is hanging below this identifier
if (buf->children[0]->type == AST_RANGE && buf->integer == 0)
// if integer == 0, this node was originally created as AST_RANGE so it's dimension is 1
dim++;
// more than one range, e.g. $size(a[3][2])
else // created an AST_MULTIRANGE, converted to AST_RANGE, but original dimension saved in 'integer' field
dim += buf->integer; // increment by multirange size
}
// We got here only if the argument is a memory
// Otherwise $size() and $bits() return the expression width
AstNode *mem_range = id_ast->children[1];
if (str == "\\$bits") {
if (mem_range->type == AST_RANGE) {
if (!mem_range->range_valid)
input_error("Failed to detect width of memory access `%s'!\n", buf->str.c_str());
mem_depth = mem_range->range_left - mem_range->range_right + 1;
} else
input_error("Unknown memory depth AST type in `%s'!\n", buf->str.c_str());
} else {
// $size(), $left(), $right(), $high(), $low()
int dims = 1;
if (mem_range->type == AST_RANGE) {
if (id_ast->multirange_dimensions.empty()) {
if (!mem_range->range_valid)
input_error("Failed to detect width of memory access `%s'!\n", buf->str.c_str());
if (dim == 1) {
left = mem_range->range_right;
right = mem_range->range_left;
high = max(left, right);
low = min(left, right);
}
} else {
dims = GetSize(id_ast->multirange_dimensions)/2;
if (dim <= dims) {
width_hint = id_ast->multirange_dimensions[2*dim-1];
high = id_ast->multirange_dimensions[2*dim-2] + id_ast->multirange_dimensions[2*dim-1] - 1;
low = id_ast->multirange_dimensions[2*dim-2];
if (id_ast->multirange_swapped[dim-1]) {
left = low;
right = high;
} else {
right = low;
left = high;
}
} else if ((dim > dims+1) || (dim < 0))
input_error("Dimension %d out of range in `%s', as it only has dimensions 1..%d!\n", dim, buf->str.c_str(), dims+1);
}
} else {
input_error("Unknown memory depth AST type in `%s'!\n", buf->str.c_str());
}
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);
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;
}
}
width = high - low + 1;
@ -4180,7 +4122,7 @@ replace_fcall_later:;
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;
}
AST::AstNode *member_node = get_struct_member(this);
AstNode *member_node = get_struct_member();
int chunk_offset = member_node ? member_node->range_right : 0;
log_assert(!(chunk_offset && param_upto));
for (int i = tmp_range_right; i <= tmp_range_left; i++) {
@ -4820,6 +4762,9 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg
{
AstNode *mem = id2ast;
if (integer < (unsigned)mem->unpacked_dimensions)
input_error("Insufficient number of array indices for %s.\n", log_id(str));
// flag if used after blocking assignment (in same proc)
if ((proc_flags[mem] & AstNode::MEM2REG_FL_EQ1) && !(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_EQ2)) {
mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line));
@ -5103,7 +5048,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
int width;
if (bit_part_sel)
{
bit_part_sel->dumpAst(nullptr, "? ");
// bit_part_sel->dumpAst(nullptr, "? ");
if (bit_part_sel->children.size() == 1)
width = 0;
else

View File

@ -197,9 +197,20 @@ static AstNode *checkRange(AstNode *type_node, AstNode *range_node)
range_node = makeRange(type_node->range_left, type_node->range_right, false);
}
}
if (range_node && range_node->children.size() != 2) {
frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]");
if (range_node) {
bool valid = true;
if (range_node->type == AST_RANGE) {
valid = range_node->children.size() == 2;
} else { // AST_MULTIRANGE
for (auto child : range_node->children) {
valid = valid && child->children.size() == 2;
}
}
if (!valid)
frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form [<expr>:<expr>]");
}
return range_node;
}
@ -672,7 +683,7 @@ module_arg:
ast_stack.back()->children.push_back(astbuf2);
delete astbuf1; // really only needed if multiple instances of same type.
} module_arg_opt_assignment |
attr wire_type range TOK_ID {
attr wire_type range_or_multirange TOK_ID {
AstNode *node = $2;
node->str = *$4;
SET_AST_NODE_LOC(node, @4, @4);
@ -1165,7 +1176,7 @@ task_func_args:
task_func_port | task_func_args ',' task_func_port;
task_func_port:
attr wire_type range {
attr wire_type range_or_multirange {
bool prev_was_input = true;
bool prev_was_output = false;
if (albuf) {
@ -1928,7 +1939,7 @@ struct_var: TOK_ID { auto *var_node = astbuf2->clone();
/////////
wire_decl:
attr wire_type range {
attr wire_type range_or_multirange {
albuf = $1;
astbuf1 = $2;
astbuf2 = checkRange(astbuf1, $3);
@ -2104,7 +2115,7 @@ type_name: TOK_ID // first time seen
;
typedef_decl:
TOK_TYPEDEF typedef_base_type range type_name range_or_multirange ';' {
TOK_TYPEDEF typedef_base_type range_or_multirange type_name range_or_multirange ';' {
astbuf1 = $2;
astbuf2 = checkRange(astbuf1, $3);
if (astbuf2)

View File

@ -16,17 +16,17 @@ assert property ($size({3{x}}) == 3*4);
assert property ($size(y) == 6);
assert property ($size(y, 1) == 6);
assert property ($size(y, (1+1)) == 4);
assert property ($size(y[2], 1) == 4);
// This is unsupported at the moment
//assert property ($size(y[2], 1) == 4);
//assert property ($size(y[2][1], 1) == 1);
assert property ($size(z) == 6);
assert property ($size(z, 1) == 6);
assert property ($size(z, 2) == 8);
assert property ($size(z, 3) == 4);
// This is unsupported at the moment
assert property ($size(z[3], 1) == 8);
assert property ($size(z[3][3], 1) == 4);
// This is unsupported at the moment
//assert property ($size(z[3][3][3], 1) == 1);
// This should trigger an error if enabled (it does).
//assert property ($size(z, 4) == 4);

View File

@ -8,9 +8,21 @@ module top;
logic a [3];
logic b [3][5];
logic c [3][5][7];
logic [2:0] d;
logic [2:0][4:0] e;
logic [2:0][4:0][6:0] f;
logic [2:0] g [3];
logic [2:0][4:0] h [3][5];
logic [2:0][4:0][6:0] i [3][5][7];
`STATIC_ASSERT($bits(a) == 3);
`STATIC_ASSERT($bits(b) == 15);
`STATIC_ASSERT($bits(c) == 105);
`STATIC_ASSERT($bits(d) == 3);
`STATIC_ASSERT($bits(e) == 15);
`STATIC_ASSERT($bits(f) == 105);
`STATIC_ASSERT($bits(g) == 9);
`STATIC_ASSERT($bits(h) == 225);
`STATIC_ASSERT($bits(i) == 11025);
endmodule