Skip to content

Commit e8681eb

Browse files
authored
[ESI][Runtime] Create a 'trace' accelerator backend (#6396)
Trace recording and playback could be very useful. Could also be used for design introspection and runtime testing (without cosim). This is the start of that system.
1 parent 3fea5df commit e8681eb

File tree

7 files changed

+221
-48
lines changed

7 files changed

+221
-48
lines changed

include/circt/Dialect/ESI/ESIPasses.td

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ def ESIBuildManifest : Pass<"esi-build-manifest", "mlir::ModuleOp"> {
4040
let constructor = "circt::esi::createESIBuildManifestPass()";
4141
let dependentDialects = ["circt::hw::HWDialect", "circt::sv::SVDialect"];
4242
let options = [
43-
Option<"toFile", "to-file", "std::string",
44-
"", "Write the manifest JSON directly to this file">,
4543
Option<"top", "top", "std::string",
4644
"", "Root module of the instance hierarchy">
4745
];

integration_test/Dialect/ESI/runtime/loopback.mlir

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// RUN: circt-opt %s --esi-connect-services --esi-appid-hier=top=top --esi-build-manifest=top=top --esi-clean-metadata > %t4.mlir
44
// RUN: circt-opt %t4.mlir --lower-esi-to-physical --lower-esi-bundles --lower-esi-ports --lower-esi-to-hw=platform=cosim --lower-seq-to-sv --export-split-verilog -o %t3.mlir
55
// RUN: cd ..
6+
// RUN: %python %s.py trace w:%t6/esi_system_manifest.json
67
// RUN: esi-cosim-runner.py --exec %s.py %t6/*.sv
78

89
!sendI8 = !esi.bundle<[!esi.channel<i8> to "send"]>

lib/Dialect/ESI/Passes/ESIBuildManifest.cpp

Lines changed: 21 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,14 @@ void ESIBuildManifestPass::runOnOperation() {
8888
// JSONify the manifest.
8989
std::string jsonManifest = json();
9090

91-
// Append a verbatim with the manifest to the end of the module.
92-
OpBuilder b = OpBuilder::atBlockEnd(&mod->getRegion(0).getBlocks().front());
93-
auto verbatim = b.create<sv::VerbatimOp>(b.getUnknownLoc(),
94-
StringAttr::get(ctxt, jsonManifest));
95-
auto outputFileAttr =
96-
hw::OutputFileAttr::getFromFilename(ctxt, "esi_system_manifest.json");
97-
verbatim->setAttr("output_file", outputFileAttr);
91+
std::error_code ec;
92+
llvm::raw_fd_ostream os("esi_system_manifest.json", ec);
93+
if (ec) {
94+
mod->emitError() << "Failed to open file for writing: " << ec.message();
95+
signalPassFailure();
96+
} else {
97+
os << jsonManifest << "\n";
98+
}
9899

99100
// If zlib is available, compress the manifest and append it to the module.
100101
SmallVector<uint8_t, 10 * 1024> compressedManifest;
@@ -104,19 +105,19 @@ void ESIBuildManifestPass::runOnOperation() {
104105
ArrayRef((uint8_t *)jsonManifest.data(), jsonManifest.length()),
105106
compressedManifest, llvm::compression::zlib::BestSizeCompression);
106107

107-
// Append a verbatim with the compressed manifest to the end of the module.
108-
auto compressedVerbatim = b.create<sv::VerbatimOp>(
109-
b.getUnknownLoc(),
110-
StringAttr::get(ctxt, StringRef((char *)compressedManifest.data(),
111-
compressedManifest.size())));
112-
auto compressedOutputFileAttr = hw::OutputFileAttr::getFromFilename(
113-
ctxt, "esi_system_manifest.json.zlib");
114-
compressedVerbatim->setAttr("output_file", compressedOutputFileAttr);
115-
116-
b.setInsertionPoint(symCache.getDefinition(appidRoot.getTopModuleRefAttr())
117-
->getRegion(0)
118-
.front()
119-
.getTerminator());
108+
llvm::raw_fd_ostream bos("esi_system_manifest.json.zlib", ec);
109+
if (ec) {
110+
mod->emitError() << "Failed to open compressed file for writing: "
111+
<< ec.message();
112+
signalPassFailure();
113+
} else {
114+
bos.write((char *)compressedManifest.data(), compressedManifest.size());
115+
}
116+
117+
OpBuilder b(symCache.getDefinition(appidRoot.getTopModuleRefAttr())
118+
->getRegion(0)
119+
.front()
120+
.getTerminator());
120121
b.create<CompressedManifestOp>(
121122
b.getUnknownLoc(),
122123
BlobAttr::get(ctxt, ArrayRef<char>(reinterpret_cast<char *>(
@@ -126,30 +127,6 @@ void ESIBuildManifestPass::runOnOperation() {
126127
mod->emitError() << "zlib not available but required for manifest support";
127128
signalPassFailure();
128129
}
129-
130-
// If directed, write the manifest to a file. Mostly for debugging.
131-
if (!toFile.empty()) {
132-
std::error_code ec;
133-
llvm::raw_fd_ostream os(toFile, ec);
134-
if (ec) {
135-
mod->emitError() << "Failed to open file for writing: " << ec.message();
136-
signalPassFailure();
137-
} else {
138-
os << jsonManifest << "\n";
139-
}
140-
141-
// If the compressed manifest is available, output it also.
142-
if (!compressedManifest.empty()) {
143-
llvm::raw_fd_ostream bos(toFile + ".zlib", ec);
144-
if (ec) {
145-
mod->emitError() << "Failed to open compressed file for writing: "
146-
<< ec.message();
147-
signalPassFailure();
148-
} else {
149-
bos.write((char *)compressedManifest.data(), compressedManifest.size());
150-
}
151-
}
152-
}
153130
}
154131

155132
void ESIBuildManifestPass::emitNode(llvm::json::OStream &j,

lib/Dialect/ESI/runtime/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ set(ESIRuntimeSources
4646
cpp/lib/Accelerator.cpp
4747
cpp/lib/Manifest.cpp
4848
cpp/lib/StdServices.cpp
49+
50+
cpp/lib/backends/Trace.cpp
4951
)
5052
set(ESIRuntimeLinkLibraries
5153
ZLIB::ZLIB
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//===- Trace.h - ESI trace backend ------------------------------*- 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+
// This is a specialization of the ESI C++ API (backend) for trace-based
10+
// Accelerator interactions. This means that it will have the capability to read
11+
// trace files recorded from interactions with an actual connection. It also has
12+
// a mode wherein it will write to a file (for sends) and produce random data
13+
// (for receives). Both modes are intended for debugging without a simulation.
14+
//
15+
// DO NOT EDIT!
16+
// This file is distributed as part of an ESI package. The source for this file
17+
// should always be modified within CIRCT (lib/dialect/ESI/runtime/cpp/).
18+
//
19+
//===----------------------------------------------------------------------===//
20+
21+
// NOLINTNEXTLINE(llvm-header-guard)
22+
#ifndef ESI_BACKENDS_COSIM_H
23+
#define ESI_BACKENDS_COSIM_H
24+
25+
#include "esi/Accelerator.h"
26+
27+
#include <filesystem>
28+
#include <memory>
29+
30+
namespace esi {
31+
namespace backends {
32+
namespace trace {
33+
34+
/// Connect to an ESI simulation.
35+
class TraceAccelerator : public esi::Accelerator {
36+
public:
37+
enum Mode {
38+
// Write data sent to the accelerator to the trace file. Produce random
39+
// garbage data for reads from the accelerator.
40+
Write,
41+
42+
// Sent data to the accelerator is compared against the trace file's record.
43+
// Data read from the accelerator is read from the trace file.
44+
// TODO: Full trace mode not yet supported.
45+
// Read
46+
};
47+
48+
/// Create a trace-based accelerator backend.
49+
/// \param mode The mode of operation. See Mode.
50+
/// \param manifestJson The path to the manifest JSON file.
51+
/// \param traceFile The path to the trace file. For 'Write' mode, this file
52+
/// is opened for writing. For 'Read' mode, this file is opened for reading.
53+
TraceAccelerator(Mode mode, std::filesystem::path manifestJson,
54+
std::filesystem::path traceFile);
55+
56+
/// Parse the connection string and instantiate the accelerator. Format is:
57+
/// "<mode>:<manifest path>[:<traceFile>]".
58+
static std::unique_ptr<Accelerator> connect(std::string connectionString);
59+
60+
protected:
61+
virtual Service *createService(Service::Type service) override;
62+
63+
private:
64+
struct Impl;
65+
std::unique_ptr<Impl> impl;
66+
};
67+
68+
} // namespace trace
69+
} // namespace backends
70+
} // namespace esi
71+
72+
#endif // ESI_BACKENDS_COSIM_H
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
//===- Trace.cpp - Implementation of trace backend -----------------------===//
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 package. The source for this file
11+
// should always be modified within CIRCT (lib/dialect/ESI/runtime/cpp/lib/).
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
#include "esi/backends/Trace.h"
16+
#include "esi/StdServices.h"
17+
18+
#include <fstream>
19+
#include <iostream>
20+
#include <regex>
21+
22+
using namespace std;
23+
24+
using namespace esi;
25+
using namespace esi::services;
26+
using namespace esi::backends::trace;
27+
28+
// We only support v1.
29+
constexpr uint32_t ESIVersion = 1;
30+
31+
unique_ptr<Accelerator> TraceAccelerator::connect(string connectionString) {
32+
string modeStr;
33+
string manifestPath;
34+
string traceFile = "trace.json";
35+
36+
// Parse the connection string.
37+
// <mode>:<manifest path>[:<traceFile>]
38+
regex connPattern("(\\w):([^:]+)(:(\\w+))?");
39+
smatch match;
40+
if (regex_search(connectionString, match, connPattern)) {
41+
modeStr = match[1];
42+
manifestPath = match[2];
43+
if (match[3].matched)
44+
traceFile = match[3];
45+
} else {
46+
throw runtime_error("connection string must be of the form "
47+
"'<mode>:<manifest path>[:<traceFile>]'");
48+
}
49+
50+
// Parse the mode.
51+
Mode mode;
52+
if (modeStr == "w")
53+
mode = Write;
54+
else
55+
throw runtime_error("unknown mode '" + modeStr + "'");
56+
57+
return std::make_unique<TraceAccelerator>(
58+
mode, filesystem::path(manifestPath), filesystem::path(traceFile));
59+
}
60+
61+
namespace {
62+
class TraceSysInfo : public SysInfo {
63+
public:
64+
TraceSysInfo(std::filesystem::path manifestJson)
65+
: manifestJson(manifestJson) {}
66+
67+
uint32_t esiVersion() const override { return ESIVersion; }
68+
69+
std::string jsonManifest() const override {
70+
// Read in the whole json file and return it.
71+
ifstream manifest(manifestJson);
72+
if (!manifest.is_open())
73+
throw runtime_error("failed to open manifest file '" +
74+
manifestJson.string() + "'");
75+
stringstream buffer;
76+
buffer << manifest.rdbuf();
77+
manifest.close();
78+
return buffer.str();
79+
}
80+
81+
std::vector<uint8_t> compressedManifest() const override {
82+
throw runtime_error("compressed manifest not supported by trace backend");
83+
}
84+
85+
private:
86+
std::filesystem::path manifestJson;
87+
};
88+
} // namespace
89+
90+
struct esi::backends::trace::TraceAccelerator::Impl {
91+
Impl(Mode mode, std::filesystem::path manifestJson,
92+
std::filesystem::path traceFile)
93+
: mode(mode), manifestJson(manifestJson), traceFile(traceFile) {
94+
if (!filesystem::exists(manifestJson))
95+
throw runtime_error("manifest file '" + manifestJson.string() +
96+
"' does not exist");
97+
}
98+
99+
Service *createService(Service::Type svcType);
100+
101+
private:
102+
Mode mode;
103+
std::filesystem::path manifestJson;
104+
std::filesystem::path traceFile;
105+
};
106+
107+
Service *TraceAccelerator::Impl::createService(Service::Type svcType) {
108+
if (svcType == typeid(SysInfo))
109+
return new TraceSysInfo(manifestJson);
110+
return nullptr;
111+
}
112+
113+
TraceAccelerator::TraceAccelerator(Mode mode,
114+
std::filesystem::path manifestJson,
115+
std::filesystem::path traceFile) {
116+
impl = std::make_unique<Impl>(mode, manifestJson, traceFile);
117+
}
118+
119+
Service *TraceAccelerator::createService(Service::Type svcType) {
120+
return impl->createService(svcType);
121+
}
122+
123+
REGISTER_ACCELERATOR("trace", TraceAccelerator);

test/Dialect/ESI/manifest.mlir

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// REQUIRES: zlib
2-
// RUN: circt-opt %s --esi-connect-services --esi-appid-hier=top=top --esi-build-manifest="top=top to-file=%t1.json" > %t1.mlir
2+
// RUN: circt-opt %s --esi-connect-services --esi-appid-hier=top=top --esi-build-manifest="top=top" > %t1.mlir
33
// RUN: circt-opt %t1.mlir | FileCheck --check-prefix=HIER %s
4-
// RUN: FileCheck --input-file=%t1.json %s
4+
// RUN: FileCheck --input-file=esi_system_manifest.json %s
55
// RUN: circt-opt %t1.mlir --esi-clean-metadata --lower-esi-bundles --lower-esi-ports --lower-esi-to-hw=platform=cosim | FileCheck --check-prefix=HW %s
66

77
hw.type_scope @__hw_typedecls {

0 commit comments

Comments
 (0)