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:
parent
cdfb2315cf
commit
1e60754ff0
|
|
@ -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 ";
|
||||
|
|
|
|||
39
elaborate.cc
39
elaborate.cc
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
||||
|
|
|
|||
13
netlist.h
13
netlist.h
|
|
@ -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;
|
||||
|
|
|
|||
14
t-dll-api.cc
14
t-dll-api.cc
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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*)
|
||||
|
|
|
|||
7
t-dll.h
7
t-dll.h
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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} },
|
||||
|
|
|
|||
101
vvp/event.cc
101
vvp/event.cc
|
|
@ -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.
|
||||
*
|
||||
*/
|
||||
|
|
|
|||
73
vvp/event.h
73
vvp/event.h
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue