Tidy up blocking assignment code

This commit is contained in:
Nick Gasson 2008-07-15 10:44:48 +01:00
parent d1e7e325b7
commit 0b48f69b4e
4 changed files with 16 additions and 142 deletions

View File

@ -58,7 +58,7 @@ static vhdl_var_ref *translate_signal(ivl_expr_t e)
const char *renamed = get_renamed_signal(sig).c_str();
const vhdl_decl *decl = scope->get_decl(strip_var(renamed));
const vhdl_decl *decl = scope->get_decl(renamed);
assert(decl);
vhdl_type *type = new vhdl_type(*decl->get_type());

View File

@ -24,120 +24,6 @@
#include <iostream>
#include <cassert>
#include <sstream>
#include <map>
/*
* Implementing blocking assignment is a little tricky since
* the semantics are a little different to VHDL:
*
* In Verilog a blocking assignment (=) can be used anywhere
* non-blocking assignment (<=) can be. In VHDL blocking
* assignment (:=) can only be used with variables, and
* non-blocking assignment (<=) can only be used with signals.
* All Verilog variables are translated into signals in the
* VHDL architecture. This means we cannot use the VHDL :=
* operator directly. Furthermore, VHDL variables can only
* be declared within processes, so it wouldn't help to
* make all Verilog variables VHDL variables.
*
* The solution is to generate a VHDL variable in a process
* whenever a blocking assignment is made to a signal. The
* assignment is made to this variable instead, and
* g_assign_vars below remembers the temporary variables
* that have been generated. Any subsequent blocking assignments
* are made to the same variable. At either the end of the
* process or a `wait' statement, the temporaries are assigned
* back to the signals, and the temporaries are forgotten.
*
* For example:
*
* initial begin
* a = 5;
* b = a + 3;
* end
*
* Is translated to:
*
* process is
* variable a_Var : Some_Type;
* variable b_Var : Some_Type;
* begin
* a_Var := 5;
* b_Var := a_Var + 3;
* a <= a_Var;
* b <= b_Var;
* end process;
*/
typedef std::map<std::string, ivl_signal_t> var_temp_set_t;
static var_temp_set_t g_assign_vars;
/*
* Called whenever a blocking assignment is made to sig.
*/
void blocking_assign_to(vhdl_procedural *proc, ivl_signal_t sig)
{
std::string var(get_renamed_signal(sig));
std::string tmpname(var + "_Var");
if (g_assign_vars.find(var) == g_assign_vars.end()) {
// This is the first time a non-blocking assignment
// has been made to this signal: create a variable
// to shadow it.
if (!proc->get_scope()->have_declared(tmpname)) {
vhdl_decl *decl = proc->get_scope()->get_decl(var);
assert(decl);
vhdl_type *type = new vhdl_type(*decl->get_type());
proc->get_scope()->add_decl(new vhdl_var_decl(tmpname.c_str(), type));
}
rename_signal(sig, tmpname);
g_assign_vars[tmpname] = sig;
}
}
/*
* Assign all _Var variables to the corresponding signals. This makes
* the new values visible outside the current process. This should be
* called before any `wait' statement or the end of the process.
*/
void draw_blocking_assigns(vhdl_procedural *proc, stmt_container *container)
{
var_temp_set_t::const_iterator it;
for (it = g_assign_vars.begin(); it != g_assign_vars.end(); ++it) {
std::string stripped(strip_var((*it).first));
vhdl_decl *decl = proc->get_scope()->get_decl(stripped);
assert(decl);
vhdl_type *type = new vhdl_type(*decl->get_type());
vhdl_var_ref *lhs = new vhdl_var_ref(stripped.c_str(), NULL);
vhdl_expr *rhs = new vhdl_var_ref((*it).first.c_str(), type);
container->add_stmt(new vhdl_nbassign_stmt(lhs, rhs));
// Undo the renaming (since the temporary is no longer needed)
rename_signal((*it).second, stripped);
}
// If this this wait is within e.g. an `if' statement then
// we cannot correctly clear the variables list here (since
// they might not be assigned on another path)
if (container == proc->get_container())
g_assign_vars.clear();
}
/*
* Remove _Var from the end of a string, if it is present.
*/
std::string strip_var(const std::string &str)
{
std::string result(str);
size_t pos = result.find("_Var");
if (pos != std::string::npos)
result.erase(pos, 4);
return result;
}
/*
* Convert a Verilog process to VHDL and add it to the architecture
@ -162,10 +48,6 @@ static int generate_vhdl_process(vhdl_entity *ent, ivl_process_t proc)
if (rc != 0)
return rc;
// Output any remaning blocking assignments
draw_blocking_assigns(vhdl_proc, vhdl_proc->get_container());
g_assign_vars.clear();
// Initial processes are translated to VHDL processes with
// no sensitivity list and and indefinite wait statement at
// the end

View File

@ -102,9 +102,9 @@ static int draw_noop(vhdl_procedural *proc, stmt_container *container,
return 0;
}
static vhdl_expr *translate_assign_rhs(ivl_signal_t sig, vhdl_scope *scope,
ivl_expr_t e, vhdl_expr *base,
int lval_width)
static vhdl_expr *make_assign_rhs(ivl_signal_t sig, vhdl_scope *scope,
ivl_expr_t e, vhdl_expr *base,
int lval_width)
{
string signame(get_renamed_signal(sig));
@ -182,11 +182,11 @@ static T *make_assignment(vhdl_procedural *proc, stmt_container *container,
// Expand ternary expressions into an if statement
vhdl_expr *test = translate_expr(ivl_expr_oper1(rval));
vhdl_expr *true_part =
translate_assign_rhs(sig, proc->get_scope(),
ivl_expr_oper2(rval), base, lval_width);
make_assign_rhs(sig, proc->get_scope(),
ivl_expr_oper2(rval), base, lval_width);
vhdl_expr *false_part =
translate_assign_rhs(sig, proc->get_scope(),
ivl_expr_oper3(rval), base, lval_width);
make_assign_rhs(sig, proc->get_scope(),
ivl_expr_oper3(rval), base, lval_width);
if (!test || !true_part || !false_part)
return NULL;
@ -216,11 +216,11 @@ static T *make_assignment(vhdl_procedural *proc, stmt_container *container,
}
else {
vhdl_expr *rhs =
translate_assign_rhs(sig, proc->get_scope(), rval, base, lval_width);
make_assign_rhs(sig, proc->get_scope(), rval, base, lval_width);
if (NULL == rhs)
return NULL;
std::string signame(get_renamed_signal(sig));
string signame(get_renamed_signal(sig));
vhdl_decl *decl = proc->get_scope()->get_decl(signame);
// Where possible, move constant assignments into the
@ -349,11 +349,7 @@ static int draw_delay(vhdl_procedural *proc, stmt_container *container,
if (type == IVL_ST_ASSIGN_NB) {
draw_nbassign(proc, container, sub_stmt, time);
}
else {
// All blocking assignments need to be made visible
// at this point
draw_blocking_assigns(proc, container);
else {
vhdl_wait_stmt *wait =
new vhdl_wait_stmt(VHDL_WAIT_FOR, time);
container->add_stmt(wait);

View File

@ -23,23 +23,19 @@ vhdl_expr *lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm);
vhdl_expr *translate_expr(ivl_expr_t e);
void remember_entity(vhdl_entity *ent);
vhdl_entity *find_entity(const std::string &tname);
vhdl_entity *find_entity(const string &tname);
ivl_design_t get_vhdl_design();
vhdl_entity *get_active_entity();
//vhdl_entity *get_active_entity();
vhdl_var_ref *nexus_to_var_ref(vhdl_scope *arch_scope, ivl_nexus_t nexus);
bool seen_signal_before(ivl_signal_t sig);
void remember_signal(ivl_signal_t sig, const vhdl_scope *scope);
void rename_signal(ivl_signal_t sig, const std::string &renamed);
void rename_signal(ivl_signal_t sig, const string &renamed);
const vhdl_scope *find_scope_for_signal(ivl_signal_t sig);
const std::string &get_renamed_signal(ivl_signal_t sig);
ivl_signal_t find_signal_named(const std::string &name, const vhdl_scope *scope);
void blocking_assign_to(vhdl_procedural *proc, ivl_signal_t sig);
std::string strip_var(const std::string &str);
void draw_blocking_assigns(vhdl_procedural *proc, stmt_container *container);
const string &get_renamed_signal(ivl_signal_t sig);
ivl_signal_t find_signal_named(const string &name, const vhdl_scope *scope);
int draw_stask_display(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt, bool newline = true);