blob: 949b660ce447d9843f9489a7138f03b7a6211b3f [file] [log] [blame]
/*!
* 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 <string>
#include <vector>
#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"