From b50158a7516534a7390ee5be8a9643f862a98b32 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Fri, 26 Jan 2024 18:05:27 +0000 Subject: [PATCH] Rework implementation of conversions in port maps. Issue #843 --- NEWS.md | 2 + src/jit/jit-core.c | 3 + src/jit/jit-dump.c | 1 + src/jit/jit-exits.c | 52 +++++-- src/jit/jit-exits.h | 7 +- src/jit/jit-irgen.c | 75 ++++++--- src/jit/jit-priv.h | 3 + src/lower.c | 164 +++++++++----------- src/rt/model.c | 268 +++++++++++++++++++-------------- src/rt/structs.h | 11 +- src/vcode.c | 111 ++++++++++---- src/vcode.h | 10 +- src/vlog/vlog-lower.c | 14 +- test/regress/bounds43.vhd | 14 ++ test/regress/conv11.vhd | 53 +++++++ test/regress/conv12.vhd | 50 ++++++ test/regress/gold/bounds43.txt | 1 + test/regress/issue843.vhd | 147 ++++++++++++++++++ test/regress/testlist.txt | 4 + test/test_lower.c | 6 +- 20 files changed, 709 insertions(+), 287 deletions(-) create mode 100644 test/regress/bounds43.vhd create mode 100644 test/regress/conv11.vhd create mode 100644 test/regress/conv12.vhd create mode 100644 test/regress/gold/bounds43.txt create mode 100644 test/regress/issue843.vhd diff --git a/NEWS.md b/NEWS.md index bf3f0310..2721b035 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,6 +8,8 @@ command uses to compile third-party libraries (#836). - The prefix of an indexed or slice name now must be another name or function call, as required by the LRM (#835). +- The implementation of conversions in port maps has been reworked and + fixes a number of long-standing issues (#843). ## Version 1.11.2 - 2024-01-04 - Fixed an incorrect length check in the equivalent process for diff --git a/src/jit/jit-core.c b/src/jit/jit-core.c index f81970fe..9b09ff1c 100644 --- a/src/jit/jit-core.c +++ b/src/jit/jit-core.c @@ -557,6 +557,9 @@ static void jit_emit_trace(diag_t *d, const loc_t *loc, object_t *enclosing, case T_WAVEFORM: diag_trace(d, loc, "Equivalent process"); break; + case T_TYPE_CONV: + diag_trace(d, loc, "Type conversion %s", type_pp(tree_type(tree))); + break; default: diag_trace(d, loc, "$$%s", istr(tree_ident(tree))); break; diff --git a/src/jit/jit-dump.c b/src/jit/jit-dump.c index f153b212..eed10321 100644 --- a/src/jit/jit-dump.c +++ b/src/jit/jit-dump.c @@ -87,6 +87,7 @@ const char *jit_exit_name(jit_exit_t exit) "DRIVING_VALUE", "CLAIM_TLAB", "COVER_TOGGLE", "PROCESS_INIT", "CLEAR_EVENT", "IMPLICIT_EVENT", "ENTER_STATE", "REFLECT_VALUE", "REFLECT_SUBTYPE", "FUNCTION_TRIGGER", "ADD_TRIGGER", "TRANSFER_SIGNAL", + "PORT_CONVERSION", "CONVERT_IN", "CONVERT_OUT", }; assert(exit < ARRAY_LEN(names)); return names[exit]; diff --git a/src/jit/jit-exits.c b/src/jit/jit-exits.c index e22cb443..d285abf6 100644 --- a/src/jit/jit-exits.c +++ b/src/jit/jit-exits.c @@ -578,19 +578,9 @@ void __nvc_do_exit(jit_exit_t which, jit_anchor_t *anchor, jit_scalar_t *args, uint32_t src_offset = args[1].integer; sig_shared_t *dst_ss = args[2].pointer; uint32_t dst_offset = args[3].integer; - uint32_t src_count = args[4].integer; - uint32_t dst_count = args[5].integer; - jit_handle_t handle = args[6].integer; - void *context = args[7].pointer; + uint32_t count = args[4].integer; - if (handle != JIT_HANDLE_INVALID) { - ffi_closure_t closure = { handle, context }; - x_map_signal(src_ss, src_offset, dst_ss, dst_offset, src_count, - dst_count, &closure); - } - else - x_map_signal(src_ss, src_offset, dst_ss, dst_offset, src_count, - dst_count, NULL); + x_map_signal(src_ss, src_offset, dst_ss, dst_offset, count); } break; @@ -1007,6 +997,44 @@ void __nvc_do_exit(jit_exit_t which, jit_anchor_t *anchor, jit_scalar_t *args, } break; + case JIT_EXIT_PORT_CONVERSION: + { + jit_handle_t handle = args[0].integer; + void *context = args[1].pointer; + + if (jit_has_runtime(thread->jit)) { + ffi_closure_t closure = { handle, context }; + args[0].pointer = x_port_conversion(&closure); + } + else + args[0].pointer = NULL; // Called during constant folding + } + break; + + case JIT_EXIT_CONVERT_IN: + { + void *conv = args[0].pointer; + sig_shared_t *shared = args[1].pointer; + int32_t offset = args[2].integer; + int32_t count = args[3].integer; + + if (conv != NULL) + x_convert_in(conv, shared, offset, count); + } + break; + + case JIT_EXIT_CONVERT_OUT: + { + void *conv = args[0].pointer; + sig_shared_t *shared = args[1].pointer; + int32_t offset = args[2].integer; + int32_t count = args[3].integer; + + if (conv != NULL) + x_convert_out(conv, shared, offset, count); + } + break; + default: fatal_trace("unhandled exit %s", jit_exit_name(which)); } diff --git a/src/jit/jit-exits.h b/src/jit/jit-exits.h index 07736990..cd394cbb 100644 --- a/src/jit/jit-exits.h +++ b/src/jit/jit-exits.h @@ -66,9 +66,7 @@ void x_report(const uint8_t *msg, int32_t msg_len, int8_t severity, int64_t x_last_event(sig_shared_t *ss, uint32_t offset, int32_t count); int64_t x_last_active(sig_shared_t *ss, uint32_t offset, int32_t count); void x_map_signal(sig_shared_t *src_ss, uint32_t src_offset, - sig_shared_t *dst_ss, uint32_t dst_offset, - uint32_t src_count, uint32_t dst_count, - ffi_closure_t *closure); + sig_shared_t *dst_ss, uint32_t dst_offset, uint32_t count); void x_map_const(sig_shared_t *ss, uint32_t offset, const uint8_t *values, uint32_t count); void x_push_scope(tree_t where, int32_t size); @@ -96,5 +94,8 @@ void *x_reflect_subtype(void *context, tree_t where, const jit_scalar_t *bounds); void *x_function_trigger(const ffi_closure_t *closure); void x_add_trigger(void *ptr); +void *x_port_conversion(const ffi_closure_t *closure); +void x_convert_in(void *ptr, sig_shared_t *ss, uint32_t offset, int32_t count); +void x_convert_out(void *ptr, sig_shared_t *ss, uint32_t offset, int32_t count); #endif // _JIT_EXITS_H diff --git a/src/jit/jit-irgen.c b/src/jit/jit-irgen.c index 6b228c83..439ca35f 100644 --- a/src/jit/jit-irgen.c +++ b/src/jit/jit-irgen.c @@ -2791,31 +2791,17 @@ static void irgen_op_alias_signal(jit_irgen_t *g, int op) static void irgen_op_map_signal(jit_irgen_t *g, int op) { - jit_value_t src_ss = irgen_get_arg(g, op, 0); - jit_value_t src_off = jit_value_from_reg(jit_value_as_reg(src_ss) + 1); - jit_value_t dst_ss = irgen_get_arg(g, op, 1); - jit_value_t dst_off = jit_value_from_reg(jit_value_as_reg(dst_ss) + 1); - jit_value_t src_count = irgen_get_arg(g, op, 2); - jit_value_t dst_count = irgen_get_arg(g, op, 3); - - jit_value_t closure, context; - if (vcode_count_args(op) == 5) { - closure = irgen_get_arg(g, op, 4); - context = jit_value_from_reg(jit_value_as_reg(closure) + 1); - } - else { - closure = jit_value_from_handle(JIT_HANDLE_INVALID); - context = jit_null_ptr(); - } + jit_value_t src_ss = irgen_get_arg(g, op, 0); + jit_value_t src_off = jit_value_from_reg(jit_value_as_reg(src_ss) + 1); + jit_value_t dst_ss = irgen_get_arg(g, op, 1); + jit_value_t dst_off = jit_value_from_reg(jit_value_as_reg(dst_ss) + 1); + jit_value_t count = irgen_get_arg(g, op, 2); j_send(g, 0, src_ss); j_send(g, 1, src_off); j_send(g, 2, dst_ss); j_send(g, 3, dst_off); - j_send(g, 4, src_count); - j_send(g, 5, dst_count); - j_send(g, 6, closure); - j_send(g, 7, context); + j_send(g, 4, count); macro_exit(g, JIT_EXIT_MAP_SIGNAL); } @@ -3471,6 +3457,46 @@ static void irgen_op_add_trigger(jit_irgen_t *g, int op) macro_exit(g, JIT_EXIT_ADD_TRIGGER); } +static void irgen_op_port_conversion(jit_irgen_t *g, int op) +{ + jit_value_t closure = irgen_get_arg(g, op, 0); + jit_value_t context = jit_value_from_reg(jit_value_as_reg(closure) + 1); + + j_send(g, 0, closure); + j_send(g, 1, context); + macro_exit(g, JIT_EXIT_PORT_CONVERSION); + + g->map[vcode_get_result(op)] = j_recv(g, 0); +} + +static void irgen_op_convert_in(jit_irgen_t *g, int op) +{ + jit_value_t conv = irgen_get_arg(g, op, 0); + jit_value_t shared = irgen_get_arg(g, op, 1); + jit_value_t offset = jit_value_from_reg(jit_value_as_reg(shared) + 1); + jit_value_t count = irgen_get_arg(g, op, 2); + + j_send(g, 0, conv); + j_send(g, 1, shared); + j_send(g, 2, offset); + j_send(g, 3, count); + macro_exit(g, JIT_EXIT_CONVERT_IN); +} + +static void irgen_op_convert_out(jit_irgen_t *g, int op) +{ + jit_value_t conv = irgen_get_arg(g, op, 0); + jit_value_t shared = irgen_get_arg(g, op, 1); + jit_value_t offset = jit_value_from_reg(jit_value_as_reg(shared) + 1); + jit_value_t count = irgen_get_arg(g, op, 2); + + j_send(g, 0, conv); + j_send(g, 1, shared); + j_send(g, 2, offset); + j_send(g, 3, count); + macro_exit(g, JIT_EXIT_CONVERT_OUT); +} + static void irgen_block(jit_irgen_t *g, vcode_block_t block) { vcode_select_block(block); @@ -3854,6 +3880,15 @@ static void irgen_block(jit_irgen_t *g, vcode_block_t block) case VCODE_OP_ADD_TRIGGER: irgen_op_add_trigger(g, i); break; + case VCODE_OP_PORT_CONVERSION: + irgen_op_port_conversion(g, i); + break; + case VCODE_OP_CONVERT_IN: + irgen_op_convert_in(g, i); + break; + case VCODE_OP_CONVERT_OUT: + irgen_op_convert_out(g, i); + break; default: fatal_trace("cannot generate JIT IR for vcode op %s", vcode_op_string(op)); diff --git a/src/jit/jit-priv.h b/src/jit/jit-priv.h index a5684191..a09a103f 100644 --- a/src/jit/jit-priv.h +++ b/src/jit/jit-priv.h @@ -163,6 +163,9 @@ typedef enum { JIT_EXIT_FUNCTION_TRIGGER, JIT_EXIT_ADD_TRIGGER, JIT_EXIT_TRANSFER_SIGNAL, + JIT_EXIT_PORT_CONVERSION, + JIT_EXIT_CONVERT_IN, + JIT_EXIT_CONVERT_OUT, } jit_exit_t; typedef uint16_t jit_reg_t; diff --git a/src/lower.c b/src/lower.c index 72c0c9c2..bbf35b73 100644 --- a/src/lower.c +++ b/src/lower.c @@ -102,14 +102,6 @@ typedef struct { vcode_reg_t reg; } concat_param_t; -typedef struct { - vcode_reg_t conv_func; - vcode_reg_t conv_reg; - vcode_reg_t conv_count; - bool reverse; - bool is_const; -} map_signal_param_t; - typedef struct { lower_unit_t *lu; vcode_reg_t wake; @@ -117,6 +109,7 @@ typedef struct { typedef void (*lower_field_fn_t)(lower_unit_t *, tree_t, vcode_reg_t, vcode_reg_t, vcode_reg_t, void *); +typedef void (*convert_emit_fn)(vcode_reg_t, vcode_reg_t, vcode_reg_t); typedef A(concat_param_t) concat_list_t; @@ -11379,16 +11372,14 @@ static void lower_map_view_field_cb(lower_unit_t *lu, tree_t field, vcode_reg_t src_nets = lower_array_data(src_reg); vcode_reg_t dst_nets = lower_array_data(dst_reg); - vcode_reg_t conv_reg = VCODE_INVALID_REG; // Not valid for interfaces - switch (converse_mode(elem, converse)) { case PORT_IN: - emit_map_signal(dst_nets, src_nets, count_reg, count_reg, conv_reg); + emit_map_signal(dst_nets, src_nets, count_reg); break; case PORT_OUT: case PORT_INOUT: case PORT_BUFFER: - emit_map_signal(src_nets, dst_nets, count_reg, count_reg, conv_reg); + emit_map_signal(src_nets, dst_nets, count_reg); break; default: fatal_trace("unhandled port mode in lower_map_view_field_cb"); @@ -11405,134 +11396,110 @@ static void lower_map_view_field_cb(lower_unit_t *lu, tree_t field, static void lower_map_signal_field_cb(lower_unit_t *lu, tree_t field, vcode_reg_t src_ptr, vcode_reg_t dst_ptr, - vcode_reg_t locus, void *__ctx) + vcode_reg_t locus, void *ctx) { type_t ftype = tree_type(field); - map_signal_param_t *args = __ctx; if (type_is_homogeneous(ftype)) { - vcode_reg_t dst_reg, dst_count = VCODE_INVALID_REG; - if (!args->reverse && args->conv_func != VCODE_INVALID_REG) { - dst_reg = args->conv_reg; - dst_count = args->conv_count; - } - else if (args->reverse) - dst_reg = emit_load_indirect(src_ptr); - else - dst_reg = emit_load_indirect(dst_ptr); + vcode_reg_t dst_reg = emit_load_indirect(dst_ptr); - vcode_reg_t src_reg, src_count = VCODE_INVALID_REG; - if (args->reverse && args->conv_func != VCODE_INVALID_REG) { - src_reg = args->conv_reg; - src_count = args->conv_count; - } - else if (args->reverse && !args->is_const) - src_reg = emit_load_indirect(dst_ptr); - else if (!args->is_const) + vcode_reg_t src_reg; + if (lower_have_signal(src_ptr)) src_reg = emit_load_indirect(src_ptr); else if (have_uarray_ptr(src_ptr)) src_reg = emit_load_indirect(src_ptr); else src_reg = src_ptr; - if (type_is_array(ftype) && args->conv_func == VCODE_INVALID_REG) + if (type_is_array(ftype)) lower_check_array_sizes(lu, ftype, ftype, src_reg, dst_reg, locus); - if (src_count == VCODE_INVALID_REG) - src_count = lower_type_width(lu, ftype, src_reg); - - if (dst_count == VCODE_INVALID_REG) - dst_count = lower_type_width(lu, ftype, dst_reg); + vcode_reg_t count_reg = lower_type_width(lu, ftype, dst_reg); src_reg = lower_array_data(src_reg); dst_reg = lower_array_data(dst_reg); - if (args->is_const) - emit_map_const(src_reg, dst_reg, src_count); + if (lower_have_signal(src_reg)) + emit_map_signal(src_reg, dst_reg, count_reg); else - emit_map_signal(src_reg, dst_reg, src_count, dst_count, - args->conv_func); + emit_map_const(src_reg, dst_reg, count_reg); } else lower_for_each_field(lu, ftype, src_ptr, dst_ptr, locus, - lower_map_signal_field_cb, __ctx); + lower_map_signal_field_cb, ctx); } static void lower_map_signal(lower_unit_t *lu, vcode_reg_t src_reg, vcode_reg_t dst_reg, type_t src_type, - type_t dst_type, vcode_reg_t conv_func, - tree_t where) + type_t dst_type, tree_t where) { if (!type_is_homogeneous(src_type)) { - map_signal_param_t args = { - .conv_func = conv_func, - .conv_reg = dst_reg, - .is_const = !lower_have_signal(src_reg), - }; - - if (conv_func != VCODE_INVALID_REG) { - args.conv_count = lower_type_width(lu, dst_type, dst_reg); - dst_reg = VCODE_INVALID_REG; - } - vcode_reg_t locus = lower_debug_locus(where); lower_for_each_field(lu, src_type, src_reg, dst_reg, locus, - lower_map_signal_field_cb, &args); - } - else if (!type_is_homogeneous(dst_type)) { - map_signal_param_t args = { - .conv_func = conv_func, - .conv_reg = src_reg, - .reverse = true, - .is_const = !lower_have_signal(src_reg), - }; - - if (conv_func != VCODE_INVALID_REG) { - args.conv_count = lower_type_width(lu, src_type, src_reg); - src_reg = VCODE_INVALID_REG; - } - - vcode_reg_t locus = lower_debug_locus(where); - lower_for_each_field(lu, dst_type, dst_reg, src_reg, locus, - lower_map_signal_field_cb, &args); - } - else if (conv_func != VCODE_INVALID_REG) { - vcode_reg_t src_count = lower_type_width(lu, src_type, src_reg); - vcode_reg_t dst_count = lower_type_width(lu, dst_type, dst_reg); - - assert(lower_have_signal(src_reg)); - - vcode_reg_t src_nets = lower_array_data(src_reg); - vcode_reg_t dst_nets = lower_array_data(dst_reg); - - emit_map_signal(src_nets, dst_nets, src_count, dst_count, conv_func); + lower_map_signal_field_cb, NULL); } else if (type_is_array(src_type)) { - vcode_reg_t src_count = lower_array_total_len(lu, src_type, src_reg); - vcode_reg_t dst_count = lower_array_total_len(lu, dst_type, dst_reg); - vcode_reg_t locus = lower_debug_locus(where); lower_check_array_sizes(lu, dst_type, src_type, dst_reg, src_reg, locus); vcode_reg_t src_nets = lower_array_data(src_reg); vcode_reg_t dst_nets = lower_array_data(dst_reg); + vcode_reg_t count_reg = lower_array_total_len(lu, dst_type, dst_reg); + if (lower_have_signal(src_reg)) - emit_map_signal(src_nets, dst_nets, src_count, dst_count, conv_func); + emit_map_signal(src_nets, dst_nets, count_reg); else - emit_map_const(src_nets, dst_nets, src_count); + emit_map_const(src_nets, dst_nets, count_reg); } else { vcode_reg_t count_reg = emit_const(vtype_offset(), 1); vcode_reg_t dst_nets = lower_array_data(dst_reg); if (lower_have_signal(src_reg)) - emit_map_signal(src_reg, dst_nets, count_reg, count_reg, conv_func); + emit_map_signal(src_reg, dst_nets, count_reg); else emit_map_const(src_reg, dst_nets, count_reg); } } +static void lower_convert_signal_field_cb(lower_unit_t *lu, tree_t field, + vcode_reg_t src_ptr, + vcode_reg_t dst_ptr, + vcode_reg_t conv_func, void *ctx) +{ + type_t ftype = tree_type(field); + void (*emit_fn)(vcode_reg_t, vcode_reg_t, vcode_reg_t) = ctx; + + if (type_is_homogeneous(ftype)) { + vcode_reg_t nets_reg = emit_load_indirect(src_ptr); + vcode_reg_t count_reg = lower_type_width(lu, ftype, nets_reg); + (*emit_fn)(conv_func, nets_reg, count_reg); + } + else + lower_for_each_field(lu, ftype, src_ptr, dst_ptr, conv_func, + lower_convert_signal_field_cb, ctx); +} + +static void lower_convert_signal(lower_unit_t *lu, vcode_reg_t src_reg, + type_t type, vcode_reg_t conv_func, + convert_emit_fn emit_fn) +{ + if (!type_is_homogeneous(type)) + lower_for_each_field(lu, type, src_reg, VCODE_INVALID_REG, conv_func, + lower_convert_signal_field_cb, emit_fn); + else if (type_is_array(type)) { + vcode_reg_t count_reg = lower_array_total_len(lu, type, src_reg); + vcode_reg_t nets_reg = lower_array_data(src_reg); + (*emit_fn)(conv_func, nets_reg, count_reg); + } + else { + vcode_reg_t count_reg = emit_const(vtype_offset(), 1); + vcode_reg_t nets_reg = lower_array_data(src_reg); + (*emit_fn)(conv_func, nets_reg, count_reg); + } +} + static void lower_non_static_actual(lower_unit_t *parent, tree_t port, tree_t target, type_t type, vcode_reg_t port_reg, tree_t wave) @@ -11738,8 +11705,15 @@ static void lower_port_map(lower_unit_t *lu, tree_t block, tree_t map, type_t src_type = mode == PORT_IN ? value_type : name_type; type_t dst_type = mode == PORT_IN ? name_type : value_type; - lower_map_signal(lu, src_reg, dst_reg, src_type, dst_type, - conv_func, map); + if (conv_func != VCODE_INVALID_REG) { + vcode_reg_t conv_reg = emit_port_conversion(conv_func); + lower_convert_signal(lu, dst_reg, dst_type, + conv_reg, emit_convert_out); + lower_convert_signal(lu, src_reg, src_type, + conv_reg, emit_convert_in); + } + else + lower_map_signal(lu, src_reg, dst_reg, src_type, dst_type, map); } else if (tree_kind(value) == T_WAVEFORM) { tree_t name = tree_subkind(map) == P_NAMED ? tree_name(map) : port; @@ -11747,8 +11721,7 @@ static void lower_port_map(lower_unit_t *lu, tree_t block, tree_t map, } else if (value_reg != VCODE_INVALID_REG) { type_t value_type = tree_type(value); - lower_map_signal(lu, value_reg, port_reg, value_type, name_type, - in_conv, map); + lower_map_signal(lu, value_reg, port_reg, value_type, name_type, map); } } @@ -12119,8 +12092,7 @@ static void lower_ports(lower_unit_t *lu, driver_set_t *ds, tree_t block) def_reg = lower_default_value(lu, type, VCODE_INVALID_REG); vcode_reg_t nets_reg = lower_port_ref(lu, port); - lower_map_signal(lu, def_reg, nets_reg, type, type, - VCODE_INVALID_REG, port); + lower_map_signal(lu, def_reg, nets_reg, type, type, port); } } diff --git a/src/rt/model.c b/src/rt/model.c index 85293c87..ec8633b7 100644 --- a/src/rt/model.c +++ b/src/rt/model.c @@ -640,13 +640,6 @@ static void cleanup_nexus(rt_model_t *m, rt_nexus_t *n) break; case SOURCE_PORT: - if (s->u.port.conv_func != NULL) { - assert(s->u.port.conv_func->refcnt > 0); - if (--(s->u.port.conv_func->refcnt) == 0) - free(s->u.port.conv_func); - } - break; - case SOURCE_FORCING: break; } @@ -1449,6 +1442,17 @@ static waveform_t *alloc_waveform(rt_model_t *m) } } +static void add_conversion_input(rt_conv_func_t *cf, rt_nexus_t *in) +{ + if (cf->ninputs == cf->maxinputs) { + cf->maxinputs = MAX(cf->maxinputs * 2, 4); + cf->inputs = xrealloc_array(cf->inputs, cf->maxinputs, + sizeof(rt_nexus_t *)); + } + + cf->inputs[cf->ninputs++] = in; +} + static void split_value(rt_nexus_t *nexus, rt_value_t *v_new, rt_value_t *v_old, int offset) { @@ -1494,10 +1498,8 @@ static void clone_source(rt_model_t *m, rt_nexus_t *nexus, { new->u.port.input = old->u.port.input; - if (old->u.port.conv_func != NULL) { + if (old->u.port.conv_func != NULL) new->u.port.conv_func = old->u.port.conv_func; - new->u.port.conv_func->refcnt++; - } else { if (old->u.port.input->width == offset) new->u.port.input = old->u.port.input->chain; // Cycle breaking @@ -1607,8 +1609,10 @@ static rt_nexus_t *clone_nexus(rt_model_t *m, rt_nexus_t *old, int offset) assert(old_o->tag != SOURCE_DRIVER); - if (old_o->u.port.conv_func != NULL) + if (old_o->u.port.conv_func != NULL) { new->outputs = old_o; + add_conversion_input(old_o->u.port.conv_func, new); + } else { rt_nexus_t *out_n; if (old_o->u.port.output->width == offset) @@ -1708,20 +1712,6 @@ static void setup_signal(rt_model_t *m, rt_signal_t *s, tree_t where, m->n_signals++; } -static void copy_sub_signals(rt_scope_t *scope, void *buf, value_fn_t fn) -{ - assert(scope->kind == SCOPE_SIGNAL); - - list_foreach(rt_signal_t *, s, scope->signals) { - rt_nexus_t *n = &(s->nexus); - for (unsigned i = 0; i < s->n_nexus; i++, n = n->chain) - memcpy(buf + s->offset + n->offset, (*fn)(n), n->size * n->width); - } - - list_foreach(rt_scope_t *, s, scope->children) - copy_sub_signals(s, buf, fn); -} - static void copy_sub_signal_sources(rt_scope_t *scope, void *buf, int stride) { assert(scope->kind == SCOPE_SIGNAL); @@ -1744,60 +1734,31 @@ static void copy_sub_signal_sources(rt_scope_t *scope, void *buf, int stride) copy_sub_signal_sources(s, buf, stride); } -static void *composite_signal(rt_signal_t *signal, size_t *psz, value_fn_t fn) +static void *call_conversion(rt_conv_func_t *cf, value_fn_t fn) { - assert(signal->parent->kind == SCOPE_SIGNAL); - - rt_scope_t *root = signal->parent; - while (root->parent->kind == SCOPE_SIGNAL) - root = root->parent; - - *psz = root->size; - - char *buf = xmalloc(root->size); - copy_sub_signals(root, buf, fn); - return buf; -} + rt_model_t *m = get_model(); -static void *call_conversion(rt_port_t *port, value_fn_t fn) -{ - rt_signal_t *i0 = port->input->signal; - rt_conv_func_t *cf = port->conv_func; + if (unlikely(cf->inbuf == NULL)) + cf->inbuf = static_alloc(m, cf->insz); - bool incopy = false; - void *indata; - size_t insz; - if (i0->parent->kind == SCOPE_SIGNAL) { - indata = composite_signal(i0, &insz, fn); - incopy = true; - } - else if (i0->n_nexus == 1) { - insz = i0->shared.size; - indata = (*fn)(&(i0->nexus)); - } - else { - insz = i0->shared.size; - indata = xmalloc(insz); - incopy = true; + if (unlikely(cf->outbuf == NULL)) + cf->outbuf = static_alloc(m, cf->outsz); - rt_nexus_t *n = &(i0->nexus); - for (unsigned i = 0; i < i0->n_nexus; i++, n = n->chain) - memcpy(indata + i0->offset + n->offset, (*fn)(n), n->size * n->width); + for (int i = 0; i < cf->ninputs; i++) { + rt_nexus_t *n = cf->inputs[i]; + memcpy(cf->inbuf + n->signal->offset + n->offset, + (*fn)(n), n->size * n->width); } - rt_model_t *m = get_model(); - TRACE("call conversion function %s insz=%zu outsz=%zu", - istr(jit_get_name(m->jit, cf->closure.handle)), insz, cf->bufsz); + istr(jit_get_name(m->jit, cf->closure.handle)), cf->insz, cf->outsz); jit_scalar_t context = { .pointer = cf->closure.context }; if (!jit_try_call_packed(m->jit, cf->closure.handle, context, - indata, insz, cf->buffer, cf->bufsz)) + cf->inbuf, cf->insz, cf->outbuf, cf->outsz)) m->force_stop = true; - if (incopy) free(indata); - - return cf->buffer + port->output->signal->offset; + return cf->outbuf; } static void *source_value(rt_nexus_t *nexus, rt_source_t *src) @@ -1813,7 +1774,8 @@ static void *source_value(rt_nexus_t *nexus, rt_source_t *src) if (likely(src->u.port.conv_func == NULL)) return driving_value(src->u.port.input); else - return call_conversion(&(src->u.port), driving_value); + return call_conversion(src->u.port.conv_func, driving_value) + + nexus->signal->offset + nexus->offset; case SOURCE_FORCING: assert(src->disconnected); @@ -1965,7 +1927,8 @@ static void *driving_value(rt_nexus_t *nexus) if (likely(s->u.port.conv_func == NULL)) return driving_value(s->u.port.input); else - return call_conversion(&(s->u.port), driving_value); + return call_conversion(s->u.port.conv_func, driving_value) + + nexus->signal->offset + nexus->offset; case SOURCE_FORCING: // An undriven signal that was previously forced @@ -2048,7 +2011,14 @@ static int nexus_rank(rt_nexus_t *nexus) if (nexus->n_sources > 0) { int rank = 0; for (rt_source_t *s = &(nexus->sources); s; s = s->chain_input) { - if (s->tag == SOURCE_PORT) + if (s->tag != SOURCE_PORT) + continue; + else if (s->u.port.conv_func != NULL) { + rt_conv_func_t *cf = s->u.port.conv_func; + for (int i = 0; i < cf->ninputs; i++) + rank = MAX(rank, nexus_rank(cf->inputs[i]) + 1); + } + else rank = MAX(rank, nexus_rank(s->u.port.input) + 1); } return rank; @@ -2751,7 +2721,16 @@ static void enqueue_effective(rt_model_t *m, rt_nexus_t *nexus) if (nexus->n_sources > 0) { for (rt_source_t *s = &(nexus->sources); s; s = s->chain_input) { - if (s->tag == SOURCE_PORT && (s->u.port.input->flags & NET_F_INOUT)) + if (s->tag != SOURCE_PORT) + continue; + else if (s->u.port.conv_func != NULL) { + rt_conv_func_t *cf = s->u.port.conv_func; + for (int i = 0; i < cf->ninputs; i++) { + if (cf->inputs[i]->flags & NET_F_INOUT) + enqueue_effective(m, cf->inputs[i]); + } + } + else if (s->u.port.input->flags & NET_F_INOUT) enqueue_effective(m, s->u.port.input); } } @@ -3447,9 +3426,18 @@ static bool nexus_active(rt_model_t *m, rt_nexus_t *nexus) if (nexus->n_sources > 0) { for (rt_source_t *s = &(nexus->sources); s; s = s->chain_input) { if (s->tag == SOURCE_PORT) { - RT_LOCK(s->u.port.input->signal->lock); - if (nexus_active(m, s->u.port.input)) - return true; + rt_conv_func_t *cf = s->u.port.conv_func; + if (cf == NULL) { + RT_LOCK(s->u.port.input->signal->lock); + if (nexus_active(m, s->u.port.input)) + return true; + } + else { + for (int i = 0; i < cf->ninputs; i++) { + if (nexus_active(m, cf->inputs[i])) + return true; + } + } } else if (s->tag == SOURCE_DRIVER && nexus->active_delta == m->iteration && s->u.driver.waveforms.when == m->now) @@ -3908,9 +3896,7 @@ int64_t x_last_active(sig_shared_t *ss, uint32_t offset, int32_t count) } void x_map_signal(sig_shared_t *src_ss, uint32_t src_offset, - sig_shared_t *dst_ss, uint32_t dst_offset, - uint32_t src_count, uint32_t dst_count, - ffi_closure_t *closure) + sig_shared_t *dst_ss, uint32_t dst_offset, uint32_t count) { rt_signal_t *src_s = container_of(src_ss, rt_signal_t, shared); RT_LOCK(src_s->lock); @@ -3918,45 +3904,25 @@ void x_map_signal(sig_shared_t *src_ss, uint32_t src_offset, rt_signal_t *dst_s = container_of(dst_ss, rt_signal_t, shared); RT_LOCK(dst_s->lock); - TRACE("map signal %s+%d to %s+%d count %d/%d%s", + TRACE("map signal %s+%d to %s+%d count %d", istr(tree_ident(src_s->where)), src_offset, - istr(tree_ident(dst_s->where)), dst_offset, - src_count, dst_count, closure ? " converted" : ""); + istr(tree_ident(dst_s->where)), dst_offset, count); - assert(src_count == dst_count || closure != NULL); assert(src_s != dst_s); - rt_conv_func_t *conv_func = NULL; - if (closure != NULL) { - size_t bufsz = dst_s->shared.size; - if (dst_s->parent->kind == SCOPE_SIGNAL) { - rt_scope_t *root = dst_s->parent; - while (root->parent->kind == SCOPE_SIGNAL) - root = root->parent; - bufsz = root->size; - } - - TRACE("need %zu bytes for conversion function buffer", bufsz); - - conv_func = xmalloc_flex(sizeof(rt_conv_func_t), 1, bufsz); - conv_func->closure = *closure; - conv_func->refcnt = 0; - conv_func->bufsz = bufsz; - } - rt_model_t *m = get_model(); - rt_nexus_t *src_n = split_nexus(m, src_s, src_offset, src_count); - rt_nexus_t *dst_n = split_nexus(m, dst_s, dst_offset, dst_count); + rt_nexus_t *src_n = split_nexus(m, src_s, src_offset, count); + rt_nexus_t *dst_n = split_nexus(m, dst_s, dst_offset, count); - while (src_count > 0 && dst_count > 0) { - if (src_n->width > dst_n->width && closure == NULL) + while (count > 0) { + if (src_n->width > dst_n->width) clone_nexus(m, src_n, dst_n->width); - else if (src_n->width < dst_n->width && closure == NULL) + else if (src_n->width < dst_n->width) clone_nexus(m, dst_n, src_n->width); - assert(src_n->width == dst_n->width || closure != NULL); - assert(src_n->size == dst_n->size || closure != NULL); + assert(src_n->width == dst_n->width); + assert(src_n->size == dst_n->size); // Effective value updates must propagate through ports src_n->flags |= (dst_n->flags & NET_F_EFFECTIVE); @@ -3965,20 +3931,11 @@ void x_map_signal(sig_shared_t *src_ss, uint32_t src_offset, rt_source_t *port = add_source(m, dst_n, SOURCE_PORT); port->u.port.input = src_n; - if (conv_func != NULL) { - port->u.port.conv_func = conv_func; - conv_func->refcnt++; - src_n->flags |= NET_F_EFFECTIVE; - dst_n->flags |= NET_F_EFFECTIVE; - } - port->chain_output = src_n->outputs; src_n->outputs = port; - src_count -= src_n->width; - dst_count -= dst_n->width; - assert(src_count >= 0); - assert(dst_count >= 0); + count -= src_n->width; + assert(count >= 0); src_n = src_n->chain; dst_n = dst_n->chain; @@ -4300,3 +4257,80 @@ void x_add_trigger(void *ptr) obj->trigger = ptr; } + +void *x_port_conversion(const ffi_closure_t *closure) +{ + rt_model_t *m = get_model(); + + TRACE("port conversion %s context %p", + istr(jit_get_name(m->jit, closure->handle)), closure->context); + + rt_conv_func_t *cf = static_alloc(m, sizeof(rt_conv_func_t)); + cf->closure = *closure; + cf->ninputs = 0; + cf->maxinputs = 0; + cf->outputs = NULL; + cf->inputs = NULL; + cf->outsz = 0; + cf->insz = 0; + + return cf; +} + +void x_convert_in(void *ptr, sig_shared_t *ss, uint32_t offset, int32_t count) +{ + rt_signal_t *s = container_of(ss, rt_signal_t, shared); + + TRACE("convert in %p %s+%d count=%d", ptr, istr(tree_ident(s->where)), + offset, count); + + rt_conv_func_t *cf = ptr; + rt_model_t *m = get_model(); + + rt_nexus_t *n = split_nexus(m, s, offset, count); + for (; count > 0; n = n->chain) { + count -= n->width; + assert(count >= 0); + + n->flags |= NET_F_EFFECTIVE; + + add_conversion_input(cf, n); + + const size_t reqd = n->signal->offset + n->offset + n->size * n->width; + cf->insz = MAX(cf->insz, reqd); + + rt_source_t **p = &(n->outputs); + for (; *p != NULL && *p != cf->outputs; p = &((*p)->chain_output)); + *p = cf->outputs; + } +} + +void x_convert_out(void *ptr, sig_shared_t *ss, uint32_t offset, int32_t count) +{ + rt_signal_t *s = container_of(ss, rt_signal_t, shared); + + TRACE("convert out %p %s+%d count=%d", ptr, istr(tree_ident(s->where)), + offset, count); + + rt_conv_func_t *cf = ptr; + rt_model_t *m = get_model(); + + assert(cf->ninputs == 0); // Add outputs first + + rt_nexus_t *n = split_nexus(m, s, offset, count); + for (; count > 0; n = n->chain) { + count -= n->width; + assert(count >= 0); + + n->flags |= NET_F_EFFECTIVE; + + rt_source_t *src = add_source(m, n, SOURCE_PORT); + src->u.port.conv_func = cf; + + const size_t reqd = n->signal->offset + n->offset + n->size * n->width; + cf->outsz = MAX(cf->outsz, reqd); + + src->chain_output = cf->outputs; + cf->outputs = src; + } +} diff --git a/src/rt/structs.h b/src/rt/structs.h index d7a02834..11027339 100644 --- a/src/rt/structs.h +++ b/src/rt/structs.h @@ -105,9 +105,14 @@ typedef struct { typedef struct { ffi_closure_t closure; - unsigned refcnt; - size_t bufsz; - uint8_t buffer[0]; + unsigned ninputs; + unsigned maxinputs; + rt_nexus_t **inputs; + rt_source_t *outputs; + size_t insz; + size_t outsz; + void *outbuf; + void *inbuf; } rt_conv_func_t; typedef struct { diff --git a/src/vcode.c b/src/vcode.c index 9e733acc..c63d2642 100644 --- a/src/vcode.c +++ b/src/vcode.c @@ -966,6 +966,7 @@ const char *vcode_op_string(vcode_op_t op) "unreachable", "package init", "trap neg", "process init", "clear event", "trap exp", "implicit event", "enter state", "reflect value", "reflect subtype", "function trigger", "add trigger", "transfer signal", + "port conversion", "convert in", "convert out", }; if ((unsigned)op >= ARRAY_LEN(strs)) return "???"; @@ -1104,6 +1105,10 @@ static int vcode_dump_one_type(vcode_type_t type) case VCODE_TYPE_TRIGGER: col += printf("T<>"); break; + + case VCODE_TYPE_CONVERSION: + col += printf("X<>"); + break; } return col; @@ -1342,20 +1347,8 @@ void vcode_dump_with_mark(int mark_op, vcode_dump_fn_t callback, void *arg) vcode_dump_reg(op->args.items[0]); printf(" to "); vcode_dump_reg(op->args.items[1]); - if (op->args.items[2] == op->args.items[3]) { - printf(" count "); - vcode_dump_reg(op->args.items[2]); - } - else { - printf(" src count "); - vcode_dump_reg(op->args.items[2]); - printf(" dst count "); - vcode_dump_reg(op->args.items[3]); - } - if (op->args.count > 4) { - printf(" conv "); - vcode_dump_reg(op->args.items[4]); - } + printf(" count "); + vcode_dump_reg(op->args.items[2]); } break; @@ -2287,6 +2280,27 @@ void vcode_dump_with_mark(int mark_op, vcode_dump_fn_t callback, void *arg) vcode_dump_reg(op->args.items[0]); } break; + + case VCODE_OP_PORT_CONVERSION: + { + col += vcode_dump_reg(op->result); + col += color_printf(" := %s ", vcode_op_string(op->kind)); + col += vcode_dump_reg(op->args.items[0]); + vcode_dump_result_type(col, op); + } + break; + + case VCODE_OP_CONVERT_IN: + case VCODE_OP_CONVERT_OUT: + { + color_printf("%s ", vcode_op_string(op->kind)); + vcode_dump_reg(op->args.items[0]); + printf(" signal "); + vcode_dump_reg(op->args.items[1]); + printf(" count "); + vcode_dump_reg(op->args.items[2]); + } + break; } if (j == mark_op && i == old_block) @@ -2338,6 +2352,7 @@ bool vtype_eq(vcode_type_t a, vcode_type_t b) case VCODE_TYPE_OPAQUE: case VCODE_TYPE_DEBUG_LOCUS: case VCODE_TYPE_TRIGGER: + case VCODE_TYPE_CONVERSION: return true; case VCODE_TYPE_RESOLUTION: case VCODE_TYPE_CLOSURE: @@ -2626,6 +2641,16 @@ vcode_type_t vtype_trigger(void) return vtype_new(n); } +vcode_type_t vtype_conversion(void) +{ + assert(active_unit != NULL); + + vtype_t *n = vtype_array_alloc(&(active_unit->types)); + n->kind = VCODE_TYPE_CONVERSION; + + return vtype_new(n); +} + vcode_type_t vtype_real(double low, double high) { assert(active_unit != NULL); @@ -4745,28 +4770,19 @@ vcode_reg_t emit_implicit_signal(vcode_type_t type, vcode_reg_t count, return (op->result = vcode_add_reg(vtype_signal(type))); } -void emit_map_signal(vcode_reg_t src, vcode_reg_t dst, vcode_reg_t src_count, - vcode_reg_t dst_count, vcode_reg_t conv) +void emit_map_signal(vcode_reg_t src, vcode_reg_t dst, vcode_reg_t count) { op_t *op = vcode_add_op(VCODE_OP_MAP_SIGNAL); vcode_add_arg(op, src); vcode_add_arg(op, dst); - vcode_add_arg(op, src_count); - vcode_add_arg(op, dst_count); - if (conv != VCODE_INVALID_REG) - vcode_add_arg(op, conv); + vcode_add_arg(op, count); VCODE_ASSERT(vcode_reg_kind(src) == VCODE_TYPE_SIGNAL, "src argument to map signal is not a signal"); VCODE_ASSERT(vcode_reg_kind(dst) == VCODE_TYPE_SIGNAL, "dst argument to map signal is not a signal"); - VCODE_ASSERT(vcode_reg_kind(src_count) == VCODE_TYPE_OFFSET, - "src count argument type to map signal is not offset"); - VCODE_ASSERT(vcode_reg_kind(dst_count) == VCODE_TYPE_OFFSET, - "dst count argument type to map signal is not offset"); - VCODE_ASSERT(conv == VCODE_INVALID_REG - || vcode_reg_kind(conv) == VCODE_TYPE_CLOSURE, - "conv argument type to map signal is not closure"); + VCODE_ASSERT(vcode_reg_kind(count) == VCODE_TYPE_OFFSET, + "count argument type to map signal is not offset"); } void emit_map_const(vcode_reg_t src, vcode_reg_t dst, vcode_reg_t count) @@ -5868,6 +5884,47 @@ void emit_add_trigger(vcode_reg_t trigger) "add trigger argument must be trigger"); } +vcode_reg_t emit_port_conversion(vcode_reg_t closure) +{ + op_t *op = vcode_add_op(VCODE_OP_PORT_CONVERSION); + vcode_add_arg(op, closure); + + VCODE_ASSERT(vcode_reg_kind(closure) == VCODE_TYPE_CLOSURE, + "port conversion argument must be a closure"); + + return (op->result = vcode_add_reg(vtype_conversion())); +} + +void emit_convert_in(vcode_reg_t conv, vcode_reg_t nets, vcode_reg_t count) +{ + op_t *op = vcode_add_op(VCODE_OP_CONVERT_IN); + vcode_add_arg(op, conv); + vcode_add_arg(op, nets); + vcode_add_arg(op, count); + + VCODE_ASSERT(vcode_reg_kind(conv) == VCODE_TYPE_CONVERSION, + "conv argument to convert must be a port conversion"); + VCODE_ASSERT(vcode_reg_kind(nets) == VCODE_TYPE_SIGNAL, + "nets argument to convert must be a signal"); + VCODE_ASSERT(vcode_reg_kind(count) == VCODE_TYPE_OFFSET, + "count argument to convert must be offset"); +} + +void emit_convert_out(vcode_reg_t conv, vcode_reg_t nets, vcode_reg_t count) +{ + op_t *op = vcode_add_op(VCODE_OP_CONVERT_OUT); + vcode_add_arg(op, conv); + vcode_add_arg(op, nets); + vcode_add_arg(op, count); + + VCODE_ASSERT(vcode_reg_kind(conv) == VCODE_TYPE_CONVERSION, + "conv argument to convert must be a port conversion"); + VCODE_ASSERT(vcode_reg_kind(nets) == VCODE_TYPE_SIGNAL, + "nets argument to convert must be a signal"); + VCODE_ASSERT(vcode_reg_kind(count) == VCODE_TYPE_OFFSET, + "count argument to convert must be offset"); +} + void vcode_walk_dependencies(vcode_unit_t vu, vcode_dep_fn_t fn, void *ctx) { vcode_select_unit(vu); diff --git a/src/vcode.h b/src/vcode.h index 712a81b5..c92d40cf 100644 --- a/src/vcode.h +++ b/src/vcode.h @@ -162,6 +162,9 @@ typedef enum { VCODE_OP_FUNCTION_TRIGGER, VCODE_OP_ADD_TRIGGER, VCODE_OP_TRANSFER_SIGNAL, + VCODE_OP_PORT_CONVERSION, + VCODE_OP_CONVERT_IN, + VCODE_OP_CONVERT_OUT, } vcode_op_t; typedef enum { @@ -181,6 +184,7 @@ typedef enum { VCODE_TYPE_CONTEXT, VCODE_TYPE_DEBUG_LOCUS, VCODE_TYPE_TRIGGER, + VCODE_TYPE_CONVERSION, } vtype_kind_t; typedef enum { @@ -492,8 +496,7 @@ vcode_reg_t emit_range_length(vcode_reg_t left, vcode_reg_t right, vcode_reg_t emit_link_var(vcode_reg_t context, ident_t name, vcode_type_t type); vcode_reg_t emit_link_package(ident_t name); vcode_reg_t emit_link_instance(ident_t name, vcode_reg_t locus); -void emit_map_signal(vcode_reg_t src, vcode_reg_t dst, vcode_reg_t src_count, - vcode_reg_t dst_count, vcode_reg_t conv); +void emit_map_signal(vcode_reg_t src, vcode_reg_t dst, vcode_reg_t count); void emit_map_const(vcode_reg_t src, vcode_reg_t dst, vcode_reg_t count); void emit_drive_signal(vcode_reg_t target, vcode_reg_t count); void emit_transfer_signal(vcode_reg_t target, vcode_reg_t source, @@ -522,5 +525,8 @@ vcode_reg_t emit_reflect_subtype(ident_t ptype, vcode_reg_t context, vcode_reg_t locus, vcode_reg_t bounds); vcode_reg_t emit_function_trigger(vcode_reg_t closure); void emit_add_trigger(vcode_reg_t trigger); +vcode_reg_t emit_port_conversion(vcode_reg_t closure); +void emit_convert_in(vcode_reg_t conv, vcode_reg_t nets, vcode_reg_t count); +void emit_convert_out(vcode_reg_t conv, vcode_reg_t nets, vcode_reg_t count); #endif // _VCODE_H diff --git a/src/vlog/vlog-lower.c b/src/vlog/vlog-lower.c index c661f550..37075cbb 100644 --- a/src/vlog/vlog-lower.c +++ b/src/vlog/vlog-lower.c @@ -281,10 +281,16 @@ static void vlog_lower_port_map(lower_unit_t *lu, vlog_node_t root, tree_t wrap) vcode_reg_t count_reg = emit_const(vtype_offset(), 1); - if (vlog_subkind(port) == V_PORT_INPUT) - emit_map_signal(vhdl_reg, vlog_reg, count_reg, count_reg, to_verilog); - else - emit_map_signal(vlog_reg, vhdl_reg, count_reg, count_reg, to_vhdl); + if (vlog_subkind(port) == V_PORT_INPUT) { + vcode_reg_t conv_reg = emit_port_conversion(to_verilog); + emit_convert_out(conv_reg, vlog_reg, count_reg); + emit_convert_in(conv_reg, vhdl_reg, count_reg); + } + else { + vcode_reg_t conv_reg = emit_port_conversion(to_vhdl); + emit_convert_out(conv_reg, vhdl_reg, count_reg); + emit_convert_in(conv_reg, vlog_reg, count_reg); + } } } diff --git a/test/regress/bounds43.vhd b/test/regress/bounds43.vhd new file mode 100644 index 00000000..2864482b --- /dev/null +++ b/test/regress/bounds43.vhd @@ -0,0 +1,14 @@ +entity bounds43 is +end entity; + +architecture test of bounds43 is + signal s : integer := 0; +begin + + b1: block is + port ( p : inout real ); + port map ( integer(p) => real(s) ); -- Error + begin + end block; + +end architecture; diff --git a/test/regress/conv11.vhd b/test/regress/conv11.vhd new file mode 100644 index 00000000..192eb293 --- /dev/null +++ b/test/regress/conv11.vhd @@ -0,0 +1,53 @@ +entity conv11 is +end entity; + +architecture test of conv11 is + type t_int is record + x, y : integer; + end record; + + type t_real is record + x, y : real; + end record; + + function to_int (r : t_real) return t_int is + begin + report "(" & real'image(r.x) & ", " & real'image(r.y) & ")"; + return (integer(r.x), integer(r.y)); + end function; + + signal si : t_int := (0, 0); + signal sr : t_real := (0.0, 0.0); +begin + + b: block is + port ( i : in t_int; + o : out t_real := (0.0, 0.0) ); + port map ( i => to_int(sr), + to_int(o) => si ); + begin + check_inner: process is + begin + assert i = (0, 0); + wait for 0 ns; + assert i = (1, 2); + o <= (5.6, 7.2); + wait; + end process; + end block; + + check_outer: process is + begin + assert si = (0, 0); + assert sr = (0.0, 0.0); + + sr <= (1.0, 2.0); + wait for 0 ns; + assert si = (0, 0); + wait for 0 ns; + assert si = (6, 7); + + wait; + end process; + +end architecture; diff --git a/test/regress/conv12.vhd b/test/regress/conv12.vhd new file mode 100644 index 00000000..4a1d612f --- /dev/null +++ b/test/regress/conv12.vhd @@ -0,0 +1,50 @@ +entity conv12 is +end entity; + +architecture test of conv12 is + function half (x : integer) return integer is + begin + return x / 2; + end function; + + function double (x : integer) return integer is + begin + return x * 2; + end function; + + signal s : integer := 0; +begin + + b1: block is + port ( p : in integer ); + port map ( p => half(s) ); + begin + check: process is + begin + assert p = 0; + wait for 1 ns; + assert p = 0; + wait for 2 ns; + assert p = 5; + wait; + end process; + end block; + + b2: block is + port ( p : in integer ); + port map ( p => double(s) ); + begin + check: process is + begin + assert p = 0; + wait for 1 ns; + assert p = 2; + wait for 2 ns; + assert p = 20; + wait; + end process; + end block; + + s <= 1 after 1 ns, 10 after 2 ns; + +end architecture; diff --git a/test/regress/gold/bounds43.txt b/test/regress/gold/bounds43.txt new file mode 100644 index 00000000..0a035161 --- /dev/null +++ b/test/regress/gold/bounds43.txt @@ -0,0 +1 @@ +(init): value -9223372036854775808 outside of INTEGER range -2147483648 to 2147483647 diff --git a/test/regress/issue843.vhd b/test/regress/issue843.vhd new file mode 100644 index 00000000..de13c432 --- /dev/null +++ b/test/regress/issue843.vhd @@ -0,0 +1,147 @@ +library ieee; +use ieee.math_real.all; +use ieee.float_pkg.all; + +package fft_pkg is + subtype num_type is float32; + type data_type is record + re, im : num_type; + end record; + type data_vector is array (natural range <>) of data_type; + function "+" (a, b : data_type) return data_type; + function "*" (a, b : data_type) return data_type; + function even_elem(x : data_vector) return data_vector; + function odd_elem(x : data_vector) return data_vector; + function weight(n : positive) return data_vector; +end package fft_pkg; + +package body fft_pkg is + function "+" (a, b : data_type) return data_type is + begin + return data_type'(a.re + b.re, a.im + b.im); + end function; + + function "*" (a, b : data_type) return data_type is + begin + return data_type'(a.re*b.re - a.im*b.im, a.re*b.im + a.im*b.re); + end function; + + function even_elem(x : data_vector) return data_vector is + variable res : data_vector (0 to x'length/2-1); + begin + for i in res'range loop + res(i) := x(2*i); + end loop; + return res; + end function; + + function odd_elem(x : data_vector) return data_vector is + variable res : data_vector (0 to x'length/2-1); + begin + for i in res'range loop + res(i) := x(2*i+1); + end loop; + return res; + end function; + + function weight(n : positive) return data_vector is + variable w : data_vector (0 to n-1); + variable omega : real; + begin + omega := math_2_pi/real(n); + for k in w'range loop + w(k) := data_type'(to_float(cos(omega*real(k))), to_float(-sin(omega*real(k)))); + end loop; + return w; + end function; +end package body fft_pkg; + +library ieee; +use ieee.float_pkg.all; +use work.fft_pkg.all; + +entity fft is + generic ( + POW : natural; + DIM : positive := 2**POW; + w: data_vector (0 to DIM-1) := weight(DIM)); + port ( + x : in data_vector (0 to DIM-1); + y : out data_vector (0 to DIM-1)); +end entity fft; + +architecture recursive of fft is +begin + stage_n : + if POW > 0 generate + subtype half_array is data_vector (0 to DIM/2-1); + signal even, odd : half_array; + alias w_b : half_array is w(0 to DIM/2-1); + alias w_t : half_array is w(DIM/2 to DIM-1); + begin + even_fft : + entity fft + generic map ( + POW => POW-1, w => even_elem(w)) + port map ( + x => even_elem(x), y => even); + odd_fft : + entity fft + generic map ( + POW => POW-1, w => even_elem(w)) + port map ( + x => odd_elem(x), y => odd); + butterfly : + process (all) is + begin + for i in half_array'range loop + y(i) <= even(i) + odd(i) * w_b(i); + y(i+DIM/2) <= even(i) + odd(i) * w_t(i); + end loop; + end process; + end generate stage_n; + stage_0 : + if POW = 0 generate + y <= x; + end generate stage_0; +end architecture recursive; + +library ieee; +use ieee.math_real.all; +use ieee.math_complex.all; +use ieee.fixed_float_types.all; +use ieee.float_pkg.all; +use work.fft_pkg.all; + +entity issue843 is +end entity issue843; + +architecture test of issue843 is + constant POW : natural := 2; + constant DIM : positive := 2**POW; + signal x, y : data_vector (0 to DIM-1) := (others => data_type'(to_float(0.0), to_float(0.0))); + type complex_vector is array (0 to DIM-1) of complex; + constant stimul : complex_vector := ((0.0, 0.0), ( 1.0, 1.0), ( 2.0, 2.0), (3.0, 3.0)); + constant result : complex_vector := ((6.0, 6.0), (-4.0, 0.0), (-2.0, -2.0), (0.0, -4.0)); + constant abstol : real := 1.0e-6; +begin + fft_inst : + entity work.fft(recursive) + generic map (POW => POW) + port map (x => x, y => y); + test_proc : + process is + alias s is to_string [integer return string]; + alias s is to_string [real return string]; + begin + for i in x'range loop + x(i) <= data_type'(to_float(stimul(i).re), to_float(stimul(i).im)); + end loop; + wait for 1 ps; + for i in y'range loop + assert abs(to_real(y(i).re) - result(i).re) < abstol and abs(to_real(y(i).im) - result(i).im) < abstol + report "Inconsistent result detected: y(" & s(i) & ") = (" & s(to_real(y(i).re)) & "," & s(to_real(y(i).im)) & ")" severity failure; + end loop; + wait; + end process; +end architecture test; diff --git a/test/regress/testlist.txt b/test/regress/testlist.txt index 6a21c3cb..8f297c65 100644 --- a/test/regress/testlist.txt +++ b/test/regress/testlist.txt @@ -927,3 +927,7 @@ issue829 normal issue839 normal issue840 wave issue842 normal,2008 +conv11 normal +conv12 normal +issue843 normal,2008 +bounds43 fail,gold diff --git a/test/test_lower.c b/test/test_lower.c index be9ab78a..ab2a5e40 100644 --- a/test/test_lower.c +++ b/test/test_lower.c @@ -5002,8 +5002,8 @@ START_TEST(test_issue613) { VCODE_OP_STORE, .name = "Q" }, { VCODE_OP_ARRAY_REF }, { VCODE_OP_MAP_CONST }, - { VCODE_OP_RANGE_LENGTH }, { VCODE_OP_DEBUG_LOCUS }, + { VCODE_OP_RANGE_LENGTH }, { VCODE_OP_LENGTH_CHECK }, // Unnecessary { VCODE_OP_UNWRAP }, { VCODE_OP_MAP_CONST }, @@ -5801,9 +5801,9 @@ START_TEST(test_subtype2) { VCODE_OP_UARRAY_DIR }, { VCODE_OP_WRAP }, { VCODE_OP_STORE, .name = "P" }, - { VCODE_OP_CONST, .value = 5 }, - { VCODE_OP_RANGE_LENGTH }, { VCODE_OP_DEBUG_LOCUS }, + { VCODE_OP_RANGE_LENGTH }, + { VCODE_OP_CONST, .value = 5 }, { VCODE_OP_LENGTH_CHECK }, { VCODE_OP_MAP_CONST }, { VCODE_OP_RETURN }, -- 2.39.2