CIRCT  19.0.0git
GrandCentral.cpp
Go to the documentation of this file.
1 //===- GrandCentral.cpp - Ingest black box sources --------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //===----------------------------------------------------------------------===//
7 //
8 // Implement SiFive's Grand Central transform. Currently, this supports
9 // SystemVerilog Interface generation.
10 //
11 //===----------------------------------------------------------------------===//
12 
23 #include "circt/Dialect/HW/HWOps.h"
25 #include "circt/Dialect/SV/SVOps.h"
26 #include "circt/Support/Debug.h"
27 #include "mlir/IR/ImplicitLocOpBuilder.h"
28 #include "mlir/Pass/Pass.h"
29 #include "llvm/ADT/DepthFirstIterator.h"
30 #include "llvm/ADT/TypeSwitch.h"
31 #include "llvm/Support/Debug.h"
32 #include "llvm/Support/YAMLTraits.h"
33 #include <variant>
34 
35 #define DEBUG_TYPE "gct"
36 
37 namespace circt {
38 namespace firrtl {
39 #define GEN_PASS_DEF_GRANDCENTRAL
40 #include "circt/Dialect/FIRRTL/Passes.h.inc"
41 } // namespace firrtl
42 } // namespace circt
43 
44 using namespace circt;
45 using namespace firrtl;
46 
47 //===----------------------------------------------------------------------===//
48 // Collateral for generating a YAML representation of a SystemVerilog interface
49 //===----------------------------------------------------------------------===//
50 
51 namespace {
52 
53 // These are used to provide hard-errors if a user tries to use the YAML
54 // infrastructure improperly. We only implement conversion to YAML and not
55 // conversion from YAML. The LLVM YAML infrastructure doesn't provide the
56 // ability to differentiate this and we don't need it for the purposes of
57 // Grand Central.
58 [[maybe_unused]] static std::string noDefault(StringRef clazz) {
59  return ("default '" + clazz +
60  "' construction is an intentionally *NOT* implemented "
61  "YAML feature (you should never be using this)")
62  .str();
63 }
64 
65 [[maybe_unused]] static std::string deNorm(StringRef clazz) {
66  return ("conversion from YAML to a '" + clazz +
67  "' is intentionally *NOT* implemented (you should not be "
68  "converting from YAML to an interface)")
69  .str();
70 }
71 
72 // This namespace provides YAML-related collateral that is specific to Grand
73 // Central and should not be placed in the `llvm::yaml` namespace.
74 namespace yaml {
75 
76 /// Context information necessary for YAML generation.
77 struct Context {
78  /// A symbol table consisting of _only_ the interfaces constructed by the
79  /// Grand Central pass. This is not a symbol table because we do not have an
80  /// up-to-date symbol table that includes interfaces at the time the Grand
81  /// Central pass finishes. This structure is easier to build up and is only
82  /// the information we need.
83  DenseMap<Attribute, sv::InterfaceOp> &interfaceMap;
84 };
85 
86 /// A representation of an `sv::InterfaceSignalOp` that includes additional
87 /// description information.
88 ///
89 /// TODO: This could be removed if we add `firrtl.DocStringAnnotation` support
90 /// or if FIRRTL dialect included support for ops to specify "comment"
91 /// information.
92 struct DescribedSignal {
93  /// The comment associated with this signal.
94  StringAttr description;
95 
96  /// The signal.
97  sv::InterfaceSignalOp signal;
98 };
99 
100 /// This exist to work around the fact that no interface can be instantiated
101 /// inside another interface. This serves to represent an op like this for the
102 /// purposes of conversion to YAML.
103 ///
104 /// TODO: Fix this once we have a solution for #1464.
105 struct DescribedInstance {
106  StringAttr name;
107 
108  /// A comment associated with the interface instance.
109  StringAttr description;
110 
111  /// The dimensionality of the interface instantiation.
112  ArrayAttr dimensions;
113 
114  /// The symbol associated with the interface.
115  FlatSymbolRefAttr interface;
116 };
117 
118 } // namespace yaml
119 } // namespace
120 
121 // These macros tell the YAML infrastructure that these are types which can
122 // show up in vectors and provides implementations of how to serialize these.
123 // Each of these macros puts the resulting class into the `llvm::yaml` namespace
124 // (which is why these are outside the `llvm::yaml` namespace below).
125 LLVM_YAML_IS_SEQUENCE_VECTOR(::yaml::DescribedSignal)
126 LLVM_YAML_IS_SEQUENCE_VECTOR(::yaml::DescribedInstance)
127 LLVM_YAML_IS_SEQUENCE_VECTOR(sv::InterfaceOp)
128 
129 // This `llvm::yaml` namespace contains implementations of classes that enable
130 // conversion from an `sv::InterfaceOp` to a YAML representation of that
131 // interface using [LLVM's YAML I/O library](https://llvm.org/docs/YamlIO.html).
132 namespace llvm {
133 namespace yaml {
134 
135 using namespace ::yaml;
136 
137 /// Convert newlines and comments to remove the comments. This produces better
138 /// looking YAML output. E.g., this will convert the following:
139 ///
140 /// // foo
141 /// // bar
142 ///
143 /// Into the following:
144 ///
145 /// foo
146 /// bar
147 std::string static stripComment(StringRef str) {
148  std::string descriptionString;
149  llvm::raw_string_ostream stream(descriptionString);
150  SmallVector<StringRef> splits;
151  str.split(splits, "\n");
152  llvm::interleave(
153  splits,
154  [&](auto substr) {
155  substr.consume_front("//");
156  stream << substr.drop_while([](auto c) { return c == ' '; });
157  },
158  [&]() { stream << "\n"; });
159  return descriptionString;
160 }
161 
162 /// Conversion from a `DescribedSignal` to YAML. This is
163 /// implemented using YAML normalization to first convert this to an internal
164 /// `Field` structure which has a one-to-one mapping to the YAML representation.
165 template <>
166 struct MappingContextTraits<DescribedSignal, Context> {
167  /// A one-to-one representation with a YAML representation of a signal/field.
168  struct Field {
169  /// The name of the field.
170  StringRef name;
171 
172  /// An optional, textual description of what the field is.
173  std::optional<std::string> description;
174 
175  /// The dimensions of the field.
176  SmallVector<unsigned, 2> dimensions;
177 
178  /// The width of the underlying type.
179  unsigned width;
180 
181  /// Construct a `Field` from a `DescribedSignal` (an `sv::InterfaceSignalOp`
182  /// with an optional description).
183  Field(IO &io, DescribedSignal &op)
184  : name(op.signal.getSymNameAttr().getValue()) {
185 
186  // Convert the description from a `StringAttr` (which may be null) to an
187  // `optional<StringRef>`. This aligns exactly with the YAML
188  // representation.
189  if (op.description)
190  description = stripComment(op.description.getValue());
191 
192  // Unwrap the type of the field into an array of dimensions and a width.
193  // By example, this is going from the following hardware type:
194  //
195  // !hw.uarray<1xuarray<2xuarray<3xi8>>>
196  //
197  // To the following representation:
198  //
199  // dimensions: [ 3, 2, 1 ]
200  // width: 8
201  //
202  // Note that the above is equivalent to the following Verilog
203  // specification.
204  //
205  // wire [7:0] foo [2:0][1:0][0:0]
206  //
207  // Do this by repeatedly unwrapping unpacked array types until you get to
208  // the underlying type. The dimensions need to be reversed as this
209  // unwrapping happens in reverse order of the final representation.
210  auto tpe = op.signal.getType();
211  while (auto vector = dyn_cast<hw::UnpackedArrayType>(tpe)) {
212  dimensions.push_back(vector.getNumElements());
213  tpe = vector.getElementType();
214  }
215  dimensions = SmallVector<unsigned>(llvm::reverse(dimensions));
216 
217  // The final non-array type must be an integer. Leave this as an assert
218  // with a blind cast because we generated this type in this pass (and we
219  // therefore cannot fail this cast).
220  assert(isa<IntegerType>(tpe));
221  width = type_cast<IntegerType>(tpe).getWidth();
222  }
223 
224  /// A no-argument constructor is necessary to work with LLVM's YAML library.
225  Field(IO &io) { llvm_unreachable(noDefault("Field").c_str()); }
226 
227  /// This cannot be denormalized back to an interface op.
228  DescribedSignal denormalize(IO &) {
229  llvm_unreachable(deNorm("DescribedSignal").c_str());
230  }
231  };
232 
233  static void mapping(IO &io, DescribedSignal &op, Context &ctx) {
234  MappingNormalization<Field, DescribedSignal> keys(io, op);
235  io.mapRequired("name", keys->name);
236  io.mapOptional("description", keys->description);
237  io.mapRequired("dimensions", keys->dimensions);
238  io.mapRequired("width", keys->width);
239  }
240 };
241 
242 /// Conversion from a `DescribedInstance` to YAML. This is implemented using
243 /// YAML normalization to first convert the `DescribedInstance` to an internal
244 /// `Instance` struct which has a one-to-one representation with the final YAML
245 /// representation.
246 template <>
247 struct MappingContextTraits<DescribedInstance, Context> {
248  /// A YAML-serializable representation of an interface instantiation.
249  struct Instance {
250  /// The name of the interface.
251  StringRef name;
252 
253  /// An optional textual description of the interface.
254  std::optional<std::string> description = std::nullopt;
255 
256  /// An array describing the dimensionality of the interface.
257  SmallVector<int64_t, 2> dimensions;
258 
259  /// The underlying interface.
260  FlatSymbolRefAttr interface;
261 
262  Instance(IO &io, DescribedInstance &op)
263  : name(op.name.getValue()), interface(op.interface) {
264 
265  // Convert the description from a `StringAttr` (which may be null) to an
266  // `optional<StringRef>`. This aligns exactly with the YAML
267  // representation.
268  if (op.description)
269  description = stripComment(op.description.getValue());
270 
271  for (auto &d : op.dimensions) {
272  auto dimension = dyn_cast<IntegerAttr>(d);
273  dimensions.push_back(dimension.getInt());
274  }
275  }
276 
277  Instance(IO &io) { llvm_unreachable(noDefault("Instance").c_str()); }
278 
279  DescribedInstance denormalize(IO &) {
280  llvm_unreachable(deNorm("DescribedInstance").c_str());
281  }
282  };
283 
284  static void mapping(IO &io, DescribedInstance &op, Context &ctx) {
285  MappingNormalization<Instance, DescribedInstance> keys(io, op);
286  io.mapRequired("name", keys->name);
287  io.mapOptional("description", keys->description);
288  io.mapRequired("dimensions", keys->dimensions);
289  io.mapRequired("interface", ctx.interfaceMap[keys->interface], ctx);
290  }
291 };
292 
293 /// Conversion from an `sv::InterfaceOp` to YAML. This is implemented using
294 /// YAML normalization to first convert the interface to an internal `Interface`
295 /// which reformats the Grand Central-generated interface into the YAML format.
296 template <>
297 struct MappingContextTraits<sv::InterfaceOp, Context> {
298  /// A YAML-serializable representation of an interface. This consists of
299  /// fields (vector or ground types) and nested interfaces.
300  struct Interface {
301  /// The name of the interface.
302  StringRef name;
303 
304  /// All ground or vectors that make up the interface.
305  std::vector<DescribedSignal> fields;
306 
307  /// Instantiations of _other_ interfaces.
308  std::vector<DescribedInstance> instances;
309 
310  /// Construct an `Interface` from an `sv::InterfaceOp`. This is tuned to
311  /// "parse" the structure of an interface that the Grand Central pass
312  /// generates. The structure of `Field`s and `Instance`s is documented
313  /// below.
314  ///
315  /// A field will look like the following. The verbatim description is
316  /// optional:
317  ///
318  /// sv.verbatim "// <description>" {
319  /// firrtl.grandcentral.yaml.type = "description",
320  /// symbols = []}
321  /// sv.interface.signal @<name> : <type>
322  ///
323  /// An interface instanctiation will look like the following. The verbatim
324  /// description is optional.
325  ///
326  /// sv.verbatim "// <description>" {
327  /// firrtl.grandcentral.type = "description",
328  /// symbols = []}
329  /// sv.verbatim "<name> <symbol>();" {
330  /// firrtl.grandcentral.yaml.name = "<name>",
331  /// firrtl.grandcentral.yaml.dimensions = [<first dimension>, ...],
332  /// firrtl.grandcentral.yaml.symbol = @<symbol>,
333  /// firrtl.grandcentral.yaml.type = "instance",
334  /// symbols = []}
335  ///
336  Interface(IO &io, sv::InterfaceOp &op) : name(op.getName()) {
337  // A mutable store of the description. This occurs in the op _before_ the
338  // field or instance, so we need someplace to put it until we use it.
339  StringAttr description = {};
340 
341  for (auto &op : op.getBodyBlock()->getOperations()) {
342  TypeSwitch<Operation *>(&op)
343  // A verbatim op is either a description or an interface
344  // instantiation.
345  .Case<sv::VerbatimOp>([&](sv::VerbatimOp op) {
346  auto tpe = op->getAttrOfType<StringAttr>(
347  "firrtl.grandcentral.yaml.type");
348 
349  // This is a description. Update the mutable description and
350  // continue;
351  if (tpe.getValue() == "description") {
352  description = op.getFormatStringAttr();
353  return;
354  }
355 
356  // This is an unsupported construct. Just drop it.
357  if (tpe.getValue() == "unsupported") {
358  description = {};
359  return;
360  }
361 
362  // This is an instance of another interface. Add the symbol to
363  // the vector of instances.
364  auto name = op->getAttrOfType<StringAttr>(
365  "firrtl.grandcentral.yaml.name");
366  auto dimensions = op->getAttrOfType<ArrayAttr>(
367  "firrtl.grandcentral.yaml.dimensions");
368  auto symbol = op->getAttrOfType<FlatSymbolRefAttr>(
369  "firrtl.grandcentral.yaml.symbol");
370  instances.push_back(
371  DescribedInstance({name, description, dimensions, symbol}));
372  description = {};
373  })
374  // An interface signal op is a field.
375  .Case<sv::InterfaceSignalOp>([&](sv::InterfaceSignalOp op) {
376  fields.push_back(DescribedSignal({description, op}));
377  description = {};
378  });
379  }
380  }
381 
382  /// A no-argument constructor is necessary to work with LLVM's YAML library.
383  Interface(IO &io) { llvm_unreachable(noDefault("Interface").c_str()); }
384 
385  /// This cannot be denormalized back to an interface op.
386  sv::InterfaceOp denormalize(IO &) {
387  llvm_unreachable(deNorm("sv::InterfaceOp").c_str());
388  }
389  };
390 
391  static void mapping(IO &io, sv::InterfaceOp &op, Context &ctx) {
392  MappingNormalization<Interface, sv::InterfaceOp> keys(io, op);
393  io.mapRequired("name", keys->name);
394  io.mapRequired("fields", keys->fields, ctx);
395  io.mapRequired("instances", keys->instances, ctx);
396  }
397 };
398 
399 } // namespace yaml
400 } // namespace llvm
401 
402 //===----------------------------------------------------------------------===//
403 // Pass Implementation
404 //===----------------------------------------------------------------------===//
405 
406 namespace {
407 
408 /// A helper to build verbatim strings with symbol placeholders. Provides a
409 /// mechanism to snapshot the current string and symbols and restore back to
410 /// this state after modifications. These snapshots are particularly useful when
411 /// the string is assembled through hierarchical traversal of some sort, which
412 /// populates the string with a prefix common to all children of a hierarchy
413 /// (like the interface field traversal in the `GrandCentralPass`).
414 ///
415 /// The intended use is as follows:
416 ///
417 /// void baz(VerbatimBuilder &v) {
418 /// foo(v.snapshot().append("bar"));
419 /// }
420 ///
421 /// The function `baz` takes a snapshot of the current verbatim text `v`, adds
422 /// "bar" to it and calls `foo` with that appended verbatim text. After the call
423 /// to `foo` returns, any changes made by `foo` as well as the "bar" are dropped
424 /// from the verbatim text `v`, as the temporary snapshot goes out of scope.
425 struct VerbatimBuilder {
426  struct Base {
427  SmallString<128> string;
428  SmallVector<Attribute> symbols;
429  VerbatimBuilder builder() { return VerbatimBuilder(*this); }
430  operator VerbatimBuilder() { return builder(); }
431  };
432 
433  /// Constructing a builder will snapshot the `Base` which holds the actual
434  /// string and symbols.
435  VerbatimBuilder(Base &base)
436  : base(base), stringBaseSize(base.string.size()),
437  symbolsBaseSize(base.symbols.size()) {}
438 
439  /// Destroying a builder will reset the `Base` to the original string and
440  /// symbols.
441  ~VerbatimBuilder() {
442  base.string.resize(stringBaseSize);
443  base.symbols.resize(symbolsBaseSize);
444  }
445 
446  // Disallow copying.
447  VerbatimBuilder(const VerbatimBuilder &) = delete;
448  VerbatimBuilder &operator=(const VerbatimBuilder &) = delete;
449 
450  /// Take a snapshot of the current string and symbols. This returns a new
451  /// `VerbatimBuilder` that will reset to the current state of the string once
452  /// destroyed.
453  VerbatimBuilder snapshot() { return VerbatimBuilder(base); }
454 
455  /// Get the current string.
456  StringRef getString() const { return base.string; }
457  /// Get the current symbols;
458  ArrayRef<Attribute> getSymbols() const { return base.symbols; }
459 
460  /// Append to the string.
461  VerbatimBuilder &append(char c) {
462  base.string.push_back(c);
463  return *this;
464  }
465 
466  /// Append to the string.
467  VerbatimBuilder &append(const Twine &twine) {
468  twine.toVector(base.string);
469  return *this;
470  }
471 
472  /// Append a placeholder and symbol to the string.
473  VerbatimBuilder &append(Attribute symbol) {
474  unsigned id = base.symbols.size();
475  base.symbols.push_back(symbol);
476  append("{{" + Twine(id) + "}}");
477  return *this;
478  }
479 
480  VerbatimBuilder &operator+=(char c) { return append(c); }
481  VerbatimBuilder &operator+=(const Twine &twine) { return append(twine); }
482  VerbatimBuilder &operator+=(Attribute symbol) { return append(symbol); }
483 
484 private:
485  Base &base;
486  size_t stringBaseSize;
487  size_t symbolsBaseSize;
488 };
489 
490 /// A wrapper around a string that is used to encode a type which cannot be
491 /// represented by an mlir::Type for some reason. This is currently used to
492 /// represent either an interface, a n-dimensional vector of interfaces, or a
493 /// tombstone for an actually unsupported type (e.g., an AugmentedBooleanType).
494 struct VerbatimType {
495  /// The textual representation of the type.
496  std::string str;
497 
498  /// True if this is a type which must be "instantiated" and requires a
499  /// trailing "()".
500  bool instantiation;
501 
502  /// A vector storing the width of each dimension of the type.
503  SmallVector<int32_t, 4> dimensions = {};
504 
505  /// Serialize this type to a string.
506  std::string toStr(StringRef name) {
507  SmallString<64> stringType(str);
508  stringType.append(" ");
509  stringType.append(name);
510  for (auto d : llvm::reverse(dimensions)) {
511  stringType.append("[");
512  stringType.append(Twine(d).str());
513  stringType.append("]");
514  }
515  if (instantiation)
516  stringType.append("()");
517  stringType.append(";");
518  return std::string(stringType);
519  }
520 };
521 
522 /// A sum type representing either a type encoded as a string (VerbatimType)
523 /// or an actual mlir::Type.
524 typedef std::variant<VerbatimType, Type> TypeSum;
525 
526 /// Stores the information content of an ExtractGrandCentralAnnotation.
527 struct ExtractionInfo {
528  /// The directory where Grand Central generated collateral (modules,
529  /// interfaces, etc.) will be written.
530  StringAttr directory = {};
531 
532  /// The name of the file where any binds will be written. This will be placed
533  /// in the same output area as normal compilation output, e.g., output
534  /// Verilog. This has no relation to the `directory` member.
535  StringAttr bindFilename = {};
536 };
537 
538 /// Stores information about the companion module of a GrandCentral view.
539 struct CompanionInfo {
540  StringRef name;
541 
542  FModuleOp companion;
543  bool isNonlocal;
544 };
545 
546 /// Stores a reference to a ground type and an optional NLA associated with
547 /// that field.
548 struct FieldAndNLA {
549  FieldRef field;
550  FlatSymbolRefAttr nlaSym;
551 };
552 
553 /// Stores the arguments required to construct the verbatim xmr assignment.
554 struct VerbatimXMRbuilder {
555  Value val;
556  StringAttr str;
557  ArrayAttr syms;
558  FModuleOp companionMod;
559  VerbatimXMRbuilder(Value val, StringAttr str, ArrayAttr syms,
560  FModuleOp companionMod)
561  : val(val), str(str), syms(syms), companionMod(companionMod) {}
562 };
563 
564 /// Stores the arguments required to construct the InterfaceOps and
565 /// InterfaceSignalOps.
566 struct InterfaceElemsBuilder {
567  StringAttr iFaceName;
568  IntegerAttr id;
569  struct Properties {
570  StringAttr description;
571  StringAttr elemName;
572  TypeSum elemType;
573  Properties(StringAttr des, StringAttr name, TypeSum &elemType)
574  : description(des), elemName(name), elemType(elemType) {}
575  };
576  SmallVector<Properties> elementsList;
577  InterfaceElemsBuilder(StringAttr iFaceName, IntegerAttr id)
578  : iFaceName(iFaceName), id(id) {}
579 };
580 
581 /// Generate SystemVerilog interfaces from Grand Central annotations. This pass
582 /// roughly works in the following three phases:
583 ///
584 /// 1. Extraction information is determined.
585 ///
586 /// 2. The circuit is walked to find all scattered annotations related to Grand
587 /// Central interfaces. These are: (a) the companion module and (b) all
588 /// leaves that are to be connected to the interface.
589 ///
590 /// 3. The circuit-level Grand Central annotation is walked to both generate and
591 /// instantiate interfaces and to generate the "mappings" file that produces
592 /// cross-module references (XMRs) to drive the interface.
593 struct GrandCentralPass
594  : public circt::firrtl::impl::GrandCentralBase<GrandCentralPass> {
595  using GrandCentralBase::companionMode;
596 
597  void runOnOperation() override;
598 
599 private:
600  /// Optionally build an AugmentedType from an attribute. Return none if the
601  /// attribute is not a dictionary or if it does not match any of the known
602  /// templates for AugmentedTypes.
603  std::optional<Attribute> fromAttr(Attribute attr);
604 
605  /// Mapping of ID to leaf ground type and an optional non-local annotation
606  /// associated with that ID.
607  DenseMap<Attribute, FieldAndNLA> leafMap;
608 
609  /// Mapping of ID to companion module.
610  DenseMap<Attribute, CompanionInfo> companionIDMap;
611 
612  /// An optional prefix applied to all interfaces in the design. This is set
613  /// based on a PrefixInterfacesAnnotation.
614  StringRef interfacePrefix;
615 
616  NLATable *nlaTable;
617 
618  /// The design-under-test (DUT) as determined by the presence of a
619  /// "sifive.enterprise.firrtl.MarkDUTAnnotation". This will be null if no DUT
620  /// was found.
621  FModuleOp dut;
622 
623  /// An optional directory for testbench-related files. This is null if no
624  /// "TestBenchDirAnnotation" is found.
625  StringAttr testbenchDir;
626 
627  /// Return a string containing the name of an interface. Apply correct
628  /// prefixing from the interfacePrefix and module-level prefix parameter.
629  std::string getInterfaceName(StringAttr prefix,
630  AugmentedBundleTypeAttr bundleType) {
631 
632  if (prefix)
633  return (prefix.getValue() + interfacePrefix +
634  bundleType.getDefName().getValue())
635  .str();
636  return (interfacePrefix + bundleType.getDefName().getValue()).str();
637  }
638 
639  /// Recursively examine an AugmentedType to populate the "mappings" file
640  /// (generate XMRs) for this interface. This does not build new interfaces.
641  bool traverseField(Attribute field, IntegerAttr id, VerbatimBuilder &path,
642  SmallVector<VerbatimXMRbuilder> &xmrElems,
643  SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
644 
645  /// Recursively examine an AugmentedType to both build new interfaces and
646  /// populate a "mappings" file (generate XMRs) using `traverseField`. Return
647  /// the type of the field examined.
648  std::optional<TypeSum>
649  computeField(Attribute field, IntegerAttr id, StringAttr prefix,
650  VerbatimBuilder &path, SmallVector<VerbatimXMRbuilder> &xmrElems,
651  SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
652 
653  /// Recursively examine an AugmentedBundleType to both build new interfaces
654  /// and populate a "mappings" file (generate XMRs). Return none if the
655  /// interface is invalid.
656  std::optional<StringAttr>
657  traverseBundle(AugmentedBundleTypeAttr bundle, IntegerAttr id,
658  StringAttr prefix, VerbatimBuilder &path,
659  SmallVector<VerbatimXMRbuilder> &xmrElems,
660  SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
661 
662  /// Return the module associated with this value.
663  igraph::ModuleOpInterface getEnclosingModule(Value value,
664  FlatSymbolRefAttr sym = {});
665 
666  /// Information about how the circuit should be extracted. This will be
667  /// non-empty if an extraction annotation is found.
668  std::optional<ExtractionInfo> maybeExtractInfo = std::nullopt;
669 
670  /// A filename describing where to put a YAML representation of the
671  /// interfaces generated by this pass.
672  std::optional<StringAttr> maybeHierarchyFileYAML = std::nullopt;
673 
674  StringAttr getOutputDirectory() {
675  if (maybeExtractInfo)
676  return maybeExtractInfo->directory;
677  return {};
678  }
679 
680  /// Store of an instance paths analysis. This is constructed inside
681  /// `runOnOperation`, to work around the deleted copy constructor of
682  /// `InstancePathCache`'s internal `BumpPtrAllocator`.
683  ///
684  /// TODO: Investigate a way to not use a pointer here like how `getNamespace`
685  /// works below.
686  InstancePathCache *instancePaths = nullptr;
687 
688  /// The namespace associated with the circuit. This is lazily constructed
689  /// using `getNamespace`.
690  std::optional<CircuitNamespace> circuitNamespace;
691 
692  /// The module namespaces. These are lazily constructed by
693  /// `getModuleNamespace`.
694  DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
695 
696  /// Return a reference to the circuit namespace. This will lazily construct a
697  /// namespace if one does not exist.
698  CircuitNamespace &getNamespace() {
699  if (!circuitNamespace)
700  circuitNamespace = CircuitNamespace(getOperation());
701  return *circuitNamespace;
702  }
703 
704  /// Get the cached namespace for a module.
705  hw::InnerSymbolNamespace &getModuleNamespace(FModuleLike module) {
706  return moduleNamespaces.try_emplace(module, module).first->second;
707  }
708 
709  /// A symbol table associated with the circuit. This is lazily constructed by
710  /// `getSymbolTable`.
711  std::optional<SymbolTable *> symbolTable;
712 
713  /// Return a reference to a circuit-level symbol table. Lazily construct one
714  /// if such a symbol table does not already exist.
715  SymbolTable &getSymbolTable() {
716  if (!symbolTable)
717  symbolTable = &getAnalysis<SymbolTable>();
718  return **symbolTable;
719  }
720 
721  // Utility that acts like emitOpError, but does _not_ include a note. The
722  // note in emitOpError includes the entire op which means the **ENTIRE**
723  // FIRRTL circuit. This doesn't communicate anything useful to the user
724  // other than flooding their terminal.
725  InFlightDiagnostic emitCircuitError(StringRef message = {}) {
726  return emitError(getOperation().getLoc(), "'firrtl.circuit' op " + message);
727  }
728 
729  // Insert comment delimiters ("// ") after newlines in the description string.
730  // This is necessary to prevent introducing invalid verbatim Verilog.
731  //
732  // TODO: Add a comment op and lower the description to that.
733  // TODO: Tracking issue: https://github.com/llvm/circt/issues/1677
734  std::string cleanupDescription(StringRef description) {
735  StringRef head;
736  SmallString<64> out;
737  do {
738  std::tie(head, description) = description.split("\n");
739  out.append(head);
740  if (!description.empty())
741  out.append("\n// ");
742  } while (!description.empty());
743  return std::string(out);
744  }
745 
746  /// A store of the YAML representation of interfaces.
747  DenseMap<Attribute, sv::InterfaceOp> interfaceMap;
748 
749  /// Emit the hierarchy yaml file.
750  void emitHierarchyYamlFile(SmallVectorImpl<sv::InterfaceOp> &intfs);
751 };
752 
753 } // namespace
754 
755 //===----------------------------------------------------------------------===//
756 // Code related to handling Grand Central View annotations
757 //===----------------------------------------------------------------------===//
758 
759 /// Recursively walk a sifive.enterprise.grandcentral.AugmentedType to extract
760 /// any annotations it may contain. This is going to generate two types of
761 /// annotations:
762 /// 1) Annotations necessary to build interfaces and store them at "~"
763 /// 2) Scattered annotations for how components bind to interfaces
764 static std::optional<DictionaryAttr>
765 parseAugmentedType(ApplyState &state, DictionaryAttr augmentedType,
766  DictionaryAttr root, StringRef companion, StringAttr name,
767  StringAttr defName, std::optional<IntegerAttr> id,
768  std::optional<StringAttr> description, Twine clazz,
769  StringAttr companionAttr, Twine path = {}) {
770 
771  auto *context = state.circuit.getContext();
772  auto loc = state.circuit.getLoc();
773 
774  /// Optionally unpack a ReferenceTarget encoded as a DictionaryAttr. Return
775  /// either a pair containing the Target string (up to the reference) and an
776  /// array of components or none if the input is malformed. The input
777  /// DictionaryAttr encoding is a JSON object of a serialized ReferenceTarget
778  /// Scala class. By example, this is converting:
779  /// ~Foo|Foo>a.b[0]
780  /// To:
781  /// {"~Foo|Foo>a", {".b", "[0]"}}
782  /// The format of a ReferenceTarget object like:
783  /// circuit: String
784  /// module: String
785  /// path: Seq[(Instance, OfModule)]
786  /// ref: String
787  /// component: Seq[TargetToken]
788  auto refToTarget =
789  [&](DictionaryAttr refTarget) -> std::optional<std::string> {
790  auto circuitAttr =
791  tryGetAs<StringAttr>(refTarget, refTarget, "circuit", loc, clazz, path);
792  auto moduleAttr =
793  tryGetAs<StringAttr>(refTarget, refTarget, "module", loc, clazz, path);
794  auto pathAttr =
795  tryGetAs<ArrayAttr>(refTarget, refTarget, "path", loc, clazz, path);
796  auto componentAttr = tryGetAs<ArrayAttr>(refTarget, refTarget, "component",
797  loc, clazz, path);
798  if (!circuitAttr || !moduleAttr || !pathAttr || !componentAttr)
799  return {};
800 
801  // Parse non-local annotations.
802  SmallString<32> strpath;
803  for (auto p : pathAttr) {
804  auto dict = dyn_cast_or_null<DictionaryAttr>(p);
805  if (!dict) {
806  mlir::emitError(loc, "annotation '" + clazz +
807  " has invalid type (expected DictionaryAttr)");
808  return {};
809  }
810  auto instHolder =
811  tryGetAs<DictionaryAttr>(dict, dict, "_1", loc, clazz, path);
812  auto modHolder =
813  tryGetAs<DictionaryAttr>(dict, dict, "_2", loc, clazz, path);
814  if (!instHolder || !modHolder) {
815  mlir::emitError(loc, "annotation '" + clazz +
816  " has invalid type (expected DictionaryAttr)");
817  return {};
818  }
819  auto inst = tryGetAs<StringAttr>(instHolder, instHolder, "value", loc,
820  clazz, path);
821  auto mod =
822  tryGetAs<StringAttr>(modHolder, modHolder, "value", loc, clazz, path);
823  if (!inst || !mod) {
824  mlir::emitError(loc, "annotation '" + clazz +
825  " has invalid type (expected DictionaryAttr)");
826  return {};
827  }
828  strpath += "/" + inst.getValue().str() + ":" + mod.getValue().str();
829  }
830 
831  SmallVector<Attribute> componentAttrs;
832  SmallString<32> componentStr;
833  for (size_t i = 0, e = componentAttr.size(); i != e; ++i) {
834  auto cPath = (path + ".component[" + Twine(i) + "]").str();
835  auto component = componentAttr[i];
836  auto dict = dyn_cast_or_null<DictionaryAttr>(component);
837  if (!dict) {
838  mlir::emitError(loc, "annotation '" + clazz + "' with path '" + cPath +
839  " has invalid type (expected DictionaryAttr)");
840  return {};
841  }
842  auto classAttr =
843  tryGetAs<StringAttr>(dict, refTarget, "class", loc, clazz, cPath);
844  if (!classAttr)
845  return {};
846 
847  auto value = dict.get("value");
848 
849  // A subfield like "bar" in "~Foo|Foo>foo.bar".
850  if (auto field = dyn_cast<StringAttr>(value)) {
851  assert(classAttr.getValue() == "firrtl.annotations.TargetToken$Field" &&
852  "A StringAttr target token must be found with a subfield target "
853  "token.");
854  componentStr.append((Twine(".") + field.getValue()).str());
855  continue;
856  }
857 
858  // A subindex like "42" in "~Foo|Foo>foo[42]".
859  if (auto index = dyn_cast<IntegerAttr>(value)) {
860  assert(classAttr.getValue() == "firrtl.annotations.TargetToken$Index" &&
861  "An IntegerAttr target token must be found with a subindex "
862  "target token.");
863  componentStr.append(
864  (Twine("[") + Twine(index.getValue().getZExtValue()) + "]").str());
865  continue;
866  }
867 
868  mlir::emitError(loc,
869  "Annotation '" + clazz + "' with path '" + cPath +
870  ".value has unexpected type (should be StringAttr "
871  "for subfield or IntegerAttr for subindex).")
872  .attachNote()
873  << "The value received was: " << value << "\n";
874  return {};
875  }
876 
877  auto refAttr =
878  tryGetAs<StringAttr>(refTarget, refTarget, "ref", loc, clazz, path);
879 
880  return (Twine("~" + circuitAttr.getValue() + "|" + moduleAttr.getValue() +
881  strpath + ">" + refAttr.getValue()) +
882  componentStr)
883  .str();
884  };
885 
886  auto classAttr =
887  tryGetAs<StringAttr>(augmentedType, root, "class", loc, clazz, path);
888  if (!classAttr)
889  return std::nullopt;
890  StringRef classBase = classAttr.getValue();
891  if (!classBase.consume_front("sifive.enterprise.grandcentral.Augmented")) {
892  mlir::emitError(loc,
893  "the 'class' was expected to start with "
894  "'sifive.enterprise.grandCentral.Augmented*', but was '" +
895  classAttr.getValue() + "' (Did you misspell it?)")
896  .attachNote()
897  << "see annotation: " << augmentedType;
898  return std::nullopt;
899  }
900 
901  // An AugmentedBundleType looks like:
902  // "defName": String
903  // "elements": Seq[AugmentedField]
904  if (classBase == "BundleType") {
905  defName =
906  tryGetAs<StringAttr>(augmentedType, root, "defName", loc, clazz, path);
907  if (!defName)
908  return std::nullopt;
909 
910  // Each element is an AugmentedField with members:
911  // "name": String
912  // "description": Option[String]
913  // "tpe": AugmentedType
914  SmallVector<Attribute> elements;
915  auto elementsAttr =
916  tryGetAs<ArrayAttr>(augmentedType, root, "elements", loc, clazz, path);
917  if (!elementsAttr)
918  return std::nullopt;
919  for (size_t i = 0, e = elementsAttr.size(); i != e; ++i) {
920  auto field = dyn_cast_or_null<DictionaryAttr>(elementsAttr[i]);
921  if (!field) {
922  mlir::emitError(
923  loc,
924  "Annotation '" + Twine(clazz) + "' with path '.elements[" +
925  Twine(i) +
926  "]' contained an unexpected type (expected a DictionaryAttr).")
927  .attachNote()
928  << "The received element was: " << elementsAttr[i] << "\n";
929  return std::nullopt;
930  }
931  auto ePath = (path + ".elements[" + Twine(i) + "]").str();
932  auto name = tryGetAs<StringAttr>(field, root, "name", loc, clazz, ePath);
933  auto tpe =
934  tryGetAs<DictionaryAttr>(field, root, "tpe", loc, clazz, ePath);
935  std::optional<StringAttr> description;
936  if (auto maybeDescription = field.get("description"))
937  description = cast<StringAttr>(maybeDescription);
938  auto eltAttr = parseAugmentedType(
939  state, tpe, root, companion, name, defName, std::nullopt, description,
940  clazz, companionAttr, path + "_" + name.getValue());
941  if (!name || !tpe || !eltAttr)
942  return std::nullopt;
943 
944  // Collect information necessary to build a module with this view later.
945  // This includes the optional description and name.
946  NamedAttrList attrs;
947  if (auto maybeDescription = field.get("description"))
948  attrs.append("description", cast<StringAttr>(maybeDescription));
949  attrs.append("name", name);
950  attrs.append("tpe", tpe.getAs<StringAttr>("class"));
951  elements.push_back(*eltAttr);
952  }
953  // Add an annotation that stores information necessary to construct the
954  // module for the view. This needs the name of the module (defName) and the
955  // names of the components inside it.
956  NamedAttrList attrs;
957  attrs.append("class", classAttr);
958  attrs.append("defName", defName);
959  if (description)
960  attrs.append("description", *description);
961  attrs.append("elements", ArrayAttr::get(context, elements));
962  if (id)
963  attrs.append("id", *id);
964  attrs.append("name", name);
965  return DictionaryAttr::getWithSorted(context, attrs);
966  }
967 
968  // An AugmentedGroundType looks like:
969  // "ref": ReferenceTarget
970  // "tpe": GroundType
971  // The ReferenceTarget is not serialized to a string. The GroundType will
972  // either be an actual FIRRTL ground type or a GrandCentral uninferred type.
973  // This can be ignored for us.
974  if (classBase == "GroundType") {
975  auto maybeTarget = refToTarget(augmentedType.getAs<DictionaryAttr>("ref"));
976  if (!maybeTarget) {
977  mlir::emitError(loc, "Failed to parse ReferenceTarget").attachNote()
978  << "See the full Annotation here: " << root;
979  return std::nullopt;
980  }
981 
982  auto id = state.newID();
983 
984  auto target = *maybeTarget;
985 
986  NamedAttrList elementIface, elementScattered;
987 
988  // Populate the annotation for the interface element.
989  elementIface.append("class", classAttr);
990  if (description)
991  elementIface.append("description", *description);
992  elementIface.append("id", id);
993  elementIface.append("name", name);
994  // Populate an annotation that will be scattered onto the element.
995  elementScattered.append("class", classAttr);
996  elementScattered.append("id", id);
997  // If there are sub-targets, then add these.
998  auto targetAttr = StringAttr::get(context, target);
999  auto xmrSrcTarget = resolvePath(targetAttr.getValue(), state.circuit,
1000  state.symTbl, state.targetCaches);
1001  if (!xmrSrcTarget) {
1002  mlir::emitError(loc, "Failed to resolve target ") << targetAttr;
1003  return std::nullopt;
1004  }
1005 
1006  // Determine the source for this Wiring Problem. The source is the value
1007  // that will be eventually by read from, via cross-module reference, to
1008  // drive this element of the SystemVerilog Interface.
1009  auto sourceRef = xmrSrcTarget->ref;
1010  ImplicitLocOpBuilder builder(sourceRef.getOp()->getLoc(), context);
1011  std::optional<Value> source =
1012  TypeSwitch<Operation *, std::optional<Value>>(sourceRef.getOp())
1013  // The target is an external module port. The source is the
1014  // instance port of this singly-instantiated external module.
1015  .Case<FExtModuleOp>([&](FExtModuleOp extMod)
1016  -> std::optional<Value> {
1017  auto portNo = sourceRef.getImpl().getPortNo();
1018  if (xmrSrcTarget->instances.empty()) {
1019  auto paths = state.instancePathCache.getAbsolutePaths(extMod);
1020  if (paths.size() > 1) {
1021  extMod.emitError(
1022  "cannot resolve a unique instance path from the "
1023  "external module '")
1024  << targetAttr << "'";
1025  return std::nullopt;
1026  }
1027  auto *it = xmrSrcTarget->instances.begin();
1028  for (auto inst : paths.back()) {
1029  xmrSrcTarget->instances.insert(it, cast<InstanceOp>(inst));
1030  ++it;
1031  }
1032  }
1033  auto lastInst = xmrSrcTarget->instances.pop_back_val();
1034  builder.setInsertionPointAfter(lastInst);
1035  return getValueByFieldID(builder, lastInst.getResult(portNo),
1036  xmrSrcTarget->fieldIdx);
1037  })
1038  // The target is a module port. The source is the port _inside_
1039  // that module.
1040  .Case<FModuleOp>([&](FModuleOp module) -> std::optional<Value> {
1041  builder.setInsertionPointToEnd(module.getBodyBlock());
1042  auto portNum = sourceRef.getImpl().getPortNo();
1043  return getValueByFieldID(builder, module.getArgument(portNum),
1044  xmrSrcTarget->fieldIdx);
1045  })
1046  // The target is something else.
1047  .Default([&](Operation *op) -> std::optional<Value> {
1048  auto module = cast<FModuleOp>(sourceRef.getModule());
1049  builder.setInsertionPointToEnd(module.getBodyBlock());
1050  auto is = dyn_cast<hw::InnerSymbolOpInterface>(op);
1051  // Resolve InnerSymbol references to their target result.
1052  if (is && is.getTargetResult())
1053  return getValueByFieldID(builder, is.getTargetResult(),
1054  xmrSrcTarget->fieldIdx);
1055  if (sourceRef.getOp()->getNumResults() != 1) {
1056  op->emitOpError()
1057  << "cannot be used as a target of the Grand Central View \""
1058  << defName.getValue()
1059  << "\" because it does not have exactly one result";
1060  return std::nullopt;
1061  }
1062  return getValueByFieldID(builder, sourceRef.getOp()->getResult(0),
1063  xmrSrcTarget->fieldIdx);
1064  });
1065 
1066  // Exit if there was an error in the source.
1067  if (!source)
1068  return std::nullopt;
1069 
1070  // Compute the sink of this Wiring Problem. The final sink will eventually
1071  // be a SystemVerilog Interface. However, this cannot exist until the
1072  // GrandCentral pass runs. Create an undriven WireOp and use that as the
1073  // sink. The WireOp will be driven later when the Wiring Problem is
1074  // resolved. Apply the scattered element annotation to this directly to save
1075  // having to reprocess this in LowerAnnotations.
1076  auto companionMod =
1077  cast<FModuleOp>(resolvePath(companionAttr.getValue(), state.circuit,
1078  state.symTbl, state.targetCaches)
1079  ->ref.getOp());
1080  builder.setInsertionPointToEnd(companionMod.getBodyBlock());
1081  // Sink type must be passive. It's required to be converted to a NodeOp by
1082  // the wiring problem solving, and later checked to be a Node.
1083  // This also ensures passive sink so works equally well w/ or w/o probes.
1084  auto sinkType = source->getType();
1085  if (auto baseSinkType = type_dyn_cast<FIRRTLBaseType>(sinkType))
1086  sinkType = baseSinkType.getPassiveType();
1087  auto sink = builder.create<WireOp>(sinkType, name);
1088  state.targetCaches.insertOp(sink);
1089  AnnotationSet annotations(context);
1090  annotations.addAnnotations(
1091  {DictionaryAttr::getWithSorted(context, elementScattered)});
1092  annotations.applyToOperation(sink);
1093 
1094  // Append this new Wiring Problem to the ApplyState. The Wiring Problem
1095  // will be resolved to bore RefType ports before LowerAnnotations finishes.
1096  state.wiringProblems.push_back({*source, sink.getResult(),
1097  (path + "__bore").str(),
1099 
1100  return DictionaryAttr::getWithSorted(context, elementIface);
1101  }
1102 
1103  // An AugmentedVectorType looks like:
1104  // "elements": Seq[AugmentedType]
1105  if (classBase == "VectorType") {
1106  auto elementsAttr =
1107  tryGetAs<ArrayAttr>(augmentedType, root, "elements", loc, clazz, path);
1108  if (!elementsAttr)
1109  return std::nullopt;
1110  SmallVector<Attribute> elements;
1111  for (auto [i, elt] : llvm::enumerate(elementsAttr)) {
1112  auto eltAttr = parseAugmentedType(
1113  state, cast<DictionaryAttr>(elt), root, companion, name,
1114  StringAttr::get(context, ""), id, std::nullopt, clazz, companionAttr,
1115  path + "_" + Twine(i));
1116  if (!eltAttr)
1117  return std::nullopt;
1118  elements.push_back(*eltAttr);
1119  }
1120  NamedAttrList attrs;
1121  attrs.append("class", classAttr);
1122  if (description)
1123  attrs.append("description", *description);
1124  attrs.append("elements", ArrayAttr::get(context, elements));
1125  attrs.append("name", name);
1126  return DictionaryAttr::getWithSorted(context, attrs);
1127  }
1128 
1129  // Any of the following are known and expected, but are legacy AugmentedTypes
1130  // do not have a target:
1131  // - AugmentedStringType
1132  // - AugmentedBooleanType
1133  // - AugmentedIntegerType
1134  // - AugmentedDoubleType
1135  bool isIgnorable =
1136  llvm::StringSwitch<bool>(classBase)
1137  .Cases("StringType", "BooleanType", "IntegerType", "DoubleType", true)
1138  .Default(false);
1139  if (isIgnorable) {
1140  NamedAttrList attrs;
1141  attrs.append("class", classAttr);
1142  attrs.append("name", name);
1143  auto value =
1144  tryGetAs<Attribute>(augmentedType, root, "value", loc, clazz, path);
1145  if (!value)
1146  return std::nullopt;
1147  attrs.append("value", value);
1148  return DictionaryAttr::getWithSorted(context, attrs);
1149  }
1150 
1151  // Anything else is unexpected or a user error if they manually wrote
1152  // annotations. Print an error and error out.
1153  mlir::emitError(loc, "found unknown AugmentedType '" + classAttr.getValue() +
1154  "' (Did you misspell it?)")
1155  .attachNote()
1156  << "see annotation: " << augmentedType;
1157  return std::nullopt;
1158 }
1159 
1160 LogicalResult circt::firrtl::applyGCTView(const AnnoPathValue &target,
1161  DictionaryAttr anno,
1162  ApplyState &state) {
1163 
1164  auto id = state.newID();
1165  auto *context = state.circuit.getContext();
1166  auto loc = state.circuit.getLoc();
1167  NamedAttrList companionAttrs;
1168  companionAttrs.append("class", StringAttr::get(context, companionAnnoClass));
1169  companionAttrs.append("id", id);
1170  auto viewAttr =
1171  tryGetAs<DictionaryAttr>(anno, anno, "view", loc, viewAnnoClass);
1172  if (!viewAttr)
1173  return failure();
1174  auto name = tryGetAs<StringAttr>(anno, anno, "name", loc, viewAnnoClass);
1175  if (!name)
1176  return failure();
1177  companionAttrs.append("name", name);
1178  auto companionAttr =
1179  tryGetAs<StringAttr>(anno, anno, "companion", loc, viewAnnoClass);
1180  if (!companionAttr)
1181  return failure();
1182  companionAttrs.append("target", companionAttr);
1183  state.addToWorklistFn(DictionaryAttr::get(context, companionAttrs));
1184 
1185  auto prunedAttr =
1186  parseAugmentedType(state, viewAttr, anno, companionAttr.getValue(), name,
1187  {}, id, {}, viewAnnoClass, companionAttr, Twine(name));
1188  if (!prunedAttr)
1189  return failure();
1190 
1191  AnnotationSet annotations(state.circuit);
1192  annotations.addAnnotations({*prunedAttr});
1193  annotations.applyToOperation(state.circuit);
1194 
1195  return success();
1196 }
1197 
1198 //===----------------------------------------------------------------------===//
1199 // GrandCentralPass Implementation
1200 //===----------------------------------------------------------------------===//
1201 
1202 std::optional<Attribute> GrandCentralPass::fromAttr(Attribute attr) {
1203  auto dict = dyn_cast<DictionaryAttr>(attr);
1204  if (!dict) {
1205  emitCircuitError() << "attribute is not a dictionary: " << attr << "\n";
1206  return std::nullopt;
1207  }
1208 
1209  auto clazz = dict.getAs<StringAttr>("class");
1210  if (!clazz) {
1211  emitCircuitError() << "missing 'class' key in " << dict << "\n";
1212  return std::nullopt;
1213  }
1214 
1215  auto classBase = clazz.getValue();
1216  classBase.consume_front("sifive.enterprise.grandcentral.Augmented");
1217 
1218  if (classBase == "BundleType") {
1219  if (dict.getAs<StringAttr>("defName") && dict.getAs<ArrayAttr>("elements"))
1220  return AugmentedBundleTypeAttr::get(&getContext(), dict);
1221  emitCircuitError() << "has an invalid AugmentedBundleType that does not "
1222  "contain 'defName' and 'elements' fields: "
1223  << dict;
1224  } else if (classBase == "VectorType") {
1225  if (dict.getAs<StringAttr>("name") && dict.getAs<ArrayAttr>("elements"))
1226  return AugmentedVectorTypeAttr::get(&getContext(), dict);
1227  emitCircuitError() << "has an invalid AugmentedVectorType that does not "
1228  "contain 'name' and 'elements' fields: "
1229  << dict;
1230  } else if (classBase == "GroundType") {
1231  auto id = dict.getAs<IntegerAttr>("id");
1232  auto name = dict.getAs<StringAttr>("name");
1233  if (id && leafMap.count(id) && name)
1234  return AugmentedGroundTypeAttr::get(&getContext(), dict);
1235  if (!id || !name)
1236  emitCircuitError() << "has an invalid AugmentedGroundType that does not "
1237  "contain 'id' and 'name' fields: "
1238  << dict;
1239  if (id && !leafMap.count(id))
1240  emitCircuitError() << "has an AugmentedGroundType with 'id == "
1241  << id.getValue().getZExtValue()
1242  << "' that does not have a scattered leaf to connect "
1243  "to in the circuit "
1244  "(was the leaf deleted or constant prop'd away?)";
1245  } else if (classBase == "StringType") {
1246  if (auto name = dict.getAs<StringAttr>("name"))
1247  return AugmentedStringTypeAttr::get(&getContext(), dict);
1248  } else if (classBase == "BooleanType") {
1249  if (auto name = dict.getAs<StringAttr>("name"))
1250  return AugmentedBooleanTypeAttr::get(&getContext(), dict);
1251  } else if (classBase == "IntegerType") {
1252  if (auto name = dict.getAs<StringAttr>("name"))
1253  return AugmentedIntegerTypeAttr::get(&getContext(), dict);
1254  } else if (classBase == "DoubleType") {
1255  if (auto name = dict.getAs<StringAttr>("name"))
1256  return AugmentedDoubleTypeAttr::get(&getContext(), dict);
1257  } else if (classBase == "LiteralType") {
1258  if (auto name = dict.getAs<StringAttr>("name"))
1259  return AugmentedLiteralTypeAttr::get(&getContext(), dict);
1260  } else if (classBase == "DeletedType") {
1261  if (auto name = dict.getAs<StringAttr>("name"))
1262  return AugmentedDeletedTypeAttr::get(&getContext(), dict);
1263  } else {
1264  emitCircuitError() << "has an invalid AugmentedType";
1265  }
1266  return std::nullopt;
1267 }
1268 
1269 bool GrandCentralPass::traverseField(
1270  Attribute field, IntegerAttr id, VerbatimBuilder &path,
1271  SmallVector<VerbatimXMRbuilder> &xmrElems,
1272  SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1273  return TypeSwitch<Attribute, bool>(field)
1274  .Case<AugmentedGroundTypeAttr>([&](AugmentedGroundTypeAttr ground) {
1275  auto [fieldRef, sym] = leafMap.lookup(ground.getID());
1276  hw::HierPathOp nla;
1277  if (sym)
1278  nla = nlaTable->getNLA(sym.getAttr());
1279  Value leafValue = fieldRef.getValue();
1280  assert(leafValue && "leafValue not found");
1281 
1282  auto companionModule = companionIDMap.lookup(id).companion;
1283  igraph::ModuleOpInterface enclosing =
1284  getEnclosingModule(leafValue, sym);
1285 
1286  auto tpe = type_cast<FIRRTLBaseType>(leafValue.getType());
1287 
1288  // If the type is zero-width then do not emit an XMR.
1289  if (!tpe.getBitWidthOrSentinel())
1290  return true;
1291 
1292  // The leafValue is assumed to conform to a very specific pattern:
1293  //
1294  // 1) The leaf value is in the companion.
1295  // 2) The leaf value is a NodeOp
1296  //
1297  // Anything else means that there is an error or the IR is somehow using
1298  // "old-style" Annotations to encode a Grand Central View. This
1299  // _really_ should be impossible to hit given that LowerAnnotations must
1300  // generate code that conforms to the check here.
1301  auto *nodeOp = leafValue.getDefiningOp();
1302  if (companionModule != enclosing) {
1303  auto diag = companionModule->emitError()
1304  << "Grand Central View \""
1305  << companionIDMap.lookup(id).name
1306  << "\" is invalid because a leaf is not inside the "
1307  "companion module";
1308  diag.attachNote(leafValue.getLoc())
1309  << "the leaf value is declared here";
1310  if (nodeOp) {
1311  auto leafModule = nodeOp->getParentOfType<FModuleOp>();
1312  diag.attachNote(leafModule.getLoc())
1313  << "the leaf value is inside this module";
1314  }
1315  return false;
1316  }
1317 
1318  if (!isa<NodeOp>(nodeOp)) {
1319  emitError(leafValue.getLoc())
1320  << "Grand Central View \"" << companionIDMap.lookup(id).name
1321  << "\" has an invalid leaf value (this must be a node)";
1322  return false;
1323  }
1324 
1325  /// Increment all the indices inside `{{`, `}}` by one. This is to
1326  /// indicate that a value is added to the `substitutions` of the
1327  /// verbatim op, other than the symbols.
1328  auto getStrAndIncrementIds = [&](StringRef base) -> StringAttr {
1329  SmallString<128> replStr;
1330  StringRef begin = "{{";
1331  StringRef end = "}}";
1332  // The replacement string.
1333  size_t from = 0;
1334  while (from < base.size()) {
1335  // Search for the first `{{` and `}}`.
1336  size_t beginAt = base.find(begin, from);
1337  size_t endAt = base.find(end, from);
1338  // If not found, then done.
1339  if (beginAt == StringRef::npos || endAt == StringRef::npos ||
1340  (beginAt > endAt)) {
1341  replStr.append(base.substr(from));
1342  break;
1343  }
1344  // Copy the string as is, until the `{{`.
1345  replStr.append(base.substr(from, beginAt - from));
1346  // Advance `from` to the character after the `}}`.
1347  from = endAt + 2;
1348  auto idChar = base.substr(beginAt + 2, endAt - beginAt - 2);
1349  int idNum;
1350  bool failed = idChar.getAsInteger(10, idNum);
1351  (void)failed;
1352  assert(!failed && "failed to parse integer from verbatim string");
1353  // Now increment the id and append.
1354  replStr.append("{{");
1355  Twine(idNum + 1).toVector(replStr);
1356  replStr.append("}}");
1357  }
1358  return StringAttr::get(&getContext(), "assign " + replStr + ";");
1359  };
1360 
1361  // This is the new style of XMRs using RefTypes. The value substitution
1362  // index is set to -1, as it will be incremented when generating the
1363  // string.
1364  // Generate the path from the LCA to the module that contains the leaf.
1365  path += " = {{-1}}";
1367  // Assemble the verbatim op.
1368  xmrElems.emplace_back(
1369  nodeOp->getOperand(0), getStrAndIncrementIds(path.getString()),
1370  ArrayAttr::get(&getContext(), path.getSymbols()), companionModule);
1371  return true;
1372  })
1373  .Case<AugmentedVectorTypeAttr>([&](auto vector) {
1374  bool notFailed = true;
1375  auto elements = vector.getElements();
1376  for (size_t i = 0, e = elements.size(); i != e; ++i) {
1377  auto field = fromAttr(elements[i]);
1378  if (!field)
1379  return false;
1380  notFailed &= traverseField(
1381  *field, id, path.snapshot().append("[" + Twine(i) + "]"),
1382  xmrElems, interfaceBuilder);
1383  }
1384  return notFailed;
1385  })
1386  .Case<AugmentedBundleTypeAttr>([&](AugmentedBundleTypeAttr bundle) {
1387  bool anyFailed = true;
1388  for (auto element : bundle.getElements()) {
1389  auto field = fromAttr(element);
1390  if (!field)
1391  return false;
1392  auto name = cast<DictionaryAttr>(element).getAs<StringAttr>("name");
1393  if (!name)
1394  name = cast<DictionaryAttr>(element).getAs<StringAttr>("defName");
1395  anyFailed &= traverseField(
1396  *field, id, path.snapshot().append("." + name.getValue()),
1397  xmrElems, interfaceBuilder);
1398  }
1399 
1400  return anyFailed;
1401  })
1402  .Case<AugmentedStringTypeAttr>([&](auto a) { return false; })
1403  .Case<AugmentedBooleanTypeAttr>([&](auto a) { return false; })
1404  .Case<AugmentedIntegerTypeAttr>([&](auto a) { return false; })
1405  .Case<AugmentedDoubleTypeAttr>([&](auto a) { return false; })
1406  .Case<AugmentedLiteralTypeAttr>([&](auto a) { return false; })
1407  .Case<AugmentedDeletedTypeAttr>([&](auto a) { return false; })
1408  .Default([](auto a) { return true; });
1409 }
1410 
1411 std::optional<TypeSum> GrandCentralPass::computeField(
1412  Attribute field, IntegerAttr id, StringAttr prefix, VerbatimBuilder &path,
1413  SmallVector<VerbatimXMRbuilder> &xmrElems,
1414  SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1415 
1416  auto unsupported = [&](StringRef name, StringRef kind) {
1417  return VerbatimType({("// <unsupported " + kind + " type>").str(), false});
1418  };
1419 
1420  return TypeSwitch<Attribute, std::optional<TypeSum>>(field)
1421  .Case<AugmentedGroundTypeAttr>(
1422  [&](AugmentedGroundTypeAttr ground) -> std::optional<TypeSum> {
1423  // Traverse to generate mappings.
1424  if (!traverseField(field, id, path, xmrElems, interfaceBuilder))
1425  return std::nullopt;
1426  FieldRef fieldRef = leafMap.lookup(ground.getID()).field;
1427  auto value = fieldRef.getValue();
1428  auto fieldID = fieldRef.getFieldID();
1429  auto tpe = firrtl::type_cast<FIRRTLBaseType>(
1431  fieldID));
1432  if (!tpe.isGround()) {
1433  value.getDefiningOp()->emitOpError()
1434  << "cannot be added to interface with id '"
1435  << id.getValue().getZExtValue()
1436  << "' because it is not a ground type";
1437  return std::nullopt;
1438  }
1439  return TypeSum(IntegerType::get(getOperation().getContext(),
1440  tpe.getBitWidthOrSentinel()));
1441  })
1442  .Case<AugmentedVectorTypeAttr>(
1443  [&](AugmentedVectorTypeAttr vector) -> std::optional<TypeSum> {
1444  auto elements = vector.getElements();
1445  auto firstElement = fromAttr(elements[0]);
1446  auto elementType =
1447  computeField(*firstElement, id, prefix,
1448  path.snapshot().append("[" + Twine(0) + "]"),
1449  xmrElems, interfaceBuilder);
1450  if (!elementType)
1451  return std::nullopt;
1452 
1453  for (size_t i = 1, e = elements.size(); i != e; ++i) {
1454  auto subField = fromAttr(elements[i]);
1455  if (!subField)
1456  return std::nullopt;
1457  (void)traverseField(*subField, id,
1458  path.snapshot().append("[" + Twine(i) + "]"),
1459  xmrElems, interfaceBuilder);
1460  }
1461 
1462  if (auto *tpe = std::get_if<Type>(&*elementType))
1463  return TypeSum(
1464  hw::UnpackedArrayType::get(*tpe, elements.getValue().size()));
1465  auto str = std::get<VerbatimType>(*elementType);
1466  str.dimensions.push_back(elements.getValue().size());
1467  return TypeSum(str);
1468  })
1469  .Case<AugmentedBundleTypeAttr>(
1470  [&](AugmentedBundleTypeAttr bundle) -> TypeSum {
1471  auto ifaceName = traverseBundle(bundle, id, prefix, path, xmrElems,
1472  interfaceBuilder);
1473  assert(ifaceName && *ifaceName);
1474  return VerbatimType({ifaceName->str(), true});
1475  })
1476  .Case<AugmentedStringTypeAttr>([&](auto field) -> TypeSum {
1477  return unsupported(field.getName().getValue(), "string");
1478  })
1479  .Case<AugmentedBooleanTypeAttr>([&](auto field) -> TypeSum {
1480  return unsupported(field.getName().getValue(), "boolean");
1481  })
1482  .Case<AugmentedIntegerTypeAttr>([&](auto field) -> TypeSum {
1483  return unsupported(field.getName().getValue(), "integer");
1484  })
1485  .Case<AugmentedDoubleTypeAttr>([&](auto field) -> TypeSum {
1486  return unsupported(field.getName().getValue(), "double");
1487  })
1488  .Case<AugmentedLiteralTypeAttr>([&](auto field) -> TypeSum {
1489  return unsupported(field.getName().getValue(), "literal");
1490  })
1491  .Case<AugmentedDeletedTypeAttr>([&](auto field) -> TypeSum {
1492  return unsupported(field.getName().getValue(), "deleted");
1493  });
1494 }
1495 
1496 /// Traverse an Annotation that is an AugmentedBundleType. During traversal,
1497 /// construct any discovered SystemVerilog interfaces. If this is the root
1498 /// interface, instantiate that interface in the companion. Recurse into fields
1499 /// of the AugmentedBundleType to construct nested interfaces and generate
1500 /// stringy-typed SystemVerilog hierarchical references to drive the
1501 /// interface. Returns false on any failure and true on success.
1502 std::optional<StringAttr> GrandCentralPass::traverseBundle(
1503  AugmentedBundleTypeAttr bundle, IntegerAttr id, StringAttr prefix,
1504  VerbatimBuilder &path, SmallVector<VerbatimXMRbuilder> &xmrElems,
1505  SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1506 
1507  unsigned lastIndex = interfaceBuilder.size();
1508  auto iFaceName = StringAttr::get(
1509  &getContext(), getNamespace().newName(getInterfaceName(prefix, bundle)));
1510  interfaceBuilder.emplace_back(iFaceName, id);
1511 
1512  for (auto element : bundle.getElements()) {
1513  auto field = fromAttr(element);
1514  if (!field)
1515  return std::nullopt;
1516 
1517  auto name = cast<DictionaryAttr>(element).getAs<StringAttr>("name");
1518  // auto signalSym = hw::InnerRefAttr::get(iface.sym_nameAttr(), name);
1519  // TODO: The `append(name.getValue())` in the following should actually be
1520  // `append(signalSym)`, but this requires that `computeField` and the
1521  // functions it calls always return a type for which we can construct an
1522  // `InterfaceSignalOp`. Since nested interface instances are currently
1523  // busted (due to the interface being a symbol table), this doesn't work at
1524  // the moment. Passing a `name` works most of the time, but can be brittle
1525  // if the interface field requires renaming in the output (e.g. due to
1526  // naming conflicts).
1527  auto elementType = computeField(
1528  *field, id, prefix, path.snapshot().append(".").append(name.getValue()),
1529  xmrElems, interfaceBuilder);
1530  if (!elementType)
1531  return std::nullopt;
1532  StringAttr description =
1533  cast<DictionaryAttr>(element).getAs<StringAttr>("description");
1534  interfaceBuilder[lastIndex].elementsList.emplace_back(description, name,
1535  *elementType);
1536  }
1537  return iFaceName;
1538 }
1539 
1540 /// Return the module that is associated with this value. Use the cached/lazily
1541 /// constructed symbol table to make this fast.
1542 igraph::ModuleOpInterface
1543 GrandCentralPass::getEnclosingModule(Value value, FlatSymbolRefAttr sym) {
1544  if (auto blockArg = dyn_cast<BlockArgument>(value))
1545  return cast<igraph::ModuleOpInterface>(blockArg.getOwner()->getParentOp());
1546 
1547  auto *op = value.getDefiningOp();
1548  if (InstanceOp instance = dyn_cast<InstanceOp>(op))
1549  return getSymbolTable().lookup<igraph::ModuleOpInterface>(
1550  instance.getModuleNameAttr().getValue());
1551 
1552  return op->getParentOfType<igraph::ModuleOpInterface>();
1553 }
1554 
1555 /// This method contains the business logic of this pass.
1556 void GrandCentralPass::runOnOperation() {
1557  LLVM_DEBUG(debugPassHeader(this) << "\n");
1558 
1559  CircuitOp circuitOp = getOperation();
1560 
1561  // Look at the circuit annotations to do two things:
1562  //
1563  // 1. Determine extraction information (directory and filename).
1564  // 2. Populate a worklist of all annotations that encode interfaces.
1565  //
1566  // Remove annotations encoding interfaces, but leave extraction information as
1567  // this may be needed by later passes.
1568  SmallVector<Annotation> worklist;
1569  bool removalError = false;
1570  AnnotationSet::removeAnnotations(circuitOp, [&](Annotation anno) {
1571  if (anno.isClass(augmentedBundleTypeClass)) {
1572  // If we are in "Instantiate" companion mode, then we don't need to
1573  // create the interface, so we can skip adding it to the worklist. This
1574  // is a janky hack for situations where you want to synthesize assertion
1575  // logic included in the companion, but don't want to have a dead
1576  // interface hanging around (or have problems with tools understanding
1577  // interfaces).
1578  if (companionMode != CompanionMode::Instantiate)
1579  worklist.push_back(anno);
1580  ++numAnnosRemoved;
1581  return true;
1582  }
1583  if (anno.isClass(extractGrandCentralClass)) {
1584  if (maybeExtractInfo) {
1585  emitCircuitError("more than one 'ExtractGrandCentralAnnotation' was "
1586  "found, but exactly one must be provided");
1587  removalError = true;
1588  return false;
1589  }
1590 
1591  auto directory = anno.getMember<StringAttr>("directory");
1592  auto filename = anno.getMember<StringAttr>("filename");
1593  if (!directory || !filename) {
1594  emitCircuitError()
1595  << "contained an invalid 'ExtractGrandCentralAnnotation' that does "
1596  "not contain 'directory' and 'filename' fields: "
1597  << anno.getDict();
1598  removalError = true;
1599  return false;
1600  }
1601  if (directory.getValue().empty())
1602  directory = StringAttr::get(circuitOp.getContext(), ".");
1603 
1604  maybeExtractInfo = {directory, filename};
1605  // Do not delete this annotation. Extraction info may be needed later.
1606  return false;
1607  }
1609  if (maybeHierarchyFileYAML) {
1610  emitCircuitError("more than one 'GrandCentralHierarchyFileAnnotation' "
1611  "was found, but zero or one may be provided");
1612  removalError = true;
1613  return false;
1614  }
1615 
1616  auto filename = anno.getMember<StringAttr>("filename");
1617  if (!filename) {
1618  emitCircuitError()
1619  << "contained an invalid 'GrandCentralHierarchyFileAnnotation' "
1620  "that does not contain 'directory' and 'filename' fields: "
1621  << anno.getDict();
1622  removalError = true;
1623  return false;
1624  }
1625 
1626  maybeHierarchyFileYAML = filename;
1627  ++numAnnosRemoved;
1628  return true;
1629  }
1630  if (anno.isClass(prefixInterfacesAnnoClass)) {
1631  if (!interfacePrefix.empty()) {
1632  emitCircuitError("more than one 'PrefixInterfacesAnnotation' was "
1633  "found, but zero or one may be provided");
1634  removalError = true;
1635  return false;
1636  }
1637 
1638  auto prefix = anno.getMember<StringAttr>("prefix");
1639  if (!prefix) {
1640  emitCircuitError()
1641  << "contained an invalid 'PrefixInterfacesAnnotation' that does "
1642  "not contain a 'prefix' field: "
1643  << anno.getDict();
1644  removalError = true;
1645  return false;
1646  }
1647 
1648  interfacePrefix = prefix.getValue();
1649  ++numAnnosRemoved;
1650  return true;
1651  }
1652  if (anno.isClass(testBenchDirAnnoClass)) {
1653  testbenchDir = anno.getMember<StringAttr>("dirname");
1654  return false;
1655  }
1656  return false;
1657  });
1658 
1659  // Find the DUT if it exists. This needs to be known before the circuit is
1660  // walked.
1661  for (auto mod : circuitOp.getOps<FModuleOp>()) {
1662  if (failed(extractDUT(mod, dut)))
1663  removalError = true;
1664  }
1665 
1666  if (removalError)
1667  return signalPassFailure();
1668 
1669  LLVM_DEBUG({
1670  llvm::dbgs() << "Extraction Info:\n";
1671  if (maybeExtractInfo)
1672  llvm::dbgs() << " directory: " << maybeExtractInfo->directory << "\n"
1673  << " filename: " << maybeExtractInfo->bindFilename << "\n";
1674  else
1675  llvm::dbgs() << " <none>\n";
1676  llvm::dbgs() << "DUT: ";
1677  if (dut)
1678  llvm::dbgs() << dut.getModuleName() << "\n";
1679  else
1680  llvm::dbgs() << "<none>\n";
1681  llvm::dbgs()
1682  << "Prefix Info (from PrefixInterfacesAnnotation):\n"
1683  << " prefix: " << interfacePrefix << "\n"
1684  << "Hierarchy File Info (from GrandCentralHierarchyFileAnnotation):\n"
1685  << " filename: ";
1686  if (maybeHierarchyFileYAML)
1687  llvm::dbgs() << *maybeHierarchyFileYAML;
1688  else
1689  llvm::dbgs() << "<none>";
1690  llvm::dbgs() << "\n";
1691  });
1692 
1693  // Exit immediately if no annotations indicative of interfaces that need to be
1694  // built exist. However, still generate the YAML file if the annotation for
1695  // this was passed in because some flows expect this.
1696  if (worklist.empty()) {
1697  SmallVector<sv::InterfaceOp, 0> interfaceVec;
1698  emitHierarchyYamlFile(interfaceVec);
1699  return markAllAnalysesPreserved();
1700  }
1701 
1702  // Setup the builder to create ops _inside the FIRRTL circuit_. This is
1703  // necessary because interfaces and interface instances are created.
1704  // Instances link to their definitions via symbols and we don't want to
1705  // break this.
1706  auto builder = OpBuilder::atBlockEnd(circuitOp.getBodyBlock());
1707 
1708  // Maybe get an "id" from an Annotation. Generate error messages on the op if
1709  // no "id" exists.
1710  auto getID = [&](Operation *op,
1711  Annotation annotation) -> std::optional<IntegerAttr> {
1712  auto id = annotation.getMember<IntegerAttr>("id");
1713  if (!id) {
1714  op->emitOpError()
1715  << "contained a malformed "
1716  "'sifive.enterprise.grandcentral.AugmentedGroundType' annotation "
1717  "that did not contain an 'id' field";
1718  removalError = true;
1719  return std::nullopt;
1720  }
1721  return id;
1722  };
1723 
1724  /// TODO: Handle this differently to allow construction of an options
1725  auto instancePathCache = InstancePathCache(getAnalysis<InstanceGraph>());
1726  instancePaths = &instancePathCache;
1727 
1728  /// Contains the set of modules which are instantiated by the DUT, but not a
1729  /// companion, instantiated by a companion, or instantiated under a bind. If
1730  /// no DUT exists, treat the top module as if it were the DUT. This works by
1731  /// doing a depth-first walk of the instance graph, starting from the
1732  /// "effective" DUT and stopping the search at any modules which are known
1733  /// companions or any instances which are marked "lowerToBind".
1734  DenseSet<InstanceGraphNode *> dutModules;
1735  InstanceGraphNode *effectiveDUT;
1736  if (dut)
1737  effectiveDUT = instancePaths->instanceGraph.lookup(dut);
1738  else
1739  effectiveDUT = instancePaths->instanceGraph.getTopLevelNode();
1740  {
1741  SmallVector<InstanceGraphNode *> modules({effectiveDUT});
1742  while (!modules.empty()) {
1743  auto *m = modules.pop_back_val();
1744  for (InstanceRecord *a : *m) {
1745  auto *mod = a->getTarget();
1746  // Skip modules that we've visited, that are are under the companion
1747  // module, or are bound/under a layer block.
1748  if (auto block = a->getInstance()->getParentOfType<LayerBlockOp>()) {
1749  auto diag = a->getInstance().emitOpError()
1750  << "is instantiated under a '" << block.getOperationName()
1751  << "' op which is unexpected by GrandCentral (did you "
1752  "forget to run the LowerLayers pass?)";
1753  diag.attachNote(block.getLoc())
1754  << "the '" << block.getOperationName() << "' op is here";
1755  removalError = true;
1756  }
1757  auto instOp = dyn_cast<InstanceOp>(*a->getInstance());
1758  if (dutModules.contains(mod) ||
1759  AnnotationSet::hasAnnotation(mod->getModule(),
1760  companionAnnoClass) ||
1761  (instOp && instOp.getLowerToBind()))
1762  continue;
1763  modules.push_back(mod);
1764  dutModules.insert(mod);
1765  }
1766  }
1767  }
1768 
1769  // Maybe return the lone instance of a module. Generate errors on the op if
1770  // the module is not instantiated or is multiply instantiated.
1771  auto exactlyOneInstance = [&](FModuleOp op,
1772  StringRef msg) -> std::optional<InstanceOp> {
1773  auto *node = instancePaths->instanceGraph[op];
1774 
1775  switch (node->getNumUses()) {
1776  case 0:
1777  op->emitOpError() << "is marked as a GrandCentral '" << msg
1778  << "', but is never instantiated";
1779  return std::nullopt;
1780  case 1:
1781  return cast<InstanceOp>(*(*node->uses().begin())->getInstance());
1782  default:
1783  auto diag = op->emitOpError()
1784  << "is marked as a GrandCentral '" << msg
1785  << "', but it is instantiated more than once";
1786  for (auto *instance : node->uses())
1787  diag.attachNote(instance->getInstance()->getLoc())
1788  << "it is instantiated here";
1789  return std::nullopt;
1790  }
1791  };
1792 
1793  nlaTable = &getAnalysis<NLATable>();
1794 
1795  /// Walk the circuit and extract all information related to scattered Grand
1796  /// Central annotations. This is used to populate: (1) the companionIDMap and
1797  /// (2) the leafMap. Annotations are removed as they are discovered and if
1798  /// they are not malformed.
1799  DenseSet<Operation *> modulesToDelete;
1800  circuitOp.walk([&](Operation *op) {
1801  TypeSwitch<Operation *>(op)
1802  .Case<RegOp, RegResetOp, WireOp, NodeOp>([&](auto op) {
1803  AnnotationSet::removeAnnotations(op, [&](Annotation annotation) {
1804  if (!annotation.isClass(augmentedGroundTypeClass))
1805  return false;
1806  auto maybeID = getID(op, annotation);
1807  if (!maybeID)
1808  return false;
1809  auto sym =
1810  annotation.getMember<FlatSymbolRefAttr>("circt.nonlocal");
1811  leafMap[*maybeID] = {{op.getResult(), annotation.getFieldID()},
1812  sym};
1813  ++numAnnosRemoved;
1814  return true;
1815  });
1816  })
1817  // TODO: Figure out what to do with this.
1818  .Case<InstanceOp>([&](auto op) {
1819  AnnotationSet::removePortAnnotations(op, [&](unsigned i,
1820  Annotation annotation) {
1821  if (!annotation.isClass(augmentedGroundTypeClass))
1822  return false;
1823  op.emitOpError()
1824  << "is marked as an interface element, but this should be "
1825  "impossible due to how the Chisel Grand Central API works";
1826  removalError = true;
1827  return false;
1828  });
1829  })
1830  .Case<MemOp>([&](auto op) {
1831  AnnotationSet::removeAnnotations(op, [&](Annotation annotation) {
1832  if (!annotation.isClass(augmentedGroundTypeClass))
1833  return false;
1834  op.emitOpError()
1835  << "is marked as an interface element, but this does not make "
1836  "sense (is there a scattering bug or do you have a "
1837  "malformed hand-crafted MLIR circuit?)";
1838  removalError = true;
1839  return false;
1840  });
1842  op, [&](unsigned i, Annotation annotation) {
1843  if (!annotation.isClass(augmentedGroundTypeClass))
1844  return false;
1845  op.emitOpError()
1846  << "has port '" << i
1847  << "' marked as an interface element, but this does not "
1848  "make sense (is there a scattering bug or do you have a "
1849  "malformed hand-crafted MLIR circuit?)";
1850  removalError = true;
1851  return false;
1852  });
1853  })
1854  .Case<FModuleOp>([&](FModuleOp op) {
1855  // Handle annotations on the ports.
1856  AnnotationSet::removePortAnnotations(op, [&](unsigned i,
1857  Annotation annotation) {
1858  if (!annotation.isClass(augmentedGroundTypeClass))
1859  return false;
1860  auto maybeID = getID(op, annotation);
1861  if (!maybeID)
1862  return false;
1863  auto sym =
1864  annotation.getMember<FlatSymbolRefAttr>("circt.nonlocal");
1865  leafMap[*maybeID] = {{op.getArgument(i), annotation.getFieldID()},
1866  sym};
1867  ++numAnnosRemoved;
1868  return true;
1869  });
1870 
1871  // Handle annotations on the module.
1872  AnnotationSet::removeAnnotations(op, [&](Annotation annotation) {
1873  if (!annotation.getClass().starts_with(viewAnnoClass))
1874  return false;
1875  auto isNonlocal = annotation.getMember<FlatSymbolRefAttr>(
1876  "circt.nonlocal") != nullptr;
1877  auto name = annotation.getMember<StringAttr>("name");
1878  auto id = annotation.getMember<IntegerAttr>("id");
1879  if (!id) {
1880  op.emitOpError()
1881  << "has a malformed "
1882  "'sifive.enterprise.grandcentral.ViewAnnotation' that did "
1883  "not contain an 'id' field with an 'IntegerAttr' value";
1884  goto FModuleOp_error;
1885  }
1886  if (!name) {
1887  op.emitOpError()
1888  << "has a malformed "
1889  "'sifive.enterprise.grandcentral.ViewAnnotation' that did "
1890  "not contain a 'name' field with a 'StringAttr' value";
1891  goto FModuleOp_error;
1892  }
1893 
1894  // If this is a companion, then:
1895  // 1. Insert it into the companion map
1896  // 2. Create a new mapping module.
1897  // 3. Instantiate the mapping module in the companion.
1898  // 4. Check that the companion is instantiated exactly once.
1899  // 5. Set attributes on that lone instance so it will become a
1900  // bind if extraction information was provided. If a DUT is
1901  // known, then anything in the test harness will not be
1902  // extracted.
1903  if (annotation.getClass() == companionAnnoClass) {
1904  builder.setInsertionPointToEnd(circuitOp.getBodyBlock());
1905 
1906  companionIDMap[id] = {name.getValue(), op, isNonlocal};
1907 
1908  // Assert that the companion is instantiated once and only once.
1909  auto instance = exactlyOneInstance(op, "companion");
1910  if (!instance)
1911  goto FModuleOp_error;
1912 
1913  // Companions are only allowed to take inputs.
1914  for (auto [i, result] : llvm::enumerate(instance->getResults())) {
1915  if (instance->getPortDirection(i) == Direction::In)
1916  continue;
1917  // Do not allow any outputs in the drop mode.
1918  auto ty = result.getType();
1919  if (isa<RefType>(ty) && companionMode != CompanionMode::Drop)
1920  continue;
1921  op.emitOpError()
1922  << "companion instance cannot have output ports";
1923  goto FModuleOp_error;
1924  }
1925 
1926  // If no extraction info was provided, exit. Otherwise, setup the
1927  // lone instance of the companion to be lowered as a bind.
1928  if (!maybeExtractInfo) {
1929  ++numAnnosRemoved;
1930  return true;
1931  }
1932 
1933  // If the companion is instantiated above the DUT, then don't
1934  // extract it.
1935  if (dut && !instancePaths->instanceGraph.isAncestor(op, dut)) {
1936  ++numAnnosRemoved;
1937  return true;
1938  }
1939 
1940  // Look for any modules/extmodules _only_ instantiated by the
1941  // companion. If these have no output file attribute, then mark
1942  // them as being extracted into the Grand Central directory.
1943  InstanceGraphNode *companionNode =
1944  instancePaths->instanceGraph.lookup(op);
1945 
1946  LLVM_DEBUG({
1947  llvm::dbgs()
1948  << "Found companion module: "
1949  << companionNode->getModule().getModuleName() << "\n"
1950  << " submodules exclusively instantiated "
1951  "(including companion):\n";
1952  });
1953 
1954  if (companionMode == CompanionMode::Drop) {
1955  // Delete the instance if companions are disabled.
1956  OpBuilder builder(&getContext());
1957  for (auto port : instance->getResults()) {
1958  builder.setInsertionPointAfterValue(port);
1959  auto wire =
1960  builder.create<WireOp>(port.getLoc(), port.getType());
1961  port.replaceAllUsesWith(wire.getResult());
1962  }
1963  instance->erase();
1964  } else {
1965  // Lower the companion to a bind unless the user told us
1966  // explicitly not to.
1967  if (companionMode == CompanionMode::Bind)
1968  (*instance)->setAttr("lowerToBind", builder.getUnitAttr());
1969 
1970  (*instance)->setAttr(
1971  "output_file",
1972  hw::OutputFileAttr::getFromFilename(
1973  &getContext(),
1974  maybeExtractInfo->bindFilename.getValue(),
1975  /*excludeFromFileList=*/true));
1976  }
1977 
1978  for (auto &node : llvm::depth_first(companionNode)) {
1979  auto mod = node->getModule();
1980 
1981  // Check to see if we should change the output directory of a
1982  // module. Only update in the following conditions:
1983  // 1) The module is the companion.
1984  // 2) The module is NOT instantiated by the effective DUT or
1985  // is under a bind.
1986  auto *modNode = instancePaths->instanceGraph.lookup(mod);
1987  SmallVector<InstanceRecord *> instances(modNode->uses());
1988  if (modNode != companionNode && dutModules.count(modNode))
1989  continue;
1990 
1991  LLVM_DEBUG({
1992  llvm::dbgs()
1993  << " - module: " << mod.getModuleName() << "\n";
1994  });
1995 
1996  if (auto extmodule = dyn_cast<FExtModuleOp>(*mod)) {
1997  for (auto anno : AnnotationSet(extmodule)) {
1998  if (companionMode == CompanionMode::Drop) {
1999  modulesToDelete.insert(mod);
2000  break;
2001  }
2002  if (!anno.isClass(blackBoxInlineAnnoClass) &&
2004  continue;
2005  if (extmodule->hasAttr("output_file"))
2006  break;
2007  extmodule->setAttr(
2008  "output_file",
2009  hw::OutputFileAttr::getAsDirectory(
2010  &getContext(),
2011  maybeExtractInfo->directory.getValue()));
2012  break;
2013  }
2014  continue;
2015  }
2016 
2017  if (companionMode == CompanionMode::Drop) {
2018  modulesToDelete.insert(mod);
2019  } else {
2020  // Move this module under the Grand Central output directory
2021  // if no pre-existing output file information is present.
2022  if (!mod->hasAttr("output_file")) {
2023  mod->setAttr("output_file",
2024  hw::OutputFileAttr::getAsDirectory(
2025  &getContext(),
2026  maybeExtractInfo->directory.getValue(),
2027  /*excludeFromFileList=*/true,
2028  /*includeReplicatedOps=*/true));
2029  mod->setAttr("comment", builder.getStringAttr(
2030  "VCS coverage exclude_file"));
2031  }
2032  }
2033  }
2034 
2035  ++numAnnosRemoved;
2036  return true;
2037  }
2038 
2039  op.emitOpError()
2040  << "unknown annotation class: " << annotation.getDict();
2041 
2042  FModuleOp_error:
2043  removalError = true;
2044  return false;
2045  });
2046  });
2047  });
2048 
2049  if (removalError)
2050  return signalPassFailure();
2051 
2052  if (companionMode == CompanionMode::Drop) {
2053  for (auto *mod : modulesToDelete) {
2054  auto name = cast<FModuleLike>(mod).getModuleNameAttr();
2055 
2056  DenseSet<hw::HierPathOp> nlas;
2057  nlaTable->getNLAsInModule(name, nlas);
2058  nlaTable->removeNLAsfromModule(nlas, name);
2059  for (auto nla : nlas) {
2060  if (nla.root() == name)
2061  nla.erase();
2062  }
2063 
2064  mod->erase();
2065  }
2066 
2067  SmallVector<sv::InterfaceOp, 0> interfaceVec;
2068  emitHierarchyYamlFile(interfaceVec);
2069  return;
2070  }
2071 
2072  LLVM_DEBUG({
2073  // Print out the companion map and all leaf values that were discovered.
2074  // Sort these by their keys before printing to make this easier to read.
2075  SmallVector<IntegerAttr> ids;
2076  auto sort = [&ids]() {
2077  llvm::sort(ids, [](IntegerAttr a, IntegerAttr b) {
2078  return a.getValue().getZExtValue() < b.getValue().getZExtValue();
2079  });
2080  };
2081  for (auto tuple : companionIDMap)
2082  ids.push_back(cast<IntegerAttr>(tuple.first));
2083  sort();
2084  llvm::dbgs() << "companionIDMap:\n";
2085  for (auto id : ids) {
2086  auto value = companionIDMap.lookup(id);
2087  llvm::dbgs() << " - " << id.getValue() << ": "
2088  << value.companion.getName() << " -> " << value.name << "\n";
2089  }
2090  ids.clear();
2091  for (auto tuple : leafMap)
2092  ids.push_back(cast<IntegerAttr>(tuple.first));
2093  sort();
2094  llvm::dbgs() << "leafMap:\n";
2095  for (auto id : ids) {
2096  auto fieldRef = leafMap.lookup(id).field;
2097  auto value = fieldRef.getValue();
2098  auto fieldID = fieldRef.getFieldID();
2099  if (auto blockArg = dyn_cast<BlockArgument>(value)) {
2100  FModuleOp module = cast<FModuleOp>(blockArg.getOwner()->getParentOp());
2101  llvm::dbgs() << " - " << id.getValue() << ": "
2102  << module.getName() + ">" +
2103  module.getPortName(blockArg.getArgNumber());
2104  if (fieldID)
2105  llvm::dbgs() << ", fieldID=" << fieldID;
2106  llvm::dbgs() << "\n";
2107  } else {
2108  llvm::dbgs() << " - " << id.getValue() << ": "
2109  << cast<StringAttr>(value.getDefiningOp()->getAttr("name"))
2110  .getValue();
2111  if (fieldID)
2112  llvm::dbgs() << ", fieldID=" << fieldID;
2113  llvm::dbgs() << "\n";
2114  }
2115  }
2116  });
2117 
2118  // Now, iterate over the worklist of interface-encoding annotations to create
2119  // the interface and all its sub-interfaces (interfaces that it instantiates),
2120  // instantiate the top-level interface, and generate a "mappings file" that
2121  // will use XMRs to drive the interface. If extraction info is available,
2122  // then the top-level instantiate interface will be marked for extraction via
2123  // a SystemVerilog bind.
2124  SmallVector<sv::InterfaceOp, 2> interfaceVec;
2126  companionToInterfaceMap;
2127  auto compareInterfaceSignal = [&](InterfaceElemsBuilder &lhs,
2128  InterfaceElemsBuilder &rhs) {
2129  auto compareProps = [&](InterfaceElemsBuilder::Properties &lhs,
2130  InterfaceElemsBuilder::Properties &rhs) {
2131  // If it's a verbatim op, no need to check the string, because the
2132  // interface names might not match. As long as the signal types match that
2133  // is sufficient.
2134  if (lhs.elemType.index() == 0 && rhs.elemType.index() == 0)
2135  return true;
2136  if (std::get<Type>(lhs.elemType) == std::get<Type>(rhs.elemType))
2137  return true;
2138  return false;
2139  };
2140  return std::equal(lhs.elementsList.begin(), lhs.elementsList.end(),
2141  rhs.elementsList.begin(), compareProps);
2142  };
2143  for (auto anno : worklist) {
2144  auto bundle = AugmentedBundleTypeAttr::get(&getContext(), anno.getDict());
2145 
2146  // The top-level AugmentedBundleType must have a global ID field so that
2147  // this can be linked to the companion.
2148  if (!bundle.isRoot()) {
2149  emitCircuitError() << "missing 'id' in root-level BundleType: "
2150  << anno.getDict() << "\n";
2151  removalError = true;
2152  continue;
2153  }
2154 
2155  if (companionIDMap.count(bundle.getID()) == 0) {
2156  emitCircuitError() << "no companion found with 'id' value '"
2157  << bundle.getID().getValue().getZExtValue() << "'\n";
2158  removalError = true;
2159  continue;
2160  }
2161 
2162  // Decide on a symbol name to use for the interface instance. This is needed
2163  // in `traverseBundle` as a placeholder for the connect operations.
2164  auto companionIter = companionIDMap.lookup(bundle.getID());
2165  auto companionModule = companionIter.companion;
2166  auto symbolName = getNamespace().newName(
2167  "__" + companionIDMap.lookup(bundle.getID()).name + "_" +
2168  getInterfaceName(bundle.getPrefix(), bundle) + "__");
2169 
2170  // Recursively walk the AugmentedBundleType to generate interfaces and XMRs.
2171  // Error out if this returns None (indicating that the annotation is
2172  // malformed in some way). A good error message is generated inside
2173  // `traverseBundle` or the functions it calls.
2174  auto instanceSymbol =
2175  hw::InnerRefAttr::get(SymbolTable::getSymbolName(companionModule),
2176  StringAttr::get(&getContext(), symbolName));
2177  VerbatimBuilder::Base verbatimData;
2178  VerbatimBuilder verbatim(verbatimData);
2179  verbatim += instanceSymbol;
2180  // List of interface elements.
2181 
2182  SmallVector<VerbatimXMRbuilder> xmrElems;
2183  SmallVector<InterfaceElemsBuilder> interfaceBuilder;
2184 
2185  auto ifaceName = traverseBundle(bundle, bundle.getID(), bundle.getPrefix(),
2186  verbatim, xmrElems, interfaceBuilder);
2187  if (!ifaceName) {
2188  removalError = true;
2189  continue;
2190  }
2191 
2192  if (companionIter.isNonlocal) {
2193  // If the companion module has two exactly same ViewAnnotation.companion
2194  // annotations, then add the interface for only one of them. This happens
2195  // when the companion is deduped.
2196  auto viewMapIter = companionToInterfaceMap.find(companionModule);
2197  if (viewMapIter != companionToInterfaceMap.end())
2198  if (std::equal(interfaceBuilder.begin(), interfaceBuilder.end(),
2199  viewMapIter->getSecond().begin(),
2200  compareInterfaceSignal)) {
2201  continue;
2202  }
2203 
2204  companionToInterfaceMap[companionModule] = interfaceBuilder;
2205  }
2206 
2207  if (interfaceBuilder.empty())
2208  continue;
2209  auto companionBuilder =
2210  OpBuilder::atBlockEnd(companionModule.getBodyBlock());
2211 
2212  // Generate gathered XMR's.
2213  for (auto xmrElem : xmrElems) {
2214  auto uloc = companionBuilder.getUnknownLoc();
2215  companionBuilder.create<sv::VerbatimOp>(uloc, xmrElem.str, xmrElem.val,
2216  xmrElem.syms);
2217  }
2218  numXMRs += xmrElems.size();
2219 
2220  sv::InterfaceOp topIface;
2221  for (const auto &ifaceBuilder : interfaceBuilder) {
2222  auto builder = OpBuilder::atBlockEnd(getOperation().getBodyBlock());
2223  auto loc = getOperation().getLoc();
2224  sv::InterfaceOp iface =
2225  builder.create<sv::InterfaceOp>(loc, ifaceBuilder.iFaceName);
2226  if (!topIface)
2227  topIface = iface;
2228  ++numInterfaces;
2229  if (dut &&
2230  !instancePaths->instanceGraph.isAncestor(
2231  companionIDMap[ifaceBuilder.id].companion, dut) &&
2232  testbenchDir)
2233  iface->setAttr("output_file",
2234  hw::OutputFileAttr::getAsDirectory(
2235  &getContext(), testbenchDir.getValue(),
2236  /*excludeFromFileList=*/true));
2237  else if (maybeExtractInfo)
2238  iface->setAttr("output_file",
2239  hw::OutputFileAttr::getAsDirectory(
2240  &getContext(), getOutputDirectory().getValue(),
2241  /*excludeFromFileList=*/true));
2242  iface.setCommentAttr(builder.getStringAttr("VCS coverage exclude_file"));
2243  builder.setInsertionPointToEnd(
2244  cast<sv::InterfaceOp>(iface).getBodyBlock());
2245  interfaceMap[FlatSymbolRefAttr::get(builder.getContext(),
2246  ifaceBuilder.iFaceName)] = iface;
2247  for (auto elem : ifaceBuilder.elementsList) {
2248 
2249  auto uloc = builder.getUnknownLoc();
2250 
2251  auto description = elem.description;
2252 
2253  if (description) {
2254  auto descriptionOp = builder.create<sv::VerbatimOp>(
2255  uloc, ("// " + cleanupDescription(description.getValue())));
2256 
2257  // If we need to generate a YAML representation of this interface,
2258  // then add an attribute indicating that this `sv::VerbatimOp` is
2259  // actually a description.
2260  if (maybeHierarchyFileYAML)
2261  descriptionOp->setAttr("firrtl.grandcentral.yaml.type",
2262  builder.getStringAttr("description"));
2263  }
2264  if (auto *str = std::get_if<VerbatimType>(&elem.elemType)) {
2265  auto instanceOp = builder.create<sv::VerbatimOp>(
2266  uloc, str->toStr(elem.elemName.getValue()));
2267 
2268  // If we need to generate a YAML representation of the interface, then
2269  // add attributes that describe what this `sv::VerbatimOp` is.
2270  if (maybeHierarchyFileYAML) {
2271  if (str->instantiation)
2272  instanceOp->setAttr("firrtl.grandcentral.yaml.type",
2273  builder.getStringAttr("instance"));
2274  else
2275  instanceOp->setAttr("firrtl.grandcentral.yaml.type",
2276  builder.getStringAttr("unsupported"));
2277  instanceOp->setAttr("firrtl.grandcentral.yaml.name", elem.elemName);
2278  instanceOp->setAttr("firrtl.grandcentral.yaml.dimensions",
2279  builder.getI32ArrayAttr(str->dimensions));
2280  instanceOp->setAttr(
2281  "firrtl.grandcentral.yaml.symbol",
2282  FlatSymbolRefAttr::get(builder.getContext(), str->str));
2283  }
2284  continue;
2285  }
2286 
2287  auto tpe = std::get<Type>(elem.elemType);
2288  builder.create<sv::InterfaceSignalOp>(uloc, elem.elemName.getValue(),
2289  tpe);
2290  }
2291  }
2292 
2293  ++numViews;
2294 
2295  interfaceVec.push_back(topIface);
2296 
2297  // Instantiate the interface inside the companion.
2298  builder.setInsertionPointToStart(companionModule.getBodyBlock());
2299  builder.create<sv::InterfaceInstanceOp>(
2300  getOperation().getLoc(), topIface.getInterfaceType(),
2301  companionIDMap.lookup(bundle.getID()).name,
2302  hw::InnerSymAttr::get(builder.getStringAttr(symbolName)));
2303 
2304  // If no extraction information was present, then just leave the interface
2305  // instantiated in the companion. Otherwise, make it a bind.
2306  if (!maybeExtractInfo)
2307  continue;
2308 
2309  // If the interface is associated with a companion that is instantiated
2310  // above the DUT (e.g.., in the test harness), then don't extract it.
2311  if (dut && !instancePaths->instanceGraph.isAncestor(
2312  companionIDMap[bundle.getID()].companion, dut))
2313  continue;
2314  }
2315 
2316  emitHierarchyYamlFile(interfaceVec);
2317 
2318  // Signal pass failure if any errors were found while examining circuit
2319  // annotations.
2320  if (removalError)
2321  return signalPassFailure();
2322  markAnalysesPreserved<NLATable>();
2323 }
2324 
2325 void GrandCentralPass::emitHierarchyYamlFile(
2326  SmallVectorImpl<sv::InterfaceOp> &intfs) {
2327  // If a `GrandCentralHierarchyFileAnnotation` was passed in, generate a YAML
2328  // representation of the interfaces that we produced with the filename that
2329  // that annotation provided.
2330  if (!maybeHierarchyFileYAML)
2331  return;
2332 
2333  CircuitOp circuitOp = getOperation();
2334 
2335  std::string yamlString;
2336  llvm::raw_string_ostream stream(yamlString);
2337  ::yaml::Context yamlContext({interfaceMap});
2338  llvm::yaml::Output yout(stream);
2339  yamlize(yout, intfs, true, yamlContext);
2340 
2341  auto builder = OpBuilder::atBlockBegin(circuitOp.getBodyBlock());
2342  builder.create<sv::VerbatimOp>(builder.getUnknownLoc(), yamlString)
2343  ->setAttr("output_file",
2344  hw::OutputFileAttr::getFromFilename(
2345  &getContext(), maybeHierarchyFileYAML->getValue(),
2346  /*excludeFromFileList=*/true));
2347  LLVM_DEBUG({ llvm::dbgs() << "Generated YAML:" << yamlString << "\n"; });
2348 }
2349 
2350 //===----------------------------------------------------------------------===//
2351 // Pass Creation
2352 //===----------------------------------------------------------------------===//
2353 
2354 std::unique_ptr<mlir::Pass>
2356  auto pass = std::make_unique<GrandCentralPass>();
2357  pass->companionMode = companionMode;
2358  return pass;
2359 }
assert(baseType &&"element must be base type")
MlirType elementType
Definition: CHIRRTL.cpp:29
int32_t width
Definition: FIRRTL.cpp:36
static std::optional< DictionaryAttr > parseAugmentedType(ApplyState &state, DictionaryAttr augmentedType, DictionaryAttr root, StringRef companion, StringAttr name, StringAttr defName, std::optional< IntegerAttr > id, std::optional< StringAttr > description, Twine clazz, StringAttr companionAttr, Twine path={})
Recursively walk a sifive.enterprise.grandcentral.AugmentedType to extract any annotations it may con...
@ Output
Definition: HW.h:35
static StringAttr append(StringAttr base, const Twine &suffix)
Return a attribute with the specified suffix appended.
static Block * getBodyBlock(FModuleLike mod)
This class represents a reference to a specific field or element of an aggregate value.
Definition: FieldRef.h:28
unsigned getFieldID() const
Get the field ID of this FieldRef, which is a unique identifier mapped to a specific field in a bundl...
Definition: FieldRef.h:59
Value getValue() const
Get the Value which created this location.
Definition: FieldRef.h:37
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
bool applyToOperation(Operation *op) const
Store the annotations in this set in an operation's annotations attribute, overwriting any existing a...
void addAnnotations(ArrayRef< Annotation > annotations)
Add more annotations to this annotation set.
bool hasAnnotation(StringRef className) const
Return true if we have an annotation with the specified class name.
static bool removePortAnnotations(Operation *module, llvm::function_ref< bool(unsigned, Annotation)> predicate)
Remove all port annotations from a module or extmodule for which predicate returns true.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
unsigned getFieldID() const
Get the field id this attribute targets.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
StringRef getClass() const
Return the 'class' that this annotation is representing.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
igraph::InstanceGraphNode * getTopLevelNode() override
Get the node corresponding to the top-level module of a circuit.
This table tracks nlas and what modules participate in them.
Definition: NLATable.h:29
void removeNLAsfromModule(const DenseSet< hw::HierPathOp > &nlas, StringAttr mod)
Remove all the nlas in the set nlas from the module.
Definition: NLATable.h:173
void getNLAsInModule(StringAttr modName, DenseSet< hw::HierPathOp > &nlas)
Get the NLAs that the module modName particiaptes in, and insert them into the DenseSet nlas.
Definition: NLATable.h:94
hw::HierPathOp getNLA(StringAttr name)
Resolve a symbol to an NLA.
Definition: NLATable.cpp:48
This is a Node in the InstanceGraph.
bool isAncestor(ModuleOpInterface child, ModuleOpInterface parent, llvm::function_ref< bool(InstanceRecord *)> skipInstance=[](InstanceRecord *_) { return false;})
Check if child is instantiated by a parent.
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
This is an edge in the InstanceGraph.
Definition: InstanceGraph.h:55
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
constexpr const char * augmentedBundleTypeClass
igraph::InstancePathCache InstancePathCache
constexpr const char * extractGrandCentralClass
LogicalResult extractDUT(FModuleOp mod, FModuleOp &dut)
Utility that searches for a MarkDUTAnnotation on a specific module, mod, and tries to update a design...
constexpr const char * testBenchDirAnnoClass
constexpr const char * augmentedGroundTypeClass
std::unique_ptr< mlir::Pass > createGrandCentralPass(CompanionMode companionMode=CompanionMode::Bind)
constexpr const char * viewAnnoClass
constexpr const char * blackBoxPathAnnoClass
Value getValueByFieldID(ImplicitLocOpBuilder builder, Value value, unsigned fieldID)
This gets the value targeted by a field id.
constexpr const char * companionAnnoClass
constexpr const char * blackBoxInlineAnnoClass
std::optional< AnnoPathValue > resolvePath(StringRef rawPath, CircuitOp circuit, SymbolTable &symTbl, CircuitTargetCache &cache)
Resolve a string path to a named item inside a circuit.
LogicalResult applyGCTView(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
constexpr const char * prefixInterfacesAnnoClass
constexpr const char * grandCentralHierarchyFileAnnoClass
::mlir::Type getFinalTypeByFieldID(Type type, uint64_t fieldID)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
llvm::raw_ostream & debugPassHeader(const mlir::Pass *pass, int width=80)
Write a boilerplate header for a pass to the debug stream.
Definition: Debug.cpp:31
static std::string stripComment(StringRef str)
Convert newlines and comments to remove the comments.
Definition: sv.py:1
State threaded through functions for resolving and applying annotations.
SmallVector< WiringProblem > wiringProblems
InstancePathCache & instancePathCache
The namespace of a CircuitOp, generally inhabited by modules.
Definition: Namespace.h:24
void insertOp(Operation *op)
Add a new op to the target cache.
A data structure that caches and provides absolute paths to module instances in the IR.
ArrayRef< InstancePath > getAbsolutePaths(ModuleOpInterface op)
InstanceGraph & instanceGraph
The instance graph of the IR.
SmallVector< int64_t, 2 > dimensions
An array describing the dimensionality of the interface.
static void mapping(IO &io, DescribedInstance &op, Context &ctx)
DescribedSignal denormalize(IO &)
This cannot be denormalized back to an interface op.
std::optional< std::string > description
An optional, textual description of what the field is.
Field(IO &io)
A no-argument constructor is necessary to work with LLVM's YAML library.
Field(IO &io, DescribedSignal &op)
Construct a Field from a DescribedSignal (an sv::InterfaceSignalOp with an optional description).
SmallVector< unsigned, 2 > dimensions
The dimensions of the field.
static void mapping(IO &io, DescribedSignal &op, Context &ctx)
Interface(IO &io)
A no-argument constructor is necessary to work with LLVM's YAML library.
std::vector< DescribedInstance > instances
Instantiations of other interfaces.
Interface(IO &io, sv::InterfaceOp &op)
Construct an Interface from an sv::InterfaceOp.
sv::InterfaceOp denormalize(IO &)
This cannot be denormalized back to an interface op.
std::vector< DescribedSignal > fields
All ground or vectors that make up the interface.
static void mapping(IO &io, sv::InterfaceOp &op, Context &ctx)