diff --git a/tgt-vhdl/process.cc b/tgt-vhdl/process.cc index 9440b25cf..93e2fba8f 100644 --- a/tgt-vhdl/process.cc +++ b/tgt-vhdl/process.cc @@ -57,15 +57,12 @@ static int generate_vhdl_process(vhdl_entity *ent, ivl_process_t proc) // However, if no statements were added to the container // by draw_stmt, don't bother adding a wait as `emit' // will optimise the process out of the output - if (ivl_process_type(proc) == IVL_PR_INITIAL) { - // Get rid of any useless `wait for 0 ns's at the end of the process - prune_wait_for_0(vhdl_proc->get_container()); - - // The above pruning might have removed all logic from the process - if (!vhdl_proc->get_container()->empty()) { - vhdl_wait_stmt *wait = new vhdl_wait_stmt(); - vhdl_proc->get_container()->add_stmt(wait); - } + bool is_initial = ivl_process_type(proc) == IVL_PR_INITIAL; + bool is_empty = vhdl_proc->get_container()->empty(); + + if (is_initial && !is_empty) { + vhdl_wait_stmt *wait = new vhdl_wait_stmt(); + vhdl_proc->get_container()->add_stmt(wait); } // Add a comment indicating where it came from diff --git a/tgt-vhdl/stmt.cc b/tgt-vhdl/stmt.cc index 774ce9d06..7ac6e6fc7 100644 --- a/tgt-vhdl/stmt.cc +++ b/tgt-vhdl/stmt.cc @@ -1,7 +1,7 @@ /* * VHDL code generation for statements. * - * Copyright (C) 2008-2009 Nick Gasson (nick@nickg.me.uk) + * Copyright (C) 2008-2010 Nick Gasson (nick@nickg.me.uk) * * 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 @@ -131,27 +131,6 @@ static int draw_noop(vhdl_procedural *proc, stmt_container *container, return 0; } -/* - * The VHDL code generator inserts `wait for 0 ns' after each - * not-last-in-block blocking assignment. - * If this is immediately followed by another `wait for ...' then - * we might as well not emit the first zero-time wait. - */ -void prune_wait_for_0(stmt_container *container) -{ - vhdl_wait_stmt *wait0; - stmt_container::stmt_list_t &stmts = container->get_stmts(); - while (stmts.size() > 0 - && (wait0 = dynamic_cast(stmts.back()))) { - if (wait0->get_type() == VHDL_WAIT_FOR0) { - delete wait0; - stmts.pop_back(); - } - else - break; - } -} - static vhdl_var_ref *make_assign_lhs(ivl_lval_t lval, vhdl_scope *scope) { ivl_signal_t sig = ivl_lval_sig(lval); @@ -263,12 +242,68 @@ bool check_valid_assignment(vhdl_decl::assign_type_t atype, vhdl_procedural *pro return true; } +// Generate a "wait for 0 ns" statement to emulate the behaviour of +// Verilog blocking assignment using VHDL signals. This is only generated +// if we read from the target of a blocking assignment in the same +// process (i.e. it is only generated when required, not for every +// blocking assignment). An example: +// +// begin +// x = 5; +// if (x == 2) +// y = 7; +// end +// +// Becomes: +// +// x <= 5; +// wait for 0 ns; -- Required to implement assignment semantics +// if x = 2 then +// y <= 7; -- No need for wait here, not read +// end if; +// +static void emit_wait_for_0(vhdl_procedural *proc, + stmt_container *container, + ivl_statement_t stmt, + vhdl_expr *expr) +{ + vhdl_var_set_t read; + expr->find_vars(read); + + bool need_wait_for_0 = false; + for (vhdl_var_set_t::const_iterator it = read.begin(); + it != read.end(); ++it) { + if (proc->is_blocking_target(*it)) + need_wait_for_0 = true; + } + + stmt_container::stmt_list_t &stmts = container->get_stmts(); + bool last_was_wait = + !stmts.empty() && dynamic_cast(stmts.back()); + + if (need_wait_for_0 && !last_was_wait) { + debug_msg("Generated wait-for-0 for %s:%d", + ivl_stmt_file(stmt), ivl_stmt_lineno(stmt)); + + vhdl_seq_stmt *wait = new vhdl_wait_stmt(VHDL_WAIT_FOR0); + + ostringstream ss; + ss << "Read target of blocking assignment (" + << ivl_stmt_file(stmt) + << ":" << ivl_stmt_lineno(stmt) << ")"; + wait->set_comment(ss.str()); + + container->add_stmt(wait); + proc->added_wait_stmt(); + } +} + // Generate an assignment of type T for the Verilog statement stmt. // If a statement was generated then `assign_type' will contain the // type of assignment that was generated; this should be initialised // to some sensible default. void make_assignment(vhdl_procedural *proc, stmt_container *container, - ivl_statement_t stmt, bool blocking, + ivl_statement_t stmt, bool emul_blocking, vhdl_decl::assign_type_t& assign_type) { list lvals; @@ -286,21 +321,33 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container, if (rhs == NULL) return; + emit_wait_for_0(proc, container, stmt, rhs); + if (rhs2) + emit_wait_for_0(proc, container, stmt, rhs2); + if (lvals.size() == 1) { vhdl_var_ref *lhs = lvals.front(); rhs = rhs->cast(lhs->get_type()); ivl_expr_t i_delay; vhdl_expr *after = NULL; - if ((i_delay = ivl_stmt_delay_expr(stmt)) != NULL) + if ((i_delay = ivl_stmt_delay_expr(stmt)) != NULL) { after = translate_time_expr(i_delay); + if (after == NULL) + return; + + emit_wait_for_0(proc, container, stmt, after); + } // Find the declaration of the LHS so we know what type // of assignment statement to generate (is it a signal, // a variable, etc?) vhdl_decl *decl = proc->get_scope()->get_decl(lhs->get_name()); assign_type = decl->assignment_type(); - + + if (assign_type == vhdl_decl::ASSIGN_NONBLOCK && emul_blocking) + proc->add_blocking_target(lhs); + // A small optimisation is to expand ternary RHSs into an // if statement (eliminates a function call and produces // more idiomatic code) @@ -313,6 +360,8 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container, if (NULL == test) return; + emit_wait_for_0(proc, container, stmt, test); + if (!check_valid_assignment(decl->assignment_type(), proc, stmt)) return; @@ -368,6 +417,7 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container, decl->set_initial(NULL); // Default initial value else { decl->set_initial(rhs); + proc->get_scope()->hoisted_initialiser(true); delete lhs; return; } @@ -380,7 +430,7 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container, assign_for(decl->assignment_type(), lhs, rhs); container->add_stmt(a); - if (after != NULL) + if (after != NULL) a->set_after(after); } else { @@ -408,8 +458,13 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container, ivl_expr_t i_delay; vhdl_expr *after = NULL; - if ((i_delay = ivl_stmt_delay_expr(stmt)) != NULL) + if ((i_delay = ivl_stmt_delay_expr(stmt)) != NULL) { after = translate_time_expr(i_delay); + if (after == NULL) + return; + + emit_wait_for_0(proc, container, stmt, after); + } // Find the declaration of the LHS so we know what type // of assignment statement to generate (is it a signal, @@ -428,10 +483,11 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container, container->add_stmt(a); width_so_far += lval_width; + + if (assign_type == vhdl_decl::ASSIGN_NONBLOCK && emul_blocking) + proc->add_blocking_target(*it); } } - - return; } /* @@ -454,26 +510,10 @@ static int draw_assign(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt, bool is_last) { vhdl_decl::assign_type_t assign_type = vhdl_decl::ASSIGN_NONBLOCK; - if (proc->get_scope()->allow_signal_assignment()) { - // Blocking assignment is implemented as non-blocking assignment - // followed by a zero-time wait - // This follows the Verilog semantics fairly closely. - - make_assignment(proc, container, stmt, false, assign_type); - - // Don't generate a zero-wait if either: - // a) this is the last statement in the process - // c) a blocking assignment was generated - if (!is_last && assign_type == vhdl_decl::ASSIGN_NONBLOCK) { - prune_wait_for_0(container); - container->add_stmt - (new vhdl_wait_stmt(VHDL_WAIT_FOR0)); - proc->added_wait_stmt(); - } - } - else - make_assignment(proc, container, stmt, true, assign_type); + bool emulate_blocking = proc->get_scope()->allow_signal_assignment(); + make_assignment(proc, container, stmt, emulate_blocking, assign_type); + return 0; } @@ -501,9 +541,7 @@ static int draw_delay(vhdl_procedural *proc, stmt_container *container, if (NULL == time) return 1; } - - prune_wait_for_0(container); - + ivl_statement_t sub_stmt = ivl_stmt_sub_stmt(stmt); vhdl_wait_stmt *wait = new vhdl_wait_stmt(VHDL_WAIT_FOR, time); @@ -523,7 +561,7 @@ static int draw_delay(vhdl_procedural *proc, stmt_container *container, // Any further assignments occur after simulation time 0 // so they cannot be used to initialise signal declarations // (if this scope is an initial process) - proc->get_scope()->set_initializing(false); + proc->get_scope()->set_initializing(false); return 0; } @@ -743,9 +781,12 @@ static int draw_wait(vhdl_procedural *_proc, stmt_container *container, // If this container is the top-level statement (i.e. it is the // first thing inside a process) then we can extract these - // events out into the sensitivity list - bool is_top_level = container == proc->get_container() - && container->empty(); + // events out into the sensitivity list as long as we haven't + // promoted any preceding assignments to initialisers + bool is_top_level = + container == proc->get_container() + && container->empty() + && !proc->get_scope()->hoisted_initialiser(); // See if this can be implemented in a more idomatic way before we // fall back on the generic translation @@ -875,9 +916,12 @@ static int draw_if(vhdl_procedural *proc, stmt_container *container, vhdl_expr *test = translate_expr(ivl_stmt_cond_expr(stmt)); if (NULL == test) return 1; + + emit_wait_for_0(proc, container, stmt, test); vhdl_if_stmt *vhdif = new vhdl_if_stmt(test); - + container->add_stmt(vhdif); + ivl_statement_t cond_true_stmt = ivl_stmt_cond_true(stmt); if (cond_true_stmt) draw_stmt(proc, vhdif->get_then_container(), cond_true_stmt, is_last); @@ -886,8 +930,6 @@ static int draw_if(vhdl_procedural *proc, stmt_container *container, if (cond_false_stmt) draw_stmt(proc, vhdif->get_else_container(), cond_false_stmt, is_last); - container->add_stmt(vhdif); - return 0; } @@ -1403,8 +1445,12 @@ int draw_while(vhdl_procedural *proc, stmt_container *container, vhdl_type boolean(VHDL_TYPE_BOOLEAN); test = test->cast(&boolean); + emit_wait_for_0(proc, container, stmt, test); + vhdl_while_stmt *loop = new vhdl_while_stmt(test); draw_stmt(proc, loop->get_container(), ivl_stmt_sub_stmt(stmt)); + + emit_wait_for_0(proc, loop->get_container(), stmt, test); container->add_stmt(loop); return 0; diff --git a/tgt-vhdl/vhdl_syntax.cc b/tgt-vhdl/vhdl_syntax.cc index b1d87b832..a65bdf276 100644 --- a/tgt-vhdl/vhdl_syntax.cc +++ b/tgt-vhdl/vhdl_syntax.cc @@ -1,7 +1,7 @@ /* * VHDL abstract syntax elements. * - * Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk) + * Copyright (C) 2008-2010 Nick Gasson (nick@nickg.me.uk) * * 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 @@ -31,7 +31,8 @@ using namespace std; vhdl_scope::vhdl_scope() - : parent_(NULL), init_(false), sig_assign_(true) + : parent_(NULL), init_(false), sig_assign_(true), + hoisted_init_(false) { } @@ -100,6 +101,16 @@ vhdl_scope *vhdl_scope::get_parent() const return parent_; } +bool vhdl_scope::hoisted_initialiser() const +{ + return hoisted_init_; +} + +void vhdl_scope::hoisted_initialiser(bool h) +{ + hoisted_init_ = h; +} + vhdl_entity::vhdl_entity(const string& name, vhdl_arch *arch, int depth__) : depth(depth__), name_(name), arch_(arch), time_unit_(TIME_UNIT_NS) @@ -193,6 +204,16 @@ void vhdl_arch::emit(std::ostream &of, int level) const blank_line(of, level); // Extra blank line after architectures; } +void vhdl_procedural::add_blocking_target(vhdl_var_ref* ref) +{ + blocking_targets_.insert(ref->get_name()); +} + +bool vhdl_procedural::is_blocking_target(vhdl_var_ref* ref) const +{ + return blocking_targets_.find(ref->get_name()) != blocking_targets_.end(); +} + void vhdl_process::add_sensitivity(const std::string &name) { sens_.push_back(name); @@ -399,6 +420,7 @@ void vhdl_wait_stmt::emit(std::ostream &of, int level) const } of << ";"; + emit_comment(of, level, true); } vhdl_decl::~vhdl_decl() @@ -630,7 +652,7 @@ vhdl_abstract_assign_stmt::~vhdl_abstract_assign_stmt() void vhdl_abstract_assign_stmt::find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write) { - write.insert(lhs_); + lhs_->find_vars(write); rhs_->find_vars(read); } diff --git a/tgt-vhdl/vhdl_syntax.hh b/tgt-vhdl/vhdl_syntax.hh index 586a8630c..8bab208d8 100644 --- a/tgt-vhdl/vhdl_syntax.hh +++ b/tgt-vhdl/vhdl_syntax.hh @@ -51,7 +51,7 @@ public: virtual vhdl_expr *to_integer(); virtual vhdl_expr *to_std_logic(); virtual vhdl_expr *to_vector(vhdl_type_name_t name, int w); - virtual void find_vars(vhdl_var_set_t& read) const {} + virtual void find_vars(vhdl_var_set_t& read) {} protected: static void open_parens(ostream& of); @@ -768,6 +768,8 @@ public: bool initializing() const { return init_; } void set_initializing(bool i); + bool hoisted_initialiser() const; + void hoisted_initialiser(bool h); void set_allow_signal_assignment(bool b) { sig_assign_ = b; } bool allow_signal_assignment() const { return sig_assign_; } @@ -775,6 +777,7 @@ private: decl_list_t decls_; vhdl_scope *parent_; bool init_, sig_assign_; + bool hoisted_init_; }; @@ -793,6 +796,11 @@ public: void added_wait_stmt() { contains_wait_stmt_ = true; } bool contains_wait_stmt() const { return contains_wait_stmt_; } + + // Managing set of blocking assignment targets in this block + void add_blocking_target(vhdl_var_ref* ref); + bool is_blocking_target(vhdl_var_ref* ref) const; + protected: stmt_container stmts_; vhdl_scope scope_; @@ -802,6 +810,10 @@ protected: // If this is the case then we can't use a sensitvity list for // the process bool contains_wait_stmt_; + + // The set of variable we have performed a blocking + // assignment to + set blocking_targets_; }; diff --git a/tgt-vhdl/vhdl_target.h b/tgt-vhdl/vhdl_target.h index 8d99d97a1..083274252 100644 --- a/tgt-vhdl/vhdl_target.h +++ b/tgt-vhdl/vhdl_target.h @@ -31,7 +31,6 @@ string make_safe_name(ivl_signal_t sig); int draw_stask_display(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt, bool newline = true); -void prune_wait_for_0(stmt_container *container); void require_support_function(support_function_t f); #endif /* #ifndef INC_VHDL_TARGET_H */