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