CIRCT 21.0.0git
Loading...
Searching...
No Matches
esitester.cpp
Go to the documentation of this file.
1//===- esitester.cpp - ESI accelerator test/example tool ------------------===//
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// DO NOT EDIT!
10// This file is distributed as part of an ESI runtime package. The source for
11// this file should always be modified within CIRCT
12// (lib/dialect/ESI/runtime/cpp/tools/esitester.cpp).
13//
14//===----------------------------------------------------------------------===//
15//
16// This application isn't a utility so much as a test driver for an ESI system.
17// It is also useful as an example of how to use the ESI C++ API. esiquery.cpp
18// is also useful as an example.
19//
20//===----------------------------------------------------------------------===//
21
22#include "esi/Accelerator.h"
23#include "esi/Manifest.h"
24#include "esi/Services.h"
25
26#include <iostream>
27#include <map>
28#include <stdexcept>
29
30using namespace esi;
31
33static void dmaTest(AcceleratorConnection *, Accelerator *, bool read,
34 bool write);
35
36int main(int argc, const char *argv[]) {
37 // TODO: find a command line parser library rather than doing this by hand.
38 if (argc < 3) {
39 std::cerr << "Expected usage: " << argv[0]
40 << " <backend> <connection specifier> [command]" << std::endl;
41 return -1;
42 }
43
44 const char *backend = argv[1];
45 const char *conn = argv[2];
46 std::string cmd;
47 if (argc > 3)
48 cmd = argv[3];
49
50 try {
51 Context ctxt = Context::withLogger<StreamLogger>(Logger::Level::Debug);
52 std::unique_ptr<AcceleratorConnection> acc = ctxt.connect(backend, conn);
53 const auto &info = *acc->getService<services::SysInfo>();
54 Manifest manifest(ctxt, info.getJsonManifest());
55 Accelerator *accel = manifest.buildAccelerator(*acc);
56 acc->getServiceThread()->addPoll(*accel);
57
58 if (cmd == "loop") {
59 registerCallbacks(acc.get(), accel);
60 while (true) {
61 std::this_thread::sleep_for(std::chrono::milliseconds(100));
62 }
63 } else if (cmd == "wait") {
64 registerCallbacks(acc.get(), accel);
65 std::this_thread::sleep_for(std::chrono::seconds(1));
66 } else if (cmd == "dmatest") {
67 dmaTest(acc.get(), accel, true, true);
68 } else if (cmd == "dmareadtest") {
69 dmaTest(acc.get(), accel, true, false);
70 } else if (cmd == "dmawritetest") {
71 dmaTest(acc.get(), accel, false, true);
72 }
73
74 acc->disconnect();
75 std::cout << "Exiting successfully\n";
76 return 0;
77
78 } catch (std::exception &e) {
79 std::cout << "Error: " << e.what() << std::endl;
80 return -1;
81 }
82}
83
85 auto ports = accel->getPorts();
86 auto f = ports.find(AppID("PrintfExample"));
87 if (f != ports.end()) {
88 auto callPort = f->second.getAs<services::CallService::Callback>();
89 if (callPort)
90 callPort->connect(
91 [conn](const MessageData &data) -> MessageData {
92 conn->getLogger().debug(
93 [&](std::string &subsystem, std::string &msg,
94 std::unique_ptr<std::map<std::string, std::any>> &details) {
95 subsystem = "ESITESTER";
96 msg = "Received PrintfExample message";
97 details = std::make_unique<std::map<std::string, std::any>>();
98 details->emplace("data", data);
99 });
100 std::cout << "PrintfExample: " << *data.as<uint32_t>() << std::endl;
101 return MessageData();
102 },
103 true);
104 else
105 std::cerr << "PrintfExample port is not a CallService::Callback"
106 << std::endl;
107 } else {
108 std::cerr << "No PrintfExample port found" << std::endl;
109 }
110}
111
112/// Initiate a test read.
114 uint32_t width, void *devicePtr, bool read, bool write) {
115 std::cout << "Running DMA test with width " << width << std::endl;
116 uint64_t *dataPtr = static_cast<uint64_t *>(region.getPtr());
117
118 if (read) {
119 auto readMemChildIter = acc->getChildren().find(AppID("readmem", width));
120 if (readMemChildIter == acc->getChildren().end())
121 throw std::runtime_error("DMA test failed. No readmem child found");
122 auto &readMemPorts = readMemChildIter->second->getPorts();
123 auto readMemPortIter = readMemPorts.find(AppID("ReadMem"));
124 if (readMemPortIter == readMemPorts.end())
125 throw std::runtime_error("DMA test failed. No ReadMem port found");
126 auto *readMem = readMemPortIter->second.getAs<services::MMIO::MMIORegion>();
127 if (!readMem)
128 throw std::runtime_error("DMA test failed. ReadMem port is not MMIO");
129
130 for (size_t i = 0; i < 8; ++i) {
131 dataPtr[0] = 0x12345678 << i;
132 dataPtr[1] = 0xDEADBEEF << i;
133 region.flush();
134 readMem->write(8, reinterpret_cast<uint64_t>(devicePtr));
135
136 // Wait for the accelerator to read the correct value. Timeout and fail
137 // after 10ms.
138 uint64_t val = 0;
139 uint64_t expected = dataPtr[0];
140 if (width < 64)
141 expected &= ((1ull << width) - 1);
142 for (int i = 0; i < 100; ++i) {
143 val = readMem->read(0);
144 if (val == expected)
145 break;
146 std::this_thread::sleep_for(std::chrono::microseconds(100));
147 }
148
149 if (val != expected)
150 throw std::runtime_error("DMA read test failed. Expected " +
151 esi::toHex(expected) + ", got " +
152 esi::toHex(val));
153 }
154 }
155
156 // Initiate a test write.
157 if (write) {
158 assert(width % 8 == 0);
159 auto check = [&](bool print) {
160 bool ret = true;
161 for (size_t i = 0; i < 8; ++i) {
162 if (print)
163 std::cout << "dataPtr[" << i << "] = 0x" << esi::toHex(dataPtr[i])
164 << std::endl;
165 if (i < (width + 63) / 64 && dataPtr[i] == 0xFFFFFFFFFFFFFFFFull)
166 ret = false;
167 }
168 return ret;
169 };
170
171 auto writeMemChildIter = acc->getChildren().find(AppID("writemem", width));
172 if (writeMemChildIter == acc->getChildren().end())
173 throw std::runtime_error("DMA test failed. No writemem child found");
174 auto &writeMemPorts = writeMemChildIter->second->getPorts();
175 auto writeMemPortIter = writeMemPorts.find(AppID("WriteMem"));
176 if (writeMemPortIter == writeMemPorts.end())
177 throw std::runtime_error("DMA test failed. No WriteMem port found");
178 auto *writeMem =
179 writeMemPortIter->second.getAs<services::MMIO::MMIORegion>();
180 if (!writeMem)
181 throw std::runtime_error("DMA test failed. WriteMem port is not MMIO");
182
183 for (size_t i = 0, e = 8; i < e; ++i)
184 dataPtr[i] = 0xFFFFFFFFFFFFFFFFull;
185 region.flush();
186
187 // Command the accelerator to write to 'devicePtr', the pointer which the
188 // device should use for 'dataPtr'.
189 writeMem->write(0, reinterpret_cast<uint64_t>(devicePtr));
190 // Wait for the accelerator to write. Timeout and fail after 10ms.
191 for (int i = 0; i < 100; ++i) {
192 if (check(false))
193 break;
194 std::this_thread::sleep_for(std::chrono::microseconds(100));
195 }
196 if (!check(true))
197 throw std::runtime_error("DMA write test failed");
198
199 // Check that the accelerator didn't write too far.
200 size_t widthInBytes = width / 8;
201 uint8_t *dataPtr8 = reinterpret_cast<uint8_t *>(region.getPtr());
202 for (size_t i = widthInBytes; i < 64; ++i) {
203 std::cout << "endcheck dataPtr8[" << i << "] = 0x"
204 << esi::toHex(dataPtr8[i]) << std::endl;
205 if (dataPtr8[i] != 0xFF)
206 throw std::runtime_error("DMA write test failed -- write went too far");
207 }
208 }
209}
210
211void dmaTest(AcceleratorConnection *conn, Accelerator *acc, bool read,
212 bool write) {
213 // Enable the host memory service.
214 auto hostmem = conn->getService<services::HostMem>();
215 hostmem->start();
216 auto scratchRegion = hostmem->allocate(/*size(bytes)=*/64, /*memOpts=*/{});
217 uint64_t *dataPtr = static_cast<uint64_t *>(scratchRegion->getPtr());
218 for (size_t i = 0; i < 8; ++i)
219 dataPtr[i] = 0;
220 scratchRegion->flush();
221
222 dmaTest(acc, *scratchRegion, 32, scratchRegion->getDevicePtr(), read, write);
223 dmaTest(acc, *scratchRegion, 64, scratchRegion->getDevicePtr(), read, write);
224 dmaTest(acc, *scratchRegion, 96, scratchRegion->getDevicePtr(), read, write);
225}
assert(baseType &&"element must be base type")
static void print(TypedAttr val, llvm::raw_ostream &os)
Abstract class representing a connection to an accelerator.
Definition Accelerator.h:79
ServiceClass * getService(AppIDPath id={}, std::string implName={}, ServiceImplDetails details={}, HWClientDetails clients={})
Get a typed reference to a particular service type.
Logger & getLogger() const
Definition Accelerator.h:84
Top level accelerator class.
Definition Accelerator.h:60
AcceleratorConnections, Accelerators, and Manifests must all share a context.
Definition Context.h:31
const std::map< AppID, const BundlePort & > & getPorts() const
Access the module's ports by ID.
Definition Design.h:76
const std::map< AppID, Instance * > & getChildren() const
Access the module's children by ID.
Definition Design.h:67
void debug(const std::string &subsystem, const std::string &msg, const std::map< std::string, std::any > *details=nullptr)
Report a debug message.
Definition Logging.h:79
Class to parse a manifest.
Definition Manifest.h:39
A logical chunk of data representing serialized data.
Definition Common.h:103
A function call which gets attached to a service port.
Definition Services.h:304
void connect(std::function< MessageData(const MessageData &)> callback, bool quick=false)
Connect a callback to code which will be executed when the accelerator invokes the callback.
Definition Services.cpp:244
virtual void start()
In cases where necessary, enable host memory services.
Definition Services.h:232
A "slice" of some parent MMIO space.
Definition Services.h:159
Information about the Accelerator system.
Definition Services.h:100
static void dmaTest(AcceleratorConnection *, Accelerator *, bool read, bool write)
static void registerCallbacks(AcceleratorConnection *, Accelerator *)
Definition esitester.cpp:84
int main(int argc, const char *argv[])
Definition esitester.cpp:36
Definition esi.py:1
std::string toHex(void *val)
Definition Common.cpp:37
RAII memory region for host memory.
Definition Services.h:208
virtual void * getPtr() const =0
Get a pointer to the host memory.
virtual void flush()
Flush the memory region to ensure that the device sees the latest contents.
Definition Services.h:222