[ImportVerilog] Add full_case attribute support (#8762)

This patch adds support of `(* full_case *)` attribute in case 
statements [1], which says that all possible case items have been 
covered and no default clause is needed.

[1]: http://www1.pldworld.com/@xilinx/html/technote/TOOL/MANUAL/21i_doc/data/fndtn/ver/ver7_4.htm
This commit is contained in:
Michael 2025-07-22 20:46:16 +03:00 committed by GitHub
parent e80a67d30d
commit 9088c29f4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 53 additions and 1 deletions

View File

@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "ImportVerilogInternals.h"
#include "slang/ast/Compilation.h"
#include "slang/ast/SystemSubroutine.h"
#include "llvm/ADT/ScopeExit.h"
@ -258,6 +259,7 @@ struct StmtVisitor {
/// Handle case statements.
LogicalResult visit(const slang::ast::CaseStatement &caseStmt) {
using slang::ast::AttributeSymbol;
using slang::ast::CaseStatementCondition;
auto caseExpr = context.convertRvalueExpression(caseStmt.expr);
if (!caseExpr)
@ -335,6 +337,12 @@ struct StmtVisitor {
}
}
const auto caseStmtAttrs = context.compilation.getAttributes(caseStmt);
const bool hasFullCaseAttr =
llvm::find_if(caseStmtAttrs, [](const AttributeSymbol *attr) {
return attr->name == "full_case";
}) != caseStmtAttrs.end();
// Check if the case statement looks exhaustive assuming two-state values.
// We use this information to work around a common bug in input Verilog
// where a case statement enumerates all possible two-state values of the
@ -369,7 +377,13 @@ struct StmtVisitor {
// If the case statement is exhaustive assuming two-state values, don't
// generate the default case. Instead, branch to the last match block. This
// will essentially make the last case item the "default".
if (twoStateExhaustive && lastMatchBlock &&
//
// Alternatively, if the case statement has an (* full_case *) attribute
// but no default case, it indicates that the developer has intentionally
// covered all known possible values. Hence, the last match block is
// treated as the implicit "default" case.
if ((twoStateExhaustive || (hasFullCaseAttr && !caseStmt.defaultCase)) &&
lastMatchBlock &&
caseStmt.condition == CaseStatementCondition::Normal) {
builder.create<mlir::cf::BranchOp>(loc, lastMatchBlock);
} else {

View File

@ -2853,6 +2853,44 @@ function void seeminglyExhaustiveCase(logic [1:0] a);
endcase
endfunction
// Check that ImportVerilog recognizes case statements with (* full_case *)
// attribute, assuming the case expression uses a two-state values (0 and 1).
// The (* full_case *) attribute informs the synthesis tool that all possible
// cases are explicitly covered, even without a default clause, thereby
// preventing latch inference.
// CHECK-LABEL: @verifyFullCaseSupport
function void verifyFullCaseSupport(logic [1:0] a);
// CHECK: [[Z:%.+]] = moore.variable
logic [2:0] z;
// CHECK: moore.constant 0 : i2
// CHECK: cf.cond_br {{%.+}}, [[CASE0:\^.+]], [[ELSE0:\^.+]]
(* full_case *)
case (a)
// CHECK: [[CASE0]]:
// CHECK: moore.constant 1 : i3
// CHECK: cf.br [[EXIT:\^.+]]
2'd0: z = 3'b001;
// CHECK: [[ELSE0]]:
// CHECK: moore.constant 1 : i2
// CHECK: cf.cond_br {{%.+}}, [[CASE1:\^.+]], [[ELSE1:\^.+]]
// CHECK: [[CASE1]]:
// CHECK: moore.constant 2 : i3
// CHECK: cf.br [[EXIT]]
2'd1: z = 3'b010;
// CHECK: [[ELSE1]]:
// CHECK: moore.constant -2 : i2
// CHECK: cf.cond_br {{%.+}}, [[CASE2:\^.+]], [[ELSE2:\^.+]]
// CHECK: [[CASE2]]:
// CHECK: moore.constant -4 : i3
// CHECK: cf.br [[EXIT]]
2'd2: z = 3'b100;
// Branch to the final item. This trivial basic block would be removed
// later during CFG simplification.
// CHECK: [[ELSE2]]:
// CHECK-NEXT: cf.br [[CASE2]]
endcase
endfunction
// Regression test for #8657.
// CHECK-LABEL: rvalueAndLvalueElementSelect
module rvalueAndLvalueElementSelect(