From 76dd8e895827e27db2686f7fbbe4f0c186c92dd6 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Mon, 26 Feb 2024 20:41:22 +0000 Subject: [PATCH] Verilog number parsing and printing improvements --- src/rt/verilog.c | 28 +++- src/vlog/vlog-number.c | 266 +++++++++++++++++++++++++----------- src/vlog/vlog-number.h | 41 +++++- test/regress/gold/vlog2.txt | 2 +- test/regress/gold/vlog5.txt | 2 +- test/regress/gold/vlog6.txt | 4 +- test/test_dump.c | 10 +- test/test_vlog.c | 10 +- 8 files changed, 255 insertions(+), 108 deletions(-) diff --git a/src/rt/verilog.c b/src/rt/verilog.c index eb50bb7a..a5c735aa 100644 --- a/src/rt/verilog.c +++ b/src/rt/verilog.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2023 Nick Gasson +// Copyright (C) 2023-2024 Nick Gasson // // 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 @@ -34,6 +34,15 @@ static const void *next_arg(jit_scalar_t **args, unsigned *length) return ptr; } +static int calc_dec_size(int nr_bits, bool is_signed) +{ + // From Icarus Verilog src/vpi/sys_display.c + if (is_signed) --nr_bits; + int r = (nr_bits * 146L + 484) / 485; + if (is_signed) ++r; + return r; +} + static void verilog_printf(jit_scalar_t *args) { unsigned fmtlen; @@ -61,18 +70,23 @@ static void verilog_printf(jit_scalar_t *args) switch (*p) { case 'd': - if (number_is_defined(num)) - printf("%*"PRIi64, ilog2(width), number_integer(num)); - else - printf("%*s", ilog2(width), "x"); + { + const int dmax = calc_dec_size(width, false); + if (number_is_defined(num)) + printf("%*"PRIi64, dmax, number_integer(num)); + else + printf("%*s", dmax, "x"); + } break; case 'x': if (number_is_defined(num)) - printf("%0*"PRIx64, width, number_integer(num)); + printf("%0*"PRIx64, width / 4, number_integer(num)); else - printf("%*s", width/4, "x"); + printf("%*s", width / 4, "x"); break; } + + number_free(&num); } break; default: diff --git a/src/vlog/vlog-number.c b/src/vlog/vlog-number.c index 32edd067..8f373902 100644 --- a/src/vlog/vlog-number.c +++ b/src/vlog/vlog-number.c @@ -21,15 +21,14 @@ #include "vlog/vlog-number.h" #include +#include #include #include -#define EMBED_WIDTH 28 - typedef enum { - RADIX_BIN, - RADIX_DEC, - RADIX_HEX, + RADIX_BIN = 2, + RADIX_DEC = 10, + RADIX_HEX = 16, } vlog_radix_t; typedef struct _bignum { @@ -42,9 +41,11 @@ typedef struct _bignum { number_t number_new(const char *str) { - int width = -1; + int width = 32; const char *tick = strchr(str, '\''), *p = str; - if (tick != NULL) { + if (tick == str) + p++; + else if (tick != NULL) { char *eptr; width = strtol(str, &eptr, 10); if (eptr != tick) @@ -61,39 +62,66 @@ number_t number_new(const char *str) default: break; } - if (width < 0) { - switch (radix) { - case RADIX_BIN: width = strlen(p); break; - case RADIX_HEX: width = strlen(p) / 16; break; - case RADIX_DEC: width = ilog2(ipow(10, strlen(p)) - 1) + 1; break; - } - } + bool has_xz = false; + for (const char *pp = p; *pp; pp++) + has_xz |= (*pp == 'x' || *pp == 'z'); - if (radix == RADIX_DEC) { + if (!has_xz && width <= INTEGER_ENCODE_MAX) { + char *eptr; + const int64_t bits = strtoll(p, &eptr, radix); + if (*eptr != '\0') + errorf("invalid character '%c' in number %s", *eptr, str); + + return (number_t) { + .intg = { + .tag = TAG_INTEGER, + .width = width, + .issigned = 0, + .packed = bits + } + }; + } + else if (radix == RADIX_DEC) { char *eptr; const int64_t bits = strtoll(p, &eptr, 10); if (*eptr != '\0') errorf("invalid character '%c' in number %s", *eptr, str); - assert(width <= EMBED_WIDTH); + assert(bits >= 0); - uint64_t packed = PACKED_ZERO; - for (int i = width; i >= 0; i--) { - packed <<= 2; - if (bits & (1 << i)) - packed |= LOGIC_1; - else - packed |= LOGIC_0; + if (width <= INTEGER_ENCODE_MAX) { + return (number_t) { + .intg = { + .tag = TAG_INTEGER, + .width = width, + .issigned = 0, + .packed = bits + } + }; } + else { + assert(false); + + uint64_t packed = PACKED_ZERO; + for (int i = width; i >= 0; i--) { + packed <<= 2; + if (bits & (1 << i)) + packed |= LOGIC_1; + else + packed |= LOGIC_0; + } - return (number_t) { - .tag = 1, - .width = width, - .issigned = 0, - .packed = packed - }; + return (number_t) { + .small = { + .tag = TAG_SMALLNUM, + .width = width, + .issigned = 0, + .packed = packed + } + }; + } } - else if (width <= EMBED_WIDTH) { + else if (width <= SMALLNUM_ENCODE_MAX) { uint64_t packed = PACKED_ZERO; for (; *p; p++) { switch (radix) { @@ -120,10 +148,12 @@ number_t number_new(const char *str) } return (number_t) { - .tag = 1, - .width = width, - .issigned = 0, - .packed = packed + .small = { + .tag = TAG_SMALLNUM, + .width = width, + .issigned = 0, + .packed = packed + } }; } else @@ -132,7 +162,7 @@ number_t number_new(const char *str) void number_free(number_t *val) { - if (!val->tag) + if (val->common.tag == TAG_BIGNUM) abort(); val->bits = 0; @@ -140,27 +170,53 @@ void number_free(number_t *val) void number_print(number_t val, text_buf_t *tb) { - tb_printf(tb, "%u'b", val.width); - - static const char map[] = "xz01"; - - bool leading = true; - for (int i = val.width - 1; i >= 0; i--) { - const int bit = (val.packed >> (i * 2)) & 3; - if (leading && bit != LOGIC_0) - leading = false; - else if (leading) - continue; - tb_append(tb, map[bit]); - } + switch (val.common.tag) { + case TAG_SMALLNUM: + { + tb_printf(tb, "%u'b", val.small.width); + + static const char map[] = "xz01"; + + bool leading = true; + for (int i = val.small.width - 1; i >= 0; i--) { + const int bit = (val.small.packed >> (i * 2)) & 3; + if (leading && bit != LOGIC_0) + leading = false; + else if (leading) + continue; + tb_append(tb, map[bit]); + } - if (leading) - tb_append(tb, '0'); + if (leading) + tb_append(tb, '0'); + } + break; + case TAG_INTEGER: + tb_printf(tb, "%u'%s", val.intg.width, val.intg.issigned ? "s" : ""); + if (val.intg.width == 1) + tb_printf(tb, "b%u", (unsigned char)val.intg.packed); + else { + const int64_t extended = val.intg.packed; + tb_printf(tb, "d%"PRIi64, extended); + } + break; + default: + DEBUG_ONLY(fatal_trace("invalid number tag %x", val.common.tag)); + break; + } } bool number_is_defined(number_t val) { - return (val.packed & PACKED_ZERO) == PACKED_ZERO; + switch (val.common.tag) { + case TAG_SMALLNUM: + return (val.small.packed & PACKED_ZERO) == PACKED_ZERO; + case TAG_INTEGER: + return true; + default: + DEBUG_ONLY(fatal_trace("invalid number tag %x", val.common.tag)); + return 0; + } } int64_t number_integer(number_t val) @@ -168,59 +224,105 @@ int64_t number_integer(number_t val) assert(number_width(val) <= 64); assert(number_is_defined(val)); - // TODO: signed vs unsigned + switch (val.common.tag) { + case TAG_SMALLNUM: + { + // TODO: signed vs unsigned - uint64_t result = 0; - for (int i = val.width - 1; i >= 0; i--) { - const int bit = val.packed >> (i * 2); - result <<= 1; - result |= bit & 1; + uint64_t result = 0; + for (int i = val.small.width - 1; i >= 0; i--) { + const int bit = val.small.packed >> (i * 2); + result <<= 1; + result |= bit & 1; + } + return result; + } + break; + case TAG_INTEGER: + return val.intg.packed; + default: + DEBUG_ONLY(fatal_trace("invalid number tag %x", val.common.tag)); + return 0; } - - /* - for (uint64_t p = val.packed; p; p >>= 2) { - result <<= 1; - result |= p & 1; - }*/ - - return result; } unsigned number_width(number_t val) { - return val.tag ? val.width : 0; + switch (val.common.tag) { + case TAG_SMALLNUM: + return val.small.width; + case TAG_INTEGER: + return val.intg.width; + default: + DEBUG_ONLY(fatal_trace("invalid number tag %x", val.common.tag)); + return 0; + } } vlog_logic_t number_bit(number_t val, unsigned n) { - assert(val.tag); - assert(n < val.width); - return (val.packed >> (n * 2)) & 3; + switch (val.common.tag) { + case TAG_SMALLNUM: + assert(n < val.small.width); + return (val.small.packed >> (n * 2)) & 3; + case TAG_INTEGER: + assert(n < val.intg.width); + return ((val.intg.packed >> n) & 1) | LOGIC_0; + default: + DEBUG_ONLY(fatal_trace("invalid number tag %x", val.common.tag)); + return 0; + } + assert(val.common.tag == TAG_SMALLNUM); } number_t number_pack(const uint8_t *bits, unsigned width) { - assert(width <= EMBED_WIDTH); + bool has_xz = false; + for (int i = 0; i < width; i++) + has_xz |= (bits[i] == LOGIC_X || bits[i] == LOGIC_Z); + + if (!has_xz && width <= INTEGER_WIDTH_MAX) { + uint64_t packed = 0; + for (int i = width - 1; i >= 0; i--) { + assert(bits[i] <= 0b11); + packed <<= 1; + packed |= (bits[i] & 1); + } - uint64_t packed = PACKED_ZERO; - for (int i = width - 1; i >= 0; i--) { - assert(bits[i] <= 0b11); - packed <<= 2; - packed |= bits[i]; + return (number_t){ + .intg = { + .tag = TAG_INTEGER, + .width = width, + .issigned = 0, + .packed = packed, + } + }; } + else { + assert(width <= SMALLNUM_WIDTH_MAX); + + uint64_t packed = PACKED_ZERO; + for (int i = width - 1; i >= 0; i--) { + assert(bits[i] <= 0b11); + packed <<= 2; + packed |= bits[i]; + } - return (number_t){ - .tag = 1, - .width = width, - .issigned = 0, - .packed = packed, - }; + return (number_t){ + .small = { + .tag = TAG_SMALLNUM, + .width = width, + .issigned = 0, + .packed = packed, + } + }; + } } void number_write(number_t val, fbuf_t *f) { - assert(val.tag); + assert(val.common.tag != TAG_BIGNUM); fbuf_put_uint(f, val.bits); } diff --git a/src/vlog/vlog-number.h b/src/vlog/vlog-number.h index 3bdf4f17..c54d7b4f 100644 --- a/src/vlog/vlog-number.h +++ b/src/vlog/vlog-number.h @@ -1,5 +1,5 @@ // -// Copyright (C) 2023 Nick Gasson +// Copyright (C) 2023-2024 Nick Gasson // // 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 @@ -32,16 +32,45 @@ typedef enum { typedef struct _bignum bignum_t; +typedef enum { + TAG_BIGNUM = 0x0, + TAG_SMALLNUM = 0x01, + TAG_INTEGER = 0x02, +} number_tag_t; + +#define SMALLNUM_PACKED_BITS 56 +#define SMALLNUM_WIDTH_BITS 5 +#define SMALLNUM_WIDTH_MAX ((1 << SMALLNUM_WIDTH_BITS) - 1) +#define SMALLNUM_ENCODE_MAX ((UINT64_C(1) << SMALLNUM_PACKED_BITS/2) - 1) + +STATIC_ASSERT(SMALLNUM_ENCODE_MAX <= (UINT64_C(1) << SMALLNUM_WIDTH_MAX) - 1); + +#define INTEGER_PACKED_BITS 32 +#define INTEGER_WIDTH_BITS 6 +#define INTEGER_WIDTH_MAX ((1 << INTEGER_WIDTH_BITS) - 1) +#define INTEGER_ENCODE_MAX ((UINT64_C(1) << INTEGER_PACKED_BITS) - 1) + +STATIC_ASSERT(INTEGER_ENCODE_MAX <= (UINT64_C(1) << INTEGER_WIDTH_MAX) - 1); + // Packed representation of Verilog's four-state logic type typedef union _number { - bignum_t *ext; + bignum_t *big; uint64_t bits; struct { - uint64_t tag : 1; - uint64_t width : 6; + uint64_t tag : 2; + } common; + struct { + uint64_t tag : 2; + uint64_t issigned : 1; + uint64_t width : SMALLNUM_WIDTH_BITS; + uint64_t packed : SMALLNUM_PACKED_BITS; + } small; + struct { + uint64_t tag : 2; uint64_t issigned : 1; - uint64_t packed : 56; - }; + uint64_t width : INTEGER_WIDTH_BITS; + uint64_t packed : INTEGER_PACKED_BITS; + } intg; } number_t; STATIC_ASSERT(sizeof(number_t) == 8); diff --git a/test/regress/gold/vlog2.txt b/test/regress/gold/vlog2.txt index 7de72212..5d3775aa 100644 --- a/test/regress/gold/vlog2.txt +++ b/test/regress/gold/vlog2.txt @@ -1,4 +1,4 @@ hello, world! string=foo -foo 42 00005 0 +foo 42 00000005 0 0ms+0: $finish called diff --git a/test/regress/gold/vlog5.txt b/test/regress/gold/vlog5.txt index f11bd20c..22f4ca46 100644 --- a/test/regress/gold/vlog5.txt +++ b/test/regress/gold/vlog5.txt @@ -1 +1 @@ -0 1 0 0 +0 1 0 0 diff --git a/test/regress/gold/vlog6.txt b/test/regress/gold/vlog6.txt index 6d3426fe..3b0d22e9 100644 --- a/test/regress/gold/vlog6.txt +++ b/test/regress/gold/vlog6.txt @@ -1,2 +1,2 @@ -1 1 1 000000ff -0 0 0 0000007f +1 1 1 ff +0 0 0 7f diff --git a/test/test_dump.c b/test/test_dump.c index db63626f..bc4af034 100644 --- a/test/test_dump.c +++ b/test/test_dump.c @@ -474,19 +474,19 @@ START_TEST(test_vlog1) vlog_dump(m2, 0); diff_dump(tb_get(tb), "module mod2;\n" - " wire [5'b111:5'b0] bus;\n" + " wire [32'd7:32'd0] bus;\n" " wire w;\n" " reg r;\n" " initial begin\n" " $display(\"hello\");\n" " if (bus)\n" - " r <= 5'b1 | r;\n" + " r <= 32'd1 | r;\n" " $finish;\n" - " r = 5'b1;\n" - " #5'b1 r <= 5'b0;\n" + " r = 32'd1;\n" + " #32'd1 r <= 32'd0;\n" " r = ~w;\n" " end\n" - " assign bus = 5'b11;\n" + " assign bus = 32'd3;\n" " pullup (supply1,supply0) p1 (w);\n" " mod u1 (w);\n" "endmodule // mod2\n\n"); diff --git a/test/test_vlog.c b/test/test_vlog.c index 66b55b31..2e716709 100644 --- a/test/test_vlog.c +++ b/test/test_vlog.c @@ -289,10 +289,12 @@ START_TEST(test_number2) int64_t ival; const char *string; } cases[] = { - { "1'b1", 1, 1, "1'b1" }, - { "5'b100", 5, 4, "5'b100" }, - { "1", 5, 1, "5'b1" }, - { "42", 8, 42, "8'b101010" }, + { "1'b1", 1, 1, "1'b1" }, + { "5'b100", 5, 4, "5'd4" }, + { "1", 32, 1, "32'd1" }, + { "42", 32, 42, "32'd42" }, + { "251251", 32, 251251, "32'd251251" }, + { "'hffffffff", 32, INTEGER_ENCODE_MAX, "32'd4294967295" }, }; LOCAL_TEXT_BUF tb = tb_new(); -- 2.39.2