|
|
|
@ -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
|
|
|
|
|