CIRCT  18.0.0git
ESITranslations.cpp
Go to the documentation of this file.
1 //===- ESITranslations.cpp - ESI translations -------------------*- C++ -*-===//
2 //
3 // ESI translations:
4 // - Cap'nProto schema generation
5 //
6 //===----------------------------------------------------------------------===//
7 
12 #include "circt/Support/LLVM.h"
13 #include "mlir/Dialect/Func/IR/FuncOps.h"
14 #include "mlir/IR/BuiltinDialect.h"
15 #include "mlir/IR/BuiltinOps.h"
16 #include "mlir/Tools/mlir-translate/Translation.h"
17 #include "llvm/Support/Format.h"
18 
19 #include <algorithm>
20 
21 #ifdef CAPNP
22 #include "capnp/ESICapnp.h"
23 #include "circt/Dialect/ESI/CosimSchema.h"
24 #endif
25 
26 using namespace circt;
27 using namespace circt::esi;
28 
29 //===----------------------------------------------------------------------===//
30 // ESI Cosim Cap'nProto schema generation.
31 //
32 // Cosimulation in ESI is done over capnp. This translation walks the IR, finds
33 // all the `esi.cosim` ops, and creates a schema for all the types. It requires
34 // CAPNP to be enabled.
35 //===----------------------------------------------------------------------===//
36 
37 #ifdef CAPNP
38 
39 namespace {
40 
41 struct ErrorCountingHandler : public mlir::ScopedDiagnosticHandler {
42  ErrorCountingHandler(mlir::MLIRContext *context)
43  : mlir::ScopedDiagnosticHandler(context) {
44  setHandler([this](Diagnostic &diag) -> LogicalResult {
45  if (diag.getSeverity() == mlir::DiagnosticSeverity::Error)
46  ++errorCount;
47  return failure();
48  });
49  }
50 
51  size_t errorCount = 0;
52 };
53 
54 struct ExportCosimSchema {
55  ExportCosimSchema(ModuleOp module, llvm::raw_ostream &os)
56  : module(module), os(os), handler(module.getContext()),
57  unknown(UnknownLoc::get(module.getContext())) {}
58 
59  /// Emit the whole schema.
60  LogicalResult emit();
61 
62  /// Collect the types for which we need to emit a schema. Output some metadata
63  /// comments.
64  LogicalResult visitEndpoint(CosimEndpointOp);
65 
66 private:
67  ModuleOp module;
68  llvm::raw_ostream &os;
69  ErrorCountingHandler handler;
70  const Location unknown;
71 
72  // All the `esi.cosim` input and output types encountered during the IR walk.
73  // This is NOT in a deterministic order!
74  llvm::SmallVector<std::shared_ptr<capnp::CapnpTypeSchema>> types;
75 };
76 } // anonymous namespace
77 
78 LogicalResult ExportCosimSchema::visitEndpoint(CosimEndpointOp ep) {
79  auto sendTypeSchema =
80  std::make_shared<capnp::CapnpTypeSchema>(ep.getSend().getType());
81  if (!sendTypeSchema->isSupported())
82  return ep.emitOpError("Type ")
83  << ep.getSend().getType() << " not supported.";
84  types.push_back(sendTypeSchema);
85 
86  auto recvTypeSchema =
87  std::make_shared<capnp::CapnpTypeSchema>(ep.getRecv().getType());
88  if (!recvTypeSchema->isSupported())
89  return ep.emitOpError("Type '")
90  << ep.getRecv().getType() << "' not supported.";
91  types.push_back(recvTypeSchema);
92 
93  os << "# Endpoint ";
94  StringAttr epName = ep->getAttrOfType<StringAttr>("name");
95  if (epName)
96  os << epName << " endpoint at " << ep.getLoc() << ":\n";
97  os << "# Send type: ";
98  sendTypeSchema->writeMetadata(os);
99  os << "\n";
100 
101  os << "# Recv type: ";
102  recvTypeSchema->writeMetadata(os);
103  os << "\n";
104 
105  return success();
106 }
107 
108 static void emitCosimSchemaBody(llvm::raw_ostream &os) {
109  StringRef entireSchemaFile = circt::esi::cosim::CosimSchema;
110  size_t idLocation = entireSchemaFile.find("@0x");
111  size_t newlineAfter = entireSchemaFile.find('\n', idLocation);
112 
113  os << "\n\n"
114  << "#########################################################\n"
115  << "## Standard RPC interfaces.\n"
116  << "#########################################################\n";
117  os << entireSchemaFile.substr(newlineAfter) << "\n";
118 }
119 
120 LogicalResult ExportCosimSchema::emit() {
121  os << "#########################################################\n"
122  << "## ESI generated schema.\n"
123  << "#########################################################\n";
124 
125  // Walk and collect the type data.
126  auto walkResult = module.walk([this](CosimEndpointOp ep) {
127  if (failed(visitEndpoint(ep)))
128  return mlir::WalkResult::interrupt();
129  return mlir::WalkResult::advance();
130  });
131  if (walkResult.wasInterrupted())
132  return failure();
133  os << "#########################################################\n";
134 
135  // We need a sorted list to ensure determinism.
136  llvm::sort(types.begin(), types.end(),
137  [](auto &a, auto &b) { return a->typeID() > b->typeID(); });
138 
139  // Compute and emit the capnp file id.
140  uint64_t fileHash = 2544816649379317016; // Some random number.
141  for (auto &schema : types)
142  fileHash = llvm::hashing::detail::hash_16_bytes(fileHash, schema->typeID());
143  // Capnp IDs always have a '1' high bit.
144  fileHash |= 0x8000000000000000;
145  capnp::emitCapnpID(os, fileHash) << ";\n\n";
146 
147  os << "#########################################################\n"
148  << "## Types for your design.\n"
149  << "#########################################################\n\n";
150  // Iterate through the various types and emit their schemas.
151  auto end = std::unique(
152  types.begin(), types.end(),
153  [&](const auto &lhs, const auto &rhs) { return *lhs == *rhs; });
154  for (auto typeIter = types.begin(); typeIter != end; ++typeIter) {
155  if (failed((*typeIter)->write(os)))
156  // If we fail during an emission, dump out early since the output may be
157  // corrupted.
158  return failure();
159  }
160 
161  // Include the RPC schema in each generated file.
162  emitCosimSchemaBody(os);
163 
164  return success(handler.errorCount == 0);
165 }
166 
167 LogicalResult circt::esi::exportCosimSchema(ModuleOp module,
168  llvm::raw_ostream &os) {
169  ExportCosimSchema schema(module, os);
170  return schema.emit();
171 }
172 
173 #else // Not CAPNP
174 
175 LogicalResult circt::esi::exportCosimSchema(ModuleOp module,
176  llvm::raw_ostream &os) {
177  return mlir::emitError(UnknownLoc::get(module.getContext()),
178  "Not compiled with CAPNP support");
179 }
180 
181 #endif
182 
183 //===----------------------------------------------------------------------===//
184 // Register all ESI translations.
185 //===----------------------------------------------------------------------===//
186 
188 #ifdef CAPNP
189  mlir::TranslateFromMLIRRegistration cosimToCapnp(
190  "export-esi-capnp", "ESI Cosim Cap'nProto schema generation",
191  exportCosimSchema, [](mlir::DialectRegistry &registry) {
192  registry.insert<ESIDialect, circt::hw::HWDialect,
193  circt::seq::SeqDialect, circt::sv::SVDialect,
194  mlir::func::FuncDialect, mlir::BuiltinDialect>();
195  });
196 #endif
197 }
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:53
llvm::raw_ostream & emitCapnpID(llvm::raw_ostream &os, int64_t id)
Emit an ID in capnp format.
Definition: ESICapnp.h:42
LogicalResult exportCosimSchema(ModuleOp module, llvm::raw_ostream &os)
void registerESITranslations()
This file defines an intermediate representation for circuits acting as an abstraction for constraint...