Merge pull request #1445 from larsclausen/procedural-block-prefix-label

Support prefix labels on procedural blocks
This commit is contained in:
Cary R. 2026-07-20 21:32:25 -07:00 committed by GitHub
commit 31d1850bc8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 331 additions and 45 deletions

View File

@ -0,0 +1,18 @@
module test;
logic a;
always_comb begin
my_blk: begin
a = 1'b1;
end
end
initial begin
#1;
if (a !== 1'b1) begin
$display("FAILED(%0d). Expected 1, got %b", `__LINE__, a);
end else begin
$display("PASSED");
end
end
endmodule

View File

@ -0,0 +1,29 @@
// Check that sequential block prefix labels work.
module test;
reg failed;
reg value;
initial begin
failed = 1'b0;
value = 1'b0;
BLOCK_LABEL: (* keep = 1 *) begin
value = 1'b1;
disable BLOCK_LABEL;
value = 1'b0;
end : BLOCK_LABEL
if (value !== 1'b1) begin
$display("FAILED(%0d). Block prefix label did not create a named scope",
`__LINE__);
failed = 1'b1;
end
if (!failed) begin
$display("PASSED");
end
end
endmodule

View File

@ -0,0 +1,6 @@
// Check that different prefix and post-begin labels are rejected.
module test;
initial LABEL_A: begin : LABEL_B
end
endmodule

View File

@ -0,0 +1,6 @@
// Check that matching prefix and post-begin labels are rejected.
module test;
initial LABEL: begin : LABEL
end
endmodule

View File

@ -0,0 +1,36 @@
// Check that fork prefix labels work with all join types.
module test;
reg failed;
reg [2:0] value;
initial begin
failed = 1'b0;
value = 3'b000;
FORK_LABEL: (* keep = 1 *) fork
value[0] = 1'b1;
join : FORK_LABEL
FORK_ANY_LABEL: (* keep = 1 *) fork
value[1] = 1'b1;
join_any : FORK_ANY_LABEL
FORK_NONE_LABEL: (* keep = 1 *) fork
value[2] = 1'b1;
join_none : FORK_NONE_LABEL
wait fork;
if (value !== 3'b111) begin
$display("FAILED(%0d). Fork prefix labels did not execute all blocks",
`__LINE__);
failed = 1'b1;
end
if (!failed) begin
$display("PASSED");
end
end
endmodule

View File

@ -0,0 +1,6 @@
// Check that different prefix and post-fork labels are rejected.
module test;
initial LABEL_A: fork : LABEL_B
join
endmodule

View File

@ -0,0 +1,6 @@
// Check that matching prefix and post-fork labels are rejected.
module test;
initial LABEL: fork : LABEL
join
endmodule

View File

@ -0,0 +1,29 @@
// Check that sequential block prefix labels can shadow type identifiers.
typedef int T;
module test;
reg failed;
reg value;
initial begin
failed = 1'b0;
value = 1'b0;
T: begin
value = 1'b1;
end : T
if (value !== 1'b1) begin
$display("FAILED(%0d). Block prefix label did not hide typedef",
`__LINE__);
failed = 1'b1;
end
if (!failed) begin
$display("PASSED");
end
end
endmodule

View File

@ -0,0 +1,29 @@
// Check that fork prefix labels can shadow type identifiers.
typedef int T;
module test;
reg failed;
reg value;
initial begin
failed = 1'b0;
value = 1'b0;
T: fork
value = 1'b1;
join : T
if (value !== 1'b1) begin
$display("FAILED(%0d). Fork prefix label did not hide typedef",
`__LINE__);
failed = 1'b1;
end
if (!failed) begin
$display("PASSED");
end
end
endmodule

View File

@ -82,6 +82,7 @@ br_gh1256b vvp_tests/br_gh1256b.json
br_gh1258a vvp_tests/br_gh1258a.json
br_gh1258b vvp_tests/br_gh1258b.json
br_gh1286 vvp_tests/br_gh1286.json
br_gh1321 vvp_tests/br_gh1321.json
br_gh1323 vvp_tests/br_gh1323.json
br_gh1384 vvp_tests/br_gh1384.json
br_gh1385 vvp_tests/br_gh1385.json
@ -264,6 +265,9 @@ sv_array_cassign_single_fail1 vvp_tests/sv_array_cassign_single_fail1.json
sv_assign_pattern_auto_force_fail vvp_tests/sv_assign_pattern_auto_force_fail.json
sv_automatic_2state vvp_tests/sv_automatic_2state.json
sv_bad_member_lval_proc_fail vvp_tests/sv_bad_member_lval_proc_fail.json
sv_block_prefix_label vvp_tests/sv_block_prefix_label.json
sv_block_prefix_name_diff_fail vvp_tests/sv_block_prefix_name_diff_fail.json
sv_block_prefix_name_same_fail vvp_tests/sv_block_prefix_name_same_fail.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
@ -320,6 +324,9 @@ sv_default_port_value2 vvp_tests/sv_default_port_value2.json
sv_default_port_value3 vvp_tests/sv_default_port_value3.json
sv_foreach9 vvp_tests/sv_foreach9.json
sv_foreach10 vvp_tests/sv_foreach10.json
sv_fork_prefix_label vvp_tests/sv_fork_prefix_label.json
sv_fork_prefix_name_diff_fail vvp_tests/sv_fork_prefix_name_diff_fail.json
sv_fork_prefix_name_same_fail vvp_tests/sv_fork_prefix_name_same_fail.json
sv_interface vvp_tests/sv_interface.json
sv_interface_identifier_block_name vvp_tests/sv_interface_identifier_block_name.json
sv_interface_identifier_instance_name vvp_tests/sv_interface_identifier_instance_name.json
@ -420,6 +427,7 @@ sv_type_identifier_assert_label vvp_tests/sv_type_identifier_assert_label.json
sv_type_identifier_attribute_name vvp_tests/sv_type_identifier_attribute_name.json
sv_type_identifier_attribute_target vvp_tests/sv_type_identifier_attribute_target.json
sv_type_identifier_block_label_name vvp_tests/sv_type_identifier_block_label_name.json
sv_type_identifier_block_prefix_label_name vvp_tests/sv_type_identifier_block_prefix_label_name.json
sv_type_identifier_config_name vvp_tests/sv_type_identifier_config_name.json
sv_type_identifier_discipline_name vvp_tests/sv_type_identifier_discipline_name.json
sv_type_identifier_discipline_nature_ref vvp_tests/sv_type_identifier_discipline_nature_ref.json
@ -429,6 +437,7 @@ sv_type_identifier_for_name vvp_tests/sv_type_identifier_for_name.json
sv_type_identifier_foreach_array_name vvp_tests/sv_type_identifier_foreach_array_name.json
sv_type_identifier_foreach_name vvp_tests/sv_type_identifier_foreach_name.json
sv_type_identifier_fork_label_name vvp_tests/sv_type_identifier_fork_label_name.json
sv_type_identifier_fork_prefix_label_name vvp_tests/sv_type_identifier_fork_prefix_label_name.json
sv_type_identifier_function_name vvp_tests/sv_type_identifier_function_name.json
sv_type_identifier_generate_label_name vvp_tests/sv_type_identifier_generate_label_name.json
sv_type_identifier_genvar_name vvp_tests/sv_type_identifier_genvar_name.json

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,9 @@
{
"type" : "normal",
"source" : "sv_fork_prefix_label.v",
"iverilog-args" : [ "-g2005-sv" ],
"vlog95" : {
"__comment" : "join_any, join_none, and wait fork are not supported",
"type" : "CE"
}
}

View File

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

View File

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

View File

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

View File

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

153
parse.y
View File

@ -594,6 +594,71 @@ static void procedural_item_list_add_declaration(const YYLTYPE&loc,
procedural_item_list_add_ports(list, ports);
}
static PBlock *pform_start_block(const YYLTYPE&loc, const char *name,
PBlock::BL_TYPE block_type)
{
auto block = pform_push_block_scope(loc, name, block_type);
current_block_stack.push(block);
return block;
}
static char *pform_start_block_with_labels(
const YYLTYPE&loc, const YYLTYPE&name_loc,
char *prefix_label, char *block_name,
std::list<named_pexpr_t> *attributes,
PBlock::BL_TYPE block_type)
{
std::unique_ptr<char[]> prefix_label_ptr(prefix_label);
std::unique_ptr<char[]> block_name_ptr(block_name);
if (prefix_label_ptr && block_name_ptr) {
yyerror(name_loc,
"error: A block cannot have both a prefix label and a block name.");
}
const char *name = block_name_ptr ? block_name_ptr.get()
: prefix_label_ptr.get();
auto block = pform_start_block(loc, name, block_type);
pform_bind_attributes(block->attributes, attributes);
return block_name_ptr ? block_name_ptr.release()
: prefix_label_ptr.release();
}
static PBlock *pform_finish_block(const YYLTYPE&block_loc,
const YYLTYPE&end_loc, const char *type,
char *raw_name, char *end_label,
PBlock::BL_TYPE block_type,
procedural_item_list_t *raw_items)
{
std::unique_ptr<char[]> name_ptr(raw_name);
std::unique_ptr<procedural_item_list_t> items(raw_items);
const char *name = name_ptr.get();
bool keep_scope = name || items->has_decls;
pform_pop_block_scope(keep_scope);
assert(! current_block_stack.empty());
auto block = current_block_stack.top();
current_block_stack.pop();
if (keep_scope) {
if (block_type != PBlock::BL_SEQ)
block->set_join_type(block_type);
} else {
delete block;
block = new PBlock(block_type);
FILE_NAME(block, block_loc);
}
if (items->statements)
block->set_statement(*items->statements);
check_end_label(end_loc, type, name, end_label);
return block;
}
static void port_declaration_context_init(void)
{
port_declaration_context.port_type = NetNet::PINOUT;
@ -756,6 +821,11 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type,
std::vector<Statement*>*statement_list;
struct procedural_item_list_t *procedural_item_list;
struct {
char *label;
std::list<named_pexpr_t> *attributes;
} block_prefix;
decl_assignment_t*decl_assignment;
std::list<decl_assignment_t*>*decl_assignments;
@ -1046,6 +1116,8 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type,
%type <procedural_item_list> block_item_or_statement_list_opt
%type <procedural_item_list> tf_item_or_statement_list
%type <procedural_item_list> tf_item_or_statement_list_opt
%type <block_prefix> block_prefix_opt
%type <text> sequential_block_start parallel_block_start
%type <statement> analog_statement
@ -7292,6 +7364,34 @@ subroutine_call
}
;
block_prefix_opt
: /* empty */
{ $$.label = nullptr;
$$.attributes = nullptr;
}
| identifier_name ':' attribute_list_opt
{ pform_requires_sv(@1, "Block prefix label");
$$.label = $1;
$$.attributes = $3;
}
;
sequential_block_start
: block_prefix_opt K_begin label_opt
{ $$ = pform_start_block_with_labels(@2, @3, $1.label, $3,
$1.attributes, PBlock::BL_SEQ);
@$ = @2;
}
;
parallel_block_start
: block_prefix_opt K_fork label_opt
{ $$ = pform_start_block_with_labels(@2, @3, $1.label, $3,
$1.attributes, PBlock::BL_PAR);
@$ = @2;
}
;
statement_item /* This is roughly statement_item in the LRM */
/* assign and deassign statements are procedural code to do
@ -7333,29 +7433,12 @@ statement_item /* This is roughly statement_item in the LRM */
the declarations. The scope is popped at the end of the block. */
/* In SystemVerilog an unnamed block can contain variable declarations. */
| K_begin label_opt
{ PBlock*tmp = pform_push_block_scope(@1, $2, PBlock::BL_SEQ);
current_block_stack.push(tmp);
}
block_item_or_statement_list_opt K_end label_opt
{ std::unique_ptr<procedural_item_list_t> items($4);
if (!$2 && items->has_decls) {
| sequential_block_start block_item_or_statement_list_opt K_end label_opt
{ if (!$1 && $2->has_decls) {
pform_block_decls_requires_sv();
}
bool keep_scope = $2 || items->has_decls;
pform_pop_block_scope(keep_scope);
assert(! current_block_stack.empty());
auto tmp = current_block_stack.top();
current_block_stack.pop();
if (!keep_scope) {
delete tmp;
tmp = new PBlock(PBlock::BL_SEQ);
FILE_NAME(tmp, @1);
}
if (items->statements) tmp->set_statement(*items->statements);
check_end_label(@6, "block", $2, $6);
delete[]$2;
$$ = tmp;
$$ = pform_finish_block(@1, @4, "block", $1, $4,
PBlock::BL_SEQ, $2);
}
/* fork-join blocks are very similar to begin-end blocks. In fact,
@ -7364,31 +7447,11 @@ statement_item /* This is roughly statement_item in the LRM */
code generator can do the right thing. */
/* In SystemVerilog an unnamed block can contain variable declarations. */
| K_fork label_opt
{ PBlock*tmp = pform_push_block_scope(@1, $2, PBlock::BL_PAR);
current_block_stack.push(tmp);
}
block_item_or_statement_list_opt join_keyword label_opt
{ std::unique_ptr<procedural_item_list_t> items($4);
if (!$2 && items->has_decls) {
pform_requires_sv(@4, "Variable declaration in unnamed block");
| parallel_block_start block_item_or_statement_list_opt join_keyword label_opt
{ if (!$1 && $2->has_decls) {
pform_requires_sv(@2, "Variable declaration in unnamed block");
}
bool keep_scope = $2 || items->has_decls;
pform_pop_block_scope(keep_scope);
assert(! current_block_stack.empty());
auto tmp = current_block_stack.top();
current_block_stack.pop();
if (keep_scope) {
tmp->set_join_type($5);
} else {
delete tmp;
tmp = new PBlock($5);
FILE_NAME(tmp, @1);
}
if (items->statements) tmp->set_statement(*items->statements);
check_end_label(@6, "fork", $2, $6);
delete[]$2;
$$ = tmp;
$$ = pform_finish_block(@1, @4, "fork", $1, $4, $3, $2);
}
| K_disable hierarchy_identifier ';'