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;
This commit is contained in:
parent
406d3936af
commit
f9da800cf5
|
|
@ -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
|
||||
|
|
|
|||
162
tgt-vhdl/stmt.cc
162
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<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)
|
||||
{
|
||||
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<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.
|
||||
// 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<vhdl_var_ref*> 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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<string> blocking_targets_;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Reference in New Issue