From 5eeb6241fb7fadb3df81196058ebf41001fbf259 Mon Sep 17 00:00:00 2001 From: AmurG Date: Tue, 28 Apr 2026 20:57:33 +0000 Subject: [PATCH] [ImportVerilog] Support use-before-declare across module bodies Predeclare module-scope variables, nets, interface instances, and module instances when slang is allowed to resolve names before their declarations. The predeclaration walk recurses through generated scopes, creates storage before expanding interfaces, and instantiates modules before earlier procedural code lowers, so forward references through local names, interface members, and hierarchical instance paths can resolve. Declaration initializers and net declaration assignments are lowered after the module body has populated the value map, then attached back to the original Moore declaration ops. This keeps the compatibility behavior behind --allow-use-before-declare and preserves existing diagnostics when the flag is absent. Add focused regression coverage for direct module declarations, generated scopes, interface instances, and module instances, plus broader expression/procedural compatibility matrices for common testbench patterns. --- .../ImportVerilog/ImportVerilogInternals.h | 5 + lib/Conversion/ImportVerilog/Structure.cpp | 311 ++++++++++- .../use-before-declare-expressions.sv | 517 ++++++++++++++++++ .../use-before-declare-generate.sv | 136 +++++ .../use-before-declare-instances.sv | 72 +++ .../use-before-declare-interface.sv | 105 ++++ .../use-before-declare-matrix.sv | 387 +++++++++++++ .../use-before-declare-procedural.sv | 516 +++++++++++++++++ .../ImportVerilog/use-before-declare.sv | 66 +++ 9 files changed, 2114 insertions(+), 1 deletion(-) create mode 100644 test/Conversion/ImportVerilog/use-before-declare-expressions.sv create mode 100644 test/Conversion/ImportVerilog/use-before-declare-generate.sv create mode 100644 test/Conversion/ImportVerilog/use-before-declare-instances.sv create mode 100644 test/Conversion/ImportVerilog/use-before-declare-interface.sv create mode 100644 test/Conversion/ImportVerilog/use-before-declare-matrix.sv create mode 100644 test/Conversion/ImportVerilog/use-before-declare-procedural.sv create mode 100644 test/Conversion/ImportVerilog/use-before-declare.sv diff --git a/lib/Conversion/ImportVerilog/ImportVerilogInternals.h b/lib/Conversion/ImportVerilog/ImportVerilogInternals.h index 7b440bfb20dd..7e65636663e4 100644 --- a/lib/Conversion/ImportVerilog/ImportVerilogInternals.h +++ b/lib/Conversion/ImportVerilog/ImportVerilogInternals.h @@ -452,6 +452,11 @@ struct Context { /// because ScopedHashTable stores values by copy. SmallVector> interfaceInstanceStorage; + /// Module instances already emitted by the `--allow-use-before-declare` + /// predeclaration pass. These are skipped during the later source-order + /// module-body walk to avoid emitting duplicate instances. + DenseSet predeclaredInstances; + /// Cached virtual interface layouts (type + field order). DenseMap virtualIfaceLowerings; diff --git a/lib/Conversion/ImportVerilog/Structure.cpp b/lib/Conversion/ImportVerilog/Structure.cpp index d815016b3306..ec03a399b737 100644 --- a/lib/Conversion/ImportVerilog/Structure.cpp +++ b/lib/Conversion/ImportVerilog/Structure.cpp @@ -9,6 +9,7 @@ #include "ImportVerilogInternals.h" #include "slang/ast/Compilation.h" #include "slang/ast/symbols/ClassSymbols.h" +#include "llvm/ADT/STLFunctionalExtras.h" #include "llvm/ADT/ScopeExit.h" using namespace circt; @@ -407,6 +408,10 @@ struct ModuleVisitor : public BaseVisitor { using slang::ast::MultiPortSymbol; using slang::ast::PortSymbol; + if (context.options.allowUseBeforeDeclare.value_or(false)) + if (context.predeclaredInstances.contains(&instNode)) + return success(); + // Always operate on the canonical instance body if there is one. // This means any symbols we record will be the symbols from the // canonical body, which will match up with the symbols encountered @@ -416,8 +421,12 @@ struct ModuleVisitor : public BaseVisitor { // Interface instances are expanded inline into individual variable/net ops // rather than creating a moore.instance op. auto defKind = body->getDefinition().definitionKind; - if (defKind == slang::ast::DefinitionKind::Interface) + if (defKind == slang::ast::DefinitionKind::Interface) { + if (context.options.allowUseBeforeDeclare.value_or(false)) + if (context.interfaceInstances.lookup(&instNode)) + return success(); return expandInterfaceInstance(instNode); + } auto *moduleLowering = context.convertModuleHeader(body); if (!moduleLowering) @@ -706,6 +715,10 @@ struct ModuleVisitor : public BaseVisitor { // Handle variables. LogicalResult visit(const slang::ast::VariableSymbol &varNode) { + if (context.options.allowUseBeforeDeclare.value_or(false)) + if (context.valueSymbols.lookup(&varNode)) + return success(); + auto loweredType = context.convertType(*varNode.getDeclaredType()); if (!loweredType) return failure(); @@ -731,6 +744,10 @@ struct ModuleVisitor : public BaseVisitor { // Handle nets. LogicalResult visit(const slang::ast::NetSymbol &netNode) { + if (context.options.allowUseBeforeDeclare.value_or(false)) + if (context.valueSymbols.lookup(&netNode)) + return success(); + auto loweredType = context.convertType(*netNode.getDeclaredType()); if (!loweredType) return failure(); @@ -1317,8 +1334,280 @@ Context::convertModuleBody(const slang::ast::InstanceBodySymbol *module) { } } + const bool allowUseBeforeDeclare = + options.allowUseBeforeDeclare.value_or(false); + SmallVector> + pendingVariableInitializers; + SmallVector> + pendingNetDeclarationAssignments; + + struct ModulePredeclaration { + Context &context; + OpBuilder &builder; + SmallVector> + &pendingVariableInitializers; + SmallVector> + &pendingNetDeclarationAssignments; + + LogicalResult declareVariable(const slang::ast::VariableSymbol &varNode, + Location loc, StringRef blockNamePrefix) { + auto loweredType = context.convertType(*varNode.getDeclaredType()); + if (!loweredType) + return failure(); + + auto varOp = moore::VariableOp::create( + builder, loc, + moore::RefType::get(cast(loweredType)), + builder.getStringAttr(Twine(blockNamePrefix) + varNode.name), + Value{}); + context.valueSymbols.insert(&varNode, varOp); + + const auto &canonTy = varNode.getType().getCanonicalType(); + if (const auto *vi = canonTy.as_if()) + if (failed(context.registerVirtualInterfaceMembers(varNode, *vi, loc))) + return failure(); + + if (const auto *init = varNode.getInitializer()) + pendingVariableInitializers.push_back({varOp, init}); + return success(); + } + + LogicalResult declareNet(const slang::ast::NetSymbol &netNode, Location loc, + StringRef blockNamePrefix) { + auto loweredType = context.convertType(*netNode.getDeclaredType()); + if (!loweredType) + return failure(); + + auto netkind = convertNetKind(netNode.netType.netKind); + if (netkind == moore::NetKind::Interconnect || + netkind == moore::NetKind::UserDefined || + netkind == moore::NetKind::Unknown) + return mlir::emitError(loc, "unsupported net kind `") + << netNode.netType.name << "`"; + + auto netOp = moore::NetOp::create( + builder, loc, + moore::RefType::get(cast(loweredType)), + builder.getStringAttr(Twine(blockNamePrefix) + netNode.name), netkind, + Value{}); + context.valueSymbols.insert(&netNode, netOp); + + if (const auto *init = netNode.getInitializer()) + pendingNetDeclarationAssignments.push_back({netOp, init}); + return success(); + } + + SmallString<64> + getGenerateBlockPrefix(const slang::ast::GenerateBlockSymbol &genNode, + StringRef blockNamePrefix) { + SmallString<64> prefix = blockNamePrefix; + if (!genNode.name.empty() || + genNode.getParentScope()->asSymbol().kind != + slang::ast::SymbolKind::GenerateBlockArray) { + prefix += genNode.getExternalName(); + prefix += '.'; + } + return prefix; + } + + LogicalResult predeclareStorageGenerateBlock( + const slang::ast::GenerateBlockSymbol &genNode, + StringRef blockNamePrefix) { + if (genNode.isUninstantiated) + return success(); + return predeclareStorageScope( + genNode, getGenerateBlockPrefix(genNode, blockNamePrefix)); + } + + LogicalResult predeclareInterfaceGenerateBlock( + const slang::ast::GenerateBlockSymbol &genNode, + StringRef blockNamePrefix) { + if (genNode.isUninstantiated) + return success(); + return predeclareInterfaceScope( + genNode, getGenerateBlockPrefix(genNode, blockNamePrefix)); + } + + LogicalResult predeclareModuleInstanceGenerateBlock( + const slang::ast::GenerateBlockSymbol &genNode, + StringRef blockNamePrefix) { + if (genNode.isUninstantiated) + return success(); + return predeclareModuleInstanceScope( + genNode, getGenerateBlockPrefix(genNode, blockNamePrefix)); + } + + LogicalResult predeclareGenerateBlockArray( + const slang::ast::GenerateBlockArraySymbol &genArrNode, + StringRef blockNamePrefix, + llvm::function_ref< + LogicalResult(const slang::ast::GenerateBlockSymbol &, StringRef)> + predeclareBlock) { + SmallString<64> prefix = blockNamePrefix; + prefix += genArrNode.getExternalName(); + prefix += '_'; + auto prefixBaseLen = prefix.size(); + + for (const auto *entry : genArrNode.entries) { + prefix.resize(prefixBaseLen); + if (entry->arrayIndex) + prefix += entry->arrayIndex->toString(); + else + Twine(entry->constructIndex).toVector(prefix); + prefix += '.'; + + if (failed(predeclareBlock(*entry, prefix))) + return failure(); + } + return success(); + } + + LogicalResult predeclareStorageMember(const slang::ast::Symbol &member, + StringRef blockNamePrefix) { + auto loc = context.convertLocation(member.location); + if (const auto *varNode = member.as_if()) + return declareVariable(*varNode, loc, blockNamePrefix); + + if (const auto *netNode = member.as_if()) + return declareNet(*netNode, loc, blockNamePrefix); + + if (const auto *genNode = member.as_if()) + return predeclareStorageGenerateBlock(*genNode, blockNamePrefix); + + if (const auto *genArrNode = + member.as_if()) + return predeclareGenerateBlockArray( + *genArrNode, blockNamePrefix, + [&](const slang::ast::GenerateBlockSymbol &gen, StringRef prefix) { + return predeclareStorageGenerateBlock(gen, prefix); + }); + + return success(); + } + + LogicalResult predeclareInterfaceMember(const slang::ast::Symbol &member, + StringRef blockNamePrefix) { + auto loc = context.convertLocation(member.location); + if (const auto *instNode = member.as_if()) { + if (instNode->body.getDefinition().definitionKind == + slang::ast::DefinitionKind::Interface) + return ModuleVisitor(context, loc, blockNamePrefix) + .expandInterfaceInstance(*instNode); + return success(); + } + + if (const auto *genNode = member.as_if()) + return predeclareInterfaceGenerateBlock(*genNode, blockNamePrefix); + + if (const auto *genArrNode = + member.as_if()) + return predeclareGenerateBlockArray( + *genArrNode, blockNamePrefix, + [&](const slang::ast::GenerateBlockSymbol &gen, StringRef prefix) { + return predeclareInterfaceGenerateBlock(gen, prefix); + }); + + return success(); + } + + LogicalResult + predeclareModuleInstanceMember(const slang::ast::Symbol &member, + StringRef blockNamePrefix) { + auto loc = context.convertLocation(member.location); + if (const auto *instNode = member.as_if()) { + if (instNode->body.getDefinition().definitionKind != + slang::ast::DefinitionKind::Interface) { + if (failed(ModuleVisitor(context, loc, blockNamePrefix) + .visit(*instNode))) + return failure(); + context.predeclaredInstances.insert(instNode); + } + return success(); + } + + if (const auto *genNode = member.as_if()) + return predeclareModuleInstanceGenerateBlock(*genNode, blockNamePrefix); + + if (const auto *genArrNode = + member.as_if()) + return predeclareGenerateBlockArray( + *genArrNode, blockNamePrefix, + [&](const slang::ast::GenerateBlockSymbol &gen, StringRef prefix) { + return predeclareModuleInstanceGenerateBlock(gen, prefix); + }); + + return success(); + } + + LogicalResult predeclareStorageScope(const slang::ast::Scope &scope, + StringRef blockNamePrefix) { + for (auto &member : scope.members()) + if (failed(predeclareStorageMember(member, blockNamePrefix))) + return failure(); + return success(); + } + + LogicalResult predeclareInterfaceScope(const slang::ast::Scope &scope, + StringRef blockNamePrefix) { + for (auto &member : scope.members()) + if (failed(predeclareInterfaceMember(member, blockNamePrefix))) + return failure(); + return success(); + } + + LogicalResult predeclareModuleInstanceScope(const slang::ast::Scope &scope, + StringRef blockNamePrefix) { + for (auto &member : scope.members()) + if (failed(predeclareModuleInstanceMember(member, blockNamePrefix))) + return failure(); + return success(); + } + + LogicalResult predeclareScope(const slang::ast::Scope &scope, + StringRef blockNamePrefix) { + // First create variables and nets for the whole generated scope tree so + // later phases can bind port connections or hierarchical references to + // declarations that appear later in source. + if (failed(predeclareStorageScope(scope, blockNamePrefix))) + return failure(); + + // Then expand interface instances. Interface expansion may lower + // continuous assignments or procedures from the interface body, so all + // storage symbols must already be available. + if (failed(predeclareInterfaceScope(scope, blockNamePrefix))) + return failure(); + + // Finally instantiate modules. This makes later hierarchical references + // to instance internals available before earlier procedural blocks lower. + return predeclareModuleInstanceScope(scope, blockNamePrefix); + } + }; + + // Slang can resolve module-scope references to declarations that appear later + // in the source when `--allow-use-before-declare` is enabled. ImportVerilog + // still has to create Moore declarations before lowering any expression that + // may refer to them. Walk generated scopes as well as the direct module body, + // and pre-expand interface instances so earlier hierarchical references like + // `bus.sig` can find the expanded member values. Since Moore modules are + // graph regions, initializer operands can be attached after all declarations + // have been registered. + if (allowUseBeforeDeclare) { + ModulePredeclaration predecl{*this, builder, pendingVariableInitializers, + pendingNetDeclarationAssignments}; + if (failed(predecl.predeclareScope(*module, ""))) + return failure(); + } + + auto isPredeclaredMember = [&](const slang::ast::Symbol &member) { + return allowUseBeforeDeclare && + (member.kind == slang::ast::SymbolKind::Variable || + member.kind == slang::ast::SymbolKind::Net); + }; + // Convert the body of the module. for (auto &member : module->members()) { + if (isPredeclaredMember(member)) + continue; auto loc = convertLocation(member.location); if (failed(member.visit(ModuleVisitor(*this, loc)))) return failure(); @@ -1328,6 +1617,26 @@ Context::convertModuleBody(const slang::ast::InstanceBodySymbol *module) { return failure(); } + // Lower deferred declaration assignments after the full module body has had a + // chance to create any additional symbols, such as generated block members. + if (allowUseBeforeDeclare) { + for (auto [netOp, init] : pendingNetDeclarationAssignments) { + auto dstType = cast(netOp.getType()).getNestedType(); + auto rvalue = convertRvalueExpression(*init, dstType); + if (!rvalue) + return failure(); + netOp.getAssignmentMutable().assign(rvalue); + } + + for (auto [varOp, init] : pendingVariableInitializers) { + auto dstType = cast(varOp.getType()).getNestedType(); + auto rvalue = convertRvalueExpression(*init, dstType); + if (!rvalue) + return failure(); + varOp.getInitialMutable().assign(rvalue); + } + } + // Create additional ops to drive input port values onto the corresponding // internal variables and nets, and to collect output port values for the // terminator. diff --git a/test/Conversion/ImportVerilog/use-before-declare-expressions.sv b/test/Conversion/ImportVerilog/use-before-declare-expressions.sv new file mode 100644 index 000000000000..cf225bb8d483 --- /dev/null +++ b/test/Conversion/ImportVerilog/use-before-declare-expressions.sv @@ -0,0 +1,517 @@ +// RUN: circt-verilog --ir-moore --allow-use-before-declare %s | FileCheck %s +// REQUIRES: slang +// +// Internal issue in Slang v3 about jump depending on uninitialised value. +// UNSUPPORTED: valgrind + +// Broad expression coverage for module-body names resolved by Slang's +// `AllowUseBeforeDeclare` mode. The focused tests check exact IR. This file +// keeps a matrix of common rvalue, lvalue, interface, instance, and generate +// expression shapes importing successfully when declarations appear late. + +interface ExprBus(input logic clk); + logic sig; + logic [7:0] data; + int count; + assign sig = clk; + assign data = {7'b0, clk}; + assign count = 13; +endinterface + +interface ExprWriteBus(input logic clk); + logic sig; + logic [7:0] data; + int count; +endinterface + +module ExprLeaf(input logic [7:0] in, output logic [7:0] out); + assign out = in; +endmodule + +typedef struct packed { + logic [3:0] lo; + logic [3:0] hi; +} expr_pair_t; + +// CHECK-LABEL: moore.module @ExprAdd( +module ExprAdd(output int out); + assign out = lhs + rhs; + int lhs; + int rhs; +endmodule + +// CHECK-LABEL: moore.module @ExprSub( +module ExprSub(output int out); + assign out = lhs - rhs; + int lhs; + int rhs; +endmodule + +// CHECK-LABEL: moore.module @ExprMul( +module ExprMul(output int out); + assign out = lhs * rhs; + int lhs; + int rhs; +endmodule + +// CHECK-LABEL: moore.module @ExprDivMod( +module ExprDivMod(output int out); + assign out = (lhs / rhs) + (lhs % rhs); + int lhs; + int rhs = 1; +endmodule + +// CHECK-LABEL: moore.module @ExprBitAnd( +module ExprBitAnd(output logic [7:0] out); + assign out = lhs & rhs; + logic [7:0] lhs; + logic [7:0] rhs; +endmodule + +// CHECK-LABEL: moore.module @ExprBitOr( +module ExprBitOr(output logic [7:0] out); + assign out = lhs | rhs; + logic [7:0] lhs; + logic [7:0] rhs; +endmodule + +// CHECK-LABEL: moore.module @ExprBitXor( +module ExprBitXor(output logic [7:0] out); + assign out = lhs ^ rhs; + logic [7:0] lhs; + logic [7:0] rhs; +endmodule + +// CHECK-LABEL: moore.module @ExprBitNot( +module ExprBitNot(output logic [7:0] out); + assign out = ~late; + logic [7:0] late; +endmodule + +// CHECK-LABEL: moore.module @ExprReductionAnd( +module ExprReductionAnd(output logic out); + assign out = ⪭ + logic [7:0] late; +endmodule + +// CHECK-LABEL: moore.module @ExprReductionOr( +module ExprReductionOr(output logic out); + assign out = |late; + logic [7:0] late; +endmodule + +// CHECK-LABEL: moore.module @ExprReductionXor( +module ExprReductionXor(output logic out); + assign out = ^late; + logic [7:0] late; +endmodule + +// CHECK-LABEL: moore.module @ExprLogicalAnd( +module ExprLogicalAnd(output logic out); + assign out = lhs && rhs; + logic lhs; + logic rhs; +endmodule + +// CHECK-LABEL: moore.module @ExprLogicalOr( +module ExprLogicalOr(output logic out); + assign out = lhs || rhs; + logic lhs; + logic rhs; +endmodule + +// CHECK-LABEL: moore.module @ExprLogicalNot( +module ExprLogicalNot(output logic out); + assign out = !late; + logic late; +endmodule + +// CHECK-LABEL: moore.module @ExprEquality( +module ExprEquality(output logic out); + assign out = lhs == rhs; + logic [7:0] lhs; + logic [7:0] rhs; +endmodule + +// CHECK-LABEL: moore.module @ExprInequality( +module ExprInequality(output logic out); + assign out = lhs != rhs; + logic [7:0] lhs; + logic [7:0] rhs; +endmodule + +// CHECK-LABEL: moore.module @ExprCaseEquality( +module ExprCaseEquality(output logic out); + assign out = lhs === rhs; + logic [7:0] lhs; + logic [7:0] rhs; +endmodule + +// CHECK-LABEL: moore.module @ExprLessThan( +module ExprLessThan(output logic out); + assign out = lhs < rhs; + int lhs; + int rhs; +endmodule + +// CHECK-LABEL: moore.module @ExprGreaterEqual( +module ExprGreaterEqual(output logic out); + assign out = lhs >= rhs; + int lhs; + int rhs; +endmodule + +// CHECK-LABEL: moore.module @ExprShiftLeft( +module ExprShiftLeft(output logic [7:0] out); + assign out = data << shift; + logic [7:0] data; + int shift; +endmodule + +// CHECK-LABEL: moore.module @ExprShiftRight( +module ExprShiftRight(output logic [7:0] out); + assign out = data >> shift; + logic [7:0] data; + int shift; +endmodule + +// CHECK-LABEL: moore.module @ExprArithmeticShift( +module ExprArithmeticShift(output logic signed [7:0] out); + assign out = data >>> shift; + logic signed [7:0] data; + int shift; +endmodule + +// CHECK-LABEL: moore.module @ExprConcat( +module ExprConcat(output logic [7:0] out); + assign out = {hi, lo}; + logic [3:0] hi; + logic [3:0] lo; +endmodule + +// CHECK-LABEL: moore.module @ExprNestedConcat( +module ExprNestedConcat(output logic [11:0] out); + assign out = {prefix, {mid, suffix}}; + logic [3:0] prefix; + logic [3:0] mid; + logic [3:0] suffix; +endmodule + +// CHECK-LABEL: moore.module @ExprReplication( +module ExprReplication(output logic [7:0] out); + assign out = {4{late}}; + logic [1:0] late; +endmodule + +// CHECK-LABEL: moore.module @ExprPartSelect( +module ExprPartSelect(output logic [3:0] out); + assign out = data[7:4]; + logic [7:0] data; +endmodule + +// CHECK-LABEL: moore.module @ExprIndexedPartSelectPlus( +module ExprIndexedPartSelectPlus(output logic [3:0] out); + assign out = data[base +: 4]; + logic [7:0] data; + int base; +endmodule + +// CHECK-LABEL: moore.module @ExprIndexedPartSelectMinus( +module ExprIndexedPartSelectMinus(output logic [3:0] out); + assign out = data[base -: 4]; + logic [7:0] data; + int base = 7; +endmodule + +// CHECK-LABEL: moore.module @ExprArraySelect( +module ExprArraySelect(output logic [7:0] out); + assign out = arr[index]; + logic [7:0] arr [0:3]; + int index; +endmodule + +// CHECK-LABEL: moore.module @ExprArrayElementWrite( +module ExprArrayElementWrite(output logic [7:0] out); + initial arr[index] = late; + assign out = arr[index]; + logic [7:0] arr [0:3]; + int index; + logic [7:0] late; +endmodule + +// CHECK-LABEL: moore.module @ExprStructFieldLow( +module ExprStructFieldLow(output logic [3:0] out); + assign out = pair.lo; + expr_pair_t pair; +endmodule + +// CHECK-LABEL: moore.module @ExprStructFieldHigh( +module ExprStructFieldHigh(output logic [3:0] out); + assign out = pair.hi; + expr_pair_t pair; +endmodule + +// CHECK-LABEL: moore.module @ExprStructWrite( +module ExprStructWrite(output logic [3:0] out); + initial pair.hi = late; + assign out = pair.hi; + expr_pair_t pair; + logic [3:0] late; +endmodule + +// CHECK-LABEL: moore.module @ExprStructConcat( +module ExprStructConcat(output logic [7:0] out); + assign out = {pair.hi, pair.lo}; + expr_pair_t pair; +endmodule + +// CHECK-LABEL: moore.module @ExprConditional( +module ExprConditional(output logic [7:0] out); + assign out = sel ? lhs : rhs; + logic sel; + logic [7:0] lhs; + logic [7:0] rhs; +endmodule + +// CHECK-LABEL: moore.module @ExprNestedConditional( +module ExprNestedConditional(output logic [7:0] out); + assign out = a ? lhs : (b ? mid : rhs); + logic a; + logic b; + logic [7:0] lhs; + logic [7:0] mid; + logic [7:0] rhs; +endmodule + +// CHECK-LABEL: moore.module @ExprLogicCast( +module ExprLogicCast(output logic out); + assign out = logic'(late); + int late; +endmodule + +// CHECK-LABEL: moore.module @ExprIntCast( +module ExprIntCast(output int out); + assign out = int'(late); + logic [7:0] late; +endmodule + +// CHECK-LABEL: moore.module @ExprSignedCast( +module ExprSignedCast(output logic signed [7:0] out); + assign out = signed'(late); + logic [7:0] late; +endmodule + +// CHECK-LABEL: moore.module @ExprUnsignedCast( +module ExprUnsignedCast(output logic [7:0] out); + assign out = unsigned'(late); + logic signed [7:0] late; +endmodule + +// CHECK-LABEL: moore.module @ExprInitializerBinary( +module ExprInitializerBinary(output int out); + int captured = lhs + rhs; + assign out = captured; + int lhs; + int rhs; +endmodule + +// CHECK-LABEL: moore.module @ExprInitializerConcat( +module ExprInitializerConcat(output logic [7:0] out); + logic [7:0] captured = {hi, lo}; + assign out = captured; + logic [3:0] hi; + logic [3:0] lo; +endmodule + +// CHECK-LABEL: moore.module @ExprInitializerArray( +module ExprInitializerArray(output logic [7:0] out); + logic [7:0] captured = arr[index]; + assign out = captured; + logic [7:0] arr [0:3]; + int index; +endmodule + +// CHECK-LABEL: moore.module @ExprInitializerStruct( +module ExprInitializerStruct(output logic [3:0] out); + logic [3:0] captured = pair.lo; + assign out = captured; + expr_pair_t pair; +endmodule + +// CHECK-LABEL: moore.module @ExprNetInitializerBinary( +module ExprNetInitializerBinary(output logic [31:0] out); + wire [31:0] captured = lhs + rhs; + assign out = captured; + logic [31:0] lhs; + logic [31:0] rhs; +endmodule + +// CHECK-LABEL: moore.module @ExprNetInitializerConcat( +module ExprNetInitializerConcat(output logic [7:0] out); + wire [7:0] captured = {hi, lo}; + assign out = captured; + logic [3:0] hi; + logic [3:0] lo; +endmodule + +// CHECK-LABEL: moore.module @ExprInterfaceSignal( +module ExprInterfaceSignal(output logic out); + assign out = bus.sig ^ late; + ExprBus bus(clk); + logic clk; + logic late; +endmodule + +// CHECK-LABEL: moore.module @ExprInterfaceData( +module ExprInterfaceData(output logic [7:0] out); + assign out = bus.data + late; + ExprBus bus(clk); + logic clk; + logic [7:0] late; +endmodule + +// CHECK-LABEL: moore.module @ExprInterfaceCount( +module ExprInterfaceCount(output int out); + assign out = bus.count + late; + ExprBus bus(clk); + logic clk; + int late; +endmodule + +// CHECK-LABEL: moore.module @ExprInterfaceWriteExpression( +module ExprInterfaceWriteExpression(output logic [7:0] out); + initial bus.data[index +: 4] = late; + assign out = bus.data; + ExprWriteBus bus(clk); + logic clk; + int index; + logic [3:0] late; +endmodule + +// CHECK-LABEL: moore.module @ExprInterfaceInitializer( +module ExprInterfaceInitializer(output logic [7:0] out); + logic [7:0] captured = bus.data ^ late; + assign out = captured; + ExprBus bus(clk); + logic clk; + logic [7:0] late; +endmodule + +// CHECK-LABEL: moore.module @ExprInstanceOutput( +module ExprInstanceOutput(output logic [7:0] out); + assign out = u.out ^ late; + ExprLeaf u(.in(source), .out(child_out)); + logic [7:0] source; + logic [7:0] child_out; + logic [7:0] late; +endmodule + +// CHECK-LABEL: moore.module @ExprInstanceInput( +module ExprInstanceInput(output logic [7:0] out); + assign out = u.out; + ExprLeaf u(.in(source ^ mask), .out(child_out)); + logic [7:0] source; + logic [7:0] mask; + logic [7:0] child_out; +endmodule + +// CHECK-LABEL: moore.module @ExprInstanceInitializer( +module ExprInstanceInitializer(output logic [7:0] out); + logic [7:0] captured = u.out + late; + assign out = captured; + ExprLeaf u(.in(source), .out(child_out)); + logic [7:0] source; + logic [7:0] child_out; + logic [7:0] late; +endmodule + +// CHECK-LABEL: moore.module @ExprGenerateIf( +module ExprGenerateIf(output logic [7:0] out); + if (1) begin : g + assign out = lhs + rhs; + logic [7:0] lhs; + logic [7:0] rhs; + end +endmodule + +// CHECK-LABEL: moore.module @ExprGenerateNestedIf( +module ExprGenerateNestedIf(output logic [7:0] out); + if (1) begin : outer + if (1) begin : inner + assign out = lhs ^ rhs; + logic [7:0] lhs; + logic [7:0] rhs; + end + end +endmodule + +// CHECK-LABEL: moore.module @ExprGenerateCase( +module ExprGenerateCase(output logic [7:0] out); + case (1) + 1: begin : g + assign out = {hi, lo}; + logic [3:0] hi; + logic [3:0] lo; + end + endcase +endmodule + +// CHECK-LABEL: moore.module @ExprGenerateLoop( +module ExprGenerateLoop(output logic [1:0] out); + for (genvar i = 0; i < 2; ++i) begin : lane + assign out[i] = lhs ^ rhs; + logic lhs; + logic rhs; + end +endmodule + +// CHECK-LABEL: moore.module @ExprGenerateLoopInitializer( +module ExprGenerateLoopInitializer(output logic [1:0] out); + for (genvar i = 0; i < 2; ++i) begin : lane + logic captured = lhs ^ bit'(i); + assign out[i] = captured; + logic lhs; + end +endmodule + +// CHECK-LABEL: moore.module @ExprGenerateInterface( +module ExprGenerateInterface(output logic [7:0] out); + if (1) begin : g + assign out = bus.data + late; + ExprBus bus(clk); + logic clk; + logic [7:0] late; + end +endmodule + +// CHECK-LABEL: moore.module @ExprGenerateInstance( +module ExprGenerateInstance(output logic [7:0] out); + if (1) begin : g + assign out = u.out + late; + ExprLeaf u(.in(source), .out(child_out)); + logic [7:0] source; + logic [7:0] child_out; + logic [7:0] late; + end +endmodule + +// CHECK-LABEL: moore.module @ExprLoopInterface( +module ExprLoopInterface(output logic [1:0] out); + for (genvar i = 0; i < 2; ++i) begin : lane + assign out[i] = bus.sig ^ late; + ExprBus bus(clk); + logic clk; + logic late; + end +endmodule + +// CHECK-LABEL: moore.module @ExprLoopInstance( +module ExprLoopInstance(output logic [1:0] out); + for (genvar i = 0; i < 2; ++i) begin : lane + assign out[i] = u.out[0] ^ late; + ExprLeaf u(.in(source), .out(child_out)); + logic [7:0] source; + logic [7:0] child_out; + logic late; + end +endmodule diff --git a/test/Conversion/ImportVerilog/use-before-declare-generate.sv b/test/Conversion/ImportVerilog/use-before-declare-generate.sv new file mode 100644 index 000000000000..b29d172b76c6 --- /dev/null +++ b/test/Conversion/ImportVerilog/use-before-declare-generate.sv @@ -0,0 +1,136 @@ +// RUN: not circt-verilog --ir-moore %s 2>&1 | FileCheck %s --check-prefix=NOALLOW +// RUN: circt-verilog --ir-moore --allow-use-before-declare %s | FileCheck %s +// REQUIRES: slang +// +// Internal issue in Slang v3 about jump depending on uninitialised value. +// UNSUPPORTED: valgrind + +// NOALLOW: identifier 'a' used before its declaration + +// CHECK-LABEL: moore.module @ForwardNamedGenerateProcedural( +module ForwardNamedGenerateProcedural(output int out); + if (1) begin : g + // CHECK-DAG: %g.a = moore.variable : + // CHECK-DAG: %g.b = moore.variable {{.*}} : + // CHECK: moore.procedure initial { + // CHECK: moore.read %g.b : + // CHECK: moore.blocking_assign %g.a, {{.*}} : i32 + // CHECK: } + initial a = b; + + int a; + int b = 7; + + // CHECK: moore.read %g.a : + // CHECK: moore.output {{.*}} : !moore.i32 + assign out = a; + end +endmodule + +// CHECK-LABEL: moore.module @ForwardNamedGenerateInitializer( +module ForwardNamedGenerateInitializer(output int out); + if (1) begin : g + // CHECK-DAG: %g.a = moore.variable {{.*}} : + // CHECK-DAG: %g.b = moore.variable {{.*}} : + // CHECK: moore.read %g.b : + // CHECK: moore.output {{.*}} : !moore.i32 + assign out = a; + + int a = b; + int b = 11; + end +endmodule + +// CHECK-LABEL: moore.module @ForwardNestedGenerate( +module ForwardNestedGenerate(output int out); + if (1) begin : outer + if (1) begin : inner + // CHECK-DAG: %outer.inner.lhs = moore.variable : + // CHECK-DAG: %outer.inner.rhs = moore.variable {{.*}} : + // CHECK: moore.procedure initial { + // CHECK: moore.read %outer.inner.rhs : + // CHECK: moore.blocking_assign %outer.inner.lhs, {{.*}} : i32 + // CHECK: } + initial lhs = rhs; + + int lhs; + int rhs = 13; + + // CHECK: moore.read %outer.inner.lhs : + assign out = lhs; + end + end +endmodule + +// CHECK-LABEL: moore.module @ForwardGenerateArrayVariables( +module ForwardGenerateArrayVariables(output logic [1:0] out); + for (genvar i = 0; i < 2; ++i) begin : lane + // CHECK-DAG: %lane_0.bit_out = moore.variable : + // CHECK-DAG: %lane_1.bit_out = moore.variable : + // CHECK: moore.blocking_assign %lane_0.bit_out + // CHECK: moore.assign {{.*}} : l1 + // CHECK: moore.blocking_assign %lane_1.bit_out + // CHECK: moore.assign {{.*}} : l1 + initial bit_out = bit'(i); + + bit bit_out; + assign out[i] = bit_out; + end +endmodule + +// CHECK-LABEL: moore.module @ForwardGenerateNetAssignments( +module ForwardGenerateNetAssignments(output logic out); + if (1) begin : g + // CHECK: %g.tmp = moore.assigned_variable %g.late : l1 + // CHECK: %g.late = moore.assigned_variable + // CHECK: moore.output %g.tmp : !moore.l1 + assign out = tmp; + + wire tmp = late; + wire late = 1'b1; + end +endmodule + +// CHECK-LABEL: moore.module @ForwardCaseGenerate( +module ForwardCaseGenerate(output int out); + parameter int Sel = 1; + case (Sel) + 0: begin : arm0 + assign out = a; + int a = 3; + end + default: begin : arm1 + // CHECK-DAG: %arm1.a = moore.variable : + // CHECK-DAG: %arm1.b = moore.variable {{.*}} : + // CHECK: moore.procedure initial { + // CHECK: moore.read %arm1.b : + // CHECK: moore.blocking_assign %arm1.a, {{.*}} : i32 + // CHECK: } + initial a = b; + + int a; + int b = 17; + assign out = a; + end + endcase +endmodule + +// CHECK-LABEL: moore.module @ForwardNestedLoopGenerate( +module ForwardNestedLoopGenerate(output logic [3:0] out); + for (genvar i = 0; i < 2; ++i) begin : row + for (genvar j = 0; j < 2; ++j) begin : col + // CHECK-DAG: %row_0.col_0.bit_out = moore.variable : + // CHECK-DAG: %row_0.col_1.bit_out = moore.variable : + // CHECK-DAG: %row_1.col_0.bit_out = moore.variable : + // CHECK-DAG: %row_1.col_1.bit_out = moore.variable : + // CHECK: moore.blocking_assign %row_0.col_0.bit_out + // CHECK: moore.blocking_assign %row_0.col_1.bit_out + // CHECK: moore.blocking_assign %row_1.col_0.bit_out + // CHECK: moore.blocking_assign %row_1.col_1.bit_out + initial bit_out = bit'(i ^ j); + + bit bit_out; + assign out[i * 2 + j] = bit_out; + end + end +endmodule diff --git a/test/Conversion/ImportVerilog/use-before-declare-instances.sv b/test/Conversion/ImportVerilog/use-before-declare-instances.sv new file mode 100644 index 000000000000..61c84b625458 --- /dev/null +++ b/test/Conversion/ImportVerilog/use-before-declare-instances.sv @@ -0,0 +1,72 @@ +// RUN: not circt-verilog --ir-moore %s 2>&1 | FileCheck %s --check-prefix=NOALLOW +// RUN: circt-verilog --ir-moore --allow-use-before-declare %s | FileCheck %s +// REQUIRES: slang +// +// Internal issue in Slang v3 about jump depending on uninitialised value. +// UNSUPPORTED: valgrind + +// NOALLOW: identifier 'data' used before its declaration + +module Storage; + int value; +endmodule + +module Leaf(input int in, output int value); + assign value = in; +endmodule + +module ConstLeaf(output int value); + assign value = 5; +endmodule + +// CHECK-LABEL: moore.module private @Storage( +// CHECK-SAME: out value : !moore.ref +// CHECK-LABEL: moore.module private @Leaf( +// CHECK-SAME: in %in : !moore.i32 +// CHECK-LABEL: moore.module private @ConstLeaf( +// CHECK-LABEL: moore.module @ForwardHierarchicalInstanceWrite( +module ForwardHierarchicalInstanceWrite(output int out); + // CHECK-DAG: %data = moore.variable {{.*}} : + // CHECK-DAG: %u.value = moore.instance "u" @Storage + // CHECK: moore.procedure initial { + // CHECK: moore.read %data : + // CHECK: moore.blocking_assign %u.value, {{.*}} : i32 + // CHECK: } + initial u.value = data; + + // CHECK: moore.read %u.value : + assign out = u.value; + + Storage u(); + int data = 29; +endmodule + +// CHECK-LABEL: moore.module @ForwardHierarchicalInstanceInput( +module ForwardHierarchicalInstanceInput(output int out); + // CHECK-DAG: %src = moore.variable {{.*}} : + // CHECK: moore.read %src : + // CHECK: moore.instance "u" @Leaf + // CHECK-SAME: in: + // CHECK: moore.procedure initial { + // CHECK: moore.read %u.value_0 : + // CHECK: moore.blocking_assign %out, {{.*}} : i32 + // CHECK: } + initial out = u.value; + + Leaf u(.in(src)); + int src = 31; +endmodule + +// CHECK-LABEL: moore.module @ForwardGenerateInstance( +module ForwardGenerateInstance(output int out); + if (1) begin : g + // CHECK: %g.u.value, %g.u.value_0 = moore.instance "g.u" @ConstLeaf + // CHECK: moore.procedure initial { + // CHECK: moore.read %g.u.value_0 : + // CHECK: moore.blocking_assign %out, {{.*}} : i32 + // CHECK: } + initial out = u.value; + + ConstLeaf u(); + end +endmodule diff --git a/test/Conversion/ImportVerilog/use-before-declare-interface.sv b/test/Conversion/ImportVerilog/use-before-declare-interface.sv new file mode 100644 index 000000000000..8e676e72cabe --- /dev/null +++ b/test/Conversion/ImportVerilog/use-before-declare-interface.sv @@ -0,0 +1,105 @@ +// RUN: not circt-verilog --ir-moore %s 2>&1 | FileCheck %s --check-prefix=NOALLOW +// RUN: circt-verilog --ir-moore --allow-use-before-declare %s | FileCheck %s +// REQUIRES: slang +// +// Internal issue in Slang v3 about jump depending on uninitialised value. +// UNSUPPORTED: valgrind + +// NOALLOW: identifier 'value' used before its declaration + +interface simple_if(input logic clk); + logic sig; +endinterface + +interface passthrough_if(input logic clk); + logic seen; + assign seen = clk; +endinterface + +// CHECK-LABEL: moore.module @ForwardInterfaceDirectMember( +module ForwardInterfaceDirectMember(output logic y); + // The interface instance appears after both uses. In use-before-declare mode + // ImportVerilog must still expand the instance early enough for `bus.sig` to + // resolve in procedural and continuous assignments. + // CHECK: %value = moore.variable : + // CHECK: %bus_sig = moore.variable : + // CHECK: moore.procedure initial { + // CHECK: moore.read %value : + // CHECK: moore.blocking_assign %bus_sig + // CHECK: } + initial bus.sig = value; + + // CHECK: moore.read %bus_sig : + // CHECK: moore.output {{.*}} : !moore.l1 + assign y = bus.sig; + + simple_if bus(clk); + logic clk; + bit value; +endmodule + +// CHECK-LABEL: moore.module @ForwardInterfaceBodyAssign( +module ForwardInterfaceBodyAssign(output logic y); + // This covers the two-phase predeclaration order: the later `clk` variable + // must be available before the earlier interface instance is expanded, since + // the interface body's continuous assignment reads the connected port. + // CHECK: %clk = moore.variable : + // CHECK: %bus_seen = moore.assigned_variable + // CHECK: moore.output %bus_seen : !moore.l1 + assign y = bus.seen; + + passthrough_if bus(clk); + logic clk; +endmodule + +// CHECK-LABEL: moore.module @ForwardInterfaceGenerate( +module ForwardInterfaceGenerate(output logic y); + if (1) begin : g + // CHECK: %g.value = moore.variable : + // CHECK: %g.bus_sig = moore.variable : + // CHECK: moore.procedure initial { + // CHECK: moore.read %g.value : + // CHECK: moore.blocking_assign %g.bus_sig + // CHECK: } + initial bus.sig = value; + + // CHECK: moore.read %g.bus_sig : + assign y = bus.sig; + + simple_if bus(clk); + logic clk; + bit value; + end +endmodule + +// CHECK-LABEL: moore.module @ForwardInterfaceLoopGenerate( +module ForwardInterfaceLoopGenerate(output logic [1:0] y); + for (genvar i = 0; i < 2; ++i) begin : lane + // CHECK-DAG: %lane_0.bus_sig = moore.variable : + // CHECK-DAG: %lane_1.bus_sig = moore.variable : + // CHECK: moore.blocking_assign %lane_0.bus_sig + // CHECK: moore.assign {{.*}} : l1 + // CHECK: moore.blocking_assign %lane_1.bus_sig + // CHECK: moore.assign {{.*}} : l1 + initial bus.sig = bit'(i); + + assign y[i] = bus.sig; + simple_if bus(clk); + logic clk; + end +endmodule + +// CHECK-LABEL: moore.module @ForwardInterfaceInitializer( +module ForwardInterfaceInitializer(output logic y); + // CHECK-DAG: %captured = moore.variable {{.*}} : + // CHECK-DAG: %bus_sig = moore.variable : + // CHECK-DAG: moore.read %bus_sig : + logic captured = bus.sig; + + // CHECK-DAG: moore.read %captured : + // CHECK: moore.output {{.*}} : !moore.l1 + assign y = captured; + + simple_if bus(clk); + logic clk; +endmodule diff --git a/test/Conversion/ImportVerilog/use-before-declare-matrix.sv b/test/Conversion/ImportVerilog/use-before-declare-matrix.sv new file mode 100644 index 000000000000..087fcd5cc0d3 --- /dev/null +++ b/test/Conversion/ImportVerilog/use-before-declare-matrix.sv @@ -0,0 +1,387 @@ +// RUN: circt-verilog --ir-moore --allow-use-before-declare %s | FileCheck %s +// REQUIRES: slang +// +// Internal issue in Slang v3 about jump depending on uninitialised value. +// UNSUPPORTED: valgrind + +// A compatibility matrix for common testbench shapes that depend on Slang's +// `AllowUseBeforeDeclare` mode. The more surgical tests in +// use-before-declare*.sv check exact IR for the new predeclaration paths; this +// file keeps broad coverage that these independently common source patterns +// continue to import successfully. + +interface MatrixIf(input logic clk); + logic sig; + logic [3:0] data; + assign sig = clk; +endinterface + +interface MatrixWriteIf(input logic clk); + logic sig; + logic [3:0] data; +endinterface + +module MatrixLeaf(input logic in, output logic out); + assign out = in; +endmodule + +module MatrixStorageBit; + int bit_value; +endmodule + +module MatrixStorageData; + logic [3:0] data; +endmodule + +module MatrixReadableStorage(output int bit_value, output logic [3:0] data); + assign bit_value = 3; + assign data = 4'h5; +endmodule + +typedef struct packed { + logic [3:0] lo; + logic [3:0] hi; +} matrix_pair_t; + +// CHECK-LABEL: moore.module @MatrixInitialBlock( +module MatrixInitialBlock(output logic out); + initial out = late; + logic late; +endmodule + +// CHECK-LABEL: moore.module @MatrixInitialBeginBlock( +module MatrixInitialBeginBlock(output logic out); + initial begin + tmp = late; + out = tmp; + end + logic tmp; + logic late; +endmodule + +// CHECK-LABEL: moore.module @MatrixAlwaysComb( +module MatrixAlwaysComb(output logic out); + always_comb out = late; + logic late; +endmodule + +// CHECK-LABEL: moore.module @MatrixAlwaysAtStar( +module MatrixAlwaysAtStar(output logic out); + always @(*) out = late; + logic late; +endmodule + +// CHECK-LABEL: moore.module @MatrixAlwaysFF( +module MatrixAlwaysFF(input logic clk, output logic out); + always_ff @(posedge clk) out <= late; + logic late; +endmodule + +// CHECK-LABEL: moore.module @MatrixFinalBlock( +module MatrixFinalBlock(output logic out); + final out = late; + logic late; +endmodule + +// CHECK-LABEL: moore.module @MatrixContinuousAssign( +module MatrixContinuousAssign(output logic out); + assign out = late; + wire late = 1'b1; +endmodule + +// CHECK-LABEL: moore.module @MatrixNetDeclarationAssign( +module MatrixNetDeclarationAssign(output logic out); + assign out = early; + wire early = late; + wire late = 1'b1; +endmodule + +// CHECK-LABEL: moore.module @MatrixVariableInitializer( +module MatrixVariableInitializer(output int out); + int early = late; + int late = 32'd9; + assign out = early; +endmodule + +// CHECK-LABEL: moore.module @MatrixInitializerChain( +module MatrixInitializerChain(output int out); + int a = b; + int b = c; + int c = 32'd12; + assign out = a; +endmodule + +// CHECK-LABEL: moore.module @MatrixArraySelect( +module MatrixArraySelect(output logic out); + initial out = data[index]; + logic [3:0] data; + int index = 1; +endmodule + +// CHECK-LABEL: moore.module @MatrixArrayElementWrite( +module MatrixArrayElementWrite(output logic [3:0] out); + initial data[index] = value; + assign out = data; + logic [3:0] data; + int index = 2; + logic value; +endmodule + +// CHECK-LABEL: moore.module @MatrixStructRead( +module MatrixStructRead(output logic [3:0] out); + initial out = pair.hi; + matrix_pair_t pair; +endmodule + +// CHECK-LABEL: moore.module @MatrixStructWrite( +module MatrixStructWrite(output logic [3:0] out); + initial pair.lo = value; + assign out = pair.lo; + matrix_pair_t pair; + logic [3:0] value; +endmodule + +// CHECK-LABEL: moore.module @MatrixPackedSliceRead( +module MatrixPackedSliceRead(output logic [3:0] out); + assign out = data[7:4]; + logic [7:0] data; +endmodule + +// CHECK-LABEL: moore.module @MatrixPackedSliceWrite( +module MatrixPackedSliceWrite(output logic [7:0] out); + initial data[3:0] = value; + assign out = data; + logic [7:0] data; + logic [3:0] value; +endmodule + +// CHECK-LABEL: moore.module @MatrixNamedGenerateInitial( +module MatrixNamedGenerateInitial(output logic out); + if (1) begin : g + initial out = late; + logic late; + end +endmodule + +// CHECK-LABEL: moore.module @MatrixNamedGenerateAssign( +module MatrixNamedGenerateAssign(output logic out); + if (1) begin : g + assign out = late; + wire late = 1'b1; + end +endmodule + +// CHECK-LABEL: moore.module @MatrixNamedGenerateInitializer( +module MatrixNamedGenerateInitializer(output int out); + if (1) begin : g + int early = late; + int late = 32'd15; + assign out = early; + end +endmodule + +// CHECK-LABEL: moore.module @MatrixNestedGenerateInitial( +module MatrixNestedGenerateInitial(output logic out); + if (1) begin : outer + if (1) begin : inner + initial out = late; + logic late; + end + end +endmodule + +// CHECK-LABEL: moore.module @MatrixNestedGenerateAssign( +module MatrixNestedGenerateAssign(output logic out); + if (1) begin : outer + if (1) begin : inner + assign out = late; + wire late = 1'b1; + end + end +endmodule + +// CHECK-LABEL: moore.module @MatrixCaseGenerateDefault( +module MatrixCaseGenerateDefault(output logic out); + parameter int Sel = 3; + case (Sel) + 0: begin : a + assign out = 1'b0; + end + default: begin : b + initial out = late; + logic late; + end + endcase +endmodule + +// CHECK-LABEL: moore.module @MatrixLoopGenerateInitial( +module MatrixLoopGenerateInitial(output logic [1:0] out); + for (genvar i = 0; i < 2; ++i) begin : lane + initial bit_out = bit'(i); + assign out[i] = bit_out; + bit bit_out; + end +endmodule + +// CHECK-LABEL: moore.module @MatrixLoopGenerateAssign( +module MatrixLoopGenerateAssign(output logic [1:0] out); + for (genvar i = 0; i < 2; ++i) begin : lane + assign out[i] = late; + wire late = bit'(i); + end +endmodule + +// CHECK-LABEL: moore.module @MatrixNestedLoopGenerate( +module MatrixNestedLoopGenerate(output logic [3:0] out); + for (genvar i = 0; i < 2; ++i) begin : row + for (genvar j = 0; j < 2; ++j) begin : col + initial bit_out = bit'(i ^ j); + assign out[i * 2 + j] = bit_out; + bit bit_out; + end + end +endmodule + +// CHECK-LABEL: moore.module @MatrixInterfaceDirect( +module MatrixInterfaceDirect(output logic out); + initial bus.sig = late; + assign out = bus.sig; + MatrixWriteIf bus(clk); + logic clk; + logic late; +endmodule + +// CHECK-LABEL: moore.module @MatrixInterfacePortLateClock( +module MatrixInterfacePortLateClock(output logic out); + assign out = bus.sig; + MatrixIf bus(clk); + logic clk; +endmodule + +// CHECK-LABEL: moore.module @MatrixInterfaceInitializer( +module MatrixInterfaceInitializer(output logic out); + logic captured = bus.sig; + assign out = captured; + MatrixIf bus(clk); + logic clk; +endmodule + +// CHECK-LABEL: moore.module @MatrixInterfaceDataSlice( +module MatrixInterfaceDataSlice(output logic out); + initial bus.data[index] = late; + assign out = bus.data[index]; + MatrixIf bus(clk); + logic clk; + int index = 2; + logic late; +endmodule + +// CHECK-LABEL: moore.module @MatrixInterfaceGenerate( +module MatrixInterfaceGenerate(output logic out); + if (1) begin : g + initial bus.sig = late; + assign out = bus.sig; + MatrixWriteIf bus(clk); + logic clk; + logic late; + end +endmodule + +// CHECK-LABEL: moore.module @MatrixInterfaceLoopGenerate( +module MatrixInterfaceLoopGenerate(output logic [1:0] out); + for (genvar i = 0; i < 2; ++i) begin : lane + initial bus.sig = bit'(i); + assign out[i] = bus.sig; + MatrixWriteIf bus(clk); + logic clk; + end +endmodule + +// CHECK-LABEL: moore.module @MatrixHierRead( +module MatrixHierRead(output logic out); + initial out = u.bit_value; + MatrixReadableStorage u(.bit_value(storage_bit), .data(storage_data)); + int storage_bit; + logic [3:0] storage_data; +endmodule + +// CHECK-LABEL: moore.module @MatrixHierWrite( +module MatrixHierWrite(output logic out); + initial u.bit_value = late; + assign out = u.bit_value; + MatrixStorageBit u(); + logic late; +endmodule + +// CHECK-LABEL: moore.module @MatrixHierDataSlice( +module MatrixHierDataSlice(output logic out); + initial u.data[index] = late; + assign out = u.data[index]; + MatrixStorageData u(); + int index = 1; + logic late; +endmodule + +// CHECK-LABEL: moore.module @MatrixHierInGenerate( +module MatrixHierInGenerate(output logic out); + if (1) begin : g + initial out = u.bit_value; + MatrixReadableStorage u(.bit_value(storage_bit), .data(storage_data)); + int storage_bit; + logic [3:0] storage_data; + end +endmodule + +// CHECK-LABEL: moore.module @MatrixHierLoopGenerate( +module MatrixHierLoopGenerate(output logic [1:0] out); + for (genvar i = 0; i < 2; ++i) begin : lane + assign out[i] = u.bit_value; + MatrixReadableStorage u(.bit_value(storage_bit), .data(storage_data)); + int storage_bit; + logic [3:0] storage_data; + end +endmodule + +// CHECK-LABEL: moore.module @MatrixInstanceInputLate( +module MatrixInstanceInputLate(output logic out); + assign out = u.out; + MatrixLeaf u(.in(late)); + logic late; +endmodule + +// CHECK-LABEL: moore.module @MatrixInstanceInGenerateInputLate( +module MatrixInstanceInGenerateInputLate(output logic out); + if (1) begin : g + assign out = u.out; + MatrixLeaf u(.in(late)); + logic late; + end +endmodule + +// CHECK-LABEL: moore.module @MatrixMixedInterfaceAndInstance( +module MatrixMixedInterfaceAndInstance(output logic out); + initial begin + bus.sig = late; + holder.bit_value = bus.sig; + end + assign out = holder.bit_value; + MatrixWriteIf bus(clk); + MatrixStorageBit holder(); + logic clk; + logic late; +endmodule + +// CHECK-LABEL: moore.module @MatrixMixedGenerateInterfaceAndInstance( +module MatrixMixedGenerateInterfaceAndInstance(output logic out); + if (1) begin : g + initial begin + bus.sig = late; + holder.bit_value = bus.sig; + end + assign out = holder.bit_value; + MatrixWriteIf bus(clk); + MatrixStorageBit holder(); + logic clk; + logic late; + end +endmodule diff --git a/test/Conversion/ImportVerilog/use-before-declare-procedural.sv b/test/Conversion/ImportVerilog/use-before-declare-procedural.sv new file mode 100644 index 000000000000..f0b6637abc9d --- /dev/null +++ b/test/Conversion/ImportVerilog/use-before-declare-procedural.sv @@ -0,0 +1,516 @@ +// RUN: circt-verilog --ir-moore --allow-use-before-declare %s | FileCheck %s +// REQUIRES: slang +// +// Internal issue in Slang v3 about jump depending on uninitialised value. +// UNSUPPORTED: valgrind + +// Procedural compatibility coverage for module-body names resolved by Slang's +// `AllowUseBeforeDeclare` mode. These cases exercise control-flow, assignments, +// event controls, subroutines, interfaces, instances, and generated scopes where +// the referenced declarations appear after the procedural code. + +interface ProcBus(input logic clk); + logic sig; + logic [7:0] data; + assign sig = clk; + assign data = {7'b0, clk}; +endinterface + +interface ProcWriteBus(input logic clk); + logic sig; + logic [7:0] data; +endinterface + +module ProcLeaf(input logic [7:0] in, output logic [7:0] out); + assign out = in; +endmodule + +module ProcReadable(output int value); + assign value = 21; +endmodule + +typedef struct packed { + logic [3:0] lo; + logic [3:0] hi; +} proc_pair_t; + +// CHECK-LABEL: moore.module @ProcInitialIf( +module ProcInitialIf(output logic out); + initial if (sel) out = late; + logic sel; + logic late; +endmodule + +// CHECK-LABEL: moore.module @ProcInitialIfElse( +module ProcInitialIfElse(output logic out); + initial if (sel) out = lhs; else out = rhs; + logic sel; + logic lhs; + logic rhs; +endmodule + +// CHECK-LABEL: moore.module @ProcInitialNestedIf( +module ProcInitialNestedIf(output logic out); + initial if (outer) if (inner) out = lhs; else out = rhs; + logic outer; + logic inner; + logic lhs; + logic rhs; +endmodule + +// CHECK-LABEL: moore.module @ProcInitialCase( +module ProcInitialCase(output logic [3:0] out); + initial case (sel) + 2'd0: out = a; + 2'd1: out = b; + default: out = c; + endcase + logic [1:0] sel; + logic [3:0] a; + logic [3:0] b; + logic [3:0] c; +endmodule + +// CHECK-LABEL: moore.module @ProcInitialCasez( +module ProcInitialCasez(output logic out); + initial casez (sel) + 2'b1?: out = a; + default: out = b; + endcase + logic [1:0] sel; + logic a; + logic b; +endmodule + +// CHECK-LABEL: moore.module @ProcInitialFor( +module ProcInitialFor(output logic [3:0] out); + initial for (i = 0; i < 4; ++i) out[i] = data[i]; + int i; + logic [3:0] data; +endmodule + +// CHECK-LABEL: moore.module @ProcInitialForBlock( +module ProcInitialForBlock(output logic [3:0] out); + initial begin + for (i = 0; i < 4; ++i) + out[i] = data[i] ^ mask[i]; + end + int i; + logic [3:0] data; + logic [3:0] mask; +endmodule + +// CHECK-LABEL: moore.module @ProcInitialRepeat( +module ProcInitialRepeat(output logic [3:0] out); + initial repeat (count) out = data; + int count; + logic [3:0] data; +endmodule + +// CHECK-LABEL: moore.module @ProcInitialWhile( +module ProcInitialWhile(output logic [3:0] out); + initial while (enable) out = data; + logic enable; + logic [3:0] data; +endmodule + +// CHECK-LABEL: moore.module @ProcInitialDoWhile( +module ProcInitialDoWhile(output logic out); + initial do out = late; while (enable); + logic late; + logic enable; +endmodule + +// CHECK-LABEL: moore.module @ProcInitialWait( +module ProcInitialWait(output logic out); + initial wait (ready) out = late; + logic ready; + logic late; +endmodule + +// CHECK-LABEL: moore.module @ProcInitialEventControl( +module ProcInitialEventControl(output logic out); + initial @(posedge clk) out = late; + logic clk; + logic late; +endmodule + +// CHECK-LABEL: moore.module @ProcInitialDelayControl( +module ProcInitialDelayControl(output logic out); + initial #delay out = late; + int delay; + logic late; +endmodule + +// CHECK-LABEL: moore.module @ProcInitialNamedBlock( +module ProcInitialNamedBlock(output int out); + initial begin : body + out = late; + end + int late; +endmodule + +// CHECK-LABEL: moore.module @ProcInitialSequentialBlock( +module ProcInitialSequentialBlock(output int out); + initial begin + tmp = lhs + rhs; + out = tmp; + end + int tmp; + int lhs; + int rhs; +endmodule + +// CHECK-LABEL: moore.module @ProcInitialNonblocking( +module ProcInitialNonblocking(output logic out); + initial out <= late; + logic late; +endmodule + +// CHECK-LABEL: moore.module @ProcInitialCompoundAdd( +module ProcInitialCompoundAdd(output int out); + initial out += late; + int late; +endmodule + +// CHECK-LABEL: moore.module @ProcInitialCompoundXor( +module ProcInitialCompoundXor(output logic [7:0] out); + initial out ^= late; + logic [7:0] late; +endmodule + +// CHECK-LABEL: moore.module @ProcInitialIncrement( +module ProcInitialIncrement(output int out); + initial begin + late++; + out = late; + end + int late; +endmodule + +// CHECK-LABEL: moore.module @ProcInitialDecrement( +module ProcInitialDecrement(output int out); + initial begin + late--; + out = late; + end + int late; +endmodule + +// CHECK-LABEL: moore.module @ProcFinalIf( +module ProcFinalIf(output logic out); + final if (sel) out = late; + logic sel; + logic late; +endmodule + +// CHECK-LABEL: moore.module @ProcFinalCase( +module ProcFinalCase(output logic [3:0] out); + final case (sel) + 1'b0: out = lhs; + default: out = rhs; + endcase + logic sel; + logic [3:0] lhs; + logic [3:0] rhs; +endmodule + +// CHECK-LABEL: moore.module @ProcAlwaysCombIf( +module ProcAlwaysCombIf(output logic out); + always_comb if (sel) out = late; else out = other; + logic sel; + logic late; + logic other; +endmodule + +// CHECK-LABEL: moore.module @ProcAlwaysCombCase( +module ProcAlwaysCombCase(output logic [3:0] out); + always_comb case (sel) + 2'd0: out = a; + 2'd1: out = b; + default: out = c; + endcase + logic [1:0] sel; + logic [3:0] a; + logic [3:0] b; + logic [3:0] c; +endmodule + +// CHECK-LABEL: moore.module @ProcAlwaysAtStarBlock( +module ProcAlwaysAtStarBlock(output logic [7:0] out); + always @(*) begin + tmp = lhs ^ rhs; + out = tmp; + end + logic [7:0] tmp; + logic [7:0] lhs; + logic [7:0] rhs; +endmodule + +// CHECK-LABEL: moore.module @ProcAlwaysLatch( +module ProcAlwaysLatch(output logic out); + always_latch if (enable) out = late; + logic enable; + logic late; +endmodule + +// CHECK-LABEL: moore.module @ProcAlwaysFF( +module ProcAlwaysFF(input logic clk, output logic out); + always_ff @(posedge clk) out <= late; + logic late; +endmodule + +// CHECK-LABEL: moore.module @ProcAlwaysFFReset( +module ProcAlwaysFFReset(input logic clk, output logic out); + always_ff @(posedge clk or negedge rst_n) + if (!rst_n) out <= reset_value; else out <= late; + logic rst_n; + logic reset_value; + logic late; +endmodule + +// CHECK-LABEL: moore.module @ProcAlwaysFFEnable( +module ProcAlwaysFFEnable(input logic clk, output logic out); + always_ff @(posedge clk) if (enable) out <= late; + logic enable; + logic late; +endmodule + +// CHECK-LABEL: moore.module @ProcFunctionReturn( +module ProcFunctionReturn(output int out); + initial out = compute(); + function int compute(); + return late; + endfunction + int late; +endmodule + +// CHECK-LABEL: moore.module @ProcFunctionExpression( +module ProcFunctionExpression(output int out); + initial out = compute(); + function int compute(); + return lhs + rhs; + endfunction + int lhs; + int rhs; +endmodule + +// CHECK-LABEL: moore.module @ProcFunctionStruct( +module ProcFunctionStruct(output logic [3:0] out); + initial out = select_lo(); + function logic [3:0] select_lo(); + return pair.lo; + endfunction + proc_pair_t pair; +endmodule + +// CHECK-LABEL: moore.module @ProcTaskAssign( +module ProcTaskAssign(output int out); + initial drive(); + task drive(); + out = late; + endtask + int late; +endmodule + +// CHECK-LABEL: moore.module @ProcTaskBlock( +module ProcTaskBlock(output int out); + initial drive(); + task drive(); + tmp = lhs + rhs; + out = tmp; + endtask + int tmp; + int lhs; + int rhs; +endmodule + +// CHECK-LABEL: moore.module @ProcInterfaceInitialRead( +module ProcInterfaceInitialRead(output logic out); + initial out = bus.sig; + ProcBus bus(clk); + logic clk; +endmodule + +// CHECK-LABEL: moore.module @ProcInterfaceInitialWrite( +module ProcInterfaceInitialWrite(output logic out); + initial bus.sig = late; + assign out = bus.sig; + ProcWriteBus bus(clk); + logic clk; + logic late; +endmodule + +// CHECK-LABEL: moore.module @ProcInterfaceAlwaysComb( +module ProcInterfaceAlwaysComb(output logic [7:0] out); + always_comb out = bus.data ^ late; + ProcBus bus(clk); + logic clk; + logic [7:0] late; +endmodule + +// CHECK-LABEL: moore.module @ProcInterfaceCase( +module ProcInterfaceCase(output logic [7:0] out); + initial case (sel) + 1'b0: bus.data = a; + default: bus.data = b; + endcase + assign out = bus.data; + ProcWriteBus bus(clk); + logic clk; + logic sel; + logic [7:0] a; + logic [7:0] b; +endmodule + +// CHECK-LABEL: moore.module @ProcInstanceInitialRead( +module ProcInstanceInitialRead(output int out); + initial out = u.value; + ProcReadable u(.value(child_value)); + int child_value; +endmodule + +// CHECK-LABEL: moore.module @ProcInstanceInitialInput( +module ProcInstanceInitialInput(output logic [7:0] out); + initial out = u.out; + ProcLeaf u(.in(source ^ mask), .out(child_out)); + logic [7:0] source; + logic [7:0] mask; + logic [7:0] child_out; +endmodule + +// CHECK-LABEL: moore.module @ProcInstanceAlwaysComb( +module ProcInstanceAlwaysComb(output logic [7:0] out); + always_comb out = u.out + late; + ProcLeaf u(.in(source), .out(child_out)); + logic [7:0] source; + logic [7:0] child_out; + logic [7:0] late; +endmodule + +// CHECK-LABEL: moore.module @ProcGenerateIfInitial( +module ProcGenerateIfInitial(output logic out); + if (1) begin : g + initial out = late; + logic late; + end +endmodule + +// CHECK-LABEL: moore.module @ProcGenerateIfAlways( +module ProcGenerateIfAlways(output logic out); + if (1) begin : g + always_comb out = lhs ^ rhs; + logic lhs; + logic rhs; + end +endmodule + +// CHECK-LABEL: moore.module @ProcGenerateIfInterface( +module ProcGenerateIfInterface(output logic out); + if (1) begin : g + initial out = bus.sig; + ProcBus bus(clk); + logic clk; + end +endmodule + +// CHECK-LABEL: moore.module @ProcGenerateIfInstance( +module ProcGenerateIfInstance(output int out); + if (1) begin : g + initial out = u.value; + ProcReadable u(.value(child_value)); + int child_value; + end +endmodule + +// CHECK-LABEL: moore.module @ProcGenerateCaseInitial( +module ProcGenerateCaseInitial(output logic [3:0] out); + case (1) + 1: begin : g + initial out = late; + logic [3:0] late; + end + endcase +endmodule + +// CHECK-LABEL: moore.module @ProcGenerateCaseInterface( +module ProcGenerateCaseInterface(output logic out); + case (1) + 1: begin : g + initial out = bus.sig; + ProcBus bus(clk); + logic clk; + end + endcase +endmodule + +// CHECK-LABEL: moore.module @ProcGenerateLoopInitial( +module ProcGenerateLoopInitial(output logic [1:0] out); + for (genvar i = 0; i < 2; ++i) begin : lane + initial out[i] = late ^ bit'(i); + logic late; + end +endmodule + +// CHECK-LABEL: moore.module @ProcGenerateLoopAlways( +module ProcGenerateLoopAlways(output logic [1:0] out); + for (genvar i = 0; i < 2; ++i) begin : lane + always_comb out[i] = lhs ^ rhs; + logic lhs; + logic rhs; + end +endmodule + +// CHECK-LABEL: moore.module @ProcGenerateLoopInterface( +module ProcGenerateLoopInterface(output logic [1:0] out); + for (genvar i = 0; i < 2; ++i) begin : lane + initial out[i] = bus.sig; + ProcBus bus(clk); + logic clk; + end +endmodule + +// CHECK-LABEL: moore.module @ProcGenerateLoopInstance( +module ProcGenerateLoopInstance(output logic [1:0] out); + for (genvar i = 0; i < 2; ++i) begin : lane + initial out[i] = u.out[0]; + ProcLeaf u(.in(source), .out(child_out)); + logic [7:0] source; + logic [7:0] child_out; + end +endmodule + +// CHECK-LABEL: moore.module @ProcNestedGenerateProcedure( +module ProcNestedGenerateProcedure(output logic [3:0] out); + for (genvar i = 0; i < 2; ++i) begin : row + for (genvar j = 0; j < 2; ++j) begin : col + initial out[i * 2 + j] = late; + logic late; + end + end +endmodule + +// CHECK-LABEL: moore.module @ProcNestedGenerateInterface( +module ProcNestedGenerateInterface(output logic [3:0] out); + for (genvar i = 0; i < 2; ++i) begin : row + for (genvar j = 0; j < 2; ++j) begin : col + initial out[i * 2 + j] = bus.sig; + ProcBus bus(clk); + logic clk; + end + end +endmodule + +// CHECK-LABEL: moore.module @ProcMixedInterfaceInstance( +module ProcMixedInterfaceInstance(output logic [7:0] out); + initial begin + bus.data = seed; + out = u.out ^ bus.data; + end + ProcWriteBus bus(clk); + ProcLeaf u(.in(source), .out(child_out)); + logic clk; + logic [7:0] seed; + logic [7:0] source; + logic [7:0] child_out; +endmodule diff --git a/test/Conversion/ImportVerilog/use-before-declare.sv b/test/Conversion/ImportVerilog/use-before-declare.sv new file mode 100644 index 000000000000..4457860b3ed6 --- /dev/null +++ b/test/Conversion/ImportVerilog/use-before-declare.sv @@ -0,0 +1,66 @@ +// RUN: not circt-verilog --ir-moore %s 2>&1 | FileCheck %s --check-prefix=NOALLOW +// RUN: circt-verilog --ir-moore --allow-use-before-declare %s | FileCheck %s +// REQUIRES: slang +// +// Internal issue in Slang v3 about jump depending on uninitialised value. +// UNSUPPORTED: valgrind + +// NOALLOW: identifier 'a' used before its declaration + +// CHECK-LABEL: moore.module @ForwardProceduralUse() +module ForwardProceduralUse; + // CHECK-DAG: %a = moore.variable : + // CHECK-DAG: %b = moore.variable : + // CHECK: moore.procedure initial { + // CHECK: moore.read %b : + // CHECK: moore.blocking_assign %a, {{.*}} : i32 + // CHECK: } + initial a = b; + + int a; + int b; +endmodule + +// CHECK-LABEL: moore.module @ForwardVariableInitializers( +module ForwardVariableInitializers(output int out); + // CHECK-DAG: %[[A:[[:alnum:]_]+]] = moore.variable %[[AINIT:[[:alnum:]_]+]] : + // CHECK-DAG: %[[B:[[:alnum:]_]+]] = moore.variable {{.*}} : + // CHECK-DAG: %[[AREAD:[[:alnum:]_]+]] = moore.read %[[A]] : + // CHECK-DAG: %[[BREAD:[[:alnum:]_]+]] = moore.read %[[B]] : + // CHECK-DAG: %[[AINIT]] = moore.add %[[BREAD]] + // CHECK: moore.output %[[AREAD]] : !moore.i32 + int a = b + 1; + int b = 41; + assign out = a; +endmodule + +// CHECK-LABEL: moore.module @ForwardNetDeclarationAssign( +module ForwardNetDeclarationAssign(input logic in, output logic out); + // CHECK: %tmp = moore.assigned_variable %late : l1 + // CHECK: %late = moore.assigned_variable %in : l1 + // CHECK: moore.output %tmp : !moore.l1 + assign out = tmp; + wire tmp = late; + wire late = in; +endmodule + +interface ForwardVif(input logic clk); + logic sig; +endinterface + +// CHECK-LABEL: moore.module @ForwardVirtualInterfaceUse( +module ForwardVirtualInterfaceUse(input logic clk); + ForwardVif bus(clk); + + // The virtual interface declaration is intentionally after the use. In + // use-before-declare mode, the predeclaration pass still has to register + // virtual interface members so `vif.sig` resolves while lowering the + // procedure. + initial begin + // CHECK: moore.struct_extract + vif.sig = value; + end + + bit value; + virtual ForwardVif vif = bus; +endmodule