diff --git a/Statement.cc b/Statement.cc index e200f7734..951dbd756 100644 --- a/Statement.cc +++ b/Statement.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2019 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2021 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 @@ -416,8 +416,8 @@ PReturn::~PReturn() delete expr_; } -PTrigger::PTrigger(PPackage*pkg, const pform_name_t&e) -: package_(pkg), event_(e) +PTrigger::PTrigger(PPackage*pkg, const pform_name_t&ev) +: package_(pkg), event_(ev) { } @@ -425,6 +425,15 @@ PTrigger::~PTrigger() { } +PNBTrigger::PNBTrigger(const pform_name_t&ev, PExpr*dly) +: event_(ev), dly_(dly) +{ +} + +PNBTrigger::~PNBTrigger() +{ +} + PWhile::PWhile(PExpr*ex, Statement*st) : cond_(ex), statement_(st) { diff --git a/Statement.h b/Statement.h index c0e5a2282..628e21167 100644 --- a/Statement.h +++ b/Statement.h @@ -590,6 +590,19 @@ class PTrigger : public Statement { pform_name_t event_; }; +class PNBTrigger : public Statement { + public: + explicit PNBTrigger(const pform_name_t&ev, PExpr*dly); + ~PNBTrigger(); + + virtual NetProc* elaborate(Design*des, NetScope*scope) const; + virtual void dump(ostream&out, unsigned ind) const; + + private: + pform_name_t event_; + PExpr*dly_; +}; + class PWhile : public Statement { public: diff --git a/design_dump.cc b/design_dump.cc index 0789e07a6..67b41a774 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -1300,6 +1300,13 @@ void NetEvTrig::dump(ostream&o, unsigned ind) const << "// " << get_fileline() << endl; } +void NetEvNBTrig::dump(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << "->> " ; + if (dly_) o << "#" << *dly_ << " "; + o << event_->name() << "; " << "// " << get_fileline() << endl; +} + void NetEvWait::dump(ostream&o, unsigned ind) const { o << setw(ind) << ""; diff --git a/elaborate.cc b/elaborate.cc index 01e41290d..481092413 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -5780,6 +5780,38 @@ NetProc* PTrigger::elaborate(Design*des, NetScope*scope) const return trig; } +NetProc* PNBTrigger::elaborate(Design*des, NetScope*scope) const +{ + assert(scope); + + NetNet* sig = 0; + const NetExpr*par = 0; + NetEvent* eve = 0; + + NetScope*found_in = symbol_search(this, des, scope, event_, + sig, par, eve); + + if (found_in == 0) { + cerr << get_fileline() << ": error: event <" << event_ << ">" + << " not found." << endl; + des->errors += 1; + return 0; + } + + if (eve == 0) { + cerr << get_fileline() << ": error: <" << event_ << ">" + << " is not a named event." << endl; + des->errors += 1; + return 0; + } + + NetExpr*dly = 0; + if (dly_) dly = elab_and_eval(des, scope, dly_, -1); + NetEvNBTrig*trig = new NetEvNBTrig(eve, dly); + trig->set_line(*this); + return trig; +} + /* * The while loop is fairly directly represented in the netlist. */ diff --git a/emit.cc b/emit.cc index c5c64c9d3..26360c4f7 100644 --- a/emit.cc +++ b/emit.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2017 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2021 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 @@ -389,6 +389,11 @@ bool NetEvTrig::emit_proc(struct target_t*tgt) const return tgt->proc_trigger(this); } +bool NetEvNBTrig::emit_proc(struct target_t*tgt) const +{ + return tgt->proc_nb_trigger(this); +} + bool NetEvWait::emit_proc(struct target_t*tgt) const { return tgt->proc_wait(this); diff --git a/ivl_target.h b/ivl_target.h index 787074c9e..7e41f6ecd 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -426,6 +426,7 @@ typedef enum ivl_statement_type_e { IVL_ST_FORK_JOIN_ANY = 28, IVL_ST_FORK_JOIN_NONE = 29, IVL_ST_FREE = 26, + IVL_ST_NB_TRIGGER = 31, IVL_ST_RELEASE = 17, IVL_ST_REPEAT = 18, IVL_ST_STASK = 19, diff --git a/lexor.lex b/lexor.lex index 7639feaa9..5ee0f2d4a 100644 --- a/lexor.lex +++ b/lexor.lex @@ -226,6 +226,7 @@ TU [munpf] "^~" { return K_NXOR; } "~&" { return K_NAND; } "->" { return K_TRIGGER; } +"->>" { return K_NB_TRIGGER; } "+:" { return K_PO_POS; } "-:" { return K_PO_NEG; } "<+" { return K_CONTRIBUTE; } diff --git a/net_event.cc b/net_event.cc index 2b6eb8d35..682a48cec 100644 --- a/net_event.cc +++ b/net_event.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2017 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2021 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 @@ -264,6 +264,39 @@ const NetEvent* NetEvTrig::event() const return event_; } +NetEvNBTrig::NetEvNBTrig(NetEvent*ev, NetExpr*dly) +: event_(ev), dly_(dly) +{ + enext_ = event_->nb_trig_; + event_->nb_trig_ = this; +} + +NetEvNBTrig::~NetEvNBTrig() +{ + if (event_->nb_trig_ == this) { + event_->nb_trig_ = enext_; + + } else { + NetEvNBTrig*cur = event_->nb_trig_; + while (cur->enext_ != this) { + assert(cur->enext_); + cur = cur->enext_; + } + + cur->enext_ = this->enext_; + } +} + +const NetExpr* NetEvNBTrig::delay() const +{ + return dly_; +} + +const NetEvent* NetEvNBTrig::event() const +{ + return event_; +} + NetEvProbe::NetEvProbe(NetScope*s, perm_string n, NetEvent*tgt, edge_t t, unsigned p) : NetNode(s, n, p), event_(tgt), edge_(t) diff --git a/net_nex_input.cc b/net_nex_input.cc index 78250316b..b3b9972ee 100644 --- a/net_nex_input.cc +++ b/net_nex_input.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2019 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2021 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 @@ -475,6 +475,11 @@ NexusSet* NetEvTrig::nex_input(bool, bool, bool) const return new NexusSet; } +NexusSet* NetEvNBTrig::nex_input(bool, bool, bool) const +{ + return new NexusSet; +} + NexusSet* NetEvWait::nex_input(bool rem_out, bool always_sens, bool nested_func) const { NexusSet*result = new NexusSet; diff --git a/net_nex_output.cc b/net_nex_output.cc index 9c1d32e10..892174a61 100644 --- a/net_nex_output.cc +++ b/net_nex_output.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2017 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2021 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 @@ -134,6 +134,10 @@ void NetEvTrig::nex_output(NexusSet&) { } +void NetEvNBTrig::nex_output(NexusSet&) +{ +} + void NetEvWait::nex_output(NexusSet&out) { if (statement_) statement_->nex_output(out); diff --git a/netlist.cc b/netlist.cc index d21c992ec..54a73d197 100644 --- a/netlist.cc +++ b/netlist.cc @@ -3260,6 +3260,13 @@ bool NetEvTrig::check_synth(ivl_process_type_t pr_type, return false; } +bool NetEvNBTrig::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const +{ + print_synth_warning(this, "A non-blocking event trigger", pr_type); + return false; +} + // The delay check above has already marked this as an error. bool NetEvWait::check_synth(ivl_process_type_t pr_type, const NetScope* scope) const diff --git a/netlist.h b/netlist.h index e8267adae..7827470c6 100644 --- a/netlist.h +++ b/netlist.h @@ -73,6 +73,7 @@ class NetFuncDef; class NetRamDq; class NetTaskDef; class NetEvTrig; +class NetEvNBTrig; class NetEvWait; class PClass; class PExpr; @@ -3346,6 +3347,11 @@ class NetDoWhile : public NetProc { * turn awakens the waiting threads. Each NetEvTrig object references * exactly one event object. * + * The NetEvNBTrig class represents non-blocking trigger statements. + * Executing this statement causes the referenced event to be triggered + * at some time in the future, which in turn awakens the waiting threads. + * Each NetEvNBTrig object references exactly one event object. + * * The NetEvProbe class is the structural equivalent of the NetEvTrig, * in that it is a node and watches bit values that it receives. It * checks for edges then if appropriate triggers the associated @@ -3358,6 +3364,7 @@ class NetEvent : public LineInfo { friend class NetScope; friend class NetEvProbe; friend class NetEvTrig; + friend class NetEvNBTrig; friend class NetEvWait; friend class NetEEvent; @@ -3416,6 +3423,9 @@ class NetEvent : public LineInfo { // Use these methods to list the triggers attached to me. NetEvTrig* trig_; + // Use these methods to list the non-blocking triggers attached to me. + NetEvNBTrig* nb_trig_; + // Use This member to count references by NetEvWait objects. unsigned waitref_; struct wcell_ { @@ -3455,6 +3465,31 @@ class NetEvTrig : public NetProc { NetEvTrig*enext_; }; +class NetEvNBTrig : public NetProc { + + friend class NetEvent; + + public: + explicit NetEvNBTrig(NetEvent*tgt, NetExpr*dly); + ~NetEvNBTrig(); + + const NetExpr*delay() const; + const NetEvent*event() const; + + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; + virtual void nex_output(NexusSet&); + virtual bool emit_proc(struct target_t*) const; + virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; + + private: + NetEvent*event_; + NetExpr*dly_; + // This is used to place me in the NetEvents lists of triggers. + NetEvNBTrig*enext_; +}; + class NetEvWait : public NetProc { public: diff --git a/parse.y b/parse.y index cab94a7fb..7eaf1d20c 100644 --- a/parse.y +++ b/parse.y @@ -478,7 +478,7 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %token K_CONTRIBUTE %token K_PO_POS K_PO_NEG K_POW %token K_PSTAR K_STARP K_DOTSTAR -%token K_LOR K_LAND K_NAND K_NOR K_NXOR K_TRIGGER K_LEQUIV +%token K_LOR K_LAND K_NAND K_NOR K_NXOR K_TRIGGER K_NB_TRIGGER K_LEQUIV %token K_SCOPE_RES %token K_edge_descriptor @@ -692,7 +692,7 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %token K_TAND %nonassoc K_PLUS_EQ K_MINUS_EQ K_MUL_EQ K_DIV_EQ K_MOD_EQ K_AND_EQ K_OR_EQ -%nonassoc K_XOR_EQ K_LS_EQ K_RS_EQ K_RSS_EQ +%nonassoc K_XOR_EQ K_LS_EQ K_RS_EQ K_RSS_EQ K_NB_TRIGGER %right K_TRIGGER K_LEQUIV %right '?' ':' K_inside %left K_LOR @@ -6716,16 +6716,37 @@ statement_item /* This is roughly statement_item in the LRM */ } | K_TRIGGER hierarchy_identifier ';' { PTrigger*tmp = pform_new_trigger(@2, 0, *$2); - FILE_NAME(tmp, @1); delete $2; $$ = tmp; } | K_TRIGGER PACKAGE_IDENTIFIER K_SCOPE_RES hierarchy_identifier { PTrigger*tmp = pform_new_trigger(@4, $2, *$4); - FILE_NAME(tmp, @1); delete $4; $$ = tmp; } + /* FIXME: Does this need support for package resolution like above? */ + | K_NB_TRIGGER hierarchy_identifier ';' + { PNBTrigger*tmp = pform_new_nb_trigger(@2, 0, *$2); + delete $2; + $$ = tmp; + } + | K_NB_TRIGGER delay1 hierarchy_identifier ';' + { PNBTrigger*tmp = pform_new_nb_trigger(@3, $2, *$3); + delete $3; + $$ = tmp; + } + | K_NB_TRIGGER event_control hierarchy_identifier ';' + { PNBTrigger*tmp = pform_new_nb_trigger(@3, 0, *$3); + delete $3; + $$ = tmp; + yywarn(@1, "Sorry: ->> with event control is not currently supported."); + } + | K_NB_TRIGGER K_repeat '(' expression ')' event_control hierarchy_identifier ';' + { PNBTrigger*tmp = pform_new_nb_trigger(@7, 0, *$7); + delete $7; + $$ = tmp; + yywarn(@1, "Sorry: ->> with repeat event control is not currently supported."); + } | procedural_assertion_statement { $$ = $1; } diff --git a/pform.cc b/pform.cc index b91f61165..f5ec4ff2c 100644 --- a/pform.cc +++ b/pform.cc @@ -752,6 +752,24 @@ PTrigger* pform_new_trigger(const struct vlltype&loc, PPackage*pkg, return tmp; } +PNBTrigger* pform_new_nb_trigger(const struct vlltype&loc, + const list*dly, + const pform_name_t&name) +{ + if (gn_system_verilog()) + check_potential_imports(loc, name.front().name, false); + + PExpr*tmp_dly = 0; + if (dly) { + assert(dly->size() == 1); + tmp_dly = dly->front(); + } + + PNBTrigger*tmp = new PNBTrigger(name, tmp_dly); + FILE_NAME(tmp, loc); + return tmp; +} + PGenerate* pform_parent_generate(void) { return pform_cur_generate; diff --git a/pform.h b/pform.h index f4a8c04ed..8531fccdb 100644 --- a/pform.h +++ b/pform.h @@ -250,6 +250,9 @@ extern PEIdent* pform_new_ident(const struct vlltype&loc, const pform_name_t&nam extern PTrigger* pform_new_trigger(const struct vlltype&loc, PPackage*pkg, const pform_name_t&name); +extern PNBTrigger* pform_new_nb_trigger(const struct vlltype&loc, + const list*dly, + const pform_name_t&name); /* * Enter/exit name scopes. The push_scope function pushes the scope diff --git a/pform_dump.cc b/pform_dump.cc index 810a598e6..f103966d0 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -1263,6 +1263,13 @@ void PTrigger::dump(ostream&out, unsigned ind) const out << setw(ind) << "" << "-> " << event_ << ";" << endl; } +void PNBTrigger::dump(ostream&out, unsigned ind) const +{ + out << setw(ind) << "" << "->> "; + if (dly_) out << "#" << *dly_ << " "; + out << event_ << ";" << endl; +} + void PWhile::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "while (" << *cond_ << ")" << endl; diff --git a/t-dll-api.cc b/t-dll-api.cc index 16098996b..74d4250bc 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -2834,6 +2834,9 @@ extern "C" ivl_expr_t ivl_stmt_delay_expr(ivl_statement_t net) case IVL_ST_DELAYX: return net->u_.delayx_.expr; + case IVL_ST_NB_TRIGGER: + return net->u_.wait_.delay; + default: assert(0); return 0; @@ -2864,12 +2867,15 @@ extern "C" unsigned ivl_stmt_nevent(ivl_statement_t net) case IVL_ST_ASSIGN_NB: return net->u_.assign_.nevent; - case IVL_ST_WAIT: - return net->u_.wait_.nevent; + case IVL_ST_NB_TRIGGER: + return 1; case IVL_ST_TRIGGER: return 1; + case IVL_ST_WAIT: + return net->u_.wait_.nevent; + default: assert(0); } @@ -2887,6 +2893,14 @@ extern "C" ivl_event_t ivl_stmt_events(ivl_statement_t net, unsigned idx) else return net->u_.assign_.events[idx]; + case IVL_ST_NB_TRIGGER: + assert(idx == 0); + return net->u_.wait_.event; + + case IVL_ST_TRIGGER: + assert(idx == 0); + return net->u_.wait_.event; + case IVL_ST_WAIT: assert(idx < net->u_.wait_.nevent); if (net->u_.wait_.nevent == 1) @@ -2894,10 +2908,6 @@ extern "C" ivl_event_t ivl_stmt_events(ivl_statement_t net, unsigned idx) else return net->u_.wait_.events[idx]; - case IVL_ST_TRIGGER: - assert(idx == 0); - return net->u_.wait_.event; - default: assert(0); } diff --git a/t-dll-proc.cc b/t-dll-proc.cc index c4115277a..a5179dcd7 100644 --- a/t-dll-proc.cc +++ b/t-dll-proc.cc @@ -825,6 +825,38 @@ bool dll_target::proc_trigger(const NetEvTrig*net) } } + return true; +} + +bool dll_target::proc_nb_trigger(const NetEvNBTrig*net) +{ + assert(stmt_cur_); + assert(stmt_cur_->type_ == IVL_ST_NONE); + FILE_NAME(stmt_cur_, net); + + stmt_cur_->type_ = IVL_ST_NB_TRIGGER; + stmt_cur_->u_.wait_.nevent = 1; + stmt_cur_->u_.wait_.delay = 0; + + if (const NetExpr*expr = net->delay()) { + assert(expr_ == 0); + expr->expr_scan(this); + stmt_cur_->u_.wait_.delay = expr_; + expr_ = 0; + } + + /* 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(); + ivl_scope_t ev_scope = lookup_scope_(ev->scope()); + + 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) { + stmt_cur_->u_.wait_.event = ev_scope->event_[idx]; + break; + } + } return true; } diff --git a/t-dll.h b/t-dll.h index 5cd84542e..1c3e34151 100644 --- a/t-dll.h +++ b/t-dll.h @@ -128,6 +128,7 @@ struct dll_target : public target_t, public expr_scan_t { void proc_repeat(const NetRepeat*); void proc_stask(const NetSTask*); bool proc_trigger(const NetEvTrig*); + bool proc_nb_trigger(const NetEvNBTrig*); void proc_utask(const NetUTask*); bool proc_wait(const NetEvWait*); void proc_while(const NetWhile*); @@ -863,13 +864,14 @@ struct ivl_statement_s { ivl_scope_t def; } utask_; - struct { /* IVL_ST_TRIGGER IVL_ST_WAIT */ + struct { /* IVL_ST_TRIGGER IVL_ST_NB_TRIGGER IVL_ST_WAIT */ unsigned needs_t0_trigger; unsigned nevent; union { ivl_event_t event; ivl_event_t*events; }; + ivl_expr_t delay; ivl_statement_t stmt_; } wait_; diff --git a/target.cc b/target.cc index 15cc27c5c..17ef35255 100644 --- a/target.cc +++ b/target.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2016 Stephen Williams + * Copyright (c) 1998-2021 Stephen Williams * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -424,6 +424,13 @@ bool target_t::proc_trigger(const NetEvTrig*tr) return false; } +bool target_t::proc_nb_trigger(const NetEvNBTrig*tr) +{ + cerr << tr->get_fileline() << ": error: target (" << typeid(*this).name() + << "): Unhandled non-blocking event trigger." << endl; + return false; +} + void target_t::proc_stask(const NetSTask*) { cerr << "target (" << typeid(*this).name() << "): " diff --git a/target.h b/target.h index be39f7ca4..f4ada8c1b 100644 --- a/target.h +++ b/target.h @@ -1,7 +1,7 @@ #ifndef IVL_target_H #define IVL_target_H /* - * Copyright (c) 1998-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2021 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 @@ -143,6 +143,7 @@ struct target_t { virtual bool proc_release(const NetRelease*); virtual void proc_repeat(const NetRepeat*); virtual bool proc_trigger(const NetEvTrig*); + virtual bool proc_nb_trigger(const NetEvNBTrig*); virtual void proc_stask(const NetSTask*); virtual void proc_utask(const NetUTask*); virtual bool proc_wait(const NetEvWait*); diff --git a/tgt-stub/statement.c b/tgt-stub/statement.c index d52d8db4d..63a5fc8df 100644 --- a/tgt-stub/statement.c +++ b/tgt-stub/statement.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2004-2021 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 @@ -271,6 +271,39 @@ static void show_stmt_trigger(ivl_statement_t net, unsigned ind) fprintf(out, ";\n"); } +/* + * A non-blocking trigger statement is the "->> name;" syntax in Verilog, + * where a non-blocking trigger signal is sent to a named event. The trigger + * statement is actually a very simple object. + */ +static void show_stmt_nb_trigger(ivl_statement_t net, unsigned ind) +{ + unsigned cnt = ivl_stmt_nevent(net); + unsigned idx; + + fprintf(out, "%*s->>", ind, ""); + + ivl_expr_t delay = ivl_stmt_delay_expr(net); + if (delay) { + fprintf(out, " #("); + show_expression(ivl_stmt_delay_expr(net), ind+4); + fprintf(out, ")"); + } + + for (idx = 0 ; idx < cnt ; idx += 1) { + ivl_event_t event = ivl_stmt_events(net, idx); + fprintf(out, " %s", ivl_event_basename(event)); + } + + /* The compiler should make exactly one target event, so if we + find more or less, then print some error text. */ + if (cnt != 1) { + fprintf(out, " /* ERROR: Expect one target event, got %u */", cnt); + } + + fprintf(out, ";\n"); +} + /* * The wait statement contains simply an array of events to wait on, * and a sub-statement to execute when an event triggers. @@ -537,6 +570,10 @@ void show_statement(ivl_statement_t net, unsigned ind) show_stmt_trigger(net, ind); break; + case IVL_ST_NB_TRIGGER: + show_stmt_nb_trigger(net, ind); + break; + case IVL_ST_UTASK: fprintf(out, "%*scall task ...\n", ind, ""); break; diff --git a/tgt-vlog95/scope.c b/tgt-vlog95/scope.c index 4d0c8a71f..393b53c63 100644 --- a/tgt-vlog95/scope.c +++ b/tgt-vlog95/scope.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2020 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2010-2021 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -878,6 +878,7 @@ static unsigned has_func_disable(ivl_scope_t scope, ivl_statement_t stmt) case IVL_ST_STASK: case IVL_ST_UTASK: // this will be generated for a SV void function case IVL_ST_TRIGGER: + case IVL_ST_NB_TRIGGER: break; /* Look for a disable in each block statement. */ case IVL_ST_BLOCK: diff --git a/tgt-vlog95/stmt.c b/tgt-vlog95/stmt.c index a4ff8d237..1a14771df 100644 --- a/tgt-vlog95/stmt.c +++ b/tgt-vlog95/stmt.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2020 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2011-2021 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -1427,6 +1427,26 @@ static void emit_stmt_trigger(ivl_scope_t scope, ivl_statement_t stmt) fprintf(vlog_out, "\n"); } +static void emit_stmt_nb_trigger(ivl_scope_t scope, ivl_statement_t stmt) +{ + fprintf(vlog_out, "%*c->> ", get_indent(), ' '); + ivl_expr_t delay = ivl_stmt_delay_expr(stmt); + if (delay) { + fprintf(vlog_out, "#("); + emit_scaled_delayx(scope, delay, 1); + fprintf(vlog_out, ") "); + } + assert(ivl_stmt_nevent(stmt) == 1); + emit_event(scope, stmt); + fprintf(vlog_out, ";"); + emit_stmt_file_line(stmt); + fprintf(vlog_out, "\n"); + fprintf(stderr, "%s:%u: vlog95 sorry: wait fork is not currently " + "translated.\n", + ivl_stmt_file(stmt), ivl_stmt_lineno(stmt)); + vlog_errors += 1; +} + /* * A user defined task call with arguments is generated as a block with * input assignments, a simple call and then output assignments. This is @@ -1596,6 +1616,9 @@ void emit_stmt(ivl_scope_t scope, ivl_statement_t stmt) case IVL_ST_TRIGGER: emit_stmt_trigger(scope, stmt); break; + case IVL_ST_NB_TRIGGER: + emit_stmt_nb_trigger(scope, stmt); + break; case IVL_ST_UTASK: emit_stmt_utask(scope, stmt); break; diff --git a/tgt-vvp/vvp_process.c b/tgt-vvp/vvp_process.c index 21e554015..2abb7a025 100644 --- a/tgt-vvp/vvp_process.c +++ b/tgt-vvp/vvp_process.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2020 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2021 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 @@ -1284,21 +1284,8 @@ static int show_stmt_delay(ivl_statement_t net, ivl_scope_t sscope) return rc; } -/* - * The delayx statement is slightly more complex in that it is - * necessary to calculate the delay first. Load the calculated delay - * into and index register and use the %delayx instruction to do the - * actual delay. - */ -static int show_stmt_delayx(ivl_statement_t net, ivl_scope_t sscope) +static void draw_expr_into_idx(ivl_expr_t expr, int use_idx) { - int rc = 0; - ivl_expr_t expr = ivl_stmt_delay_expr(net); - ivl_statement_t stmt = ivl_stmt_sub_stmt(net); - - show_stmt_file_line(net, "Delay statement."); - - int use_idx = allocate_word(); switch (ivl_expr_value(expr)) { case IVL_VT_BOOL: @@ -1317,6 +1304,25 @@ static int show_stmt_delayx(ivl_statement_t net, ivl_scope_t sscope) default: assert(0); } +} + + +/* + * The delayx statement is slightly more complex in that it is + * necessary to calculate the delay first. Load the calculated delay + * into and index register and use the %delayx instruction to do the + * actual delay. + */ +static int show_stmt_delayx(ivl_statement_t net, ivl_scope_t sscope) +{ + int rc = 0; + ivl_expr_t expr = ivl_stmt_delay_expr(net); + ivl_statement_t stmt = ivl_stmt_sub_stmt(net); + + show_stmt_file_line(net, "Delay statement."); + + int use_idx = allocate_word(); + draw_expr_into_idx(expr, use_idx); fprintf(vvp_out, " %%delayx %d;\n", use_idx); clr_word(use_idx); @@ -1643,6 +1649,34 @@ static int show_stmt_trigger(ivl_statement_t net) return 0; } +/* + * The non-blocking trigger statement is straight forward. All we have to + * do is write a single bit of fake data to the event object. + */ +static int show_stmt_nb_trigger(ivl_statement_t net) +{ + ivl_event_t ev = ivl_stmt_events(net, 0); + assert(ev); + + show_stmt_file_line(net, "Non-blocking event trigger statement."); + + ivl_expr_t expr = ivl_stmt_delay_expr(net); + int use_idx = allocate_word(); + if (expr) { + draw_expr_into_idx(expr, use_idx); + } else { + fprintf(vvp_out, " %%ix/load %d, 0, 0;\n", use_idx); + } + + fprintf(vvp_out, " %%event/nb E_%p, %d;\n", ev, use_idx); + clr_word(use_idx); + // FIXME: VVP needs to be updated to correctly support %event/nb + fprintf(stderr, "%s:%u: vvp.tgt sorry: ->> is not currently supported.\n", + ivl_stmt_file(net), ivl_stmt_lineno(net)); + vvp_errors += 1; + return 0; +} + static int show_stmt_utask(ivl_statement_t net) { ivl_scope_t task = ivl_stmt_call(net); @@ -2383,6 +2417,10 @@ static int show_statement(ivl_statement_t net, ivl_scope_t sscope) rc += show_stmt_trigger(net); break; + case IVL_ST_NB_TRIGGER: + rc += show_stmt_nb_trigger(net); + break; + case IVL_ST_UTASK: rc += show_stmt_utask(net); break; diff --git a/vvp/codes.h b/vvp/codes.h index 41f706cc7..5c75d3855 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -1,7 +1,7 @@ #ifndef IVL_codes_H #define IVL_codes_H /* - * Copyright (c) 2001-2020 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2021 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 @@ -112,6 +112,7 @@ extern bool of_DUP_REAL(vthread_t thr, vvp_code_t code); extern bool of_DUP_VEC4(vthread_t thr, vvp_code_t code); extern bool of_END(vthread_t thr, vvp_code_t code); extern bool of_EVENT(vthread_t thr, vvp_code_t code); +extern bool of_EVENT_NB(vthread_t thr, vvp_code_t code); extern bool of_EVCTL(vthread_t thr, vvp_code_t code); extern bool of_EVCTLC(vthread_t thr, vvp_code_t code); extern bool of_EVCTLI(vthread_t thr, vvp_code_t code); diff --git a/vvp/compile.cc b/vvp/compile.cc index bb21586ee..5626103b0 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2020 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2021 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 @@ -167,7 +167,8 @@ static const struct opcode_table_s opcode_table[] = { { "%evctl/c",of_EVCTLC, 0, {OA_NONE, OA_NONE, 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} }, - { "%event", of_EVENT, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, + { "%event", of_EVENT, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, + { "%event/nb", of_EVENT_NB, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, { "%flag_get/vec4", of_FLAG_GET_VEC4, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%flag_inv", of_FLAG_INV, 1, {OA_BIT1, OA_NONE, OA_NONE} }, { "%flag_mov", of_FLAG_MOV, 2, {OA_BIT1, OA_BIT2, OA_NONE} }, diff --git a/vvp/opcodes.txt b/vvp/opcodes.txt index f2bd3d0a2..97c7a9652 100644 --- a/vvp/opcodes.txt +++ b/vvp/opcodes.txt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2020 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2021 Stephen Williams (steve@icarus.com) * */ @@ -517,6 +517,7 @@ that this information has been cleared. You can get an assert if this information is not managed correctly. * %event +* %event/nb This instruction is used to send a pulse to an event object. The is an event variable. This instruction simply writes diff --git a/vvp/schedule.cc b/vvp/schedule.cc index 95caeccf5..8a0f04091 100644 --- a/vvp/schedule.cc +++ b/vvp/schedule.cc @@ -892,6 +892,19 @@ void schedule_propagate_vector(vvp_net_t*net, schedule_event_(cur, delay, SEQ_NBASSIGN); } +// FIXME: This needs to create a non-blocking event, but only one per time slot. +// Is schedule_event_ or execution actually filtering since the net is +// already X because this is not triggering? +void schedule_propagate_event(vvp_net_t*net, + vvp_time64_t delay) +{ + vvp_vector4_t tmp (1, BIT4_X); + struct propagate_vector4_event_s*cur + = new struct propagate_vector4_event_s(tmp); + cur->net = net; + schedule_event_(cur, delay, SEQ_NBASSIGN); +} + void schedule_assign_array_word(vvp_array_t mem, unsigned word_addr, unsigned off, diff --git a/vvp/schedule.h b/vvp/schedule.h index f932fa226..70cace8cd 100644 --- a/vvp/schedule.h +++ b/vvp/schedule.h @@ -1,7 +1,7 @@ #ifndef IVL_schedule_H #define IVL_schedule_H /* - * Copyright (c) 2001-2020 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2021 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 @@ -75,9 +75,15 @@ extern void schedule_force_vector(vvp_net_t*net, * Create an event to propagate the output of a net. */ extern void schedule_propagate_vector(vvp_net_t*ptr, - vvp_time64_t delay, + vvp_time64_t delay, const vvp_vector4_t&val); +/* + * Create an event to propagate the output of an event. + */ +extern void schedule_propagate_event(vvp_net_t*ptr, + vvp_time64_t delay); + /* * This is very similar to schedule_assign_vector, but generates an * event in the active queue. It is used at link time to assign a diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 8e47312b8..cb583a157 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -3106,6 +3106,18 @@ bool of_EVENT(vthread_t thr, vvp_code_t cp) return true; } +/* + * %event/nb , + */ +bool of_EVENT_NB(vthread_t thr, vvp_code_t cp) +{ + vvp_time64_t delay; + + delay = thr->words[cp->bit_idx[0]].w_uint; + schedule_propagate_event(cp->net, delay); + return true; +} + bool of_EVCTL(vthread_t thr, vvp_code_t cp) { assert(thr->event == 0 && thr->ecount == 0);