From 309579f0910f31f7e3cab55f9a2651529cd29de3 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sat, 20 Apr 2024 17:34:54 +0100 Subject: [PATCH] Lowering for delays in Verilog procedural assignments --- lib/nvc/verilog-body.vhd | 26 +++++- lib/nvc/verilog.vhd | 3 +- src/rt/verilog.c | 25 ++++-- src/vlog/vlog-lower.c | 38 +++++++-- src/vlog/vlog-node.c | 2 +- src/vlog/vlog-number.c | 128 +++++++++++++++++++++------- test/regress/gold/ivtest2.txt | 12 +++ test/regress/{vlog9.v => ivtest1.v} | 2 +- test/regress/ivtest2.v | 79 +++++++++++++++++ test/regress/testlist.txt | 3 +- test/test_vlog.c | 2 + 11 files changed, 267 insertions(+), 53 deletions(-) create mode 100644 test/regress/gold/ivtest2.txt rename test/regress/{vlog9.v => ivtest1.v} (99%) create mode 100644 test/regress/ivtest2.v diff --git a/lib/nvc/verilog-body.vhd b/lib/nvc/verilog-body.vhd index eea1a1d4..dc17993d 100644 --- a/lib/nvc/verilog-body.vhd +++ b/lib/nvc/verilog-body.vhd @@ -90,6 +90,28 @@ package body verilog is return result; end function; + function to_logic (value : t_int64; width : natural) return t_packed_logic is + variable result : t_packed_logic(width - 1 downto 0); + variable b_val : t_logic := '0'; + variable i_val : t_int64 := value; + begin + if width < 1 then + return result; + elsif value < 0 then + b_val := '1'; + i_val := -(value + 1); + end if; + for i in 0 to result'left loop + if (i_val mod 2) = 0 then + result(i) := b_val; + else + result(i) := not b_val; + end if; + i_val := i_val/2; + end loop; + return result; + end function; + function to_net_value (value : t_logic) return t_net_value is begin case value is @@ -371,8 +393,8 @@ package body verilog is return not (l = r); end function; - impure function sys_time return time is + impure function sys_time return t_packed_logic is begin - return now; + return to_logic(time'pos(now), 64); end function; end package body; diff --git a/lib/nvc/verilog.vhd b/lib/nvc/verilog.vhd index 47a77f64..11062a31 100644 --- a/lib/nvc/verilog.vhd +++ b/lib/nvc/verilog.vhd @@ -44,6 +44,7 @@ package verilog is function to_logic (value : t_net_value) return t_logic; function to_logic (value : t_net_array) return t_packed_logic; function to_logic (value : t_resolved_net_array) return t_packed_logic; + function to_logic (value : t_int64; width : natural) return t_packed_logic; function to_net_value (value : t_logic) return t_net_value; function to_net_value (value : t_packed_logic) return t_net_array; @@ -82,7 +83,7 @@ package verilog is function "/=" (l, r : t_packed_logic) return boolean; procedure sys_finish; - impure function sys_time return time; + impure function sys_time return t_packed_logic; -- These procedures are called with a special variadic calling convention -- which cannot be represented in VHDL diff --git a/src/rt/verilog.c b/src/rt/verilog.c index a5c735aa..981d4442 100644 --- a/src/rt/verilog.c +++ b/src/rt/verilog.c @@ -53,7 +53,14 @@ static void verilog_printf(jit_scalar_t *args) if (start < p) fwrite(start, 1, p - start, stdout); - switch (*++p) { + p++; // Skip over '%' + + int fwidth = 0; + if (isdigit_iso88591(*p)) + fwidth = strtol(p + 1, (char **)&p, 10); + (void)fwidth; // TODO + + switch (*p) { case 's': { unsigned len; @@ -63,15 +70,18 @@ static void verilog_printf(jit_scalar_t *args) break; case 'd': case 'x': + case 'h': + case 't': { - unsigned width; - const uint8_t *bits = next_arg(&args, &width); - number_t num = number_pack(bits, width); + unsigned nbits; + const uint8_t *bits = next_arg(&args, &nbits); + number_t num = number_pack(bits, nbits); switch (*p) { case 'd': + case 't': { - const int dmax = calc_dec_size(width, false); + const int dmax = calc_dec_size(nbits, false); if (number_is_defined(num)) printf("%*"PRIi64, dmax, number_integer(num)); else @@ -79,10 +89,11 @@ static void verilog_printf(jit_scalar_t *args) } break; case 'x': + case 'h': if (number_is_defined(num)) - printf("%0*"PRIx64, width / 4, number_integer(num)); + printf("%0*"PRIx64, nbits / 4, number_integer(num)); else - printf("%*s", width / 4, "x"); + printf("%*s", nbits / 4, "x"); break; } diff --git a/src/vlog/vlog-lower.c b/src/vlog/vlog-lower.c index 7ce73208..e4fa7d74 100644 --- a/src/vlog/vlog-lower.c +++ b/src/vlog/vlog-lower.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2023-2024 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 @@ -408,7 +408,7 @@ static vcode_reg_t vlog_lower_sysfunc(lower_unit_t *lu, vlog_node_t v) { const v_sysfunc_kind_t kind = vlog_subkind(v); static const char *fns[] = { - "NVC.VERILOG.SYS_TIME()T" + "NVC.VERILOG.SYS_TIME()" T_PACKED_LOGIC }; assert(kind < ARRAY_LEN(fns)); @@ -417,9 +417,10 @@ static vcode_reg_t vlog_lower_sysfunc(lower_unit_t *lu, vlog_node_t v) switch (kind) { case V_SYS_TIME: { - vcode_type_t vtime = vtype_time(); + vcode_type_t vlogic = vlog_logic_type(); + vcode_type_t vpacked = vlog_packed_logic_type(); vcode_reg_t args[] = { context_reg }; - return emit_fcall(ident_new(fns[kind]), vtime, vtime, + return emit_fcall(ident_new(fns[kind]), vpacked, vlogic, args, ARRAY_LEN(args)); } @@ -623,6 +624,19 @@ static void vlog_lower_timing(lower_unit_t *lu, vlog_node_t v, bool is_static) static void vlog_lower_procedural_assign(lower_unit_t *lu, vlog_node_t v) { + if (vlog_kind(v) == V_BASSIGN && vlog_has_delay(v)) { + vlog_node_t delay = vlog_delay(v); + assert(vlog_kind(delay) == V_DELAY_CONTROL); + + vcode_block_t delay_bb = emit_block(); + vcode_reg_t delay_reg = vlog_lower_rvalue(lu, vlog_value(delay)); + vcode_reg_t time_reg = vlog_lower_to_time(lu, delay_reg); + + emit_wait(delay_bb, time_reg); + + vcode_select_block(delay_bb); + } + vlog_node_t target = vlog_target(v); vcode_reg_t target_reg = vlog_lower_lvalue(lu, target); @@ -661,7 +675,17 @@ static void vlog_lower_procedural_assign(lower_unit_t *lu, vlog_node_t v) { vcode_type_t vtime = vtype_time(); vcode_reg_t reject_reg = emit_const(vtime, 0); - vcode_reg_t after_reg = emit_const(vtime, 0); + + vcode_reg_t after_reg; + if (vlog_has_delay(v)) { + vlog_node_t delay = vlog_delay(v); + assert(vlog_kind(delay) == V_DELAY_CONTROL); + + vcode_reg_t delay_reg = vlog_lower_rvalue(lu, vlog_value(delay)); + after_reg = vlog_lower_to_time(lu, delay_reg); + } + else + after_reg = emit_const(vtime, 0); emit_sched_waveform(nets_reg, count_reg, data_reg, reject_reg, after_reg); @@ -674,10 +698,10 @@ static void vlog_lower_procedural_assign(lower_unit_t *lu, vlog_node_t v) // Delay one delta cycle to see the update vcode_type_t vtime = vtype_time(); - vcode_reg_t delay_reg = emit_const(vtime, 0); + vcode_reg_t zero_time_reg = emit_const(vtime, 0); vcode_block_t resume_bb = emit_block(); - emit_wait(resume_bb, delay_reg); + emit_wait(resume_bb, zero_time_reg); vcode_select_block(resume_bb); } diff --git a/src/vlog/vlog-node.c b/src/vlog/vlog-node.c index 400fcec0..aa3c5d4a 100644 --- a/src/vlog/vlog-node.c +++ b/src/vlog/vlog-node.c @@ -66,7 +66,7 @@ static const imask_t has_map[V_LAST_NODE_KIND] = { (I_IDENT | I_SUBKIND | I_RANGES | I_DATATYPE), // V_ASSIGN - (I_TARGET | I_VALUE | I_IDENT), + (I_TARGET | I_VALUE | I_IDENT | I_DELAY), // V_DIMENSION (I_SUBKIND | I_LEFT | I_RIGHT), diff --git a/src/vlog/vlog-number.c b/src/vlog/vlog-number.c index 1f4e07bf..d91d1965 100644 --- a/src/vlog/vlog-number.c +++ b/src/vlog/vlog-number.c @@ -37,7 +37,32 @@ typedef struct _bignum { uint64_t packed[0]; } bignum_t; -#define PACKED_ZERO UINT64_C(0xaaaaaaaaaaaaaa) +#define SMALL_PACKED_ZERO UINT64_C(0xaaaaaaaaaaaaaa) +#define BIG_PACKED_ZERO UINT64_C(0xaaaaaaaaaaaaaaaa) +#define BIGNUM_WORDS(w) (((w) + 31) / 32) + +static void number_shift_left(number_t *n, unsigned count, uint64_t carry_in) +{ + switch (n->common.tag) { + case TAG_BIGNUM: + { + assert(count < 32); + for (int i = 0; i < BIGNUM_WORDS(n->big->width); i++) { + const uint64_t carry_out = n->big->packed[i] >> (64 - count * 2); + n->big->packed[i] <<= count * 2; + n->big->packed[i] |= carry_in; + carry_in = carry_out; + } + } + break; + case TAG_SMALLNUM: + assert(count < n->small.width); + n->small.packed <<= count * 2; + break; + default: + DEBUG_ONLY(fatal_trace("invalid number tag %x", n->common.tag)); + } +} number_t number_new(const char *str) { @@ -66,7 +91,7 @@ number_t number_new(const char *str) for (const char *pp = p; *pp; pp++) has_xz |= (*pp == 'x' || *pp == 'z'); - if (!has_xz && width <= INTEGER_ENCODE_MAX) { + if (!has_xz && width <= INTEGER_WIDTH_MAX) { char *eptr; const int64_t bits = strtoll(p, &eptr, radix); if (*eptr != '\0') @@ -89,7 +114,7 @@ number_t number_new(const char *str) assert(bits >= 0); - if (width <= INTEGER_ENCODE_MAX) { + if (width <= INTEGER_WIDTH_MAX) { return (number_t) { .intg = { .tag = TAG_INTEGER, @@ -102,7 +127,7 @@ number_t number_new(const char *str) else { assert(false); - uint64_t packed = PACKED_ZERO; + uint64_t packed = SMALL_PACKED_ZERO; for (int i = width; i >= 0; i--) { packed <<= 2; if (bits & (1 << i)) @@ -121,21 +146,39 @@ number_t number_new(const char *str) }; } } - else if (width <= SMALLNUM_ENCODE_MAX) { - uint64_t packed = PACKED_ZERO; + else { + number_t result = { .bits = 0 }; + if (width <= SMALLNUM_WIDTH_MAX) { + result.small.tag = TAG_SMALLNUM; + result.small.width = width; + result.small.issigned = 0; + result.small.packed = SMALL_PACKED_ZERO; + } + else { + const int nwords = BIGNUM_WORDS(width); + result.big = xmalloc_flex(sizeof(bignum_t), nwords, sizeof(uint64_t)); + result.big->width = width; + result.big->issigned = 0; + + for (int i = 0; i < nwords; i++) + result.big->packed[i] = BIG_PACKED_ZERO; + } + for (; *p; p++) { switch (radix) { case RADIX_BIN: { - packed <<= 2; + uint64_t carry = LOGIC_X; switch (*p) { - case '0': packed |= LOGIC_0; break; - case '1': packed |= LOGIC_1; break; - case 'x': packed |= LOGIC_X; break; - case 'z': packed |= LOGIC_Z; break; + case '0': carry = LOGIC_0; break; + case '1': carry = LOGIC_1; break; + case 'x': carry = LOGIC_X; break; + case 'z': carry = LOGIC_Z; break; default: errorf("invalid character '%c' in number %s", *p, str); } + + number_shift_left(&result, 1, carry); } break; @@ -147,23 +190,14 @@ number_t number_new(const char *str) } } - return (number_t) { - .small = { - .tag = TAG_SMALLNUM, - .width = width, - .issigned = 0, - .packed = packed - } - }; + return result; } - else - abort(); } void number_free(number_t *val) { if (val->common.tag == TAG_BIGNUM) - abort(); + free(val->big); val->bits = 0; } @@ -172,14 +206,15 @@ void number_print(number_t val, text_buf_t *tb) { switch (val.common.tag) { case TAG_SMALLNUM: + case TAG_BIGNUM: { - tb_printf(tb, "%u'b", val.small.width); + tb_printf(tb, "%u'b", number_width(val)); 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; + for (int i = number_width(val) - 1; i >= 0; i--) { + const vlog_logic_t bit = number_bit(val, i); if (leading && bit != LOGIC_0) leading = false; else if (leading) @@ -211,9 +246,15 @@ bool number_is_defined(number_t val) { switch (val.common.tag) { case TAG_SMALLNUM: - return (val.small.packed & PACKED_ZERO) == PACKED_ZERO; + return (val.small.packed & SMALL_PACKED_ZERO) == SMALL_PACKED_ZERO; case TAG_INTEGER: return true; + case TAG_BIGNUM: + for (int i = 0; i < BIGNUM_WORDS(val.big->width); i++) { + if ((val.big->packed[i] & BIG_PACKED_ZERO) != BIG_PACKED_ZERO) + return false; + } + return true; default: DEBUG_ONLY(fatal_trace("invalid number tag %x", val.common.tag)); return 0; @@ -227,12 +268,13 @@ int64_t number_integer(number_t val) switch (val.common.tag) { case TAG_SMALLNUM: + case TAG_BIGNUM: { // TODO: signed vs unsigned uint64_t result = 0; - for (int i = val.small.width - 1; i >= 0; i--) { - const int bit = val.small.packed >> (i * 2); + for (int i = number_width(val) - 1; i >= 0; i--) { + const vlog_logic_t bit = number_bit(val, i); result <<= 1; result |= bit & 1; } @@ -255,6 +297,8 @@ unsigned number_width(number_t val) return val.small.width; case TAG_INTEGER: return val.intg.width; + case TAG_BIGNUM: + return val.big->width; default: DEBUG_ONLY(fatal_trace("invalid number tag %x", val.common.tag)); return 0; @@ -270,11 +314,13 @@ vlog_logic_t number_bit(number_t val, unsigned n) case TAG_INTEGER: assert(n < val.intg.width); return ((val.intg.packed >> n) & 1) | LOGIC_0; + case TAG_BIGNUM: + assert(n < val.big->width); + return (val.big->packed[n / 32] >> ((n % 32) * 2)) & 3; 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) @@ -300,10 +346,8 @@ number_t number_pack(const uint8_t *bits, unsigned width) } }; } - else { - assert(width <= SMALLNUM_WIDTH_MAX); - - uint64_t packed = PACKED_ZERO; + else if (width <= SMALLNUM_WIDTH_MAX) { + uint64_t packed = SMALL_PACKED_ZERO; for (int i = width - 1; i >= 0; i--) { assert(bits[i] <= 0b11); packed <<= 2; @@ -319,6 +363,24 @@ number_t number_pack(const uint8_t *bits, unsigned width) } }; } + else { + const int nwords = BIGNUM_WORDS(width); + bignum_t *bn = xmalloc_flex(sizeof(bignum_t), nwords, sizeof(uint64_t)); + bn->width = width; + bn->issigned = 0; + + for (int i = nwords - 1, b = 0; i >= 0; i--) { + bn->packed[i] = BIG_PACKED_ZERO; + + for (int j = 0; j < 32 && b < width; j++, b++) { + assert(bits[b] <= 0b11); + bn->packed[i] <<= 2; + bn->packed[i] |= bits[b]; + } + } + + return (number_t){ .big = bn }; + } } bool number_equal(number_t a, number_t b) diff --git a/test/regress/gold/ivtest2.txt b/test/regress/gold/ivtest2.txt new file mode 100644 index 00000000..1c324e1f --- /dev/null +++ b/test/regress/gold/ivtest2.txt @@ -0,0 +1,12 @@ +time= 51, cat1=1 +time= 52, cat2=1 +time= 53, cat3=1 +time= 54, cat4=1 +time= 55, foo1=1 +time= 56, foo2=1 +time= 57, foo3=1 +time= 58, foo4=1 +time= 59, bar1=1 +time= 59, bar2=1 +time= 59, bar3=1 +time= 59, bar4=1 diff --git a/test/regress/vlog9.v b/test/regress/ivtest1.v similarity index 99% rename from test/regress/vlog9.v rename to test/regress/ivtest1.v index 94afb0f4..0fc48b0c 100644 --- a/test/regress/vlog9.v +++ b/test/regress/ivtest1.v @@ -34,7 +34,7 @@ always @(posedge clk) endmodule -module vlog9 ; +module ivtest1 ; reg clk, d; diff --git a/test/regress/ivtest2.v b/test/regress/ivtest2.v new file mode 100644 index 00000000..10c90996 --- /dev/null +++ b/test/regress/ivtest2.v @@ -0,0 +1,79 @@ +// From Icarus Verilog ivtest/ivltests/delay.v + +`timescale 1ns/100ps + +module ivtest2; + + +reg clk; +reg cat1; +reg cat2; +reg cat3; +reg cat4; +reg foo1; +reg foo2; +reg foo3; +reg foo4; +reg bar1; +reg bar2; +reg bar3; +reg bar4; + + +initial begin + clk = 0; + #100 $finish(0); +end + + +always begin + clk = 0; + #50; + clk = 1; + #50; +end + + +always @(posedge clk) begin + cat1 = #1 1; + cat2 = #1 1; + cat3 = #1 1; + cat4 = #1 1; + foo1 = #1 1; + foo2 = #1 1; + foo3 = #1 1; + foo4 = #1 1; + bar1 <= #1 1; + bar2 <= #1 1; + bar3 <= #1 1; + bar4 <= #1 1; +end + + +always @(cat1) + $write("time=%0t, cat1=%0h\n", $time, cat1); + +always @(cat2) $write("time=%04d, cat2=%0h\n", $time, cat2); + +always @(cat3) $write("time=%04d, cat3=%0h\n", $time, cat3); + +always @(cat4) $write("time=%04d, cat4=%0h\n", $time, cat4); + +always @(foo1) $write("time=%04d, foo1=%0h\n", $time, foo1); + +always @(foo2) $write("time=%04d, foo2=%0h\n", $time, foo2); + +always @(foo3) $write("time=%04d, foo3=%0h\n", $time, foo3); + +always @(foo4) $write("time=%04d, foo4=%0h\n", $time, foo4); + +always @(bar1) $write("time=%04d, bar1=%0h\n", $time, bar1); + +always @(bar2) $write("time=%04d, bar2=%0h\n", $time, bar2); + +always @(bar3) $write("time=%04d, bar3=%0h\n", $time, bar3); + +always @(bar4) $write("time=%04d, bar4=%0h\n", $time, bar4); + + +endmodule diff --git a/test/regress/testlist.txt b/test/regress/testlist.txt index cbd40001..0820d75a 100644 --- a/test/regress/testlist.txt +++ b/test/regress/testlist.txt @@ -949,7 +949,7 @@ issue851 wave,2008 vlog8 verilog issue852 wave,2019,dump-arrays wave11 wave,2008,dump-arrays -vlog9 verilog +ivtest1 verilog issue654 normal,2008 issue854 normal,2019 ename8 normal,2008 @@ -965,3 +965,4 @@ issue874 normal,2008 vhpi14 normal,vhpi issue877 normal,2008 issue878 wave,2008,dump-arrays +ivtest2 verilog,gold diff --git a/test/test_vlog.c b/test/test_vlog.c index 31232ce3..ee4cfd99 100644 --- a/test/test_vlog.c +++ b/test/test_vlog.c @@ -297,6 +297,8 @@ START_TEST(test_number2) { "42", 32, 42, "42" }, { "251251", 32, 251251, "251251" }, { "'hffffffff", 32, INTEGER_ENCODE_MAX, "4294967295" }, + { "64'b0", 64, 0, "64'b0" }, + { "64'b101", 64, 5, "64'b101" }, }; LOCAL_TEXT_BUF tb = tb_new(); -- 2.39.2