From db815c237c89f33d0e40090f7f75c868b00cffb6 Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Mon, 14 Jul 2025 17:25:57 -0400 Subject: [PATCH] [hw] Add container op Add a new `hw.container` op. This is intended to be a generic container of hardware. Critically, this exists to enable the checking of ops that are users of inner symbol tables. This is shown with the one error test case that shows validation of a `hw.hierpath` op with an invalid inner symbol use. (This will not be checked if a `builtin.module` is used.) Signed-off-by: Schuyler Eldridge --- include/circt/Dialect/HW/HWStructure.td | 20 ++++++++++++++++++-- test/Dialect/HW/basic.mlir | 6 ++++++ test/Dialect/HW/errors.mlir | 12 ++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/include/circt/Dialect/HW/HWStructure.td b/include/circt/Dialect/HW/HWStructure.td index f92bf2884b75..d0b6676f27ce 100644 --- a/include/circt/Dialect/HW/HWStructure.td +++ b/include/circt/Dialect/HW/HWStructure.td @@ -26,6 +26,22 @@ include "mlir/IR/SymbolInterfaces.td" include "mlir/Interfaces/ControlFlowInterfaces.td" include "mlir/Interfaces/SideEffectInterfaces.td" +def ContainerOp : HWOp<"container", + [IsolatedFromAbove, SymbolTable, SingleBlock, NoTerminator, + NoRegionArguments, InnerRefNamespace]> { + let summary = "HW Container (of modules and other things)"; + let description = [{ + A HW container is a top-level container of HW operations (and anything + else). This is roughly analogous to `builtin.module`, except that it also + implements an `InnerRefNamespace` which allows for verification of inner + refs. + }]; + let arguments = (ins OptionalAttr:$sym_name, + OptionalAttr:$sym_visibility); + let regions = (region SizedRegion<1>:$bodyRegion); + let assemblyFormat = "($sym_name^)? attr-dict-with-keyword $bodyRegion"; +} + /// Base class factoring out some of the additional class declarations common to /// the module-like operations. class HWModuleOpBase traits = []> : @@ -34,7 +50,7 @@ class HWModuleOpBase traits = []> : DeclareOpInterfaceMethods, DeclareOpInterfaceMethods, Symbol, InnerSymbolTable, - OpAsmOpInterface, HasParent<"mlir::ModuleOp">]> { + OpAsmOpInterface, ParentOneOf<["mlir::ModuleOp", "hw::ContainerOp"]>]> { /// Additional class declarations inside the module op. code extraModuleClassDeclaration = ?; @@ -304,7 +320,7 @@ def HWModuleExternOp : HWModuleOpBase<"module.extern"> { } def HWGeneratorSchemaOp : HWOp<"generator.schema", - [Symbol, HasParent<"mlir::ModuleOp">]> { + [Symbol, ParentOneOf<["mlir::ModuleOp", "hw::ContainerOp"]>]> { let summary = "HW Generator Schema declaration"; let description = [{ The "hw.generator.schema" operation declares a kind of generated module by diff --git a/test/Dialect/HW/basic.mlir b/test/Dialect/HW/basic.mlir index 3e0e6275abbb..0d83091ef8a5 100644 --- a/test/Dialect/HW/basic.mlir +++ b/test/Dialect/HW/basic.mlir @@ -270,3 +270,9 @@ hw.module @aggregate_const(out o : !hw.array<1x!seq.clock>) { %0 = hw.aggregate_constant [#seq : !seq.clock] : !hw.array<1x!seq.clock> hw.output %0 : !hw.array<1x!seq.clock> } + +hw.container { + + hw.module @Foo() {} + +} diff --git a/test/Dialect/HW/errors.mlir b/test/Dialect/HW/errors.mlir index 3e79e8447d46..ec3c2afc5e29 100644 --- a/test/Dialect/HW/errors.mlir +++ b/test/Dialect/HW/errors.mlir @@ -546,3 +546,15 @@ hw.array_get %0[%1] : !hw.array<1000xi42>, i9 %2 = hw.constant 0 : i42 // expected-error @below {{index bit width equals ceil(log2(length(input))), or 0 or 1 if input contains only one element}} hw.array_inject %0[%1], %2 : !hw.array<1000xi42>, i9 + +// ----- + +hw.container { + + // expected-error @below {{operation with symbol: #hw.innerNameRef<@Foo::@b> was not found}} + hw.hierpath @path [@Foo::@b] + hw.module @Foo(in %in: i1) { + %a = hw.wire %in sym @a : i1 + } + +}