diff --git a/docs/source/cell/word_mux.rst b/docs/source/cell/word_mux.rst index 3eca310f3..234d1016b 100644 --- a/docs/source/cell/word_mux.rst +++ b/docs/source/cell/word_mux.rst @@ -24,8 +24,8 @@ are zero, the value from ``A`` input is sent to the output. If the :math:`n`\ 'th bit from ``S`` is set, the value :math:`n`\ 'th ``WIDTH`` bits wide slice of the ``B`` input is sent to the output. When more than one bit from ``S`` is set the output is undefined. Cells of this type are used to model "parallel cases" -(defined by using the ``parallel_case`` attribute or detected by an -optimization). +(defined by using the ``parallel_case`` attribute, the ``unique`` or ``unique0`` +SystemVerilog keywords, or detected by an optimization). The `$tribuf` cell is used to implement tristate logic. Cells of this type have a ``WIDTH`` parameter and inputs ``A`` and ``EN`` and an output ``Y``. The ``A`` diff --git a/docs/source/yosys_internals/verilog.rst b/docs/source/yosys_internals/verilog.rst index eef215e5d..0039aaab7 100644 --- a/docs/source/yosys_internals/verilog.rst +++ b/docs/source/yosys_internals/verilog.rst @@ -377,7 +377,7 @@ from SystemVerilog: - Assignments within expressions are supported. - The ``unique``, ``unique0``, and ``priority`` SystemVerilog keywords are - accepted on ``if`` and ``case`` conditionals. (Those keywords are currently - handled in the same way as their equivalent ``full_case`` and - ``parallel_case`` attributes on ``case`` statements, and checked - for syntactic validity but otherwise ignored on ``if`` statements.) + supported on ``if`` and ``case`` conditionals. (The Verilog frontend + will process conditionals using these keywords by annotating their + representation with the appropriate ``full_case`` and/or ``parallel_case`` + attributes, which are described above.) diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index fc236fa29..7e53005f3 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -2873,16 +2873,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 { @@ -2908,21 +2926,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: diff --git a/tests/proc/case_attr.ys b/tests/proc/case_attr.ys new file mode 100644 index 000000000..63be79fe5 --- /dev/null +++ b/tests/proc/case_attr.ys @@ -0,0 +1,57 @@ +read_verilog -sv << EOF +module top (input A, B, C, D, E, F, output reg W, X, Y, Z); + always @* begin + W = F; + (* full_case *) + case (C) + A: W = D; + B: W = E; + endcase + end + + always @* begin + X = F; + case (C) + A: X = D; + B: X = E; + endcase + end + + always @* begin + Y = F; + (* full_case, parallel_case *) + case (C) + A: Y = D; + B: Y = E; + endcase + end + + always @* begin + Z = F; + (* parallel_case *) + case (C) + A: Z = D; + B: Z = E; + endcase + end +endmodule +EOF +prep +# For the ones which use full_case, the F signal shouldn't be included in +# the input cone of W and Y. +select -set full o:W o:Y %u %ci* +select -assert-none i:F @full %i +select -assert-count 1 o:X %ci* i:F %i +select -assert-count 1 o:Z %ci* i:F %i + +# And for parallel_case there should be 1 $pmux compared to the 2 $mux +# cells without. +select -assert-none o:W %ci* t:$pmux %i +select -assert-none o:X %ci* t:$pmux %i +select -assert-count 1 o:Y %ci* t:$pmux %i +select -assert-count 1 o:Z %ci* t:$pmux %i + +select -assert-count 2 o:W %ci* t:$mux %i +select -assert-count 2 o:X %ci* t:$mux %i +select -assert-none o:Y %ci* t:$mux %i +select -assert-none o:Z %ci* t:$mux %i diff --git a/tests/verilog/unique_priority_case.ys b/tests/verilog/unique_priority_case.ys new file mode 100644 index 000000000..1f87fb19c --- /dev/null +++ b/tests/verilog/unique_priority_case.ys @@ -0,0 +1,54 @@ +read_verilog -sv << EOF +module top (input A, B, C, D, E, F, output reg W, X, Y, Z); + always_comb begin + W = F; + priority case (C) + A: W = D; + B: W = E; + endcase + end + + always_comb begin + X = F; + case (C) + A: X = D; + B: X = E; + endcase + end + + always_comb begin + Y = F; + unique case (C) + A: Y = D; + B: Y = E; + endcase + end + + always_comb begin + Z = F; + unique0 case (C) + A: Z = D; + B: Z = E; + endcase + end +endmodule +EOF +prep +# For the ones which use priority/unique, the F signal shouldn't be included in +# the input cone of W and Y. +select -set full o:W o:Y %u %ci* +select -assert-none i:F @full %i +select -assert-count 1 o:X %ci* i:F %i +select -assert-count 1 o:Z %ci* i:F %i + +# And for unique/unique0 there should be 1 $pmux compared to the 2 $mux +# cells without. +select -assert-none o:W %ci* t:$pmux %i +select -assert-none o:X %ci* t:$pmux %i +select -assert-count 1 o:Y %ci* t:$pmux %i +select -assert-count 1 o:Z %ci* t:$pmux %i + +select -assert-count 2 o:W %ci* t:$mux %i +select -assert-count 2 o:X %ci* t:$mux %i +select -assert-none o:Y %ci* t:$mux %i +select -assert-none o:Z %ci* t:$mux %i diff --git a/tests/verilog/unique_priority_if.ys b/tests/verilog/unique_priority_if.ys new file mode 100644 index 000000000..a6053e809 --- /dev/null +++ b/tests/verilog/unique_priority_if.ys @@ -0,0 +1,54 @@ +read_verilog -sv << EOF +module top (input A, B, C, D, E, F, output reg W, X, Y, Z); + always_comb begin + W = F; + priority if (C == A) + W = D; + else if (C == B) + W = E; + end + + always_comb begin + X = F; + if (C == A) + X = D; + else if (C == B) + X = E; + end + + always_comb begin + Y = F; + unique if (C == A) + Y = D; + else if (C == B) + Y = E; + end + + always_comb begin + Z = F; + unique0 if (C == A) + Z = D; + else if (C == B) + Z = E; + end +endmodule +EOF +prep +# For the ones which use priority/unique, the F signal shouldn't be included in +# the input cone of W and Y. +select -set full o:W o:Y %u %ci* +select -assert-none i:F @full %i +select -assert-count 1 o:X %ci* i:F %i +select -assert-count 1 o:Z %ci* i:F %i + +# And for unique/unique0 there should be 1 $pmux compared to the 2 $mux +# cells without. +select -assert-none o:W %ci* t:$pmux %i +select -assert-none o:X %ci* t:$pmux %i +select -assert-count 1 o:Y %ci* t:$pmux %i +select -assert-count 1 o:Z %ci* t:$pmux %i + +select -assert-count 2 o:W %ci* t:$mux %i +select -assert-count 2 o:X %ci* t:$mux %i +select -assert-none o:Y %ci* t:$mux %i +select -assert-none o:Z %ci* t:$mux %i