3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-06-09 15:43:25 +00:00

verilog: implement SystemVerilog unique/unique0/priority if semantics.

There are two elements involved:

1) Apply the relevant full_case and/or parallel_case attribute(s) to
the generated AST_CASE node(s), so that the existing AST frontend and
subsequent passes will generate RTLIL with appropriate behaviour.
(This is handled in the parser "if_attr" non-terminal.)

2) Rearrange the AST_CASE structure when necessary.  For "priority if"
(i.e., full_case), this requires only ensuring that directly nested
"else if" branches also inherit the full_case attribute.  For
"unique if" and "unique0 if" (i.e., parallel_case+full_case and
parallel_case alone), there are two steps:
    a) Flatten the AST_CASE structure such that any direct "else if"
    branches are mapped to additional AST_CONDs in the parent;
    b) Reverse the "direction" of the test: the constant 1 (true)
    is provided in the AST_CASE node, and the expression(s) in the
    if statement(s) are given in each AST_COND.  This is necessary
    because the constant 1, being the common factor, must occupy the
    shared AST_CASE position.
(This is handled in the parser "TOK_IF" expansion of behavioral_stmt.)

Observe that:
 * The generated AST has not been changed for bare "if"s (those
 without unique/priority).  This should minimise the risk of
 unexpected regressions.

 * It is possible that the flattening described in 2) a) above might
 affect the behaviour of expressions with side effects in "unique if"
 statements (consider "unique if( a ) ...; else if( b++ ) ...": if
 a is true, is b incremented?).  While it might be possible to provide
 precise semantics here, IEEE 1800-2012 12.4.2 seems to be deliberately
 vague ("In unique-if and unique0-if, the conditions may be evaluated
 and compared in any order[...] The presence of side effects in
 conditions may cause nondeterministic results.") and so it seems
 doubtful that there is benefit in Yosys providing stronger promises
 on the interpretation of questionable code.
This commit is contained in:
Gary Wong 2025-05-29 20:42:49 -06:00
parent 3ef4c91c31
commit 370d5871f4
2 changed files with 34 additions and 15 deletions

View file

@ -2872,16 +2872,34 @@ behavioral_stmt:
ast_stack.pop_back();
} |
if_attr TOK_IF '(' expr ')' {
AstNode *node = new AstNode(AST_CASE);
AstNode *node = 0;
AstNode *context = ast_stack.back();
if (context && context->type == AST_BLOCK && context->get_bool_attribute(ID::promoted_if)) {
AstNode *outer = ast_stack[ast_stack.size() - 2];
log_assert (outer && outer->type == AST_CASE);
if (outer->get_bool_attribute(ID::parallel_case)) {
// parallel "else if": append condition to outer "if"
node = outer;
log_assert (node->children.size());
delete node->children.back();
node->children.pop_back();
} else if (outer->get_bool_attribute(ID::full_case))
(*$1)[ID::full_case] = AstNode::mkconst_int(1, false);
}
AstNode *expr = new AstNode(AST_REDUCE_BOOL, $4);
if (!node) {
// not parallel "else if": begin new construction
node = new AstNode(AST_CASE);
append_attr(node, $1);
ast_stack.back()->children.push_back(node);
node->children.push_back(node->get_bool_attribute(ID::parallel_case) ? AstNode::mkconst_int(1, false, 1) : expr);
}
AstNode *block = new AstNode(AST_BLOCK);
AstNode *cond = new AstNode(AST_COND, AstNode::mkconst_int(1, false, 1), block);
AstNode *cond = new AstNode(AST_COND, node->get_bool_attribute(ID::parallel_case) ? expr : AstNode::mkconst_int(1, false, 1), block);
SET_AST_NODE_LOC(cond, @4, @4);
ast_stack.back()->children.push_back(node);
node->children.push_back(new AstNode(AST_REDUCE_BOOL, $4));
node->children.push_back(cond);
ast_stack.push_back(node);
ast_stack.push_back(block);
append_attr(node, $1);
} behavioral_stmt {
SET_AST_NODE_LOC(ast_stack.back(), @7, @7);
} optional_else {
@ -2907,21 +2925,25 @@ if_attr:
} |
attr TOK_UNIQUE0 {
AstNode *context = ast_stack.back();
if( context && context->type == AST_BLOCK && context->get_bool_attribute(ID::promoted_if) )
if (context && context->type == AST_BLOCK && context->get_bool_attribute(ID::promoted_if))
frontend_verilog_yyerror("unique0 keyword cannot be used for 'else if' branch.");
$$ = $1; // accept unique0 keyword, but ignore it for now
(*$1)[ID::parallel_case] = AstNode::mkconst_int(1, false);
$$ = $1;
} |
attr TOK_PRIORITY {
AstNode *context = ast_stack.back();
if( context && context->type == AST_BLOCK && context->get_bool_attribute(ID::promoted_if) )
if (context && context->type == AST_BLOCK && context->get_bool_attribute(ID::promoted_if))
frontend_verilog_yyerror("priority keyword cannot be used for 'else if' branch.");
$$ = $1; // accept priority keyword, but ignore it for now
(*$1)[ID::full_case] = AstNode::mkconst_int(1, false);
$$ = $1;
} |
attr TOK_UNIQUE {
AstNode *context = ast_stack.back();
if( context && context->type == AST_BLOCK && context->get_bool_attribute(ID::promoted_if) )
if (context && context->type == AST_BLOCK && context->get_bool_attribute(ID::promoted_if))
frontend_verilog_yyerror("unique keyword cannot be used for 'else if' branch.");
$$ = $1; // accept unique keyword, but ignore it for now
(*$1)[ID::full_case] = AstNode::mkconst_int(1, false);
(*$1)[ID::parallel_case] = AstNode::mkconst_int(1, false);
$$ = $1;
};
case_attr: