mirror of
https://github.com/YosysHQ/yosys
synced 2025-04-10 03:07:14 +00:00
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:
parent
ac0fb2e301
commit
39fea32c6e
frontends
tests
|
@ -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
|
||||
|
|
|
@ -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>> ¶meters);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue