circt/tools/arcilator/arcilator-runtime.h

218 lines
5.7 KiB
C++

// NOLINTBEGIN
#pragma once
#include <array>
#include <cstdarg>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <functional>
#include <ostream>
#include <vector>
// Sanity checks for binary compatibility
#ifdef __BYTE_ORDER__
#if (__BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__)
#error Unsupported endianess
#endif
#endif
static_assert(sizeof(int) == 4, "Unsupported ABI");
static_assert(sizeof(long long) == 8, "Unsupported ABI");
// --- Exports to the IR ---
#ifdef _WIN32
#define ARC_EXPORT extern "C" __declspec(dllexport)
#else
#define ARC_EXPORT extern "C" __attribute__((visibility("default")))
#endif
#ifndef ARC_NO_LIBC_EXPORTS
// libc Adapters
ARC_EXPORT int _arc_libc_fprintf(FILE *stream, const char *format, ...) {
int result;
va_list args;
va_start(args, format);
result = vfprintf(stream, format, args);
va_end(args);
return result;
}
ARC_EXPORT int _arc_libc_fputs(const char *str, FILE *stream) {
return fputs(str, stream);
}
ARC_EXPORT int _arc_libc_fputc(int ch, FILE *stream) {
return fputc(ch, stream);
}
#endif // ARC_NO_LIBC_EXPORTS
// Runtime Environment calls
#define ARC_ENV_DECL_GET_PRINT_STREAM(idarg) \
ARC_EXPORT FILE *_arc_env_get_print_stream(uint32_t idarg)
#ifndef ARC_NO_DEFAULT_GET_PRINT_STREAM
ARC_ENV_DECL_GET_PRINT_STREAM(id) {
(void)id;
return stderr;
}
#endif // ARC_NO_DEFAULT_GET_PRINT_STREAM
// ----------------
struct Signal {
const char *name;
unsigned offset;
unsigned numBits;
enum Type { Input, Output, Register, Memory, Wire } type;
// for memories:
unsigned stride;
unsigned depth;
};
struct Hierarchy {
const char *name;
unsigned numStates;
unsigned numChildren;
Signal *states;
Hierarchy *children;
};
template <unsigned N>
struct Bytes {
uint8_t byte[N];
};
template <typename T, unsigned Stride, unsigned Depth>
struct Memory {
union {
T data;
uint8_t stride[Stride];
} words[Depth];
};
template <class ModelLayout>
class ValueChangeDump {
public:
ValueChangeDump(std::basic_ostream<char> &os, const uint8_t *state)
: os(os), state(state) {}
void writeHeader(bool withHierarchy = true) {
os << "$date\n October 21, 2015\n$end\n";
os << "$version\n Some cryptic MLIR magic\n$end\n";
os << "$timescale 1ns $end\n";
os << "$scope module " << ModelLayout::name << " $end\n";
auto writeSignal = [&](const Signal &state) {
if (state.type != Signal::Memory) {
auto &signal =
allocSignal(state, state.offset, (state.numBits + 7) / 8);
if (state.type == Signal::Register) {
os << "$var reg " << state.numBits << " " << signal.abbrev << " "
<< state.name;
} else {
os << "$var wire " << state.numBits << " " << signal.abbrev << " "
<< state.name;
}
if (state.numBits > 1)
os << " [" << (state.numBits - 1) << ":0]";
os << " $end\n";
} else {
for (unsigned i = 0; i < state.depth; ++i) {
auto &signal = allocSignal(state, state.offset + i * state.stride,
(state.numBits + 7) / 8);
os << "$var reg " << state.numBits << " " << signal.abbrev << " "
<< state.name << "[" << i << "]";
if (state.numBits > 1)
os << " [" << (state.numBits - 1) << ":0]";
os << " $end\n";
}
}
};
std::function<void(const Hierarchy &)> writeHierarchy =
[&](const Hierarchy &hierarchy) {
os << "$scope module " << hierarchy.name << " $end\n";
for (unsigned i = 0; i < hierarchy.numStates; ++i)
writeSignal(hierarchy.states[i]);
for (unsigned i = 0; i < hierarchy.numChildren; ++i)
writeHierarchy(hierarchy.children[i]);
os << "$upscope $end\n";
};
for (auto &port : ModelLayout::io)
writeSignal(port);
if (withHierarchy)
writeHierarchy(ModelLayout::hierarchy);
os << "$upscope $end\n";
os << "$enddefinitions $end\n";
}
void writeValues(bool includeUnchanged = false) {
for (auto &signal : signals) {
const uint8_t *valNew = state + signal.offset;
uint8_t *valOld = &previousValues[0] + signal.previousOffset;
size_t numBytes = (signal.state.numBits + 7) / 8;
bool unchanged = std::equal(valNew, valNew + numBytes, valOld);
if (unchanged && !includeUnchanged)
continue;
if (signal.state.numBits > 1)
os << 'b';
for (unsigned n = signal.state.numBits; n > 0; --n)
os << (valNew[(n - 1) / 8] & (1 << ((n - 1) % 8)) ? '1' : '0');
if (signal.state.numBits > 1)
os << ' ';
os << signal.abbrev << "\n";
std::copy(valNew, valNew + numBytes, valOld);
}
}
void writeDumpvars() {
os << "$dumpvars\n";
writeValues(true);
}
void writeTimestep(size_t timeIncrement) {
time += timeIncrement;
os << "#" << time << "\n";
writeValues();
}
size_t time = 0;
private:
struct VcdSignal {
std::string abbrev;
unsigned offset;
const Signal &state;
unsigned previousOffset;
};
VcdSignal &allocSignal(const Signal &state, unsigned offset,
unsigned numBytes) {
std::string abbrev;
unsigned rest = signals.size() + 1;
while (rest != 0) {
uint8_t c = (rest % 84) + 33;
if (c >= '0')
c += 10;
abbrev += c;
rest /= 84;
}
signals.push_back(
VcdSignal{abbrev, offset, state, unsigned(previousValues.size())});
previousValues.resize(previousValues.size() + numBytes);
return signals.back();
}
std::basic_ostream<char> &os;
const uint8_t *state;
std::vector<VcdSignal> signals;
std::vector<uint8_t> previousValues;
};
// NOLINTEND