/* * Copyright (C) 2011-2021 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it 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. */ # include # include # include # include "config.h" # include "vlog95_priv.h" /* * Extract an int32_t value from the given bit information. If the result * type is 0 then the returned value is valid. If it is positive then the * value was too large and if it is negative then the value had undefined * bits. -2 is all bits z and -3 is all bits x. */ static int32_t get_int32_from_bits(const char *bits, unsigned nbits, unsigned is_signed, int *result_type) { unsigned trim_wid = nbits - 1; const char msb = is_signed ? bits[trim_wid] : '0'; unsigned idx; int32_t value = 0; /* Trim any duplicate bits from the MSB. */ for (/* none */; trim_wid > 0; trim_wid -= 1) { if (msb != bits[trim_wid]) { trim_wid += 1; break; } } if (trim_wid < nbits) trim_wid += 1; /* Check to see if the value is too large. */ if (trim_wid > 32U) { *result_type = trim_wid; return 0; } /* Now build the value from the bits. */ for (idx = 0; idx < trim_wid; idx += 1) { if (bits[idx] == '1') value |= (int32_t)1 << idx; else if (bits[idx] != '0') { *result_type = -1; /* If the value is entirely x/z then return -2 or -3. */ if (trim_wid == 1) { if (bits[idx] == 'x') *result_type -= 1; *result_type -= 1; } return 0; } } /* Sign extend as needed. */ // HERE: Need to emit 1 instead of -1 for some of the constants. // if (is_signed && (nbits > 1) && (msb == '1') && (trim_wid < 32U)) { if (is_signed && (msb == '1') && (trim_wid < 32U)) { value |= ~(((uint32_t)1 << trim_wid) - (uint32_t)1); } *result_type = 0; return value; } /* Emit the given bits as either a signed or unsigned constant. If the * bits contain an undefined value then emit them as a binary constant * otherwise emit them as a hex constant. */ static void emit_bits(const char *bits, unsigned nbits, unsigned is_signed) { unsigned has_undef = 0; assert(nbits > 0); /* Check for an undefined bit. */ for (int idx = (int)nbits-1; idx >= 0; idx -= 1) { if ((bits[idx] != '0') && (bits[idx] != '1')) { has_undef = 1; break; } } fprintf(vlog_out, "%u'", nbits); if (is_signed) fprintf(vlog_out, "s"); /* Emit as a binary constant. */ if (has_undef || (nbits < 2)) { int start = nbits - 1; char sbit = bits[start]; /* Trim extra leading bits. */ if (! is_signed && (sbit == '1')) sbit = ' '; while (start && (sbit == bits[start-1])) start -= 1; /* Print the trimmed value. */ fprintf(vlog_out, "b"); for (int idx = start; idx >= 0; idx -= 1) { fprintf(vlog_out, "%c", bits[idx]); } /* Emit as a hex constant. */ } else { unsigned start = 4*(nbits/4); unsigned result = 0; fprintf(vlog_out, "h"); /* The first digit may not be a full hex digit. */ if (start < nbits) { for (unsigned idx = start; idx < nbits; idx += 1) { if (bits[idx] == '1') result |= 1U << (idx%4); } fprintf(vlog_out, "%1x", result); } /* Now print the full hex digits. */ for (int idx = start-1; idx >= 0; idx -= 4) { result = 0; if (bits[idx] == '1') result |= 0x8; if (bits[idx-1] == '1') result |= 0x4; if (bits[idx-2] == '1') result |= 0x2; if (bits[idx-3] == '1') result |= 0x1; fprintf(vlog_out, "%1x", result); } } } void emit_number(const char *bits, unsigned nbits, unsigned is_signed, const char *file, unsigned lineno) { /* If the user is allowing signed constructs then we can emit a * signed number as a normal integer or with the 's syntax if * an integer is not appropriate. */ if (is_signed && allow_signed) { int rtype; int32_t value = get_int32_from_bits(bits, nbits, 1, &rtype); if (rtype != 0) emit_bits(bits, nbits, is_signed); else fprintf(vlog_out, "%"PRId32, value); /* Otherwise a signed value can only be 32 bits long since it can * only be represented as an integer. We can trim any matching MSB * bits to make it fit. We cannot support individual undefined * bits in the constant. */ } else if (is_signed) { int rtype; int32_t value = get_int32_from_bits(bits, nbits, 1, &rtype); if (rtype > 0) { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Signed number is " "greater than 32 bits (%d) and cannot be " "safely represented.\n", file, lineno, rtype); vlog_errors += 1; } else if (rtype == -1) { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Signed number has " "an undefined bit and cannot be " "represented.\n", file, lineno); vlog_errors += 1; return; } else if (rtype == -2) { fprintf(vlog_out, "%u'bz", nbits); } else if (rtype == -3) { /* If this is a 32-bit wide constant then generate the * undefined with integers to get a signed value. */ if (nbits == 32) fprintf(vlog_out, "1/0"); else fprintf(vlog_out, "%u'bx", nbits); } else { fprintf(vlog_out, "%"PRId32, value); } /* An unsigned number is represented in hex if all the bits are * defined and it is more than a single bit otherwise it is * represented in binary form to preserve all the information. */ } else { emit_bits(bits, nbits, is_signed); } } void emit_real_number(double value) { /* Check for NaN. */ if (isnan(value)) { fprintf(vlog_out, "(0.0/0.0)"); return; } /* Check for the infinities. */ if (isinf(value)) { if (signbit(value)) fprintf(vlog_out, "(-1.0/0.0)"); else fprintf(vlog_out, "(1.0/0.0)"); return; } /* Check for +/- zero. */ if (value == 0.0) { if (signbit(value)) fprintf(vlog_out, "-0.0"); else fprintf(vlog_out, "0.0"); } else { char buffer[32]; char *cptr; unsigned len; /* Print the double to a temporary string using an extra digit. */ buffer[sizeof(buffer)-1] = 0; snprintf(buffer, sizeof(buffer), "%#.17g", value); assert(buffer[sizeof(buffer)-1] == 0); /* Check to see if there is a digit after the decimal point and * add a digit if it is missing. */ len = strlen(buffer); if (buffer[len-1] == '.') { assert((len + 1) < sizeof(buffer)); buffer[len] = '0'; len += 1; buffer[len] = 0; } /* Now trim any extra trailing zero digits. */ cptr = buffer + len - 1; while ((*cptr == '0') && (*(cptr-1) != '.')) cptr -= 1; *(cptr+1) = 0; /* Now print the processed output. */ fprintf(vlog_out, "%s", buffer); } } /* * Extract an uint64_t value from the given number expression. If the result * type is 0 then the returned value is valid. If it is positive then the * value was too large and if it is negative then the value had undefined * bits. */ uint64_t get_uint64_from_number(ivl_expr_t expr, int *result_type) { unsigned nbits = ivl_expr_width(expr); unsigned trim_wid = nbits - 1; const char *bits = ivl_expr_bits(expr); unsigned idx; uint64_t value = 0; assert(ivl_expr_type(expr) == IVL_EX_NUMBER); assert(! ivl_expr_signed(expr)); /* Trim any '0' bits from the MSB. */ for (/* none */; trim_wid > 0; trim_wid -= 1) { if ('0' != bits[trim_wid]) { trim_wid += 1; break; } } if (trim_wid < nbits) trim_wid += 1; /* Check to see if the value is too large. */ if (trim_wid > 64U) { *result_type = trim_wid; return 0; } /* Now build the value from the bits. */ for (idx = 0; idx < trim_wid; idx += 1) { if (bits[idx] == '1') value |= (uint64_t)1 << idx; else if (bits[idx] != '0') { *result_type = -1; /* If the value is entirely x/z then return -2 or -3. */ if (trim_wid == 1) { if (bits[idx] == 'x') *result_type -= 1; *result_type -= 1; } return 0; } } *result_type = 0; return value; } /* * Extract an int64_t value from the given number expression. If the result * type is 0 then the returned value is valid. If it is positive then the * value was too large and if it is negative then the value had undefined * bits. -2 is all bits z and -3 is all bits x. */ int64_t get_int64_from_number(ivl_expr_t expr, int *result_type) { unsigned is_signed = ivl_expr_signed(expr); unsigned nbits = ivl_expr_width(expr); unsigned trim_wid = nbits - 1; const char *bits = ivl_expr_bits(expr); const char msb = is_signed ? bits[trim_wid] : '0'; unsigned idx; int64_t value = 0; assert(ivl_expr_type(expr) == IVL_EX_NUMBER); /* Trim any duplicate bits from the MSB. */ for (/* none */; trim_wid > 0; trim_wid -= 1) { if (msb != bits[trim_wid]) { trim_wid += 1; break; } } if (trim_wid < nbits) trim_wid += 1; /* Check to see if the value is too large. */ if (trim_wid > 64U) { *result_type = trim_wid; return 0; } /* Now build the value from the bits. */ for (idx = 0; idx < trim_wid; idx += 1) { if (bits[idx] == '1') value |= (int64_t)1 << idx; else if (bits[idx] != '0') { *result_type = -1; /* If the value is entirely x/z then return -2 or -3. */ if (trim_wid == 1) { if (bits[idx] == 'x') *result_type -= 1; *result_type -= 1; } return 0; } } /* Sign extend as needed. */ if (is_signed && (msb == '1') && (trim_wid < 64U)) { value |= ~(((uint64_t)1 << trim_wid) - (uint64_t)1); } *result_type = 0; return value; } /* * Extract an int32_t value from the given number expression. If the result * type is 0 then the returned value is valid. If it is positive then the * value was too large and if it is negative then the value had undefined * bits. -2 is all bits z and -3 is all bits x. */ int32_t get_int32_from_number(ivl_expr_t expr, int *result_type) { assert(ivl_expr_type(expr) == IVL_EX_NUMBER); return get_int32_from_bits(ivl_expr_bits(expr), ivl_expr_width(expr), ivl_expr_signed(expr), result_type); } /* * Routine to remove two characters starting at the given address. */ static void remove_two_chars(char* str) { for (; str[2]; str += 1) { str[0] = str[2]; } str[0] = 0; } /* * Routine to print a string value as a string after removing any leading * escaped NULL bytes. */ void emit_string(const char* string) { char *buffer = strdup(string); char *bptr = buffer; char *cptr; fprintf(vlog_out, "\""); /* Prune any leading escaped NULL bytes. */ while ((bptr[0] == '\\') && (bptr[1] == '0') && (bptr[2] == '0') && (bptr[3] == '0')) bptr += 4; for (cptr = bptr; *cptr; cptr += 1) { if (*cptr == '\\') { /* Replace any \011 with \t */ if ((cptr[1] == '0') && (cptr[2] == '1') && (cptr[3] == '1')) { cptr[1] = 't'; remove_two_chars(cptr+2); cptr += 1; /* Replace any \012 with \n */ } else if ((cptr[1] == '0') && (cptr[2] == '1') && (cptr[3] == '2')) { cptr[1] = 'n'; remove_two_chars(cptr+2); cptr += 1; /* Replace any \042 with \" */ } else if ((cptr[1] == '0') && (cptr[2] == '4') && (cptr[3] == '2')) { cptr[1] = '"'; remove_two_chars(cptr+2); cptr += 1; /* Replace any \134 with \\ */ } else if ((cptr[1] == '1') && (cptr[2] == '3') && (cptr[3] == '4')) { cptr[1] = '\\'; remove_two_chars(cptr+2); cptr += 1; } else cptr += 3; } } if (*bptr) fprintf(vlog_out, "%s", bptr); free(buffer); fprintf(vlog_out, "\""); }