Add non-blocking EC for arrays and other fixes.
This patch adds non-blocking event control for array words. It also fixes a problem where the word used to put the calculated delay for a non-blocking array assignment was not being released. It also fixes the non-blocking array assignments to correctly handle off the end/beginning part selects.
This commit is contained in:
parent
a39b9d4ef1
commit
f7d3c7c711
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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} },
|
||||
|
|
|
|||
26
vvp/event.cc
26
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<waitable_hooks_s*> (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)
|
||||
{
|
||||
|
|
|
|||
23
vvp/event.h
23
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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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 <label>, <delay>, <bit>
|
||||
* Index register 0 contains a vector width.
|
||||
|
|
@ -757,8 +819,8 @@ bool of_ASSIGN_V0E(vthread_t thr, vvp_code_t cp)
|
|||
|
||||
vvp_net_ptr_t ptr (cp->net, 0);
|
||||
|
||||
// If the count is zero then just put the value.
|
||||
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_plucked_vector(ptr, 0, value, 0, wid);
|
||||
} else {
|
||||
|
|
@ -786,7 +848,6 @@ bool of_ASSIGN_V0X1(vthread_t thr, vvp_code_t cp)
|
|||
vvp_fun_signal_vec*sig
|
||||
= reinterpret_cast<vvp_fun_signal_vec*> (cp->net->fun);
|
||||
assert(sig);
|
||||
assert(wid > 0);
|
||||
|
||||
// We fell off the MSB end.
|
||||
if (off >= (long)sig->size()) return true;
|
||||
|
|
@ -799,6 +860,8 @@ bool of_ASSIGN_V0X1(vthread_t thr, vvp_code_t cp)
|
|||
off = 0;
|
||||
}
|
||||
|
||||
assert(wid > 0);
|
||||
|
||||
vvp_vector4_t value = vthread_bits_to_vector(thr, bit, wid);
|
||||
|
||||
vvp_net_ptr_t ptr (cp->net, 0);
|
||||
|
|
@ -822,7 +885,6 @@ bool of_ASSIGN_V0X1D(vthread_t thr, vvp_code_t cp)
|
|||
vvp_fun_signal_vec*sig
|
||||
= reinterpret_cast<vvp_fun_signal_vec*> (cp->net->fun);
|
||||
assert(sig);
|
||||
assert(wid > 0);
|
||||
|
||||
// We fell off the MSB end.
|
||||
if (off >= (long)sig->size()) return true;
|
||||
|
|
@ -835,6 +897,8 @@ bool of_ASSIGN_V0X1D(vthread_t thr, vvp_code_t cp)
|
|||
off = 0;
|
||||
}
|
||||
|
||||
assert(wid > 0);
|
||||
|
||||
vvp_vector4_t value = vthread_bits_to_vector(thr, bit, wid);
|
||||
|
||||
vvp_net_ptr_t ptr (cp->net, 0);
|
||||
|
|
@ -857,7 +921,6 @@ bool of_ASSIGN_V0X1E(vthread_t thr, vvp_code_t cp)
|
|||
vvp_fun_signal_vec*sig
|
||||
= reinterpret_cast<vvp_fun_signal_vec*> (cp->net->fun);
|
||||
assert(sig);
|
||||
assert(wid > 0);
|
||||
|
||||
// We fell off the MSB end.
|
||||
if (off >= (long)sig->size()) {
|
||||
|
|
@ -877,9 +940,12 @@ bool of_ASSIGN_V0X1E(vthread_t thr, vvp_code_t cp)
|
|||
off = 0;
|
||||
}
|
||||
|
||||
assert(wid > 0);
|
||||
|
||||
vvp_vector4_t value = vthread_bits_to_vector(thr, bit, wid);
|
||||
|
||||
vvp_net_ptr_t ptr (cp->net, 0);
|
||||
// If the count is zero then just put the value.
|
||||
if (thr->ecount == 0) {
|
||||
schedule_assign_vector(ptr, off, sig->size(), value, 0);
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in New Issue