diff --git a/Statement.cc b/Statement.cc index 1b7e4e389..979a11a16 100644 --- a/Statement.cc +++ b/Statement.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2008,2010,2012-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2013 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 @@ -247,6 +247,17 @@ PDisable::~PDisable() { } +PDoWhile::PDoWhile(PExpr*ex, Statement*st) +: cond_(ex), statement_(st) +{ +} + +PDoWhile::~PDoWhile() +{ + delete cond_; + delete statement_; +} + PEventStatement::PEventStatement(const svector&ee) : expr_(ee), statement_(0) { @@ -360,8 +371,8 @@ PTrigger::~PTrigger() { } -PWhile::PWhile(PExpr*e1, Statement*st) -: cond_(e1), statement_(st) +PWhile::PWhile(PExpr*ex, Statement*st) +: cond_(ex), statement_(st) { } diff --git a/Statement.h b/Statement.h index 510298e43..31468bdf1 100644 --- a/Statement.h +++ b/Statement.h @@ -336,6 +336,22 @@ class PDisable : public Statement { pform_name_t scope_; }; +class PDoWhile : public Statement { + + public: + PDoWhile(PExpr*ex, Statement*st); + ~PDoWhile(); + + virtual NetProc* elaborate(Design*des, NetScope*scope) const; + virtual void elaborate_scope(Design*des, NetScope*scope) const; + virtual void elaborate_sig(Design*des, NetScope*scope) const; + virtual void dump(ostream&out, unsigned ind) const; + + private: + PExpr*cond_; + Statement*statement_; +}; + /* * The event statement represents the event delay in behavioral * code. It comes from such things as: @@ -499,7 +515,7 @@ class PTrigger : public Statement { class PWhile : public Statement { public: - PWhile(PExpr*e1, Statement*st); + PWhile(PExpr*ex, Statement*st); ~PWhile(); virtual NetProc* elaborate(Design*des, NetScope*scope) const; diff --git a/design_dump.cc b/design_dump.cc index 290b8428b..fb88272cd 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2013 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 @@ -1030,6 +1030,13 @@ void NetDisable::dump(ostream&o, unsigned ind) const << "/* " << get_fileline() << " */" << endl; } +void NetDoWhile::dump(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << "do" << endl; + proc_->dump(o, ind+3); + o << setw(ind) << "" << "while (" << *cond_ << ");" << endl; +} + void NetEvProbe::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << ""; diff --git a/elab_scope.cc b/elab_scope.cc index 7b0aa224a..f9753fa77 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -1795,6 +1795,17 @@ void PDelayStatement::elaborate_scope(Design*des, NetScope*scope) const statement_ -> elaborate_scope(des, scope); } +/* + * Statements that contain a further statement but do not + * intrinsically add a scope need to elaborate_scope the contained + * statement. + */ +void PDoWhile::elaborate_scope(Design*des, NetScope*scope) const +{ + if (statement_) + statement_ -> elaborate_scope(des, scope); +} + /* * Statements that contain a further statement but do not * intrinsically add a scope need to elaborate_scope the contained diff --git a/elab_sig.cc b/elab_sig.cc index de2ff4345..a4d812566 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2012 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -755,6 +755,12 @@ void PDelayStatement::elaborate_sig(Design*des, NetScope*scope) const statement_->elaborate_sig(des, scope); } +void PDoWhile::elaborate_sig(Design*des, NetScope*scope) const +{ + if (statement_) + statement_->elaborate_sig(des, scope); +} + void PEventStatement::elaborate_sig(Design*des, NetScope*scope) const { if (statement_) diff --git a/elaborate.cc b/elaborate.cc index 48b016172..b9497666e 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -3718,6 +3718,18 @@ NetProc* PDisable::elaborate(Design*des, NetScope*scope) const return obj; } +/* + * The do/while loop is fairly directly represented in the netlist. + */ +NetProc* PDoWhile::elaborate(Design*des, NetScope*scope) const +{ + NetExpr*tmp = elab_and_eval(des, scope, cond_, -1); + tmp->set_line(*this); + NetDoWhile*loop = new NetDoWhile(tmp, statement_->elaborate(des, scope)); + loop->set_line(*this); + return loop; +} + /* * An event statement is an event delay of some sort, attached to a * statement. Some Verilog examples are: diff --git a/emit.cc b/emit.cc index 227ccabb0..e58b7e098 100644 --- a/emit.cc +++ b/emit.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2013 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 @@ -261,6 +261,17 @@ bool NetDisable::emit_proc(struct target_t*tgt) const return tgt->proc_disable(this); } +bool NetDoWhile::emit_proc(struct target_t*tgt) const +{ + tgt->proc_do_while(this); + return true; +} + +void NetDoWhile::emit_proc_recurse(struct target_t*tgt) const +{ + proc_->emit_proc(tgt); +} + bool NetForce::emit_proc(struct target_t*tgt) const { return tgt->proc_force(this); @@ -318,6 +329,11 @@ bool NetWhile::emit_proc(struct target_t*tgt) const return true; } +void NetWhile::emit_proc_recurse(struct target_t*tgt) const +{ + proc_->emit_proc(tgt); +} + void NetBlock::emit_recurse(struct target_t*tgt) const { if (last_ == 0) @@ -459,11 +475,6 @@ bool NetScope::emit_defs(struct target_t*tgt) const return flag; } -void NetWhile::emit_proc_recurse(struct target_t*tgt) const -{ - proc_->emit_proc(tgt); -} - int Design::emit(struct target_t*tgt) const { int rc = 0; diff --git a/ivl_target.h b/ivl_target.h index c8af23946..130bcbdc3 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -402,6 +402,7 @@ typedef enum ivl_statement_type_e { IVL_ST_DELAY = 11, IVL_ST_DELAYX = 12, IVL_ST_DISABLE = 13, + IVL_ST_DO_WHILE = 30, IVL_ST_FORCE = 14, IVL_ST_FOREVER = 15, IVL_ST_FORK = 16, diff --git a/net_func_eval.cc b/net_func_eval.cc index 3777328d6..b877cd936 100644 --- a/net_func_eval.cc +++ b/net_func_eval.cc @@ -562,6 +562,49 @@ bool NetDisable::evaluate_function(const LineInfo&, return true; } +bool NetDoWhile::evaluate_function(const LineInfo&loc, + map&context_map) const +{ + bool flag = true; + + if (debug_eval_tree) { + cerr << get_fileline() << ": NetDoWhile::evaluate_function: " + << "Start loop" << endl; + } + + while (!disable) { + // Evaluate the statement. + flag = proc_->evaluate_function(loc, context_map); + if (! flag) + break; + + // Evaluate the condition expression to try and get the + // condition for the loop. + NetExpr*cond = cond_->evaluate_function(loc, context_map); + if (cond == 0) { + flag = false; + break; + } + + NetEConst*cond_const = dynamic_cast (cond); + ivl_assert(loc, cond_const); + + long val = cond_const->value().as_long(); + delete cond; + + // If the condition is false, then the loop is done. + if (val == 0) + break; + } + + if (debug_eval_tree) { + cerr << get_fileline() << ": NetDoWhile::evaluate_function: " + << "Done loop, flag=" << (flag?"true":"false") << endl; + } + + return flag; +} + bool NetForever::evaluate_function(const LineInfo&loc, map&context_map) const { diff --git a/net_nex_input.cc b/net_nex_input.cc index 1a1a1a179..7b83577c4 100644 --- a/net_nex_input.cc +++ b/net_nex_input.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2013 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 @@ -374,6 +374,15 @@ NexusSet* NetCondit::nex_input(bool rem_out) return result; } +NexusSet* NetDoWhile::nex_input(bool rem_out) +{ + NexusSet*result = proc_->nex_input(rem_out); + NexusSet*tmp = cond_->nex_input(rem_out); + result->add(*tmp); + delete tmp; + return result; +} + NexusSet* NetForce::nex_input(bool) { cerr << get_fileline() << ": internal warning: NetForce::nex_input()" diff --git a/net_nex_output.cc b/net_nex_output.cc index 534fe0441..5b3f4b1ca 100644 --- a/net_nex_output.cc +++ b/net_nex_output.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2013 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 @@ -117,6 +117,12 @@ void NetCondit::nex_output(NexusSet&out) else_->nex_output(out); } +void NetDoWhile::nex_output(NexusSet&out) +{ + if (proc_ != 0) + proc_->nex_output(out); +} + void NetEvWait::nex_output(NexusSet&out) { assert(statement_); diff --git a/netlist.cc b/netlist.cc index d82036e2d..e3f876c88 100644 --- a/netlist.cc +++ b/netlist.cc @@ -2810,6 +2810,14 @@ DelayType NetCondit::delay_type() const return combine_delays(if_type, el_type); } +/* + * A do/while will execute the body at least once. + */ +DelayType NetDoWhile::delay_type() const +{ + return proc_->delay_type(); +} + DelayType NetEvWait::delay_type() const { return DEFINITE_DELAY; diff --git a/netlist.h b/netlist.h index 6c337958b..e945d7fca 100644 --- a/netlist.h +++ b/netlist.h @@ -2913,6 +2913,34 @@ class NetDisable : public NetProc { NetDisable& operator= (const NetDisable&); }; +/* + * The do/while statement is a condition that is tested at the end of + * each iteration, and a statement (a NetProc) that is executed once and + * then again as long as the condition is true. + */ +class NetDoWhile : public NetProc { + + public: + NetDoWhile(NetExpr*c, NetProc*p) + : cond_(c), proc_(p) { } + + const NetExpr*expr() const { return cond_; } + + void emit_proc_recurse(struct target_t*) const; + + virtual NexusSet* nex_input(bool rem_out = true); + virtual void nex_output(NexusSet&); + virtual bool emit_proc(struct target_t*) const; + virtual void dump(ostream&, unsigned ind) const; + virtual DelayType delay_type() const; + virtual bool evaluate_function(const LineInfo&loc, + map&ctx) const; + + private: + NetExpr* cond_; + NetProc*proc_; +}; + /* * A NetEvent is an object that represents an event object, that is * objects declared like so in Verilog: diff --git a/parse.y b/parse.y index a9230f221..0c8c6918d 100644 --- a/parse.y +++ b/parse.y @@ -1289,6 +1289,12 @@ loop_statement /* IEEE1800-2005: A.6.8 */ $$ = tmp; } + | K_do statement_or_null K_while '(' expression ')' ';' + { PDoWhile*tmp = new PDoWhile($5, $2); + FILE_NAME(tmp, @1); + $$ = tmp; + } + | K_foreach '(' IDENTIFIER '[' loop_variables ']' ')' statement_or_null { yyerror(@1, "sorry: foreach loops not supported"); delete[]$3; @@ -1321,6 +1327,11 @@ loop_statement /* IEEE1800-2005: A.6.8 */ yyerror(@1, "error: Error in while loop condition."); } + | K_do statement_or_null K_while '(' error ')' ';' + { $$ = 0; + yyerror(@1, "error: Error in do/while loop condition."); + } + | K_foreach '(' IDENTIFIER '[' error ']' ')' statement_or_null { $$ = 0; yyerror(@4, "error: Errors in foreach loop variables list."); diff --git a/pform_dump.cc b/pform_dump.cc index 4ab900fb1..5389b6e02 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -840,6 +840,13 @@ void PDisable::dump(ostream&out, unsigned ind) const << get_fileline() << " */" << endl; } +void PDoWhile::dump(ostream&out, unsigned ind) const +{ + out << setw(ind) << "" << "do" << endl; + statement_->dump(out, ind+3); + out << setw(ind) << "" << "while (" << *cond_ << ");" << endl; +} + void PEventStatement::dump(ostream&out, unsigned ind) const { if (expr_.count() == 0) { diff --git a/t-dll-api.cc b/t-dll-api.cc index 902930314..b63db6f0b 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -2557,6 +2557,7 @@ extern "C" ivl_expr_t ivl_stmt_cond_expr(ivl_statement_t net) case IVL_ST_CASEZ: return net->u_.case_.cond; + case IVL_ST_DO_WHILE: case IVL_ST_REPEAT: case IVL_ST_WHILE: return net->u_.while_.cond_; @@ -2819,6 +2820,7 @@ extern "C" ivl_statement_t ivl_stmt_sub_stmt(ivl_statement_t net) return net->u_.forever_.stmt_; case IVL_ST_WAIT: return net->u_.wait_.stmt_; + case IVL_ST_DO_WHILE: case IVL_ST_REPEAT: case IVL_ST_WHILE: return net->u_.while_.stmt_; diff --git a/t-dll-proc.cc b/t-dll-proc.cc index 0cf6fe8bb..292d7cc7e 100644 --- a/t-dll-proc.cc +++ b/t-dll-proc.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2013 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 @@ -618,6 +618,31 @@ bool dll_target::proc_disable(const NetDisable*net) return true; } +void dll_target::proc_do_while(const NetDoWhile*net) +{ + assert(stmt_cur_); + assert(stmt_cur_->type_ == IVL_ST_NONE); + FILE_NAME(stmt_cur_, net); + + stmt_cur_->type_ = IVL_ST_DO_WHILE; + stmt_cur_->u_.while_.stmt_ = (struct ivl_statement_s*) + calloc(1, sizeof(struct ivl_statement_s)); + + assert(expr_ == 0); + net->expr()->expr_scan(this); + stmt_cur_->u_.while_.cond_ = expr_; + expr_ = 0; + + /* Now generate the statement of the do/while loop. We know it is + a single statement, and we know that the + emit_proc_recurse() will call emit_proc() for it. */ + + ivl_statement_t save_cur_ = stmt_cur_; + stmt_cur_ = save_cur_->u_.while_.stmt_; + net->emit_proc_recurse(this); + stmt_cur_ = save_cur_; +} + bool dll_target::proc_force(const NetForce*net) { diff --git a/t-dll.h b/t-dll.h index 810daddcd..499fdad84 100644 --- a/t-dll.h +++ b/t-dll.h @@ -118,6 +118,7 @@ struct dll_target : public target_t, public expr_scan_t { bool proc_deassign(const NetDeassign*); bool proc_delay(const NetPDelay*); bool proc_disable(const NetDisable*); + void proc_do_while(const NetDoWhile*); bool proc_force(const NetForce*); void proc_forever(const NetForever*); void proc_free(const NetFree*); diff --git a/target.cc b/target.cc index f3a3acd79..d20ba32f5 100644 --- a/target.cc +++ b/target.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2011 Stephen Williams + * Copyright (c) 1998-2013 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 @@ -364,6 +364,13 @@ bool target_t::proc_disable(const NetDisable*obj) return false; } +void target_t::proc_do_while(const NetDoWhile*net) +{ + cerr << "target (" << typeid(*this).name() << "): " + "Unhandled do/while:" << endl; + net->dump(cerr, 6); +} + bool target_t::proc_force(const NetForce*) { cerr << "target (" << typeid(*this).name() << "): " diff --git a/target.h b/target.h index d65ffb50b..a70047d73 100644 --- a/target.h +++ b/target.h @@ -1,7 +1,7 @@ #ifndef __target_H #define __target_H /* - * Copyright (c) 1998-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2013 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 @@ -130,6 +130,7 @@ struct target_t { virtual bool proc_deassign(const NetDeassign*); virtual bool proc_delay(const NetPDelay*); virtual bool proc_disable(const NetDisable*); + virtual void proc_do_while(const NetDoWhile*); virtual bool proc_force(const NetForce*); virtual void proc_forever(const NetForever*); virtual void proc_free(const NetFree*); diff --git a/tgt-stub/statement.c b/tgt-stub/statement.c index eed16f055..73f2ae2e0 100644 --- a/tgt-stub/statement.c +++ b/tgt-stub/statement.c @@ -388,6 +388,13 @@ void show_statement(ivl_statement_t net, unsigned ind) show_stmt_disable(net, ind); break; + case IVL_ST_DO_WHILE: + fprintf(out, "%*sdo\n", ind, ""); + show_statement(ivl_stmt_sub_stmt(net), ind+2); + fprintf(out, "%*swhile\n", ind, ""); + show_expression(ivl_stmt_cond_expr(net), ind+4); + break; + case IVL_ST_FORCE: show_stmt_force(net, ind); break; diff --git a/tgt-vlog95/stmt.c b/tgt-vlog95/stmt.c index 157f5f40b..0283ae621 100644 --- a/tgt-vlog95/stmt.c +++ b/tgt-vlog95/stmt.c @@ -1269,6 +1269,13 @@ void emit_stmt(ivl_scope_t scope, ivl_statement_t stmt) case IVL_ST_DISABLE: emit_stmt_disable(scope, stmt); break; + case IVL_ST_DO_WHILE: + fprintf(stderr, "%s:%u: vlog95 sorry: do/while is not " + "currently translated.\n", + ivl_stmt_file(stmt), + ivl_stmt_lineno(stmt)); + vlog_errors += 1; + break; case IVL_ST_FORCE: emit_stmt_force(scope, stmt); break; diff --git a/tgt-vvp/vvp_process.c b/tgt-vvp/vvp_process.c index bb3059744..81c33d340 100644 --- a/tgt-vvp/vvp_process.c +++ b/tgt-vvp/vvp_process.c @@ -1334,6 +1334,71 @@ static int show_stmt_disable(ivl_statement_t net, ivl_scope_t sscope) return rc; } +static struct vector_info reduction_or(struct vector_info cvec) +{ + struct vector_info result; + + switch (cvec.base) { + case 0: + result.base = 0; + result.wid = 1; + break; + case 1: + result.base = 1; + result.wid = 1; + break; + case 2: + case 3: + result.base = 0; + result.wid = 1; + break; + default: + clr_vector(cvec); + result.base = allocate_vector(1); + result.wid = 1; + assert(result.base); + fprintf(vvp_out, " %%or/r %u, %u, %u;\n", result.base, + cvec.base, cvec.wid); + break; + } + + return result; +} + +static int show_stmt_do_while(ivl_statement_t net, ivl_scope_t sscope) +{ + int rc = 0; + struct vector_info cvec; + + unsigned top_label = local_count++; + + show_stmt_file_line(net, "Do/While statement."); + + /* Start the loop. The top of the loop starts a basic block + because it can be entered from above or from the bottom of + the loop. */ + fprintf(vvp_out, "T_%u.%u ;\n", thread_count, top_label); + clear_expression_lookaside(); + + /* Draw the body of the loop. */ + rc += show_statement(ivl_stmt_sub_stmt(net), sscope); + + /* Draw the evaluation of the condition expression, and test + the result. If the expression evaluates to true, then + branch to the top label. */ + cvec = draw_eval_expr(ivl_stmt_cond_expr(net), STUFF_OK_XZ|STUFF_OK_47); + if (cvec.wid > 1) + cvec = reduction_or(cvec); + + fprintf(vvp_out, " %%jmp/1 T_%u.%u, %u;\n", + thread_count, top_label, cvec.base); + if (cvec.base >= 8) + clr_vector(cvec); + + clear_expression_lookaside(); + return rc; +} + static int show_stmt_force(ivl_statement_t net) { ivl_expr_t rval; @@ -1645,37 +1710,6 @@ static int show_stmt_wait(ivl_statement_t net, ivl_scope_t sscope) return show_statement(ivl_stmt_sub_stmt(net), sscope); } -static struct vector_info reduction_or(struct vector_info cvec) -{ - struct vector_info result; - - switch (cvec.base) { - case 0: - result.base = 0; - result.wid = 1; - break; - case 1: - result.base = 1; - result.wid = 1; - break; - case 2: - case 3: - result.base = 0; - result.wid = 1; - break; - default: - clr_vector(cvec); - result.base = allocate_vector(1); - result.wid = 1; - assert(result.base); - fprintf(vvp_out, " %%or/r %u, %u, %u;\n", result.base, - cvec.base, cvec.wid); - break; - } - - return result; -} - static int show_stmt_while(ivl_statement_t net, ivl_scope_t sscope) { int rc = 0; @@ -2184,6 +2218,10 @@ static int show_statement(ivl_statement_t net, ivl_scope_t sscope) rc += show_stmt_disable(net, sscope); break; + case IVL_ST_DO_WHILE: + rc += show_stmt_do_while(net, sscope); + break; + case IVL_ST_FORCE: rc += show_stmt_force(net); break;