From 4e1df74a60d0e39b3ebf42c8319062034dab7b7f Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Fri, 22 Sep 2023 20:43:01 +0100 Subject: [PATCH] Lowering for Verilog variables with packed dimensions --- lib/nvc/verilog-body.vhd | 13 ++++- lib/nvc/verilog.vhd | 4 +- src/rt/verilog.c | 6 +- src/vcode.c | 11 ++++ src/vlog/vlog-lower.c | 113 +++++++++++++++++++++++++++++++----- src/vlog/vlog-parse.y | 26 ++++++++- test/regress/gold/vlog2.txt | 2 +- test/regress/gold/vlog4.txt | 2 + test/regress/testlist.txt | 1 + test/regress/vlog4.v | 9 +++ 10 files changed, 163 insertions(+), 24 deletions(-) create mode 100644 test/regress/gold/vlog4.txt create mode 100644 test/regress/vlog4.v diff --git a/lib/nvc/verilog-body.vhd b/lib/nvc/verilog-body.vhd index c1ff4ff2..7a12432a 100644 --- a/lib/nvc/verilog-body.vhd +++ b/lib/nvc/verilog-body.vhd @@ -16,9 +16,18 @@ package body verilog is - function to_integer (value : t_packed_logic) return integer is + function to_integer (value : t_packed_logic) return t_int64 is + alias v : t_packed_logic(0 to value'length - 1) is value; + variable r : t_int64 := 0; + variable add : t_int64 := 1; begin - return 0; + for i in v'range loop + if v(i) = '1' then + r := r + add; + end if; + add := add * 2; + end loop; + return r; end function; function to_time (value : t_packed_logic) return delay_length is diff --git a/lib/nvc/verilog.vhd b/lib/nvc/verilog.vhd index bbed9591..f1708671 100644 --- a/lib/nvc/verilog.vhd +++ b/lib/nvc/verilog.vhd @@ -20,11 +20,13 @@ package verilog is + type t_int64 is range -9223372036854775807 - 1 to 9223372036854775807; + type t_logic is ('0', '1', 'Z', 'X'); type t_packed_logic is array (natural range <>) of t_logic; - function to_integer (value : t_packed_logic) return integer; + function to_integer (value : t_packed_logic) return t_int64; function to_time (value : t_packed_logic) return delay_length; diff --git a/src/rt/verilog.c b/src/rt/verilog.c index 9710be8b..644f9f4f 100644 --- a/src/rt/verilog.c +++ b/src/rt/verilog.c @@ -62,15 +62,15 @@ static void verilog_printf(jit_scalar_t *args) switch (*p) { case 'd': if (number_is_defined(num)) - printf("%*"PRIi64, width, number_integer(num)); + printf("%*"PRIi64, ilog2(width), number_integer(num)); else - printf("x"); + printf("%*s", ilog2(width), "x"); break; case 'x': if (number_is_defined(num)) printf("%0*"PRIx64, width, number_integer(num)); else - printf("x"); + printf("%*s", width/4, "x"); break; } } diff --git a/src/vcode.c b/src/vcode.c index 60b1e161..46729f4b 100644 --- a/src/vcode.c +++ b/src/vcode.c @@ -4446,6 +4446,17 @@ vcode_reg_t emit_wrap(vcode_reg_t data, const vcode_dim_t *dims, int ndims) VCODE_ASSERT(ptrkind == VCODE_TYPE_POINTER || ptrkind == VCODE_TYPE_SIGNAL, "wrapped data is not pointer or signal"); +#ifdef DEBUG + for (int i = 0; i < ndims; i++) { + VCODE_ASSERT(vtype_is_scalar(vcode_reg_type(dims[i].left)), + "dimension %d left bound must be scalar", i + 1); + VCODE_ASSERT(vtype_is_scalar(vcode_reg_type(dims[i].right)), + "dimension %d right bound must be scalar", i + 1); + VCODE_ASSERT(vtype_eq(vtype_bool(), vcode_reg_type(dims[i].dir)), + "dimension %d direction must be bool", i + 1); + } +#endif + vcode_type_t elem = (ptrkind == VCODE_TYPE_POINTER) ? vtype_pointed(ptr_type) : ptr_type; diff --git a/src/vlog/vlog-lower.c b/src/vlog/vlog-lower.c index e842167a..9f9397a8 100644 --- a/src/vlog/vlog-lower.c +++ b/src/vlog/vlog-lower.c @@ -40,6 +40,7 @@ emit_debug_info(vlog_loc((v))); \ static void vlog_lower_stmts(lower_unit_t *lu, vlog_node_t v); +static vcode_reg_t vlog_lower_rvalue(lower_unit_t *lu, vlog_node_t v); static inline vcode_type_t vlog_logic_type(void) { @@ -59,7 +60,6 @@ static vcode_reg_t vlog_lower_wrap(lower_unit_t *lu, vcode_reg_t reg) { vcode_type_t voffset = vtype_offset(); vcode_reg_t left_reg = emit_const(voffset, 0), right_reg, data_reg; - vcode_reg_t dir_reg = emit_const(voffset, RANGE_TO); switch (vcode_reg_kind(reg)) { case VCODE_TYPE_CARRAY: @@ -77,11 +77,15 @@ static vcode_reg_t vlog_lower_wrap(lower_unit_t *lu, vcode_reg_t reg) right_reg = left_reg; } break; + case VCODE_TYPE_UARRAY: + return reg; default: vcode_dump(); fatal_trace("cannot wrap r%d", reg); } + vcode_reg_t dir_reg = emit_const(vtype_bool(), RANGE_TO); + vcode_dim_t dims[1] = { { left_reg, right_reg, dir_reg } }; @@ -99,6 +103,19 @@ static vcode_reg_t vlog_lower_to_time(lower_unit_t *lu, vcode_reg_t reg) return emit_fcall(func, vtime, vtime, VCODE_CC_VHDL, args, ARRAY_LEN(args)); } +static vcode_reg_t vlog_lower_to_integer(lower_unit_t *lu, vcode_reg_t reg) +{ + vcode_reg_t context_reg = emit_link_package(ident_new("NVC.VERILOG")); + vcode_reg_t wrap_reg = vlog_lower_wrap(lu, reg); + vcode_reg_t args[] = { context_reg, wrap_reg }; + vcode_type_t vint64 = vtype_int(INT64_MIN, INT64_MAX); + ident_t func = ident_new("NVC.VERILOG.TO_INTEGER" + "(26NVC.VERILOG.T_PACKED_LOGIC)" + "19NVC.VERILOG.T_INT64"); + return emit_fcall(func, vint64, vint64, VCODE_CC_VHDL, + args, ARRAY_LEN(args)); +} + static vcode_reg_t vlog_lower_to_bool(lower_unit_t *lu, vcode_reg_t reg) { @@ -117,24 +134,57 @@ static vcode_reg_t vlog_lower_to_bool(lower_unit_t *lu, vcode_reg_t reg) } } -static void vlog_lower_signal_decl(lower_unit_t *lu, vlog_node_t port) +static void vlog_lower_signal_decl(lower_unit_t *lu, vlog_node_t decl) { vcode_type_t vlogic = vlog_logic_type(); vcode_type_t vsignal = vtype_signal(vlogic); vcode_type_t voffset = vtype_offset(); - vcode_var_t var = emit_var(vsignal, vlogic, vlog_ident(port), VAR_SIGNAL); - lower_put_vcode_obj(port, var, lu); + const int nranges = vlog_ranges(decl); + + vcode_dim_t *dims = NULL; + if (nranges > 0) { + dims = xmalloc_array(nranges, sizeof(vcode_dim_t)); + vsignal = vtype_uarray(nranges, vsignal, vsignal); + } + + vcode_var_t var = emit_var(vsignal, vlogic, vlog_ident(decl), VAR_SIGNAL); + lower_put_vcode_obj(decl, var, lu); vcode_reg_t size = emit_const(voffset, 1); vcode_reg_t count = emit_const(voffset, 1); vcode_reg_t init = emit_const(vlogic, 3); vcode_reg_t flags = emit_const(voffset, 0); - vcode_reg_t locus = vlog_debug_locus(port); + vcode_reg_t locus = vlog_debug_locus(decl); + + for (int i = 0; i < nranges; i++) { + vlog_node_t r = vlog_range(decl, i); + vcode_reg_t left_reg = vlog_lower_rvalue(lu, vlog_left(r)); + vcode_reg_t right_reg = vlog_lower_rvalue(lu, vlog_right(r)); + + vcode_reg_t ileft_reg = vlog_lower_to_integer(lu, left_reg); + vcode_reg_t iright_reg = vlog_lower_to_integer(lu, right_reg); + + vcode_reg_t dir_reg = emit_cmp(VCODE_CMP_GT, ileft_reg, iright_reg); + vcode_reg_t length_reg = + emit_range_length(ileft_reg, iright_reg, dir_reg); + + count = emit_mul(count, length_reg); + + dims[i].dir = dir_reg; + dims[i].left = ileft_reg; + dims[i].right = iright_reg; + } - vcode_reg_t nets_reg = emit_init_signal(vlogic, size, count, init, flags, + vcode_reg_t nets_reg = emit_init_signal(vlogic, count, size, init, flags, locus, VCODE_INVALID_REG); - emit_store(nets_reg, var); + + if (nranges > 0) { + vcode_reg_t wrap_reg = emit_wrap(nets_reg, dims, nranges); + emit_store(wrap_reg, var); + } + else + emit_store(nets_reg, var); } static void vlog_lower_decls(lower_unit_t *lu, vlog_node_t scope) @@ -226,7 +276,19 @@ static vcode_reg_t vlog_lower_rvalue(lower_unit_t *lu, vlog_node_t v) else nets_reg = emit_load_indirect(emit_var_upref(hops, var)); - return emit_load_indirect(emit_resolved(nets_reg)); + if (vcode_reg_kind(nets_reg) == VCODE_TYPE_UARRAY) { + vcode_reg_t data_reg = emit_resolved(emit_unwrap(nets_reg)); + + // XXX: add a rewrap opcode + vcode_dim_t dims[1] = { + { .left = emit_uarray_left(nets_reg, 0), + .right = emit_uarray_right(nets_reg, 0), + .dir = emit_uarray_dir(nets_reg, 0) }, + }; + return emit_wrap(data_reg, dims, 1); + } + else + return emit_load_indirect(emit_resolved(nets_reg)); } case V_EVENT: { @@ -343,12 +405,24 @@ static void vlog_lower_timing(lower_unit_t *lu, vlog_node_t v, bool is_static) static void vlog_lower_nbassign(lower_unit_t *lu, vlog_node_t v) { - vcode_reg_t nets_reg = vlog_lower_lvalue(lu, vlog_target(v)); - vcode_reg_t count_reg = emit_const(vtype_offset(), 1); + vcode_reg_t target_reg = vlog_lower_lvalue(lu, vlog_target(v)); vcode_reg_t value_reg = vlog_lower_rvalue(lu, vlog_value(v)); - if (vcode_reg_kind(value_reg) == VCODE_TYPE_CARRAY) - value_reg = emit_load_indirect(emit_address_of(value_reg)); // XXX + vcode_reg_t count_reg, nets_reg; + if (vcode_reg_kind(target_reg) == VCODE_TYPE_UARRAY) { + nets_reg = emit_unwrap(target_reg); + count_reg = emit_uarray_len(target_reg, 0); + + if (vcode_reg_kind(value_reg) == VCODE_TYPE_CARRAY) + value_reg = emit_address_of(value_reg); // XXX + } + else { + nets_reg = target_reg; + count_reg = emit_const(vtype_offset(), 1); + + if (vcode_reg_kind(value_reg) == VCODE_TYPE_CARRAY) + value_reg = emit_load_indirect(emit_address_of(value_reg)); // XXX + } vcode_type_t vtime = vtype_time(); vcode_reg_t reject_reg = emit_const(vtime, 0); @@ -463,8 +537,19 @@ static void vlog_lower_stmts(lower_unit_t *lu, vlog_node_t v) static void vlog_lower_driver(lower_unit_t *lu, vlog_node_t v) { - vcode_reg_t nets_reg = vlog_lower_lvalue(lu, v); - emit_drive_signal(nets_reg, emit_const(vtype_offset(), 1)); + vcode_reg_t target_reg = vlog_lower_lvalue(lu, v); + + vcode_reg_t nets_reg, count_reg; + if (vcode_reg_kind(target_reg) == VCODE_TYPE_UARRAY) { + nets_reg = emit_unwrap(target_reg); + count_reg = emit_uarray_len(target_reg, 0); + } + else { + nets_reg = target_reg; + count_reg = emit_const(vtype_offset(), 1); + } + + emit_drive_signal(nets_reg, count_reg); } static void vlog_driver_cb(vlog_node_t v, void *context) diff --git a/src/vlog/vlog-parse.y b/src/vlog/vlog-parse.y index 33656fa2..84d3b1ef 100644 --- a/src/vlog/vlog-parse.y +++ b/src/vlog/vlog-parse.y @@ -123,7 +123,7 @@ static bool is_decl(vlog_node_t v) %type module_or_generate_item_declaration %type module_or_generate_item continuous_assign %type list_of_net_assignments reg_declaration -%type list_of_variable_identifiers +%type list_of_variable_identifiers list_of_statements_opt %type external_identifier %type net_type @@ -356,6 +356,20 @@ reg_declaration: { $$ = $2; } + | tREG '[' expression ':' expression ']' + list_of_variable_identifiers ';' + { + vlog_node_t r = vlog_new(V_DIMENSION); + vlog_set_loc(r, &@$); + vlog_set_subkind(r, V_DIM_PACKED); + vlog_set_left(r, $3); + vlog_set_right(r, $5); + + for (node_list_t *it = $7; it; it = it->next) + vlog_add_range(it->value, r); + + $$ = $7; + } ; list_of_variable_identifiers: @@ -439,6 +453,12 @@ statement_or_null: | ';' { $$ = NULL; } ; +list_of_statements_opt: + list_of_statements + | /* Empty */ + { $$ = NULL; } + ; + list_of_statements: list_of_statements statement { @@ -508,7 +528,7 @@ system_task_enable: } ; -seq_block: tBEGIN list_of_statements tEND +seq_block: tBEGIN list_of_statements_opt tEND { $$ = vlog_new(V_SEQ_BLOCK); vlog_set_loc($$, &@$); @@ -517,7 +537,7 @@ seq_block: tBEGIN list_of_statements tEND vlog_add_stmt($$, it->value); node_list_free($2); } - | tBEGIN ':' identifier list_of_statements tEND + | tBEGIN ':' identifier list_of_statements_opt tEND { $$ = vlog_new(V_SEQ_BLOCK); vlog_set_loc($$, &@$); diff --git a/test/regress/gold/vlog2.txt b/test/regress/gold/vlog2.txt index 125c2a74..7de72212 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 00005 0 0ms+0: $finish called diff --git a/test/regress/gold/vlog4.txt b/test/regress/gold/vlog4.txt new file mode 100644 index 00000000..7931ff74 --- /dev/null +++ b/test/regress/gold/vlog4.txt @@ -0,0 +1,2 @@ +x ==> x +x ==> 42 diff --git a/test/regress/testlist.txt b/test/regress/testlist.txt index ae38e03f..2c2079a3 100644 --- a/test/regress/testlist.txt +++ b/test/regress/testlist.txt @@ -876,3 +876,4 @@ vhpi11 normal,vhpi,2008 attr18 normal,2019 issue762 normal,vhpi vlog3 verilog,gold +vlog4 verilog,gold diff --git a/test/regress/vlog4.v b/test/regress/vlog4.v new file mode 100644 index 00000000..70e87bdb --- /dev/null +++ b/test/regress/vlog4.v @@ -0,0 +1,9 @@ +module vlog4; + reg [7:0] x; + + initial begin + x <= 42; + $display("x ==> %d", x); + #1 $display("x ==> %d", x); + end +endmodule // vlog4 -- 2.39.2