Skip to content

Commit f63ea4d

Browse files
Add sv.verbatim.source and sv.verbatim.module ops for inline black box extmodules (#9227)
A second attempt of #9131. Changes from the first PR were made in order to support parametrized black boxes. Because parameters can affect the port interface of a module, we need two ops: 1. sv.verbatim.source to represent the shared verbatim definition of a module 2. sv.verbatim.module to represent a particular parametrization of that module that can be instantiated The pipeline is largely the same: - BlackBoxInlineAnno and BlackBoxPathAnno are converted to VerbatimBlackBoxAnno by BlackBoxReader for consumption by LowerToHW. - LowerToHW will lower firrtl.extmodules with a VerbatimBlackBoxAnno to a sv.verbatim.source (only one per file) and a sv.verbatim.module.
1 parent 1691fff commit f63ea4d

File tree

18 files changed

+1315
-85
lines changed

18 files changed

+1315
-85
lines changed

docs/Dialects/FIRRTL/FIRRTLAnnotations.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,38 @@ Example:
305305
}
306306
```
307307

308+
### VerbatimBlackBoxAnno
309+
310+
| Property | Type | Description |
311+
| ---------- | ------ | ------------- |
312+
| class | string | `circt.VerbatimBlackBoxAnno` |
313+
| target | string | An ExtModule name target |
314+
| files | array | Array of file objects |
315+
316+
Specifies verbatim black box source code with one or more files. Each file
317+
object in the `files` array contains:
318+
319+
- `content`: The literal source code content
320+
- `output_file`: Path to the output file
321+
322+
This annotation is used internally by CIRCT to represent partially lowered
323+
FIRRTL extmodules with verbatim content.
324+
325+
Example:
326+
327+
```json
328+
{
329+
"class": "circt.VerbatimBlackBoxAnno",
330+
"target": "~Foo|MyBlackBox",
331+
"files": [
332+
{
333+
"content": "module MyBlackBox(\n input clk,\n output out\n);\n assign out = clk;\nendmodule",
334+
"output_file": "blackbox.v"
335+
}
336+
]
337+
}
338+
```
339+
308340
### Convention
309341

310342
| Property | Type | Description |

include/circt/Dialect/FIRRTL/AnnotationDetails.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ constexpr const char *blackBoxTargetDirAnnoClass =
5858
"firrtl.transforms.BlackBoxTargetDirAnno";
5959
constexpr const char *blackBoxAnnoClass =
6060
"firrtl.transforms.BlackBox"; // Not in SFC
61+
constexpr const char *verbatimBlackBoxAnnoClass = "circt.VerbatimBlackBoxAnno";
6162
constexpr const char *mustDedupAnnoClass =
6263
"firrtl.transforms.MustDeduplicateAnnotation";
6364
constexpr const char *runFIRRTLTransformAnnoClass =

include/circt/Dialect/SV/SVTypeDecl.td

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
include "circt/Dialect/HW/HWOpInterfaces.td"
14+
include "circt/Dialect/HW/HWTypes.td"
15+
include "circt/Dialect/HW/HWAttributes.td"
1416
include "mlir/IR/EnumAttr.td"
1517

1618
//===----------------------------------------------------------------------===//
@@ -206,6 +208,89 @@ def ModportType : SVType<"Modport"> {
206208
let assemblyFormat = "`<` $modport `>`";
207209
}
208210

211+
//===----------------------------------------------------------------------===//
212+
// Verbatim source operation
213+
//===----------------------------------------------------------------------===//
214+
215+
def SVVerbatimSourceOp
216+
: SVOp<"verbatim.source", [Symbol, HasParent<"mlir::ModuleOp">,
217+
DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
218+
let summary = "Verbatim verilog source";
219+
let description = [{
220+
The "sv.verbatim.source" operation represents verbatim verilog definition for
221+
a module which may have parameters. Concrete parametrizations and their ports
222+
are represented by a `hw.module.extern`.
223+
}];
224+
225+
let arguments = (ins SymbolNameAttr:$sym_name,
226+
StrAttr:$content,
227+
OutputFileAttr:$output_file,
228+
ParamDeclArrayAttr:$parameters,
229+
OptionalAttr<SymbolRefArrayAttr>:$additional_files,
230+
StrAttr:$verilogName);
231+
232+
let hasCustomAssemblyFormat = 1;
233+
234+
let hasVerifier = 1;
235+
}
236+
237+
//===----------------------------------------------------------------------===//
238+
// Verbatim module operation
239+
//===----------------------------------------------------------------------===//
240+
241+
def SVVerbatimModuleOp
242+
: SVOp<"verbatim.module", [DeclareOpInterfaceMethods<PortList>,
243+
DeclareOpInterfaceMethods<HWModuleLike>, Symbol,
244+
DeclareOpInterfaceMethods<SymbolUserOpInterface>,
245+
HasParent<"mlir::ModuleOp">]> {
246+
let summary = "Verbatim verilog module";
247+
let description = [{
248+
The "sv.verbatim.module" operation represents a the module interface of a
249+
verbatim verilog module. It references an "sv.verbatim.source" operation
250+
that contains the actual verilog source code.
251+
}];
252+
253+
let arguments = (ins SymbolNameAttr:$sym_name,
254+
TypeAttrOf<ModuleType>:$module_type,
255+
OptionalAttr<DictArrayAttr>:$per_port_attrs,
256+
OptionalAttr<LocationArrayAttr>:$port_locs,
257+
ParamDeclArrayAttr:$parameters,
258+
FlatSymbolRefAttr:$source,
259+
OptionalAttr<StrAttr>:$verilogName);
260+
261+
let results = (outs);
262+
263+
let hasCustomAssemblyFormat = 1;
264+
265+
let builders = [
266+
OpBuilder<(ins "StringAttr":$name,
267+
"ArrayRef<hw::PortInfo>":$ports,
268+
"FlatSymbolRefAttr":$source,
269+
CArg<"ArrayAttr", "{}">:$parameters,
270+
CArg<"StringAttr", "{}">:$verilogName), [{
271+
SmallVector<hw::ModulePort> modulePorts;
272+
SmallVector<Attribute> portAttrs, portLocs;
273+
for (const auto &port : ports) {
274+
Type portType = port.type;
275+
hw::ModulePort::Direction portDir = port.dir;
276+
if (auto inoutType = dyn_cast<hw::InOutType>(port.type)) {
277+
portType = inoutType.getElementType();
278+
portDir = hw::ModulePort::Direction::InOut;
279+
}
280+
modulePorts.push_back({port.name, portType, portDir});
281+
portAttrs.push_back(port.attrs ? port.attrs : $_builder.getDictionaryAttr({}));
282+
portLocs.push_back(port.loc ? static_cast<Location>(port.loc) : $_builder.getUnknownLoc());
283+
}
284+
auto moduleType = hw::ModuleType::get($_builder.getContext(), modulePorts);
285+
build($_builder, $_state, name, TypeAttr::get(moduleType),
286+
$_builder.getArrayAttr(portAttrs), $_builder.getArrayAttr(portLocs),
287+
parameters, source, verilogName);
288+
}]>
289+
];
290+
291+
let hasVerifier = 1;
292+
}
293+
209294
//===----------------------------------------------------------------------===//
210295
// Other operations
211296
//===----------------------------------------------------------------------===//

include/circt/Dialect/SV/SVVisitors.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ class Visitor {
4343
FWriteOp, FFlushOp, SystemFunctionOp, VerbatimOp, MacroRefOp,
4444
FuncCallOp, FuncCallProceduralOp, ReturnOp, IncludeOp, MacroErrorOp,
4545
// Type declarations.
46-
InterfaceOp, InterfaceSignalOp, InterfaceModportOp,
47-
InterfaceInstanceOp, GetModportOp, AssignInterfaceSignalOp,
48-
ReadInterfaceSignalOp, MacroDeclOp, MacroDefOp, FuncOp,
49-
FuncDPIImportOp,
46+
InterfaceOp, SVVerbatimSourceOp, InterfaceSignalOp,
47+
InterfaceModportOp, InterfaceInstanceOp, GetModportOp,
48+
AssignInterfaceSignalOp, ReadInterfaceSignalOp, MacroDeclOp,
49+
MacroDefOp, FuncOp, FuncDPIImportOp,
5050
// Verification statements.
5151
AssertOp, AssumeOp, CoverOp, AssertConcurrentOp, AssumeConcurrentOp,
5252
CoverConcurrentOp, AssertPropertyOp, AssumePropertyOp,
@@ -147,6 +147,7 @@ class Visitor {
147147

148148
// Type declarations.
149149
HANDLE(InterfaceOp, Unhandled);
150+
HANDLE(SVVerbatimSourceOp, Unhandled);
150151
HANDLE(InterfaceInstanceOp, Unhandled);
151152
HANDLE(InterfaceSignalOp, Unhandled);
152153
HANDLE(InterfaceModportOp, Unhandled);
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# REQUIRES: bindings_python
2+
# RUN: %PYTHON% %s | FileCheck %s
3+
4+
import circt
5+
from circt.dialects import hw, sv, emit
6+
from circt import ir
7+
8+
with ir.Context() as ctx, ir.Location.unknown():
9+
circt.register_dialects(ctx)
10+
11+
i1 = ir.IntegerType.get_signless(1)
12+
i8 = ir.IntegerType.get_signless(8)
13+
i32 = ir.IntegerType.get_signless(32)
14+
15+
m = ir.Module.create()
16+
with ir.InsertionPoint(m.body):
17+
verbatim_content = "module MyVerbatim(input clk, output reg out); always @(posedge clk) out <= ~out; endmodule"
18+
verbatim_source = sv.SVVerbatimSourceOp(
19+
sym_name="MyVerbatim.v",
20+
content=verbatim_content,
21+
output_file=hw.OutputFileAttr.get_from_filename(
22+
ir.StringAttr.get("MyVerbatim.v"), False, False),
23+
verilog_name="MyVerbatim")
24+
25+
# CHECK: sv.verbatim.source @MyVerbatim.v
26+
# CHECK-SAME: attributes {
27+
# CHECK-SAME: content = "module MyVerbatim(input clk, output reg out); always @(posedge clk) out <= ~out; endmodule",
28+
# CHECK-SAME: output_file = #hw.output_file<"MyVerbatim.v">,
29+
# CHECK-SAME: verilogName = "MyVerbatim"
30+
# CHECK-SAME: }
31+
print(verbatim_source)
32+
33+
verbatim_module = sv.SVVerbatimModuleOp(
34+
name="MyVerbatim",
35+
source=ir.FlatSymbolRefAttr.get("MyVerbatim.v"),
36+
input_ports=[("clk", i1)],
37+
output_ports=[("out", i1)],
38+
verilog_name="MyVerbatim")
39+
40+
# CHECK: sv.verbatim.module @MyVerbatim
41+
# CHECK-SAME: (in %clk : i1, out out : i1)
42+
# CHECK-SAME attributes {
43+
# CHECK-SAME: source = @MyVerbatim.v,
44+
# CHECK-SAME: verilogName = "MyVerbatim"
45+
# CHECK-SAME: }
46+
print(verbatim_module)
47+
48+
param = hw.ParamDeclAttr.get("WIDTH", i32, ir.IntegerAttr.get(i32, 8))
49+
parameters = ir.ArrayAttr.get([param])
50+
51+
param_verbatim_source = sv.SVVerbatimSourceOp(
52+
sym_name="ParametrizedVerbatim.v",
53+
content=
54+
"module ParametrizedVerbatim #(parameter int WIDTH = 8)(); endmodule",
55+
output_file=hw.OutputFileAttr.get_from_filename(
56+
ir.StringAttr.get("ParametrizedVerbatim.v"), False, False),
57+
parameters=parameters,
58+
verilog_name="ParametrizedVerbatim")
59+
60+
# CHECK: sv.verbatim.source @ParametrizedVerbatim.v
61+
# CHECK-SAME: <WIDTH: i32 = 8>
62+
# CHECK-SAME: attributes {
63+
# CHECK-SAME: content = "module ParametrizedVerbatim #(parameter int WIDTH = 8)(); endmodule",
64+
# CHECK-SAME: output_file = #hw.output_file<"ParametrizedVerbatim.v">,
65+
# CHECK-SAME: verilogName = "ParametrizedVerbatim"
66+
# CHECK-SAME: }
67+
print(param_verbatim_source)
68+
69+
# Test parametrized verbatim module
70+
param_verbatim_module = sv.SVVerbatimModuleOp(
71+
name="ParametrizedVerbatim",
72+
source=ir.FlatSymbolRefAttr.get("ParametrizedVerbatim.v"),
73+
input_ports=[("data_in", i8)],
74+
output_ports=[("data_out", i8)],
75+
parameters=[param],
76+
verilog_name="ParametrizedVerbatim")
77+
78+
# CHECK: sv.verbatim.module @ParametrizedVerbatim
79+
# CHECK-SAME: <WIDTH: i32 = 8>
80+
# CHECK-SAME (in %data_in : i8, out data_out : i8)
81+
# CHECK-SAME: attributes {
82+
# CHECK-SAME: source = @ParametrizedVerbatim.v,
83+
# CHECK-SAME: verilogName = "ParametrizedVerbatim"
84+
# CHECK-SAME: }
85+
print(param_verbatim_module)
86+
87+
print("=== SV Verbatim Operations Test Completed ===")
88+
# CHECK: === SV Verbatim Operations Test Completed ===

lib/Bindings/Python/dialects/sv.py

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from ._sv_ops_gen import *
1111
from ._sv_ops_gen import _Dialect
1212

13-
from typing import List
13+
from typing import List, Optional
1414

1515

1616
@_ods_cext.register_operation(_Dialect, replace=True)
@@ -109,3 +109,109 @@ def create(value):
109109
value = support.get_value(value)
110110
type = support.get_self_or_inner(value.type).element_type
111111
return sv.ReadInOutOp(value)
112+
113+
114+
@_ods_cext.register_operation(_Dialect, replace=True)
115+
class SVVerbatimSourceOp(SVVerbatimSourceOp):
116+
117+
def __init__(self,
118+
sym_name,
119+
content,
120+
output_file,
121+
*,
122+
parameters=None,
123+
additional_files=None,
124+
verilog_name=None,
125+
loc=None,
126+
ip=None):
127+
attributes = {}
128+
attributes["sym_name"] = StringAttr.get(str(sym_name))
129+
attributes["content"] = StringAttr.get(str(content))
130+
attributes["output_file"] = output_file
131+
132+
if parameters is not None:
133+
attributes["parameters"] = parameters
134+
else:
135+
attributes["parameters"] = ArrayAttr.get([])
136+
137+
if additional_files is not None:
138+
attributes["additional_files"] = additional_files
139+
140+
if verilog_name is not None:
141+
attributes["verilogName"] = StringAttr.get(str(verilog_name))
142+
else:
143+
# Default to using the symbol name without file extension
144+
name = str(sym_name)
145+
if name.endswith('.v'):
146+
attributes["verilogName"] = StringAttr.get(name[:-2])
147+
else:
148+
attributes["verilogName"] = StringAttr.get(name)
149+
150+
OpView.__init__(
151+
self,
152+
self.build_generic(attributes=attributes,
153+
results=[],
154+
operands=[],
155+
successors=None,
156+
regions=0,
157+
loc=loc,
158+
ip=ip))
159+
160+
161+
@_ods_cext.register_operation(_Dialect, replace=True)
162+
class SVVerbatimModuleOp(SVVerbatimModuleOp):
163+
164+
def __init__(self,
165+
name,
166+
source,
167+
input_ports=[],
168+
output_ports=[],
169+
*,
170+
parameters=None,
171+
verilog_name=None,
172+
attributes={},
173+
loc=None,
174+
ip=None):
175+
attributes = dict(attributes)
176+
attributes["source"] = source
177+
178+
if parameters is not None:
179+
attributes["parameters"] = parameters
180+
else:
181+
attributes["parameters"] = ArrayAttr.get([])
182+
183+
if verilog_name is not None:
184+
attributes["verilogName"] = StringAttr.get(str(verilog_name))
185+
186+
hw.ModuleLike.init(self,
187+
name,
188+
input_ports,
189+
output_ports,
190+
parameters=parameters if parameters is not None else [],
191+
attributes=attributes,
192+
body_builder=None,
193+
loc=loc,
194+
ip=ip)
195+
196+
def instantiate(self, *args, **kwargs):
197+
return hw.ModuleLike.instantiate(self, *args, **kwargs)
198+
199+
@property
200+
def type(self):
201+
return hw.ModuleLike.type(self)
202+
203+
@property
204+
def name(self):
205+
return hw.ModuleLike.name(self)
206+
207+
@property
208+
def is_external(self):
209+
return True
210+
211+
@property
212+
def parameters(self) -> list[hw.ParamDeclAttr]:
213+
return hw.ModuleLike.parameters(self)
214+
215+
216+
VerbatimSourceOp = SVVerbatimSourceOp
217+
VerbatimModuleOp = SVVerbatimModuleOp

0 commit comments

Comments
 (0)