Support named blocks with local variables in VHDL target
This patch adds code to generate process-local variables for scopes of type IVL_SCT_BLOCK. This also handles using the correct assignment operator (:=) for the local VHDL variables.
This commit is contained in:
parent
3f0b798ef6
commit
501106dc92
|
|
@ -27,9 +27,6 @@
|
|||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
static string make_safe_name(ivl_signal_t sig);
|
||||
|
||||
|
||||
/*
|
||||
* This represents the portion of a nexus that is visible within
|
||||
* a VHDL scope. If that nexus portion does not contain a signal,
|
||||
|
|
@ -379,10 +376,8 @@ static void declare_logic(vhdl_arch *arch, ivl_scope_t scope)
|
|||
draw_logic(arch, ivl_scope_log(scope, i));
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure a signal name conforms to VHDL naming rules.
|
||||
*/
|
||||
static string make_safe_name(ivl_signal_t sig)
|
||||
// Make sure a signal name conforms to VHDL naming rules.
|
||||
string make_safe_name(ivl_signal_t sig)
|
||||
{
|
||||
string base(ivl_signal_basename(sig));
|
||||
|
||||
|
|
|
|||
101
tgt-vhdl/stmt.cc
101
tgt-vhdl/stmt.cc
|
|
@ -85,14 +85,33 @@ static int draw_stask(vhdl_procedural *proc, stmt_container *container,
|
|||
}
|
||||
|
||||
/*
|
||||
* Generate VHDL for a block of Verilog statements. This doesn't
|
||||
* actually do anything, other than recursively translate the
|
||||
* block's statements and add them to the process. This is OK as
|
||||
* the stmt_container class behaves like a Verilog block.
|
||||
* Generate VHDL for a block of Verilog statements. If this block
|
||||
* doesn't have its own scope then this function does nothing, other
|
||||
* than recursively translate the block's statements and add them
|
||||
* to the process. This is OK as the stmt_container class behaves
|
||||
* like a Verilog block.
|
||||
*
|
||||
* If this block has its own scope with local variables then these
|
||||
* are added to the process as local variables and the statements
|
||||
* are generated as above.
|
||||
*/
|
||||
static int draw_block(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt, bool is_last)
|
||||
{
|
||||
ivl_scope_t block_scope = ivl_stmt_block_scope(stmt);
|
||||
if (block_scope) {
|
||||
int nsigs = ivl_scope_sigs(block_scope);
|
||||
for (int i = 0; i < nsigs; i++) {
|
||||
ivl_signal_t sig = ivl_scope_sig(block_scope, i);
|
||||
remember_signal(sig, proc->get_scope());
|
||||
|
||||
vhdl_type* type = vhdl_type::type_for(ivl_signal_width(sig),
|
||||
ivl_signal_signed(sig));
|
||||
proc->get_scope()->add_decl
|
||||
(new vhdl_var_decl(make_safe_name(sig), type));
|
||||
}
|
||||
}
|
||||
|
||||
int count = ivl_stmt_block_count(stmt);
|
||||
for (int i = 0; i < count; i++) {
|
||||
ivl_statement_t stmt_i = ivl_stmt_block_stmt(stmt, i);
|
||||
|
|
@ -112,6 +131,27 @@ 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);
|
||||
|
|
@ -223,11 +263,13 @@ bool check_valid_assignment(vhdl_decl::assign_type_t atype, vhdl_procedural *pro
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
// 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 blocking,
|
||||
vhdl_decl::assign_type_t& assign_type)
|
||||
{
|
||||
list<vhdl_var_ref*> lvals;
|
||||
if (!assignment_lvals(stmt, proc, lvals))
|
||||
|
|
@ -257,6 +299,7 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container,
|
|||
// 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();
|
||||
|
||||
// A small optimisation is to expand ternary RHSs into an
|
||||
// if statement (eliminates a function call and produces
|
||||
|
|
@ -331,7 +374,7 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container,
|
|||
}
|
||||
|
||||
if (!check_valid_assignment(decl->assignment_type(), proc, stmt))
|
||||
return;
|
||||
return;
|
||||
|
||||
vhdl_abstract_assign_stmt *a =
|
||||
assign_for(decl->assignment_type(), lhs, rhs);
|
||||
|
|
@ -372,6 +415,7 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container,
|
|||
// of assignment statement to generate (is it a signal,
|
||||
// a variable, etc?)
|
||||
vhdl_decl *decl = proc->get_scope()->get_decl((*it)->get_name());
|
||||
assign_type = decl->assignment_type();
|
||||
|
||||
if (!check_valid_assignment(decl->assignment_type(), proc, stmt))
|
||||
return;
|
||||
|
|
@ -386,6 +430,8 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container,
|
|||
width_so_far += lval_width;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -398,7 +444,8 @@ static int draw_nbassign(vhdl_procedural *proc, stmt_container *container,
|
|||
{
|
||||
assert(proc->get_scope()->allow_signal_assignment());
|
||||
|
||||
make_assignment(proc, container, stmt, false);
|
||||
vhdl_decl::assign_type_t ignored;
|
||||
make_assignment(proc, container, stmt, false, ignored);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -406,48 +453,30 @@ static int draw_nbassign(vhdl_procedural *proc, stmt_container *container,
|
|||
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);
|
||||
make_assignment(proc, container, stmt, false, assign_type);
|
||||
|
||||
// Don't generate a zero-wait if this is the last statement in
|
||||
// the process
|
||||
if (!is_last) {
|
||||
// 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);
|
||||
make_assignment(proc, container, stmt, true, assign_type);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Delay statements are equivalent to the `wait for' form of the
|
||||
* VHDL wait statement.
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@
|
|||
|
||||
#include "version.h"
|
||||
#include "vhdl_target.h"
|
||||
#include "vhdl_element.hh"
|
||||
#include "state.hh"
|
||||
|
||||
#include <iostream>
|
||||
|
|
|
|||
|
|
@ -4,10 +4,8 @@
|
|||
#include "vhdl_config.h"
|
||||
#include "ivl_target.h"
|
||||
|
||||
#include "vhdl_syntax.hh"
|
||||
#include "vhdl_type.hh"
|
||||
|
||||
#include "support.hh"
|
||||
#include "vhdl_syntax.hh"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
|
@ -29,7 +27,8 @@ vhdl_expr *translate_time_expr(ivl_expr_t e);
|
|||
ivl_design_t get_vhdl_design();
|
||||
vhdl_var_ref *nexus_to_var_ref(vhdl_scope *arch_scope, ivl_nexus_t nexus);
|
||||
vhdl_var_ref* readable_ref(vhdl_scope* scope, ivl_nexus_t nex);
|
||||
|
||||
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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue