CIRCT 24.0.0git
Loading...
Searching...
No Matches
ESIStdServices.cpp
Go to the documentation of this file.
1//===- ESIStdServices.cpp - ESI standard services -------------------------===//
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
14
15#include "mlir/IR/BuiltinTypes.h"
16#include "mlir/IR/ImplicitLocOpBuilder.h"
17
18#include <map>
19#include <memory>
20
21using namespace circt;
22using namespace circt::esi;
23
24/// Utility function to create a req/resp pair bundle service port.
25static ServicePortInfo createReqResp(StringAttr sym, Twine name,
26 StringRef reqName, Type reqType,
27 StringRef respName, Type respType) {
28 assert(reqType && respType);
29 auto *ctxt = reqType ? reqType.getContext() : respType.getContext();
30 auto bundle = ChannelBundleType::get(
31 ctxt,
32 {BundledChannel{StringAttr::get(ctxt, reqName), ChannelDirection::from,
33 ChannelType::get(ctxt, reqType)},
34 BundledChannel{StringAttr::get(ctxt, respName), ChannelDirection::to,
35 ChannelType::get(ctxt, respType)}},
36 /*resettable=false*/ UnitAttr());
37 return {hw::InnerRefAttr::get(sym, StringAttr::get(ctxt, name)), bundle};
38}
39
40void ChannelServiceDeclOp::getPortList(
41 SmallVectorImpl<ServicePortInfo> &ports) {
42 auto *ctxt = getContext();
43 ports.push_back(ServicePortInfo{
44 hw::InnerRefAttr::get(getSymNameAttr(), StringAttr::get(ctxt, "to_host")),
45 ChannelBundleType::get(
46 ctxt,
47 {BundledChannel{StringAttr::get(ctxt, "data"), ChannelDirection::from,
48 ChannelType::get(ctxt, AnyType::get(ctxt))}},
49 /*resettable=*/UnitAttr())});
50 ports.push_back(ServicePortInfo{
51 hw::InnerRefAttr::get(getSymNameAttr(),
52 StringAttr::get(ctxt, "from_host")),
53 ChannelBundleType::get(
54 ctxt,
55 {BundledChannel{StringAttr::get(ctxt, "data"), ChannelDirection::to,
56 ChannelType::get(ctxt, AnyType::get(ctxt))}},
57 /*resettable=*/UnitAttr())});
58}
59
60ServicePortInfo RandomAccessMemoryDeclOp::writePortInfo() {
61 auto *ctxt = getContext();
62 auto addressType =
63 IntegerType::get(ctxt, llvm::Log2_64_Ceil(getDepth()),
64 IntegerType::SignednessSemantics::Unsigned);
65
66 // Write port
67 hw::StructType writeType = hw::StructType::get(
68 ctxt,
69 {hw::StructType::FieldInfo{StringAttr::get(ctxt, "address"), addressType},
70 hw::StructType::FieldInfo{StringAttr::get(ctxt, "data"),
71 getInnerType()}});
72 return createReqResp(getSymNameAttr(), "write", "req", writeType, "ack",
73 IntegerType::get(ctxt, 0));
74}
75
76ServicePortInfo RandomAccessMemoryDeclOp::readPortInfo() {
77 auto *ctxt = getContext();
78 auto addressType =
79 IntegerType::get(ctxt, llvm::Log2_64_Ceil(getDepth()),
80 IntegerType::SignednessSemantics::Unsigned);
81
82 return createReqResp(getSymNameAttr(), "read", "address", addressType, "data",
83 getInnerType());
84}
85
86void RandomAccessMemoryDeclOp::getPortList(
87 SmallVectorImpl<ServicePortInfo> &ports) {
88 ports.push_back(writePortInfo());
89 ports.push_back(readPortInfo());
90}
91
92void CallServiceDeclOp::getPortList(SmallVectorImpl<ServicePortInfo> &ports) {
93 auto *ctxt = getContext();
94 ports.push_back(ServicePortInfo{
95 hw::InnerRefAttr::get(getSymNameAttr(), StringAttr::get(ctxt, "call")),
96 ChannelBundleType::get(
97 ctxt,
98 {BundledChannel{StringAttr::get(ctxt, "arg"), ChannelDirection::from,
99 ChannelType::get(ctxt, AnyType::get(ctxt))},
100 BundledChannel{StringAttr::get(ctxt, "result"), ChannelDirection::to,
101 ChannelType::get(ctxt, AnyType::get(ctxt))}},
102 /*resettable=*/UnitAttr())});
103}
104
105void FuncServiceDeclOp::getPortList(SmallVectorImpl<ServicePortInfo> &ports) {
106 auto *ctxt = getContext();
107 ports.push_back(ServicePortInfo{
108 hw::InnerRefAttr::get(getSymNameAttr(), StringAttr::get(ctxt, "call")),
109 ChannelBundleType::get(
110 ctxt,
111 {BundledChannel{StringAttr::get(ctxt, "arg"), ChannelDirection::to,
112 ChannelType::get(ctxt, AnyType::get(ctxt))},
113 BundledChannel{StringAttr::get(ctxt, "result"),
114 ChannelDirection::from,
115 ChannelType::get(ctxt, AnyType::get(ctxt))}},
116 /*resettable=*/UnitAttr())});
117}
118
119void MMIOServiceDeclOp::getPortList(SmallVectorImpl<ServicePortInfo> &ports) {
120 auto *ctxt = getContext();
121 // Read only port.
122 ports.push_back(ServicePortInfo{
123 hw::InnerRefAttr::get(getSymNameAttr(), StringAttr::get(ctxt, "read")),
124 ChannelBundleType::get(
125 ctxt,
127 StringAttr::get(ctxt, "offset"), ChannelDirection::to,
128 ChannelType::get(
129 ctxt,
130 IntegerType::get(
131 ctxt, 32, IntegerType::SignednessSemantics::Unsigned))},
132 BundledChannel{StringAttr::get(ctxt, "data"), ChannelDirection::from,
133 ChannelType::get(ctxt, IntegerType::get(ctxt, 64))}},
134 /*resettable=*/UnitAttr())});
135 // Read-write port.
136 auto cmdType = hw::StructType::get(
137 ctxt, {
138 hw::StructType::FieldInfo{StringAttr::get(ctxt, "write"),
139 IntegerType::get(ctxt, 1)},
140 hw::StructType::FieldInfo{
141 StringAttr::get(ctxt, "offset"),
142 IntegerType::get(
143 ctxt, 32, IntegerType::SignednessSemantics::Unsigned)},
144 hw::StructType::FieldInfo{StringAttr::get(ctxt, "data"),
145 IntegerType::get(ctxt, 64)},
146 });
147 ports.push_back(ServicePortInfo{
148 hw::InnerRefAttr::get(getSymNameAttr(),
149 StringAttr::get(ctxt, "read_write")),
150 ChannelBundleType::get(
151 ctxt,
152 {BundledChannel{StringAttr::get(ctxt, "cmd"), ChannelDirection::to,
153 ChannelType::get(ctxt, cmdType)},
154 BundledChannel{StringAttr::get(ctxt, "data"), ChannelDirection::from,
155 ChannelType::get(ctxt, IntegerType::get(ctxt, 64))}},
156 /*resettable=*/UnitAttr())});
157}
158
159ServicePortInfo HostMemServiceDeclOp::writePortInfo() {
160 auto *ctxt = getContext();
161
162 // Unified write port. The request is 'AnyType' so a single connection can be
163 // either a single-message write (struct{address, tag, data}) or a burst/list
164 // write (a window over struct{address, tag, data: list}, framed at the engine
165 // width); the concrete request type is supplied per-connection. Responds with
166 // an 'ackTag' per written element.
167 return createReqResp(
168 getSymNameAttr(), "write", "req", AnyType::get(ctxt), "ackTag",
169 IntegerType::get(ctxt, 8, IntegerType::SignednessSemantics::Unsigned));
170}
171
172ServicePortInfo HostMemServiceDeclOp::readPortInfo() {
173 auto *ctxt = getContext();
174 auto addressType =
175 IntegerType::get(ctxt, 64, IntegerType::SignednessSemantics::Unsigned);
176
177 // Single-message read: request struct{address, tag}, response struct{tag,
178 // data}. The client supplies the concrete 'data' type per-connection.
179 hw::StructType readReqType = hw::StructType::get(
180 ctxt, {
181 hw::StructType::FieldInfo{StringAttr::get(ctxt, "address"),
182 addressType},
183 hw::StructType::FieldInfo{
184 StringAttr::get(ctxt, "tag"),
185 IntegerType::get(
186 ctxt, 8, IntegerType::SignednessSemantics::Unsigned)},
187 });
188 hw::StructType readRespType = hw::StructType::get(
189 ctxt, {
190 hw::StructType::FieldInfo{
191 StringAttr::get(ctxt, "tag"),
192 IntegerType::get(
193 ctxt, 8, IntegerType::SignednessSemantics::Unsigned)},
194 hw::StructType::FieldInfo{StringAttr::get(ctxt, "data"),
195 AnyType::get(ctxt)},
196 });
197 return createReqResp(getSymNameAttr(), "read", "req", readReqType, "resp",
198 readRespType);
199}
200
201ServicePortInfo HostMemServiceDeclOp::readListPortInfo() {
202 auto *ctxt = getContext();
203 auto ui64 =
204 IntegerType::get(ctxt, 64, IntegerType::SignednessSemantics::Unsigned);
205 auto ui8 =
206 IntegerType::get(ctxt, 8, IntegerType::SignednessSemantics::Unsigned);
207
208 // Burst (list) read: read 'length' list items starting at 'address' and
209 // return them as a list. To receive the list, the client requests a
210 // *windowed channel* for the response (a window over the returned list,
211 // framed at the engine width), so the concrete response type is supplied
212 // per-connection -- hence 'AnyType' for 'resp'. 'length' is likewise
213 // 'AnyType' so the client may supply an unsigned integer of any width. Since
214 // 'AnyType' cannot itself express "unsigned integer of any width",
215 // 'verifyRequest' (below) rejects a non-unsigned-integer 'length' at
216 // op-verification time.
217 hw::StructType readReqType = hw::StructType::get(
218 ctxt, {hw::StructType::FieldInfo{StringAttr::get(ctxt, "address"), ui64},
219 hw::StructType::FieldInfo{StringAttr::get(ctxt, "tag"), ui8},
220 hw::StructType::FieldInfo{StringAttr::get(ctxt, "length"),
221 AnyType::get(ctxt)}});
222 return createReqResp(getSymNameAttr(), "read_list", "req", readReqType,
223 "resp", AnyType::get(ctxt));
224}
225
226void HostMemServiceDeclOp::getPortList(
227 SmallVectorImpl<ServicePortInfo> &ports) {
228 ports.push_back(writePortInfo());
229 ports.push_back(readPortInfo());
230 ports.push_back(readListPortInfo());
231}
232
233LogicalResult HostMemServiceDeclOp::verifyRequest(const ServicePortInfo &port,
234 ChannelBundleType reqType,
235 Operation *reqOp) {
236 // Only the 'read_list' port constrains 'length'. Its request struct declares
237 // 'length' as 'AnyType' (so the generic type match accepts any bit width),
238 // but here we additionally require it to be an unsigned integer -- the
239 // constraint 'AnyType' alone cannot express.
240 if (port.port.getName().getValue() != "read_list")
241 return success();
242
243 for (BundledChannel ch : reqType.getChannels()) {
244 if (ch.name.getValue() != "req")
245 continue;
246 auto structType = dyn_cast<hw::StructType>(ch.type.getInner());
247 if (!structType)
248 break;
249 Type lengthType = structType.getFieldType("length");
250 if (!lengthType)
251 break;
252 if (auto intType = dyn_cast<IntegerType>(lengthType);
253 intType && intType.isUnsigned())
254 return success();
255 return reqOp->emitOpError()
256 << "'read_list' request 'length' must be an unsigned integer, got "
257 << lengthType;
258 }
259 return success();
260}
261
262void TelemetryServiceDeclOp::getPortList(
263 SmallVectorImpl<ServicePortInfo> &ports) {
264 auto *ctxt = getContext();
265 ports.push_back(ServicePortInfo{
266 hw::InnerRefAttr::get(getSymNameAttr(), StringAttr::get(ctxt, "report")),
267 ChannelBundleType::get(
268 ctxt,
269 {BundledChannel{StringAttr::get(ctxt, "get"), ChannelDirection::to,
270 ChannelType::get(ctxt, IntegerType::get(ctxt, 0))},
271 BundledChannel{StringAttr::get(ctxt, "data"), ChannelDirection::from,
272 ChannelType::get(ctxt, AnyType::get(ctxt))}},
273 /*resettable=*/UnitAttr())});
274}
assert(baseType &&"element must be base type")
static ServicePortInfo createReqResp(StringAttr sym, Twine name, StringRef reqName, Type reqType, StringRef respName, Type respType)
Utility function to create a req/resp pair bundle service port.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Describes a service port.
Definition ESIOps.h:38
hw::InnerRefAttr port
Definition ESIOps.h:39