From a9436263bd8b9a7dcacd263922dac8b271f355d4 Mon Sep 17 00:00:00 2001 From: Hideto Ueno Date: Thu, 8 Aug 2024 15:39:05 +0900 Subject: [PATCH] [ExportVerilog] Add a lowering option to fix up empty modules (#7454) This commit adds a new lowering option to sanitize empty modules by creating a dummy wire in it. --- docs/VerilogGeneration.md | 3 +++ include/circt/Support/LoweringOptions.h | 4 ++++ .../ExportVerilog/PrepareForEmission.cpp | 22 +++++++++++++++++++ lib/Support/LoweringOptions.cpp | 4 ++++ .../ExportVerilog/fixup-empty-modules.mlir | 19 ++++++++++++++++ 5 files changed, 52 insertions(+) create mode 100644 test/Conversion/ExportVerilog/fixup-empty-modules.mlir diff --git a/docs/VerilogGeneration.md b/docs/VerilogGeneration.md index 5efb26373422..0a006d3b0888 100644 --- a/docs/VerilogGeneration.md +++ b/docs/VerilogGeneration.md @@ -136,6 +136,9 @@ The current set of "lint warnings fix" Lowering Options is: collisions with Verilog keywords insensitively. E.g., this will treat a variable called `WIRE` as a collision with the keyword and rename it to `WIRE_0` (or similar). When set to `false`, then `WIRE` will not be renamed. + * `fixUpEmptyModules` (default=`false`). If true, then add a dummy wire to + empty modules since some vendor tools consider empty modules as a blackbox and + raise synthesis errors. ## Recommended `LoweringOptions` by Target diff --git a/include/circt/Support/LoweringOptions.h b/include/circt/Support/LoweringOptions.h index 59ce7aadc966..d72fcdfc260d 100644 --- a/include/circt/Support/LoweringOptions.h +++ b/include/circt/Support/LoweringOptions.h @@ -166,6 +166,10 @@ struct LoweringOptions { /// If true, then update the the mlir to include output verilog locations. bool emitVerilogLocations = false; + + /// If true, add a dummy wire to empty modules to prevent tools from regarding + /// the module as blackbox. + bool fixUpEmptyModules = false; }; } // namespace circt diff --git a/lib/Conversion/ExportVerilog/PrepareForEmission.cpp b/lib/Conversion/ExportVerilog/PrepareForEmission.cpp index 59d1b660f7d4..4789346f0760 100644 --- a/lib/Conversion/ExportVerilog/PrepareForEmission.cpp +++ b/lib/Conversion/ExportVerilog/PrepareForEmission.cpp @@ -1315,6 +1315,24 @@ static LogicalResult legalizeHWModule(Block &block, return success(); } +static void fixUpEmptyModules(hw::HWEmittableModuleLike module) { + auto outputOp = dyn_cast(module.getBodyBlock()->begin()); + if (!outputOp || outputOp->getNumOperands() > 0) + return; // Not empty so no need to fix up. + OpBuilder builder(module->getContext()); + builder.setInsertionPoint(outputOp); + auto constant = builder.create(module.getLoc(), + builder.getBoolAttr(true)); + auto wire = builder.create(module.getLoc(), builder.getI1Type()); + sv::setSVAttributes(wire, + sv::SVAttributeAttr::get( + builder.getContext(), + "This wire is added to avoid emitting empty modules. " + "See `fixUpEmptyModules` lowering option in CIRCT.", + /*emitAsComment=*/true)); + builder.create(module.getLoc(), wire, constant); +} + // NOLINTNEXTLINE(misc-no-recursion) LogicalResult ExportVerilog::prepareHWModule(hw::HWEmittableModuleLike module, const LoweringOptions &options) { @@ -1325,6 +1343,10 @@ LogicalResult ExportVerilog::prepareHWModule(hw::HWEmittableModuleLike module, // Zero-valued logic pruning. pruneZeroValuedLogic(module); + // Fix up empty modules if necessary. + if (options.fixUpEmptyModules) + fixUpEmptyModules(module); + // Legalization. if (failed(legalizeHWModule(*module.getBodyBlock(), options))) return failure(); diff --git a/lib/Support/LoweringOptions.cpp b/lib/Support/LoweringOptions.cpp index d6dfff5ee97f..f424494f18f1 100644 --- a/lib/Support/LoweringOptions.cpp +++ b/lib/Support/LoweringOptions.cpp @@ -121,6 +121,8 @@ void LoweringOptions::parse(StringRef text, ErrorHandlerT errorHandler) { caseInsensitiveKeywords = true; } else if (option == "emitVerilogLocations") { emitVerilogLocations = true; + } else if (option == "fixUpEmptyModules") { + fixUpEmptyModules = true; } else { errorHandler(llvm::Twine("unknown style option \'") + option + "\'"); // We continue parsing options after a failure. @@ -180,6 +182,8 @@ std::string LoweringOptions::toString() const { options += "caseInsensitiveKeywords,"; if (emitVerilogLocations) options += "emitVerilogLocations,"; + if (fixUpEmptyModules) + options += "fixUpEmptyModules,"; // Remove a trailing comma if present. if (!options.empty()) { diff --git a/test/Conversion/ExportVerilog/fixup-empty-modules.mlir b/test/Conversion/ExportVerilog/fixup-empty-modules.mlir new file mode 100644 index 000000000000..e0304327fdfa --- /dev/null +++ b/test/Conversion/ExportVerilog/fixup-empty-modules.mlir @@ -0,0 +1,19 @@ +// RUN: circt-opt --test-apply-lowering-options='options=fixUpEmptyModules' --export-verilog %s | FileCheck %s --check-prefixes=CHECK + +// CHECK-LABEL: module empty1 +hw.module @empty1() { + // CHECK-NEXT: /* This wire is added to avoid emitting empty modules. See `fixUpEmptyModules` lowering option in CIRCT. */ + // CHECK-NEXT: wire _GEN = 1'h1; +} + +// CHECK-LABEL: module empty2 +hw.module @empty2(in %in: i1, in %in2: i32) { + // CHECK: /* This wire is added to avoid emitting empty modules. See `fixUpEmptyModules` lowering option in CIRCT. */ + // CHECK-NEXT: wire _GEN = 1'h1; +} + +// CHECK-LABEL: module not_empty +hw.module @not_empty(in %in: i1, out out: i1) { + // CHECK: assign out = in; + hw.output %in : i1 +}