diff --git a/tgt-vhdl/Makefile.in b/tgt-vhdl/Makefile.in index 1b2f15a1c..36727ce67 100644 --- a/tgt-vhdl/Makefile.in +++ b/tgt-vhdl/Makefile.in @@ -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 diff --git a/tgt-vhdl/cast.cc b/tgt-vhdl/cast.cc new file mode 100644 index 000000000..7d83a67f4 --- /dev/null +++ b/tgt-vhdl/cast.cc @@ -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 +#include + +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); +} diff --git a/tgt-vhdl/expr.cc b/tgt-vhdl/expr.cc index fadc0d3d4..f64ac4ac6 100644 --- a/tgt-vhdl/expr.cc +++ b/tgt-vhdl/expr.cc @@ -19,14 +19,17 @@ */ #include "vhdl_target.h" +#include "support.hh" #include #include +#include + /* - * 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(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); diff --git a/tgt-vhdl/lpm.cc b/tgt-vhdl/lpm.cc index b6e6ae874..3221b85c6 100644 --- a/tgt-vhdl/lpm.cc +++ b/tgt-vhdl/lpm.cc @@ -23,54 +23,6 @@ #include #include -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; } diff --git a/tgt-vhdl/process.cc b/tgt-vhdl/process.cc index 73cc51e92..5276feed6 100644 --- a/tgt-vhdl/process.cc +++ b/tgt-vhdl/process.cc @@ -24,120 +24,6 @@ #include #include #include -#include - -/* - * 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 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 diff --git a/tgt-vhdl/scope.cc b/tgt-vhdl/scope.cc index a798efbd9..ac1202fff 100644 --- a/tgt-vhdl/scope.cc +++ b/tgt-vhdl/scope.cc @@ -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 + (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(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(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(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(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); diff --git a/tgt-vhdl/stmt.cc b/tgt-vhdl/stmt.cc index 302833b75..87efac0e2 100644 --- a/tgt-vhdl/stmt.cc +++ b/tgt-vhdl/stmt.cc @@ -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 -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 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(proc, vhdif->get_then_container(), sig, - true_part, blocking, base, lval_width); - make_vhdl_assignment(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(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(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(proc, container, stmt, false, after); + return 0; } static int draw_assign(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt) { - make_assignment(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(proc, container, stmt, false, NULL); + + container->add_stmt + (new vhdl_wait_stmt(VHDL_WAIT_FOR, new vhdl_const_time(0))); + } + else + make_assignment(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(_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); diff --git a/tgt-vhdl/support.cc b/tgt-vhdl/support.cc new file mode 100644 index 000000000..159618739 --- /dev/null +++ b/tgt-vhdl/support.cc @@ -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 +#include + +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); +} diff --git a/tgt-vhdl/support.hh b/tgt-vhdl/support.hh new file mode 100644 index 000000000..a5e247c94 --- /dev/null +++ b/tgt-vhdl/support.hh @@ -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 diff --git a/tgt-vhdl/verilog_support.vhd b/tgt-vhdl/verilog_support.vhd index 34ec5df20..b52cecd89 100644 --- a/tgt-vhdl/verilog_support.vhd +++ b/tgt-vhdl/verilog_support.vhd @@ -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 diff --git a/tgt-vhdl/vhdl.cc b/tgt-vhdl/vhdl.cc index a6d2b61c8..35c30e4f9 100644 --- a/tgt-vhdl/vhdl.cc +++ b/tgt-vhdl/vhdl.cc @@ -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; diff --git a/tgt-vhdl/vhdl_element.cc b/tgt-vhdl/vhdl_element.cc index e631319fc..fc09fe158 100644 --- a/tgt-vhdl/vhdl_element.cc +++ b/tgt-vhdl/vhdl_element.cc @@ -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. */ diff --git a/tgt-vhdl/vhdl_element.hh b/tgt-vhdl/vhdl_element.hh index abccdfdb2..f4248500b 100644 --- a/tgt-vhdl/vhdl_element.hh +++ b/tgt-vhdl/vhdl_element.hh @@ -49,6 +49,7 @@ typedef std::list 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 diff --git a/tgt-vhdl/vhdl_syntax.cc b/tgt-vhdl/vhdl_syntax.cc index d509ebc00..3cc70853a 100644 --- a/tgt-vhdl/vhdl_syntax.cc +++ b/tgt-vhdl/vhdl_syntax.cc @@ -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(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::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::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 diff --git a/tgt-vhdl/vhdl_syntax.hh b/tgt-vhdl/vhdl_syntax.hh index 89b26d217..fcf06286a 100644 --- a/tgt-vhdl/vhdl_syntax.hh +++ b/tgt-vhdl/vhdl_syntax.hh @@ -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 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 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 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: diff --git a/tgt-vhdl/vhdl_target.h b/tgt-vhdl/vhdl_target.h index c70870ca2..20584eecf 100644 --- a/tgt-vhdl/vhdl_target.h +++ b/tgt-vhdl/vhdl_target.h @@ -7,8 +7,12 @@ #include "vhdl_syntax.hh" #include "vhdl_type.hh" +#include "support.hh" + #include +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 */ diff --git a/tgt-vhdl/vhdl_type.cc b/tgt-vhdl/vhdl_type.cc index b431b02f1..e14c3fbe9 100644 --- a/tgt-vhdl/vhdl_type.cc +++ b/tgt-vhdl/vhdl_type.cc @@ -20,7 +20,9 @@ #include "vhdl_type.hh" +#include #include +#include 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); +} diff --git a/tgt-vhdl/vhdl_type.hh b/tgt-vhdl/vhdl_type.hh index 7b5878478..f3bfb2211 100644 --- a/tgt-vhdl/vhdl_type.hh +++ b/tgt-vhdl/vhdl_type.hh @@ -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