41 const std::vector<uint32_t> &widths,
bool read,
44int main(
int argc,
const char *argv[]) {
46 cli.description(
"Test an ESI system running the ESI tester image.");
47 cli.require_subcommand(1);
49 CLI::App *loopSub = cli.add_subcommand(
"loop",
"Loop indefinitely");
52 cli.add_subcommand(
"wait",
"Wait for a certain number of seconds");
53 uint32_t waitTime = 1;
54 waitSub->add_option(
"-t,--time", waitTime,
"Number of seconds to wait");
56 CLI::App *hostmemtestSub =
57 cli.add_subcommand(
"hostmem",
"Run the host memory test");
60 hostmemtestSub->add_flag(
"-w,--write", hmWrite,
61 "Enable host memory write test");
62 hostmemtestSub->add_flag(
"-r,--read", hmRead,
"Enable host memory read test");
64 CLI::App *dmatestSub = cli.add_subcommand(
"dma",
"Run the DMA test");
66 bool dmaWrite =
false;
67 dmatestSub->add_flag(
"-w,--write", dmaWrite,
"Enable dma write test");
68 dmatestSub->add_flag(
"-r,--read", dmaRead,
"Enable dma read test");
70 CLI::App *bandwidthSub =
71 cli.add_subcommand(
"bandwidth",
"Run the bandwidth test");
72 uint32_t xferCount = 1000;
73 bandwidthSub->add_option(
"-c,--count", xferCount,
74 "Number of transfers to perform");
75 bool bandwidthRead =
false;
76 bool bandwidthWrite =
false;
77 std::vector<uint32_t> widths = {32, 64, 128, 256, 384, 504, 512};
78 bandwidthSub->add_option(
"--widths", widths,
79 "Width of the transfers to perform (default: 32, "
80 "64, 128, 256, 384, 504, 512)");
81 bandwidthSub->add_flag(
"-w,--write", bandwidthWrite,
82 "Enable bandwidth write");
83 bandwidthSub->add_flag(
"-r,--read", bandwidthRead,
"Enable bandwidth read");
85 if (
int rc = cli.
esiParse(argc, argv))
87 if (!cli.get_help_ptr()->empty())
92 std::unique_ptr<AcceleratorConnection> acc = cli.
connect();
94 Manifest manifest(ctxt, info.getJsonManifest());
95 Accelerator *accel = manifest.buildAccelerator(*acc);
96 acc->getServiceThread()->addPoll(*accel);
101 std::this_thread::sleep_for(std::chrono::milliseconds(100));
103 }
else if (*waitSub) {
105 std::this_thread::sleep_for(std::chrono::seconds(waitTime));
106 }
else if (*hostmemtestSub) {
108 }
else if (*dmatestSub) {
109 dmaTest(acc.get(), accel, dmaRead, dmaWrite);
110 }
else if (*bandwidthSub) {
111 bandwidthTest(acc.get(), accel, xferCount, widths, bandwidthRead,
116 std::cout <<
"Exiting successfully\n";
118 }
catch (std::exception &e) {
119 ctxt.getLogger().error(
"esitester", e.what());
44int main(
int argc,
const char *argv[]) {
…}
126 auto f = ports.find(
AppID(
"PrintfExample"));
127 if (f != ports.end()) {
133 [&](std::string &subsystem, std::string &msg,
134 std::unique_ptr<std::map<std::string, std::any>> &details) {
135 subsystem =
"ESITESTER";
136 msg =
"Received PrintfExample message";
137 details = std::make_unique<std::map<std::string, std::any>>();
138 details->emplace(
"data", data);
140 std::cout <<
"PrintfExample: " << *data.as<uint32_t>() << std::endl;
145 std::cerr <<
"PrintfExample port is not a CallService::Callback"
148 std::cerr <<
"No PrintfExample port found" << std::endl;
155 void *devicePtr,
bool read,
bool write) {
156 std::cout <<
"Running hostmem test with width " << width << std::endl;
157 uint64_t *dataPtr =
static_cast<uint64_t *
>(region.
getPtr());
162 throw std::runtime_error(
"hostmem test failed. No readmem child found");
163 auto &readMemPorts = readMemChildIter->second->getPorts();
164 auto readMemPortIter = readMemPorts.find(
AppID(
"ReadMem"));
165 if (readMemPortIter == readMemPorts.end())
166 throw std::runtime_error(
"hostmem test failed. No ReadMem port found");
169 throw std::runtime_error(
"hostmem test failed. ReadMem port is not MMIO");
171 for (
size_t i = 0; i < 8; ++i) {
172 dataPtr[0] = 0x12345678 << i;
173 dataPtr[1] = 0xDEADBEEF << i;
175 readMem->write(8,
reinterpret_cast<uint64_t
>(devicePtr));
180 uint64_t expected = dataPtr[0];
182 expected &= ((1ull << width) - 1);
183 for (
int i = 0; i < 100; ++i) {
184 val = readMem->read(0);
187 std::this_thread::sleep_for(std::chrono::microseconds(100));
191 throw std::runtime_error(
"hostmem read test failed. Expected " +
200 auto check = [&](
bool print) {
202 for (
size_t i = 0; i < 8; ++i) {
204 std::cout <<
"dataPtr[" << i <<
"] = 0x" <<
esi::toHex(dataPtr[i])
206 if (i < (width + 63) / 64 && dataPtr[i] == 0xFFFFFFFFFFFFFFFFull)
214 throw std::runtime_error(
"hostmem test failed. No writemem child found");
215 auto &writeMemPorts = writeMemChildIter->second->getPorts();
216 auto writeMemPortIter = writeMemPorts.find(
AppID(
"WriteMem"));
217 if (writeMemPortIter == writeMemPorts.end())
218 throw std::runtime_error(
"hostmem test failed. No WriteMem port found");
222 throw std::runtime_error(
223 "hostmem test failed. WriteMem port is not MMIO");
225 for (
size_t i = 0, e = 8; i < e; ++i)
226 dataPtr[i] = 0xFFFFFFFFFFFFFFFFull;
231 writeMem->write(0,
reinterpret_cast<uint64_t
>(devicePtr));
233 for (
int i = 0; i < 100; ++i) {
236 std::this_thread::sleep_for(std::chrono::microseconds(100));
239 throw std::runtime_error(
"hostmem write test failed");
242 size_t widthInBytes = width / 8;
243 uint8_t *dataPtr8 =
reinterpret_cast<uint8_t *
>(region.
getPtr());
244 for (
size_t i = widthInBytes; i < 64; ++i) {
245 std::cout <<
"endcheck dataPtr8[" << i <<
"] = 0x"
247 if (dataPtr8[i] != 0xFF)
248 throw std::runtime_error(
249 "hostmem write test failed -- write went too far");
259 auto scratchRegion = hostmem->allocate(512, {});
260 uint64_t *dataPtr =
static_cast<uint64_t *
>(scratchRegion->getPtr());
261 for (
size_t i = 0; i < scratchRegion->getSize() / 8; ++i)
263 scratchRegion->flush();
265 for (
size_t width : {32, 64, 128, 256, 384, 504, 512})
266 hostmemTest(acc, *scratchRegion, width, scratchRegion->getDevicePtr(), read,
273 logger.
info(
"esitester",
274 "== Running DMA read test with width " + std::to_string(width));
277 {
AppID(
"tohostdmatest", width),
AppID(
"ToHostDMATest")}, lastPath);
279 throw std::runtime_error(
"dma read test failed. No tohostdmatest[" +
280 std::to_string(width) +
"] found");
283 throw std::runtime_error(
"dma read test failed. MMIO port is not MMIO");
290 size_t xferCount = 24;
293 toHostMMIO->write(0, xferCount);
294 for (
size_t i = 0; i < xferCount; ++i) {
297 uint64_t val = *data.as<uint64_t>();
299 throw std::runtime_error(
"dma read test failed. Out of order data");
302 logger.
debug(
"esitester",
303 "Cycle count [" + std::to_string(i) +
"] = 0x" + data.toHex());
306 logger.
info(
"esitester",
"== DMA read test complete");
312 logger.
info(
"esitester",
313 "== Running DMA write test with width " + std::to_string(width));
316 {
AppID(
"fromhostdmatest", width),
AppID(
"FromHostDMATest")}, lastPath);
317 if (!fromHostMMIOPort)
318 throw std::runtime_error(
"dma read test for " +
toString(width) +
319 " bits failed. No fromhostdmatest[" +
320 std::to_string(width) +
"] found");
323 throw std::runtime_error(
"dma write test for " +
toString(width) +
324 " bits failed. MMIO port is not MMIO");
327 {
AppID(
"fromhostdmatest", width),
AppID(
"in")}, lastPath);
329 throw std::runtime_error(
"dma write test for " +
toString(width) +
330 " bits failed. No out port found");
334 size_t xferCount = 24;
335 uint8_t *data =
new uint8_t[width];
336 for (
size_t i = 0; i < width / 8; ++i)
338 fromHostMMIO->read(8);
339 fromHostMMIO->write(0, xferCount);
340 for (
size_t i = 1; i < xferCount + 1; ++i) {
347 std::this_thread::sleep_for(std::chrono::milliseconds(10));
349 }
while (!successWrite && ++attempts < 100);
351 throw std::runtime_error(
"dma write test for " +
toString(width) +
352 " bits failed. Write failed");
353 uint64_t lastReadMMIO;
354 for (
size_t a = 0; a < 20; ++a) {
355 lastReadMMIO = fromHostMMIO->read(8);
356 if (lastReadMMIO == i)
358 std::this_thread::sleep_for(std::chrono::milliseconds(10));
360 throw std::runtime_error(
"dma write for " +
toString(width) +
361 " bits test failed. Read from MMIO failed");
366 logger.
info(
"esitester",
367 "== DMA write test for " +
toString(width) +
" bits complete");
374 for (
size_t width : {32, 64, 128, 256, 384, 504, 512})
377 }
catch (std::exception &e) {
379 std::cerr <<
"DMA write test for " << width
380 <<
"bits failed: " << e.what() << std::endl;
383 for (
size_t width : {32, 64, 128, 256, 384, 504, 512})
386 throw std::runtime_error(
"DMA test failed");
390 size_t width,
size_t xferCount) {
394 {
AppID(
"tohostdmatest", width),
AppID(
"ToHostDMATest")}, lastPath);
396 throw std::runtime_error(
"bandwidth test failed. No tohostdmatest[" +
397 std::to_string(width) +
"] found");
400 throw std::runtime_error(
"bandwidth test failed. MMIO port is not MMIO");
408 logger.
info(
"esitester",
"Starting bandwidth test read with " +
409 std::to_string(xferCount) +
" x " +
410 std::to_string(width) +
" bit transfers");
412 auto start = std::chrono::high_resolution_clock::now();
413 toHostMMIO->write(0, xferCount);
414 for (
size_t i = 0; i < xferCount; ++i) {
417 [i, &data](std::string &subsystem, std::string &msg,
418 std::unique_ptr<std::map<std::string, std::any>> &details) {
419 subsystem =
"esitester";
420 msg =
"Cycle count [" + std::to_string(i) +
"] = 0x" + data.toHex();
423 auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
424 std::chrono::high_resolution_clock::now() - start);
425 logger.
info(
"esitester",
426 " Bandwidth test: " + std::to_string(xferCount) +
" x " +
427 std::to_string(width) +
" bit transfers in " +
428 std::to_string(duration.count()) +
" microseconds");
429 logger.
info(
"esitester",
431 std::to_string((xferCount * (width / 8) * 1000000) /
432 duration.count() / 1024) +
437 size_t width,
size_t xferCount) {
441 {
AppID(
"fromhostdmatest", width),
AppID(
"FromHostDMATest")}, lastPath);
442 if (!fromHostMMIOPort)
443 throw std::runtime_error(
"bandwidth test failed. No tohostdmatest[" +
444 std::to_string(width) +
"] found");
447 throw std::runtime_error(
"bandwidth test failed. MMIO port is not MMIO");
450 {
AppID(
"fromhostdmatest", width),
AppID(
"in")}, lastPath);
455 logger.
info(
"esitester",
"Starting bandwidth write read with " +
456 std::to_string(xferCount) +
" x " +
457 std::to_string(width) +
" bit transfers");
458 std::vector<uint8_t> dataVec(width / 8);
459 for (
size_t i = 0; i < width / 8; ++i)
462 auto start = std::chrono::high_resolution_clock::now();
463 fromHostMMIO->write(0, xferCount);
464 for (
size_t i = 0; i < xferCount; ++i) {
467 [i, &data](std::string &subsystem, std::string &msg,
468 std::unique_ptr<std::map<std::string, std::any>> &details) {
469 subsystem =
"esitester";
470 msg =
"Cycle count [" + std::to_string(i) +
"] = 0x" + data.toHex();
473 auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
474 std::chrono::high_resolution_clock::now() - start);
475 logger.
info(
"esitester",
476 " Bandwidth test: " + std::to_string(xferCount) +
" x " +
477 std::to_string(width) +
" bit transfers in " +
478 std::to_string(duration.count()) +
" microseconds");
479 logger.
info(
"esitester",
481 std::to_string((xferCount * (width / 8) * 1000000) /
482 duration.count() / 1024) +
488 const std::vector<uint32_t> &widths,
bool read,
491 for (
size_t width : widths)
494 for (
size_t width : widths)
assert(baseType &&"element must be base type")
static void print(TypedAttr val, llvm::raw_ostream &os)
static void writePort(uint16_t port)
Write the port number to a file.
Abstract class representing a connection to an accelerator.
ServiceClass * getService(AppIDPath id={}, std::string implName={}, ServiceImplDetails details={}, HWClientDetails clients={})
Get a typed reference to a particular service type.
Logger & getLogger() const
Top level accelerator class.
Services provide connections to 'bundles' – collections of named, unidirectional communication channe...
T * getAs() const
Cast this Bundle port to a subclass which is actually useful.
ReadChannelPort & getRawRead(const std::string &name) const
WriteChannelPort & getRawWrite(const std::string &name) const
Get access to the raw byte streams of a channel.
Common options and code for ESI runtime tools.
Context & getContext()
Get the context.
std::unique_ptr< AcceleratorConnection > connect()
Connect to the accelerator using the specified backend and connection.
int esiParse(int argc, const char **argv)
Run the parser.
AcceleratorConnections, Accelerators, and Manifests must all share a context.
BundlePort * resolvePort(const AppIDPath &path, AppIDPath &lastLookup) const
Attempt to resolve a path to a port.
const std::map< AppID, BundlePort & > & getPorts() const
Access the module's ports by ID.
const std::map< AppID, Instance * > & getChildren() const
Access the module's children by ID.
virtual void info(const std::string &subsystem, const std::string &msg, const std::map< std::string, std::any > *details=nullptr)
Report an informational message.
void debug(const std::string &subsystem, const std::string &msg, const std::map< std::string, std::any > *details=nullptr)
Report a debug message.
Class to parse a manifest.
A logical chunk of data representing serialized data.
A ChannelPort which reads data from the accelerator.
virtual void connect(std::function< bool(MessageData)> callback, std::optional< unsigned > bufferSize=std::nullopt)
virtual void disconnect() override
virtual void read(MessageData &outData)
Specify a buffer to read into.
A ChannelPort which sends data to the accelerator.
virtual void write(const MessageData &)=0
A very basic blocking write API.
virtual void connect(std::optional< unsigned > bufferSize=std::nullopt) override
Set up a connection to the accelerator.
A function call which gets attached to a service port.
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.
virtual void start()
In cases where necessary, enable host memory services.
A "slice" of some parent MMIO space.
Information about the Accelerator system.
static void bandwidthReadTest(AcceleratorConnection *conn, Accelerator *acc, size_t width, size_t xferCount)
static void dmaWriteTest(AcceleratorConnection *conn, Accelerator *acc, size_t width)
static void bandwidthWriteTest(AcceleratorConnection *conn, Accelerator *acc, size_t width, size_t xferCount)
static void registerCallbacks(AcceleratorConnection *, Accelerator *)
static void bandwidthTest(AcceleratorConnection *, Accelerator *, uint32_t xferCount, const std::vector< uint32_t > &widths, bool read, bool write)
static void dmaReadTest(AcceleratorConnection *conn, Accelerator *acc, size_t width)
static void hostmemTest(AcceleratorConnection *, Accelerator *, bool read, bool write)
int main(int argc, const char *argv[])
static void dmaTest(AcceleratorConnection *, Accelerator *, bool read, bool write)
std::string toString(const std::any &a)
'Stringify' a std::any. This is used to log std::any values by some loggers.
std::string toHex(void *val)
RAII memory region for host memory.
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.