Partial non-blocking event control implementation

This patch pushes the non-blocking event control information to
the code generator. It adds the %evctl statements that are used
to put the event control information into the special thread
event control registers. The signed version (%evctl/s) required
the implementation of %ix/getv/s to load a signed value into
an index register. It then adds %assign/wr/e event control based
non-blocking assignment for real values. It also fixes the other
non-blocking real assignments to use Transport instead of inertial
delays.
This commit is contained in:
Cary R 2008-09-10 19:37:11 -07:00 committed by Stephen Williams
parent cdfb2315cf
commit 1e60754ff0
15 changed files with 471 additions and 145 deletions

View File

@ -758,6 +758,11 @@ void NetAssignNB::dump(ostream&o, unsigned ind) const
if (const NetExpr*de = get_delay())
o << "#(" << *de << ") ";
if (count_)
o << "repeat(" << *count_ << ") ";
if (event_) {
o << *event_;
}
o << *rval() << ";" << endl;
@ -902,6 +907,25 @@ void NetEvWait::dump(ostream&o, unsigned ind) const
o << setw(ind+2) << "" << "/* noop */ ;" << endl;
}
ostream& operator << (ostream&out, const NetEvWait&obj)
{
obj.dump_inline(out);
return out;
}
void NetEvWait::dump_inline(ostream&o) const
{
o << "@(";
if (nevents() > 0)
o << event(0)->name();
for (unsigned idx = 1 ; idx < nevents() ; idx += 1)
o << " or " << event(idx)->name();
o << ") ";
}
void NetForce::dump(ostream&o, unsigned ind) const
{
o << setw(ind) << "" << "force ";

View File

@ -1906,11 +1906,14 @@ NetProc* PAssignNB::elaborate(Design*des, NetScope*scope) const
}
NetExpr*delay = 0;
if (delay_ != 0)
if (delay_ != 0) {
assert(count_ == 0 && event_ == 0);
delay = elaborate_delay_expr(delay_, des, scope);
}
NetExpr*count = 0;
NetEvWait*event = 0;
if (count_ != 0 || event_ != 0) {
NetExpr*count = 0;
if (count_ != 0) {
assert(event_ != 0);
count = elab_and_eval(des, scope, count_, -1);
@ -1918,27 +1921,39 @@ NetProc* PAssignNB::elaborate(Design*des, NetScope*scope) const
cerr << get_fileline() << ": Unable to elaborate "
"repeat expression." << endl;
des->errors += 1;
// return 0;
return 0;
}
}
NetProc* event = event_->elaborate(des, scope);
if (event == 0) {
NetProc*st = event_->elaborate(des, scope);
if (st == 0) {
cerr << get_fileline() << ": unable to elaborate "
"event expression." << endl;
des->errors += 1;
// return 0;
return 0;
}
event = dynamic_cast<NetEvWait*>(st) ;
assert(event);
cerr << get_fileline() << ": sorry: non blocking ";
if (count_) cerr << "repeat ";
cerr << "event controls are not supported." << endl;
des->errors += 1;
return 0;
// Some constant values are special.
if (NetEConst*ce = dynamic_cast<NetEConst*>(count)) {
long val = ce->value().as_long();
// We only need the assignment statement.
if (val <= 0) {
delete count;
delete event;
count = 0;
event = 0;
// We only need the event.
} else if (val == 1) {
delete count;
count = 0;
}
}
}
/* All done with this node. Mark its line number and check it in. */
NetAssignNB*cur = new NetAssignNB(lv, rv);
NetAssignNB*cur = new NetAssignNB(lv, rv, event, count);
cur->set_delay(delay);
cur->set_line(*this);
return cur;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000 Stephen Williams (steve@icarus.com)
* Copyright (c) 2000-2008 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -16,9 +16,6 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CVS_IDENT
#ident "$Id: net_assign.cc,v 1.22 2007/01/16 05:44:15 steve Exp $"
#endif
# include "config.h"
@ -212,15 +209,34 @@ NetAssign::~NetAssign()
{
}
NetAssignNB::NetAssignNB(NetAssign_*lv, NetExpr*rv)
NetAssignNB::NetAssignNB(NetAssign_*lv, NetExpr*rv, NetEvWait*ev, NetExpr*cnt)
: NetAssignBase(lv, rv)
{
event_ = ev;
count_ = cnt;
}
NetAssignNB::~NetAssignNB()
{
}
unsigned NetAssignNB::nevents() const
{
if (event_) return event_->nevents();
return 0;
}
const NetEvent*NetAssignNB::event(unsigned idx) const
{
if (event_) return event_->event(idx);
return 0;
}
const NetExpr*NetAssignNB::get_count() const
{
return count_;
}
NetCAssign::NetCAssign(NetAssign_*lv, NetExpr*rv)
: NetAssignBase(lv, rv)
{
@ -256,34 +272,3 @@ NetRelease::NetRelease(NetAssign_*l)
NetRelease::~NetRelease()
{
}
/*
* $Log: net_assign.cc,v $
* Revision 1.22 2007/01/16 05:44:15 steve
* Major rework of array handling. Memories are replaced with the
* more general concept of arrays. The NetMemory and NetEMemory
* classes are removed from the ivl core program, and the IVL_LPM_RAM
* lpm type is removed from the ivl_target API.
*
* Revision 1.21 2006/02/02 02:43:58 steve
* Allow part selects of memory words in l-values.
*
* Revision 1.20 2005/07/11 16:56:50 steve
* Remove NetVariable and ivl_variable_t structures.
*
* Revision 1.19 2004/12/11 02:31:26 steve
* Rework of internals to carry vectors through nexus instead
* of single bits. Make the ivl, tgt-vvp and vvp initial changes
* down this path.
*
* Revision 1.18 2004/08/28 15:08:31 steve
* Do not change reg to wire in NetAssign_ unless synthesizing.
*
* Revision 1.17 2004/02/18 17:11:56 steve
* Use perm_strings for named langiage items.
*
* Revision 1.16 2003/01/26 21:15:58 steve
* Rework expression parsing and elaboration to
* accommodate real/realtime values and expressions.
*/

View File

@ -2230,7 +2230,8 @@ class NetAssign : public NetAssignBase {
class NetAssignNB : public NetAssignBase {
public:
explicit NetAssignNB(NetAssign_*lv, NetExpr*rv);
explicit NetAssignNB(NetAssign_*lv, NetExpr*rv, NetEvWait*ev,
NetExpr*cnt);
~NetAssignNB();
@ -2238,7 +2239,13 @@ class NetAssignNB : public NetAssignBase {
virtual int match_proc(struct proc_match_t*);
virtual void dump(ostream&, unsigned ind) const;
unsigned nevents() const;
const NetEvent*event(unsigned) const;
const NetExpr* get_count() const;
private:
NetEvWait*event_;
NetExpr*count_;
};
/*
@ -2620,6 +2627,8 @@ class NetEvWait : public NetProc {
const svector<NetEvProbe*>&events);
virtual void dump(ostream&, unsigned ind) const;
// This will ignore any statement.
virtual void dump_inline(ostream&) const;
virtual DelayType delay_type() const;
private:
@ -2629,6 +2638,8 @@ class NetEvWait : public NetProc {
NetEvent**events_;
};
ostream& operator << (ostream&out, const NetEvWait&obj);
class NetEvProbe : public NetNode {
friend class NetEvent;

View File

@ -1972,6 +1972,9 @@ extern "C" ivl_statement_t ivl_stmt_case_stmt(ivl_statement_t net, unsigned idx)
extern "C" ivl_expr_t ivl_stmt_cond_expr(ivl_statement_t net)
{
switch (net->type_) {
case IVL_ST_ASSIGN_NB:
return net->u_.assign_.count;
case IVL_ST_CONDIT:
return net->u_.condit_.cond_;
@ -2034,6 +2037,9 @@ extern "C" uint64_t ivl_stmt_delay_val(ivl_statement_t net)
extern "C" unsigned ivl_stmt_nevent(ivl_statement_t net)
{
switch (net->type_) {
case IVL_ST_ASSIGN_NB:
return net->u_.assign_.nevent;
case IVL_ST_WAIT:
return net->u_.wait_.nevent;
@ -2049,6 +2055,13 @@ extern "C" unsigned ivl_stmt_nevent(ivl_statement_t net)
extern "C" ivl_event_t ivl_stmt_events(ivl_statement_t net, unsigned idx)
{
switch (net->type_) {
case IVL_ST_ASSIGN_NB:
assert(idx < net->u_.assign_.nevent);
if (net->u_.assign_.nevent == 1)
return net->u_.assign_.event;
else
return net->u_.assign_.events[idx];
case IVL_ST_WAIT:
assert(idx < net->u_.wait_.nevent);
if (net->u_.wait_.nevent == 1)
@ -2059,6 +2072,7 @@ extern "C" ivl_event_t ivl_stmt_events(ivl_statement_t net, unsigned idx)
case IVL_ST_TRIGGER:
assert(idx == 0);
return net->u_.wait_.event;
default:
assert(0);
}

View File

@ -59,7 +59,7 @@ bool dll_target::process(const NetProcTop*net)
/* This little bit causes the process to be completely
generated so that it can be passed to the DLL. The
stmt_cur_ member us used to hold a pointer to the current
stmt_cur_ member is used to hold a pointer to the current
statement in progress, and the emit_proc() method fills in
that object.
@ -222,6 +222,7 @@ bool dll_target::proc_assign(const NetAssign*net)
void dll_target::proc_assign_nb(const NetAssignNB*net)
{
const NetExpr* delay_exp = net->get_delay();
const NetExpr* cnt_exp = net->get_count();
assert(stmt_cur_);
assert(stmt_cur_->type_ == IVL_ST_NONE);
@ -229,6 +230,8 @@ void dll_target::proc_assign_nb(const NetAssignNB*net)
FILE_NAME(stmt_cur_, net);
stmt_cur_->u_.assign_.delay = 0;
stmt_cur_->u_.assign_.count = 0;
stmt_cur_->u_.assign_.nevent = 0;
/* Make the lval fields. */
make_assign_lvals_(net);
@ -239,6 +242,7 @@ void dll_target::proc_assign_nb(const NetAssignNB*net)
stmt_cur_->u_.assign_.rval_ = expr_;
expr_ = 0;
/* Process a delay if it exists. */
if (const NetEConst*delay_num = dynamic_cast<const NetEConst*>(delay_exp)) {
verinum val = delay_num->value();
ivl_expr_t de = new struct ivl_expr_s;
@ -253,6 +257,96 @@ void dll_target::proc_assign_nb(const NetAssignNB*net)
stmt_cur_->u_.assign_.delay = expr_;
expr_ = 0;
}
/* Process a count if it exists. */
if (const NetEConst*cnt_num = dynamic_cast<const NetEConst*>(cnt_exp)) {
verinum val = cnt_num->value();
ivl_expr_t cnt = new struct ivl_expr_s;
cnt->type_ = IVL_EX_ULONG;
cnt->width_ = 8 * sizeof(unsigned long);
cnt->signed_ = 0;
cnt->u_.ulong_.value = val.as_ulong();
stmt_cur_->u_.assign_.count = cnt;
} else if (cnt_exp != 0) {
cnt_exp->expr_scan(this);
stmt_cur_->u_.assign_.count = expr_;
expr_ = 0;
}
/* Process the events if they exist. This is a copy of code
* from NetEvWait below. */
if (net->nevents() > 0) {
stmt_cur_->u_.assign_.nevent = net->nevents();
if (net->nevents() > 1) {
stmt_cur_->u_.assign_.events = (ivl_event_t*)
calloc(net->nevents(), sizeof(ivl_event_t*));
}
for (unsigned edx = 0 ; edx < net->nevents() ; edx += 1) {
/* Locate the event by name. Save the ivl_event_t in the
statement so that the generator can find it easily. */
const NetEvent*ev = net->event(edx);
ivl_scope_t ev_scope = lookup_scope_(ev->scope());
ivl_event_t ev_tmp=0;
assert(ev_scope);
assert(ev_scope->nevent_ > 0);
for (unsigned idx = 0; idx < ev_scope->nevent_; idx += 1) {
const char*ename =
ivl_event_basename(ev_scope->event_[idx]);
if (strcmp(ev->name(), ename) == 0) {
ev_tmp = ev_scope->event_[idx];
break;
}
}
// XXX should we assert(ev_tmp)?
if (net->nevents() == 1)
stmt_cur_->u_.assign_.event = ev_tmp;
else
stmt_cur_->u_.assign_.events[edx] = ev_tmp;
/* If this is an event with a probe, then connect up the
pins. This wasn't done during the ::event method because
the signals weren't scanned yet. */
if (ev->nprobe() >= 1) {
unsigned iany = 0;
unsigned ineg = ev_tmp->nany;
unsigned ipos = ineg + ev_tmp->nneg;
for (unsigned idx = 0; idx < ev->nprobe(); idx += 1) {
const NetEvProbe*pr = ev->probe(idx);
unsigned base = 0;
switch (pr->edge()) {
case NetEvProbe::ANYEDGE:
base = iany;
iany += pr->pin_count();
break;
case NetEvProbe::NEGEDGE:
base = ineg;
ineg += pr->pin_count();
break;
case NetEvProbe::POSEDGE:
base = ipos;
ipos += pr->pin_count();
break;
}
for (unsigned bit = 0; bit < pr->pin_count();
bit += 1) {
ivl_nexus_t nex = (ivl_nexus_t)
pr->pin(bit).nexus()->t_cookie();
assert(nex);
ev_tmp->pins[base+bit] = nex;
}
}
}
}
}
}
bool dll_target::proc_block(const NetBlock*net)
@ -643,6 +737,7 @@ bool dll_target::proc_wait(const NetEvWait*net)
stmt_cur_->u_.wait_.stmt_ = (struct ivl_statement_s*)
calloc(1, sizeof(struct ivl_statement_s));
// This event processing code is also in the NB assign above.
stmt_cur_->u_.wait_.nevent = net->nevents();
if (net->nevents() > 1) {
stmt_cur_->u_.wait_.events = (ivl_event_t*)

View File

@ -671,6 +671,13 @@ struct ivl_statement_s {
struct ivl_lval_s*lval_;
ivl_expr_t rval_;
ivl_expr_t delay;
// The following are only for NB event control.
ivl_expr_t count;
unsigned nevent;
union {
ivl_event_t event;
ivl_event_t*events;
};
} assign_;
struct { /* IVL_ST_BLOCK, IVL_ST_FORK */

View File

@ -162,7 +162,9 @@ static void eval_logic_into_integer(ivl_expr_t expr, unsigned ix)
break;
}
}
fprintf(vvp_out, " %%ix/getv %u, v%p_%u;\n", ix, sig, word);
char*type = ivl_signal_signed(sig) ? "/s" : "";
fprintf(vvp_out, " %%ix/getv%s %u, v%p_%u;\n", type, ix,
sig, word);
break;
}

View File

@ -532,6 +532,7 @@ static int show_stmt_assign_nb_real(ivl_statement_t net)
/* thread address for a word value. */
int word;
unsigned long delay = 0;
unsigned nevents = ivl_stmt_nevent(net);
/* Must be exactly 1 l-value. */
assert(ivl_stmt_lvals(net) == 1);
@ -557,13 +558,17 @@ static int show_stmt_assign_nb_real(ivl_statement_t net)
/* We need to calculate the delay expression. */
if (del) {
assert(nevents == 0 && ivl_stmt_cond_expr(net) == 0);
int delay_index = allocate_word();
draw_eval_expr_into_integer(del, delay_index);
fprintf(vvp_out, " %%assign/wr/d v%p_%lu, %d, %u;\n",
fprintf(vvp_out, " %%assign/wr/d v%p_%lu, %d, %u;\n",
sig, use_word, delay_index, word);
clr_word(delay_index);
} else if (nevents) {
fprintf(vvp_out, " %%assign/wr/e v%p_%lu, %u;\n",
sig, use_word, word);
} else {
fprintf(vvp_out, " %%assign/wr v%p_%lu, %lu, %u;\n",
fprintf(vvp_out, " %%assign/wr v%p_%lu, %lu, %u;\n",
sig, use_word, delay, word);
}
@ -578,6 +583,49 @@ static int show_stmt_assign_nb(ivl_statement_t net)
ivl_expr_t rval = ivl_stmt_rval(net);
ivl_expr_t del = ivl_stmt_delay_expr(net);
ivl_signal_t sig;
unsigned nevents = ivl_stmt_nevent(net);
// If we have an event control build the control structure.
if (nevents) {
assert(del == 0);
ivl_expr_t cnt = ivl_stmt_cond_expr(net);
unsigned long count = 1;
if (cnt && (ivl_expr_type(cnt) == IVL_EX_ULONG)) {
count = ivl_expr_uvalue(cnt);
cnt = 0;
}
char name[256];
if (nevents == 1) {
ivl_event_t ev = ivl_stmt_events(net, 0);
snprintf(name, sizeof(name), "E_%p", ev);
} else {
unsigned idx;
static unsigned int cascade_counter = 0;
ivl_event_t ev = ivl_stmt_events(net, 0);
fprintf(vvp_out, "Eassign_%u .event/or E_%p",
cascade_counter, ev);
for (idx = 1; idx < nevents; idx += 1) {
ev = ivl_stmt_events(net, idx);
fprintf(vvp_out, ", E_%p", ev);
}
snprintf(name, sizeof(name), "Eassign_%u", cascade_counter);
cascade_counter += 1;
}
if (cnt) {
int count_index = allocate_word();
char*type = ivl_expr_signed(cnt) ? "/s" : "";
draw_eval_expr_into_integer(cnt, count_index);
fprintf(vvp_out, " %%evctl%s %s, %d;\n", type, name,
count_index);
clr_word(count_index);
} else {
fprintf(vvp_out, " %%evctl/i %s, %lu;\n", name, count);
}
}
unsigned long delay = 0;
@ -592,6 +640,16 @@ static int show_stmt_assign_nb(ivl_statement_t net)
}
}
if (nevents) {
fprintf(stderr, "%s:%u: vvp-tgt sorry: non-blocking ",
ivl_stmt_file(net), ivl_stmt_lineno(net));
if (ivl_stmt_cond_expr(net)) {
fprintf(stderr, "repeat ");
}
fprintf(stderr, "event controls are not supported!\n");
exit(1);
}
if (del && (ivl_expr_type(del) == IVL_EX_ULONG)) {
delay = ivl_expr_uvalue(del);
del = 0;

View File

@ -49,6 +49,7 @@ extern bool of_ASSIGN_V0X1(vthread_t thr, vvp_code_t code);
extern bool of_ASSIGN_V0X1D(vthread_t thr, vvp_code_t code);
extern bool of_ASSIGN_WR(vthread_t thr, vvp_code_t code);
extern bool of_ASSIGN_WRD(vthread_t thr, vvp_code_t code);
extern bool of_ASSIGN_WRE(vthread_t thr, vvp_code_t code);
extern bool of_ASSIGN_X0(vthread_t thr, vvp_code_t code);
extern bool of_BLEND(vthread_t thr, vvp_code_t code);
extern bool of_BLEND_WR(vthread_t thr, vvp_code_t code);
@ -78,6 +79,9 @@ extern bool of_DIV(vthread_t thr, vvp_code_t code);
extern bool of_DIV_S(vthread_t thr, vvp_code_t code);
extern bool of_DIV_WR(vthread_t thr, vvp_code_t code);
extern bool of_END(vthread_t thr, vvp_code_t code);
extern bool of_EVCTL(vthread_t thr, vvp_code_t code);
extern bool of_EVCTLI(vthread_t thr, vvp_code_t code);
extern bool of_EVCTLS(vthread_t thr, vvp_code_t code);
extern bool of_FORCE_LINK(vthread_t thr, vvp_code_t code);
extern bool of_FORCE_V(vthread_t thr, vvp_code_t code);
extern bool of_FORCE_WR(vthread_t thr, vvp_code_t code);
@ -87,6 +91,7 @@ extern bool of_INV(vthread_t thr, vvp_code_t code);
extern bool of_IX_ADD(vthread_t thr, vvp_code_t code);
extern bool of_IX_GET(vthread_t thr, vvp_code_t code);
extern bool of_IX_GETV(vthread_t thr, vvp_code_t code);
extern bool of_IX_GETVS(vthread_t thr, vvp_code_t code);
extern bool of_IX_GET_S(vthread_t thr, vvp_code_t code);
extern bool of_IX_LOAD(vthread_t thr, vvp_code_t code);
extern bool of_IX_MUL(vthread_t thr, vvp_code_t code);

View File

@ -92,8 +92,9 @@ const static struct opcode_table_s opcode_table[] = {
{ "%assign/v0/d",of_ASSIGN_V0D,3,{OA_FUNC_PTR,OA_BIT1, OA_BIT2} },
{ "%assign/v0/x1",of_ASSIGN_V0X1,3,{OA_FUNC_PTR,OA_BIT1,OA_BIT2} },
{ "%assign/v0/x1/d",of_ASSIGN_V0X1D,3,{OA_FUNC_PTR,OA_BIT1,OA_BIT2} },
{ "%assign/wr",of_ASSIGN_WR,3,{OA_VPI_PTR,OA_BIT1, OA_BIT2} },
{ "%assign/wr/d",of_ASSIGN_WRD,3,{OA_VPI_PTR,OA_BIT1, OA_BIT2} },
{ "%assign/wr", of_ASSIGN_WR, 3,{OA_VPI_PTR, OA_BIT1, OA_BIT2} },
{ "%assign/wr/d",of_ASSIGN_WRD,3,{OA_VPI_PTR, OA_BIT1, OA_BIT2} },
{ "%assign/wr/e",of_ASSIGN_WRE,2,{OA_VPI_PTR, OA_BIT1, OA_NONE} },
{ "%assign/x0",of_ASSIGN_X0,3,{OA_FUNC_PTR,OA_BIT1, OA_BIT2} },
{ "%blend", of_BLEND, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
{ "%blend/wr", of_BLEND_WR,2, {OA_BIT1, OA_BIT2, OA_NONE} },
@ -122,6 +123,9 @@ const static struct opcode_table_s opcode_table[] = {
{ "%div/s", of_DIV_S, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
{ "%div/wr", of_DIV_WR, 2, {OA_BIT1, OA_BIT2, OA_NONE} },
{ "%end", of_END, 0, {OA_NONE, OA_NONE, OA_NONE} },
{ "%evctl", of_EVCTL, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} },
{ "%evctl/i",of_EVCTLI, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} },
{ "%evctl/s",of_EVCTLS, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} },
{ "%force/link",of_FORCE_LINK,2,{OA_FUNC_PTR,OA_FUNC_PTR2,OA_NONE} },
{ "%force/v",of_FORCE_V,3, {OA_FUNC_PTR, OA_BIT1, OA_BIT2} },
{ "%force/wr",of_FORCE_WR,2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} },
@ -131,6 +135,7 @@ const static struct opcode_table_s opcode_table[] = {
{ "%ix/get", of_IX_GET, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
{ "%ix/get/s",of_IX_GET_S,3,{OA_BIT1, OA_BIT2, OA_NUMBER} },
{ "%ix/getv",of_IX_GETV,2, {OA_BIT1, OA_FUNC_PTR, OA_NONE} },
{ "%ix/getv/s",of_IX_GETVS,2, {OA_BIT1, OA_FUNC_PTR, OA_NONE} },
{ "%ix/load",of_IX_LOAD,2, {OA_BIT1, OA_NUMBER, OA_NONE} },
{ "%ix/mul", of_IX_MUL, 2, {OA_BIT1, OA_NUMBER, OA_NONE} },
{ "%ix/sub", of_IX_SUB, 2, {OA_BIT1, OA_NUMBER, OA_NONE} },

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2004 Stephen Williams (steve@icarus.com)
* Copyright (c) 2004-2008 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -16,9 +16,6 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CVS_IDENT
#ident "$Id: event.cc,v 1.23 2006/12/09 19:06:53 steve Exp $"
#endif
# include "event.h"
# include "compile.h"
@ -37,6 +34,20 @@
void waitable_hooks_s::run_waiting_threads_()
{
// Run the non-blocking event controls.
last = &event_ctls;
for (evctl*cur = event_ctls; cur != 0;) {
if (cur->dec_and_run()) {
evctl*nxt = cur->next;
delete cur;
cur = nxt;
*last = cur;
} else {
last = &(cur->next);
cur = cur->next;
}
}
if (threads == 0)
return;
@ -45,6 +56,48 @@ void waitable_hooks_s::run_waiting_threads_()
vthread_schedule_list(tmp);
}
evctl::evctl(unsigned long ecount)
{
ecount_ = ecount;
next = 0;
}
evctl_real::evctl_real(struct __vpiHandle*handle, double value,
unsigned long ecount)
:evctl(ecount)
{
handle_ = handle;
value_ = value;
}
bool evctl_real::dec_and_run()
{
assert(ecount_ != 0);
ecount_ -= 1;
if (ecount_ == 0) {
t_vpi_value val;
val.format = vpiRealVal;
val.value.real = value_;
vpi_put_value(handle_, &val, 0, vpiNoDelay);
}
return ecount_ == 0;
}
void schedule_evctl(struct __vpiHandle*handle, double value,
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_real(handle, value, ecount);
ep->last = &((*(ep->last))->next);
}
inline vvp_fun_edge::edge_t VVP_EDGE(vvp_bit4_t from, vvp_bit4_t to)
{
return 1 << ((from << 2) | to);
@ -270,43 +323,3 @@ void compile_named_event(char*label, char*name)
free(label);
free(name);
}
/*
* $Log: event.cc,v $
* Revision 1.23 2006/12/09 19:06:53 steve
* Handle vpiRealVal reads of signals, and real anyedge events.
*
* Revision 1.22 2006/11/22 06:10:05 steve
* Fix spurious event from net8 that is forced.
*
* Revision 1.21 2006/02/21 04:57:26 steve
* Callbacks for named event triggers.
*
* Revision 1.20 2005/06/22 00:04:49 steve
* Reduce vvp_vector4 copies by using const references.
*
* Revision 1.19 2005/06/17 23:47:02 steve
* threads member for waitable_hook_s needs initailizing.
*
* Revision 1.18 2005/05/25 05:44:51 steve
* Handle event/or with specific, efficient nodes.
*
* Revision 1.17 2004/12/29 23:45:13 steve
* Add the part concatenation node (.concat).
*
* Add a vvp_event_anyedge class to handle the special
* case of .event statements of edge type. This also
* frees the posedge/negedge types to handle all 4 inputs.
*
* Implement table functor recv_vec4 method to receive
* and process vectors.
*
* Revision 1.16 2004/12/18 18:52:44 steve
* Rework named events and event/or.
*
* Revision 1.15 2004/12/11 02:31:29 steve
* Rework of internals to carry vectors through nexus instead
* of single bits. Make the ivl, tgt-vvp and vvp initial changes
* down this path.
*
*/

View File

@ -1,7 +1,7 @@
#ifndef __event_H
#define __event_H
/*
* Copyright (c) 2004 Stephen Williams (steve@icarus.com)
* Copyright (c) 2004-2008 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -18,13 +18,38 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CVS_IDENT
#ident "$Id: event.h,v 1.13 2006/12/09 19:06:53 steve Exp $"
#endif
# include "vvp_net.h"
# include "pointers.h"
class evctl {
public:
explicit evctl(unsigned long ecount);
virtual bool dec_and_run() = 0;
virtual ~evctl() {}
evctl*next;
protected:
unsigned long ecount_;
};
class evctl_real : public evctl {
public:
explicit evctl_real(struct __vpiHandle*handle, double value,
unsigned long ecount);
virtual ~evctl_real() {}
bool dec_and_run();
private:
__vpiHandle*handle_;
double value_;
};
extern void schedule_evctl(struct __vpiHandle*handle, double value,
vvp_net_t*event, unsigned long ecount);
/*
* Event / edge detection functors
*/
@ -36,8 +61,10 @@
struct waitable_hooks_s {
public:
waitable_hooks_s() : threads(0) { }
waitable_hooks_s() : threads(0), event_ctls(0) { last = &event_ctls; }
vthread_t threads;
evctl*event_ctls;
evctl**last;
protected:
void run_waiting_threads_();
@ -126,40 +153,4 @@ class vvp_named_event : public vvp_net_fun_t, public waitable_hooks_s {
struct __vpiHandle*handle_;
};
/*
* $Log: event.h,v $
* Revision 1.13 2006/12/09 19:06:53 steve
* Handle vpiRealVal reads of signals, and real anyedge events.
*
* Revision 1.12 2006/11/22 06:10:05 steve
* Fix spurious event from net8 that is forced.
*
* Revision 1.11 2005/06/22 00:04:49 steve
* Reduce vvp_vector4 copies by using const references.
*
* Revision 1.10 2005/06/17 23:47:02 steve
* threads member for waitable_hook_s needs initailizing.
*
* Revision 1.9 2005/05/25 05:44:51 steve
* Handle event/or with specific, efficient nodes.
*
* Revision 1.8 2004/12/29 23:45:13 steve
* Add the part concatenation node (.concat).
*
* Add a vvp_event_anyedge class to handle the special
* case of .event statements of edge type. This also
* frees the posedge/negedge types to handle all 4 inputs.
*
* Implement table functor recv_vec4 method to receive
* and process vectors.
*
* Revision 1.7 2004/12/18 18:52:44 steve
* Rework named events and event/or.
*
* Revision 1.6 2004/12/11 02:31:29 steve
* Rework of internals to carry vectors through nexus instead
* of single bits. Make the ivl, tgt-vvp and vvp initial changes
* down this path.
*/
#endif // __event_H

View File

@ -105,6 +105,7 @@ vector is to be written. This allows for part writes into the vector.
* %assign/wr <vpi-label>, <delay>, <index>
* %assign/wr/d <vpi-label>, <delayx>, <index>
* %assign/wr/e <vpi-label>, <index>
This instruction provides a non-blocking assign of the real value
given in <index> to the real object addressed by the <vpi-label>
@ -113,6 +114,10 @@ label after the given <delay>.
The %assign/wr/d variation gets the delay from integer register
<delayx>.
The %assign/wr/e variation uses the information in the thread
event control registers to determine when to perform the assign.
%evctl is used to set the event control information.
* %assign/x0 <var-label>, <delay>, <bit> (OBSOLETE -- See %assign/v0x)
This does a non-blocking assignment to a functor, similar to the
@ -296,6 +301,18 @@ This opcode divides the left operand by the right operand. If the
right operand is 0, then the result is NaN.
* %evctl <functor-label> <idx>
* %evctl/s <functor-label> <idx>
* %evctl/i <functor-label> <value>
These instructions are used to put event and repetition information
into the thread event control registers. These values are then used
by the %assign/e instructions to do not blocking event control. The
<functor-label> is the event to trigger on and the <idx> is an index
register to read the repetition count from (signed or unsigned).
%evctl/i sets the repetition to an immediate unsigned value.
* %force/v <label>, <bit>, <wid>
Force a constant value to the target variable. This is similar to %set
@ -363,10 +380,11 @@ bits.
6: (reserved)
* %ix/getv <functor-label>, <bit>
* %ix/getv/s <functor-label>, <bit>
This instruction is like the %ix/get instruction, except that is reads
directly from a functor label instead of from thread bits. It sets
bits 4/5/6 just line %ix/get.
These instructions are like the %ix/get instructions, except that they
read directly from a functor label instead of from thread bits. They
set bit 4 just like %ix/get.
* %ix/load <idx>, <value>

View File

@ -117,6 +117,10 @@ struct vthread_s {
struct vthread_s*wait_next;
/* These are used to keep the thread in a scope. */
struct vthread_s*scope_next, *scope_prev;
/* These are used to pass non-blocking event control information. */
vvp_net_t*event;
uint64_t ecount;
};
// this table maps the thread special index bit addresses to
@ -332,6 +336,8 @@ vthread_t vthread_new(vvp_code_t pc, struct __vpiScope*scope)
thr->waiting_for_event = 0;
thr->is_scheduled = 0;
thr->fork_count = 0;
thr->event = 0;
thr->ecount = 0;
thr_put_bit(thr, 0, BIT4_0);
thr_put_bit(thr, 1, BIT4_1);
@ -815,7 +821,7 @@ bool of_ASSIGN_WR(vthread_t thr, vvp_code_t cp)
t_vpi_value val;
val.format = vpiRealVal;
val.value.real = thr->words[index].w_real;
vpi_put_value(tmp, &val, &del, vpiInertialDelay);
vpi_put_value(tmp, &val, &del, vpiTransportDelay);
return true;
}
@ -834,7 +840,31 @@ bool of_ASSIGN_WRD(vthread_t thr, vvp_code_t cp)
t_vpi_value val;
val.format = vpiRealVal;
val.value.real = thr->words[index].w_real;
vpi_put_value(tmp, &val, &del, vpiInertialDelay);
vpi_put_value(tmp, &val, &del, vpiTransportDelay);
return true;
}
bool of_ASSIGN_WRE(vthread_t thr, vvp_code_t cp)
{
assert(thr->event != 0);
unsigned index = cp->bit_idx[0];
struct __vpiHandle*tmp = cp->handle;
// If the count is zero then just put the value.
if (thr->ecount == 0) {
t_vpi_value val;
val.format = vpiRealVal;
val.value.real = thr->words[index].w_real;
vpi_put_value(tmp, &val, 0, vpiNoDelay);
} else {
schedule_evctl(tmp, thr->words[index].w_real, thr->event,
thr->ecount);
}
thr->event = 0;
thr->ecount = 0;
return true;
}
@ -1931,6 +1961,32 @@ bool of_END(vthread_t thr, vvp_code_t)
return false;
}
bool of_EVCTL(vthread_t thr, vvp_code_t cp)
{
assert(thr->event == 0 && thr->ecount == 0);
thr->event = cp->net;
thr->ecount = thr->words[cp->bit_idx[0]].w_uint;
return true;
}
bool of_EVCTLI(vthread_t thr, vvp_code_t cp)
{
assert(thr->event == 0 && thr->ecount == 0);
thr->event = cp->net;
thr->ecount = cp->bit_idx[0];
return true;
}
bool of_EVCTLS(vthread_t thr, vvp_code_t cp)
{
assert(thr->event == 0 && thr->ecount == 0);
thr->event = cp->net;
int64_t val = thr->words[cp->bit_idx[0]].w_int;
if (val < 0) val = 0;
thr->ecount = val;
return true;
}
static void unlink_force(vvp_net_t*net)
{
vvp_fun_signal_base*sig
@ -2277,6 +2333,33 @@ bool of_IX_GETV(vthread_t thr, vvp_code_t cp)
return true;
}
bool of_IX_GETVS(vthread_t thr, vvp_code_t cp)
{
unsigned index = cp->bit_idx[0];
vvp_net_t*net = cp->net;
vvp_fun_signal_vec*sig = dynamic_cast<vvp_fun_signal_vec*>(net->fun);
if (sig == 0) {
cerr << "%%ix/getv/s error: Net arg not a vector signal? "
<< typeid(*net->fun).name() << endl;
}
assert(sig);
vvp_vector4_t vec = sig->vec4_value();
long val;
bool known_flag = vector4_to_value(vec, val, true, true);
if (known_flag)
thr->words[index].w_int = val;
else
thr->words[index].w_int = 0;
/* Set bit 4 as a flag if the input is unknown. */
thr_put_bit(thr, 4, known_flag? BIT4_0 : BIT4_1);
return true;
}
/*
* The various JMP instruction work simply by pulling the new program
* counter from the instruction and resuming. If the jump is