Simulator will be rewritten
This commit is contained in:
parent
dec2e23fc7
commit
f1dc008d32
|
@ -1,229 +0,0 @@
|
||||||
import warnings
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from migen.fhdl.std import *
|
|
||||||
from migen.fhdl.structure import _Fragment
|
|
||||||
from migen.fhdl import verilog
|
|
||||||
from migen.sim.ipc import *
|
|
||||||
from migen.sim import icarus
|
|
||||||
|
|
||||||
|
|
||||||
class TopLevel:
|
|
||||||
def __init__(self, vcd_name=None, vcd_level=1,
|
|
||||||
top_name="top", dut_type="dut", dut_name="dut",
|
|
||||||
cd_name="sys", clk_period=10):
|
|
||||||
self.vcd_name = vcd_name
|
|
||||||
self.vcd_level = vcd_level
|
|
||||||
self.top_name = top_name
|
|
||||||
self.dut_type = dut_type
|
|
||||||
self.dut_name = dut_name
|
|
||||||
|
|
||||||
self._cd_name = cd_name
|
|
||||||
self._clk_period = clk_period
|
|
||||||
|
|
||||||
cd = ClockDomain(self._cd_name)
|
|
||||||
self.clock_domains = [cd]
|
|
||||||
self.ios = {cd.clk, cd.rst}
|
|
||||||
|
|
||||||
def get(self, sockaddr):
|
|
||||||
if sys.platform == "win32":
|
|
||||||
sockaddr = sockaddr[0] # Get the IP address only
|
|
||||||
|
|
||||||
template1 = """`timescale 1ns / 1ps
|
|
||||||
|
|
||||||
module {top_name}();
|
|
||||||
|
|
||||||
reg {clk_name};
|
|
||||||
reg {rst_name};
|
|
||||||
|
|
||||||
initial begin
|
|
||||||
{rst_name} <= 1'b1;
|
|
||||||
@(posedge {clk_name});
|
|
||||||
{rst_name} <= 1'b0;
|
|
||||||
end
|
|
||||||
|
|
||||||
always begin
|
|
||||||
{clk_name} <= 1'b0;
|
|
||||||
#{hclk_period};
|
|
||||||
{clk_name} <= 1'b1;
|
|
||||||
#{hclk_period};
|
|
||||||
end
|
|
||||||
|
|
||||||
{dut_type} {dut_name}(
|
|
||||||
.{rst_name}({rst_name}),
|
|
||||||
.{clk_name}({clk_name})
|
|
||||||
);
|
|
||||||
|
|
||||||
initial $migensim_connect("{sockaddr}");
|
|
||||||
always @(posedge {clk_name}) $migensim_tick;
|
|
||||||
"""
|
|
||||||
template2 = """
|
|
||||||
initial begin
|
|
||||||
$dumpfile("{vcd_name}");
|
|
||||||
$dumpvars({vcd_level}, {dut_name});
|
|
||||||
end
|
|
||||||
"""
|
|
||||||
r = template1.format(top_name=self.top_name,
|
|
||||||
dut_type=self.dut_type,
|
|
||||||
dut_name=self.dut_name,
|
|
||||||
clk_name=self._cd_name + "_clk",
|
|
||||||
rst_name=self._cd_name + "_rst",
|
|
||||||
hclk_period=str(self._clk_period/2),
|
|
||||||
sockaddr=sockaddr)
|
|
||||||
if self.vcd_name is not None:
|
|
||||||
r += template2.format(vcd_name=self.vcd_name,
|
|
||||||
vcd_level=str(self.vcd_level),
|
|
||||||
dut_name=self.dut_name)
|
|
||||||
r += "\nendmodule"
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
class Simulator:
|
|
||||||
def __init__(self, fragment, top_level=None, sim_runner=None, sockaddr="simsocket", **vopts):
|
|
||||||
if not isinstance(fragment, _Fragment):
|
|
||||||
fragment = fragment.get_fragment()
|
|
||||||
if top_level is None:
|
|
||||||
top_level = TopLevel()
|
|
||||||
if sim_runner is None:
|
|
||||||
sim_runner = icarus.Runner()
|
|
||||||
self.top_level = top_level
|
|
||||||
if sys.platform == "win32":
|
|
||||||
sockaddr = ("127.0.0.1", 50007)
|
|
||||||
self.ipc = Initiator(sockaddr)
|
|
||||||
else:
|
|
||||||
self.ipc = Initiator(sockaddr)
|
|
||||||
|
|
||||||
self.sim_runner = sim_runner
|
|
||||||
|
|
||||||
c_top = self.top_level.get(sockaddr)
|
|
||||||
|
|
||||||
fragment = fragment + _Fragment(clock_domains=top_level.clock_domains)
|
|
||||||
c_fragment = verilog.convert(fragment,
|
|
||||||
ios=self.top_level.ios,
|
|
||||||
name=self.top_level.dut_type,
|
|
||||||
**vopts)
|
|
||||||
self.namespace = c_fragment.ns
|
|
||||||
|
|
||||||
self.cycle_counter = -1
|
|
||||||
|
|
||||||
self.sim_runner = sim_runner
|
|
||||||
self.sim_runner.start(c_top, c_fragment)
|
|
||||||
self.ipc.accept()
|
|
||||||
reply = self.ipc.recv()
|
|
||||||
assert(isinstance(reply, MessageTick))
|
|
||||||
|
|
||||||
self.sim_functions = fragment.sim
|
|
||||||
self.active_sim_functions = set(f for f in fragment.sim if not hasattr(f, "passive") or not f.passive)
|
|
||||||
self.unreferenced = {}
|
|
||||||
|
|
||||||
def run(self, ncycles=None):
|
|
||||||
counter = 0
|
|
||||||
|
|
||||||
if self.active_sim_functions:
|
|
||||||
if ncycles is None:
|
|
||||||
def continue_simulation():
|
|
||||||
return bool(self.active_sim_functions)
|
|
||||||
else:
|
|
||||||
def continue_simulation():
|
|
||||||
return self.active_sim_functions and counter < ncycles
|
|
||||||
else:
|
|
||||||
if ncycles is None:
|
|
||||||
raise ValueError("No active simulation function present - must specify ncycles to end simulation")
|
|
||||||
def continue_simulation():
|
|
||||||
return counter < ncycles
|
|
||||||
|
|
||||||
while continue_simulation():
|
|
||||||
self.cycle_counter += 1
|
|
||||||
counter += 1
|
|
||||||
self.ipc.send(MessageGo())
|
|
||||||
reply = self.ipc.recv()
|
|
||||||
assert(isinstance(reply, MessageTick))
|
|
||||||
|
|
||||||
del_list = []
|
|
||||||
for s in self.sim_functions:
|
|
||||||
try:
|
|
||||||
s(self)
|
|
||||||
except StopSimulation:
|
|
||||||
del_list.append(s)
|
|
||||||
for s in del_list:
|
|
||||||
self.sim_functions.remove(s)
|
|
||||||
try:
|
|
||||||
self.active_sim_functions.remove(s)
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_unreferenced(self, item, index):
|
|
||||||
try:
|
|
||||||
return self.unreferenced[(item, index)]
|
|
||||||
except KeyError:
|
|
||||||
if isinstance(item, Memory):
|
|
||||||
try:
|
|
||||||
init = item.init[index]
|
|
||||||
except (TypeError, IndexError):
|
|
||||||
init = 0
|
|
||||||
else:
|
|
||||||
init = item.reset
|
|
||||||
self.unreferenced[(item, index)] = init
|
|
||||||
return init
|
|
||||||
|
|
||||||
def rd(self, item, index=0):
|
|
||||||
try:
|
|
||||||
name = self.top_level.top_name + "." \
|
|
||||||
+ self.top_level.dut_name + "." \
|
|
||||||
+ self.namespace.get_name(item)
|
|
||||||
self.ipc.send(MessageRead(name, Int32(index)))
|
|
||||||
reply = self.ipc.recv()
|
|
||||||
assert(isinstance(reply, MessageReadReply))
|
|
||||||
value = reply.value
|
|
||||||
except KeyError:
|
|
||||||
value = self.get_unreferenced(item, index)
|
|
||||||
if isinstance(item, Memory):
|
|
||||||
signed = False
|
|
||||||
nbits = item.width
|
|
||||||
else:
|
|
||||||
signed = item.signed
|
|
||||||
nbits = flen(item)
|
|
||||||
value = value & (2**nbits - 1)
|
|
||||||
if signed and (value & 2**(nbits - 1)):
|
|
||||||
value -= 2**nbits
|
|
||||||
return value
|
|
||||||
|
|
||||||
def wr(self, item, value, index=0):
|
|
||||||
if isinstance(item, Memory):
|
|
||||||
nbits = item.width
|
|
||||||
else:
|
|
||||||
nbits = flen(item)
|
|
||||||
if value < 0:
|
|
||||||
value += 2**nbits
|
|
||||||
assert(value >= 0 and value < 2**nbits)
|
|
||||||
try:
|
|
||||||
name = self.top_level.top_name + "." \
|
|
||||||
+ self.top_level.dut_name + "." \
|
|
||||||
+ self.namespace.get_name(item)
|
|
||||||
self.ipc.send(MessageWrite(name, Int32(index), value))
|
|
||||||
except KeyError:
|
|
||||||
self.unreferenced[(item, index)] = value
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
if hasattr(self, "ipc"):
|
|
||||||
warnings.warn("call Simulator.close() to clean up "
|
|
||||||
"or use it as a contextmanager", DeprecationWarning)
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self.ipc.close()
|
|
||||||
self.sim_runner.close()
|
|
||||||
del self.ipc
|
|
||||||
del self.sim_runner
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, type, value, traceback):
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
|
|
||||||
def run_simulation(fragment, ncycles=None, vcd_name=None, **kwargs):
|
|
||||||
with Simulator(fragment, TopLevel(vcd_name), icarus.Runner(**kwargs)) as s:
|
|
||||||
s.run(ncycles)
|
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
# Copyright (C) 2012 Vermeer Manufacturing Co.
|
|
||||||
# License: GPLv3 with additional permissions (see README).
|
|
||||||
|
|
||||||
import subprocess
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
|
|
||||||
|
|
||||||
class Runner:
|
|
||||||
def __init__(self, options=None, extra_files=None, top_file="migensim_top.v", dut_file="migensim_dut.v", vvp_file=None, keep_files=False):
|
|
||||||
if extra_files is None: extra_files = []
|
|
||||||
if vvp_file is None: vvp_file = dut_file + "vp"
|
|
||||||
if options is None: options = []
|
|
||||||
self.options = options
|
|
||||||
self.extra_files = extra_files
|
|
||||||
self.top_file = top_file
|
|
||||||
self.dut_file = dut_file
|
|
||||||
self.vvp_file = vvp_file
|
|
||||||
self.data_files = []
|
|
||||||
self.keep_files = keep_files
|
|
||||||
|
|
||||||
def start(self, c_top, c_dut):
|
|
||||||
with open(self.top_file, "w") as f:
|
|
||||||
f.write(c_top)
|
|
||||||
c_dut.write(self.dut_file)
|
|
||||||
self.data_files += c_dut.data_files.keys()
|
|
||||||
subprocess.check_call(["iverilog", "-o", self.vvp_file] + self.options + [self.top_file, self.dut_file] + self.extra_files)
|
|
||||||
self.process = subprocess.Popen(["vvp", "-mmigensim", "-Mvpi", self.vvp_file])
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
if hasattr(self, "process"):
|
|
||||||
self.process.terminate()
|
|
||||||
if self.process.poll() is None:
|
|
||||||
time.sleep(.1)
|
|
||||||
self.process.kill()
|
|
||||||
self.process.wait()
|
|
||||||
if not self.keep_files:
|
|
||||||
for f in [self.top_file, self.dut_file, self.vvp_file] + self.data_files:
|
|
||||||
try:
|
|
||||||
os.remove(f)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
self.data_files.clear()
|
|
228
migen/sim/ipc.py
228
migen/sim/ipc.py
|
@ -1,228 +0,0 @@
|
||||||
# Copyright (C) 2012 Vermeer Manufacturing Co.
|
|
||||||
# License: GPLv3 with additional permissions (see README).
|
|
||||||
|
|
||||||
import socket
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import struct
|
|
||||||
|
|
||||||
if sys.platform == "win32":
|
|
||||||
header_len = 2
|
|
||||||
|
|
||||||
#
|
|
||||||
# Message classes
|
|
||||||
#
|
|
||||||
|
|
||||||
class Int32(int):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Message:
|
|
||||||
def __init__(self, *pvalues):
|
|
||||||
for parameter, value in zip(self.parameters, pvalues):
|
|
||||||
setattr(self, parameter[1], parameter[0](value))
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
p = []
|
|
||||||
for parameter in self.parameters:
|
|
||||||
p.append(parameter[1] + "=" + str(getattr(self, parameter[1])))
|
|
||||||
if p:
|
|
||||||
pf = " " + " ".join(p)
|
|
||||||
else:
|
|
||||||
pf = ""
|
|
||||||
return "<" + self.__class__.__name__ + pf + ">"
|
|
||||||
|
|
||||||
|
|
||||||
class MessageTick(Message):
|
|
||||||
code = 0
|
|
||||||
parameters = []
|
|
||||||
|
|
||||||
|
|
||||||
class MessageGo(Message):
|
|
||||||
code = 1
|
|
||||||
parameters = []
|
|
||||||
|
|
||||||
|
|
||||||
class MessageWrite(Message):
|
|
||||||
code = 2
|
|
||||||
parameters = [(str, "name"), (Int32, "index"), (int, "value")]
|
|
||||||
|
|
||||||
|
|
||||||
class MessageRead(Message):
|
|
||||||
code = 3
|
|
||||||
parameters = [(str, "name"), (Int32, "index")]
|
|
||||||
|
|
||||||
|
|
||||||
class MessageReadReply(Message):
|
|
||||||
code = 4
|
|
||||||
parameters = [(int, "value")]
|
|
||||||
|
|
||||||
message_classes = [MessageTick, MessageGo, MessageWrite, MessageRead, MessageReadReply]
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Packing
|
|
||||||
#
|
|
||||||
|
|
||||||
def _pack_int(v):
|
|
||||||
if v == 0:
|
|
||||||
p = [1, 0]
|
|
||||||
else:
|
|
||||||
p = []
|
|
||||||
while v != 0:
|
|
||||||
p.append(v & 0xff)
|
|
||||||
v >>= 8
|
|
||||||
p.insert(0, len(p))
|
|
||||||
return p
|
|
||||||
|
|
||||||
|
|
||||||
def _pack_str(v):
|
|
||||||
p = [ord(c) for c in v]
|
|
||||||
p.append(0)
|
|
||||||
return p
|
|
||||||
|
|
||||||
|
|
||||||
def _pack_int16(v):
|
|
||||||
return [v & 0xff,
|
|
||||||
(v & 0xff00) >> 8]
|
|
||||||
|
|
||||||
|
|
||||||
def _pack_int32(v):
|
|
||||||
return [
|
|
||||||
v & 0xff,
|
|
||||||
(v & 0xff00) >> 8,
|
|
||||||
(v & 0xff0000) >> 16,
|
|
||||||
(v & 0xff000000) >> 24
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def _pack(message):
|
|
||||||
r = [message.code]
|
|
||||||
for t, p in message.parameters:
|
|
||||||
value = getattr(message, p)
|
|
||||||
assert(isinstance(value, t))
|
|
||||||
if t == int:
|
|
||||||
r += _pack_int(value)
|
|
||||||
elif t == str:
|
|
||||||
r += _pack_str(value)
|
|
||||||
elif t == Int32:
|
|
||||||
r += _pack_int32(value)
|
|
||||||
else:
|
|
||||||
raise TypeError
|
|
||||||
if sys.platform == "win32":
|
|
||||||
size = _pack_int16(len(r) + header_len)
|
|
||||||
r = size + r
|
|
||||||
return bytes(r)
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Unpacking
|
|
||||||
#
|
|
||||||
|
|
||||||
def _unpack_int(i, nchunks=None):
|
|
||||||
v = 0
|
|
||||||
power = 1
|
|
||||||
if nchunks is None:
|
|
||||||
nchunks = next(i)
|
|
||||||
for j in range(nchunks):
|
|
||||||
v += power*next(i)
|
|
||||||
power *= 256
|
|
||||||
return v
|
|
||||||
|
|
||||||
|
|
||||||
def _unpack_str(i):
|
|
||||||
v = ""
|
|
||||||
c = next(i)
|
|
||||||
while c:
|
|
||||||
v += chr(c)
|
|
||||||
c = next(i)
|
|
||||||
return v
|
|
||||||
|
|
||||||
|
|
||||||
def _unpack(message):
|
|
||||||
i = iter(message)
|
|
||||||
code = next(i)
|
|
||||||
msgclass = next(filter(lambda x: x.code == code, message_classes))
|
|
||||||
pvalues = []
|
|
||||||
for t, p in msgclass.parameters:
|
|
||||||
if t == int:
|
|
||||||
v = _unpack_int(i)
|
|
||||||
elif t == str:
|
|
||||||
v = _unpack_str(i)
|
|
||||||
elif t == Int32:
|
|
||||||
v = _unpack_int(i, 4)
|
|
||||||
else:
|
|
||||||
raise TypeError
|
|
||||||
pvalues.append(v)
|
|
||||||
return msgclass(*pvalues)
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# I/O
|
|
||||||
#
|
|
||||||
|
|
||||||
class PacketTooLarge(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Initiator:
|
|
||||||
def __init__(self, sockaddr):
|
|
||||||
self.sockaddr = sockaddr
|
|
||||||
if sys.platform == "win32":
|
|
||||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
else:
|
|
||||||
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
|
|
||||||
self._cleanup_file()
|
|
||||||
self.socket.bind(self.sockaddr)
|
|
||||||
self.socket.listen(1)
|
|
||||||
|
|
||||||
self.ipc_rxbuffer = bytearray()
|
|
||||||
|
|
||||||
def _cleanup_file(self):
|
|
||||||
try:
|
|
||||||
os.remove(self.sockaddr)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def accept(self):
|
|
||||||
self.conn, addr = self.socket.accept()
|
|
||||||
|
|
||||||
def send(self, message):
|
|
||||||
self.conn.send(_pack(message))
|
|
||||||
|
|
||||||
def recv_packet(self, maxlen):
|
|
||||||
if sys.platform == "win32":
|
|
||||||
while len(self.ipc_rxbuffer) < header_len:
|
|
||||||
self.ipc_rxbuffer += self.conn.recv(maxlen)
|
|
||||||
packet_len = struct.unpack("<H", self.ipc_rxbuffer[:header_len])[0]
|
|
||||||
while len(self.ipc_rxbuffer) < packet_len:
|
|
||||||
self.ipc_rxbuffer += self.conn.recv(maxlen)
|
|
||||||
packet = self.ipc_rxbuffer[header_len:packet_len]
|
|
||||||
self.ipc_rxbuffer = self.ipc_rxbuffer[packet_len:]
|
|
||||||
else:
|
|
||||||
packet = self.conn.recv(maxlen)
|
|
||||||
return packet
|
|
||||||
|
|
||||||
def recv(self):
|
|
||||||
maxlen = 2048
|
|
||||||
packet = self.recv_packet(maxlen)
|
|
||||||
if len(packet) < 1:
|
|
||||||
return None
|
|
||||||
if len(packet) >= maxlen:
|
|
||||||
raise PacketTooLarge
|
|
||||||
return _unpack(packet)
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
if hasattr(self, "conn"):
|
|
||||||
self.conn.shutdown(socket.SHUT_RDWR)
|
|
||||||
self.conn.close()
|
|
||||||
if hasattr(self, "socket"):
|
|
||||||
if sys.platform == "win32":
|
|
||||||
# don't shutdown our socket since closing connection
|
|
||||||
# seems to already have done it. (trigger an error
|
|
||||||
# otherwise)
|
|
||||||
self.socket.close()
|
|
||||||
else:
|
|
||||||
self.socket.shutdown(socket.SHUT_RDWR)
|
|
||||||
self.socket.close()
|
|
||||||
self._cleanup_file()
|
|
|
@ -1,112 +0,0 @@
|
||||||
from migen.fhdl.structure import Signal, StopSimulation
|
|
||||||
from migen.fhdl.specials import Memory
|
|
||||||
|
|
||||||
|
|
||||||
class MemoryProxy:
|
|
||||||
def __init__(self, simulator, obj):
|
|
||||||
self.simulator = simulator
|
|
||||||
self._simproxy_obj = obj
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
if isinstance(key, int):
|
|
||||||
return self.simulator.rd(self._simproxy_obj, key)
|
|
||||||
else:
|
|
||||||
start, stop, step = key.indices(self._simproxy_obj.depth)
|
|
||||||
return [self.simulator.rd(self._simproxy_obj, i) for i in range(start, stop, step)]
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
if isinstance(key, int):
|
|
||||||
self.simulator.wr(self._simproxy_obj, key, value)
|
|
||||||
else:
|
|
||||||
start, stop, step = key.indices(self.__obj.depth)
|
|
||||||
if len(value) != (stop - start)//step:
|
|
||||||
raise ValueError
|
|
||||||
for i, v in zip(range(start, stop, step), value):
|
|
||||||
self.simulator.wr(self._simproxy_obj, i, v)
|
|
||||||
|
|
||||||
|
|
||||||
class Proxy:
|
|
||||||
def __init__(self, simulator, obj):
|
|
||||||
object.__setattr__(self, "simulator", simulator)
|
|
||||||
object.__setattr__(self, "_simproxy_obj", obj)
|
|
||||||
|
|
||||||
def __process_get(self, item):
|
|
||||||
if isinstance(item, Signal):
|
|
||||||
return self.simulator.rd(item)
|
|
||||||
elif isinstance(item, Memory):
|
|
||||||
return MemoryProxy(self.simulator, item)
|
|
||||||
else:
|
|
||||||
return Proxy(self.simulator, item)
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
return self.__process_get(getattr(self._simproxy_obj, name))
|
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
|
||||||
item = getattr(self._simproxy_obj, name)
|
|
||||||
assert(isinstance(item, Signal))
|
|
||||||
self.simulator.wr(item, value)
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
return self.__process_get(self._simproxy_obj[key])
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
item = self._simproxy_obj[key]
|
|
||||||
assert(isinstance(item, Signal))
|
|
||||||
self.simulator.wr(item, value)
|
|
||||||
|
|
||||||
|
|
||||||
def gen_sim(simg):
|
|
||||||
gens = dict()
|
|
||||||
resume_cycle = 0
|
|
||||||
|
|
||||||
def do_simulation(s):
|
|
||||||
nonlocal resume_cycle, gens
|
|
||||||
|
|
||||||
if isinstance(s, Proxy):
|
|
||||||
simulator = s.simulator
|
|
||||||
else:
|
|
||||||
simulator = s
|
|
||||||
|
|
||||||
if simulator.cycle_counter >= resume_cycle:
|
|
||||||
try:
|
|
||||||
gen = gens[simulator]
|
|
||||||
except KeyError:
|
|
||||||
gen = simg(s)
|
|
||||||
gens[simulator] = gen
|
|
||||||
try:
|
|
||||||
n = next(gen)
|
|
||||||
except StopIteration:
|
|
||||||
del gens[simulator]
|
|
||||||
raise StopSimulation
|
|
||||||
else:
|
|
||||||
if n is None:
|
|
||||||
n = 1
|
|
||||||
resume_cycle = simulator.cycle_counter + n
|
|
||||||
|
|
||||||
if hasattr(simg, "passive"):
|
|
||||||
do_simulation.passive = simg.passive
|
|
||||||
|
|
||||||
return do_simulation
|
|
||||||
|
|
||||||
|
|
||||||
def proxy_sim(target, simf):
|
|
||||||
proxies = dict()
|
|
||||||
|
|
||||||
def do_simulation(simulator):
|
|
||||||
nonlocal proxies
|
|
||||||
|
|
||||||
try:
|
|
||||||
proxy = proxies[simulator]
|
|
||||||
except KeyError:
|
|
||||||
proxy = Proxy(simulator, target)
|
|
||||||
proxies[simulator] = proxy
|
|
||||||
try:
|
|
||||||
simf(proxy)
|
|
||||||
except StopSimulation:
|
|
||||||
del proxies[simulator]
|
|
||||||
raise
|
|
||||||
|
|
||||||
if hasattr(simf, "passive"):
|
|
||||||
do_simulation.passive = simf.passive
|
|
||||||
|
|
||||||
return do_simulation
|
|
27
vpi/Makefile
27
vpi/Makefile
|
@ -1,27 +0,0 @@
|
||||||
INSTDIR = $(shell iverilog-vpi --install-dir)
|
|
||||||
|
|
||||||
CFLAGS = -Wall -O2 $(CFLAGS_$@)
|
|
||||||
VPI_CFLAGS := $(shell iverilog-vpi --cflags)
|
|
||||||
# Define the below flags for a Windows build.
|
|
||||||
# Make sure to run iverilog-vpi with -mingw and -ivl options if necessary!
|
|
||||||
# i.e. iverilog-vpi -mingw=C:\msys64\mingw32 -ivl=C:\msys64\mingw32
|
|
||||||
# MINGW_FLAGS=-lWs2_32
|
|
||||||
|
|
||||||
OBJ=ipc.o main.o
|
|
||||||
|
|
||||||
all: migensim.vpi
|
|
||||||
|
|
||||||
%.o: %.c
|
|
||||||
$(CC) $(CFLAGS) $(VPI_CFLAGS) -c $(INCDIRS) -o $@ $<
|
|
||||||
|
|
||||||
migensim.vpi: $(OBJ)
|
|
||||||
iverilog-vpi $(MINGW_FLAGS) --name=migensim $^
|
|
||||||
|
|
||||||
install: migensim.vpi
|
|
||||||
install -m755 -t $(INSTDIR) $^
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f $(OBJ)
|
|
||||||
rm -f migensim.vpi
|
|
||||||
|
|
||||||
.PHONY: install clean
|
|
260
vpi/ipc.c
260
vpi/ipc.c
|
@ -1,260 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2012 Vermeer Manufacturing Co.
|
|
||||||
* License: GPLv3 with additional permissions (see README).
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define WINVER 0x501
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <winsock2.h>
|
|
||||||
#include <ws2tcpip.h>
|
|
||||||
#else
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/un.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "ipc.h"
|
|
||||||
|
|
||||||
struct ipc_softc {
|
|
||||||
int socket;
|
|
||||||
go_handler h_go;
|
|
||||||
write_handler h_write;
|
|
||||||
read_handler h_read;
|
|
||||||
void *user;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define MAX_LEN 2048
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define HEADER_LEN 2
|
|
||||||
#define SOCKET_PORT "50007"
|
|
||||||
|
|
||||||
unsigned char ipc_rxbuffer[2*MAX_LEN];
|
|
||||||
int ipc_rxlen;
|
|
||||||
#else
|
|
||||||
#define HEADER_LEN 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct ipc_softc *ipc_connect(const char *sockaddr,
|
|
||||||
go_handler h_go, write_handler h_write, read_handler h_read, void *user)
|
|
||||||
{
|
|
||||||
struct ipc_softc *sc;
|
|
||||||
#ifdef _WIN32
|
|
||||||
struct addrinfo hints, *my_addrinfo;
|
|
||||||
WSADATA wsaData;
|
|
||||||
ipc_rxlen = 0;
|
|
||||||
#else
|
|
||||||
struct sockaddr_un addr;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
sc = malloc(sizeof(struct ipc_softc));
|
|
||||||
if(!sc) return NULL;
|
|
||||||
|
|
||||||
sc->h_go = h_go;
|
|
||||||
sc->h_write = h_write;
|
|
||||||
sc->h_read = h_read;
|
|
||||||
sc->user = user;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
/* Initialize Winsock. */
|
|
||||||
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
|
||||||
free(sc);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&hints, 0, sizeof(hints));
|
|
||||||
hints.ai_family = AF_INET;
|
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
|
||||||
hints.ai_protocol = IPPROTO_TCP;
|
|
||||||
|
|
||||||
if(getaddrinfo(sockaddr, SOCKET_PORT, NULL, &my_addrinfo) != 0) {
|
|
||||||
free(sc);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
sc->socket = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if(sc->socket < 0) {
|
|
||||||
free(sc);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(connect(sc->socket, my_addrinfo->ai_addr, my_addrinfo->ai_addrlen) != 0) {
|
|
||||||
close(sc->socket);
|
|
||||||
free(sc);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
sc->socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
|
||||||
if(sc->socket < 0) {
|
|
||||||
free(sc);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
addr.sun_family = AF_UNIX;
|
|
||||||
strcpy(addr.sun_path, sockaddr);
|
|
||||||
if(connect(sc->socket, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
|
|
||||||
close(sc->socket);
|
|
||||||
free(sc);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return sc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ipc_destroy(struct ipc_softc *sc)
|
|
||||||
{
|
|
||||||
close(sc->socket);
|
|
||||||
free(sc);
|
|
||||||
#ifdef _WIN32
|
|
||||||
WSACleanup();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
enum {
|
|
||||||
MESSAGE_TICK = 0,
|
|
||||||
MESSAGE_GO,
|
|
||||||
MESSAGE_WRITE,
|
|
||||||
MESSAGE_READ,
|
|
||||||
MESSAGE_READ_REPLY
|
|
||||||
};
|
|
||||||
|
|
||||||
static int ipc_receive_packet(struct ipc_softc *sc, unsigned char *buffer) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
int len;
|
|
||||||
int packet_len;
|
|
||||||
/* ensure we have packet header */
|
|
||||||
while(ipc_rxlen < HEADER_LEN) {
|
|
||||||
len = recv(sc->socket, (char *)&ipc_rxbuffer[ipc_rxlen], MAX_LEN, 0);
|
|
||||||
if(len)
|
|
||||||
ipc_rxlen += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* compute packet length and ensure we have the payload */
|
|
||||||
packet_len = (ipc_rxbuffer[1] << 8) | ipc_rxbuffer[0];
|
|
||||||
while(ipc_rxlen < packet_len) {
|
|
||||||
len = recv(sc->socket, (char *)&ipc_rxbuffer[ipc_rxlen], MAX_LEN, 0);
|
|
||||||
if(len)
|
|
||||||
ipc_rxlen += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* copy packet to buffer */
|
|
||||||
memcpy(buffer, ipc_rxbuffer + HEADER_LEN, packet_len - HEADER_LEN);
|
|
||||||
|
|
||||||
/* prepare ipc_rxbuffer for next packet */
|
|
||||||
ipc_rxlen = ipc_rxlen - packet_len;
|
|
||||||
memcpy(ipc_rxbuffer, ipc_rxbuffer + packet_len, ipc_rxlen);
|
|
||||||
|
|
||||||
return packet_len - HEADER_LEN;
|
|
||||||
#else
|
|
||||||
return recv(sc->socket, buffer, MAX_LEN, 0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 0 -> error
|
|
||||||
* 1 -> success
|
|
||||||
* 2 -> graceful shutdown
|
|
||||||
*/
|
|
||||||
int ipc_receive(struct ipc_softc *sc)
|
|
||||||
{
|
|
||||||
unsigned char buffer[MAX_LEN];
|
|
||||||
ssize_t l = 0;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
l = ipc_receive_packet(sc, (unsigned char *)&buffer);
|
|
||||||
if(l == 0)
|
|
||||||
return 2;
|
|
||||||
if((l < 0) || (l >= MAX_LEN))
|
|
||||||
return 0;
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
switch(buffer[i++]) {
|
|
||||||
case MESSAGE_GO:
|
|
||||||
assert((l - i) == 0);
|
|
||||||
|
|
||||||
return sc->h_go(sc->user);
|
|
||||||
case MESSAGE_WRITE: {
|
|
||||||
char *name;
|
|
||||||
int nchunks;
|
|
||||||
unsigned char *chunks;
|
|
||||||
unsigned int chunk_index;
|
|
||||||
|
|
||||||
name = (char *)&buffer[i];
|
|
||||||
i += strlen(name) + 1;
|
|
||||||
assert((i+4) < l);
|
|
||||||
chunk_index = buffer[i] | buffer[i+1] << 8 | buffer[i+2] << 16 | buffer[i+3] << 24;
|
|
||||||
i += 4;
|
|
||||||
nchunks = buffer[i++];
|
|
||||||
assert(i + nchunks == l);
|
|
||||||
chunks = (unsigned char *)&buffer[i];
|
|
||||||
|
|
||||||
return sc->h_write(name, chunk_index, nchunks, chunks, sc->user);
|
|
||||||
}
|
|
||||||
case MESSAGE_READ: {
|
|
||||||
char *name;
|
|
||||||
unsigned int name_index;
|
|
||||||
|
|
||||||
name = (char *)&buffer[i];
|
|
||||||
i += strlen(name) + 1;
|
|
||||||
assert((i+4) == l);
|
|
||||||
name_index = buffer[i] | buffer[i+1] << 8 | buffer[i+2] << 16 | buffer[i+3] << 24;
|
|
||||||
|
|
||||||
return sc->h_read(name, name_index, sc->user);
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int ipc_tick(struct ipc_softc *sc)
|
|
||||||
{
|
|
||||||
ssize_t l;
|
|
||||||
char c[HEADER_LEN + 1];
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
c[0] = 3;
|
|
||||||
c[1] = 0;
|
|
||||||
#endif
|
|
||||||
c[HEADER_LEN + 0] = MESSAGE_TICK;
|
|
||||||
l = send(sc->socket, c, HEADER_LEN + 1, 0);
|
|
||||||
if(l != (HEADER_LEN + 1))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ipc_read_reply(struct ipc_softc *sc, int nchunks, const unsigned char *chunks)
|
|
||||||
{
|
|
||||||
int len;
|
|
||||||
char buffer[MAX_LEN];
|
|
||||||
ssize_t l;
|
|
||||||
|
|
||||||
len = nchunks + HEADER_LEN + 2;
|
|
||||||
assert(len < MAX_LEN);
|
|
||||||
assert(nchunks < 256);
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
buffer[0] = len & 0xFF;
|
|
||||||
buffer[1] = (0xFF00 & len) >> 8;
|
|
||||||
#endif
|
|
||||||
buffer[HEADER_LEN + 0] = MESSAGE_READ_REPLY;
|
|
||||||
buffer[HEADER_LEN + 1] = nchunks;
|
|
||||||
memcpy(&buffer[HEADER_LEN + 2], chunks, nchunks);
|
|
||||||
|
|
||||||
l = send(sc->socket, buffer, len, 0);
|
|
||||||
if(l != len)
|
|
||||||
return 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
24
vpi/ipc.h
24
vpi/ipc.h
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2012 Vermeer Manufacturing Co.
|
|
||||||
* License: GPLv3 with additional permissions (see README).
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __IPC_H
|
|
||||||
#define __IPC_H
|
|
||||||
|
|
||||||
struct ipc_softc;
|
|
||||||
|
|
||||||
typedef int(*go_handler)(void *);
|
|
||||||
typedef int(*write_handler)(char *, int, int, const unsigned char *, void *);
|
|
||||||
typedef int(*read_handler)(char *, int, void *);
|
|
||||||
|
|
||||||
struct ipc_softc *ipc_connect(const char *sockaddr,
|
|
||||||
go_handler h_go, write_handler h_write, read_handler h_read, void *user);
|
|
||||||
void ipc_destroy(struct ipc_softc *sc);
|
|
||||||
|
|
||||||
int ipc_receive(struct ipc_softc *sc);
|
|
||||||
|
|
||||||
int ipc_tick(struct ipc_softc *sc);
|
|
||||||
int ipc_read_reply(struct ipc_softc *sc, int nchunks, const unsigned char *value);
|
|
||||||
|
|
||||||
#endif /* __IPC_H */
|
|
203
vpi/main.c
203
vpi/main.c
|
@ -1,203 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2012 Vermeer Manufacturing Co.
|
|
||||||
* License: GPLv3 with additional permissions (see README).
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <vpi_user.h>
|
|
||||||
|
|
||||||
#include "ipc.h"
|
|
||||||
|
|
||||||
struct migensim_softc {
|
|
||||||
struct ipc_softc *ipc;
|
|
||||||
int has_go;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int h_go(void *user)
|
|
||||||
{
|
|
||||||
struct migensim_softc *sc = (struct migensim_softc *)user;
|
|
||||||
sc->has_go = 1;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static s_vpi_time zero_delay = {
|
|
||||||
.type = vpiSimTime,
|
|
||||||
.high = 0,
|
|
||||||
.low = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
static int h_write(char *name, int index, int nchunks, const unsigned char *chunks, void *user)
|
|
||||||
{
|
|
||||||
vpiHandle item;
|
|
||||||
s_vpi_vecval vector[64];
|
|
||||||
int i;
|
|
||||||
s_vpi_value value;
|
|
||||||
|
|
||||||
item = vpi_handle_by_name(name, NULL);
|
|
||||||
if(item == NULL) {
|
|
||||||
fprintf(stderr, "Attempted to write non-existing signal %s\n", name);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if(vpi_get(vpiType, item) == vpiMemory)
|
|
||||||
item = vpi_handle_by_index(item, index);
|
|
||||||
else
|
|
||||||
assert(index == 0);
|
|
||||||
|
|
||||||
assert(nchunks <= 255);
|
|
||||||
for(i=0;i<64;i++) {
|
|
||||||
vector[i].aval = 0;
|
|
||||||
vector[i].bval = 0;
|
|
||||||
}
|
|
||||||
for(i=0;i<nchunks;i++)
|
|
||||||
vector[i/4].aval |= chunks[i] << 8*(i % 4);
|
|
||||||
|
|
||||||
value.format = vpiVectorVal;
|
|
||||||
value.value.vector = vector;
|
|
||||||
vpi_put_value(item, &value, &zero_delay, vpiInertialDelay);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int h_read(char *name, int index, void *user)
|
|
||||||
{
|
|
||||||
struct migensim_softc *sc = (struct migensim_softc *)user;
|
|
||||||
vpiHandle item;
|
|
||||||
s_vpi_value value;
|
|
||||||
int size;
|
|
||||||
int i;
|
|
||||||
int nvals;
|
|
||||||
unsigned int vals[64];
|
|
||||||
int nchunks;
|
|
||||||
unsigned char chunks[255];
|
|
||||||
|
|
||||||
item = vpi_handle_by_name(name, NULL);
|
|
||||||
if(item == NULL) {
|
|
||||||
fprintf(stderr, "Attempted to read non-existing signal %s\n", name);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if(vpi_get(vpiType, item) == vpiMemory)
|
|
||||||
item = vpi_handle_by_index(item, index);
|
|
||||||
else
|
|
||||||
assert(index == 0);
|
|
||||||
|
|
||||||
value.format = vpiVectorVal;
|
|
||||||
vpi_get_value(item, &value);
|
|
||||||
size = vpi_get(vpiSize, item);
|
|
||||||
nvals = (size + 31)/32;
|
|
||||||
assert(nvals <= 64);
|
|
||||||
for(i=0;i<nvals;i++)
|
|
||||||
vals[i] = value.value.vector[i].aval & ~value.value.vector[i].bval;
|
|
||||||
nchunks = (size + 7)/8;
|
|
||||||
assert(nchunks <= 255);
|
|
||||||
for(i=0;i<nchunks;i++) {
|
|
||||||
switch(i % 4) {
|
|
||||||
case 0:
|
|
||||||
chunks[i] = vals[i/4] & 0xff;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
chunks[i] = (vals[i/4] & 0xff00) >> 8;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
chunks[i] = (vals[i/4] & 0xff0000) >> 16;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
chunks[i] = (vals[i/4] & 0xff000000) >> 24;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!ipc_read_reply(sc->ipc, nchunks, chunks)) {
|
|
||||||
perror("ipc_read_reply");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int process_until_go(struct migensim_softc *sc)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
|
|
||||||
sc->has_go = 0;
|
|
||||||
while(!sc->has_go) {
|
|
||||||
r = ipc_receive(sc->ipc);
|
|
||||||
if(r != 1)
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PLI_INT32 connect_calltf(PLI_BYTE8 *user)
|
|
||||||
{
|
|
||||||
struct migensim_softc *sc = (struct migensim_softc *)user;
|
|
||||||
vpiHandle sys;
|
|
||||||
vpiHandle argv;
|
|
||||||
vpiHandle item;
|
|
||||||
s_vpi_value value;
|
|
||||||
|
|
||||||
sys = vpi_handle(vpiSysTfCall, 0);
|
|
||||||
argv = vpi_iterate(vpiArgument, sys);
|
|
||||||
item = vpi_scan(argv);
|
|
||||||
value.format = vpiStringVal;
|
|
||||||
vpi_get_value(item, &value);
|
|
||||||
|
|
||||||
sc->ipc = ipc_connect(value.value.str, h_go, h_write, h_read, sc);
|
|
||||||
if(sc->ipc == NULL) {
|
|
||||||
perror("ipc_connect");
|
|
||||||
vpi_control(vpiFinish, 1);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PLI_INT32 tick_calltf(PLI_BYTE8 *user)
|
|
||||||
{
|
|
||||||
struct migensim_softc *sc = (struct migensim_softc *)user;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
if(!ipc_tick(sc->ipc)) {
|
|
||||||
perror("ipc_tick");
|
|
||||||
vpi_control(vpiFinish, 1);
|
|
||||||
ipc_destroy(sc->ipc);
|
|
||||||
sc->ipc = NULL;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
r = process_until_go(sc);
|
|
||||||
if(r != 1) {
|
|
||||||
vpi_control(vpiFinish, r == 2 ? 0 : 1);
|
|
||||||
ipc_destroy(sc->ipc);
|
|
||||||
sc->ipc = NULL;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct migensim_softc sc;
|
|
||||||
|
|
||||||
static void simple_register(const char *tfname, PLI_INT32 (*calltf)(PLI_BYTE8 *))
|
|
||||||
{
|
|
||||||
s_vpi_systf_data tf_data;
|
|
||||||
|
|
||||||
tf_data.type = vpiSysTask;
|
|
||||||
tf_data.tfname = tfname;
|
|
||||||
tf_data.calltf = calltf;
|
|
||||||
tf_data.compiletf = NULL;
|
|
||||||
tf_data.sizetf = 0;
|
|
||||||
tf_data.user_data = (void *)≻
|
|
||||||
vpi_register_systf(&tf_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void migensim_register()
|
|
||||||
{
|
|
||||||
simple_register("$migensim_connect", connect_calltf);
|
|
||||||
simple_register("$migensim_tick", tick_calltf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void (*vlog_startup_routines[])() = {
|
|
||||||
migensim_register,
|
|
||||||
0
|
|
||||||
};
|
|
Loading…
Reference in New Issue