Fix assignment to VHDL function arguments

This patch is a fix for pr2516774.

The idea is to generate a local variable inside
a function whenever an argument is assigned to. The
variable has an initial value of the argument value
and is used in its place for the remainder of the
function. This patch also handles the case where the
argument is assigned to inside a while loop.
This commit is contained in:
Nick Gasson 2009-01-18 10:27:12 +00:00 committed by Stephen Williams
parent e6846ea3a7
commit 78bc4b5d47
2 changed files with 48 additions and 13 deletions

View File

@ -1,7 +1,7 @@
/*
* VHDL code generation for statements.
*
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
* Copyright (C) 2008-2009 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
@ -137,9 +137,29 @@ static vhdl_var_ref *make_assign_lhs(ivl_lval_t lval, vhdl_scope *scope)
string signame(get_renamed_signal(sig));
vhdl_decl *decl = scope->get_decl(signame);
assert(decl);
// Verilog allows assignments to elements that are constant in VHDL:
// function parameters, for example
// To work around this we generate a local variable to shadow the
// constant and assign to that
if (decl->assignment_type() == vhdl_decl::ASSIGN_CONST) {
const string shadow_name = signame + "_Shadow";
vhdl_var_decl* shadow_decl =
new vhdl_var_decl(shadow_name, decl->get_type());
shadow_decl->set_initial
(new vhdl_var_ref(signame, decl->get_type()));
scope->add_decl(shadow_decl);
// Make sure all future references to this signal use the
// shadow variable
rename_signal(sig, shadow_name);
// ...and use this new variable as the assignment LHS
decl = shadow_decl;
}
vhdl_type *ltype = new vhdl_type(*decl->get_type());
vhdl_var_ref *lval_ref = new vhdl_var_ref(signame.c_str(), ltype);
vhdl_var_ref *lval_ref = new vhdl_var_ref(decl->get_name(), ltype);
if (base) {
if (decl->get_type()->get_name() == VHDL_TYPE_ARRAY)
lval_ref->set_slice(base, 0);
@ -175,6 +195,7 @@ assign_for(vhdl_decl::assign_type_t atype, vhdl_var_ref *lhs, vhdl_expr *rhs)
{
switch (atype) {
case vhdl_decl::ASSIGN_BLOCK:
case vhdl_decl::ASSIGN_CONST:
return new vhdl_assign_stmt(lhs, rhs);
case vhdl_decl::ASSIGN_NONBLOCK:
return new vhdl_nbassign_stmt(lhs, rhs);
@ -1336,6 +1357,16 @@ int draw_casezx(vhdl_procedural *proc, stmt_container *container,
int draw_while(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt)
{
// Generate the body inside a temporary container before
// generating the test
// The reason for this is that some of the signals in the
// test might be renamed while expanding the body (e.g. if
// we need to generate an assignment to a constant signal)
stmt_container tmp_container;
int rc = draw_stmt(proc, &tmp_container, ivl_stmt_sub_stmt(stmt));
if (rc != 0)
return 1;
vhdl_expr *test = translate_expr(ivl_stmt_cond_expr(stmt));
if (NULL == test)
return 1;
@ -1346,10 +1377,9 @@ int draw_while(vhdl_procedural *proc, stmt_container *container,
test = test->cast(&boolean);
vhdl_while_stmt *loop = new vhdl_while_stmt(test);
container->add_stmt(loop);
draw_stmt(proc, loop->get_container(), ivl_stmt_sub_stmt(stmt));
container->add_stmt(loop);
return 0;
}

View File

@ -1,7 +1,7 @@
/*
* VHDL abstract syntax elements.
*
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
* Copyright (C) 2008-2009 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
@ -26,13 +26,15 @@
#include "vhdl_element.hh"
#include "vhdl_type.hh"
using namespace std;
class vhdl_scope;
class vhdl_entity;
class vhdl_arch;
class vhdl_expr : public vhdl_element {
public:
vhdl_expr(vhdl_type* type, bool isconst=false)
vhdl_expr(const vhdl_type* type, bool isconst=false)
: type_(type), isconst_(isconst) {}
virtual ~vhdl_expr();
@ -46,7 +48,7 @@ public:
virtual vhdl_expr *to_std_logic();
virtual vhdl_expr *to_vector(vhdl_type_name_t name, int w);
protected:
vhdl_type *type_;
const vhdl_type *type_;
bool isconst_;
};
@ -56,7 +58,7 @@ protected:
*/
class vhdl_var_ref : public vhdl_expr {
public:
vhdl_var_ref(const char *name, vhdl_type *type,
vhdl_var_ref(const string& name, const vhdl_type *type,
vhdl_expr *slice = NULL)
: vhdl_expr(type), name_(name), slice_(slice) {}
~vhdl_var_ref();
@ -538,7 +540,7 @@ private:
*/
class vhdl_decl : public vhdl_element {
public:
vhdl_decl(const char *name, vhdl_type *type = NULL,
vhdl_decl(const string& name, const vhdl_type *type = NULL,
vhdl_expr *initial = NULL)
: name_(name), type_(type), initial_(initial),
has_initial_(initial != NULL) {}
@ -551,7 +553,9 @@ public:
bool has_initial() const { return has_initial_; }
// The different sorts of assignment statement
enum assign_type_t { ASSIGN_BLOCK, ASSIGN_NONBLOCK };
// ASSIGN_CONST is used to generate a variable to shadow a
// constant that cannot be assigned to (e.g. a function parameter)
enum assign_type_t { ASSIGN_BLOCK, ASSIGN_NONBLOCK, ASSIGN_CONST };
// Get the sort of assignment statement to generate for
// assignemnts to this declaration
@ -561,7 +565,7 @@ public:
virtual assign_type_t assignment_type() const { assert(false); }
protected:
std::string name_;
vhdl_type *type_;
const vhdl_type *type_;
vhdl_expr *initial_;
bool has_initial_;
};
@ -589,7 +593,7 @@ private:
class vhdl_type_decl : public vhdl_decl {
public:
vhdl_type_decl(const char *name, vhdl_type *base)
vhdl_type_decl(const string& name, const vhdl_type *base)
: vhdl_decl(name, base) {}
void emit(std::ostream &of, int level) const;
};
@ -600,7 +604,7 @@ public:
*/
class vhdl_var_decl : public vhdl_decl {
public:
vhdl_var_decl(const char *name, vhdl_type *type)
vhdl_var_decl(const string& name, const vhdl_type *type)
: vhdl_decl(name, type) {}
void emit(std::ostream &of, int level) const;
assign_type_t assignment_type() const { return ASSIGN_BLOCK; }
@ -627,6 +631,7 @@ public:
vhdl_param_decl(const char *name, vhdl_type *type)
: vhdl_decl(name, type) {}
void emit(std::ostream &of, int level) const;
assign_type_t assignment_type() const { return ASSIGN_CONST; }
};
enum vhdl_port_mode_t {