Merge branch 'vhdl' of file:///media/disk/data/iverilog/ into vhdl

This commit is contained in:
Nick Gasson 2008-07-21 15:20:42 +01:00
commit e25f946ac0
18 changed files with 1296 additions and 682 deletions

View File

@ -50,7 +50,7 @@ dep:
mv $*.d dep
O = vhdl.o vhdl_element.o vhdl_type.o vhdl_syntax.o scope.o process.o \
stmt.o expr.o lpm.o display.o
stmt.o expr.o lpm.o display.o support.o cast.o
ifeq (@WIN32@,yes)
TGTLDFLAGS=-L.. -livl

173
tgt-vhdl/cast.cc Normal file
View File

@ -0,0 +1,173 @@
/*
* Generate code to convert between VHDL types.
*
* Copyright (C) 2008 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "vhdl_syntax.hh"
#include "vhdl_target.h"
#include "support.hh"
#include <cassert>
#include <iostream>
vhdl_expr *vhdl_expr::cast(const vhdl_type *to)
{
//std::cout << "Cast: from=" << type_->get_string()
// << " (" << type_->get_width() << ") "
// << " to=" << to->get_string() << " ("
// << to->get_width() << ")" << std::endl;
if (to->get_name() == type_->get_name()) {
if (to->get_width() == type_->get_width())
return this; // Identical
else
return resize(to->get_width());
}
else if (to->get_name() == VHDL_TYPE_BOOLEAN) {
if (type_->get_name() == VHDL_TYPE_STD_LOGIC) {
// '1' is true all else are false
vhdl_const_bit *one = new vhdl_const_bit('1');
return new vhdl_binop_expr
(this, VHDL_BINOP_EQ, one, vhdl_type::boolean());
}
else if (type_->get_name() == VHDL_TYPE_UNSIGNED) {
// Need to use a support function for this conversion
require_support_function(SF_UNSIGNED_TO_BOOLEAN);
vhdl_fcall *conv =
new vhdl_fcall(support_function::function_name(SF_UNSIGNED_TO_BOOLEAN),
vhdl_type::boolean());
conv->add_expr(this);
return conv;
}
else if (type_->get_name() == VHDL_TYPE_SIGNED) {
require_support_function(SF_SIGNED_TO_BOOLEAN);
vhdl_fcall *conv =
new vhdl_fcall(support_function::function_name(SF_SIGNED_TO_BOOLEAN),
vhdl_type::boolean());
conv->add_expr(this);
return conv;
}
else {
assert(false);
}
}
else if (to->get_name() == VHDL_TYPE_INTEGER) {
vhdl_fcall *conv = new vhdl_fcall("To_Integer", new vhdl_type(*to));
conv->add_expr(this);
return conv;
}
else if (to->get_name() == VHDL_TYPE_STD_LOGIC &&
type_->get_name() == VHDL_TYPE_BOOLEAN) {
require_support_function(SF_BOOLEAN_TO_LOGIC);
vhdl_fcall *ah =
new vhdl_fcall(support_function::function_name(SF_BOOLEAN_TO_LOGIC),
vhdl_type::std_logic());
ah->add_expr(this);
return ah;
}
else {
// We have to cast the expression before resizing or the
// wrong sign bit may be extended (i.e. when casting between
// signed/unsigned *and* resizing)
vhdl_fcall *conv =
new vhdl_fcall(to->get_string().c_str(), new vhdl_type(*to));
conv->add_expr(this);
if (to->get_width() != type_->get_width())
return conv->resize(to->get_width());
else
return conv;
}
}
vhdl_expr *vhdl_expr::resize(int newwidth)
{
vhdl_type *rtype;
assert(type_);
if (type_->get_name() == VHDL_TYPE_SIGNED)
rtype = vhdl_type::nsigned(newwidth);
else if (type_->get_name() == VHDL_TYPE_UNSIGNED)
rtype = vhdl_type::nunsigned(newwidth);
else
return this; // Doesn't make sense to resize non-vector type
vhdl_fcall *resize = new vhdl_fcall("Resize", rtype);
resize->add_expr(this);
resize->add_expr(new vhdl_const_int(newwidth));
return resize;
}
int vhdl_const_bits::bits_to_int() const
{
char msb = value_[value_.size() - 1];
int result = 0, bit;
for (int i = sizeof(int)*8 - 1; i >= 0; i--) {
if (i > (int)value_.size() - 1)
bit = msb == '1' ? 1 : 0;
else
bit = value_[i] == '1' ? 1 : 0;
result = (result << 1) | bit;
}
return result;
}
vhdl_expr *vhdl_const_bits::cast(const vhdl_type *to)
{
if (to->get_name() == VHDL_TYPE_STD_LOGIC) {
// VHDL won't let us cast directly between a vector and
// a scalar type
// But we don't need to here as we have the bits available
// Take the least significant bit
char lsb = value_[0];
return new vhdl_const_bit(lsb);
}
else if (to->get_name() == VHDL_TYPE_STD_LOGIC_VECTOR) {
// Don't need to do anything
return this;
}
else if (to->get_name() == VHDL_TYPE_SIGNED
|| to->get_name() == VHDL_TYPE_UNSIGNED) {
// Extend with sign bit
value_.resize(to->get_width(), value_[0]);
return this;
}
else if (to->get_name() == VHDL_TYPE_INTEGER)
return new vhdl_const_int(bits_to_int());
else
return vhdl_expr::cast(to);
}
vhdl_expr *vhdl_const_bit::cast(const vhdl_type *to)
{
if (to->get_name() == VHDL_TYPE_INTEGER)
return new vhdl_const_int(bit_ == '1' ? 1 : 0);
else
return vhdl_expr::cast(to);
}

View File

@ -19,14 +19,17 @@
*/
#include "vhdl_target.h"
#include "support.hh"
#include <iostream>
#include <cassert>
#include <cstring>
/*
* Change the signdness of a vector.
* Change the signedness of a vector.
*/
static vhdl_expr *change_signdness(vhdl_expr *e, bool issigned)
static vhdl_expr *change_signedness(vhdl_expr *e, bool issigned)
{
int msb = e->get_type()->get_msb();
int lsb = e->get_type()->get_lsb();
@ -58,12 +61,29 @@ 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));
vhdl_decl *decl = scope->get_decl(renamed);
assert(decl);
vhdl_type *type = new vhdl_type(*decl->get_type());
return new vhdl_var_ref(renamed, type);
// Can't generate a constant initialiser for this signal
// later as it has already been read
if (scope->initializing())
decl->set_initial(NULL);
vhdl_var_ref *ref =
new vhdl_var_ref(renamed, new vhdl_type(*decl->get_type()));
ivl_expr_t off;
if (ivl_signal_array_count(sig) > 0 && (off = ivl_expr_oper1(e))) {
// Select from an array
vhdl_expr *vhd_off = translate_expr(off);
if (NULL == vhd_off)
return NULL;
vhdl_type integer(VHDL_TYPE_INTEGER);
ref->set_slice(vhd_off->cast(&integer));
}
return ref;
}
/*
@ -71,8 +91,33 @@ static vhdl_var_ref *translate_signal(ivl_expr_t e)
*/
static vhdl_expr *translate_number(ivl_expr_t e)
{
return new vhdl_const_bits(ivl_expr_bits(e), ivl_expr_width(e),
ivl_expr_signed(e) != 0);
if (ivl_expr_width(e) == 1)
return new vhdl_const_bit(ivl_expr_bits(e)[0]);
else
return new vhdl_const_bits(ivl_expr_bits(e), ivl_expr_width(e),
ivl_expr_signed(e) != 0);
}
static vhdl_expr *translate_ulong(ivl_expr_t e)
{
return new vhdl_const_int(ivl_expr_uvalue(e));
}
static vhdl_expr *translate_reduction(support_function_t f, bool neg,
vhdl_expr *operand)
{
require_support_function(f);
vhdl_fcall *fcall =
new vhdl_fcall(support_function::function_name(f),
vhdl_type::std_logic());
vhdl_type std_logic_vector(VHDL_TYPE_STD_LOGIC_VECTOR);
fcall->add_expr(operand->cast(&std_logic_vector));
if (neg)
return new vhdl_unaryop_expr(VHDL_UNARYOP_NOT, fcall,
vhdl_type::std_logic());
else
return fcall;
}
static vhdl_expr *translate_unary(ivl_expr_t e)
@ -83,17 +128,19 @@ static vhdl_expr *translate_unary(ivl_expr_t e)
bool should_be_signed = ivl_expr_signed(e) != 0;
if (operand->get_type()->get_name() == VHDL_TYPE_UNSIGNED && should_be_signed) {
if (operand->get_type()->get_name() == VHDL_TYPE_UNSIGNED
&& should_be_signed) {
//operand->print();
//std::cout << "^ should be signed but is not" << std::endl;
operand = change_signdness(operand, true);
operand = change_signedness(operand, true);
}
else if (operand->get_type()->get_name() == VHDL_TYPE_SIGNED && !should_be_signed) {
else if (operand->get_type()->get_name() == VHDL_TYPE_SIGNED
&& !should_be_signed) {
//operand->print();
//std::cout << "^ should be unsigned but is not" << std::endl;
operand = change_signdness(operand, false);
operand = change_signedness(operand, false);
}
char opcode = ivl_expr_opcode(e);
@ -103,15 +150,13 @@ static vhdl_expr *translate_unary(ivl_expr_t e)
return new vhdl_unaryop_expr
(VHDL_UNARYOP_NOT, operand, new vhdl_type(*operand->get_type()));
case 'N': // NOR
return translate_reduction(SF_REDUCE_OR, true, operand);
case '|':
{
vhdl_fcall *f = new vhdl_fcall("Reduce_OR", vhdl_type::std_logic());
f->add_expr(operand);
if ('N' == opcode)
return new vhdl_unaryop_expr(VHDL_UNARYOP_NOT, f, vhdl_type::std_logic());
else
return f;
}
return translate_reduction(SF_REDUCE_OR, false, operand);
case 'A': // NAND
return translate_reduction(SF_REDUCE_AND, true, operand);
case '&':
return translate_reduction(SF_REDUCE_AND, false, operand);
default:
error("No translation for unary opcode '%c'\n",
ivl_expr_opcode(e));
@ -284,13 +329,13 @@ static vhdl_expr *translate_binary(ivl_expr_t e)
//result->print();
//std::cout << "^ should be signed but is not" << std::endl;
result = change_signdness(result, true);
result = change_signedness(result, true);
}
else if (result->get_type()->get_name() == VHDL_TYPE_SIGNED && !should_be_signed) {
//result->print();
//std::cout << "^ should be unsigned but is not" << std::endl;
result = change_signdness(result, false);
result = change_signedness(result, false);
}
int actual_width = result->get_type()->get_width();
@ -382,6 +427,23 @@ static vhdl_expr *translate_concat(ivl_expr_t e)
return translate_parms<vhdl_binop_expr>(concat, e);
}
vhdl_expr *translate_sfunc_time(ivl_expr_t e)
{
cerr << "warning: no translation for time (returning 0)" << endl;
return new vhdl_const_int(0);
}
vhdl_expr *translate_sfunc(ivl_expr_t e)
{
const char *name = ivl_expr_name(e);
if (strcmp(name, "$time") == 0)
return translate_sfunc_time(e);
else {
error("No translation for system function %s", name);
return NULL;
}
}
/*
* Generate a VHDL expression from a Verilog expression.
*/
@ -397,6 +459,8 @@ vhdl_expr *translate_expr(ivl_expr_t e)
return translate_signal(e);
case IVL_EX_NUMBER:
return translate_number(e);
case IVL_EX_ULONG:
return translate_ulong(e);
case IVL_EX_UNARY:
return translate_unary(e);
case IVL_EX_BINARY:
@ -409,6 +473,8 @@ vhdl_expr *translate_expr(ivl_expr_t e)
return translate_ternary(e);
case IVL_EX_CONCAT:
return translate_concat(e);
case IVL_EX_SFUNC:
return translate_sfunc(e);
default:
error("No VHDL translation for expression at %s:%d (type = %d)",
ivl_expr_file(e), ivl_expr_lineno(e), type);

View File

@ -23,54 +23,6 @@
#include <iostream>
#include <cassert>
static vhdl_expr *draw_concat_lpm(vhdl_scope *scope, ivl_lpm_t lpm)
{
vhdl_type *result_type =
vhdl_type::type_for(ivl_lpm_width(lpm), ivl_lpm_signed(lpm) != 0);
vhdl_binop_expr *expr =
new vhdl_binop_expr(VHDL_BINOP_CONCAT, result_type);
for (int i = ivl_lpm_selects(lpm) - 1; i >= 0; i--) {
vhdl_expr *e = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
if (NULL == e)
return NULL;
expr->add_expr(e);
}
return expr;
}
static vhdl_expr *draw_binop_lpm(vhdl_scope *scope, ivl_lpm_t lpm, vhdl_binop_t op)
{
vhdl_type *result_type =
vhdl_type::type_for(ivl_lpm_width(lpm), ivl_lpm_signed(lpm) != 0);
vhdl_binop_expr *expr = new vhdl_binop_expr(op, result_type);
for (int i = 0; i < 2; i++) {
vhdl_expr *e = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
if (NULL == e)
return NULL;
expr->add_expr(e);
}
if (op == VHDL_BINOP_MULT) {
// Need to resize the output to the desired size,
// as this does not happen automatically in VHDL
unsigned out_width = ivl_lpm_width(lpm);
vhdl_fcall *resize =
new vhdl_fcall("Resize", vhdl_type::nsigned(out_width));
resize->add_expr(expr);
resize->add_expr(new vhdl_const_int(out_width));
return resize;
}
else
return expr;
}
/*
* Return the base of a part select.
*/
@ -88,12 +40,105 @@ static vhdl_expr *part_select_base(vhdl_scope *scope, ivl_lpm_t lpm)
return off->cast(&integer);
}
static vhdl_expr *draw_part_select_vp_lpm(vhdl_scope *scope, ivl_lpm_t lpm)
vhdl_var_ref *lpm_output(vhdl_scope *scope, ivl_lpm_t lpm)
{
vhdl_var_ref *out = nexus_to_var_ref(scope, ivl_lpm_q(lpm, 0));
if (NULL == out) {
vhdl_type *type =
vhdl_type::type_for(ivl_lpm_width(lpm),
ivl_lpm_signed(lpm) != 0);
string name("LPM");
name += ivl_lpm_basename(lpm);
name += "_Out";
if (!scope->have_declared(name)) {
scope->add_decl
(new vhdl_signal_decl(name.c_str(), new vhdl_type(*type)));
}
out = new vhdl_var_ref(name.c_str(), type);
}
if (ivl_lpm_type(lpm) == IVL_LPM_PART_PV) {
vhdl_expr *off = part_select_base(scope, lpm);
assert(off);
out->set_slice(off, ivl_lpm_width(lpm) - 1);
}
return out;
}
static vhdl_expr *concat_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
vhdl_type *result_type =
vhdl_type::type_for(ivl_lpm_width(lpm), ivl_lpm_signed(lpm) != 0);
vhdl_binop_expr *expr =
new vhdl_binop_expr(VHDL_BINOP_CONCAT, result_type);
for (int i = ivl_lpm_selects(lpm) - 1; i >= 0; i--) {
vhdl_expr *e = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
if (NULL == e)
return NULL;
expr->add_expr(e);
}
return expr;
}
static vhdl_expr *binop_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm, vhdl_binop_t op)
{
unsigned out_width = ivl_lpm_width(lpm);
vhdl_type *result_type =
vhdl_type::type_for(out_width, ivl_lpm_signed(lpm) != 0);
vhdl_binop_expr *expr = new vhdl_binop_expr(op, result_type);
for (int i = 0; i < 2; i++) {
vhdl_expr *e = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
if (NULL == e)
return NULL;
expr->add_expr(e->cast(result_type));
}
if (op == VHDL_BINOP_MULT) {
// Need to resize the output to the desired size,
// as this does not happen automatically in VHDL
vhdl_fcall *resize =
new vhdl_fcall("Resize", vhdl_type::nsigned(out_width));
resize->add_expr(expr);
resize->add_expr(new vhdl_const_int(out_width));
return resize;
}
else
return expr;
}
static vhdl_expr *rel_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm, vhdl_binop_t op)
{
vhdl_binop_expr *expr = new vhdl_binop_expr(op, vhdl_type::boolean());
for (int i = 0; i < 2; i++) {
vhdl_expr *e = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
if (NULL == e)
return NULL;
expr->add_expr(e);
}
return expr;
}
static vhdl_expr *part_select_vp_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
vhdl_var_ref *selfrom = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
if (NULL == selfrom)
return NULL;
vhdl_expr *off = part_select_base(scope, lpm);;
if (NULL == off)
return NULL;
@ -102,28 +147,16 @@ static vhdl_expr *draw_part_select_vp_lpm(vhdl_scope *scope, ivl_lpm_t lpm)
return selfrom;
}
static int draw_part_select_pv_lpm(vhdl_arch *arch, ivl_lpm_t lpm)
{
vhdl_var_ref *selfrom = nexus_to_var_ref(arch->get_scope(), ivl_lpm_data(lpm, 0));
if (NULL == selfrom)
return 1;
vhdl_expr *off = part_select_base(arch->get_scope(), lpm);;
if (NULL == off)
return 1;
vhdl_var_ref *out = nexus_to_var_ref(arch->get_scope(), ivl_lpm_q(lpm, 0));
if (NULL == out)
return 1;
out->set_slice(off, ivl_lpm_width(lpm) - 1);
arch->add_stmt(new vhdl_cassign_stmt(out, selfrom));
return 0;
static vhdl_expr *part_select_pv_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
return nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
}
static vhdl_expr *draw_ufunc_lpm(vhdl_scope *scope, ivl_lpm_t lpm)
static vhdl_expr *ufunc_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
vhdl_fcall *fcall = new vhdl_fcall(ivl_lpm_basename(lpm), NULL);
ivl_scope_t f_scope = ivl_lpm_define(lpm);
vhdl_fcall *fcall = new vhdl_fcall(ivl_scope_basename(f_scope), NULL);
for (unsigned i = 0; i < ivl_lpm_size(lpm); i++) {
vhdl_var_ref *ref = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
@ -136,16 +169,19 @@ static vhdl_expr *draw_ufunc_lpm(vhdl_scope *scope, ivl_lpm_t lpm)
return fcall;
}
static vhdl_expr *draw_reduction_lpm(vhdl_scope *scope, ivl_lpm_t lpm,
const char *rfunc, bool invert)
static vhdl_expr *reduction_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm,
support_function_t f, bool invert)
{
vhdl_fcall *fcall = new vhdl_fcall(rfunc, vhdl_type::std_logic());
require_support_function(f);
vhdl_fcall *fcall = new vhdl_fcall(support_function::function_name(f),
vhdl_type::std_logic());
vhdl_var_ref *ref = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
if (NULL == ref)
return NULL;
fcall->add_expr(ref);
vhdl_type std_logic_vector(VHDL_TYPE_STD_LOGIC_VECTOR);
fcall->add_expr(ref->cast(&std_logic_vector));
if (invert)
return new vhdl_unaryop_expr
@ -154,7 +190,7 @@ static vhdl_expr *draw_reduction_lpm(vhdl_scope *scope, ivl_lpm_t lpm,
return fcall;
}
static vhdl_expr *draw_sign_extend_lpm(vhdl_scope *scope, ivl_lpm_t lpm)
static vhdl_expr *sign_extend_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
vhdl_expr *ref = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
if (ref)
@ -163,35 +199,88 @@ static vhdl_expr *draw_sign_extend_lpm(vhdl_scope *scope, ivl_lpm_t lpm)
return NULL;
}
vhdl_expr *lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
static vhdl_expr *array_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
ivl_signal_t array = ivl_lpm_array(lpm);
if (!seen_signal_before(array))
return NULL;
const char *renamed = get_renamed_signal(array).c_str();
vhdl_decl *adecl = scope->get_decl(renamed);
assert(adecl);
vhdl_type *atype = new vhdl_type(*adecl->get_type());
vhdl_expr *select = nexus_to_var_ref(scope, ivl_lpm_select(lpm));
if (NULL == select)
return NULL;
vhdl_var_ref *ref = new vhdl_var_ref(renamed, atype);
vhdl_type integer(VHDL_TYPE_INTEGER);
ref->set_slice(select->cast(&integer));
return ref;
}
static vhdl_expr *shift_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm,
vhdl_binop_t shift_op)
{
vhdl_expr *lhs = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
vhdl_expr *rhs = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 1));
if (!lhs || !rhs)
return NULL;
// The RHS must be an integer
vhdl_type integer(VHDL_TYPE_INTEGER);
vhdl_expr *r_cast = rhs->cast(&integer);
vhdl_type *rtype = new vhdl_type(*lhs->get_type());
return new vhdl_binop_expr(lhs, shift_op, r_cast, rtype);
}
static vhdl_expr *lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
switch (ivl_lpm_type(lpm)) {
case IVL_LPM_ADD:
return draw_binop_lpm(scope, lpm, VHDL_BINOP_ADD);
return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_ADD);
case IVL_LPM_SUB:
return draw_binop_lpm(scope, lpm, VHDL_BINOP_SUB);
return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_SUB);
case IVL_LPM_MULT:
return draw_binop_lpm(scope, lpm, VHDL_BINOP_MULT);
return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_MULT);
case IVL_LPM_CONCAT:
return draw_concat_lpm(scope, lpm);
return concat_lpm_to_expr(scope, lpm);
case IVL_LPM_CMP_GE:
return rel_lpm_to_expr(scope, lpm, VHDL_BINOP_GEQ);
case IVL_LPM_CMP_EQ:
case IVL_LPM_CMP_EEQ:
return rel_lpm_to_expr(scope, lpm, VHDL_BINOP_EQ);
case IVL_LPM_PART_VP:
return draw_part_select_vp_lpm(scope, lpm);
return part_select_vp_lpm_to_expr(scope, lpm);
case IVL_LPM_PART_PV:
return part_select_pv_lpm_to_expr(scope, lpm);
case IVL_LPM_UFUNC:
return draw_ufunc_lpm(scope, lpm);
return ufunc_lpm_to_expr(scope, lpm);
case IVL_LPM_RE_AND:
return draw_reduction_lpm(scope, lpm, "Reduce_AND", false);
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_AND, false);
case IVL_LPM_RE_NAND:
return draw_reduction_lpm(scope, lpm, "Reduce_AND", true);
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_AND, true);
case IVL_LPM_RE_NOR:
return draw_reduction_lpm(scope, lpm, "Reduce_OR", true);
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_OR, true);
case IVL_LPM_RE_OR:
return draw_reduction_lpm(scope, lpm, "Reduce_OR", false);
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_OR, false);
case IVL_LPM_RE_XOR:
return draw_reduction_lpm(scope, lpm, "Reduce_XOR", false);
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_XOR, false);
case IVL_LPM_RE_XNOR:
return draw_reduction_lpm(scope, lpm, "Reduce_XNOR", false);
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_XOR, false);
case IVL_LPM_SIGN_EXT:
return draw_sign_extend_lpm(scope, lpm);
return sign_extend_lpm_to_expr(scope, lpm);
case IVL_LPM_ARRAY:
return array_lpm_to_expr(scope, lpm);
case IVL_LPM_SHIFTL:
return shift_lpm_to_expr(scope, lpm, VHDL_BINOP_SL);
case IVL_LPM_SHIFTR:
return shift_lpm_to_expr(scope, lpm, VHDL_BINOP_SR);
default:
error("Unsupported LPM type: %d", ivl_lpm_type(lpm));
return NULL;
@ -200,18 +289,13 @@ vhdl_expr *lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
int draw_lpm(vhdl_arch *arch, ivl_lpm_t lpm)
{
if (ivl_lpm_type(lpm) == IVL_LPM_PART_PV)
return draw_part_select_pv_lpm(arch, lpm);
else {
vhdl_expr *f = lpm_to_expr(arch->get_scope(), lpm);
if (NULL == f)
return 1;
vhdl_var_ref *out = nexus_to_var_ref(arch->get_scope(), ivl_lpm_q(lpm, 0));
if (out)
arch->add_stmt(new vhdl_cassign_stmt(out, f));
vhdl_expr *f = lpm_to_expr(arch->get_scope(), lpm);
if (NULL == f)
return 0;
}
vhdl_var_ref *out = lpm_output(arch->get_scope(), lpm);
arch->add_stmt(new vhdl_cassign_stmt(out, f));
return 0;
}

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
@ -145,6 +31,8 @@ std::string strip_var(const std::string &str)
*/
static int generate_vhdl_process(vhdl_entity *ent, ivl_process_t proc)
{
set_active_entity(ent);
// Create a new process and store it in the entity's
// architecture. This needs to be done first or the
// parent link won't be valid (and draw_stmt needs this
@ -154,18 +42,14 @@ static int generate_vhdl_process(vhdl_entity *ent, ivl_process_t proc)
// If this is an initial process, push signal initialisation
// into the declarations
if (ivl_process_type(proc) == IVL_PR_INITIAL)
vhdl_proc->get_scope()->set_initializing(true);
vhdl_proc->get_scope()->set_initializing
(ivl_process_type(proc) == IVL_PR_INITIAL);
ivl_statement_t stmt = ivl_process_stmt(proc);
int rc = draw_stmt(vhdl_proc, vhdl_proc->get_container(), stmt);
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

@ -28,40 +28,39 @@
static vhdl_expr *translate_logic(vhdl_scope *scope, ivl_net_logic_t log);
static std::string make_safe_name(ivl_signal_t sig);
/*
* Given a nexus find a constant value in it that can be used
* as an initial signal value.
*/
static vhdl_expr *nexus_to_const(ivl_nexus_t nexus)
{
int nptrs = ivl_nexus_ptrs(nexus);
for (int i = 0; i < nptrs; i++) {
ivl_nexus_ptr_t nexus_ptr = ivl_nexus_ptr(nexus, i);
static vhdl_entity *g_active_entity = NULL;
ivl_net_const_t con;
if ((con = ivl_nexus_ptr_con(nexus_ptr))) {
if (ivl_const_width(con) == 1)
return new vhdl_const_bit(ivl_const_bits(con)[0]);
else
return new vhdl_const_bits
(ivl_const_bits(con), ivl_const_width(con),
ivl_const_signed(con) != 0);
}
else {
// Ignore other types of nexus pointer
}
}
return NULL;
vhdl_entity *get_active_entity()
{
return g_active_entity;
}
void set_active_entity(vhdl_entity *ent)
{
g_active_entity = ent;
}
/*
* Given a nexus and an architecture scope, find the first signal
* that is connected to the nexus, if there is one. Never return
* a reference to 'ignore' if it is found in the nexus.
* The types of VHDL object a nexus can be converted into.
*/
enum vhdl_nexus_obj_t {
NEXUS_TO_VAR_REF = 1<<0,
NEXUS_TO_CONST = 1<<1,
NEXUS_TO_OTHER = 1<<2,
};
#define NEXUS_TO_ANY \
NEXUS_TO_VAR_REF | NEXUS_TO_CONST | NEXUS_TO_OTHER
/*
* Given a nexus, generate a VHDL expression object to represent it.
* The allowed VHDL expression types are given by vhdl_nexus_obj_t.
*
* If a vhdl_var_ref is returned, the reference is guaranteed to be
* to a signal in arch_scope or its parent (the entity's ports).
*/
static vhdl_expr *nexus_to_expr(vhdl_scope *arch_scope, ivl_nexus_t nexus,
ivl_signal_t ignore = NULL)
int allowed = NEXUS_TO_ANY)
{
int nptrs = ivl_nexus_ptrs(nexus);
for (int i = 0; i < nptrs; i++) {
@ -70,61 +69,58 @@ static vhdl_expr *nexus_to_expr(vhdl_scope *arch_scope, ivl_nexus_t nexus,
ivl_signal_t sig;
ivl_net_logic_t log;
ivl_lpm_t lpm;
if ((sig = ivl_nexus_ptr_sig(nexus_ptr))) {
if (!seen_signal_before(sig) || sig == ignore)
ivl_net_const_t con;
ivl_switch_t sw;
if ((allowed & NEXUS_TO_VAR_REF) &&
(sig = ivl_nexus_ptr_sig(nexus_ptr))) {
if (!seen_signal_before(sig) ||
(find_scope_for_signal(sig) != arch_scope
&& find_scope_for_signal(sig) != arch_scope->get_parent()))
continue;
const char *signame = get_renamed_signal(sig).c_str();
vhdl_decl *decl = arch_scope->get_decl(signame);
if (NULL == decl)
continue; // Not in this scope
vhdl_type *type = new vhdl_type(*(decl->get_type()));
return new vhdl_var_ref(signame, type);
}
else if ((log = ivl_nexus_ptr_log(nexus_ptr))) {
else if ((allowed & NEXUS_TO_OTHER) &&
(log = ivl_nexus_ptr_log(nexus_ptr))) {
return translate_logic(arch_scope, log);
}
else if ((lpm = ivl_nexus_ptr_lpm(nexus_ptr))) {
vhdl_expr *e = lpm_to_expr(arch_scope, lpm);
if (e)
return e;
else if ((allowed & NEXUS_TO_OTHER) &&
(lpm = ivl_nexus_ptr_lpm(nexus_ptr))) {
return lpm_output(arch_scope, lpm);
}
else if ((allowed & NEXUS_TO_CONST) &&
(con = ivl_nexus_ptr_con(nexus_ptr))) {
if (ivl_const_width(con) == 1)
return new vhdl_const_bit(ivl_const_bits(con)[0]);
else
return new vhdl_const_bits
(ivl_const_bits(con), ivl_const_width(con),
ivl_const_signed(con) != 0);
}
else if ((allowed & NEXUS_TO_OTHER) &&
(sw = ivl_nexus_ptr_switch(nexus_ptr))) {
std::cout << "SWITCH type=" << ivl_switch_type(sw) << std::endl;
assert(false);
}
else {
// Ignore other types of nexus pointer
}
}
assert(false);
return NULL;
}
/*
* Guarantees the result will never be NULL.
*/
vhdl_var_ref *nexus_to_var_ref(vhdl_scope *arch_scope, ivl_nexus_t nexus)
{
int nptrs = ivl_nexus_ptrs(nexus);
for (int i = 0; i < nptrs; i++) {
ivl_nexus_ptr_t nexus_ptr = ivl_nexus_ptr(nexus, i);
ivl_signal_t sig;
if ((sig = ivl_nexus_ptr_sig(nexus_ptr))) {
if (!seen_signal_before(sig))
continue;
const char *signame = get_renamed_signal(sig).c_str();
vhdl_decl *decl = arch_scope->get_decl(signame);
if (NULL == decl)
continue; // Not in this scope
vhdl_type *type = new vhdl_type(*(decl->get_type()));
return new vhdl_var_ref(signame, type);
}
else {
// Ignore other types of nexus pointer
}
}
return NULL;
return dynamic_cast<vhdl_var_ref*>
(nexus_to_expr(arch_scope, nexus, NEXUS_TO_VAR_REF));
}
/*
@ -160,6 +156,42 @@ static vhdl_expr *input_to_expr(vhdl_scope *scope, vhdl_unaryop_t op,
return new vhdl_unaryop_expr(op, operand, vhdl_type::std_logic());
}
static void bufif_logic(vhdl_arch *arch, ivl_net_logic_t log, bool if0)
{
ivl_nexus_t output = ivl_logic_pin(log, 0);
vhdl_var_ref *lhs = nexus_to_var_ref(arch->get_scope(), output);
assert(lhs);
vhdl_expr *val = nexus_to_expr(arch->get_scope(), ivl_logic_pin(log, 1));
assert(val);
vhdl_expr *sel = nexus_to_expr(arch->get_scope(), ivl_logic_pin(log, 2));
assert(val);
vhdl_expr *on = new vhdl_const_bit(if0 ? '0' : '1');
vhdl_expr *cmp = new vhdl_binop_expr(sel, VHDL_BINOP_EQ, on, NULL);
ivl_signal_t sig = find_signal_named(lhs->get_name(), arch->get_scope());
char zbit;
switch (ivl_signal_type(sig)) {
case IVL_SIT_TRI0:
zbit = '0';
break;
case IVL_SIT_TRI1:
zbit = '1';
break;
case IVL_SIT_TRI:
default:
zbit = 'Z';
}
vhdl_const_bit *z = new vhdl_const_bit(zbit);
vhdl_cassign_stmt *cass = new vhdl_cassign_stmt(lhs, z);
cass->add_condition(val, cmp);
arch->add_stmt(cass);
}
static vhdl_expr *translate_logic(vhdl_scope *scope, ivl_net_logic_t log)
{
switch (ivl_logic_type(log)) {
@ -174,8 +206,12 @@ static vhdl_expr *translate_logic(vhdl_scope *scope, ivl_net_logic_t log)
case IVL_LO_BUF:
case IVL_LO_BUFZ:
return nexus_to_expr(scope, ivl_logic_pin(log, 1));
case IVL_LO_PULLUP:
return new vhdl_const_bit('1');
case IVL_LO_PULLDOWN:
return new vhdl_const_bit('0');
default:
error("Don't know how to translate logic type = %d",
error("Don't know how to translate logic type = %d to expression",
ivl_logic_type(log));
return NULL;
}
@ -191,16 +227,26 @@ static void declare_logic(vhdl_arch *arch, ivl_scope_t scope)
for (int i = 0; i < nlogs; i++) {
ivl_net_logic_t log = ivl_scope_log(scope, i);
// The output is always pin zero
ivl_nexus_t output = ivl_logic_pin(log, 0);
vhdl_var_ref *lhs =
dynamic_cast<vhdl_var_ref*>(nexus_to_expr(arch->get_scope(), output));
if (NULL == lhs)
continue; // Not suitable for continuous assignment
vhdl_expr *rhs = translate_logic(arch->get_scope(), log);
arch->add_stmt(new vhdl_cassign_stmt(lhs, rhs));
switch (ivl_logic_type(log)) {
case IVL_LO_BUFIF0:
bufif_logic(arch, log, true);
break;
case IVL_LO_BUFIF1:
bufif_logic(arch, log, false);
break;
default:
{
// The output is always pin zero
ivl_nexus_t output = ivl_logic_pin(log, 0);
vhdl_var_ref *lhs =
dynamic_cast<vhdl_var_ref*>(nexus_to_expr(arch->get_scope(), output));
if (NULL == lhs)
continue; // Not suitable for continuous assignment
vhdl_expr *rhs = translate_logic(arch->get_scope(), log);
arch->add_stmt(new vhdl_cassign_stmt(lhs, rhs));
}
}
}
}
@ -227,20 +273,6 @@ static std::string make_safe_name(ivl_signal_t sig)
return name;
}
/*
* Create a VHDL type for a Verilog signal.
*/
static vhdl_type *get_signal_type(ivl_signal_t sig)
{
int width = ivl_signal_width(sig);
if (width == 1)
return vhdl_type::std_logic();
else if (ivl_signal_signed(sig))
return vhdl_type::nsigned(width);
else
return vhdl_type::nunsigned(width);
}
/*
* Declare all signals and ports for a scope.
*/
@ -251,12 +283,39 @@ static void declare_signals(vhdl_entity *ent, ivl_scope_t scope)
ivl_signal_t sig = ivl_scope_sig(scope, i);
remember_signal(sig, ent->get_arch()->get_scope());
vhdl_type *sig_type =
vhdl_type::type_for(ivl_signal_width(sig), ivl_signal_signed(sig) != 0);
std::string name = make_safe_name(sig);
string name(make_safe_name(sig));
rename_signal(sig, name);
vhdl_type *sig_type;
unsigned dimensions = ivl_signal_dimensions(sig);
if (dimensions > 0) {
// Arrays are implemented by generating a separate type
// declaration for each array, and then declaring a
// signal of that type
if (dimensions > 1) {
error("> 1 dimension arrays not implemented yet");
return;
}
string type_name = name + "_Type";
vhdl_type *base_type =
vhdl_type::type_for(ivl_signal_width(sig), ivl_signal_signed(sig) != 0);
int lsb = ivl_signal_array_base(sig);
int msb = lsb + ivl_signal_array_count(sig) - 1;
vhdl_type *array_type =
vhdl_type::array_of(base_type, type_name, msb, lsb);
vhdl_decl *array_decl = new vhdl_type_decl(type_name.c_str(), array_type);
ent->get_arch()->get_scope()->add_decl(array_decl);
sig_type = new vhdl_type(*array_type);
}
else
sig_type =
vhdl_type::type_for(ivl_signal_width(sig), ivl_signal_signed(sig) != 0);
ivl_signal_port_t mode = ivl_signal_port(sig);
switch (mode) {
case IVL_SIP_NONE:
@ -266,7 +325,9 @@ static void declare_signals(vhdl_entity *ent, ivl_scope_t scope)
// A local signal can have a constant initializer in VHDL
// This may be found in the signal's nexus
// TODO: Make this work for multiple words
vhdl_expr *init = nexus_to_const(ivl_signal_nex(sig, 0));
vhdl_expr *init =
nexus_to_expr(ent->get_scope(), ivl_signal_nex(sig, 0),
NEXUS_TO_CONST);
if (init != NULL)
decl->set_initial(init);
@ -278,8 +339,23 @@ static void declare_signals(vhdl_entity *ent, ivl_scope_t scope)
(new vhdl_port_decl(name.c_str(), sig_type, VHDL_PORT_IN));
break;
case IVL_SIP_OUTPUT:
ent->get_scope()->add_decl
(new vhdl_port_decl(name.c_str(), sig_type, VHDL_PORT_OUT));
{
vhdl_port_decl *decl =
new vhdl_port_decl(name.c_str(), sig_type, VHDL_PORT_OUT);
// Check for constant values
// For outputs these must be continuous assigns of
// the constant to the port
vhdl_expr *init =
nexus_to_expr(ent->get_scope(), ivl_signal_nex(sig, 0),
NEXUS_TO_CONST);
if (init != NULL) {
vhdl_var_ref *ref = new vhdl_var_ref(name.c_str(), NULL);
ent->get_arch()->add_stmt(new vhdl_cassign_stmt(ref, init));
}
ent->get_scope()->add_decl(decl);
}
if (ivl_signal_type(sig) == IVL_SIT_REG) {
// A registered output
@ -292,7 +368,8 @@ static void declare_signals(vhdl_entity *ent, ivl_scope_t scope)
rename_signal(sig, newname.c_str());
vhdl_type *reg_type = new vhdl_type(*sig_type);
ent->get_arch()->get_scope()->add_decl(new vhdl_signal_decl(newname.c_str(), reg_type));
ent->get_arch()->get_scope()->add_decl
(new vhdl_signal_decl(newname.c_str(), reg_type));
// Create a concurrent assignment statement to
// connect the register to the output
@ -319,8 +396,9 @@ static void declare_lpm(vhdl_arch *arch, ivl_scope_t scope)
{
int nlpms = ivl_scope_lpms(scope);
for (int i = 0; i < nlpms; i++) {
if (draw_lpm(arch, ivl_scope_lpm(scope, i)) != 0)
error("Failed to translate LPM");
ivl_lpm_t lpm = ivl_scope_lpm(scope, i);
if (draw_lpm(arch, lpm) != 0)
error("Failed to translate LPM %s", ivl_lpm_name(lpm));
}
}
@ -344,6 +422,8 @@ static vhdl_entity *create_entity_for(ivl_scope_t scope)
// retain a 1-to-1 mapping of scope to VHDL element)
vhdl_arch *arch = new vhdl_arch(tname, "FromVerilog");
vhdl_entity *ent = new vhdl_entity(tname, derived_from, arch);
set_active_entity(ent);
// Locate all the signals in this module and add them to
// the architecture
@ -372,11 +452,12 @@ static vhdl_entity *create_entity_for(ivl_scope_t scope)
*/
static void map_signal(ivl_signal_t to, vhdl_entity *parent,
vhdl_comp_inst *inst)
{
{
// TODO: Work for multiple words
ivl_nexus_t nexus = ivl_signal_nex(to, 0);
vhdl_expr *to_e = nexus_to_expr(parent->get_arch()->get_scope(), nexus, to);
vhdl_expr *to_e = nexus_to_expr(parent->get_arch()->get_scope(),
nexus, NEXUS_TO_ANY);
assert(to_e);
// The expressions in a VHDL port map must be 'globally static'
@ -389,7 +470,49 @@ static void map_signal(ivl_signal_t to, vhdl_entity *parent,
std::string name = make_safe_name(to);
vhdl_var_ref *to_ref;
if ((to_ref = dynamic_cast<vhdl_var_ref*>(to_e))) {
inst->map_port(name.c_str(), to_ref);
// If we're mapping an output of this entity to an output of
// the child entity, then VHDL will not let us read the value
// of the signal (i.e. it must pass straight through).
// However, Verilog allows the signal to be read in the parent.
// To get around this we create an internal signal name_Sig
// that takes the value of the output and can be read.
vhdl_decl *decl =
parent->get_arch()->get_scope()->get_decl(to_ref->get_name());
vhdl_port_decl *pdecl;
if ((pdecl = dynamic_cast<vhdl_port_decl*>(decl))
&& pdecl->get_mode() == VHDL_PORT_OUT) {
// We need to create a readable signal to shadow this output
std::string shadow_name(to_ref->get_name());
shadow_name += "_Sig";
vhdl_signal_decl *shadow =
new vhdl_signal_decl(shadow_name.c_str(),
new vhdl_type(*decl->get_type()));
shadow->set_comment("Needed to make output readable");
parent->get_arch()->get_scope()->add_decl(shadow);
// Make a continuous assignment of the shadow to the output
parent->get_arch()->add_stmt
(new vhdl_cassign_stmt
(to_ref, new vhdl_var_ref(shadow_name.c_str(), NULL)));
// Make sure any future references to this signal read the
// shadow not the output
ivl_signal_t sig = find_signal_named(to_ref->get_name(),
parent->get_arch()->get_scope());
rename_signal(sig, shadow_name);
// Finally map the child port to the shadow signal
inst->map_port(name.c_str(),
new vhdl_var_ref(shadow_name.c_str(), NULL));
}
else {
// Not an output port declaration therefore we can
// definitely read it
inst->map_port(name.c_str(), to_ref);
}
}
else {
// Not a static expression
@ -450,6 +573,7 @@ static int draw_module(ivl_scope_t scope, ivl_scope_t parent)
if (NULL == ent)
ent = create_entity_for(scope);
assert(ent);
set_active_entity(ent);
// Is this module instantiated inside another?
if (parent != NULL) {
@ -470,11 +594,21 @@ static int draw_module(ivl_scope_t scope, ivl_scope_t parent)
}
// And an instantiation statement
std::string inst_name(ivl_scope_basename(scope));
string inst_name(ivl_scope_basename(scope));
if (inst_name == ent->get_name()) {
// Cannot have instance name the same as type in VHDL
inst_name += "_Inst";
}
// Need to replace any [ and ] characters that result
// from generate statements
string::size_type loc = inst_name.find('[', 0);
if (loc != string::npos)
inst_name.erase(loc, 1);
loc = inst_name.find(']', 0);
if (loc != string::npos)
inst_name.erase(loc, 1);
vhdl_comp_inst *inst =
new vhdl_comp_inst(inst_name.c_str(), ent->get_name().c_str());
@ -516,7 +650,9 @@ int draw_function(ivl_scope_t scope, ivl_scope_t parent)
int nsigs = ivl_scope_sigs(scope);
for (int i = 0; i < nsigs; i++) {
ivl_signal_t sig = ivl_scope_sig(scope, i);
vhdl_type *sigtype = get_signal_type(sig);
vhdl_type *sigtype =
vhdl_type::type_for(ivl_signal_width(sig),
ivl_signal_signed(sig) != 0);
std::string signame = make_safe_name(sig);

View File

@ -102,28 +102,24 @@ static int draw_noop(vhdl_procedural *proc, stmt_container *container,
return 0;
}
/*
* Generate an assignment of VHDL expr rhs to signal sig. This, unlike
* the procedure below, is a generic routine used for more than just
* Verilog signal assignment (e.g. it is used to expand ternary
* expressions).
*/
template <class T>
static T *make_vhdl_assignment(vhdl_procedural *proc, stmt_container *container,
ivl_signal_t sig, vhdl_expr *rhs, bool blocking,
vhdl_expr *base = NULL, unsigned lval_width = 0)
static vhdl_expr *make_assign_rhs(ivl_signal_t sig, vhdl_scope *scope,
ivl_expr_t e, vhdl_expr *base,
int lval_width)
{
std::string signame(get_renamed_signal(sig));
string signame(get_renamed_signal(sig));
vhdl_decl *decl = proc->get_scope()->get_decl(signame);
vhdl_decl *decl = scope->get_decl(signame);
assert(decl);
vhdl_expr *rhs = translate_expr(e);
if (rhs == NULL)
return rhs;
if (base == NULL)
rhs = rhs->cast(decl->get_type());
return rhs->cast(decl->get_type());
else if (decl->get_type()->get_name() == VHDL_TYPE_ARRAY)
return rhs->cast(decl->get_type()->get_base());
else {
vhdl_type integer(VHDL_TYPE_INTEGER);
base = base->cast(&integer);
// Doesn't make sense to part select on something that's
// not a vector
vhdl_type_name_t tname = decl->get_type()->get_name();
@ -131,81 +127,31 @@ static T *make_vhdl_assignment(vhdl_procedural *proc, stmt_container *container,
if (lval_width == 1) {
vhdl_type t(VHDL_TYPE_STD_LOGIC);
rhs = rhs->cast(&t);
return rhs->cast(&t);
}
else {
vhdl_type t(tname, lval_width);
rhs = rhs->cast(&t);
vhdl_type t(tname, lval_width - 1);
return rhs->cast(&t);
}
}
bool isvar = strip_var(signame) != signame;
}
// Where possible, move constant assignments into the
// declaration as initializers. This optimisation is only
// performed on assignments of constant values to prevent
// ordering problems.
// This also has another application: If this is an `inital'
// process and we haven't yet generated a `wait' statement then
// moving the assignment to the initialization preserves the
// expected Verilog behaviour: VHDL does not distinguish
// `initial' and `always' processes so an `always' process might
// be activatated before an `initial' process at time 0. The
// `always' process may then use the uninitialized signal value.
// The second test ensures that we only try to initialise
// internal signals not ports
if (proc->get_scope()->initializing()
&& ivl_signal_port(sig) == IVL_SIP_NONE
&& !decl->has_initial() && rhs->constant()
&& container == proc->get_container() // Top-level container
&& !isvar) {
decl->set_initial(rhs);
if (blocking && proc->get_scope()->allow_signal_assignment()) {
// This signal may be used e.g. in a loop test so we need
// to make a variable as well
blocking_assign_to(proc, sig);
// The signal may have been renamed by the above call
const std::string &renamed = get_renamed_signal(sig);
vhdl_var_ref *lval_ref = new vhdl_var_ref(renamed.c_str(), NULL);
vhdl_var_ref *sig_ref = new vhdl_var_ref(signame.c_str(), NULL);
if (base)
lval_ref->set_slice(base, lval_width-1);
T *a = new T(lval_ref, sig_ref);
container->add_stmt(a);
return a;
}
static vhdl_var_ref *make_assign_lhs(ivl_signal_t sig, vhdl_scope *scope,
vhdl_expr *base, int lval_width)
{
string signame(get_renamed_signal(sig));
vhdl_decl *decl = scope->get_decl(signame);
vhdl_type *ltype = new vhdl_type(*decl->get_type());
vhdl_var_ref *lval_ref = new vhdl_var_ref(signame.c_str(), ltype);
if (base) {
if (decl->get_type()->get_name() == VHDL_TYPE_ARRAY)
lval_ref->set_slice(base, 0);
else
return NULL; // No statement need be emitted
lval_ref->set_slice(base, lval_width - 1);
}
else {
if (blocking && proc->get_scope()->allow_signal_assignment()) {
// Remember we need to write the variable back to the
// original signal
blocking_assign_to(proc, sig);
// The signal may have been renamed by the above call
signame = get_renamed_signal(sig);
}
vhdl_type *ltype =
new vhdl_type(*proc->get_scope()->get_decl(signame)->get_type());
vhdl_var_ref *lval_ref = new vhdl_var_ref(signame.c_str(), ltype);
if (base)
lval_ref->set_slice(base, lval_width-1);
T *a = new T(lval_ref, rhs);
container->add_stmt(a);
return a;
}
return lval_ref;
}
/*
@ -213,7 +159,7 @@ static T *make_vhdl_assignment(vhdl_procedural *proc, stmt_container *container,
*/
template <class T>
static T *make_assignment(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt, bool blocking)
ivl_statement_t stmt, bool blocking, vhdl_expr *after)
{
int nlvals = ivl_stmt_lvals(stmt);
if (nlvals != 1) {
@ -227,9 +173,14 @@ static T *make_assignment(vhdl_procedural *proc, stmt_container *container,
vhdl_expr *base = NULL;
ivl_expr_t e_off = ivl_lval_part_off(lval);
if (NULL == e_off)
e_off = ivl_lval_idx(lval);
if (e_off) {
if ((base = translate_expr(e_off)) == NULL)
return NULL;
vhdl_type integer(VHDL_TYPE_INTEGER);
base = base->cast(&integer);
}
unsigned lval_width = ivl_lval_width(lval);
@ -238,27 +189,104 @@ static T *make_assignment(vhdl_procedural *proc, stmt_container *container,
if (ivl_expr_type(rval) == IVL_EX_TERNARY) {
// Expand ternary expressions into an if statement
vhdl_expr *test = translate_expr(ivl_expr_oper1(rval));
vhdl_expr *true_part = translate_expr(ivl_expr_oper2(rval));
vhdl_expr *false_part = translate_expr(ivl_expr_oper3(rval));
vhdl_expr *true_part =
make_assign_rhs(sig, proc->get_scope(),
ivl_expr_oper2(rval), base, lval_width);
vhdl_expr *false_part =
make_assign_rhs(sig, proc->get_scope(),
ivl_expr_oper3(rval), base, lval_width);
if (!test || !true_part || !false_part)
return NULL;
vhdl_if_stmt *vhdif = new vhdl_if_stmt(test);
make_vhdl_assignment<T>(proc, vhdif->get_then_container(), sig,
true_part, blocking, base, lval_width);
make_vhdl_assignment<T>(proc, vhdif->get_else_container(), sig,
false_part, blocking, base, lval_width);
// True part
{
vhdl_var_ref *lval_ref =
make_assign_lhs(sig, proc->get_scope(), base, lval_width);
T *a = new T(lval_ref, true_part);
vhdif->get_then_container()->add_stmt(a);
}
// False part
{
vhdl_var_ref *lval_ref =
make_assign_lhs(sig, proc->get_scope(), base, lval_width);
T *a = new T(lval_ref, false_part);
vhdif->get_else_container()->add_stmt(a);
}
container->add_stmt(vhdif);
return NULL;
}
else {
vhdl_expr *rhs = translate_expr(rval);
vhdl_expr *rhs =
make_assign_rhs(sig, proc->get_scope(), rval, base, lval_width);
if (NULL == rhs)
return NULL;
string signame(get_renamed_signal(sig));
vhdl_decl *decl = proc->get_scope()->get_decl(signame);
// Where possible, move constant assignments into the
// declaration as initializers. This optimisation is only
// performed on assignments of constant values to prevent
// ordering problems.
return make_vhdl_assignment<T>(proc, container, sig, rhs, blocking, base, lval_width);
// This also has another application: If this is an `inital'
// process and we haven't yet generated a `wait' statement then
// moving the assignment to the initialization preserves the
// expected Verilog behaviour: VHDL does not distinguish
// `initial' and `always' processes so an `always' process might
// be activatated before an `initial' process at time 0. The
// `always' process may then use the uninitialized signal value.
// The second test ensures that we only try to initialise
// internal signals not ports
if (proc->get_scope()->initializing()
&& ivl_signal_port(sig) == IVL_SIP_NONE
&& !decl->has_initial()
&& rhs->constant()
&& decl->get_type()->get_name() != VHDL_TYPE_ARRAY) {
// If this assignment is not in the top-level container
// it will not be made on all paths through the code
// This precludes any future extraction of an initialiser
if (container != proc->get_container())
decl->set_initial(NULL); // Default initial value
else {
decl->set_initial(rhs);
return NULL;
}
}
vhdl_var_ref *lval_ref =
make_assign_lhs(sig, proc->get_scope(), base, lval_width);
T *a = new T(lval_ref, rhs);
container->add_stmt(a);
ivl_expr_t i_delay;
if (NULL == after && (i_delay = ivl_stmt_delay_expr(stmt))) {
if ((after = translate_expr(i_delay)) == NULL)
return NULL;
// Need to make 'after' a time value
// we can do this by multiplying by 1ns
vhdl_type integer(VHDL_TYPE_INTEGER);
after = after->cast(&integer);
vhdl_expr *ns1 = new vhdl_const_time(1, TIME_UNIT_NS);
after = new vhdl_binop_expr(after, VHDL_BINOP_MULT, ns1,
vhdl_type::time());
}
if (after != NULL)
a->set_after(after);
return a;
}
}
else {
@ -277,22 +305,27 @@ static int draw_nbassign(vhdl_procedural *proc, stmt_container *container,
{
assert(proc->get_scope()->allow_signal_assignment());
vhdl_nbassign_stmt *a =
make_assignment<vhdl_nbassign_stmt>(proc, container, stmt, false);
if (a != NULL) {
// Assignment is a statement and not moved into the initialisation
if (after != NULL)
a->set_after(after);
}
make_assignment<vhdl_nbassign_stmt>(proc, container, stmt, false, after);
return 0;
}
static int draw_assign(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt)
{
make_assignment<vhdl_assign_stmt>(proc, container, stmt, true);
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<vhdl_nbassign_stmt>(proc, container, stmt, false, NULL);
container->add_stmt
(new vhdl_wait_stmt(VHDL_WAIT_FOR, new vhdl_const_time(0)));
}
else
make_assignment<vhdl_assign_stmt>(proc, container, stmt, true, NULL);
return 0;
}
@ -336,11 +369,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);
@ -360,25 +389,12 @@ static int draw_delay(vhdl_procedural *proc, stmt_container *container,
return 0;
}
/*
* Make edge detectors from the signals in `nexus' and add them
* to the expression `test'. Also adds the signals to the process
* sensitivity list. Type should be one of `rising_edge' or
* `falling_edge'.
*/
static void edge_detector(ivl_nexus_t nexus, vhdl_process *proc,
vhdl_binop_expr *test, const char *type)
{
vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope()->get_parent(), nexus);
vhdl_fcall *detect = new vhdl_fcall(type, vhdl_type::boolean());
detect->add_expr(ref);
test->add_expr(detect);
proc->add_sensitivity(ref->get_name().c_str());
}
/*
* A wait statement waits for a level change on a @(..) list of
* signals.
* signals. Purely combinatorial processes (i.e. no posedge/negedge
* events) produce a `wait on' statement at the end of the process.
* Sequential processes produce a `wait until' statement at the
* start of the process.
*/
static int draw_wait(vhdl_procedural *_proc, stmt_container *container,
ivl_statement_t stmt)
@ -386,82 +402,76 @@ static int draw_wait(vhdl_procedural *_proc, stmt_container *container,
// Wait statements only occur in processes
vhdl_process *proc = dynamic_cast<vhdl_process*>(_proc);
assert(proc); // Catch not process
vhdl_binop_expr *test =
new vhdl_binop_expr(VHDL_BINOP_OR, vhdl_type::boolean());
ivl_statement_t sub_stmt = ivl_stmt_sub_stmt(stmt);
// TODO: This should really be merged with the
// nexus_to_XXX code
int nevents = ivl_stmt_nevent(stmt);
bool combinatorial = true; // True if no negedge/posedge events
for (int i = 0; i < nevents; i++) {
ivl_event_t event = ivl_stmt_events(stmt, i);
if (ivl_event_npos(event) > 0 || ivl_event_nneg(event) > 0)
combinatorial = false;
}
// A list of the non-edge triggered signals to they can
// be added to the edge-detecting `if' statement later
string_list_t non_edges;
int nany = ivl_event_nany(event);
for (int i = 0; i < nany; i++) {
ivl_nexus_t nexus = ivl_event_any(event, i);
int nptrs = ivl_nexus_ptrs(nexus);
for (int j = 0; j < nptrs; j++) {
ivl_nexus_ptr_t nexus_ptr = ivl_nexus_ptr(nexus, j);
ivl_signal_t sig;
if ((sig = ivl_nexus_ptr_sig(nexus_ptr))) {
if (!seen_signal_before(sig))
continue;
std::string signame(get_renamed_signal(sig));
// Only add this signal to the sensitivity if it's part
// of the containing architecture (i.e. it has already
// been declared)
if (proc->get_scope()->get_parent()->have_declared(signame)) {
proc->add_sensitivity(signame.c_str());
non_edges.push_back(signame);
break;
}
}
else {
// Ignore all other types of nexus pointer
}
}
}
int nneg = ivl_event_nneg(event);
int npos = ivl_event_npos(event);
if (nneg + npos > 0) {
vhdl_binop_expr *test =
new vhdl_binop_expr(VHDL_BINOP_OR, vhdl_type::boolean());
// Generate falling_edge(..) calls for each negedge event
for (int i = 0; i < nneg; i++)
edge_detector(ivl_event_neg(event, i), proc, test, "falling_edge");
// Generate rising_edge(..) calls for each posedge event
for (int i = 0; i < npos; i++)
edge_detector(ivl_event_pos(event, i), proc, test, "rising_edge");
// Add Name'Event terms for each non-edge-triggered signal
string_list_t::iterator it;
for (it = non_edges.begin(); it != non_edges.end(); ++it) {
test->add_expr
(new vhdl_var_ref((*it + "'Event").c_str(),
vhdl_type::boolean()));
}
vhdl_if_stmt *edge_det = new vhdl_if_stmt(test);
container->add_stmt(edge_det);
if (combinatorial) {
vhdl_wait_stmt *wait = new vhdl_wait_stmt(VHDL_WAIT_ON);
for (int i = 0; i < nevents; i++) {
ivl_event_t event = ivl_stmt_events(stmt, i);
draw_stmt(proc, edge_det->get_then_container(), sub_stmt);
int nany = ivl_event_nany(event);
for (int i = 0; i < nany; i++) {
ivl_nexus_t nexus = ivl_event_any(event, i);
vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope(), nexus);
wait->add_sensitivity(ref->get_name());
delete ref;
}
}
else {
// Don't bother generating an edge detector if there
// are no edge-triggered events
draw_stmt(proc, container, sub_stmt);
draw_stmt(proc, container, ivl_stmt_sub_stmt(stmt));
container->add_stmt(wait);
}
else {
for (int i = 0; i < nevents; i++) {
ivl_event_t event = ivl_stmt_events(stmt, i);
int nany = ivl_event_nany(event);
for (int i = 0; i < nany; i++) {
ivl_nexus_t nexus = ivl_event_any(event, i);
vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope(), nexus);
ref->set_name(ref->get_name() + "'Event");
test->add_expr(ref);
}
int nneg = ivl_event_nneg(event);
for (int i = 0; i < nneg; i++) {
ivl_nexus_t nexus = ivl_event_neg(event, i);
vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope(), nexus);
vhdl_fcall *detect =
new vhdl_fcall("falling_edge", vhdl_type::boolean());
detect->add_expr(ref);
test->add_expr(detect);
}
int npos = ivl_event_npos(event);
for (int i = 0; i < npos; i++) {
ivl_nexus_t nexus = ivl_event_pos(event, i);
vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope(), nexus);
vhdl_fcall *detect =
new vhdl_fcall("rising_edge", vhdl_type::boolean());
detect->add_expr(ref);
test->add_expr(detect);
}
}
container->add_stmt(new vhdl_wait_stmt(VHDL_WAIT_UNTIL, test));
draw_stmt(proc, container, ivl_stmt_sub_stmt(stmt));
}
return 0;
@ -476,8 +486,9 @@ static int draw_if(vhdl_procedural *proc, stmt_container *container,
vhdl_if_stmt *vhdif = new vhdl_if_stmt(test);
draw_stmt(proc, vhdif->get_then_container(),
ivl_stmt_cond_true(stmt));
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);
ivl_statement_t cond_false_stmt = ivl_stmt_cond_false(stmt);
if (cond_false_stmt)
@ -596,6 +607,7 @@ int draw_stmt(vhdl_procedural *proc, stmt_container *container,
case IVL_ST_CONDIT:
return draw_if(proc, container, stmt);
case IVL_ST_CASE:
case IVL_ST_CASEX:
return draw_case(proc, container, stmt);
case IVL_ST_WHILE:
return draw_while(proc, container, stmt);

129
tgt-vhdl/support.cc Normal file
View File

@ -0,0 +1,129 @@
/*
* Support functions for VHDL output.
*
* Copyright (C) 2008 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "vhdl_target.h"
#include "support.hh"
#include <cassert>
#include <iostream>
void require_support_function(support_function_t f)
{
vhdl_scope *scope = get_active_entity()->get_arch()->get_scope();
if (!scope->have_declared(support_function::function_name(f)))
scope->add_decl(new support_function(f));
}
const char *support_function::function_name(support_function_t type)
{
switch (type) {
case SF_UNSIGNED_TO_BOOLEAN:
return "Unsigned_To_Boolean";
case SF_SIGNED_TO_BOOLEAN:
return "Signed_To_Boolean";
case SF_BOOLEAN_TO_LOGIC:
return "Boolean_To_Logic";
case SF_REDUCE_OR:
return "Reduce_OR";
case SF_REDUCE_AND:
return "Reduce_AND";
case SF_REDUCE_XOR:
return "Reduce_XOR";
default:
assert(false);
}
}
vhdl_type *support_function::function_type(support_function_t type)
{
switch (type) {
case SF_UNSIGNED_TO_BOOLEAN:
case SF_SIGNED_TO_BOOLEAN:
return vhdl_type::boolean();
case SF_BOOLEAN_TO_LOGIC:
case SF_REDUCE_OR:
case SF_REDUCE_AND:
case SF_REDUCE_XOR:
return vhdl_type::std_logic();
default:
assert(false);
}
}
void support_function::emit(std::ostream &of, int level) const
{
of << "function " << function_name(type_);
switch (type_) {
case SF_UNSIGNED_TO_BOOLEAN:
of << "(X : unsigned) return Boolean is" << nl_string(level)
<< "begin" << nl_string(indent(level))
<< "return X /= To_Unsigned(0, X'Length);" << nl_string(level);
break;
case SF_SIGNED_TO_BOOLEAN:
of << "(X : signed) return Boolean is" << nl_string(level)
<< "begin" << nl_string(indent(level))
<< "return X /= To_Signed(0, X'Length);" << nl_string (level);
break;
case SF_BOOLEAN_TO_LOGIC:
of << "(B : Boolean) return std_logic is" << nl_string(level)
<< "begin" << nl_string(indent(level))
<< "if B then" << nl_string(indent(indent(level)))
<< "return '1'" << nl_string(indent(level))
<< "else" << nl_string(indent(indent(level)))
<< "return '0'" << nl_string(indent(level))
<< "end if;" << nl_string(level);
break;
case SF_REDUCE_OR:
of << "(X : std_logic_vector) return std_logic is" << nl_string(level)
<< "begin" << nl_string(indent(level))
<< "for I in X'Range loop" << nl_string(indent(indent(level)))
<< "if X(I) = '1' then" << nl_string(indent(indent(indent(level))))
<< "return '1';" << nl_string(indent(indent(level)))
<< "end if;" << nl_string(indent(level))
<< "end loop;" << nl_string(indent(level))
<< "return '0';" << nl_string(level);
break;
case SF_REDUCE_AND:
of << "(X : std_logic_vector) return std_logic is" << nl_string(level)
<< "begin" << nl_string(indent(level))
<< "for I in X'Range loop" << nl_string(indent(indent(level)))
<< "if X(I) = '0' then" << nl_string(indent(indent(indent(level))))
<< "return '0';" << nl_string(indent(indent(level)))
<< "end if;" << nl_string(indent(level))
<< "end loop;" << nl_string(indent(level))
<< "return '1';" << nl_string(level);
break;
case SF_REDUCE_XOR:
of << "(X : std_logic_vector) return std_logic is"
<< nl_string(indent(level))
<< "variable R : std_logic := '0';" << nl_string(level)
<< "begin" << nl_string(indent(level))
<< "for I in X'Range loop" << nl_string(indent(indent(level)))
<< "R := X(I) xor R;" << nl_string(indent(level))
<< "end loop;" << nl_string(indent(level))
<< "return R;" << nl_string(level);
break;
default:
assert(false);
}
of << "end function;" << nl_string(level);
}

48
tgt-vhdl/support.hh Normal file
View File

@ -0,0 +1,48 @@
/*
* Support functions for VHDL output.
*
* Copyright (C) 2008 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef INC_SUPPORT_HH
#define INC_SUPPORT_HH
#include "vhdl_syntax.hh"
enum support_function_t {
SF_UNSIGNED_TO_BOOLEAN,
SF_SIGNED_TO_BOOLEAN,
SF_BOOLEAN_TO_LOGIC,
SF_REDUCE_OR,
SF_REDUCE_AND,
SF_REDUCE_XOR,
};
class support_function : public vhdl_function {
public:
support_function(support_function_t type)
: vhdl_function(function_name(type), function_type(type)),
type_(type) {}
void emit(std::ostream &of, int level) const;
static const char *function_name(support_function_t type);
static vhdl_type *function_type(support_function_t type);
private:
support_function_t type_;
};
#endif

View File

@ -15,6 +15,7 @@ package Verilog_Support is
-- Routines to implement Verilog reduction operators
function Reduce_OR(X : unsigned) return std_logic;
function Reduce_Or(X : std_logic) return std_logic;
-- Convert Boolean to std_logic
function Active_High(B : Boolean) return std_logic;
@ -40,6 +41,11 @@ package body Verilog_Support is
return '0';
end function;
function Reduce_OR(X : std_logic) return std_logic is
begin
return X;
end function;
function Active_High(B : Boolean) return std_logic is
begin
if B then

View File

@ -130,6 +130,18 @@ const std::string &get_renamed_signal(ivl_signal_t sig)
return g_known_signals[sig].renamed;
}
ivl_signal_t find_signal_named(const std::string &name, const vhdl_scope *scope)
{
signal_defn_map_t::const_iterator it;
for (it = g_known_signals.begin(); it != g_known_signals.end(); ++it) {
if (((*it).second.scope == scope
|| (*it).second.scope == scope->get_parent())
&& (*it).second.renamed == name)
return (*it).first;
}
assert(false);
}
ivl_design_t get_vhdl_design()
{
return g_design;

View File

@ -35,6 +35,13 @@ int indent(int level)
return level + VHDL_INDENT;
}
std::string nl_string(int level)
{
std::ostringstream ss;
newline(ss, level);
return ss.str();
}
/*
* Emit a newline and indent to the correct level.
*/

View File

@ -49,6 +49,7 @@ typedef std::list<vhdl_element*> element_list_t;
int indent(int level);
void newline(std::ostream &of, int level);
std::string nl_string(int level);
void blank_line(std::ostream &of, int level);
#endif

View File

@ -28,7 +28,7 @@
vhdl_scope::vhdl_scope()
: parent_(NULL), init_(false), sig_assign_(true)
{
}
vhdl_scope::~vhdl_scope()
@ -36,6 +36,13 @@ vhdl_scope::~vhdl_scope()
delete_children<vhdl_decl>(decls_);
}
void vhdl_scope::set_initializing(bool i)
{
init_ = i;
if (parent_)
parent_->set_initializing(i);
}
void vhdl_scope::add_decl(vhdl_decl *decl)
{
decls_.push_back(decl);
@ -57,7 +64,7 @@ bool vhdl_scope::have_declared(const std::string &name) const
return get_decl(name) != NULL;
}
vhdl_scope *vhdl_scope::get_parent()
vhdl_scope *vhdl_scope::get_parent() const
{
assert(parent_);
return parent_;
@ -88,6 +95,7 @@ void vhdl_entity::emit(std::ostream &of, int level) const
of << "use ieee.std_logic_1164.all;" << std::endl;
of << "use ieee.numeric_std.all;" << std::endl;
of << "use std.textio.all;" << std::endl;
//of << "use work.verilog_support.all;" << std::endl;
of << std::endl;
emit_comment(of, level);
@ -292,6 +300,22 @@ void vhdl_wait_stmt::emit(std::ostream &of, int level) const
of << " for ";
expr_->emit(of, level);
break;
case VHDL_WAIT_UNTIL:
assert(expr_);
of << " until ";
expr_->emit(of, level);
break;
case VHDL_WAIT_ON:
{
of << " on ";
string_list_t::const_iterator it = sensitivity_.begin();
while (it != sensitivity_.end()) {
of << *it;
if (++it != sensitivity_.end())
of << ", ";
}
}
break;
}
of << ";";
@ -312,10 +336,12 @@ const vhdl_type *vhdl_decl::get_type() const
}
void vhdl_decl::set_initial(vhdl_expr *initial)
{
if (initial_ != NULL)
delete initial_;
initial_ = initial;
{
if (!has_initial_) {
assert(initial_ == NULL);
initial_ = initial;
has_initial_ = true;
}
}
void vhdl_port_decl::emit(std::ostream &of, int level) const
@ -365,84 +391,19 @@ void vhdl_signal_decl::emit(std::ostream &of, int level) const
emit_comment(of, level, true);
}
void vhdl_type_decl::emit(std::ostream &of, int level) const
{
of << "type " << name_ << " is ";
of << type_->get_type_decl_string() << ";";
emit_comment(of, level, true);
}
vhdl_expr::~vhdl_expr()
{
if (type_ != NULL)
delete type_;
}
/*
* The default cast just assumes there's a VHDL cast function to
* do the job for us.
*/
vhdl_expr *vhdl_expr::cast(const vhdl_type *to)
{
//std::cout << "Cast: from=" << type_->get_string()
// << " (" << type_->get_width() << ") "
// << " to=" << to->get_string() << " ("
// << to->get_width() << ")" << std::endl;
if (to->get_name() == type_->get_name()) {
if (to->get_width() == type_->get_width())
return this; // Identical
else
return resize(to->get_width());
}
else if (to->get_name() == VHDL_TYPE_BOOLEAN) {
// '1' is true all else are false
vhdl_const_bit *one = new vhdl_const_bit('1');
return new vhdl_binop_expr
(this, VHDL_BINOP_EQ, one, vhdl_type::boolean());
}
else if (to->get_name() == VHDL_TYPE_INTEGER) {
vhdl_fcall *conv = new vhdl_fcall("To_Integer", new vhdl_type(*to));
conv->add_expr(this);
return conv;
}
else if (to->get_name() == VHDL_TYPE_STD_LOGIC &&
type_->get_name() == VHDL_TYPE_BOOLEAN) {
// Verilog assumes active-high logic and there
// is a special routine in verilog_support.vhd
// to do this for us
vhdl_fcall *ah = new vhdl_fcall("Active_High", vhdl_type::std_logic());
ah->add_expr(this);
return ah;
}
else {
// We have to cast the expression before resizing or the
// wrong sign bit may be extended (i.e. when casting between
// signed/unsigned *and* resizing)
vhdl_fcall *conv =
new vhdl_fcall(to->get_string().c_str(), new vhdl_type(*to));
conv->add_expr(this);
if (to->get_width() != type_->get_width())
return conv->resize(to->get_width());
else
return conv;
}
}
vhdl_expr *vhdl_expr::resize(int newwidth)
{
vhdl_type *rtype;
assert(type_);
if (type_->get_name() == VHDL_TYPE_SIGNED)
rtype = vhdl_type::nsigned(newwidth);
else if (type_->get_name() == VHDL_TYPE_UNSIGNED)
rtype = vhdl_type::nunsigned(newwidth);
else
return this; // Doesn't make sense to resize non-vector type
vhdl_fcall *resize = new vhdl_fcall("Resize", rtype);
resize->add_expr(this);
resize->add_expr(new vhdl_const_int(newwidth));
return resize;
}
void vhdl_expr_list::add_expr(vhdl_expr *e)
{
exprs_.push_back(e);
@ -485,22 +446,27 @@ vhdl_var_ref::~vhdl_var_ref()
void vhdl_var_ref::set_slice(vhdl_expr *s, int w)
{
assert(type_);
vhdl_type_name_t tname = type_->get_name();
assert(tname == VHDL_TYPE_UNSIGNED || tname == VHDL_TYPE_SIGNED);
slice_ = s;
slice_width_ = w;
vhdl_type_name_t tname = type_->get_name();
if (tname == VHDL_TYPE_ARRAY) {
type_ = new vhdl_type(*type_->get_base());
}
else {
assert(tname == VHDL_TYPE_UNSIGNED || tname == VHDL_TYPE_SIGNED);
if (type_)
delete type_;
if (w > 0)
type_ = new vhdl_type(tname, w);
else
type_ = vhdl_type::std_logic();
if (type_)
delete type_;
if (w > 0)
type_ = new vhdl_type(tname, w);
else
type_ = vhdl_type::std_logic();
}
}
void vhdl_var_ref::emit(std::ostream &of, int level) const
{
of << name_;
@ -576,40 +542,6 @@ vhdl_const_bits::vhdl_const_bits(const char *value, int width, bool issigned)
value_.push_back(*value++);
}
vhdl_expr *vhdl_const_bits::cast(const vhdl_type *to)
{
if (to->get_name() == VHDL_TYPE_STD_LOGIC) {
// VHDL won't let us cast directly between a vector and
// a scalar type
// But we don't need to here as we have the bits available
// Take the least significant bit
char lsb = value_[0];
return new vhdl_const_bit(lsb);
}
else if (to->get_name() == VHDL_TYPE_STD_LOGIC_VECTOR) {
// Don't need to do anything
return this;
}
else if (to->get_name() == VHDL_TYPE_SIGNED
|| to->get_name() == VHDL_TYPE_UNSIGNED) {
// Extend with sign bit
value_.resize(to->get_width(), value_[0]);
return this;
}
else if (to->get_name() == VHDL_TYPE_INTEGER) {
// Need to explicitly qualify the type (or the VHDL
// compiler gets confused between signed/unsigned)
qualified_ = true;
return vhdl_expr::cast(to);
}
else
return vhdl_expr::cast(to);
}
void vhdl_const_bits::emit(std::ostream &of, int level) const
{
if (qualified_)
@ -648,12 +580,36 @@ vhdl_cassign_stmt::~vhdl_cassign_stmt()
{
delete lhs_;
delete rhs_;
for (std::list<when_part_t>::const_iterator it = whens_.begin();
it != whens_.end();
++it) {
delete (*it).value;
delete (*it).cond;
}
}
void vhdl_cassign_stmt::add_condition(vhdl_expr *value, vhdl_expr *cond)
{
when_part_t when = { value, cond };
whens_.push_back(when);
}
void vhdl_cassign_stmt::emit(std::ostream &of, int level) const
{
lhs_->emit(of, level);
of << " <= ";
if (!whens_.empty()) {
for (std::list<when_part_t>::const_iterator it = whens_.begin();
it != whens_.end();
++it) {
(*it).value->emit(of, level);
of << " when ";
(*it).cond->emit(of, level);
of << " ";
}
of << "else ";
}
rhs_->emit(of, level);
of << ";";
}
@ -818,6 +774,7 @@ void vhdl_function::emit(std::ostream &of, int level) const
of << " return Verilog_Result;";
newline(of, level);
of << "end function;";
newline(of, level);
}
void vhdl_param_decl::emit(std::ostream &of, int level) const

View File

@ -56,6 +56,7 @@ public:
void emit(std::ostream &of, int level) const;
const std::string &get_name() const { return name_; }
void set_name(const std::string &name) { name_ = name; }
void set_slice(vhdl_expr *s, int w=0);
private:
std::string name_;
@ -139,6 +140,8 @@ public:
const std::string &get_value() const { return value_; }
vhdl_expr *cast(const vhdl_type *to);
private:
int bits_to_int() const;
std::string value_;
bool qualified_, signed_;
};
@ -148,6 +151,7 @@ public:
vhdl_const_bit(char bit)
: vhdl_expr(vhdl_type::std_logic(), true), bit_(bit) {}
void emit(std::ostream &of, int level) const;
vhdl_expr *cast(const vhdl_type *to);
private:
char bit_;
};
@ -158,7 +162,7 @@ enum time_unit_t {
class vhdl_const_time : public vhdl_expr {
public:
vhdl_const_time(int64_t value, time_unit_t units)
vhdl_const_time(int64_t value, time_unit_t units = TIME_UNIT_NS)
: vhdl_expr(vhdl_type::time(), true), value_(value), units_(units) {}
void emit(std::ostream &of, int level) const;
private:
@ -216,6 +220,8 @@ typedef std::list<vhdl_conc_stmt*> conc_stmt_list_t;
/*
* A concurrent signal assignment (i.e. not part of a process).
* Can have any number of `when' clauses, in which case the original
* rhs becomes the `else' part.
*/
class vhdl_cassign_stmt : public vhdl_conc_stmt {
public:
@ -224,9 +230,15 @@ public:
~vhdl_cassign_stmt();
void emit(std::ostream &of, int level) const;
void add_condition(vhdl_expr *value, vhdl_expr *cond);
private:
vhdl_var_ref *lhs_;
vhdl_expr *rhs_;
struct when_part_t {
vhdl_expr *value, *cond;
};
std::list<when_part_t> whens_;
};
/*
@ -295,6 +307,8 @@ public:
enum vhdl_wait_type_t {
VHDL_WAIT_INDEF, // Suspend indefinitely
VHDL_WAIT_FOR, // Wait for a constant amount of time
VHDL_WAIT_UNTIL, // Wait on an expression
VHDL_WAIT_ON, // Wait on a sensitivity list
};
/*
@ -309,9 +323,11 @@ public:
~vhdl_wait_stmt();
void emit(std::ostream &of, int level) const;
void add_sensitivity(const std::string &s) { sensitivity_.push_back(s); }
private:
vhdl_wait_type_t type_;
vhdl_expr *expr_;
string_list_t sensitivity_;
};
@ -415,18 +431,20 @@ class vhdl_decl : public vhdl_element {
public:
vhdl_decl(const char *name, vhdl_type *type = NULL,
vhdl_expr *initial = NULL)
: name_(name), type_(type), initial_(initial) {}
: name_(name), type_(type), initial_(initial),
has_initial_(initial != NULL) {}
virtual ~vhdl_decl();
const std::string &get_name() const { return name_; }
const vhdl_type *get_type() const;
void set_type(vhdl_type *t) { type_ = t; }
void set_initial(vhdl_expr *initial);
bool has_initial() const { return initial_ != NULL; }
bool has_initial() const { return has_initial_; }
protected:
std::string name_;
vhdl_type *type_;
vhdl_expr *initial_;
bool has_initial_;
};
typedef std::list<vhdl_decl*> decl_list_t;
@ -450,6 +468,14 @@ private:
};
class vhdl_type_decl : public vhdl_decl {
public:
vhdl_type_decl(const char *name, vhdl_type *base)
: vhdl_decl(name, base) {}
void emit(std::ostream &of, int level) const;
};
/*
* A variable declaration inside a process (although this isn't
* enforced here).
@ -501,6 +527,7 @@ public:
: vhdl_decl(name, type), mode_(mode) {}
void emit(std::ostream &of, int level) const;
vhdl_port_mode_t get_mode() const { return mode_; }
private:
vhdl_port_mode_t mode_;
};
@ -548,14 +575,14 @@ public:
void add_decl(vhdl_decl *decl);
vhdl_decl *get_decl(const std::string &name) const;
bool have_declared(const std::string &name) const;
vhdl_scope *get_parent();
vhdl_scope *get_parent() const;
bool empty() const { return decls_.empty(); }
const decl_list_t &get_decls() const { return decls_; }
void set_parent(vhdl_scope *p) { parent_ = p; }
bool initializing() const { return init_; }
void set_initializing(bool i) { init_ = i; }
void set_initializing(bool i);
void set_allow_signal_assignment(bool b) { sig_assign_ = b; }
bool allow_signal_assignment() const { return sig_assign_; }
@ -587,7 +614,7 @@ class vhdl_function : public vhdl_decl, public vhdl_procedural {
public:
vhdl_function(const char *name, vhdl_type *ret_type);
void emit(std::ostream &of, int level) const;
virtual void emit(std::ostream &of, int level) const;
vhdl_scope *get_scope() { return &variables_; }
void add_param(vhdl_param_decl *p) { scope_.add_decl(p); }
private:

View File

@ -7,8 +7,12 @@
#include "vhdl_syntax.hh"
#include "vhdl_type.hh"
#include "support.hh"
#include <string>
using namespace std;
void error(const char *fmt, ...);
int draw_scope(ivl_scope_t scope, void *_parent);
@ -17,29 +21,29 @@ int draw_stmt(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt);
int draw_lpm(vhdl_arch *arch, ivl_lpm_t lpm);
vhdl_expr *lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm);
vhdl_expr *translate_expr(ivl_expr_t e);
vhdl_var_ref *lpm_output(vhdl_scope *scope, ivl_lpm_t lpm);
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();
void set_active_entity(vhdl_entity *ent);
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);
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);
void require_support_function(support_function_t f);
#endif /* #ifndef INC_VHDL_TARGET_H */

View File

@ -20,7 +20,9 @@
#include "vhdl_type.hh"
#include <cassert>
#include <sstream>
#include <iostream>
vhdl_type *vhdl_type::std_logic()
@ -63,6 +65,12 @@ vhdl_type *vhdl_type::time()
return new vhdl_type(VHDL_TYPE_TIME);
}
vhdl_type *vhdl_type::get_base() const
{
assert(name_ == VHDL_TYPE_ARRAY);
return base_;
}
/*
* This is just the name of the type, without any parameters.
*/
@ -87,6 +95,9 @@ std::string vhdl_type::get_string() const
return std::string("signed");
case VHDL_TYPE_UNSIGNED:
return std::string("unsigned");
case VHDL_TYPE_ARRAY:
// Each array has its own type declaration
return array_name_;
default:
return std::string("BadType");
}
@ -112,11 +123,46 @@ std::string vhdl_type::get_decl_string() const
}
}
/*
* Like get_decl_string but completely expands array declarations.
*/
std::string vhdl_type::get_type_decl_string() const
{
switch (name_) {
case VHDL_TYPE_ARRAY:
{
std::ostringstream ss;
ss << "array (" << msb_ << " downto "
<< lsb_ << ") of "
<< base_->get_decl_string();
return ss.str();
}
default:
return get_decl_string();
}
}
void vhdl_type::emit(std::ostream &of, int level) const
{
of << get_decl_string();
}
vhdl_type::vhdl_type(const vhdl_type &other)
: name_(other.name_), msb_(other.msb_), lsb_(other.lsb_),
array_name_(other.array_name_)
{
if (other.base_ != NULL)
base_ = new vhdl_type(*other.base_);
else
base_ = NULL;
}
vhdl_type::~vhdl_type()
{
if (base_ != NULL)
delete base_;
}
vhdl_type *vhdl_type::std_logic_vector(int msb, int lsb)
{
return new vhdl_type(VHDL_TYPE_STD_LOGIC_VECTOR, msb, lsb);
@ -131,3 +177,8 @@ vhdl_type *vhdl_type::type_for(int width, bool issigned, int lsb)
else
return vhdl_type::nunsigned(width, lsb);
}
vhdl_type *vhdl_type::array_of(vhdl_type *b, std::string &n, int m, int l)
{
return new vhdl_type(b, n, m, l);
}

View File

@ -34,6 +34,7 @@ enum vhdl_type_name_t {
VHDL_TYPE_SIGNED,
VHDL_TYPE_UNSIGNED,
VHDL_TYPE_TIME,
VHDL_TYPE_ARRAY,
};
/*
@ -43,14 +44,27 @@ enum vhdl_type_name_t {
*/
class vhdl_type : public vhdl_element {
public:
// Scalar constructor
vhdl_type(vhdl_type_name_t name, int msb = 0, int lsb = 0)
: name_(name), msb_(msb), lsb_(lsb) {}
virtual ~vhdl_type() {}
: name_(name), msb_(msb), lsb_(lsb), base_(NULL) {}
// Array constructor
vhdl_type(vhdl_type *base, const std::string &array_name,
int msb, int lsb)
: name_(VHDL_TYPE_ARRAY), msb_(msb), lsb_(lsb), base_(base),
array_name_(array_name) {}
// Copy constructor
vhdl_type(const vhdl_type &other);
virtual ~vhdl_type();
void emit(std::ostream &of, int level) const;
vhdl_type_name_t get_name() const { return name_; }
std::string get_string() const;
std::string get_decl_string() const;
std::string get_type_decl_string() const;
vhdl_type *get_base() const;
int get_width() const { return msb_ - lsb_ + 1; }
int get_msb() const { return msb_; }
int get_lsb() const { return lsb_; }
@ -67,9 +81,12 @@ public:
static vhdl_type *time();
static vhdl_type *type_for(int width, bool issigned, int lsb=0);
static vhdl_type *array_of(vhdl_type *b, std::string &n, int m, int l);
protected:
vhdl_type_name_t name_;
int msb_, lsb_;
vhdl_type *base_; // Array base type for VHDL_TYPE_ARRAY
std::string array_name_; // Type name for the array `type array_name_ is ...'
};
#endif