diff --git a/include/circt/Dialect/SV/SVStatements.td b/include/circt/Dialect/SV/SVStatements.td index 429aea26852b..4f0305d5dfce 100644 --- a/include/circt/Dialect/SV/SVStatements.td +++ b/include/circt/Dialect/SV/SVStatements.td @@ -879,3 +879,19 @@ def MacroDefOp : SVOp<"macro.def", MacroDeclOp getReferencedMacro(const hw::HWSymbolCache *cache); }]; } + + +//===----------------------------------------------------------------------===// +// SV output control. +//===----------------------------------------------------------------------===// + +def ReserveNamesOp : SVOp<"reserve_names", [ + HasParent<"mlir::ModuleOp"> +]> { + let summary = "Disallow a set of names to be used during emission"; + + let arguments = (ins StrArrayAttr:$reservedNames); + let assemblyFormat = [{ + $reservedNames attr-dict + }]; +} diff --git a/lib/Conversion/ExportVerilog/ExportVerilog.cpp b/lib/Conversion/ExportVerilog/ExportVerilog.cpp index 81ad22bf3dc5..e9320ab632a6 100644 --- a/lib/Conversion/ExportVerilog/ExportVerilog.cpp +++ b/lib/Conversion/ExportVerilog/ExportVerilog.cpp @@ -6344,6 +6344,9 @@ void SharedEmitterState::gatherFiles(bool separateModules) { .Case([&](auto op) { symbolCache.addDefinition(op.getSymNameAttr(), op); }) + .Case([](auto op) { + // This op was already used in gathering used names. + }) .Case([&](auto op) { symbolCache.addDefinition(op.getSymNameAttr(), op); }) diff --git a/lib/Conversion/ExportVerilog/ExportVerilogInternals.h b/lib/Conversion/ExportVerilog/ExportVerilogInternals.h index 1ec0a5186503..e56c19f823bf 100644 --- a/lib/Conversion/ExportVerilog/ExportVerilogInternals.h +++ b/lib/Conversion/ExportVerilog/ExportVerilogInternals.h @@ -27,6 +27,7 @@ struct LoweringOptions; namespace ExportVerilog { class GlobalNameResolver; +struct NameCollisionResolver; /// Check if the value is from read of a wire or reg or is a port. bool isSimpleReadOrPort(Value v); @@ -57,6 +58,9 @@ struct GlobalNameTable { return it != enumPrefixes.end() ? it->second : StringAttr(); } + // Add the set of reserved names to a resolver. + void addReservedNames(NameCollisionResolver &nameResolver) const; + private: friend class GlobalNameResolver; GlobalNameTable() {} @@ -76,6 +80,9 @@ struct GlobalNameTable { // This contains prefixes for any typedecl'd enum types. Keys are type-aliases // of enum types. DenseMap enumPrefixes; + + /// List of names which are marked as reserved for any name. + DenseSet reservedNames; }; //===----------------------------------------------------------------------===// diff --git a/lib/Conversion/ExportVerilog/LegalizeNames.cpp b/lib/Conversion/ExportVerilog/LegalizeNames.cpp index 66319d527b0f..8d510629a60e 100644 --- a/lib/Conversion/ExportVerilog/LegalizeNames.cpp +++ b/lib/Conversion/ExportVerilog/LegalizeNames.cpp @@ -24,6 +24,15 @@ using namespace sv; using namespace hw; using namespace ExportVerilog; +//===----------------------------------------------------------------------===// +// GlobalNameTable +//===----------------------------------------------------------------------===// + +void GlobalNameTable::addReservedNames(NameCollisionResolver &resolver) const { + for (auto &name : reservedNames) + resolver.insertUsedName(name); +} + //===----------------------------------------------------------------------===// // NameCollisionResolver //===----------------------------------------------------------------------===// @@ -134,6 +143,8 @@ static void legalizeModuleLocalNames(HWModuleOp module, const GlobalNameTable &globalNameTable) { // A resolver for a local name collison. NameCollisionResolver nameResolver(options); + globalNameTable.addReservedNames(nameResolver); + // Register names used by parameters. for (auto param : module.getParameters()) nameResolver.insertUsedName(globalNameTable.getParameterVerilogName( @@ -237,6 +248,12 @@ GlobalNameResolver::GlobalNameResolver(mlir::ModuleOp topLevel, op.emitError("name \"") << name << "\" is not allowed in Verilog output"; globalNameResolver.insertUsedName(name); + } else if (auto reservedNamesOp = dyn_cast(op)) { + for (StringAttr name : + reservedNamesOp.getReservedNames().getAsRange()) { + globalNameTable.reservedNames.insert(name); + globalNameResolver.insertUsedName(name); + } } } diff --git a/test/Conversion/ExportVerilog/sv-dialect.mlir b/test/Conversion/ExportVerilog/sv-dialect.mlir index 00da7d17d8ea..b10fc8866f12 100644 --- a/test/Conversion/ExportVerilog/sv-dialect.mlir +++ b/test/Conversion/ExportVerilog/sv-dialect.mlir @@ -3,6 +3,14 @@ sv.macro.decl @SYNTHESIS sv.macro.decl @VERILATOR +// CHECK-NOT: module ReservedName1( +// CHECK-NOT: input reservedName2{{$}} +// CHECK-NOT: reg reservedName3; +sv.reserve_names ["ReservedName1", "reservedName2", "reservedName3"] +hw.module @ReservedName1 (in %reservedName2 : i1) { + %reservedName3 = sv.reg : !hw.inout +} + // CHECK-LABEL: module M1 // CHECK-NEXT: #(parameter [41:0] param1) ( hw.module @M1(in %clock : i1, in %cond : i1, in %val : i8) {