CIRCT  20.0.0git
ESIPasses.cpp
Go to the documentation of this file.
1 //===- ESIPasses.cpp - Common code for ESI passes ---------------*- 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 
9 #include "PassDetails.h"
10 
12 #include "circt/Dialect/HW/HWOps.h"
13 #include "circt/Dialect/SV/SVOps.h"
14 #include "circt/Support/LLVM.h"
15 
16 #include "mlir/IR/SymbolTable.h"
17 #include "mlir/Pass/Pass.h"
18 #include "mlir/Transforms/DialectConversion.h"
19 
20 using namespace circt;
21 using namespace circt::esi;
22 using namespace circt::esi::detail;
23 using namespace circt::hw;
24 using namespace circt::sv;
25 
26 StringAttr circt::esi::detail::getTypeID(Type t) {
27  std::string typeID;
28  llvm::raw_string_ostream(typeID) << t;
29  return StringAttr::get(t.getContext(), typeID);
30 }
31 
32 uint64_t circt::esi::detail::getWidth(Type t) {
33  if (auto ch = dyn_cast<ChannelType>(t))
34  t = ch.getInner();
35  return hw::getBitWidth(t);
36 }
37 
38 //===----------------------------------------------------------------------===//
39 // ESI custom op builder.
40 //===----------------------------------------------------------------------===//
41 
42 // C++ requires this for showing it what object file it should store these
43 // symbols in. They should be inline but that feature wasn't added until C++17.
47 
49  : ImplicitLocOpBuilder(UnknownLoc::get(top->getContext()), top),
50  a(StringAttr::get(getContext(), "a")),
51  aValid(StringAttr::get(getContext(), "a_valid")),
52  aReady(StringAttr::get(getContext(), "a_ready")),
53  x(StringAttr::get(getContext(), "x")),
54  xValid(StringAttr::get(getContext(), "x_valid")),
55  xReady(StringAttr::get(getContext(), "x_ready")),
56  dataOutValid(StringAttr::get(getContext(), "DataOutValid")),
57  dataOutReady(StringAttr::get(getContext(), "DataOutReady")),
58  dataOut(StringAttr::get(getContext(), "DataOut")),
59  dataInValid(StringAttr::get(getContext(), "DataInValid")),
60  dataInReady(StringAttr::get(getContext(), "DataInReady")),
61  dataIn(StringAttr::get(getContext(), "DataIn")),
62  clk(StringAttr::get(getContext(), "clk")),
63  rst(StringAttr::get(getContext(), "rst")),
64  width(StringAttr::get(getContext(), "WIDTH")) {
65 
66  auto regions = top->getRegions();
67  if (regions.empty()) {
68  top->emitError("ESI HW Builder needs a region to insert HW.");
69  }
70  auto &region = regions.front();
71  if (!region.empty())
72  setInsertionPoint(&region.front(), region.front().begin());
73 }
74 
75 static StringAttr constructUniqueSymbol(Operation *tableOp,
76  StringRef proposedNameRef) {
77  SmallString<64> proposedName = proposedNameRef;
78 
79  // Normalize the type name.
80  for (char &ch : proposedName) {
81  if (isalpha(ch) || isdigit(ch) || ch == '_')
82  continue;
83  ch = '_';
84  }
85 
86  // Make sure that this symbol isn't taken. If it is, append a number and try
87  // again.
88  size_t baseLength = proposedName.size();
89  size_t tries = 0;
90  while (SymbolTable::lookupSymbolIn(tableOp, proposedName)) {
91  proposedName.resize(baseLength);
92  proposedName.append(llvm::utostr(++tries));
93  }
94 
95  return StringAttr::get(tableOp->getContext(), proposedName);
96 }
97 
98 StringAttr ESIHWBuilder::constructInterfaceName(ChannelType port) {
99  Operation *tableOp =
100  getInsertionPoint()->getParentWithTrait<mlir::OpTrait::SymbolTable>();
101 
102  // Get a name based on the type.
103  std::string portTypeName;
104  llvm::raw_string_ostream nameOS(portTypeName);
105  TypeSwitch<Type>(port.getInner())
106  .Case([&](hw::ArrayType arr) {
107  nameOS << "ArrayOf" << arr.getNumElements() << 'x'
108  << arr.getElementType();
109  })
110  .Case([&](hw::StructType t) { nameOS << "Struct"; })
111  .Default([&](Type t) { nameOS << port.getInner(); });
112 
113  // Don't allow the name to end with '_'.
114  ssize_t i = portTypeName.size() - 1;
115  while (i >= 0 && portTypeName[i] == '_') {
116  --i;
117  }
118  portTypeName = portTypeName.substr(0, i + 1);
119 
120  // All stage names start with this.
121  SmallString<64> proposedName("IValidReady_");
122  proposedName.append(portTypeName);
123  return constructUniqueSymbol(tableOp, proposedName);
124 }
125 
126 /// Return a parameter list for the stage module with the specified value.
127 ArrayAttr ESIHWBuilder::getStageParameterList(Attribute value) {
128  auto type = IntegerType::get(width.getContext(), 32, IntegerType::Unsigned);
129  auto widthParam = ParamDeclAttr::get(width.getContext(), width, type, value);
130  return ArrayAttr::get(width.getContext(), widthParam);
131 }
132 
133 /// Write an 'ExternModuleOp' to use a hand-coded SystemVerilog module. Said
134 /// module implements pipeline stage, adding 1 cycle latency. This particular
135 /// implementation is double-buffered and fully pipelines the reverse-flow ready
136 /// signal.
137 HWModuleExternOp ESIHWBuilder::declareStage(Operation *symTable,
138  PipelineStageOp stage) {
139  Type dataType = stage.innerType();
140  HWModuleExternOp &stageMod = declaredStage[dataType];
141  if (stageMod)
142  return stageMod;
143 
144  // Since this module has parameterized widths on the a input and x output,
145  // give the extern declation a None type since nothing else makes sense.
146  // Will be refining this when we decide how to better handle parameterized
147  // types and ops.
148  size_t argn = 0;
149  size_t resn = 0;
150  llvm::SmallVector<PortInfo> ports = {
152  {{rst, getI1Type(), ModulePort::Direction::Input}, argn++}};
153 
154  ports.push_back({{a, dataType, ModulePort::Direction::Input}, argn++});
155  ports.push_back(
156  {{aValid, getI1Type(), ModulePort::Direction::Input}, argn++});
157  ports.push_back(
158  {{aReady, getI1Type(), ModulePort::Direction::Output}, resn++});
159  ports.push_back({{x, dataType, ModulePort::Direction::Output}, resn++});
160 
161  ports.push_back(
162  {{xValid, getI1Type(), ModulePort::Direction::Output}, resn++});
163  ports.push_back(
164  {{xReady, getI1Type(), ModulePort::Direction::Input}, argn++});
165 
166  stageMod = create<HWModuleExternOp>(
167  constructUniqueSymbol(symTable, "ESI_PipelineStage"), ports,
168  "ESI_PipelineStage", getStageParameterList({}));
169  return stageMod;
170 }
171 
172 /// Write an 'ExternModuleOp' to use a hand-coded SystemVerilog module. Said
173 /// module contains a bi-directional Cosimulation DPI interface with valid/ready
174 /// semantics.
175 HWModuleExternOp
179 
180  SmallVector<Attribute, 8> params;
181  params.push_back(
182  ParamDeclAttr::get("ENDPOINT_ID", NoneType::get(getContext())));
183  params.push_back(
184  ParamDeclAttr::get("TO_HOST_TYPE_ID", NoneType::get(getContext())));
185  params.push_back(ParamDeclAttr::get("TO_HOST_SIZE_BITS", getI32Type()));
186 
188  StringAttr::get(getContext(), "TO_HOST_SIZE_BITS"),
189  getIntegerType(32, false)));
190  PortInfo ports[] = {
192  {{rst, getI1Type(), ModulePort::Direction::Input}, 1},
193  {{dataInValid, getI1Type(), ModulePort::Direction::Input}, 2},
194  {{dataInReady, getI1Type(), ModulePort::Direction::Output}, 3},
195  {{dataIn, dataInType, ModulePort::Direction::Input}, 4}};
196 
197  declaredCosimEndpointToHostModule = create<HWModuleExternOp>(
198  constructUniqueSymbol(symTable, "Cosim_Endpoint_ToHost"), ports,
199  "Cosim_Endpoint_ToHost", ArrayAttr::get(getContext(), params));
201 }
202 
203 HWModuleExternOp
207 
208  SmallVector<Attribute, 8> params;
209  params.push_back(
210  ParamDeclAttr::get("ENDPOINT_ID", NoneType::get(getContext())));
211  params.push_back(
212  ParamDeclAttr::get("FROM_HOST_TYPE_ID", NoneType::get(getContext())));
213  params.push_back(ParamDeclAttr::get("FROM_HOST_SIZE_BITS", getI32Type()));
214 
216  StringAttr::get(getContext(), "FROM_HOST_SIZE_BITS"),
217  getIntegerType(32, false)));
218  PortInfo ports[] = {
220  {{rst, getI1Type(), ModulePort::Direction::Input}, 1},
221  {{dataOutValid, getI1Type(), ModulePort::Direction::Output}, 2},
222  {{dataOutReady, getI1Type(), ModulePort::Direction::Input}, 3},
223  {{dataOut, dataInType, ModulePort::Direction::Output}, 4}};
224 
225  declaredCosimEndpointFromHostModule = create<HWModuleExternOp>(
226  constructUniqueSymbol(symTable, "Cosim_Endpoint_FromHost"), ports,
227  "Cosim_Endpoint_FromHost", ArrayAttr::get(getContext(), params));
229 }
230 
231 /// Return the InterfaceType which corresponds to an ESI port type. If it
232 /// doesn't exist in the cache, build the InterfaceOp and the corresponding
233 /// type.
234 InterfaceOp ESIHWBuilder::getOrConstructInterface(ChannelType t) {
235  auto ifaceIter = portTypeLookup.find(t);
236  if (ifaceIter != portTypeLookup.end())
237  return ifaceIter->second;
238  auto iface = constructInterface(t);
239  portTypeLookup[t] = iface;
240  return iface;
241 }
242 
243 InterfaceOp ESIHWBuilder::constructInterface(ChannelType chan) {
244  return create<InterfaceOp>(constructInterfaceName(chan).getValue(), [&]() {
245  create<InterfaceSignalOp>(validStr, getI1Type());
246  create<InterfaceSignalOp>(readyStr, getI1Type());
247  create<InterfaceSignalOp>(dataStr, chan.getInner());
248  llvm::SmallVector<StringRef> validDataStrs;
249  validDataStrs.push_back(validStr);
250  validDataStrs.push_back(dataStr);
251  create<InterfaceModportOp>(sinkStr,
252  /*inputs=*/ArrayRef<StringRef>{readyStr},
253  /*outputs=*/validDataStrs);
254  create<InterfaceModportOp>(sourceStr,
255  /*inputs=*/validDataStrs,
256  /*outputs=*/ArrayRef<StringRef>{readyStr});
257  });
258 }
259 
260 Type ESIHWBuilder::getClockType() { return seq::ClockType::get(getContext()); }
261 
static void registerPasses()
Definition: CIRCTModule.cpp:40
static StringAttr constructUniqueSymbol(Operation *tableOp, StringRef proposedNameRef)
Definition: ESIPasses.cpp:75
#define isalpha(x)
Definition: FIRLexer.cpp:27
#define isdigit(x)
Definition: FIRLexer.cpp:26
int32_t width
Definition: FIRRTL.cpp:36
@ Input
Definition: HW.h:35
@ Output
Definition: HW.h:35
hw::HWModuleExternOp declareCosimEndpointFromHostModule(Operation *symTable)
Definition: ESIPasses.cpp:204
static constexpr char validStr[]
Definition: PassDetails.h:76
static constexpr char sinkStr[]
Definition: PassDetails.h:78
std::optional< hw::HWModuleExternOp > declaredCosimEndpointToHostModule
Definition: PassDetails.h:87
llvm::DenseMap< Type, sv::InterfaceOp > portTypeLookup
Definition: PassDetails.h:90
static constexpr char readyStr[]
Definition: PassDetails.h:77
hw::HWModuleExternOp declareCosimEndpointToHostModule(Operation *symTable)
Write an 'ExternModuleOp' to use a hand-coded SystemVerilog module.
Definition: ESIPasses.cpp:176
static constexpr char dataStr[]
Definition: PassDetails.h:76
StringAttr constructInterfaceName(ChannelType)
Construct a type-appropriate name for the interface, making sure it's not taken in the symbol table.
Definition: ESIPasses.cpp:98
sv::InterfaceOp constructInterface(ChannelType)
Definition: ESIPasses.cpp:243
sv::InterfaceOp getOrConstructInterface(ChannelType)
Return the InterfaceType which corresponds to an ESI port type.
Definition: ESIPasses.cpp:234
std::optional< hw::HWModuleExternOp > declaredCosimEndpointFromHostModule
Definition: PassDetails.h:88
llvm::DenseMap< Type, hw::HWModuleExternOp > declaredStage
Definition: PassDetails.h:89
static constexpr char sourceStr[]
Definition: PassDetails.h:77
hw::HWModuleExternOp declareStage(Operation *symTable, PipelineStageOp)
Write an 'ExternModuleOp' to use a hand-coded SystemVerilog module.
Definition: ESIPasses.cpp:137
ArrayAttr getStageParameterList(Attribute value)
Return a parameter list for the stage module with the specified value.
Definition: ESIPasses.cpp:127
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
uint64_t getWidth(Type t)
Definition: ESIPasses.cpp:32
StringAttr getTypeID(Type t)
Definition: ESIPasses.cpp:26
void registerESIPasses()
Definition: ESIPasses.cpp:262
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
Definition: HWTypes.cpp:110
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
This holds the name, type, direction of a module's ports.