[VERILOG] Basic Verilog Testflow (#70)
* [VERILOG] Basic Verilog Testflow * fix build * fix the comment * fix lint in verilog
This commit is contained in:
Родитель
2548cedcb8
Коммит
9e660dbe74
|
@ -37,12 +37,14 @@ addons:
|
|||
- python3-dev
|
||||
- python3-nose
|
||||
- graphviz
|
||||
- bison
|
||||
- flex
|
||||
|
||||
before_install:
|
||||
- source dmlc-core/scripts/travis/travis_setup_env.sh
|
||||
- export PYTHONPATH=${PYTHONPATH}:${PWD}/python
|
||||
|
||||
install:
|
||||
- source dmlc-core/scripts/travis/travis_setup_env.sh
|
||||
- source tests/travis/setup.sh
|
||||
|
||||
script:
|
||||
|
|
9
Makefile
9
Makefile
|
@ -11,7 +11,7 @@ endif
|
|||
include $(config)
|
||||
|
||||
# specify tensor path
|
||||
.PHONY: clean all test doc pylint cpplint lint
|
||||
.PHONY: clean all test doc pylint cpplint lint verilog
|
||||
|
||||
all: lib/libtvm.so lib/libtvm_runtime.so lib/libtvm.a
|
||||
|
||||
|
@ -79,6 +79,9 @@ include tests/cpp/unittest.mk
|
|||
|
||||
test: $(TEST)
|
||||
|
||||
include verilog/verilog.mk
|
||||
verilog: $(VER_LIBS)
|
||||
|
||||
build/%.o: src/%.cc
|
||||
@mkdir -p $(@D)
|
||||
$(CXX) $(CFLAGS) -MM -MT build/$*.o $< >build/$*.d
|
||||
|
@ -92,6 +95,8 @@ lib/libtvm_runtime.so: $(RUNTIME_DEP)
|
|||
@mkdir -p $(@D)
|
||||
$(CXX) $(CFLAGS) $(FRAMEWORKS) -shared -o $@ $(filter %.o %.a, $^) $(LDFLAGS)
|
||||
|
||||
|
||||
|
||||
lib/libtvm.a: $(ALL_DEP)
|
||||
@mkdir -p $(@D)
|
||||
ar crv $@ $(filter %.o, $?)
|
||||
|
@ -102,7 +107,7 @@ LIBHALIDEIR:
|
|||
+ cd HalideIR; make lib/libHalideIR.a ; cd $(ROOTDIR)
|
||||
|
||||
cpplint:
|
||||
python2 dmlc-core/scripts/lint.py tvm cpp include src
|
||||
python2 dmlc-core/scripts/lint.py tvm cpp include src verilog
|
||||
|
||||
pylint:
|
||||
pylint python/tvm --rcfile=$(ROOTDIR)/tests/lint/pylintrc
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 53751c3da2999d841f4f952639bd766505b51d84
|
||||
Subproject commit c2871f5db50830f5278ff6e323e8e51a6d5516dd
|
|
@ -23,6 +23,8 @@ class Channel : public NodeRef {
|
|||
* \return the pointer to the internal node container
|
||||
*/
|
||||
inline const ChannelNode* operator->() const;
|
||||
// The container type
|
||||
using ContainerType = ChannelNode;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
"""Information about nnvm."""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
|
||||
from .. import _api_internal
|
||||
from .._base import string_types
|
||||
from .._ctypes._node import NodeBase, register_node
|
||||
from . import testing
|
||||
|
||||
@register_node
|
||||
class VPISession(NodeBase):
|
||||
"""Verilog session"""
|
||||
def __init__(self, handle):
|
||||
super(VPISession, self).__init__(handle)
|
||||
self.proc = None
|
||||
self.execpath = None
|
||||
|
||||
def __del__(self):
|
||||
self.proc.kill()
|
||||
super(VPISession, self).__del__()
|
||||
|
||||
def arg(self, index):
|
||||
"""Get handle passed to host session.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
index : int
|
||||
The index value.
|
||||
|
||||
Returns
|
||||
-------
|
||||
handle : VPIHandle
|
||||
The handle
|
||||
"""
|
||||
return _api_internal._vpi_SessGetArg(self, index)
|
||||
|
||||
def __getitem__(self, name):
|
||||
if not isinstance(name, string_types):
|
||||
raise ValueError("have to be string types")
|
||||
return _api_internal._vpi_SessGetHandleByName(self, name)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return _api_internal._vpi_SessGetHandleByName(self, name)
|
||||
|
||||
def yield_until_posedge(self):
|
||||
"""Yield until next posedge"""
|
||||
return _api_internal._vpi_SessYield(self)
|
||||
|
||||
def shutdown(self):
|
||||
"""Shutdown the simulator"""
|
||||
return _api_internal._vpi_SessShutdown(self)
|
||||
|
||||
|
||||
@register_node
|
||||
class VPIHandle(NodeBase):
|
||||
"""Handle to a verilog variable."""
|
||||
def __init__(self, handle):
|
||||
super(VPIHandle, self).__init__(handle)
|
||||
self._name = None
|
||||
self._size = None
|
||||
|
||||
def get_int(self):
|
||||
"""Get integer value from handle.
|
||||
|
||||
Returns
|
||||
-------
|
||||
value : int
|
||||
"""
|
||||
return _api_internal._vpi_HandleGetInt(self)
|
||||
|
||||
def put_int(self, value):
|
||||
"""Put integer value to handle.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
value : int
|
||||
The value to put
|
||||
"""
|
||||
return _api_internal._vpi_HandlePutInt(self, value)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
if self._name is None:
|
||||
self._name = _api_internal._vpi_HandleGetName(self)
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
if self._size is None:
|
||||
self._size = _api_internal._vpi_HandleGetSize(self)
|
||||
return self._size
|
||||
|
||||
def __getitem__(self, name):
|
||||
if not isinstance(name, string_types):
|
||||
raise ValueError("have to be string types")
|
||||
return _api_internal._vpi_HandleGetHandleByName(self, name)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return _api_internal._vpi_HandleGetHandleByName(self, name)
|
||||
|
||||
|
||||
def _find_vpi_path():
|
||||
curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__)))
|
||||
api_path = os.path.join(curr_path, '../../../lib/')
|
||||
vpi_path = [curr_path, api_path]
|
||||
vpi_path = [os.path.join(p, 'tvm_vpi.vpi') for p in vpi_path]
|
||||
vpi_found = [p for p in vpi_path if os.path.exists(p) and os.path.isfile(p)]
|
||||
if vpi_found:
|
||||
return os.path.dirname(vpi_found[0])
|
||||
else:
|
||||
raise ValueError("Cannot find tvm_vpi.vpi, make sure you did `make verilog`")
|
||||
|
||||
def search_path():
|
||||
"""Get the search directory."""
|
||||
curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__)))
|
||||
ver_path = [os.path.join(curr_path, '../../../verilog/')]
|
||||
ver_path += [os.path.join(curr_path, '../../../tests/verilog/')]
|
||||
return ver_path
|
||||
|
||||
|
||||
def find_file(file_name):
|
||||
"""Find file in the search directories.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
file_name : str
|
||||
The file name
|
||||
|
||||
Return
|
||||
------
|
||||
file_name : str
|
||||
The absolute path to the file, raise Error if cannot find it.
|
||||
"""
|
||||
ver_path = search_path()
|
||||
flist = [os.path.join(p, file_name) for p in ver_path]
|
||||
found = [p for p in flist if os.path.exists(p) and os.path.isfile(p)]
|
||||
if len(found):
|
||||
return found[0]
|
||||
else:
|
||||
raise ValueError("Cannot find %s in %s" % (file_name, flist))
|
||||
|
||||
|
||||
def compile_file(file_name, file_target, options=None):
|
||||
"""Compile verilog via iverilog
|
||||
|
||||
Parameters
|
||||
----------
|
||||
file_name : str or list of str
|
||||
The cuda code.
|
||||
|
||||
file_target : str
|
||||
The target file.
|
||||
"""
|
||||
cmd = ["iverilog"]
|
||||
for path in search_path():
|
||||
cmd += ["-I%s" % path]
|
||||
|
||||
cmd += ["-o", file_target]
|
||||
if options:
|
||||
cmd += options
|
||||
|
||||
if isinstance(file_name, string_types):
|
||||
file_name = [file_name]
|
||||
cmd += file_name
|
||||
proc = subprocess.Popen(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT)
|
||||
(out, _) = proc.communicate()
|
||||
|
||||
if proc.returncode != 0:
|
||||
raise ValueError("Compilation error:\n%s" % out)
|
||||
|
||||
|
||||
def session(file_name):
|
||||
"""Create a new iverilog session by compile the file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
file_name : str or list of str
|
||||
The name of the file
|
||||
|
||||
Returns
|
||||
-------
|
||||
sess : VPISession
|
||||
The created session.
|
||||
"""
|
||||
if isinstance(file_name, string_types):
|
||||
file_name = [file_name]
|
||||
|
||||
for name in file_name:
|
||||
if not os.path.exists(name):
|
||||
raise ValueError("Cannot find file %s" % name)
|
||||
|
||||
path = testing.tempdir()
|
||||
target = path.relpath(os.path.basename(file_name[0].rsplit(".", 1)[0]))
|
||||
compile_file(file_name, target)
|
||||
vpi_path = _find_vpi_path()
|
||||
|
||||
cmd = ["vvp"]
|
||||
cmd += ["-M", vpi_path]
|
||||
cmd += ["-m", "tvm_vpi"]
|
||||
cmd += [target]
|
||||
env = os.environ.copy()
|
||||
|
||||
read_device, write_host = os.pipe()
|
||||
read_host, write_device = os.pipe()
|
||||
|
||||
if sys.platform == "win32":
|
||||
import msvcrt
|
||||
env['TVM_DREAD_PIPE'] = str(msvcrt.get_osfhandle(read_device))
|
||||
env['TVM_DWRITE_PIPE'] = str(msvcrt.get_osfhandle(write_device))
|
||||
read_host = msvcrt.get_osfhandle(read_host)
|
||||
write_host = msvcrt.get_osfhandle(write_host)
|
||||
else:
|
||||
env['TVM_DREAD_PIPE'] = str(read_device)
|
||||
env['TVM_DWRITE_PIPE'] = str(write_device)
|
||||
|
||||
env['TVM_HREAD_PIPE'] = str(read_host)
|
||||
env['TVM_HWRITE_PIPE'] = str(write_host)
|
||||
|
||||
proc = subprocess.Popen(cmd, env=env, close_fds=False)
|
||||
# close device side pipe
|
||||
os.close(read_device)
|
||||
os.close(write_device)
|
||||
|
||||
sess = _api_internal._vpi_SessMake(read_host, write_host)
|
||||
sess.proc = proc
|
||||
sess.execpath = path
|
||||
return sess
|
|
@ -4,6 +4,7 @@ Header files in include are public APIs that share across modules.
|
|||
There can be internal header files within each module that sit in src.
|
||||
|
||||
The current code modules in src.
|
||||
- common Internal common utilities.
|
||||
- api API function registration
|
||||
- lang The definition of DSL related data structure
|
||||
- arithmetic Arithmetic expression and set simplification
|
||||
|
|
|
@ -0,0 +1,242 @@
|
|||
/*!
|
||||
* Copyright (c) 2017 by Contributors
|
||||
* \file vpi_session.cc
|
||||
* \brief IPC session call to verilog simulator via VPI.
|
||||
*/
|
||||
#include <tvm/api_registry.h>
|
||||
#include "./vpi_session.h"
|
||||
|
||||
namespace tvm {
|
||||
namespace codegen {
|
||||
|
||||
using namespace vpi;
|
||||
|
||||
/*! \brief Container for session. */
|
||||
class VPISessionNode : public Node {
|
||||
public:
|
||||
// Whether in control.
|
||||
bool in_control{false};
|
||||
// Internal reader and writer.
|
||||
common::Pipe reader;
|
||||
common::Pipe writer;
|
||||
|
||||
// internal constructor
|
||||
VPISessionNode(int h_pipe_read, int h_pipe_write)
|
||||
: reader(h_pipe_read), writer(h_pipe_write) {
|
||||
}
|
||||
~VPISessionNode() {
|
||||
if (in_control) {
|
||||
VPIReturnCode cd;
|
||||
writer.Write(kShutDown);
|
||||
reader.Read(&cd);
|
||||
}
|
||||
reader.Close();
|
||||
writer.Close();
|
||||
}
|
||||
// visit all attributes
|
||||
void VisitAttrs(AttrVisitor* v) final {
|
||||
}
|
||||
void ReadExpect(VPIReturnCode rcode) {
|
||||
VPIReturnCode code;
|
||||
CHECK(reader.Read(&code));
|
||||
CHECK_EQ(code, rcode) << "Error in simulation";
|
||||
}
|
||||
|
||||
static constexpr const char* _type_key = "VPISession";
|
||||
TVM_DECLARE_NODE_TYPE_INFO(VPISessionNode, Node);
|
||||
};
|
||||
|
||||
/*! \brief Container for handle */
|
||||
class VPIHandleNode : public Node {
|
||||
public:
|
||||
// The internal session.
|
||||
VPISession sess;
|
||||
// Internal handle
|
||||
VPIRawHandle handle;
|
||||
|
||||
void VisitAttrs(AttrVisitor* v) final {
|
||||
v->Visit("sess", &sess);
|
||||
}
|
||||
static VPIHandle make(const VPISession& sess, VPIRawHandle handle) {
|
||||
std::shared_ptr<VPIHandleNode> n =
|
||||
std::make_shared<VPIHandleNode>();
|
||||
n->sess = sess;
|
||||
n->handle = handle;
|
||||
return VPIHandle(n);
|
||||
}
|
||||
|
||||
static constexpr const char* _type_key = "VPIHandle";
|
||||
TVM_DECLARE_NODE_TYPE_INFO(VPIHandleNode, Node);
|
||||
};
|
||||
|
||||
// Inline implementations
|
||||
inline VPISessionNode* VPISession::get() const {
|
||||
return static_cast<VPISessionNode*>(node_.get());
|
||||
}
|
||||
inline VPIHandleNode* VPIHandle::get() const {
|
||||
return static_cast<VPIHandleNode*>(node_.get());
|
||||
}
|
||||
|
||||
VPISession VPISession::make(int h_pipe_read, int h_pipe_write) {
|
||||
std::shared_ptr<VPISessionNode> n = std::make_shared<VPISessionNode>(
|
||||
h_pipe_read, h_pipe_write);
|
||||
n->ReadExpect(kPosEdgeTrigger);
|
||||
n->in_control = true;
|
||||
return VPISession(n);
|
||||
}
|
||||
|
||||
VPIHandle VPISession::operator[](const std::string& name) const {
|
||||
return GetByName(name, nullptr);
|
||||
}
|
||||
|
||||
VPIHandle VPISession::GetByName(const std::string& name, VPIRawHandle handle) const {
|
||||
VPISessionNode* n = get();
|
||||
CHECK(n->in_control);
|
||||
n->writer.Write(kGetHandleByName);
|
||||
n->writer.Write(name);
|
||||
n->writer.Write(handle);
|
||||
n->ReadExpect(kSuccess);
|
||||
CHECK(n->reader.Read(&handle));
|
||||
CHECK(handle != nullptr)
|
||||
<< "Cannot find handle with name=" << name;
|
||||
return VPIHandleNode::make(*this, handle);
|
||||
}
|
||||
|
||||
void VPISession::yield() {
|
||||
VPISessionNode* n = get();
|
||||
CHECK(n->in_control);
|
||||
n->writer.Write(kYield);
|
||||
n->ReadExpect(kSuccess);
|
||||
n->in_control = false;
|
||||
n->ReadExpect(kPosEdgeTrigger);
|
||||
n->in_control = true;
|
||||
}
|
||||
|
||||
void VPISession::shutdown() {
|
||||
VPISessionNode* n = get();
|
||||
if (n->in_control) {
|
||||
n->writer.Write(kShutDown);
|
||||
n->ReadExpect(kSuccess);
|
||||
n->in_control = false;
|
||||
}
|
||||
}
|
||||
|
||||
int VPIHandle::size() const {
|
||||
VPIHandleNode* h = get();
|
||||
VPISessionNode* n = h->sess.get();
|
||||
CHECK(n->in_control);
|
||||
n->writer.Write(kGetSize);
|
||||
n->writer.Write(h->handle);
|
||||
n->ReadExpect(kSuccess);
|
||||
int value;
|
||||
CHECK(n->reader.Read(&value));
|
||||
return value;
|
||||
}
|
||||
|
||||
void VPIHandle::put_int(int value) {
|
||||
VPIHandleNode* h = get();
|
||||
VPISessionNode* n = h->sess.get();
|
||||
CHECK(n->in_control);
|
||||
n->writer.Write(kPutInt32);
|
||||
n->writer.Write(h->handle);
|
||||
n->writer.Write(value);
|
||||
n->ReadExpect(kSuccess);
|
||||
}
|
||||
|
||||
int VPIHandle::get_int() const {
|
||||
VPIHandleNode* h = get();
|
||||
VPISessionNode* n = h->sess.get();
|
||||
CHECK(n->in_control);
|
||||
n->writer.Write(kGetInt32);
|
||||
n->writer.Write(h->handle);
|
||||
n->ReadExpect(kSuccess);
|
||||
int value;
|
||||
CHECK(n->reader.Read(&value));
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string VPIHandle::name() const {
|
||||
VPIHandleNode* h = get();
|
||||
VPISessionNode* n = h->sess.get();
|
||||
CHECK(n->in_control);
|
||||
n->writer.Write(kGetName);
|
||||
n->writer.Write(h->handle);
|
||||
n->ReadExpect(kSuccess);
|
||||
std::string str;
|
||||
CHECK(n->reader.Read(&str));
|
||||
return str;
|
||||
}
|
||||
|
||||
void VPIHandle::put_vec(const std::vector<VPIVecVal>& vec) const {
|
||||
VPIHandleNode* h = get();
|
||||
VPISessionNode* n = h->sess.get();
|
||||
CHECK(n->in_control);
|
||||
n->writer.Write(kPutVec);
|
||||
n->writer.Write(h->handle);
|
||||
n->writer.Write(vec);
|
||||
n->ReadExpect(kSuccess);
|
||||
}
|
||||
|
||||
void VPIHandle::get_vec(std::vector<VPIVecVal>* vec) const {
|
||||
VPIHandleNode* h = get();
|
||||
VPISessionNode* n = h->sess.get();
|
||||
CHECK(n->in_control);
|
||||
n->writer.Write(kPutVec);
|
||||
n->writer.Write(h->handle);
|
||||
n->ReadExpect(kSuccess);
|
||||
CHECK(n->reader.Read(&vec));
|
||||
}
|
||||
|
||||
VPIHandle VPIHandle::operator[](const std::string& name) const {
|
||||
VPIHandleNode* h = get();
|
||||
return h->sess.GetByName(name, h->handle);
|
||||
}
|
||||
|
||||
// API registration
|
||||
TVM_REGISTER_API(_vpi_SessMake)
|
||||
.set_body([](TVMArgs args, TVMRetValue *ret) {
|
||||
*ret = VPISession::make(args[0], args[1]);
|
||||
});
|
||||
|
||||
TVM_REGISTER_API(_vpi_SessGetHandleByName)
|
||||
.set_body([](TVMArgs args, TVMRetValue *ret) {
|
||||
*ret = args[0].operator VPISession().operator[](args[1]);
|
||||
});
|
||||
|
||||
TVM_REGISTER_API(_vpi_SessYield)
|
||||
.set_body([](TVMArgs args, TVMRetValue *ret) {
|
||||
args[0].operator VPISession().yield();
|
||||
});
|
||||
|
||||
TVM_REGISTER_API(_vpi_SessShutdown)
|
||||
.set_body([](TVMArgs args, TVMRetValue *ret) {
|
||||
args[0].operator VPISession().shutdown();
|
||||
});
|
||||
|
||||
TVM_REGISTER_API(_vpi_HandlePutInt)
|
||||
.set_body([](TVMArgs args, TVMRetValue *ret) {
|
||||
args[0].operator VPIHandle().put_int(args[1]);
|
||||
});
|
||||
|
||||
TVM_REGISTER_API(_vpi_HandleGetInt)
|
||||
.set_body([](TVMArgs args, TVMRetValue *ret) {
|
||||
*ret = args[0].operator VPIHandle().get_int();
|
||||
});
|
||||
|
||||
TVM_REGISTER_API(_vpi_HandleGetName)
|
||||
.set_body([](TVMArgs args, TVMRetValue *ret) {
|
||||
*ret = args[0].operator VPIHandle().name();
|
||||
});
|
||||
|
||||
TVM_REGISTER_API(_vpi_HandleGetSize)
|
||||
.set_body([](TVMArgs args, TVMRetValue *ret) {
|
||||
*ret = args[0].operator VPIHandle().size();
|
||||
});
|
||||
|
||||
TVM_REGISTER_API(_vpi_HandleGetHandleByName)
|
||||
.set_body([](TVMArgs args, TVMRetValue *ret) {
|
||||
*ret = args[0].operator VPIHandle().operator[](args[1]);
|
||||
});
|
||||
|
||||
} // namespace codegen
|
||||
} // namespace tvm
|
|
@ -0,0 +1,100 @@
|
|||
/*!
|
||||
* Copyright (c) 2017 by Contributors
|
||||
* \file vpi_session.h
|
||||
* \brief IPC session call to verilog simulator via VPI.
|
||||
*/
|
||||
#ifndef TVM_CODEGEN_VERILOG_VPI_SESSION_H_
|
||||
#define TVM_CODEGEN_VERILOG_VPI_SESSION_H_
|
||||
|
||||
#include <tvm/base.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "../../common/pipe.h"
|
||||
#include "../../../verilog/tvm_vpi.h"
|
||||
|
||||
namespace tvm {
|
||||
namespace codegen {
|
||||
// node containers
|
||||
class VPISessionNode;
|
||||
class VPIHandleNode;
|
||||
class VPIHandle;
|
||||
|
||||
/*! \brief Environment */
|
||||
class VPISession : public NodeRef {
|
||||
public:
|
||||
VPISession() {}
|
||||
explicit VPISession(std::shared_ptr<Node> n) : NodeRef(n) {}
|
||||
/*!
|
||||
* \brief Get handle by name.
|
||||
* \param name The name of the handle.
|
||||
*/
|
||||
VPIHandle operator[](const std::string& name) const;
|
||||
/*!
|
||||
* \brief Yield control back to the simulator
|
||||
* Block until next cycle.
|
||||
*/
|
||||
void yield();
|
||||
/*!
|
||||
* \brief Shutdown the session.
|
||||
*/
|
||||
void shutdown();
|
||||
/*!
|
||||
* \brief Create new session by giving a read and write pipe to VPI process.
|
||||
* \param h_pipe_read a read pipe from VPI process.
|
||||
* \param h_pipe_write a write pipe from VPI process.
|
||||
*/
|
||||
static VPISession make(int h_pipe_read, int h_pipe_write);
|
||||
// Internal methods.
|
||||
using ContainerType = VPISessionNode;
|
||||
|
||||
private:
|
||||
friend class VPIHandle;
|
||||
inline VPISessionNode* get() const;
|
||||
// Get handle by name
|
||||
VPIHandle GetByName(const std::string& name, vpi::VPIRawHandle handle) const;
|
||||
};
|
||||
|
||||
/*! \brief VPI Handle */
|
||||
class VPIHandle : public NodeRef {
|
||||
public:
|
||||
VPIHandle() {}
|
||||
explicit VPIHandle(std::shared_ptr<Node> n) : NodeRef(n) {}
|
||||
/*!
|
||||
* \brief Get handle by name.
|
||||
* \param name The name of the handle.
|
||||
*/
|
||||
VPIHandle operator[](const std::string& name) const;
|
||||
/*! \return number of bits */
|
||||
int size() const;
|
||||
/*!
|
||||
* \brief Set int value to the handle.
|
||||
* \param value The value to set.
|
||||
*/
|
||||
void put_int(int value);
|
||||
/*!
|
||||
* \brief Get int value from handle.
|
||||
* \return The result int value.
|
||||
*/
|
||||
int get_int() const;
|
||||
/*! \return Name of the handle. */
|
||||
std::string name() const;
|
||||
/*!
|
||||
* \brief Put byte vector into the handle.
|
||||
* \param vec The vector to be put.
|
||||
* \return The result int value.
|
||||
*/
|
||||
void put_vec(const std::vector<vpi::VPIVecVal>& vec) const;
|
||||
/*!
|
||||
* \brief Get byte vector from handle.
|
||||
* \param vec The result data container.
|
||||
*/
|
||||
void get_vec(std::vector<vpi::VPIVecVal>* vec) const;
|
||||
// Internal methods
|
||||
using ContainerType = VPIHandleNode;
|
||||
|
||||
private:
|
||||
inline VPIHandleNode* get() const;
|
||||
};
|
||||
} // namespace codegen
|
||||
} // namespace tvm
|
||||
#endif // TVM_CODEGEN_VERILOG_VPI_SESSION_H_
|
|
@ -0,0 +1,106 @@
|
|||
/*!
|
||||
* Copyright (c) 2017 by Contributors
|
||||
* \file pipe.h
|
||||
* \brief Platform independent pipe, used for IPC.
|
||||
*/
|
||||
#ifndef TVM_COMMON_PIPE_H_
|
||||
#define TVM_COMMON_PIPE_H_
|
||||
|
||||
#include <dmlc/logging.h>
|
||||
#include <dmlc/io.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#endif
|
||||
|
||||
namespace tvm {
|
||||
namespace common {
|
||||
|
||||
/*! \brief Platform independent pipe */
|
||||
class Pipe : public dmlc::Stream {
|
||||
public:
|
||||
#ifdef _WIN32
|
||||
using PipeHandle = HANDLE;
|
||||
#else
|
||||
using PipeHandle = int;
|
||||
#endif
|
||||
/*! \brief Construct a pipe from system handle. */
|
||||
explicit Pipe(int64_t handle)
|
||||
: handle_(static_cast<PipeHandle>(handle)) {}
|
||||
/*! \brief destructor */
|
||||
~Pipe() {
|
||||
Flush();
|
||||
}
|
||||
using Stream::Read;
|
||||
using Stream::Write;
|
||||
/*!
|
||||
* \brief reads data from a file descriptor
|
||||
* \param ptr pointer to a memory buffer
|
||||
* \param size block size
|
||||
* \return the size of data read
|
||||
*/
|
||||
size_t Read(void *ptr, size_t size) final {
|
||||
if (size == 0) return 0;
|
||||
#ifdef _WIN32
|
||||
DWORD nread;
|
||||
CHECK(ReadFile(handle_, static_cast<TCHAR*>(ptr),
|
||||
&nread, nullptr))
|
||||
<< "Read Error: " << GetLastError();
|
||||
#else
|
||||
ssize_t nread;
|
||||
nread = read(handle_, ptr, size);
|
||||
CHECK_GE(nread, 0)
|
||||
<< "Write Error: " << strerror(errno);
|
||||
#endif
|
||||
return static_cast<size_t>(nread);
|
||||
}
|
||||
/*!
|
||||
* \brief write data to a file descriptor
|
||||
* \param ptr pointer to a memory buffer
|
||||
* \param size block size
|
||||
* \return the size of data read
|
||||
*/
|
||||
void Write(const void *ptr, size_t size) final {
|
||||
if (size == 0) return;
|
||||
#ifdef _WIN32
|
||||
DWORD nwrite;
|
||||
CHECK(WriteFile(handle_, static_cast<const TCHAR*>(ptr),
|
||||
&nwrite, nullptr) &&
|
||||
static_cast<size_t>(nwrite) == size)
|
||||
<< "Write Error: " << GetLastError();
|
||||
#else
|
||||
ssize_t nwrite;
|
||||
nwrite = write(handle_, ptr, size);
|
||||
CHECK_EQ(static_cast<size_t>(nwrite), size)
|
||||
<< "Write Error: " << strerror(errno);
|
||||
#endif
|
||||
}
|
||||
/*!
|
||||
* \brief Flush the pipe;
|
||||
*/
|
||||
void Flush() {
|
||||
#ifdef _WIN32
|
||||
FlushFileBuffers(handle_);
|
||||
#endif
|
||||
}
|
||||
/*! \brief close the pipe */
|
||||
void Close() {
|
||||
#ifdef _WIN32
|
||||
CloseHandle(handle_);
|
||||
#else
|
||||
close(handle_);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
PipeHandle handle_;
|
||||
};
|
||||
} // namespace common
|
||||
} // namespace tvm
|
||||
|
||||
#endif // TVM_COMMON_PIPE_H_
|
|
@ -0,0 +1,10 @@
|
|||
# rules for gtest
|
||||
.PHONY: iverilog
|
||||
|
||||
iverilog: | ${CACHE_PREFIX}/bin/vvp
|
||||
|
||||
${CACHE_PREFIX}/bin/vvp:
|
||||
rm -rf verilog-10.1.tar.gz verilog-10.1
|
||||
wget ftp://icarus.com/pub/eda/verilog/v10/verilog-10.1.tar.gz
|
||||
tar xf verilog-10.1.tar.gz
|
||||
cd verilog-10.1;./configure --prefix=${CACHE_PREFIX}; make install
|
|
@ -30,6 +30,16 @@ else
|
|||
echo "USE_OPENCL=0" >> config.mk
|
||||
fi
|
||||
|
||||
if [ ${TASK} == "verilog_test" ] || [ ${TASK} == "all_test" ]; then
|
||||
if [ ! ${TRAVIS_OS_NAME} == "osx" ]; then
|
||||
echo ${PATH}
|
||||
make -f tests/travis/packages.mk iverilog
|
||||
make verilog || exit -1
|
||||
make all || exit -1
|
||||
nosetests -v tests/verilog || exit -1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ${TASK} == "cpp_test" ] || [ ${TASK} == "all_test" ]; then
|
||||
make -f dmlc-core/scripts/packages.mk gtest
|
||||
make test || exit -1
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import tvm
|
||||
import os
|
||||
from tvm.addon import verilog
|
||||
|
||||
def test_counter():
|
||||
# Start a new session by run simulation on test_counter.v
|
||||
# Find file will search root/verilog and root/tests/verilog
|
||||
sess = verilog.session([
|
||||
verilog.find_file("test_counter.v"),
|
||||
verilog.find_file("example_counter.v")
|
||||
])
|
||||
# Get the handles by their names
|
||||
rst = sess.main.rst
|
||||
counter = sess.main.counter
|
||||
cnt = sess.main["counter_unit1"]
|
||||
assert(counter.name == "main.counter")
|
||||
assert(counter.size == 4)
|
||||
rst.put_int(1)
|
||||
# This will advance the cycle to next pos-edge of clk.
|
||||
sess.yield_until_posedge()
|
||||
rst.put_int(0)
|
||||
|
||||
for i in range(10):
|
||||
# get value of counter.
|
||||
assert(counter.get_int() == i)
|
||||
sess.yield_until_posedge()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_counter()
|
|
@ -0,0 +1,15 @@
|
|||
module main();
|
||||
parameter PER = 10;
|
||||
reg clk;
|
||||
reg rst;
|
||||
wire [3:0] counter;
|
||||
|
||||
counter counter_unit1(.clk(clk), .rst(rst), .out(counter));
|
||||
always begin
|
||||
#(PER/2) clk =~ clk;
|
||||
end
|
||||
initial begin
|
||||
// This will allow tvm session to be called every cycle.
|
||||
$tvm_session(clk);
|
||||
end
|
||||
endmodule
|
|
@ -0,0 +1,36 @@
|
|||
import tvm
|
||||
import os
|
||||
from tvm.addon import verilog
|
||||
|
||||
def test_loop():
|
||||
sess = verilog.session([
|
||||
verilog.find_file("test_loop.v")
|
||||
])
|
||||
# Get the handles by their names
|
||||
rst = sess.main.rst
|
||||
init = sess.main.init
|
||||
iter0 = sess.main.iter0
|
||||
iter1 = sess.main.iter1
|
||||
enable = sess.main.enable
|
||||
invalid = sess.main.done
|
||||
|
||||
rst.put_int(1)
|
||||
# This will advance the cycle to next pos-edge of clk.
|
||||
sess.yield_until_posedge()
|
||||
rst.put_int(0)
|
||||
init.put_int(1)
|
||||
sess.yield_until_posedge()
|
||||
enable.put_int(1)
|
||||
init.put_int(0)
|
||||
|
||||
for i in range(0, 3):
|
||||
for j in range(0, 4):
|
||||
while invalid.get_int():
|
||||
sess.yield_until_posedge()
|
||||
assert(iter1.get_int() == i)
|
||||
assert(iter0.get_int() == j)
|
||||
sess.yield_until_posedge()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_loop()
|
|
@ -0,0 +1,24 @@
|
|||
`include "tvm_marcos.v"
|
||||
|
||||
module main();
|
||||
parameter PER = 10;
|
||||
reg clk;
|
||||
reg rst;
|
||||
wire init;
|
||||
wire done;
|
||||
wire enable;
|
||||
|
||||
`NORMAL_LOOP_LEAF(iter0, 4, init0, enable, iter0_done, 0, 4, 1)
|
||||
`NORMAL_LOOP_NEST(iter1, 4, init, iter0_done, iter1_done, 0, 3, 1, init0)
|
||||
|
||||
assign done = iter0_done;
|
||||
|
||||
always begin
|
||||
#(PER/2) clk =~ clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
// This will allow tvm session to be called every cycle.
|
||||
$tvm_session(clk);
|
||||
end
|
||||
endmodule
|
|
@ -0,0 +1,17 @@
|
|||
// a counter that counts up
|
||||
// Use as example of testcaase
|
||||
module counter(clk, rst, out);
|
||||
input clk;
|
||||
input rst;
|
||||
output [3:0] out;
|
||||
reg [3:0] counter;
|
||||
assign out = counter;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
counter <= 0;
|
||||
end else begin
|
||||
counter <= counter +1;
|
||||
end
|
||||
end
|
||||
endmodule
|
|
@ -0,0 +1,70 @@
|
|||
// Leaf of a normal loop nest
|
||||
// Starts at done = 1
|
||||
// Need init to reset to done = 0
|
||||
// increases when enabled = 1
|
||||
`define NORMAL_LOOP_LEAF(iter, width, init, enable, done, min, max, incr)\
|
||||
reg [width-1:0] iter;\
|
||||
reg valid;\
|
||||
reg done;\
|
||||
always@(posedge clk) begin\
|
||||
if(rst) begin\
|
||||
iter <= 0;\
|
||||
done <= 1;\
|
||||
end else if(init) begin\
|
||||
iter <= (min);\
|
||||
done <= 0;\
|
||||
end else if(done) begin\
|
||||
iter <= 0;\
|
||||
done <= 1;\
|
||||
end else if(enable) begin\
|
||||
if (iter < ((max)-(incr))) begin\
|
||||
iter <= iter + (incr);\
|
||||
done <= 0;\
|
||||
end else begin\
|
||||
iter <= 0;\
|
||||
done <= 1;\
|
||||
end\
|
||||
end else begin\
|
||||
iter <= iter;\
|
||||
done <= done;\
|
||||
end\
|
||||
end
|
||||
|
||||
// Normal loop nest that can connect to a child which is a normal loop
|
||||
`define NORMAL_LOOP_NEST(iter, width, init, body_done, done, min, max, incr, body_init)\
|
||||
reg [width-1:0] iter;\
|
||||
reg done;\
|
||||
reg body_init;\
|
||||
always@(posedge clk) begin\
|
||||
if(rst) begin\
|
||||
iter <= 0;\
|
||||
done <= 1;\
|
||||
body_init <= 0;\
|
||||
end else if(init) begin\
|
||||
iter <= (min);\
|
||||
done <= 0;\
|
||||
body_init <= 1;\
|
||||
end else if(done) begin\
|
||||
iter <= 0;\
|
||||
done <= 1;\
|
||||
body_init <= 0;\
|
||||
end else if (body_init) begin\
|
||||
iter <= iter;\
|
||||
done <= done;\
|
||||
body_init <= 0;\
|
||||
end else if (body_done) begin\
|
||||
if (iter < ((max)-(incr))) begin\
|
||||
iter <= iter + (incr);\
|
||||
done <= 0;\
|
||||
body_init <= 1;\
|
||||
end else begin\
|
||||
iter <= 0;\
|
||||
done <= 1;\
|
||||
body_init <= 0;\
|
||||
end\
|
||||
end else begin\
|
||||
iter <= iter;\
|
||||
done <= done;\
|
||||
body_init <= 0;\
|
||||
end\
|
||||
end
|
|
@ -0,0 +1,244 @@
|
|||
/*!
|
||||
* 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 {
|
||||
static_assert(sizeof(vpiHandle) == sizeof(VPIRawHandle),
|
||||
"VPI handle condition");
|
||||
// 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);
|
||||
CHECK(vpi_scan(arg_iter) == nullptr)
|
||||
<< "tvm_session can only take in one clock";
|
||||
PutInt(clock_, 0);
|
||||
}
|
||||
int Callback() {
|
||||
if (GetInt(clock_)) {
|
||||
try {
|
||||
return AtPosEedge();
|
||||
} 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 positive edge.
|
||||
int AtPosEedge() {
|
||||
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 kGetName: {
|
||||
CHECK(reader_.Read(&handle));
|
||||
std::string name = vpi_get_str(
|
||||
vpiFullName, static_cast<vpiHandle>(handle));
|
||||
writer_.Write(kSuccess);
|
||||
writer_.Write(name);
|
||||
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 kGetSize: {
|
||||
CHECK(reader_.Read(&handle));
|
||||
value = vpi_get(vpiSize, static_cast<vpiHandle>(handle));
|
||||
writer_.Write(kSuccess);
|
||||
writer_.Write(value);
|
||||
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);
|
||||
size_t nwords = vec_buf_.size();
|
||||
svec_buf_.resize(nwords);
|
||||
reader_.Read(&vec_buf_[0], nwords * sizeof(s_vpi_vecval));
|
||||
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;
|
||||
value_s.format = vpiVectorVal;
|
||||
value_s.value.vector = &svec_buf_[0];
|
||||
vpi_put_value(h, &value_s, 0, vpiNoDelay);
|
||||
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;
|
||||
value_s.format = vpiIntVal;
|
||||
value_s.value.integer = value;
|
||||
vpi_put_value(h, &value_s, 0, vpiNoDelay);
|
||||
}
|
||||
// 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"
|
|
@ -0,0 +1,42 @@
|
|||
/*!
|
||||
* Copyright (c) 2017 by Contributors
|
||||
* \file tvm_vpi.h
|
||||
* \brief Messages passed around VPI used for simulation.
|
||||
*/
|
||||
#ifndef VERILOG_TVM_VPI_H_
|
||||
#define VERILOG_TVM_VPI_H_
|
||||
|
||||
namespace tvm {
|
||||
namespace vpi {
|
||||
|
||||
enum VPICallCode : int {
|
||||
kGetHandleByName,
|
||||
kGetHandleByIndex,
|
||||
kGetName,
|
||||
kGetInt32,
|
||||
kPutInt32,
|
||||
kGetSize,
|
||||
kGetVec,
|
||||
kPutVec,
|
||||
kYield,
|
||||
kShutDown
|
||||
};
|
||||
|
||||
enum VPIReturnCode : int {
|
||||
kPosEdgeTrigger = 0,
|
||||
kSuccess = 1,
|
||||
kFail = 2
|
||||
};
|
||||
|
||||
/*! \brief The vector value used in trasmission */
|
||||
struct VPIVecVal {
|
||||
int aval;
|
||||
int bval;
|
||||
};
|
||||
|
||||
/*! \brief User facing vpi handle. */
|
||||
typedef void* VPIRawHandle;
|
||||
|
||||
} // namespace vpi
|
||||
} // namespace tvm
|
||||
#endif // VERILOG_TVM_VPI_H_
|
|
@ -0,0 +1,10 @@
|
|||
VPI_CFLAGS=`iverilog-vpi --cflags`
|
||||
VPI_LDLAGS=`iverilog-vpi --ldlags`
|
||||
|
||||
VER_SRCS = $(wildcard verilog/*.v)
|
||||
|
||||
VER_LIBS=lib/tvm_vpi.vpi
|
||||
|
||||
lib/tvm_vpi.vpi: verilog/tvm_vpi.cc verilog/tvm_vpi.h
|
||||
@mkdir -p $(@D)
|
||||
$(CXX) $(VPI_CFLAGS) $(CFLAGS) -shared -o $@ $(filter %.cc, $^) $(LDFLAGS) $(VPI_LDFLAGS)
|
Загрузка…
Ссылка в новой задаче