Reduce number of 0 ns waits in generated VHDL

Previous we generated a "wait for 0 ns" statement after
every blocking assignment that wasn't the last statement
in the process. While this implements the Verilog semantics,
it generates excessive waits, and cannot usually be synthesised.
This patch only generates "wait for 0 ns" statements when it
cannot be avoid (e.g. when the target of a blocking assignment
is read in the same process).

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
    -- wait for 0 ns  (previously)
  end if;

Conflicts:

	tgt-vhdl/process.cc
	tgt-vhdl/stmt.cc
	tgt-vhdl/vhdl_target.h
This commit is contained in:
Nick Gasson 2010-10-05 19:59:25 +01:00
parent ec49f10e2d
commit 2187f30207
5 changed files with 149 additions and 74 deletions

View File

@ -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 // However, if no statements were added to the container
// by draw_stmt, don't bother adding a wait as `emit' // by draw_stmt, don't bother adding a wait as `emit'
// will optimise the process out of the output // will optimise the process out of the output
if (ivl_process_type(proc) == IVL_PR_INITIAL) { bool is_initial = ivl_process_type(proc) == IVL_PR_INITIAL;
// Get rid of any useless `wait for 0 ns's at the end of the process bool is_empty = vhdl_proc->get_container()->empty();
prune_wait_for_0(vhdl_proc->get_container());
if (is_initial && !is_empty) {
// The above pruning might have removed all logic from the process vhdl_wait_stmt *wait = new vhdl_wait_stmt();
if (!vhdl_proc->get_container()->empty()) { vhdl_proc->get_container()->add_stmt(wait);
vhdl_wait_stmt *wait = new vhdl_wait_stmt();
vhdl_proc->get_container()->add_stmt(wait);
}
} }
// Add a comment indicating where it came from // Add a comment indicating where it came from

View File

@ -1,7 +1,7 @@
/* /*
* VHDL code generation for statements. * 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 * 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 * 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; 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<vhdl_wait_stmt*>(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) static vhdl_var_ref *make_assign_lhs(ivl_lval_t lval, vhdl_scope *scope)
{ {
ivl_signal_t sig = ivl_lval_sig(lval); 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; 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<vhdl_wait_stmt*>(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. // Generate an assignment of type T for the Verilog statement stmt.
// If a statement was generated then `assign_type' will contain the // If a statement was generated then `assign_type' will contain the
// type of assignment that was generated; this should be initialised // type of assignment that was generated; this should be initialised
// to some sensible default. // to some sensible default.
void make_assignment(vhdl_procedural *proc, stmt_container *container, 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) vhdl_decl::assign_type_t& assign_type)
{ {
list<vhdl_var_ref*> lvals; list<vhdl_var_ref*> lvals;
@ -286,14 +321,23 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container,
if (rhs == NULL) if (rhs == NULL)
return; return;
emit_wait_for_0(proc, container, stmt, rhs);
if (rhs2)
emit_wait_for_0(proc, container, stmt, rhs2);
if (lvals.size() == 1) { if (lvals.size() == 1) {
vhdl_var_ref *lhs = lvals.front(); vhdl_var_ref *lhs = lvals.front();
rhs = rhs->cast(lhs->get_type()); rhs = rhs->cast(lhs->get_type());
ivl_expr_t i_delay; ivl_expr_t i_delay;
vhdl_expr *after = NULL; 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); 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 // Find the declaration of the LHS so we know what type
// of assignment statement to generate (is it a signal, // of assignment statement to generate (is it a signal,
@ -301,6 +345,9 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container,
vhdl_decl *decl = proc->get_scope()->get_decl(lhs->get_name()); vhdl_decl *decl = proc->get_scope()->get_decl(lhs->get_name());
assign_type = decl->assignment_type(); 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 // A small optimisation is to expand ternary RHSs into an
// if statement (eliminates a function call and produces // if statement (eliminates a function call and produces
// more idiomatic code) // more idiomatic code)
@ -313,6 +360,8 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container,
if (NULL == test) if (NULL == test)
return; return;
emit_wait_for_0(proc, container, stmt, test);
if (!check_valid_assignment(decl->assignment_type(), proc, stmt)) if (!check_valid_assignment(decl->assignment_type(), proc, stmt))
return; return;
@ -368,6 +417,7 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container,
decl->set_initial(NULL); // Default initial value decl->set_initial(NULL); // Default initial value
else { else {
decl->set_initial(rhs); decl->set_initial(rhs);
proc->get_scope()->hoisted_initialiser(true);
delete lhs; delete lhs;
return; return;
} }
@ -380,8 +430,7 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container,
assign_for(decl->assignment_type(), lhs, rhs); assign_for(decl->assignment_type(), lhs, rhs);
container->add_stmt(a); container->add_stmt(a);
if (after != NULL) a->set_after(after);
a->set_after(after);
} }
else { else {
// Multiple lvals are implemented by first assigning the complete // Multiple lvals are implemented by first assigning the complete
@ -408,8 +457,13 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container,
ivl_expr_t i_delay; ivl_expr_t i_delay;
vhdl_expr *after = NULL; 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); 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 // Find the declaration of the LHS so we know what type
// of assignment statement to generate (is it a signal, // of assignment statement to generate (is it a signal,
@ -428,10 +482,11 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container,
container->add_stmt(a); container->add_stmt(a);
width_so_far += lval_width; width_so_far += lval_width;
if (assign_type == vhdl_decl::ASSIGN_NONBLOCK && emul_blocking)
proc->add_blocking_target(*it);
} }
} }
return;
} }
/* /*
@ -454,26 +509,10 @@ static int draw_assign(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt, bool is_last) ivl_statement_t stmt, bool is_last)
{ {
vhdl_decl::assign_type_t assign_type = vhdl_decl::ASSIGN_NONBLOCK; vhdl_decl::assign_type_t assign_type = vhdl_decl::ASSIGN_NONBLOCK;
if (proc->get_scope()->allow_signal_assignment()) { bool emulate_blocking = proc->get_scope()->allow_signal_assignment();
// Blocking assignment is implemented as non-blocking assignment
// followed by a zero-time wait make_assignment(proc, container, stmt, emulate_blocking, assign_type);
// 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);
return 0; return 0;
} }
@ -501,9 +540,7 @@ static int draw_delay(vhdl_procedural *proc, stmt_container *container,
if (NULL == time) if (NULL == time)
return 1; return 1;
} }
prune_wait_for_0(container);
ivl_statement_t sub_stmt = ivl_stmt_sub_stmt(stmt); ivl_statement_t sub_stmt = ivl_stmt_sub_stmt(stmt);
vhdl_wait_stmt *wait = vhdl_wait_stmt *wait =
new vhdl_wait_stmt(VHDL_WAIT_FOR, time); new vhdl_wait_stmt(VHDL_WAIT_FOR, time);
@ -523,8 +560,8 @@ static int draw_delay(vhdl_procedural *proc, stmt_container *container,
// Any further assignments occur after simulation time 0 // Any further assignments occur after simulation time 0
// so they cannot be used to initialise signal declarations // so they cannot be used to initialise signal declarations
// (if this scope is an initial process) // (if this scope is an initial process)
proc->get_scope()->set_initializing(false); proc->get_scope()->set_initializing(false);
return 0; return 0;
} }
@ -743,9 +780,12 @@ static int draw_wait(vhdl_procedural *_proc, stmt_container *container,
// If this container is the top-level statement (i.e. it is the // If this container is the top-level statement (i.e. it is the
// first thing inside a process) then we can extract these // first thing inside a process) then we can extract these
// events out into the sensitivity list // events out into the sensitivity list as long as we haven't
bool is_top_level = container == proc->get_container() // promoted any preceding assignments to initialisers
&& container->empty(); bool is_top_level =
container == proc->get_container()
&& container->empty()
&& !proc->get_scope()->hoisted_initialiser();
// See if this can be implemented in a more idiomatic way before we // See if this can be implemented in a more idiomatic way before we
// fall back on the generic translation // fall back on the generic translation
@ -876,8 +916,11 @@ static int draw_if(vhdl_procedural *proc, stmt_container *container,
if (NULL == test) if (NULL == test)
return 1; return 1;
emit_wait_for_0(proc, container, stmt, test);
vhdl_if_stmt *vhdif = new vhdl_if_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); ivl_statement_t cond_true_stmt = ivl_stmt_cond_true(stmt);
if (cond_true_stmt) if (cond_true_stmt)
draw_stmt(proc, vhdif->get_then_container(), cond_true_stmt, is_last); draw_stmt(proc, vhdif->get_then_container(), cond_true_stmt, is_last);
@ -886,8 +929,6 @@ static int draw_if(vhdl_procedural *proc, stmt_container *container,
if (cond_false_stmt) if (cond_false_stmt)
draw_stmt(proc, vhdif->get_else_container(), cond_false_stmt, is_last); draw_stmt(proc, vhdif->get_else_container(), cond_false_stmt, is_last);
container->add_stmt(vhdif);
return 0; return 0;
} }
@ -1403,9 +1444,13 @@ int draw_while(vhdl_procedural *proc, stmt_container *container,
vhdl_type boolean(VHDL_TYPE_BOOLEAN); vhdl_type boolean(VHDL_TYPE_BOOLEAN);
test = test->cast(&boolean); test = test->cast(&boolean);
emit_wait_for_0(proc, container, stmt, test);
vhdl_while_stmt *loop = new vhdl_while_stmt(test); vhdl_while_stmt *loop = new vhdl_while_stmt(test);
draw_stmt(proc, loop->get_container(), ivl_stmt_sub_stmt(stmt)); 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); container->add_stmt(loop);
return 0; return 0;
} }

View File

@ -1,7 +1,7 @@
/* /*
* VHDL abstract syntax elements. * 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 * 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 * it under the terms of the GNU General Public License as published by
@ -31,7 +31,8 @@
using namespace std; using namespace std;
vhdl_scope::vhdl_scope() 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_; 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__) vhdl_entity::vhdl_entity(const string& name, vhdl_arch *arch, int depth__)
: depth(depth__), name_(name), arch_(arch), : depth(depth__), name_(name), arch_(arch),
time_unit_(TIME_UNIT_NS) 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; 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) void vhdl_process::add_sensitivity(const std::string &name)
{ {
sens_.push_back(name); sens_.push_back(name);
@ -399,6 +420,7 @@ void vhdl_wait_stmt::emit(std::ostream &of, int level) const
} }
of << ";"; of << ";";
emit_comment(of, level, true);
} }
vhdl_decl::~vhdl_decl() 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, void vhdl_abstract_assign_stmt::find_vars(vhdl_var_set_t& read,
vhdl_var_set_t& write) vhdl_var_set_t& write)
{ {
write.insert(lhs_); lhs_->find_vars(write);
rhs_->find_vars(read); rhs_->find_vars(read);
} }

View File

@ -52,7 +52,7 @@ public:
virtual vhdl_expr *to_std_logic(); virtual vhdl_expr *to_std_logic();
virtual vhdl_expr *to_std_ulogic(); virtual vhdl_expr *to_std_ulogic();
virtual vhdl_expr *to_vector(vhdl_type_name_t name, int w); 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: protected:
static void open_parens(ostream& of); static void open_parens(ostream& of);
@ -770,6 +770,8 @@ public:
bool initializing() const { return init_; } bool initializing() const { return init_; }
void set_initializing(bool i); void set_initializing(bool i);
bool hoisted_initialiser() const;
void hoisted_initialiser(bool h);
void set_allow_signal_assignment(bool b) { sig_assign_ = b; } void set_allow_signal_assignment(bool b) { sig_assign_ = b; }
bool allow_signal_assignment() const { return sig_assign_; } bool allow_signal_assignment() const { return sig_assign_; }
@ -777,6 +779,7 @@ private:
decl_list_t decls_; decl_list_t decls_;
vhdl_scope *parent_; vhdl_scope *parent_;
bool init_, sig_assign_; bool init_, sig_assign_;
bool hoisted_init_;
}; };
@ -795,6 +798,11 @@ public:
void added_wait_stmt() { contains_wait_stmt_ = true; } void added_wait_stmt() { contains_wait_stmt_ = true; }
bool contains_wait_stmt() const { return contains_wait_stmt_; } 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: protected:
stmt_container stmts_; stmt_container stmts_;
vhdl_scope scope_; vhdl_scope scope_;
@ -804,6 +812,10 @@ protected:
// If this is the case then we can't use a sensitivity list for // If this is the case then we can't use a sensitivity list for
// the process // the process
bool contains_wait_stmt_; bool contains_wait_stmt_;
// The set of variable we have performed a blocking
// assignment to
set<string> blocking_targets_;
}; };

View File

@ -31,7 +31,6 @@ string make_safe_name(ivl_signal_t sig);
int draw_stask_display(vhdl_procedural *proc, stmt_container *container, int draw_stask_display(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt, bool newline = true); ivl_statement_t stmt, bool newline = true);
void prune_wait_for_0(stmt_container *container);
void require_support_function(support_function_t f); void require_support_function(support_function_t f);
#endif /* #ifndef INC_VHDL_TARGET_H */ #endif /* #ifndef INC_VHDL_TARGET_H */