diff --git a/lib/Conversion/ImportVerilog/ImportVerilogInternals.h b/lib/Conversion/ImportVerilog/ImportVerilogInternals.h index 7b440bfb20dd..c39b0d4bb3e0 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 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..708acbedc2c0 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,9 @@ struct ModuleVisitor : public BaseVisitor { using slang::ast::MultiPortSymbol; using slang::ast::PortSymbol; + 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 +420,11 @@ 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.interfaceInstances.lookup(&instNode)) + return success(); return expandInterfaceInstance(instNode); + } auto *moduleLowering = context.convertModuleHeader(body); if (!moduleLowering) @@ -706,55 +713,48 @@ struct ModuleVisitor : public BaseVisitor { // Handle variables. LogicalResult visit(const slang::ast::VariableSymbol &varNode) { - auto loweredType = context.convertType(*varNode.getDeclaredType()); - if (!loweredType) - return failure(); + auto ref = context.valueSymbols.lookup(&varNode); + if (!ref) + return mlir::emitError(loc) + << "internal error: missing predeclared variable `" << varNode.name + << "`"; + + auto varOp = ref.getDefiningOp(); + if (!varOp) + return mlir::emitError(loc) + << "internal error: predeclared variable `" << varNode.name + << "` is not a moore.variable"; - Value initial; if (const auto *init = varNode.getInitializer()) { - initial = context.convertRvalueExpression(*init, loweredType); + auto loweredType = cast(ref.getType()).getNestedType(); + auto initial = context.convertRvalueExpression(*init, loweredType); if (!initial) return failure(); + varOp.getInitialMutable().assign(initial); } - auto varOp = moore::VariableOp::create( - builder, loc, - moore::RefType::get(cast(loweredType)), - builder.getStringAttr(Twine(blockNamePrefix) + varNode.name), initial); - 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(); return success(); } // Handle nets. LogicalResult visit(const slang::ast::NetSymbol &netNode) { - auto loweredType = context.convertType(*netNode.getDeclaredType()); - if (!loweredType) - return failure(); + auto ref = context.valueSymbols.lookup(&netNode); + if (!ref) + return mlir::emitError(loc) << "internal error: missing predeclared net `" + << netNode.name << "`"; + + auto netOp = ref.getDefiningOp(); + if (!netOp) + return mlir::emitError(loc) << "internal error: predeclared net `" + << netNode.name << "` is not a moore.net"; - Value assignment; if (const auto *init = netNode.getInitializer()) { - assignment = context.convertRvalueExpression(*init, loweredType); + auto loweredType = cast(ref.getType()).getNestedType(); + auto assignment = context.convertRvalueExpression(*init, loweredType); if (!assignment) return failure(); + netOp.getAssignmentMutable().assign(assignment); } - - 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, - assignment); - context.valueSymbols.insert(&netNode, netOp); return success(); } @@ -913,6 +913,240 @@ struct ModuleVisitor : public BaseVisitor { return failure(); } }; + +struct ModulePredeclaration { + Context &context; + OpBuilder &builder; + + ModulePredeclaration(Context &context) + : context(context), builder(context.builder) {} + + 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(); + + 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); + 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 + 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); + } +}; } // namespace //===----------------------------------------------------------------------===// @@ -1317,6 +1551,20 @@ Context::convertModuleBody(const slang::ast::InstanceBodySymbol *module) { } } + predeclaredInstances.clear(); + llvm::scope_exit predeclaredInstancesGuard( + [&] { predeclaredInstances.clear(); }); + + // Always create module-scope storage, expanded interface members, and + // instance shells before the source-order body walk. Slang rejects + // use-before-declare before ImportVerilog runs unless the option is enabled, + // but once the AST is valid this predeclaration supports both source-order + // and forward references. Declaration initializers are still lowered when the + // body visitor reaches the declaration, so they see the same local context as + // other source-ordered expressions. + if (failed(ModulePredeclaration(*this).predeclareScope(*module, ""))) + return failure(); + // Convert the body of the module. for (auto &member : module->members()) { auto loc = convertLocation(member.location); 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.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