diff --git a/tgt-vvp/vvp_process.c b/tgt-vvp/vvp_process.c index c1007820a..e6233a363 100644 --- a/tgt-vvp/vvp_process.c +++ b/tgt-vvp/vvp_process.c @@ -224,7 +224,8 @@ static void set_to_lvariable(ivl_lval_t lval, static void assign_to_array_word(ivl_signal_t lsig, ivl_expr_t word_ix, unsigned bit, unsigned delay, ivl_expr_t dexp, - ivl_expr_t part_off_ex, unsigned width) + ivl_expr_t part_off_ex, unsigned width, + unsigned nevents) { unsigned skip_assign = transient_id++; @@ -236,57 +237,45 @@ static void assign_to_array_word(ivl_signal_t lsig, ivl_expr_t word_ix, part_off_ex = 0; } - if (dexp == 0) { - /* Constant delay... */ - if (number_is_immediate(word_ix, 64, 0)) { - fprintf(vvp_out, " %%ix/load 3, %lu; address\n", - get_number_immediate(word_ix)); - } else { - /* Calculate array word index into index register 3 */ - draw_eval_expr_into_integer(word_ix, 3); - /* Skip assignment if word expression is not defined. */ - fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_assign); - } - /* Store expression width into index word 0 */ - fprintf(vvp_out, " %%ix/load 0, %u; word width\n", width); - if (part_off_ex) { - draw_eval_expr_into_integer(part_off_ex, 1); - /* If the index expression has XZ bits, skip the assign. */ - fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_assign); - } else { - /* Store word part select base into index 1 */ - fprintf(vvp_out, " %%ix/load 1, %u; part base\n", part_off); - } - fprintf(vvp_out, " %%assign/av v%p, %u, %u;\n", lsig, - delay, bit); + /* This code is common to all the different types of array delays. */ + if (number_is_immediate(word_ix, 64, 0)) { + fprintf(vvp_out, " %%ix/load 3, %lu; address\n", + get_number_immediate(word_ix)); } else { + /* Calculate array word index into index register 3 */ + draw_eval_expr_into_integer(word_ix, 3); + /* Skip assignment if word expression is not defined. */ + fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_assign); + } + /* Store expression width into index word 0 */ + fprintf(vvp_out, " %%ix/load 0, %u; word width\n", width); + if (part_off_ex) { + draw_eval_expr_into_integer(part_off_ex, 1); + /* If the index expression has XZ bits, skip the assign. */ + fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_assign); + } else { + /* Store word part select into index 1 */ + fprintf(vvp_out, " %%ix/load 1, %u; part off\n", part_off); + } + + if (dexp != 0) { /* Calculated delay... */ int delay_index = allocate_word(); draw_eval_expr_into_integer(dexp, delay_index); - if (number_is_immediate(word_ix, 64, 0)) { - fprintf(vvp_out, " %%ix/load 3, %lu; address\n", - get_number_immediate(word_ix)); - } else { - /* Calculate array word index into index register 3 */ - draw_eval_expr_into_integer(word_ix, 3); - /* Skip assignment if word expression is not defined. */ - fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_assign); - } - /* Store expression width into index word 0 */ - fprintf(vvp_out, " %%ix/load 0, %u; word width\n", width); - if (part_off_ex) { - draw_eval_expr_into_integer(part_off_ex, 1); - /* If the index expression has XZ bits, skip the assign. */ - fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_assign); - } else { - /* Store word part select into index 1 */ - fprintf(vvp_out, " %%ix/load 1, %u; part off\n", part_off); - } fprintf(vvp_out, " %%assign/av/d v%p, %d, %u;\n", lsig, - delay_index, bit); + delay_index, bit); + clr_word(delay_index); + } else if (nevents != 0) { + /* Event control delay... */ + fprintf(vvp_out, " %%assign/av/e v%p, %u;\n", lsig, bit); + } else { + /* Constant delay... */ + fprintf(vvp_out, " %%assign/av v%p, %u, %u;\n", lsig, + delay, bit); } fprintf(vvp_out, "t_%u ;\n", skip_assign); + if (nevents != 0) fprintf(vvp_out, " %%evctl/c;\n"); clear_expression_lookaside(); } @@ -303,16 +292,9 @@ static void assign_to_lvector(ivl_lval_t lval, unsigned bit, const unsigned long use_word = 0; if (ivl_signal_dimensions(sig) > 0) { - - if (nevents) { - fprintf(stderr, "vvp-tgt sorry: non-blocking event " - "controls are not supported on arrays!\n"); - exit(1); - } - assert(nevents == 0); - assert(word_ix); - assign_to_array_word(sig, word_ix, bit, delay, dexp, part_off_ex, width); + assign_to_array_word(sig, word_ix, bit, delay, dexp, part_off_ex, + width, nevents); return; } diff --git a/vvp/array.cc b/vvp/array.cc index 8ff092ade..eb3454182 100644 --- a/vvp/array.cc +++ b/vvp/array.cc @@ -124,6 +124,13 @@ struct __vpiArrayVthrA { } }; +/* Get the array word size. This has only been checked for reg arrays. */ +unsigned get_array_word_size(vvp_array_t array) +{ + assert(array->vals); + return array->vals_width; +} + /* * The vpiArrayWord is magic. It is used as the handle to return when * vpi code tries to index or scan an array of variable words. The diff --git a/vvp/array.h b/vvp/array.h index 1407fee76..7bac81088 100644 --- a/vvp/array.h +++ b/vvp/array.h @@ -29,6 +29,7 @@ typedef struct __vpiArray* vvp_array_t; * table of all the arrays in the design. */ extern vvp_array_t array_find(const char*label); +extern unsigned get_array_word_size(vvp_array_t array); extern vpiHandle array_index_iterate(int code, vpiHandle ref); extern void array_word_change(vvp_array_t array, unsigned long addr); diff --git a/vvp/codes.h b/vvp/codes.h index b524b5aad..28e600c2c 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -41,6 +41,7 @@ extern bool of_ANDI(vthread_t thr, vvp_code_t code); extern bool of_ANDR(vthread_t thr, vvp_code_t code); extern bool of_ASSIGN_AV(vthread_t thr, vvp_code_t code); extern bool of_ASSIGN_AVD(vthread_t thr, vvp_code_t code); +extern bool of_ASSIGN_AVE(vthread_t thr, vvp_code_t code); extern bool of_ASSIGN_D(vthread_t thr, vvp_code_t code); extern bool of_ASSIGN_MV(vthread_t thr, vvp_code_t code); extern bool of_ASSIGN_V0(vthread_t thr, vvp_code_t code); diff --git a/vvp/compile.cc b/vvp/compile.cc index 6e5e7e6c1..64a06fc57 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -88,6 +88,7 @@ const static struct opcode_table_s opcode_table[] = { { "%andi", of_ANDI, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, { "%assign/av",of_ASSIGN_AV,3,{OA_ARR_PTR,OA_BIT1, OA_BIT2} }, { "%assign/av/d",of_ASSIGN_AVD,3,{OA_ARR_PTR,OA_BIT1, OA_BIT2} }, + { "%assign/av/e",of_ASSIGN_AVE,2,{OA_ARR_PTR,OA_BIT1, OA_NONE} }, { "%assign/v0",of_ASSIGN_V0,3,{OA_FUNC_PTR,OA_BIT1, OA_BIT2} }, { "%assign/v0/d",of_ASSIGN_V0D,3,{OA_FUNC_PTR,OA_BIT1, OA_BIT2} }, { "%assign/v0/e",of_ASSIGN_V0E,2,{OA_FUNC_PTR,OA_BIT1, OA_NONE} }, diff --git a/vvp/event.cc b/vvp/event.cc index 0c44b68e7..0f80a6efa 100644 --- a/vvp/event.cc +++ b/vvp/event.cc @@ -130,6 +130,32 @@ void schedule_evctl(vvp_net_ptr_t ptr, const vvp_vector4_t&value, ep->last = &((*(ep->last))->next); } +evctl_array::evctl_array(vvp_array_t memory, unsigned index, + const vvp_vector4_t&value, unsigned off, + unsigned long ecount) +:evctl(ecount), value_(value) +{ + mem_ = memory; + idx_ = index; + off_ = off; +} + +void evctl_array::run_run() +{ + array_set_word(mem_, idx_, off_, value_); +} + +void schedule_evctl(vvp_array_t memory, unsigned index, + const vvp_vector4_t&value, unsigned offset, + vvp_net_t*event, unsigned long ecount) +{ + // Get the functor we are going to wait on. + waitable_hooks_s*ep = dynamic_cast (event->fun); + assert(ep); + // Now add this call to the end of the event list. + *(ep->last) = new evctl_array(memory, index, value, offset, ecount); + ep->last = &((*(ep->last))->next); +} inline vvp_fun_edge::edge_t VVP_EDGE(vvp_bit4_t from, vvp_bit4_t to) { diff --git a/vvp/event.h b/vvp/event.h index 49af1f31e..26b062dfb 100644 --- a/vvp/event.h +++ b/vvp/event.h @@ -21,6 +21,7 @@ # include "vvp_net.h" # include "pointers.h" +# include "array.h" class evctl { @@ -31,7 +32,7 @@ class evctl { virtual ~evctl() {} evctl*next; - protected: + private: unsigned long ecount_; }; @@ -63,6 +64,22 @@ class evctl_vector : public evctl { unsigned wid_; }; +class evctl_array : public evctl { + + public: + explicit evctl_array(vvp_array_t memory, unsigned index, + const vvp_vector4_t&value, unsigned off, + unsigned long ecount); + virtual ~evctl_array() {} + virtual void run_run(); + + private: + vvp_array_t mem_; + unsigned idx_; + vvp_vector4_t value_; + unsigned off_; +}; + extern void schedule_evctl(struct __vpiHandle*handle, double value, vvp_net_t*event, unsigned long ecount); @@ -70,6 +87,10 @@ extern void schedule_evctl(vvp_net_ptr_t ptr, const vvp_vector4_t&value, unsigned offset, unsigned wid, vvp_net_t*event, unsigned long ecount); +extern void schedule_evctl(vvp_array_t memory, unsigned index, + const vvp_vector4_t&value, unsigned offset, + vvp_net_t*event, unsigned long ecount); + /* * Event / edge detection functors */ diff --git a/vvp/vthread.cc b/vvp/vthread.cc index b67c2fb97..9b2e56550 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -658,14 +658,28 @@ bool of_ADDI(vthread_t thr, vvp_code_t cp) bool of_ASSIGN_AV(vthread_t thr, vvp_code_t cp) { unsigned wid = thr->words[0].w_int; - unsigned off = thr->words[1].w_int; + long off = thr->words[1].w_int; unsigned adr = thr->words[3].w_int; - - assert(wid > 0); - unsigned delay = cp->bit_idx[0]; unsigned bit = cp->bit_idx[1]; + long vwidth = get_array_word_size(cp->array); + // We fell off the MSB end. + if (off >= vwidth) return true; + // Trim the bits after the MSB + if (off + (long)wid > vwidth) { + wid += vwidth - off - wid; + } else if (off < 0 ) { + // We fell off the LSB end. + if ((unsigned)-off > wid ) return true; + // Trim the bits before the LSB + wid += off; + bit -= off; + off = 0; + } + + assert(wid > 0); + vvp_vector4_t value = vthread_bits_to_vector(thr, bit, wid); schedule_assign_array_word(cp->array, adr, off, value, delay); @@ -681,20 +695,68 @@ bool of_ASSIGN_AV(vthread_t thr, vvp_code_t cp) bool of_ASSIGN_AVD(vthread_t thr, vvp_code_t cp) { unsigned wid = thr->words[0].w_int; - unsigned off = thr->words[1].w_int; + long off = thr->words[1].w_int; unsigned adr = thr->words[3].w_int; - - assert(wid > 0); - unsigned long delay = thr->words[cp->bit_idx[0]].w_int; unsigned bit = cp->bit_idx[1]; + long vwidth = get_array_word_size(cp->array); + // We fell off the MSB end. + if (off >= vwidth) return true; + // Trim the bits after the MSB + if (off + (long)wid > vwidth) { + wid += vwidth - off - wid; + } else if (off < 0 ) { + // We fell off the LSB end. + if ((unsigned)-off > wid ) return true; + // Trim the bits before the LSB + wid += off; + bit -= off; + off = 0; + } + + assert(wid > 0); + vvp_vector4_t value = vthread_bits_to_vector(thr, bit, wid); schedule_assign_array_word(cp->array, adr, off, value, delay); return true; } +bool of_ASSIGN_AVE(vthread_t thr, vvp_code_t cp) +{ + unsigned wid = thr->words[0].w_int; + long off = thr->words[1].w_int; + unsigned adr = thr->words[3].w_int; + unsigned bit = cp->bit_idx[0]; + + long vwidth = get_array_word_size(cp->array); + // We fell off the MSB end. + if (off >= vwidth) return true; + // Trim the bits after the MSB + if (off + (long)wid > vwidth) { + wid += vwidth - off - wid; + } else if (off < 0 ) { + // We fell off the LSB end. + if ((unsigned)-off > wid ) return true; + // Trim the bits before the LSB + wid += off; + bit -= off; + off = 0; + } + + assert(wid > 0); + + vvp_vector4_t value = vthread_bits_to_vector(thr, bit, wid); + // If the count is zero then just put the value. + if (thr->ecount == 0) { + schedule_assign_array_word(cp->array, adr, off, value, 0); + } else { + schedule_evctl(cp->array, adr, value, off, thr->event, thr->ecount); + } + return true; +} + /* * This is %assign/v0