268 строки
7.8 KiB
C++
268 строки
7.8 KiB
C++
/*!
|
|
* Copyright (c) 2017 by Contributors
|
|
* \file tvm_vpi.cc
|
|
* \brief Messages passed around VPI used for simulation.
|
|
*/
|
|
#include <dmlc/logging.h>
|
|
#include <vpi_user.h>
|
|
#include <cstdlib>
|
|
#include <memory>
|
|
#include <queue>
|
|
#include "./tvm_vpi.h"
|
|
#include "../src/common/pipe.h"
|
|
|
|
namespace tvm {
|
|
namespace vpi {
|
|
// standard consistency checks
|
|
static_assert(sizeof(vpiHandle) == sizeof(VPIRawHandle),
|
|
"VPI standard");
|
|
// type codes
|
|
static_assert(vpiModule == kVPIModule, "VPI standard");
|
|
// Property code
|
|
static_assert(vpiType == kVPIType, "VPI standard");
|
|
static_assert(vpiFullName == kVPIFullName, "VPI standard");
|
|
static_assert(vpiSize == kVPISize, "VPI standard");
|
|
static_assert(vpiDefName == kVPIDefName, "VPI standard");
|
|
|
|
// IPC client for VPI
|
|
class IPCClient {
|
|
public:
|
|
// constructor
|
|
IPCClient(int64_t hread, int64_t hwrite)
|
|
: reader_(hread), writer_(hwrite) {
|
|
}
|
|
void Init() {
|
|
vpiHandle argv = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle arg_iter = vpi_iterate(vpiArgument, argv);
|
|
clock_ = vpi_scan(arg_iter);
|
|
std::vector<VPIRawHandle> handles;
|
|
while (vpiHandle h = vpi_scan(arg_iter)) {
|
|
handles.push_back(h);
|
|
}
|
|
writer_.Write(handles);
|
|
PutInt(clock_, 0);
|
|
}
|
|
int Callback() {
|
|
if (!GetInt(clock_)) {
|
|
try {
|
|
return AtNegEdge();
|
|
} catch (const std::runtime_error& e) {
|
|
reader_.Close();
|
|
writer_.Close();
|
|
vpi_printf("ERROR: encountered %s\n", e.what());
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
// called at neg edge.
|
|
int AtNegEdge() {
|
|
// This is actually called at neg-edge
|
|
// The put values won't take effect until next neg-edge.
|
|
// This allow us to see the registers before snc
|
|
writer_.Write(kPosEdgeTrigger);
|
|
VPICallCode rcode;
|
|
VPIRawHandle handle;
|
|
int32_t index, value;
|
|
|
|
while (true) {
|
|
CHECK(reader_.Read(&rcode));
|
|
switch (rcode) {
|
|
case kGetHandleByName: {
|
|
std::string str;
|
|
CHECK(reader_.Read(&str));
|
|
CHECK(reader_.Read(&handle));
|
|
handle = vpi_handle_by_name(
|
|
str.c_str(), static_cast<vpiHandle>(handle));
|
|
writer_.Write(kSuccess);
|
|
writer_.Write(handle);
|
|
break;
|
|
}
|
|
case kGetHandleByIndex: {
|
|
CHECK(reader_.Read(&handle));
|
|
CHECK(reader_.Read(&index));
|
|
handle = vpi_handle_by_index(
|
|
static_cast<vpiHandle>(handle), index);
|
|
writer_.Write(kSuccess);
|
|
writer_.Write(handle);
|
|
break;
|
|
}
|
|
case kGetStrProp: {
|
|
CHECK(reader_.Read(&value));
|
|
CHECK(reader_.Read(&handle));
|
|
std::string prop = vpi_get_str(
|
|
value, static_cast<vpiHandle>(handle));
|
|
writer_.Write(kSuccess);
|
|
writer_.Write(prop);
|
|
break;
|
|
}
|
|
case kGetIntProp: {
|
|
CHECK(reader_.Read(&value));
|
|
CHECK(reader_.Read(&handle));
|
|
value = vpi_get(value, static_cast<vpiHandle>(handle));
|
|
writer_.Write(kSuccess);
|
|
writer_.Write(value);
|
|
break;
|
|
}
|
|
case kGetInt32: {
|
|
CHECK(reader_.Read(&handle));
|
|
value = GetInt(static_cast<vpiHandle>(handle));
|
|
writer_.Write(kSuccess);
|
|
writer_.Write(value);
|
|
break;
|
|
}
|
|
case kPutInt32: {
|
|
CHECK(reader_.Read(&handle));
|
|
CHECK(reader_.Read(&value));
|
|
CHECK(handle != clock_) << "Cannot write to clock";
|
|
PutInt(static_cast<vpiHandle>(handle), value);
|
|
writer_.Write(kSuccess);
|
|
break;
|
|
}
|
|
case kGetVec: {
|
|
CHECK(reader_.Read(&handle));
|
|
vpiHandle h = static_cast<vpiHandle>(handle);
|
|
int bits = vpi_get(vpiSize, h);
|
|
int nwords = (bits + 31) / 32;
|
|
s_vpi_value value_s;
|
|
value_s.format = vpiVectorVal;
|
|
vpi_get_value(h, &value_s);
|
|
vec_buf_.resize(nwords);
|
|
for (size_t i = 0; i < vec_buf_.size(); ++i) {
|
|
vec_buf_[i].aval = value_s.value.vector[i].aval;
|
|
vec_buf_[i].bval = value_s.value.vector[i].bval;
|
|
}
|
|
writer_.Write(kSuccess);
|
|
writer_.Write(vec_buf_);
|
|
break;
|
|
}
|
|
case kPutVec: {
|
|
CHECK(reader_.Read(&handle));
|
|
CHECK(reader_.Read(&vec_buf_));
|
|
CHECK(handle != clock_) << "Cannot write to clock";
|
|
vpiHandle h = static_cast<vpiHandle>(handle);
|
|
svec_buf_.resize(vec_buf_.size());
|
|
for (size_t i = 0; i < vec_buf_.size(); ++i) {
|
|
svec_buf_[i].aval = vec_buf_[i].aval;
|
|
svec_buf_[i].bval = vec_buf_[i].bval;
|
|
}
|
|
s_vpi_value value_s;
|
|
s_vpi_time time_s;
|
|
time_s.type = vpiSimTime;
|
|
time_s.high = 0;
|
|
time_s.low = 10;
|
|
value_s.format = vpiVectorVal;
|
|
value_s.value.vector = &svec_buf_[0];
|
|
vpi_put_value(h, &value_s, &time_s, vpiTransportDelay);
|
|
writer_.Write(kSuccess);
|
|
break;
|
|
}
|
|
case kYield: {
|
|
writer_.Write(kSuccess);
|
|
return 0;
|
|
}
|
|
case kShutDown : {
|
|
writer_.Write(kSuccess);
|
|
vpi_control(vpiFinish, 0);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Create a new FSM from ENV.
|
|
static IPCClient* Create() {
|
|
const char* d_read = getenv("TVM_DREAD_PIPE");
|
|
const char* d_write = getenv("TVM_DWRITE_PIPE");
|
|
const char* h_read = getenv("TVM_HREAD_PIPE");
|
|
const char* h_write = getenv("TVM_HWRITE_PIPE");
|
|
if (d_write == nullptr ||
|
|
d_read == nullptr ||
|
|
h_read == nullptr ||
|
|
h_write == nullptr) {
|
|
vpi_printf("ERROR: need environment var TVM_READ_PIPE, TVM_WRITE_PIPE\n");
|
|
vpi_control(vpiFinish, 1);
|
|
return nullptr;
|
|
}
|
|
// close host side pipe.
|
|
common::Pipe(atoi(h_read)).Close();
|
|
common::Pipe(atoi(h_write)).Close();
|
|
IPCClient* client = new IPCClient(atoi(d_read), atoi(d_write));
|
|
client->Init();
|
|
return client;
|
|
}
|
|
// Get integer from handle.
|
|
static int GetInt(vpiHandle h) {
|
|
s_vpi_value value_s;
|
|
value_s.format = vpiIntVal;
|
|
vpi_get_value(h, &value_s);
|
|
return value_s.value.integer;
|
|
}
|
|
// Put integer into handle.
|
|
static void PutInt(vpiHandle h, int value) {
|
|
s_vpi_value value_s;
|
|
s_vpi_time time_s;
|
|
time_s.type = vpiSimTime;
|
|
time_s.high = 0;
|
|
time_s.low = 10;
|
|
value_s.format = vpiIntVal;
|
|
value_s.value.integer = value;
|
|
vpi_put_value(h, &value_s, &time_s, vpiTransportDelay);
|
|
}
|
|
// Handles
|
|
vpiHandle clock_;
|
|
// the communicator
|
|
common::Pipe reader_, writer_;
|
|
// data buf
|
|
std::vector<VPIVecVal> vec_buf_;
|
|
std::vector<s_vpi_vecval> svec_buf_;
|
|
};
|
|
} // namespace vpi
|
|
} // namespace tvm
|
|
|
|
extern "C" {
|
|
static PLI_INT32 tvm_host_clock_cb(p_cb_data cb_data) {
|
|
return reinterpret_cast<tvm::vpi::IPCClient*>(
|
|
cb_data->user_data)->Callback();
|
|
}
|
|
|
|
static PLI_INT32 tvm_init(char* cb) {
|
|
s_vpi_value value_s;
|
|
s_vpi_time time_s;
|
|
s_cb_data cb_data_s;
|
|
tvm::vpi::IPCClient* client = tvm::vpi::IPCClient::Create();
|
|
if (client) {
|
|
cb_data_s.user_data = reinterpret_cast<char*>(client);
|
|
cb_data_s.reason = cbValueChange;
|
|
cb_data_s.cb_rtn = tvm_host_clock_cb;
|
|
cb_data_s.time = &time_s;
|
|
cb_data_s.value = &value_s;
|
|
time_s.type = vpiSuppressTime;
|
|
value_s.format = vpiIntVal;
|
|
cb_data_s.obj = client->clock_;
|
|
vpi_register_cb(&cb_data_s);
|
|
} else {
|
|
vpi_printf("ERROR: canot initalize host\n");
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void tvm_vpi_register() {
|
|
s_vpi_systf_data tf_data;
|
|
tf_data.type = vpiSysTask;
|
|
tf_data.tfname = "$tvm_session";
|
|
tf_data.calltf = tvm_init;
|
|
tf_data.compiletf = nullptr;
|
|
tf_data.sizetf = nullptr;
|
|
tf_data.user_data = nullptr;
|
|
vpi_register_systf(&tf_data);
|
|
}
|
|
|
|
void (*vlog_startup_routines[])() = {
|
|
tvm_vpi_register,
|
|
0
|
|
};
|
|
} // extern "C"
|