Skip to content

Commit a04eac9

Browse files
[ImportVerilog] Add global variable support (#9176)
Add new `moore.global_variable` and `moore.get_global_variable` ops to the Moore dialect, which define global variables as a symbol and return a reference to a global variable given its symbol, respectively. Add support for variables at the root level and within packages in SV inputs. Both of these get converted to `moore.global_variable`s. When referring to a global variable by name, create `get_global_variable` ops to resolve the symbol name to a `!moore.ref<T>` which can then be read. Lowering to the core dialects is not yet supported. We very likely need an equivalent global signal construct in LLHD.
1 parent bd2f112 commit a04eac9

File tree

8 files changed

+262
-21
lines changed

8 files changed

+262
-21
lines changed

include/circt/Dialect/Moore/MooreOps.td

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,50 @@ def ReadOp : MooreOp<"read", [
325325
}];
326326
}
327327

328+
def GlobalVariableOp : MooreOp<"global_variable", [
329+
IsolatedFromAbove,
330+
NoRegionArguments,
331+
SingleBlock,
332+
Symbol,
333+
]> {
334+
let summary = "A global variable declaration";
335+
let description = [{
336+
Defines a global or package variable.
337+
338+
See IEEE 1800-2017 § 6.8 "Variable declarations".
339+
}];
340+
let arguments = (ins
341+
SymbolNameAttr:$sym_name,
342+
TypeAttrOf<UnpackedType>:$type
343+
);
344+
let regions = (region MaxSizedRegion<1>:$initRegion);
345+
let assemblyFormat = [{
346+
$sym_name attr-dict `:` $type (`init` $initRegion^)?
347+
}];
348+
let hasRegionVerifier = 1;
349+
let extraClassDeclaration = [{
350+
Block *getInitBlock();
351+
}];
352+
}
353+
354+
def GetGlobalVariableOp : MooreOp<"get_global_variable", [
355+
DeclareOpInterfaceMethods<SymbolUserOpInterface>,
356+
Pure,
357+
]> {
358+
let summary = "Get a reference to a global variable";
359+
let arguments = (ins FlatSymbolRefAttr:$global_name);
360+
let results = (outs RefType:$result);
361+
let assemblyFormat = [{
362+
$global_name attr-dict `:` type($result)
363+
}];
364+
let builders = [
365+
OpBuilder<(ins "moore::GlobalVariableOp":$global), [{
366+
build($_builder, $_state,
367+
RefType::get(global.getType()), global.getSymName());
368+
}]>,
369+
];
370+
}
371+
328372
//===----------------------------------------------------------------------===//
329373
// Assignments
330374
//===----------------------------------------------------------------------===//
@@ -1778,7 +1822,7 @@ def ConditionalOp : MooreOp<"conditional",[
17781822
def YieldOp : MooreOp<"yield", [
17791823
Pure,
17801824
Terminator,
1781-
HasParent<"ConditionalOp">
1825+
ParentOneOf<["ConditionalOp", "GlobalVariableOp"]>,
17821826
]> {
17831827
let summary = "conditional yield and termination operation";
17841828
let description = [{

lib/Conversion/ImportVerilog/Expressions.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ struct RvalueExprVisitor : public ExprVisitor {
429429

430430
// Handle named values, such as references to declared variables.
431431
Value visit(const slang::ast::NamedValueExpression &expr) {
432+
// Handle local variables.
432433
if (auto value = context.valueSymbols.lookup(&expr.symbol)) {
433434
if (isa<moore::RefType>(value.getType())) {
434435
auto readOp = moore::ReadOp::create(builder, loc, value);
@@ -439,6 +440,12 @@ struct RvalueExprVisitor : public ExprVisitor {
439440
return value;
440441
}
441442

443+
// Handle global variables.
444+
if (auto globalOp = context.globalVariables.lookup(&expr.symbol)) {
445+
auto value = moore::GetGlobalVariableOp::create(builder, loc, globalOp);
446+
return moore::ReadOp::create(builder, loc, value);
447+
}
448+
442449
// We're reading a class property.
443450
if (auto *const property =
444451
expr.symbol.as_if<slang::ast::ClassPropertySymbol>()) {
@@ -1813,8 +1820,14 @@ struct LvalueExprVisitor : public ExprVisitor {
18131820

18141821
// Handle named values, such as references to declared variables.
18151822
Value visit(const slang::ast::NamedValueExpression &expr) {
1823+
// Handle local variables.
18161824
if (auto value = context.valueSymbols.lookup(&expr.symbol))
18171825
return value;
1826+
1827+
// Handle global variables.
1828+
if (auto globalOp = context.globalVariables.lookup(&expr.symbol))
1829+
return moore::GetGlobalVariableOp::create(builder, loc, globalOp);
1830+
18181831
auto d = mlir::emitError(loc, "unknown name `") << expr.symbol.name << "`";
18191832
d.attachNote(context.convertLocation(expr.symbol.location))
18201833
<< "no lvalue generated for " << slang::ast::toString(expr.symbol.kind);
@@ -1823,9 +1836,14 @@ struct LvalueExprVisitor : public ExprVisitor {
18231836

18241837
// Handle hierarchical values, such as `Top.sub.var = x`.
18251838
Value visit(const slang::ast::HierarchicalValueExpression &expr) {
1839+
// Handle local variables.
18261840
if (auto value = context.valueSymbols.lookup(&expr.symbol))
18271841
return value;
18281842

1843+
// Handle global variables.
1844+
if (auto globalOp = context.globalVariables.lookup(&expr.symbol))
1845+
return moore::GetGlobalVariableOp::create(builder, loc, globalOp);
1846+
18291847
// Emit an error for those hierarchical values not recorded in the
18301848
// `valueSymbols`.
18311849
auto d = mlir::emitError(loc, "unknown hierarchical name `")

lib/Conversion/ImportVerilog/ImportVerilogInternals.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ struct Context {
122122
LogicalResult finalizeFunctionBodyCaptures(FunctionLowering &lowering);
123123
LogicalResult convertClassDeclaration(const slang::ast::ClassType &classdecl);
124124
ClassLowering *declareClass(const slang::ast::ClassType &cls);
125+
LogicalResult convertGlobalVariable(const slang::ast::VariableSymbol &var);
125126

126127
/// Checks whether one class (actualTy) is derived from another class
127128
/// (baseTy). True if it's a subclass, false otherwise.
@@ -285,6 +286,14 @@ struct Context {
285286
using ValueSymbolScope = ValueSymbols::ScopeTy;
286287
ValueSymbols valueSymbols;
287288

289+
/// A table of defined global variables that may be referred to by name in
290+
/// expressions.
291+
DenseMap<const slang::ast::ValueSymbol *, moore::GlobalVariableOp>
292+
globalVariables;
293+
/// A list of global variables that still need their initializers to be
294+
/// converted.
295+
SmallVector<const slang::ast::ValueSymbol *> globalVariableWorklist;
296+
288297
/// Collect all hierarchical names used for the per module/instance.
289298
DenseMap<const slang::ast::InstanceBodySymbol *, SmallVector<HierPathInfo>>
290299
hierPaths;

lib/Conversion/ImportVerilog/Structure.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,11 @@ struct RootVisitor : public BaseVisitor {
148148
return context.convertFunction(subroutine);
149149
}
150150

151+
// Handle global variables.
152+
LogicalResult visit(const slang::ast::VariableSymbol &var) {
153+
return context.convertGlobalVariable(var);
154+
}
155+
151156
// Emit an error for all other members.
152157
template <typename T>
153158
LogicalResult visit(T &&node) {
@@ -172,6 +177,11 @@ struct PackageVisitor : public BaseVisitor {
172177
return context.convertFunction(subroutine);
173178
}
174179

180+
// Handle global variables.
181+
LogicalResult visit(const slang::ast::VariableSymbol &var) {
182+
return context.convertGlobalVariable(var);
183+
}
184+
175185
/// Emit an error for all other members.
176186
template <typename T>
177187
LogicalResult visit(T &&node) {
@@ -701,6 +711,20 @@ LogicalResult Context::convertCompilation() {
701711
return failure();
702712
}
703713

714+
// Convert the initializers of global variables.
715+
for (auto *var : globalVariableWorklist) {
716+
auto varOp = globalVariables.at(var);
717+
auto &block = varOp.getInitRegion().emplaceBlock();
718+
OpBuilder::InsertionGuard guard(builder);
719+
builder.setInsertionPointToEnd(&block);
720+
auto value =
721+
convertRvalueExpression(*var->getInitializer(), varOp.getType());
722+
if (!value)
723+
return failure();
724+
moore::YieldOp::create(builder, varOp.getLoc(), value);
725+
}
726+
globalVariableWorklist.clear();
727+
704728
return success();
705729
}
706730

@@ -1676,3 +1700,43 @@ Context::convertClassDeclaration(const slang::ast::ClassType &classdecl) {
16761700

16771701
return success();
16781702
}
1703+
1704+
/// Convert a variable to a `moore.global_variable` operation.
1705+
LogicalResult
1706+
Context::convertGlobalVariable(const slang::ast::VariableSymbol &var) {
1707+
auto loc = convertLocation(var.location);
1708+
1709+
// Pick an insertion point for this variable according to the source file
1710+
// location.
1711+
OpBuilder::InsertionGuard g(builder);
1712+
auto it = orderedRootOps.upper_bound(var.location);
1713+
if (it == orderedRootOps.end())
1714+
builder.setInsertionPointToEnd(intoModuleOp.getBody());
1715+
else
1716+
builder.setInsertionPoint(it->second);
1717+
1718+
// Prefix the variable name with the surrounding namespace to create somewhat
1719+
// sane names in the IR.
1720+
SmallString<64> symName;
1721+
guessNamespacePrefix(var.getParentScope()->asSymbol(), symName);
1722+
symName += var.name;
1723+
1724+
// Determine the type of the variable.
1725+
auto type = convertType(var.getType());
1726+
if (!type)
1727+
return failure();
1728+
1729+
// Create the variable op itself.
1730+
auto varOp = moore::GlobalVariableOp::create(builder, loc, symName,
1731+
cast<moore::UnpackedType>(type));
1732+
orderedRootOps.insert({var.location, varOp});
1733+
globalVariables.insert({&var, varOp});
1734+
1735+
// If the variable has an initializer expression, remember it for later such
1736+
// that we can convert the initializers once we have seen all global
1737+
// variables.
1738+
if (var.getInitializer())
1739+
globalVariableWorklist.push_back(&var);
1740+
1741+
return success();
1742+
}

lib/Dialect/Moore/MooreOps.cpp

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,54 @@ LogicalResult AssignedVariableOp::canonicalize(AssignedVariableOp op,
536536
return failure();
537537
}
538538

539+
//===----------------------------------------------------------------------===//
540+
// GlobalVariableOp
541+
//===----------------------------------------------------------------------===//
542+
543+
LogicalResult GlobalVariableOp::verifyRegions() {
544+
if (auto *block = getInitBlock()) {
545+
auto &terminator = block->back();
546+
if (!isa<YieldOp>(terminator))
547+
return emitOpError() << "must have a 'moore.yield' terminator";
548+
}
549+
return success();
550+
}
551+
552+
Block *GlobalVariableOp::getInitBlock() {
553+
if (getInitRegion().empty())
554+
return nullptr;
555+
return &getInitRegion().front();
556+
}
557+
558+
//===----------------------------------------------------------------------===//
559+
// GetGlobalVariableOp
560+
//===----------------------------------------------------------------------===//
561+
562+
LogicalResult
563+
GetGlobalVariableOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
564+
// Resolve the target symbol.
565+
auto *symbol =
566+
symbolTable.lookupNearestSymbolFrom(*this, getGlobalNameAttr());
567+
if (!symbol)
568+
return emitOpError() << "references unknown symbol " << getGlobalNameAttr();
569+
570+
// Check that the symbol is a global variable.
571+
auto var = dyn_cast<GlobalVariableOp>(symbol);
572+
if (!var)
573+
return emitOpError() << "must reference a 'moore.global_variable', but "
574+
<< getGlobalNameAttr() << " is a '"
575+
<< symbol->getName() << "'";
576+
577+
// Check that the types match.
578+
auto expType = var.getType();
579+
auto actType = getType().getNestedType();
580+
if (expType != actType)
581+
return emitOpError() << "returns a " << actType << " reference, but "
582+
<< getGlobalNameAttr() << " is of type " << expType;
583+
584+
return success();
585+
}
586+
539587
//===----------------------------------------------------------------------===//
540588
// ConstantOp
541589
//===----------------------------------------------------------------------===//
@@ -1027,22 +1075,21 @@ LogicalResult UnionExtractRefOp::verify() {
10271075
//===----------------------------------------------------------------------===//
10281076

10291077
LogicalResult YieldOp::verify() {
1030-
// Check that YieldOp's parent operation is ConditionalOp.
1031-
auto cond = dyn_cast<ConditionalOp>(*(*this).getParentOp());
1032-
if (!cond) {
1033-
emitOpError("must have a conditional parent");
1034-
return failure();
1078+
Type expType;
1079+
auto *parentOp = getOperation()->getParentOp();
1080+
if (auto cond = dyn_cast<ConditionalOp>(parentOp)) {
1081+
expType = cond.getType();
1082+
} else if (auto varOp = dyn_cast<GlobalVariableOp>(parentOp)) {
1083+
expType = varOp.getType();
1084+
} else {
1085+
llvm_unreachable("all in ParentOneOf handled");
10351086
}
10361087

1037-
// Check that the operand matches the parent operation's result.
1038-
auto condType = cond.getType();
1039-
auto yieldType = getOperand().getType();
1040-
if (condType != yieldType) {
1041-
emitOpError("yield type must match conditional. Expected ")
1042-
<< condType << ", but got " << yieldType << ".";
1043-
return failure();
1088+
auto actType = getOperand().getType();
1089+
if (expType != actType) {
1090+
return emitOpError() << "yields " << actType << ", but parent expects "
1091+
<< expType;
10441092
}
1045-
10461093
return success();
10471094
}
10481095

test/Conversion/ImportVerilog/basic.sv

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3715,3 +3715,21 @@ module RejectInnerCapture(input bit u, output bit v);
37153715
foo = a;
37163716
endfunction
37173717
endmodule
3718+
3719+
// CHECK-LABEL: moore.global_variable @rootGlobal1 : !moore.i42
3720+
bit [41:0] rootGlobal1;
3721+
// CHECK-LABEL: moore.global_variable @rootGlobal2 : !moore.i42 init {
3722+
// CHECK-NEXT: [[TMP1:%.+]] = moore.get_global_variable @"PackageGlobal::packageGlobal2" : <i42>
3723+
// CHECK-NEXT: [[TMP2:%.+]] = moore.read [[TMP1]] : <i42>
3724+
// CHECK-NEXT: moore.yield [[TMP2]] : i42
3725+
bit [41:0] rootGlobal2 = PackageGlobal::packageGlobal2;
3726+
3727+
package PackageGlobal;
3728+
// CHECK-LABEL: moore.global_variable @"PackageGlobal::packageGlobal1" : !moore.i42
3729+
bit [41:0] packageGlobal1;
3730+
// CHECK-LABEL: moore.global_variable @"PackageGlobal::packageGlobal2" : !moore.i42 init {
3731+
// CHECK-NEXT: [[TMP1:%.+]] = moore.get_global_variable @"PackageGlobal::packageGlobal1" : <i42>
3732+
// CHECK-NEXT: [[TMP2:%.+]] = moore.read [[TMP1]] : <i42>
3733+
// CHECK-NEXT: moore.yield [[TMP2]] : i42
3734+
bit [41:0] packageGlobal2 = packageGlobal1;
3735+
endpackage

test/Dialect/Moore/basic.mlir

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -471,18 +471,35 @@ func.func @TimeConversion(%arg0: !moore.time, %arg1: !moore.l64) {
471471

472472
// CHECK-LABEL: func.func @RealConversion32(%arg0: !moore.f32, %arg1: !moore.i42)
473473
func.func @RealConversion32(%arg0: !moore.f32, %arg1: !moore.i42) {
474-
// CHECK: %0 = moore.real_to_int %arg0 : f32 -> i42
474+
// CHECK: moore.real_to_int %arg0 : f32 -> i42
475475
%0 = moore.real_to_int %arg0 : f32 -> i42
476-
// CHECK: %1 = moore.int_to_real %arg1 : i42 -> f32
476+
// CHECK: moore.int_to_real %arg1 : i42 -> f32
477477
%1 = moore.int_to_real %arg1 : i42 -> f32
478478
return
479479
}
480480

481481
// CHECK-LABEL: func.func @RealConversion64(%arg0: !moore.f64, %arg1: !moore.i42)
482482
func.func @RealConversion64(%arg0: !moore.f64, %arg1: !moore.i42) {
483-
// CHECK: %0 = moore.real_to_int %arg0 : f64 -> i42
483+
// CHECK: moore.real_to_int %arg0 : f64 -> i42
484484
%0 = moore.real_to_int %arg0 : f64 -> i42
485-
// CHECK: %1 = moore.int_to_real %arg1 : i42 -> f64
485+
// CHECK: moore.int_to_real %arg1 : i42 -> f64
486486
%1 = moore.int_to_real %arg1 : i42 -> f64
487487
return
488488
}
489+
490+
// CHECK-LABEL: moore.global_variable @GlobalVar1 : !moore.i42
491+
moore.global_variable @GlobalVar1 : !moore.i42
492+
493+
// CHECK: moore.get_global_variable @GlobalVar1 : <i42>
494+
moore.get_global_variable @GlobalVar1 : <i42>
495+
496+
// CHECK-LABEL: moore.global_variable @GlobalVar2 : !moore.i42
497+
moore.global_variable @GlobalVar2 : !moore.i42 init {
498+
// CHECK-NEXT: moore.constant
499+
%0 = moore.constant 9001 : i42
500+
// CHECK-NEXT: moore.yield
501+
moore.yield %0 : !moore.i42
502+
}
503+
504+
// CHECK: moore.get_global_variable @GlobalVar2 : <i42>
505+
moore.get_global_variable @GlobalVar2 : <i42>

0 commit comments

Comments
 (0)