iverilog/tgt-vhdl/expr.cc

438 lines
13 KiB
C++
Raw Normal View History

2008-06-04 15:59:04 +02:00
/*
* VHDL code generation for expressions.
*
* 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 <iostream>
#include <cassert>
/*
2008-07-10 20:27:17 +02:00
* Change the signedness of a vector.
*/
2008-07-10 20:27:17 +02:00
static vhdl_expr *change_signedness(vhdl_expr *e, bool issigned)
{
int msb = e->get_type()->get_msb();
int lsb = e->get_type()->get_lsb();
vhdl_type u(issigned ? VHDL_TYPE_SIGNED : VHDL_TYPE_UNSIGNED, msb, lsb);
return e->cast(&u);
}
/*
* Convert a constant Verilog string to a constant VHDL string.
*/
static vhdl_expr *translate_string(ivl_expr_t e)
{
// TODO: May need to inspect or escape parts of this
const char *str = ivl_expr_string(e);
return new vhdl_const_string(str);
}
2008-06-04 15:59:04 +02:00
2008-06-07 12:24:09 +02:00
/*
* A reference to a signal in an expression. It's assumed that the
* signal has already been defined elsewhere.
*/
2008-06-21 17:17:44 +02:00
static vhdl_var_ref *translate_signal(ivl_expr_t e)
2008-06-07 12:24:09 +02:00
{
ivl_signal_t sig = ivl_expr_signal(e);
const vhdl_scope *scope = find_scope_for_signal(sig);
assert(scope);
2008-06-14 19:11:10 +02:00
const char *renamed = get_renamed_signal(sig).c_str();
vhdl_decl *decl = scope->get_decl(renamed);
2008-06-14 19:11:10 +02:00
assert(decl);
// 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;
2008-06-07 12:24:09 +02:00
}
/*
* A numeric literal ends up as std_logic bit string.
*/
static vhdl_expr *translate_number(ivl_expr_t e)
{
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_unary(ivl_expr_t e)
{
2008-06-12 11:56:28 +02:00
vhdl_expr *operand = translate_expr(ivl_expr_oper1(e));
if (NULL == operand)
return NULL;
bool should_be_signed = ivl_expr_signed(e) != 0;
if (operand->get_type()->get_name() == VHDL_TYPE_UNSIGNED && should_be_signed) {
//operand->print();
//std::cout << "^ should be signed but is not" << std::endl;
2008-07-10 20:27:17 +02:00
operand = change_signedness(operand, true);
}
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;
2008-07-10 20:27:17 +02:00
operand = change_signedness(operand, false);
}
char opcode = ivl_expr_opcode(e);
switch (opcode) {
2008-06-12 11:56:28 +02:00
case '!':
2008-06-19 17:08:33 +02:00
case '~':
2008-06-12 11:56:28 +02:00
return new vhdl_unaryop_expr
(VHDL_UNARYOP_NOT, operand, new vhdl_type(*operand->get_type()));
case 'N': // NOR
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;
}
2008-06-12 11:56:28 +02:00
default:
error("No translation for unary opcode '%c'\n",
ivl_expr_opcode(e));
2008-06-14 18:09:31 +02:00
delete operand;
return NULL;
}
}
/*
* Translate a numeric binary operator (+, -, etc.) to
* a VHDL equivalent using the numeric_std package.
*/
static vhdl_expr *translate_numeric(vhdl_expr *lhs, vhdl_expr *rhs,
vhdl_binop_t op)
2008-06-23 14:04:28 +02:00
{
// May need to make either side Boolean for operators
// to work
vhdl_type boolean(VHDL_TYPE_BOOLEAN);
if (lhs->get_type()->get_name() == VHDL_TYPE_BOOLEAN)
rhs = rhs->cast(&boolean);
else if (rhs->get_type()->get_name() == VHDL_TYPE_BOOLEAN)
lhs = lhs->cast(&boolean);
2008-06-23 14:04:28 +02:00
vhdl_type *rtype = new vhdl_type(*lhs->get_type());
return new vhdl_binop_expr(lhs, op, rhs, rtype);
2008-06-14 18:09:31 +02:00
}
2008-06-16 13:20:28 +02:00
static vhdl_expr *translate_relation(vhdl_expr *lhs, vhdl_expr *rhs,
vhdl_binop_t op)
{
// Generate any necessary casts
// Arbitrarily, the RHS is casted to the type of the LHS
vhdl_expr *r_cast = rhs->cast(lhs->get_type());
return new vhdl_binop_expr(lhs, op, r_cast, vhdl_type::boolean());
2008-06-16 13:20:28 +02:00
}
/*
* Like translate_relation but both operands must be Boolean.
*/
static vhdl_expr *translate_logical(vhdl_expr *lhs, vhdl_expr *rhs,
vhdl_binop_t op)
{
vhdl_type boolean(VHDL_TYPE_BOOLEAN);
return translate_relation(lhs->cast(&boolean), rhs->cast(&boolean), op);
}
2008-06-23 13:14:12 +02:00
static vhdl_expr *translate_shift(vhdl_expr *lhs, vhdl_expr *rhs,
vhdl_binop_t op)
{
// 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, op, r_cast, rtype);
}
2008-06-14 18:09:31 +02:00
static vhdl_expr *translate_binary(ivl_expr_t e)
{
vhdl_expr *lhs = translate_expr(ivl_expr_oper1(e));
if (NULL == lhs)
return NULL;
vhdl_expr *rhs = translate_expr(ivl_expr_oper2(e));
if (NULL == rhs)
return NULL;
2008-06-23 14:04:28 +02:00
int lwidth = lhs->get_type()->get_width();
int rwidth = rhs->get_type()->get_width();
2008-07-08 01:20:31 +02:00
int result_width = ivl_expr_width(e);
2008-06-19 17:08:33 +02:00
// For === and !== we need to compare std_logic_vectors
// rather than signeds
2008-07-08 01:20:31 +02:00
vhdl_type std_logic_vector(VHDL_TYPE_STD_LOGIC_VECTOR, result_width-1, 0);
vhdl_type_name_t ltype = lhs->get_type()->get_name();
vhdl_type_name_t rtype = rhs->get_type()->get_name();
2008-06-19 17:08:33 +02:00
bool vectorop =
(ltype == VHDL_TYPE_SIGNED || ltype == VHDL_TYPE_UNSIGNED) &&
(rtype == VHDL_TYPE_SIGNED || rtype == VHDL_TYPE_UNSIGNED);
2008-07-08 01:20:31 +02:00
// May need to resize the left or right hand side
if (vectorop) {
if (lwidth < rwidth)
lhs = lhs->resize(rwidth);
else if (rwidth < lwidth)
rhs = rhs->resize(lwidth);
}
vhdl_expr *result;
2008-06-14 18:09:31 +02:00
switch (ivl_expr_opcode(e)) {
case '+':
result = translate_numeric(lhs, rhs, VHDL_BINOP_ADD);
break;
2008-06-21 17:17:44 +02:00
case '-':
result = translate_numeric(lhs, rhs, VHDL_BINOP_SUB);
break;
2008-07-07 20:27:52 +02:00
case '*':
result = translate_numeric(lhs, rhs, VHDL_BINOP_MULT);
break;
2008-06-16 13:20:28 +02:00
case 'e':
result = translate_relation(lhs, rhs, VHDL_BINOP_EQ);
break;
2008-06-19 17:08:33 +02:00
case 'E':
if (vectorop)
result = translate_relation(lhs->cast(&std_logic_vector),
2008-06-19 17:08:33 +02:00
rhs->cast(&std_logic_vector), VHDL_BINOP_EQ);
else
result = translate_relation(lhs, rhs, VHDL_BINOP_EQ);
break;
2008-06-19 17:08:33 +02:00
case 'n':
result = translate_relation(lhs, rhs, VHDL_BINOP_NEQ);
break;
2008-06-19 17:08:33 +02:00
case 'N':
if (vectorop)
result = translate_relation(lhs->cast(&std_logic_vector),
2008-06-19 17:08:33 +02:00
rhs->cast(&std_logic_vector), VHDL_BINOP_NEQ);
else
result = translate_relation(lhs, rhs, VHDL_BINOP_NEQ);
break;
2008-06-21 16:05:48 +02:00
case '&': // Bitwise AND
result = translate_numeric(lhs, rhs, VHDL_BINOP_AND);
break;
2008-07-04 12:10:20 +02:00
case 'a': // Logical AND
result = translate_logical(lhs, rhs, VHDL_BINOP_AND);
break;
2008-07-04 13:05:49 +02:00
case '|': // Bitwise OR
result = translate_numeric(lhs, rhs, VHDL_BINOP_OR);
break;
case 'o': // Logical OR
result = translate_logical(lhs, rhs, VHDL_BINOP_OR);
break;
2008-06-21 16:16:22 +02:00
case '<':
result = translate_relation(lhs, rhs, VHDL_BINOP_LT);
break;
2008-07-07 22:19:59 +02:00
case 'L':
result = translate_relation(lhs, rhs, VHDL_BINOP_LEQ);
break;
2008-06-21 16:16:22 +02:00
case '>':
result = translate_relation(lhs, rhs, VHDL_BINOP_GT);
break;
2008-07-07 22:19:59 +02:00
case 'G':
result = translate_relation(lhs, rhs, VHDL_BINOP_GEQ);
break;
2008-06-21 16:19:33 +02:00
case 'l':
result = translate_shift(lhs, rhs, VHDL_BINOP_SL);
break;
2008-06-21 16:19:33 +02:00
case 'r':
result = translate_shift(lhs, rhs, VHDL_BINOP_SR);
break;
case '^':
result = translate_numeric(lhs, rhs, VHDL_BINOP_XOR);
break;
2008-06-14 18:09:31 +02:00
default:
error("No translation for binary opcode '%c'\n",
ivl_expr_opcode(e));
delete lhs;
delete rhs;
2008-06-12 11:56:28 +02:00
return NULL;
}
if (NULL == result)
return NULL;
if (vectorop) {
bool should_be_signed = ivl_expr_signed(e) != 0;
if (result->get_type()->get_name() == VHDL_TYPE_UNSIGNED && should_be_signed) {
//result->print();
//std::cout << "^ should be signed but is not" << std::endl;
2008-07-10 20:27:17 +02:00
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;
2008-07-10 20:27:17 +02:00
result = change_signedness(result, false);
}
int actual_width = result->get_type()->get_width();
if (actual_width != result_width) {
//result->print();
//std::cout << "^ should be " << result_width << " but is " << actual_width << std::endl;
}
}
return result;
}
static vhdl_expr *translate_select(ivl_expr_t e)
2008-06-21 17:17:44 +02:00
{
2008-07-07 22:19:59 +02:00
vhdl_var_ref *from =
dynamic_cast<vhdl_var_ref*>(translate_expr(ivl_expr_oper1(e)));
2008-06-21 17:17:44 +02:00
if (NULL == from)
2008-07-07 22:19:59 +02:00
return NULL;
ivl_expr_t o2 = ivl_expr_oper2(e);
if (o2) {
vhdl_expr *base = translate_expr(ivl_expr_oper2(e));
if (NULL == base)
2008-07-08 01:20:31 +02:00
return NULL;
2008-07-07 22:19:59 +02:00
vhdl_type integer(VHDL_TYPE_INTEGER);
from->set_slice(base->cast(&integer), ivl_expr_width(e) - 1);
return from;
}
else
return from->resize(ivl_expr_width(e));
2008-06-21 17:17:44 +02:00
}
2008-07-06 19:21:34 +02:00
static vhdl_type *expr_to_vhdl_type(ivl_expr_t e)
{
if (ivl_expr_signed(e))
return vhdl_type::nsigned(ivl_expr_width(e));
else
return vhdl_type::nunsigned(ivl_expr_width(e));
}
template <class T>
static T *translate_parms(T *t, ivl_expr_t e)
{
int nparams = ivl_expr_parms(e);
for (int i = 0; i < nparams; i++) {
vhdl_expr *param = translate_expr(ivl_expr_parm(e, i));
if (NULL == param)
return NULL;
t->add_expr(param);
}
return t;
}
static vhdl_expr *translate_ufunc(ivl_expr_t e)
{
ivl_scope_t defscope = ivl_expr_def(e);
ivl_scope_t parentscope = ivl_scope_parent(defscope);
assert(ivl_scope_type(parentscope) == IVL_SCT_MODULE);
// A function is always declared in a module, which should have
// a corresponding entity by this point: so we can get type
// information, etc. from the declaration
vhdl_entity *parent_ent = find_entity(ivl_scope_tname(parentscope));
assert(parent_ent);
const char *funcname = ivl_scope_tname(defscope);
2008-07-06 19:21:34 +02:00
vhdl_type *rettype = expr_to_vhdl_type(e);
vhdl_fcall *fcall = new vhdl_fcall(funcname, rettype);
2008-07-06 19:21:34 +02:00
return translate_parms<vhdl_fcall>(fcall, e);
}
static vhdl_expr *translate_ternary(ivl_expr_t e)
{
error("Ternary expression only supported as RHS of assignment");
return NULL;
}
2008-07-06 19:21:34 +02:00
static vhdl_expr *translate_concat(ivl_expr_t e)
{
vhdl_type *rtype = expr_to_vhdl_type(e);
vhdl_binop_expr *concat = new vhdl_binop_expr(VHDL_BINOP_CONCAT, rtype);
return translate_parms<vhdl_binop_expr>(concat, e);
}
2008-06-04 15:59:04 +02:00
/*
* Generate a VHDL expression from a Verilog expression.
*/
vhdl_expr *translate_expr(ivl_expr_t e)
{
2008-06-21 16:03:36 +02:00
assert(e);
ivl_expr_type_t type = ivl_expr_type(e);
switch (type) {
case IVL_EX_STRING:
return translate_string(e);
2008-06-07 12:24:09 +02:00
case IVL_EX_SIGNAL:
return translate_signal(e);
case IVL_EX_NUMBER:
return translate_number(e);
case IVL_EX_UNARY:
return translate_unary(e);
2008-06-14 18:09:31 +02:00
case IVL_EX_BINARY:
return translate_binary(e);
2008-06-21 17:17:44 +02:00
case IVL_EX_SELECT:
return translate_select(e);
case IVL_EX_UFUNC:
return translate_ufunc(e);
case IVL_EX_TERNARY:
return translate_ternary(e);
2008-07-06 19:21:34 +02:00
case IVL_EX_CONCAT:
return translate_concat(e);
default:
error("No VHDL translation for expression at %s:%d (type = %d)",
ivl_expr_file(e), ivl_expr_lineno(e), type);
return NULL;
}
2008-06-04 15:59:04 +02:00
}