944 lines
27 KiB
C
944 lines
27 KiB
C
/*
|
|
* Copyright (c) 2015 CERN
|
|
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
|
*
|
|
* This source code is free software; you can redistribute it
|
|
* and/or modify it in source code form under the terms of the GNU
|
|
* General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
/**
|
|
* The following VPI module implements some of the functions available
|
|
* in std.textio library.
|
|
*
|
|
* Type counterparts:
|
|
* VHDL SystemVerilog
|
|
* ------------------------------
|
|
* LINE string (line of text, in VHDL it is a pointer to string)
|
|
* TEXT int (file handle)
|
|
*
|
|
* Some of functions offered by std.textio library are not implemented here,
|
|
* as they can be directly replaced with SystemVerilog system functions.
|
|
*
|
|
* VHDL SystemVerilog
|
|
* --------------------------------------
|
|
* FILE_CLOSE(file F: TEXT) $fclose(fd)
|
|
* ENDFILE(file F: TEXT) $feof(fd)
|
|
*
|
|
* Procedures:
|
|
* HREAD (L: inout LINE; VALUE: out BIT_VECTOR)
|
|
* HWRITE (L: inout LINE; VALUE: out BIT_VECTOR)
|
|
* are handled with $ivlh_read/write() using FORMAT_HEX parameter (see format_t enum).
|
|
*/
|
|
|
|
# include "sys_priv.h"
|
|
# include "vpi_config.h"
|
|
# include "vpi_user.h"
|
|
# include <assert.h>
|
|
# include <string.h>
|
|
# include <ctype.h>
|
|
# include "ivl_alloc.h"
|
|
|
|
/* additional parameter values to distinguish between integer, boolean and
|
|
* time types or to use hex format */
|
|
enum format_t { FORMAT_STD, FORMAT_BOOL, FORMAT_TIME, FORMAT_HEX, FORMAT_STRING };
|
|
|
|
enum file_mode_t { FILE_MODE_READ, FILE_MODE_WRITE, FILE_MODE_APPEND, FILE_MODE_LAST };
|
|
|
|
/* bits per vector, in a single s_vpi_vecval struct */
|
|
static const size_t BPW = 8 * sizeof(PLI_INT32);
|
|
|
|
static int is_integer_var(vpiHandle obj)
|
|
{
|
|
PLI_INT32 type = vpi_get(vpiType, obj);
|
|
|
|
return (type == vpiIntegerVar || type == vpiShortIntVar ||
|
|
type == vpiIntVar || type == vpiLongIntVar);
|
|
}
|
|
|
|
static void show_error_line(vpiHandle callh) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
}
|
|
|
|
static void show_warning_line(vpiHandle callh) {
|
|
vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
}
|
|
|
|
/* sets a single bit value in a bit/logic vector */
|
|
static int set_vec_val(s_vpi_vecval* vector, char value, int idx) {
|
|
s_vpi_vecval*v = &vector[idx / BPW];
|
|
PLI_INT32 bit = idx % BPW;
|
|
|
|
switch(toupper(value)) {
|
|
case '0':
|
|
v->bval &= ~(1 << bit);
|
|
v->aval &= ~(1 << bit);
|
|
break;
|
|
|
|
case '1':
|
|
v->bval &= ~(1 << bit);
|
|
v->aval |= (1 << bit);
|
|
break;
|
|
|
|
case 'Z':
|
|
v->bval |= (1 << bit);
|
|
v->aval &= ~(1 << bit);
|
|
break;
|
|
|
|
case 'X':
|
|
v->bval |= (1 << bit);
|
|
v->aval |= (1 << bit);
|
|
break;
|
|
|
|
default:
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Converts a string of characters to a vector in s_vpi_value struct.
|
|
* Returns number of processed characters, 0 in case of failure.
|
|
* string is the data to be converted.
|
|
* val is the target s_vpi_value struct.
|
|
* var is the variable that is converted (to obtain size & type [2/4-state]).
|
|
*/
|
|
static int read_vector(const char *string, s_vpi_value *val, vpiHandle var)
|
|
{
|
|
#if 0
|
|
/* It could be easier to simply use val.format = vpiBinStrVal
|
|
* but there is no way to check if processing went fine */
|
|
val.format = vpiBinStrVal;
|
|
val.value.str = string;
|
|
processed_chars = size;
|
|
#endif
|
|
/* Vector size (==1 for scalars) */
|
|
int size = vpi_get(vpiSize, var);
|
|
|
|
/* Number of required s_vpi_vecval structs to store the result */
|
|
int words = (size + BPW - 1) / BPW; /* == ceil(size / BPW) */
|
|
int len = strlen(string);
|
|
|
|
val->format = vpiVectorVal; /* it also covers scalars */
|
|
val->value.vector = calloc(words, sizeof(s_vpi_vecval));
|
|
|
|
/* Skip spaces in the beginning */
|
|
int skipped = 0;
|
|
while(*string && *string == ' ') {
|
|
--len;
|
|
++string;
|
|
++skipped;
|
|
}
|
|
|
|
/* Process bits */
|
|
int p;
|
|
for(p = 0; p < size && p < len; ++p) {
|
|
if(set_vec_val(val->value.vector, string[p], size - p - 1)) {
|
|
free(val->value.vector); /* error */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* 2-logic variables cannot hold X or Z values, so change them to 0 */
|
|
if(vpi_get(vpiType, var) == vpiBitVar) {
|
|
for(int i = 0; i < words; ++i) {
|
|
val->value.vector[i].aval &= ~val->value.vector[i].bval;
|
|
val->value.vector[i].bval = 0;
|
|
}
|
|
}
|
|
|
|
return p + skipped;
|
|
}
|
|
|
|
/* Converts a string of characters to a time value, stored in vector filed of
|
|
* s_vpi_value struct.
|
|
* Returns number of processed characters, 0 in case of failure.
|
|
* string is the data to be converted.
|
|
* val is the target s_vpi_value struct.
|
|
* scope_unit is the time unit used in the scope (-3 for millisecond,
|
|
* -6 for microsecond, etc.)
|
|
*/
|
|
static int read_time(const char *string, s_vpi_value *val, PLI_INT32 scope_unit) {
|
|
PLI_UINT64 period;
|
|
char units[2];
|
|
int time_unit, processed_chars;
|
|
|
|
if(sscanf(string, "%ld %2s%n", &period, units, &processed_chars) != 2)
|
|
return 0;
|
|
|
|
if(!strncasecmp(units, "fs", 2))
|
|
time_unit = -15;
|
|
else if(!strncasecmp(units, "ps", 2))
|
|
time_unit = -12;
|
|
else if(!strncasecmp(units, "ns", 2))
|
|
time_unit = -9;
|
|
else if(!strncasecmp(units, "us", 2))
|
|
time_unit = -6;
|
|
else if(!strncasecmp(units, "ms", 2))
|
|
time_unit = -3;
|
|
else if(!strncasecmp(units, "s", 1))
|
|
time_unit = 0;
|
|
else
|
|
return 0;
|
|
|
|
/* Scale the time units to the one used in the scope */
|
|
int scale_diff = time_unit - scope_unit;
|
|
|
|
if(scale_diff > 0) {
|
|
for(int i = 0; i < scale_diff; ++i)
|
|
period *= 10;
|
|
} else {
|
|
for(int i = 0; i < -scale_diff; ++i)
|
|
period /= 10;
|
|
}
|
|
|
|
/* vpiTimeVal format is not handled at the moment,
|
|
* so return the read value as a vector*/
|
|
val->format = vpiVectorVal;
|
|
val->value.vector = calloc(2, sizeof(s_vpi_vecval));
|
|
memset(val->value.vector, 0, 2 * sizeof(s_vpi_vecval));
|
|
|
|
val->value.vector[1].aval = (PLI_UINT32) (period >> 32);
|
|
val->value.vector[0].aval = (PLI_UINT32) period;
|
|
|
|
return processed_chars;
|
|
}
|
|
|
|
static int read_string(const char *string, s_vpi_value *val) {
|
|
char buf[1024];
|
|
int processed_chars;
|
|
|
|
if(sscanf(string, "%1024s%n", buf, &processed_chars) != 1)
|
|
return 0;
|
|
|
|
val->format = vpiStringVal;
|
|
val->value.str = strndup(buf, processed_chars);
|
|
|
|
return processed_chars;
|
|
}
|
|
|
|
static int write_time(char *string, const s_vpi_value* val,
|
|
size_t width, PLI_INT32 scope_unit) {
|
|
char prefix = 0;
|
|
PLI_UINT64 period;
|
|
|
|
switch(val->format) {
|
|
case vpiIntVal:
|
|
period = val->value.integer;
|
|
break;
|
|
|
|
case vpiVectorVal:
|
|
period = val->value.vector[0].aval;
|
|
|
|
if(width > BPW)
|
|
period |= (PLI_UINT64)(val->value.vector[1].aval) << 32;
|
|
break;
|
|
|
|
default:
|
|
return 1;
|
|
}
|
|
|
|
/* Handle the case when the time unit base is 10 or 100 */
|
|
int remainder = scope_unit % -3;
|
|
if(remainder) {
|
|
remainder += 3;
|
|
scope_unit -= remainder;
|
|
|
|
while(remainder--)
|
|
period *= 10;
|
|
}
|
|
|
|
switch(scope_unit) {
|
|
case -15: prefix = 'f'; break;
|
|
case -12: prefix = 'p'; break;
|
|
case -9: prefix = 'n'; break;
|
|
case -6: prefix = 'u'; break;
|
|
case -3: prefix = 'm'; break;
|
|
}
|
|
|
|
if(prefix)
|
|
sprintf(string, "%ld %cs", period, prefix);
|
|
else
|
|
sprintf(string, "%ld s", period);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* slightly modified sys_fopen_compiletf */
|
|
static PLI_INT32 ivlh_file_open_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv;
|
|
assert(callh != 0);
|
|
|
|
argv = vpi_iterate(vpiArgument, callh);
|
|
|
|
/* Check that there is a file name argument and that it is a string. */
|
|
if (argv == 0) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s requires a string file name argument.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
if(!is_integer_var(vpi_scan(argv))) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s's first argument has to be an integer variable (file handle).\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
|
|
if(!is_string_obj(vpi_scan(argv))) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s's second argument argument must be a string (file name).\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
|
|
/* When provided, the type argument must be a string. */
|
|
if(!vpi_scan(argv)) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s's third argument must be an integer (open mode).\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
|
|
/* Make sure there are no extra arguments. */
|
|
check_for_extra_args(argv, callh, name, "three arguments", 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* procedure FILE_MODE(file F: TEXT; External_Name; in STRING;
|
|
Open_Kind: in FILE_MODE_KIND := READ_MODE); */
|
|
/* slightly modified sys_fopen_calltf */
|
|
static PLI_INT32 ivlh_file_open_calltf(ICARUS_VPI_CONST PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
s_vpi_value val;
|
|
int mode;
|
|
char *fname;
|
|
|
|
vpiHandle fhandleh = vpi_scan(argv);
|
|
vpiHandle fnameh = vpi_scan(argv);
|
|
vpiHandle modeh = vpi_scan(argv);
|
|
|
|
/* Get the mode handle */
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(modeh, &val);
|
|
mode = val.value.integer;
|
|
vpi_free_object(argv);
|
|
|
|
if(mode < 0 || mode >= FILE_MODE_LAST) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s's file open mode argument is invalid.\n", name);
|
|
return 0;
|
|
}
|
|
|
|
fname = get_filename(callh, name, fnameh);
|
|
|
|
if(fname == 0) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s's could not obtain the file name.\n", name);
|
|
return 0;
|
|
}
|
|
|
|
/* Open file and save the handle */
|
|
switch(mode) {
|
|
case FILE_MODE_READ:
|
|
val.value.integer = vpi_fopen(fname, "r");
|
|
break;
|
|
|
|
case FILE_MODE_WRITE:
|
|
val.value.integer = vpi_fopen(fname, "w");
|
|
break;
|
|
|
|
case FILE_MODE_APPEND:
|
|
val.value.integer = vpi_fopen(fname, "a");
|
|
break;
|
|
}
|
|
|
|
val.format = vpiIntVal;
|
|
vpi_put_value(fhandleh, &val, 0, vpiNoDelay);
|
|
free(fname);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static PLI_INT32 ivlh_readwriteline_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
vpiHandle arg;
|
|
|
|
/* Check that there are two arguments and that the first is an
|
|
integer (file handle) and that the second is string. */
|
|
if(argv == 0) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s requires two arguments.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
arg = vpi_scan(argv);
|
|
if(!arg || !is_integer_var(arg)) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s's first argument must be an integer variable (file handle).\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
|
|
arg = vpi_scan(argv);
|
|
if(!arg || !is_string_obj(arg)) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s's second argument must be a string.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
|
|
/* Make sure there are no extra arguments. */
|
|
check_for_extra_args(argv, callh, name, "two arguments", 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* procedure READLINE (file F: TEXT; L: inout LINE); */
|
|
/* slightly modified sys_fgets_calltf */
|
|
static PLI_INT32 ivlh_readline_calltf(ICARUS_VPI_CONST PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
vpiHandle stringh, arg;
|
|
s_vpi_value val;
|
|
PLI_UINT32 fd;
|
|
FILE *fp;
|
|
char *text;
|
|
const int BUF_SIZE = 1024;
|
|
char buf[BUF_SIZE];
|
|
|
|
/* Get the file descriptor. */
|
|
arg = vpi_scan(argv);
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(arg, &val);
|
|
fd = val.value.integer;
|
|
|
|
/* Get the string handle. */
|
|
stringh = vpi_scan(argv);
|
|
vpi_free_object(argv);
|
|
|
|
/* Return zero if this is not a valid fd. */
|
|
fp = vpi_get_file(fd);
|
|
if(!fp) {
|
|
show_warning_line(callh);
|
|
vpi_printf("invalid file descriptor (0x%x) given to %s.\n",
|
|
(unsigned int)fd, name);
|
|
return 0;
|
|
}
|
|
|
|
/* Read in the bytes. Return 0 if there was an error. */
|
|
if(fgets(buf, BUF_SIZE, fp) == 0) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s reading past the end of file.\n", name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int len = strlen(buf);
|
|
|
|
if(len == 0) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s read 0 bytes.\n", name);
|
|
return 0;
|
|
} else if(len == BUF_SIZE - 1) {
|
|
show_warning_line(callh);
|
|
vpi_printf("%s has reached the buffer limit, part of the "
|
|
"processed string might have been skipped.\n", name);
|
|
}
|
|
|
|
/* Return the characters to the register. */
|
|
text = strndup(buf, len - 1); /* skip the newline character */
|
|
val.format = vpiStringVal;
|
|
val.value.str = text;
|
|
vpi_put_value(stringh, &val, 0, vpiNoDelay);
|
|
free(text);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* procedure WRITELINE (file F: TEXT; L: inout LINE); */
|
|
/* slightly modified sys_fgets_calltf */
|
|
static PLI_INT32 ivlh_writeline_calltf(ICARUS_VPI_CONST PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
vpiHandle stringh, arg;
|
|
s_vpi_value val;
|
|
PLI_UINT32 fd;
|
|
FILE *fp;
|
|
char *empty;
|
|
|
|
/* Get the file descriptor. */
|
|
arg = vpi_scan(argv);
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(arg, &val);
|
|
fd = val.value.integer;
|
|
|
|
/* Get the string contents. */
|
|
stringh = vpi_scan(argv);
|
|
val.format = vpiStringVal;
|
|
vpi_get_value(stringh, &val);
|
|
|
|
vpi_free_object(argv);
|
|
|
|
/* Return zero if this is not a valid fd. */
|
|
fp = vpi_get_file(fd);
|
|
if(!fp) {
|
|
show_warning_line(callh);
|
|
vpi_printf("invalid file descriptor (0x%x) given to %s.\n",
|
|
(unsigned int)fd, name);
|
|
return 0;
|
|
}
|
|
|
|
fprintf(fp, "%s\n", val.value.str);
|
|
|
|
/* Clear the written string */
|
|
empty = strdup("");
|
|
val.format = vpiStringVal;
|
|
val.value.str = empty;
|
|
vpi_put_value(stringh, &val, 0, vpiNoDelay);
|
|
free(empty);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static PLI_INT32 ivlh_read_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
vpiHandle arg;
|
|
|
|
if(argv == 0) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s requires three arguments.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
arg = vpi_scan(argv);
|
|
if(!arg || !is_string_obj(arg)) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s's first argument must be a string.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
|
|
arg = vpi_scan(argv);
|
|
if(!arg || is_constant_obj(arg)) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s's second argument must be a variable.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
arg = vpi_scan(argv);
|
|
if(!arg) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s's third argument must be an integer.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
/* Make sure there are no extra arguments. */
|
|
check_for_extra_args(argv, callh, name, "three arguments", 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* procedure READ (L: inout LINE;
|
|
VALUE: out BIT/BIT_VECTOR/BOOLEAN/CHARACTER/INTEGER/REAL/STRING/TIME); */
|
|
static PLI_INT32 ivlh_read_calltf(ICARUS_VPI_CONST PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
vpiHandle stringh, varh, formath;
|
|
s_vpi_value val;
|
|
PLI_INT32 type, format;
|
|
char *string = 0;
|
|
int processed_chars = 0, fail = 0;
|
|
|
|
/* Get the string */
|
|
stringh = vpi_scan(argv);
|
|
val.format = vpiStringVal;
|
|
vpi_get_value(stringh, &val);
|
|
|
|
/* Get the destination variable */
|
|
varh = vpi_scan(argv);
|
|
type = vpi_get(vpiType, varh);
|
|
|
|
if(strlen(val.value.str) == 0) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s cannot read from an empty string.\n", name);
|
|
return 0;
|
|
}
|
|
|
|
string = strdup(val.value.str);
|
|
|
|
/* Get the format (see enum format_t) */
|
|
formath = vpi_scan(argv);
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(formath, &val);
|
|
format = val.value.integer;
|
|
vpi_free_object(argv);
|
|
|
|
switch(format) {
|
|
case FORMAT_STD:
|
|
switch(type) {
|
|
/* TODO longint is 64-bit, so it has to be handled by vector */
|
|
/*case vpiLongIntVar:*/
|
|
case vpiShortIntVar:
|
|
case vpiIntVar:
|
|
case vpiByteVar:
|
|
case vpiIntegerVar:
|
|
val.format = vpiIntVal;
|
|
if(sscanf(string, "%d%n", &val.value.integer, &processed_chars) != 1)
|
|
fail = 1;
|
|
break;
|
|
|
|
case vpiBitVar: /* bit, bit vector */
|
|
case vpiLogicVar: /* ==vpiReg time, logic, logic vector */
|
|
processed_chars = read_vector(string, &val, varh);
|
|
break;
|
|
|
|
case vpiRealVar:
|
|
val.format = vpiRealVal;
|
|
if(sscanf(string, "%lf%n", &val.value.real, &processed_chars) != 1)
|
|
fail = 1;
|
|
break;
|
|
|
|
case vpiStringVar:
|
|
processed_chars = read_string(string, &val);
|
|
break;
|
|
|
|
default:
|
|
fail = 1;
|
|
show_warning_line(callh);
|
|
vpi_printf("%s does not handle such type (%d).\n", name, type);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case FORMAT_BOOL:
|
|
{
|
|
char buf[5];
|
|
|
|
val.format = vpiIntVal;
|
|
if(sscanf(string, "%5s%n", buf, &processed_chars) == 1)
|
|
{
|
|
if(!strncasecmp(buf, "true", 4))
|
|
val.value.integer = 1;
|
|
else if(!strncasecmp(buf, "false", 5))
|
|
val.value.integer = 0;
|
|
else
|
|
fail = 1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FORMAT_TIME:
|
|
val.format = vpiIntVal;
|
|
processed_chars = read_time(string, &val, vpi_get(vpiTimeUnit, callh));
|
|
break;
|
|
|
|
case FORMAT_HEX:
|
|
val.format = vpiIntVal;
|
|
if(sscanf(string, "%x%n", &val.value.integer, &processed_chars) != 1)
|
|
fail = 1;
|
|
break;
|
|
|
|
case FORMAT_STRING:
|
|
processed_chars = read_string(string, &val);
|
|
break;
|
|
}
|
|
|
|
if(processed_chars == 0) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s could not read a valid value.\n", name);
|
|
fail = 1;
|
|
} else if(val.format == vpiStringVar && processed_chars == 1024) {
|
|
show_warning_line(callh);
|
|
vpi_printf("%s has reached the buffer limit, part of the "
|
|
"processed string might have been skipped.\n", name);
|
|
}
|
|
|
|
if(!fail) {
|
|
assert(processed_chars > 0);
|
|
|
|
/* Store the read value */
|
|
vpi_put_value(varh, &val, 0, vpiNoDelay);
|
|
|
|
/* Clean up */
|
|
if(val.format == vpiStringVar)
|
|
free(val.value.str);
|
|
else if(val.format == vpiVectorVal)
|
|
free(val.value.vector);
|
|
|
|
/* Strip the read token from the string */
|
|
char* tmp = strdup(&string[processed_chars]);
|
|
val.format = vpiStringVal;
|
|
val.value.str = tmp;
|
|
vpi_put_value(stringh, &val, 0, vpiNoDelay);
|
|
free(tmp);
|
|
} else {
|
|
show_error_line(callh);
|
|
vpi_printf("%s failed.\n", name);
|
|
/*vpi_control(vpiFinish, 1);*/
|
|
}
|
|
|
|
free(string);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static PLI_INT32 ivlh_write_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
vpiHandle arg;
|
|
|
|
if(argv == 0) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s requires three arguments.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
arg = vpi_scan(argv);
|
|
if(!arg || !is_string_obj(arg)) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s's first argument must be a string.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
|
|
arg = vpi_scan(argv);
|
|
if(!arg) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s requires three arguments.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
arg = vpi_scan(argv);
|
|
if(!arg) {
|
|
show_error_line(callh);
|
|
vpi_printf("%s's third argument must be an integer.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
/* Make sure there are no extra arguments. */
|
|
check_for_extra_args(argv, callh, name, "three arguments", 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*procedure WRITE (L: inout LINE;
|
|
VALUE: in BIT/BIT_VECTOR/BOOLEAN/CHARACTER/INTEGER/REAL/STRING/TIME);
|
|
JUSTIFIED: in SIDE:= RIGHT; FIELD: in WIDTH := 0); */
|
|
/* JUSTIFIED & FIELD are not handled at the moment */
|
|
static PLI_INT32 ivlh_write_calltf(ICARUS_VPI_CONST PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
vpiHandle stringh, varh, formath;
|
|
s_vpi_value val;
|
|
PLI_INT32 type, format;
|
|
char *string = 0;
|
|
int fail = 0, res = 0;
|
|
const int BUF_SIZE = 1024;
|
|
char buf[BUF_SIZE];
|
|
|
|
/* Get the string */
|
|
stringh = vpi_scan(argv);
|
|
val.format = vpiStringVal;
|
|
vpi_get_value(stringh, &val);
|
|
string = strdup(val.value.str);
|
|
|
|
/* Get the destination variable */
|
|
varh = vpi_scan(argv);
|
|
type = vpi_get(vpiType, varh);
|
|
|
|
/* Get the format (see enum format_t) */
|
|
formath = vpi_scan(argv);
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(formath, &val);
|
|
format = val.value.integer;
|
|
vpi_free_object(argv);
|
|
|
|
/* Convert constant types to variable types */
|
|
if(type == vpiConstant) {
|
|
type = vpi_get(vpiConstType, varh);
|
|
|
|
switch(type) {
|
|
case vpiRealConst:
|
|
type = vpiRealVar;
|
|
break;
|
|
|
|
case vpiStringConst:
|
|
type = vpiStringVar;
|
|
break;
|
|
|
|
case vpiDecConst:
|
|
case vpiBinaryConst:
|
|
case vpiOctConst:
|
|
case vpiHexConst:
|
|
type = vpiIntVar;
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch(format) {
|
|
case FORMAT_STD:
|
|
switch(type) {
|
|
/* TODO longint is 64-bit, so it has to be handled by vector */
|
|
/*case vpiLongIntVar:*/
|
|
case vpiShortIntVar:
|
|
case vpiIntVar:
|
|
case vpiByteVar:
|
|
case vpiIntegerVar:
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(varh, &val);
|
|
res = snprintf(buf, BUF_SIZE, "%s%d", string, val.value.integer);
|
|
break;
|
|
|
|
case vpiBitVar: /* bit, bit vector */
|
|
case vpiLogicVar: /* ==vpiReg time, logic, logic vector */
|
|
val.format = vpiBinStrVal;
|
|
vpi_get_value(varh, &val);
|
|
|
|
/* VHDL stores X/Z values uppercase, so follow the rule */
|
|
for(size_t i = 0; i< strlen(val.value.str); ++i)
|
|
val.value.str[i] = toupper(val.value.str[i]);
|
|
|
|
res = snprintf(buf, BUF_SIZE, "%s%s", string, val.value.str);
|
|
break;
|
|
|
|
case vpiRealVar:
|
|
val.format = vpiRealVal;
|
|
vpi_get_value(varh, &val);
|
|
res = snprintf(buf, BUF_SIZE, "%s%lf", string, val.value.real);
|
|
break;
|
|
|
|
case vpiStringVar:
|
|
val.format = vpiStringVal;
|
|
vpi_get_value(varh, &val);
|
|
res = snprintf(buf, BUF_SIZE, "%s%s", string, val.value.str);
|
|
break;
|
|
|
|
default:
|
|
fail = 1;
|
|
show_warning_line(callh);
|
|
vpi_printf("%s does not handle such type (%d).\n", name, type);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case FORMAT_BOOL:
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(varh, &val);
|
|
res = snprintf(buf, BUF_SIZE, "%s%s", string,
|
|
val.value.integer ? "TRUE" : "FALSE");
|
|
break;
|
|
|
|
case FORMAT_TIME:
|
|
{
|
|
char tmp[64];
|
|
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(varh, &val);
|
|
|
|
if(write_time(tmp, &val, vpi_get(vpiSize, varh), vpi_get(vpiTimeUnit, callh))) {
|
|
fail = 1;
|
|
break;
|
|
}
|
|
|
|
res = snprintf(buf, BUF_SIZE, "%s%s", string, tmp);
|
|
}
|
|
break;
|
|
|
|
case FORMAT_HEX:
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(varh, &val);
|
|
res = snprintf(buf, BUF_SIZE, "%s%X", string, val.value.integer);
|
|
break;
|
|
|
|
case FORMAT_STRING:
|
|
val.format = vpiStringVal;
|
|
vpi_get_value(varh, &val);
|
|
res = snprintf(buf, BUF_SIZE, "%s%s", string, val.value.str);
|
|
break;
|
|
}
|
|
|
|
if(res > BUF_SIZE)
|
|
fail = 1;
|
|
|
|
if(!fail) {
|
|
/* Strip the read token from the string */
|
|
char* tmp = strndup(buf, BUF_SIZE);
|
|
val.format = vpiStringVal;
|
|
val.value.str = tmp;
|
|
vpi_put_value(stringh, &val, 0, vpiNoDelay);
|
|
free(tmp);
|
|
} else {
|
|
show_error_line(callh);
|
|
vpi_printf("%s failed.\n", name);
|
|
/*vpi_control(vpiFinish, 1);*/
|
|
}
|
|
|
|
free(string);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void vhdl_register(void)
|
|
{
|
|
vpiHandle res;
|
|
|
|
s_vpi_systf_data tf_data[] = {
|
|
{ vpiSysTask, 0, "$ivlh_file_open",
|
|
ivlh_file_open_calltf, ivlh_file_open_compiletf, 0,
|
|
"$ivlh_file_open" },
|
|
|
|
{ vpiSysTask, 0, "$ivlh_readline",
|
|
ivlh_readline_calltf, ivlh_readwriteline_compiletf, 0,
|
|
"$ivlh_readline" },
|
|
|
|
{ vpiSysTask, 0, "$ivlh_writeline",
|
|
ivlh_writeline_calltf, ivlh_readwriteline_compiletf, 0,
|
|
"$ivlh_writeline" },
|
|
|
|
{ vpiSysTask, 0, "$ivlh_read",
|
|
ivlh_read_calltf, ivlh_read_compiletf, 0,
|
|
"$ivlh_read" },
|
|
|
|
{ vpiSysTask, 0, "$ivlh_write",
|
|
ivlh_write_calltf, ivlh_write_compiletf, 0,
|
|
"$ivlh_write" },
|
|
};
|
|
|
|
for(unsigned int i = 0; i < sizeof(tf_data) / sizeof(s_vpi_systf_data); ++i) {
|
|
res = vpi_register_systf(&tf_data[i]);
|
|
vpip_make_systf_system_defined(res);
|
|
}
|
|
}
|
|
|
|
void (*vlog_startup_routines[])(void) = {
|
|
vhdl_register,
|
|
0
|
|
};
|