Merge pull request #1338 from larsclausen/byte-array-string-literal

Support assignment of string literals to byte arrays
This commit is contained in:
Cary R. 2026-05-06 20:44:13 -07:00 committed by GitHub
commit 99c7a9f940
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 382 additions and 7 deletions

View File

@ -690,12 +690,16 @@ class PEString : public PExpr {
virtual unsigned test_width(Design*des, NetScope*scope,
width_mode_t&mode) override;
virtual NetEConst*elaborate_expr(Design*des, NetScope*scope,
virtual NetExpr*elaborate_expr(Design*des, NetScope*scope,
ivl_type_t type, unsigned flags) const override;
virtual NetEConst*elaborate_expr(Design*des, NetScope*,
unsigned expr_wid, unsigned) const override;
NetExpr *elaborate_expr_uarray_(Design *des, NetScope *scope,
const netuarray_t *uarray_type,
const std::vector<netrange_t> &dims,
unsigned int cur_dim) const;
private:
char*text_;
};

View File

@ -344,6 +344,9 @@ NetExpr* PEAssignPattern::elaborate_expr_uarray_(Design *des, NetScope *scope,
if (const auto ap = dynamic_cast<PEAssignPattern*>(parms_[idx])) {
expr = ap->elaborate_expr_uarray_(des, scope, uarray_type,
dims, cur_dim, need_const);
} else if (auto s = dynamic_cast<PEString*>(parms_[idx])) {
expr = s->elaborate_expr_uarray_(des, scope, uarray_type,
dims, cur_dim);
} else if (dynamic_cast<PEConcat*>(parms_[idx])) {
cerr << get_fileline() << ": sorry: "
<< "Array concatenation is not yet supported."
@ -6986,13 +6989,86 @@ unsigned PEString::test_width(Design*, NetScope*, width_mode_t&)
return expr_width_;
}
NetEConst* PEString::elaborate_expr(Design*, NetScope*, ivl_type_t, unsigned) const
NetExpr* PEString::elaborate_expr_uarray_(Design *des, NetScope *,
const netuarray_t *uarray_type,
const std::vector<netrange_t> &dims,
unsigned int cur_dim) const
{
NetECString*tmp = new NetECString(value());
tmp->cast_signed(signed_flag_);
tmp->set_line(*this);
// This is a special case. The LRM allows string literals to be
// assigned to unpacked arrays of bytes.
const auto element_type = uarray_type->element_type();
return tmp;
if (dims.size() - 1 != cur_dim || !element_type->packed() ||
element_type->base_type() != IVL_VT_BOOL ||
element_type->packed_width() != 8 || !element_type->get_signed()) {
cerr << get_fileline() << ": error: "
<< "String literal can not be implicitly cast to the target type."
<< endl;
des->errors++;
return nullptr;
}
// The size doesn't have to match. Elements are copied left aligned, which
// is different from assignments of string literals to packed arrays where
// they are copied right aligned.
vector<NetExpr*> elem_exprs(dims[cur_dim].width());
bool asc = dims[cur_dim].get_msb() < dims[cur_dim].get_lsb();
unsigned int elem_idx = asc ? 0 : elem_exprs.size() - 1;
verinum text_val(text_);
if (text_val.len() > elem_exprs.size() * 8) {
cerr << get_fileline() << ": warning: "
<< "Target array smaller than assigned value. "
<< "Value will be truncated." << endl;
}
for (unsigned int i = 0; i < min((size_t)text_val.len() / 8, elem_exprs.size()); i++) {
verinum val(text_val >> (text_val.len() - 8 - i * 8), 8);
val.has_sign(true);
elem_exprs[elem_idx] = new NetEConst(element_type, val);
if (asc)
elem_idx++;
else
elem_idx--;
}
// Add padding if necessary
for (unsigned int i = text_val.len() / 8; i < elem_exprs.size(); i++) {
verinum val(verinum::V0, 8);
val.has_sign(true);
elem_exprs[elem_idx] = new NetEConst(element_type, val);
if (asc)
elem_idx++;
else
elem_idx--;
}
return new NetEArrayPattern(uarray_type, elem_exprs);
}
NetExpr* PEString::elaborate_expr(Design *des, NetScope *scope, ivl_type_t type, unsigned) const
{
NetExpr *expr;
auto uarray_type = dynamic_cast<const netuarray_t*>(type);
if (uarray_type) {
expr = elaborate_expr_uarray_(des, scope, uarray_type,
uarray_type->static_dimensions(), 0);
} else {
expr = new NetECString(value());
expr->cast_signed(signed_flag_);
}
if (expr)
expr->set_line(*this);
return expr;
}
/*

View File

@ -266,7 +266,8 @@ NetNet *elaborate_unpacked_array(Design *des, NetScope *scope, const LineInfo &l
<< endl;
des->errors++;
return nullptr;
} else if (dynamic_cast<PEAssignPattern*> (expr)) {
} else if (dynamic_cast<PEAssignPattern*> (expr) ||
dynamic_cast<PEString*> (expr)) {
auto net_expr = elaborate_rval_expr(des, scope, lval->array_type(), expr);
if (! net_expr) return nullptr;
expr_net = net_expr->synthesize(des, scope, net_expr);

View File

@ -0,0 +1,45 @@
// Check that string literals can be assigned to one-dimensional byte arrays.
module test;
bit failed = 1'b0;
`define check(val, exp) do \
if (val !== exp) begin \
$display("FAILED(%0d). '%s' expected %02h, got %02h", `__LINE__, \
`"val`", exp, val); \
failed = 1'b1; \
end \
while(0)
byte desc [3:0] = "AB\n";
byte asc [0:3];
byte unsized [4];
assign asc = "AB\n";
initial begin
#1;
`check(desc[3], 8'h41);
`check(desc[2], 8'h42);
`check(desc[1], 8'h0a);
`check(desc[0], 8'h00);
`check(asc[0], 8'h41);
`check(asc[1], 8'h42);
`check(asc[2], 8'h0a);
`check(asc[3], 8'h00);
unsized = "AB\n";
`check(unsized[0], 8'h41);
`check(unsized[1], 8'h42);
`check(unsized[2], 8'h0a);
`check(unsized[3], 8'h00);
if (!failed) begin
$display("PASSED");
end
end
endmodule

View File

@ -0,0 +1,61 @@
// Check that string literals can be assigned to nested byte arrays using
// assignment patterns.
module test;
bit failed = 1'b0;
`define check(val, exp) do \
if (val !== exp) begin \
$display("FAILED(%0d). '%s' expected %02h, got %02h", `__LINE__, \
`"val`", exp, val); \
failed = 1'b1; \
end \
while(0)
byte desc [0:1][3:0] = '{"AB\n", "CD\t"};
byte asc [1:0][0:3];
byte unsized [2][4];
assign asc = '{"AB\n", "CD\t"};
initial begin
#1;
`check(desc[0][3], 8'h41);
`check(desc[0][2], 8'h42);
`check(desc[0][1], 8'h0a);
`check(desc[0][0], 8'h00);
`check(desc[1][3], 8'h43);
`check(desc[1][2], 8'h44);
`check(desc[1][1], 8'h09);
`check(desc[1][0], 8'h00);
`check(asc[0][0], 8'h43);
`check(asc[0][1], 8'h44);
`check(asc[0][2], 8'h09);
`check(asc[0][3], 8'h00);
`check(asc[1][0], 8'h41);
`check(asc[1][1], 8'h42);
`check(asc[1][2], 8'h0a);
`check(asc[1][3], 8'h00);
unsized = '{"AB\n", "CD\t"};
`check(unsized[0][0], 8'h41);
`check(unsized[0][1], 8'h42);
`check(unsized[0][2], 8'h0a);
`check(unsized[0][3], 8'h00);
`check(unsized[1][0], 8'h43);
`check(unsized[1][1], 8'h44);
`check(unsized[1][2], 8'h09);
`check(unsized[1][3], 8'h00);
if (!failed) begin
$display("PASSED");
end
end
endmodule

View File

@ -0,0 +1,46 @@
// Check that string literals shorter than the target byte array are padded
// with null bytes.
module test;
bit failed = 1'b0;
`define check(val, exp) do \
if (val !== exp) begin \
$display("FAILED(%0d). '%s' expected %02h, got %02h", `__LINE__, \
`"val`", exp, val); \
failed = 1'b1; \
end \
while(0)
byte desc [3:0] = "AB";
byte asc [0:3];
byte unsized [4];
assign asc = "AB";
initial begin
#1;
`check(desc[3], 8'h41);
`check(desc[2], 8'h42);
`check(desc[1], 8'h00);
`check(desc[0], 8'h00);
`check(asc[0], 8'h41);
`check(asc[1], 8'h42);
`check(asc[2], 8'h00);
`check(asc[3], 8'h00);
unsized = "AB";
`check(unsized[0], 8'h41);
`check(unsized[1], 8'h42);
`check(unsized[2], 8'h00);
`check(unsized[3], 8'h00);
if (!failed) begin
$display("PASSED");
end
end
endmodule

View File

@ -0,0 +1,39 @@
// Check that string literals longer than the target byte array are truncated.
module test;
bit failed = 1'b0;
`define check(val, exp) do \
if (val !== exp) begin \
$display("FAILED(%0d). '%s' expected %02h, got %02h", `__LINE__, \
`"val`", exp, val); \
failed = 1'b1; \
end \
while(0)
byte desc [1:0] = "ABC";
byte asc [0:1];
byte unsized [2];
assign asc = "ABC";
initial begin
#1;
`check(desc[1], 8'h41);
`check(desc[0], 8'h42);
`check(asc[0], 8'h41);
`check(asc[1], 8'h42);
unsized = "ABCD";
`check(unsized[0], 8'h41);
`check(unsized[1], 8'h42);
if (!failed) begin
$display("PASSED");
end
end
endmodule

View File

@ -0,0 +1,8 @@
// Check that string literals cannot be assigned to unpacked arrays whose
// element type is 4-state.
module test;
logic [7:0] value [0:3] = "AB"; // Error: target element type is 4-state
endmodule

View File

@ -0,0 +1,8 @@
// Check that string literals cannot be assigned directly to multi-dimensional
// byte arrays.
module test;
byte value [0:1][0:3] = "AB"; // Error: string is not nested
endmodule

View File

@ -0,0 +1,17 @@
// Check that string literals cannot be connected to output byte array ports.
module M (
output byte out [0:1]
);
initial begin
out = "CD";
end
endmodule
module test;
M i_m("AB"); // Error: output expression is not assignable
endmodule

View File

@ -0,0 +1,8 @@
// Check that string literals cannot be assigned to unpacked arrays whose
// element type is narrower than 8 bits.
module test;
bit [6:0] value [0:3] = "AB"; // Error: target element type is too narrow
endmodule

View File

@ -0,0 +1,8 @@
// Check that string literals cannot be assigned to unpacked arrays whose
// element type is wider than 8 bits.
module test;
bit [8:0] value [0:3] = "AB"; // Error: target element type is too wide
endmodule

View File

@ -226,6 +226,15 @@ sv_array_cassign6 vvp_tests/sv_array_cassign6.json
sv_array_cassign7 vvp_tests/sv_array_cassign7.json
sv_array_cassign8 vvp_tests/sv_array_cassign8.json
sv_automatic_2state vvp_tests/sv_automatic_2state.json
sv_byte_array_string1 vvp_tests/sv_byte_array_string1.json
sv_byte_array_string2 vvp_tests/sv_byte_array_string2.json
sv_byte_array_string3 vvp_tests/sv_byte_array_string3.json
sv_byte_array_string4 vvp_tests/sv_byte_array_string4.json
sv_byte_array_string_fail1 vvp_tests/sv_byte_array_string_fail1.json
sv_byte_array_string_fail2 vvp_tests/sv_byte_array_string_fail2.json
sv_byte_array_string_fail3 vvp_tests/sv_byte_array_string_fail3.json
sv_byte_array_string_fail4 vvp_tests/sv_byte_array_string_fail4.json
sv_byte_array_string_fail5 vvp_tests/sv_byte_array_string_fail5.json
sv_chained_constructor1 vvp_tests/sv_chained_constructor1.json
sv_chained_constructor2 vvp_tests/sv_chained_constructor2.json
sv_chained_constructor3 vvp_tests/sv_chained_constructor3.json

View File

@ -0,0 +1,5 @@
{
"type" : "normal",
"source" : "sv_byte_array_string1.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "normal",
"source" : "sv_byte_array_string2.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "normal",
"source" : "sv_byte_array_string3.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "normal",
"source" : "sv_byte_array_string4.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "CE",
"source" : "sv_byte_array_string_fail1.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "CE",
"source" : "sv_byte_array_string_fail2.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "CE",
"source" : "sv_byte_array_string_fail3.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "CE",
"source" : "sv_byte_array_string_fail4.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "CE",
"source" : "sv_byte_array_string_fail5.v",
"iverilog-args" : [ "-g2005-sv" ]
}