verilator/include/verilated.cpp
2009-10-26 20:12:09 -04:00

885 lines
28 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// -*- C++ -*-
//*************************************************************************
//
// Copyright 2003-2009 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License.
// Version 2.0.
//
// Verilator 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.
//
//=========================================================================
///
/// \file
/// \brief Verilator: Linked against all applications using Verilated source.
///
/// This file must be compiled and linked against all objects
/// created from Verilator.
///
/// Code available from: http://www.veripool.org/verilator
///
//=========================================================================
#include "verilated.h"
#include <cctype>
#define VL_VALUE_STRING_MAX_WIDTH 1024 ///< Max static char array for VL_VALUE_STRING
//===========================================================================
// Global variables
int Verilated::s_randReset = 0;
int Verilated::s_debug = 1;
bool Verilated::s_calcUnusedSigs = false;
bool Verilated::s_gotFinish = false;
bool Verilated::s_assertOn = true;
//===========================================================================
// User definable functions
#ifndef VL_USER_FINISH // Define this to override this function
void vl_finish (const char* filename, int linenum, const char* hier) {
if (0 && hier) {}
VL_PRINTF("- %s:%d: Verilog $finish\n", filename, linenum);
if (Verilated::gotFinish()) {
VL_PRINTF("- %s:%d: Second verilog $finish, exiting\n", filename, linenum);
exit(0);
}
Verilated::gotFinish(true);
}
#endif
#ifndef VL_USER_STOP // Define this to override this function
void vl_stop (const char* filename, int linenum, const char* hier) {
Verilated::gotFinish(true);
vl_fatal (filename,linenum,hier,"Verilog $stop");
}
#endif
#ifndef VL_USER_FATAL // Define this to override this function
void vl_fatal (const char* filename, int linenum, const char* hier, const char* msg) {
if (0 && hier) {}
Verilated::gotFinish(true);
VL_PRINTF("%%Error: %s:%d: %s\n", filename, linenum, msg);
abort();
}
#endif
//===========================================================================
// Random reset -- Only called at init time, so don't inline.
IData VL_RAND32() {
#if defined(_WIN32) && !defined(__CYGWIN__)
// Windows doesn't have lrand48(), although Cygwin does.
return (rand()<<16) ^ rand();
#else
return (lrand48()<<16) ^ lrand48();
#endif
}
IData VL_RANDOM_I(int obits) {
return VL_RAND32() & VL_MASK_I(obits);
}
QData VL_RANDOM_Q(int obits) {
QData data = ((QData)VL_RAND32()<<VL_ULL(32)) | (QData)VL_RAND32();
return data & VL_MASK_Q(obits);
}
WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) {
for (int i=0; i<VL_WORDS_I(obits); i++) {
if (i<(VL_WORDS_I(obits)-1)) {
outwp[i] = VL_RAND32();
} else {
outwp[i] = VL_RAND32() & VL_MASK_I(obits);
}
}
return outwp;
}
IData VL_RAND_RESET_I(int obits) {
if (Verilated::randReset()==0) return 0;
IData data = ~0;
if (Verilated::randReset()!=1) { // if 2, randomize
data = VL_RANDOM_I(obits);
}
if (obits<32) data &= VL_MASK_I(obits);
return data;
}
QData VL_RAND_RESET_Q(int obits) {
if (Verilated::randReset()==0) return 0;
QData data = VL_ULL(~0);
if (Verilated::randReset()!=1) { // if 2, randomize
data = VL_RANDOM_Q(obits);
}
if (obits<64) data &= VL_MASK_Q(obits);
return data;
}
WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) {
for (int i=0; i<VL_WORDS_I(obits); i++) {
if (i<(VL_WORDS_I(obits)-1)) {
outwp[i] = VL_RAND_RESET_I(32);
} else {
outwp[i] = VL_RAND_RESET_I(32) & VL_MASK_I(obits);
}
}
return outwp;
}
WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) {
for (int i=0; i<VL_WORDS_I(obits); i++) outwp[i] = 0;
return outwp;
}
//===========================================================================
// Slow math
WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, bool is_modulus) {
// See Knuth Algorithm D. Computes u/v = q.r
// This isn't massively tuned, as wide division is rare
// for debug see V3Number version
// Requires clean input
int words = VL_WORDS_I(lbits);
for (int i=0; i<words; i++) owp[i]=0;
// Find MSB and check for zero.
int umsbp1 = VL_MOSTSETBITP1_W(words,lwp); // dividend
int vmsbp1 = VL_MOSTSETBITP1_W(words,rwp); // divisor
if (VL_UNLIKELY(vmsbp1==0) // rwp==0 so division by zero. Return 0.
|| VL_UNLIKELY(umsbp1==0)) { // 0/x so short circuit and return 0
return owp;
}
int uw = VL_WORDS_I(umsbp1); // aka "m" in the algorithm
int vw = VL_WORDS_I(vmsbp1); // aka "n" in the algorithm
if (vw == 1) { // Single divisor word breaks rest of algorithm
vluint64_t k = 0;
for (int j = uw-1; j >= 0; j--) {
vluint64_t unw64 = ((k<<VL_ULL(32)) + (vluint64_t)(lwp[j]));
owp[j] = unw64 / (vluint64_t)(rwp[0]);
k = unw64 - (vluint64_t)(owp[j])*(vluint64_t)(rwp[0]);
}
if (is_modulus) {
owp[0] = k;
for (int i=1; i<words; i++) owp[i]=0;
}
return owp;
}
// +1 word as we may shift during normalization
uint32_t un[VL_MULS_MAX_WORDS+1]; // Fixed size, as MSVC++ doesn't allow [words] here
uint32_t vn[VL_MULS_MAX_WORDS+1]; // v normalized
// Zero for ease of debugging and to save having to zero for shifts
for (int i=0; i<words; i++) { un[i]=vn[i]=0; }
// Algorithm requires divisor MSB to be set
// Copy and shift to normalize divisor so MSB of vn[vw-1] is set
int s = 31-VL_BITBIT_I(vmsbp1-1); // shift amount (0...31)
uint32_t shift_mask = s ? 0xffffffff : 0; // otherwise >> 32 won't mask the value
for (int i = vw-1; i>0; i--) {
vn[i] = (rwp[i] << s) | (shift_mask & (rwp[i-1] >> (32-s)));
}
vn[0] = rwp[0] << s;
// Copy and shift dividend by same amount; may set new upper word
if (s) un[uw] = lwp[uw-1] >> (32-s);
else un[uw] = 0;
for (int i=uw-1; i>0; i--) {
un[i] = (lwp[i] << s) | (shift_mask & (lwp[i-1] >> (32-s)));
}
un[0] = lwp[0] << s;
// Main loop
for (int j = uw - vw; j >= 0; j--) {
// Estimate
vluint64_t unw64 = ((vluint64_t)(un[j+vw])<<VL_ULL(32) | (vluint64_t)(un[j+vw-1]));
vluint64_t qhat = unw64 / (vluint64_t)(vn[vw-1]);
vluint64_t rhat = unw64 - qhat*(vluint64_t)(vn[vw-1]);
again:
if (qhat >= VL_ULL(0x100000000)
|| ((qhat*vn[vw-2]) > ((rhat<<VL_ULL(32)) + un[j+vw-2]))) {
qhat = qhat - 1;
rhat = rhat + vn[vw-1];
if (rhat < VL_ULL(0x100000000)) goto again;
}
vlsint64_t t = 0; // Must be signed
vluint64_t k = 0;
for (int i=0; i<vw; i++) {
vluint64_t p = qhat*vn[i]; // Multiply by estimate
t = un[i+j] - k - (p & VL_ULL(0xFFFFFFFF)); // Subtract
un[i+j] = t;
k = (p >> VL_ULL(32)) - (t >> VL_ULL(32));
}
t = un[j+vw] - k;
un[j+vw] = t;
owp[j] = qhat; // Save quotient digit
if (t < 0) {
// Over subtracted; correct by adding back
owp[j]--;
k = 0;
for (int i=0; i<vw; i++) {
t = (vluint64_t)(un[i+j]) + (vluint64_t)(vn[i]) + k;
un[i+j] = t;
k = t >> VL_ULL(32);
}
un[j+vw] = un[j+vw] + k;
}
}
if (is_modulus) { // modulus
// Need to reverse normalization on copy to output
for (int i=0; i<vw; i++) {
owp[i] = (un[i] >> s) | (shift_mask & (un[i+1] << (32-s)));
}
for (int i=vw; i<words; i++) owp[i] = 0;
return owp;
} else { // division
return owp;
}
}
//===========================================================================
// Formatting
// Do a va_arg returning a quad, assuming input argument is anything less than wide
#define _VL_VA_ARG_Q(ap, bits) (((bits) <= VL_WORDSIZE) ? va_arg(ap,IData) : va_arg(ap,QData))
void _vl_vsformat(string& output, const char* formatp, va_list ap) {
// Format a Verilog $write style format into the output list
// The format must be pre-processed (and lower cased) by Verilator
// Arguments are in "width, arg-value (or WDataIn* if wide)" form
//
// Note uses a single buffer internally; presumes only one usage per printf
// Note also assumes variables < 64 are not wide, this assumption is
// sometimes not true in low-level routines written here in verilated.cpp
static VL_THREAD char tmp[VL_VALUE_STRING_MAX_WIDTH];
bool inPct = false;
bool widthSet = false;
int width = 0;
const char* pos = formatp;
for (; *pos; ++pos) {
if (!inPct && pos[0]=='%') {
inPct = true;
widthSet = false;
width = 0;
} else if (!inPct) { // Normal text
// Fast-forward to next escape and add to output
const char *ep = pos;
while (ep[0] && ep[0]!='%') ep++;
if (ep != pos) {
output += string(pos, ep-pos);
pos += ep-pos-1;
}
} else { // Format character
inPct = false;
char fmt = pos[0];
switch (fmt) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
inPct = true; // Get more digits
widthSet = true;
width = width*10 + (fmt - '0');
break;
case '%':
output += '%';
break;
case 'S': { // "C" string
const char* cstrp = va_arg(ap, const char*);
output += cstrp;
break;
}
default: {
// Deal with all read-and-print somethings
const int lbits = va_arg(ap, int);
QData ld = 0;
WDataInP lwp;
if (lbits <= VL_QUADSIZE) {
WData qlwp[2];
ld = _VL_VA_ARG_Q(ap, lbits);
VL_SET_WQ(qlwp,ld);
lwp = qlwp;
} else {
lwp = va_arg(ap,WDataInP);
ld = lwp[0];
if (fmt == 'u' || fmt == 'd') fmt = 'x'; // Not supported, but show something
}
int lsb=lbits-1;
if (widthSet && width==0) while (lsb && !VL_BITISSET_W(lwp,lsb)) lsb--;
switch (fmt) {
case 'c': {
IData charval = ld & 0xff;
output += charval;
break;
}
case 's':
for (; lsb>=0; lsb--) {
lsb = (lsb / 8) * 8; // Next digit
IData charval = (lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 0xff;
output += (charval==0)?' ':charval;
}
break;
case 'd': { // Signed decimal
int digits=sprintf(tmp,"%lld",(vlsint64_t)(VL_EXTENDS_QQ(lbits,lbits,ld)));
int needmore = width-digits;
if (needmore>0) output.append(needmore,' '); // Pre-pad spaces
output += tmp;
break;
}
case 'u': { // Unsigned decimal
int digits=sprintf(tmp,"%llu",ld);
int needmore = width-digits;
if (needmore>0) output.append(needmore,' '); // Pre-pad spaces
output += tmp;
break;
}
case 't': { // Time
int digits;
if (VL_TIME_MULTIPLIER==1) {
digits=sprintf(tmp,"%llu",ld);
} else if (VL_TIME_MULTIPLIER==1000) {
digits=sprintf(tmp,"%llu.%03llu",
(QData)(ld/VL_TIME_MULTIPLIER),
(QData)(ld%VL_TIME_MULTIPLIER));
} else {
vl_fatal(__FILE__,__LINE__,"","%%Error: Unsupported VL_TIME_MULTIPLIER");
}
int needmore = width-digits;
if (needmore>0) output.append(needmore,' '); // Pre-pad spaces
output += tmp;
break;
}
case 'b':
for (; lsb>=0; lsb--) {
output += ((lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 1) + '0';
}
break;
case 'o':
for (; lsb>=0; lsb--) {
lsb = (lsb / 3) * 3; // Next digit
// Octal numbers may span more than one wide word,
// so we need to grab each bit separately and check for overrun
// Octal is rare, so we'll do it a slow simple way
output += ('0'
+ ((VL_BITISSETLIMIT_W(lwp, lbits, lsb+0)) ? 1 : 0)
+ ((VL_BITISSETLIMIT_W(lwp, lbits, lsb+1)) ? 2 : 0)
+ ((VL_BITISSETLIMIT_W(lwp, lbits, lsb+2)) ? 4 : 0));
}
break;
case 'x':
for (; lsb>=0; lsb--) {
lsb = (lsb / 4) * 4; // Next digit
IData charval = (lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 0xf;
output += "0123456789abcdef"[charval];
}
break;
default:
string msg = string("%%Error: Unknown _vl_vsformat code: ")+pos[0]+"\n";
vl_fatal(__FILE__,__LINE__,"",msg.c_str());
break;
} // switch
}
} // switch
}
}
}
static inline bool _vl_vsss_eof(FILE* fp, int& floc) {
return fp ? feof(fp) : (floc<0);
}
static inline void _vl_vsss_advance(FILE* fp, int& floc) {
if (fp) fgetc(fp);
else floc -= 8;
}
static inline int _vl_vsss_peek(FILE* fp, int& floc, WDataInP fromp) {
// Get a character without advancing
if (fp) {
int data = fgetc(fp);
if (data == EOF) return EOF;
ungetc(data,fp);
return data;
} else {
if (floc < 0) return EOF;
floc = floc & ~7; // Align to closest character
int data = (fromp[VL_BITWORD_I(floc)] >> VL_BITBIT_I(floc)) & 0xff;
return data;
}
}
static inline void _vl_vsss_skipspace(FILE* fp, int& floc, WDataInP fromp) {
while (1) {
int c = _vl_vsss_peek(fp, floc, fromp);
if (c==EOF || !isspace(c)) return;
_vl_vsss_advance(fp, floc);
}
}
static inline void _vl_vsss_read(FILE* fp, int& floc, WDataInP fromp,
char* tmpp, const char* acceptp) {
// Read into tmp, consisting of characters from acceptp list
char* cp = tmpp;
while (1) {
int c = _vl_vsss_peek(fp, floc, fromp);
if (c==EOF || isspace(c)) break;
if (acceptp!=NULL // String - allow anything
&& NULL==strchr(acceptp, c)) break;
if (acceptp!=NULL) c = tolower(c); // Non-strings we'll simplify
*cp++ = c;
_vl_vsss_advance(fp, floc);
}
*cp++ = '\0';
//VL_PRINTF("\t_read got='%s'\n", tmpp);
}
static inline void _vl_vsss_setbit(WDataOutP owp, int obits, int lsb, int nbits, IData ld) {
for (; nbits && lsb<obits; nbits--, lsb++, ld>>=1) {
VL_ASSIGNBIT_WI(0, lsb, owp, ld & 1);
}
}
IData _vl_vsscanf(FILE* fp, // If a fscanf
int fbits, WDataInP fromp, // Else if a sscanf
const char* formatp, va_list ap) {
// Read a Verilog $sscanf/$fscanf style format into the output list
// The format must be pre-processed (and lower cased) by Verilator
// Arguments are in "width, arg-value (or WDataIn* if wide)" form
static VL_THREAD char tmp[VL_VALUE_STRING_MAX_WIDTH];
int floc = fbits - 1;
IData got = 0;
bool inPct = false;
const char* pos = formatp;
for (; *pos && !_vl_vsss_eof(fp,floc); ++pos) {
//VL_PRINTF("_vlscan fmt='%c' floc=%d file='%c'\n", pos[0], floc, _vl_vsss_peek(fp,floc,fromp));
if (!inPct && pos[0]=='%') {
inPct = true;
} else if (!inPct && isspace(pos[0])) { // Format spaces
while (isspace(pos[1])) pos++;
_vl_vsss_skipspace(fp,floc,fromp);
} else if (!inPct) { // Expected Format
_vl_vsss_skipspace(fp,floc,fromp);
int c = _vl_vsss_peek(fp,floc,fromp);
if (c != pos[0]) goto done;
else _vl_vsss_advance(fp,floc);
} else { // Format character
// Skip loading spaces
inPct = false;
char fmt = pos[0];
switch (fmt) {
case '%': {
int c = _vl_vsss_peek(fp,floc,fromp);
if (c != '%') goto done;
else _vl_vsss_advance(fp,floc);
break;
}
default: {
// Deal with all read-and-scan somethings
// Note LSBs are preserved if there's an overflow
const int obits = va_arg(ap, int);
int lsb = 0;
WData qowp[2];
WDataOutP owp = qowp;
if (obits > VL_QUADSIZE) {
owp = va_arg(ap,WDataOutP);
}
for (int i=0; i<VL_WORDS_I(obits); i++) owp[i] = 0;
switch (fmt) {
case 'c': {
int c = _vl_vsss_peek(fp,floc,fromp);
if (c==EOF) goto done;
else _vl_vsss_advance(fp,floc);
owp[0] = c;
break;
}
case 's': {
_vl_vsss_skipspace(fp,floc,fromp);
_vl_vsss_read(fp,floc,fromp, tmp, NULL);
if (!tmp[0]) goto done;
int pos = strlen(tmp)-1;
for (int i=0; i<obits && pos>=0; pos--) {
_vl_vsss_setbit(owp,obits,lsb, 8, tmp[pos]); lsb+=8;
}
break;
}
case 'd': { // Signed decimal
_vl_vsss_skipspace(fp,floc,fromp);
_vl_vsss_read(fp,floc,fromp, tmp, "0123456789+-xz?_");
if (!tmp[0]) goto done;
vlsint64_t ld;
sscanf(tmp,"%lld",&ld);
VL_SET_WQ(owp,ld);
break;
}
case 't': // FALLTHRU // Time
case 'u': { // Unsigned decimal
_vl_vsss_skipspace(fp,floc,fromp);
_vl_vsss_read(fp,floc,fromp, tmp, "0123456789+-xz?_");
if (!tmp[0]) goto done;
QData ld;
sscanf(tmp,"%llu",&ld);
VL_SET_WQ(owp,ld);
break;
}
case 'b': {
_vl_vsss_skipspace(fp,floc,fromp);
_vl_vsss_read(fp,floc,fromp, tmp, "01xz?_");
if (!tmp[0]) goto done;
int pos = strlen(tmp)-1;
for (int i=0; i<obits && pos>=0; pos--) {
switch(tmp[pos]) {
case 'x': case 'z': case '?': //FALLTHRU
case '0': lsb++; break;
case '1': _vl_vsss_setbit(owp,obits,lsb, 1, 1); lsb++; break;
case '_': break;
}
}
break;
}
case 'o': {
_vl_vsss_skipspace(fp,floc,fromp);
_vl_vsss_read(fp,floc,fromp, tmp, "01234567xz?_");
if (!tmp[0]) goto done;
int pos = strlen(tmp)-1;
for (int i=0; i<obits && pos>=0; pos--) {
switch(tmp[pos]) {
case 'x': case 'z': case '?': //FALLTHRU
case '0': lsb+=3; break;
case '1': _vl_vsss_setbit(owp,obits,lsb, 3, 1); lsb+=3; break;
case '2': _vl_vsss_setbit(owp,obits,lsb, 3, 2); lsb+=3; break;
case '3': _vl_vsss_setbit(owp,obits,lsb, 3, 3); lsb+=3; break;
case '4': _vl_vsss_setbit(owp,obits,lsb, 3, 4); lsb+=3; break;
case '5': _vl_vsss_setbit(owp,obits,lsb, 3, 5); lsb+=3; break;
case '6': _vl_vsss_setbit(owp,obits,lsb, 3, 6); lsb+=3; break;
case '7': _vl_vsss_setbit(owp,obits,lsb, 3, 7); lsb+=3; break;
case '_': break;
}
}
break;
}
case 'x': {
_vl_vsss_skipspace(fp,floc,fromp);
_vl_vsss_read(fp,floc,fromp, tmp, "0123456789abcdefxz?_");
if (!tmp[0]) goto done;
int pos = strlen(tmp)-1;
for (int i=0; i<obits && pos>=0; pos--) {
switch(tmp[pos]) {
case 'x': case 'z': case '?': //FALLTHRU
case '0': lsb+=4; break;
case '1': _vl_vsss_setbit(owp,obits,lsb, 4, 1); lsb+=4; break;
case '2': _vl_vsss_setbit(owp,obits,lsb, 4, 2); lsb+=4; break;
case '3': _vl_vsss_setbit(owp,obits,lsb, 4, 3); lsb+=4; break;
case '4': _vl_vsss_setbit(owp,obits,lsb, 4, 4); lsb+=4; break;
case '5': _vl_vsss_setbit(owp,obits,lsb, 4, 5); lsb+=4; break;
case '6': _vl_vsss_setbit(owp,obits,lsb, 4, 6); lsb+=4; break;
case '7': _vl_vsss_setbit(owp,obits,lsb, 4, 7); lsb+=4; break;
case '8': _vl_vsss_setbit(owp,obits,lsb, 4, 8); lsb+=4; break;
case '9': _vl_vsss_setbit(owp,obits,lsb, 4, 9); lsb+=4; break;
case 'a': _vl_vsss_setbit(owp,obits,lsb, 4, 10); lsb+=4; break;
case 'b': _vl_vsss_setbit(owp,obits,lsb, 4, 11); lsb+=4; break;
case 'c': _vl_vsss_setbit(owp,obits,lsb, 4, 12); lsb+=4; break;
case 'd': _vl_vsss_setbit(owp,obits,lsb, 4, 13); lsb+=4; break;
case 'e': _vl_vsss_setbit(owp,obits,lsb, 4, 14); lsb+=4; break;
case 'f': _vl_vsss_setbit(owp,obits,lsb, 4, 15); lsb+=4; break;
case '_': break;
}
}
break;
}
default:
string msg = string("%%Error: Unknown _vl_vsscanf code: ")+pos[0]+"\n";
vl_fatal(__FILE__,__LINE__,"",msg.c_str());
break;
} // switch
got++;
// Reload data if non-wide (if wide, we put it in the right place directly)
if (obits <= VL_BYTESIZE) {
CData* p = va_arg(ap,CData*); *p = owp[0];
} else if (obits <= VL_SHORTSIZE) {
SData* p = va_arg(ap,SData*); *p = owp[0];
} else if (obits <= VL_WORDSIZE) {
IData* p = va_arg(ap,IData*); *p = owp[0];
} else if (obits <= VL_QUADSIZE) {
QData* p = va_arg(ap,QData*); *p = VL_SET_QW(owp);
}
}
} // switch
}
}
done:
return got;
}
//===========================================================================
// File I/O
void _VL_VINT_TO_STRING(int obits, char* destoutp, WDataInP sourcep) {
int lsb=obits-1;
bool start=true;
char* destp = destoutp;
for (; lsb>=0; lsb--) {
lsb = (lsb / 8) * 8; // Next digit
IData charval = (sourcep[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 0xff;
if (!start || charval) {
*destp++ = (charval==0)?' ':charval;
start = false; // Drop leading 0s
}
}
*destp++ = '\0'; // Terminate
while (isspace(*(destp-1)) && destp>destoutp) *--destp = '\0'; // Drop trailing spaces
}
void _VL_STRING_TO_VINT(int obits, void* destp, int srclen, const char* srcp) {
// Convert C string to Verilog format
int bytes = VL_BYTES_I(obits);
char* op = ((char*)(destp));
if (srclen > bytes) srclen = bytes; // Don't overflow destination
int i;
for (i=0; i<srclen; i++) { *op++ = srcp[srclen-1-i]; }
for (; i<bytes; i++) { *op++ = 0; }
}
IData VL_FGETS_IXQ(int obits, void* destp, QData fpq) {
FILE* fp = VL_CVT_Q_FP(fpq);
if (VL_UNLIKELY(!fp)) return 0;
// The string needs to be padded with 0's in unused spaces in front of
// any read data. This means we can't know in what location the first
// character will finally live, so we need to copy. Yuk.
IData bytes = VL_BYTES_I(obits);
char buffer[VL_TO_STRING_MAX_WORDS*VL_WORDSIZE+1];
// V3Emit has static check that bytes < VL_TO_STRING_MAX_WORDS, but be safe
if (VL_UNLIKELY(bytes > VL_TO_STRING_MAX_WORDS*VL_WORDSIZE)) {
vl_fatal(__FILE__,__LINE__,"","Internal: fgets buffer overrun");
}
// We don't use fgets, as we must read \0s.
IData got = 0;
char* cp = buffer;
while (got < bytes) {
int c = getc(fp);
if (c==EOF) break;
*cp++ = c; got++;
if (c=='\n') break;
}
_VL_STRING_TO_VINT(obits, destp, got, buffer);
return got;
}
QData VL_FOPEN_QI(QData filename, IData mode) {
IData fnw[2]; VL_SET_WQ(fnw, filename);
return VL_FOPEN_WI(2, fnw, mode);
}
QData VL_FOPEN_WI(int fnwords, WDataInP filenamep, IData mode) {
char filenamez[VL_TO_STRING_MAX_WORDS*VL_WORDSIZE+1];
_VL_VINT_TO_STRING(fnwords*VL_WORDSIZE, filenamez, filenamep);
char modez[5];
_VL_VINT_TO_STRING(VL_WORDSIZE, modez, &mode);
return VL_CVT_FP_Q(fopen(filenamez,modez));
}
void VL_WRITEF(const char* formatp, ...) {
va_list ap;
va_start(ap,formatp);
string output;
_vl_vsformat(output, formatp, ap);
va_end(ap);
// Users can redefine VL_PRINTF if they wish.
VL_PRINTF("%s", output.c_str());
}
void VL_FWRITEF(QData fpq, const char* formatp, ...) {
FILE* fp = VL_CVT_Q_FP(fpq);
if (VL_UNLIKELY(!fp)) return;
va_list ap;
va_start(ap,formatp);
string output;
_vl_vsformat(output, formatp, ap);
va_end(ap);
fputs(output.c_str(), fp);
}
IData VL_FSCANF_IX(QData fpq, const char* formatp, ...) {
FILE* fp = VL_CVT_Q_FP(fpq);
if (VL_UNLIKELY(!fp)) return 0;
va_list ap;
va_start(ap,formatp);
IData got = _vl_vsscanf(fp, 0, NULL, formatp, ap);
va_end(ap);
return got;
}
IData VL_SSCANF_IIX(int lbits, IData ld, const char* formatp, ...) {
IData fnw[2]; VL_SET_WI(fnw, ld);
va_list ap;
va_start(ap,formatp);
IData got = _vl_vsscanf(NULL, lbits, fnw, formatp, ap);
va_end(ap);
return got;
}
IData VL_SSCANF_IQX(int lbits, QData ld, const char* formatp, ...) {
IData fnw[2]; VL_SET_WQ(fnw, ld);
va_list ap;
va_start(ap,formatp);
IData got = _vl_vsscanf(NULL, lbits, fnw, formatp, ap);
va_end(ap);
return got;
}
IData VL_SSCANF_IWX(int lbits, WDataInP lwp, const char* formatp, ...) {
va_list ap;
va_start(ap,formatp);
IData got = _vl_vsscanf(NULL, lbits, lwp, formatp, ap);
va_end(ap);
return got;
}
void VL_READMEM_Q(bool hex, int width, int depth, int array_lsb, int,
QData ofilename, void* memp, IData start, IData end) {
IData fnw[2]; VL_SET_WQ(fnw, ofilename);
return VL_READMEM_W(hex,width,depth,array_lsb,2, fnw,memp,start,end);
}
void VL_READMEM_W(bool hex, int width, int depth, int array_lsb, int fnwords,
WDataInP ofilenamep, void* memp, IData start, IData end) {
char ofilenamez[VL_TO_STRING_MAX_WORDS*VL_WORDSIZE+1];
_VL_VINT_TO_STRING(fnwords*VL_WORDSIZE, ofilenamez, ofilenamep);
FILE* fp = fopen(ofilenamez, "r");
if (!fp) {
// We don't report the Verilog source filename as it slow to have to pass it down
vl_fatal (ofilenamez, 0, "", "$readmem file not found");
return;
}
// Prep for reading
IData addr = start;
int linenum = 1;
bool innum = false;
bool ignore_to_eol = false;
bool ignore_to_cmt = false;
bool needinc = false;
bool reading_addr = false;
int lastc = ' ';
// Read the data
// We process a character at a time, as then we don't need to deal
// with changing buffer sizes dynamically, etc.
while (1) {
int c = fgetc(fp);
if (c==EOF) break;
//printf("%d: Got '%c' Addr%x IN%d IgE%d IgC%d ninc%d\n", linenum, c, addr, innum, ignore_to_eol, ignore_to_cmt, needinc);
if (c=='\n') { linenum++; ignore_to_eol=false; if (innum) reading_addr=false; innum=false; }
else if (c=='\t' || c==' ' || c=='\r' || c=='\f') { if (innum) reading_addr=false; innum=false; }
// Skip // comments and detect /* comments
else if (ignore_to_cmt && lastc=='*' && c=='/') {
ignore_to_cmt = false; if (innum) reading_addr=false; innum=false;
} else if (!ignore_to_eol && !ignore_to_cmt) {
if (lastc=='/' && c=='*') { ignore_to_cmt = true; }
else if (lastc=='/' && c=='/') { ignore_to_eol = true; }
else if (c=='/') {} // Part of /* or //
else if (c=='_') {}
else if (c=='@') { reading_addr = true; innum=false; needinc=false; }
// Check for hex or binary digits as file format requests
else if (isxdigit(c)) {
c = tolower(c);
int value = (c >= 'a' ? (c-'a'+10) : (c-'0'));
if (!innum) { // Prep for next number
if (needinc) { addr++; needinc=false; }
}
if (reading_addr) {
// Decode @ addresses
if (!innum) addr=0;
addr = (addr<<4) + value;
} else {
needinc = true;
//printf(" Value width=%d @%x = %c\n", width, addr, c);
if (addr >= (IData)(depth+array_lsb) || addr < (IData)(array_lsb)) {
vl_fatal (ofilenamez, linenum, "", "$readmem file address beyond bounds of array");
} else {
int entry = addr - array_lsb;
QData shift = hex ? VL_ULL(4) : VL_ULL(1);
// Shift value in
if (width<=8) {
CData* datap = &((CData*)(memp))[entry];
if (!innum) { *datap = 0; }
*datap = ((*datap << shift) + value) & VL_MASK_I(width);
} else if (width<=16) {
SData* datap = &((SData*)(memp))[entry];
if (!innum) { *datap = 0; }
*datap = ((*datap << shift) + value) & VL_MASK_I(width);
} else if (width<=VL_WORDSIZE) {
IData* datap = &((IData*)(memp))[entry];
if (!innum) { *datap = 0; }
*datap = ((*datap << shift) + value) & VL_MASK_I(width);
} else if (width<=VL_QUADSIZE) {
QData* datap = &((QData*)(memp))[entry];
if (!innum) { *datap = 0; }
*datap = ((*datap << (QData)(shift)) + (QData)(value)) & VL_MASK_Q(width);
} else {
WDataOutP datap = &((WDataOutP)(memp))[ entry*VL_WORDS_I(width) ];
if (!innum) { VL_ZERO_RESET_W(width, datap); }
_VL_SHIFTL_INPLACE_W(width, datap, shift);
datap[0] |= value;
}
if (value>=(1<<shift)) {
vl_fatal (ofilenamez, linenum, "", "$readmemb (binary) file contains hex characters");
}
}
}
innum = true;
}
else {
vl_fatal (ofilenamez, linenum, "", "$readmem file syntax error");
}
}
lastc = c;
}
if (needinc) { addr++; needinc=false; }
// Final checks
fclose(fp);
if (end != (IData)(~ VL_ULL(0)) && addr != (end+1)) {
vl_fatal (ofilenamez, linenum, "", "$readmem file ended before specified ending-address");
}
}
//===========================================================================
// Verilated:: Methods
const char* Verilated::catName(const char* n1, const char* n2) {
// Returns new'ed data
// Used by symbol table creation to make module names
static char* strp = NULL;
static int len = -1;
int newlen = strlen(n1)+strlen(n2)+2;
if (newlen > len) {
if (strp) delete [] strp;
strp = new char[newlen];
len = newlen;
}
strcpy(strp,n1);
strcat(strp,n2);
return strp;
}
//===========================================================================
// VerilatedModule:: Methods
VerilatedModule::VerilatedModule(const char* namep)
: m_namep(strdup(namep)) {
}
VerilatedModule::~VerilatedModule() {
if (m_namep) free((void*)m_namep); m_namep=NULL;
}
//===========================================================================