CIRCT 20.0.0git
Loading...
Searching...
No Matches
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
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
20using namespace circt;
21using namespace circt::esi;
22using namespace circt::esi::detail;
23using namespace circt::hw;
24using namespace circt::sv;
25
27 std::string typeID;
28 llvm::raw_string_ostream(typeID) << t;
29 return StringAttr::get(t.getContext(), typeID);
30}
31
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
75static 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
98StringAttr 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.
127ArrayAttr 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.
137HWModuleExternOp 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 = {
151 {{clk, getClockType(), ModulePort::Direction::Input}, argn++},
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.
175HWModuleExternOp
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
187 auto dataInType = hw::IntType::get(hw::ParamDeclRefAttr::get(
188 StringAttr::get(getContext(), "TO_HOST_SIZE_BITS"),
189 getIntegerType(32, false)));
190 PortInfo ports[] = {
191 {{clk, getClockType(), ModulePort::Direction::Input}, 0},
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
203HWModuleExternOp
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
215 auto dataInType = hw::IntType::get(hw::ParamDeclRefAttr::get(
216 StringAttr::get(getContext(), "FROM_HOST_SIZE_BITS"),
217 getIntegerType(32, false)));
218 PortInfo ports[] = {
219 {{clk, getClockType(), ModulePort::Direction::Input}, 0},
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.
234InterfaceOp 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
243InterfaceOp 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
260Type ESIHWBuilder::getClockType() { return seq::ClockType::get(getContext()); }
261
static void registerPasses()
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
hw::HWModuleExternOp declareCosimEndpointFromHostModule(Operation *symTable)
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.
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)
sv::InterfaceOp getOrConstructInterface(ChannelType)
Return the InterfaceType which corresponds to an ESI port type.
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.
ArrayAttr getStageParameterList(Attribute value)
Return a parameter list for the stage module with the specified value.
uint64_t getWidth(Type t)
Definition ESIPasses.cpp:32
StringAttr getTypeID(Type t)
Definition ESIPasses.cpp:26
void registerESIPasses()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
This holds the name, type, direction of a module's ports.