1 /*
2  *  Generate code to convert between VHDL types.
3  *
4  *  Copyright (C) 2008-2012  Nick Gasson (nick@nickg.me.uk)
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License along
17  *  with this program; if not, write to the Free Software Foundation, Inc.,
18  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20 
21 #include "vhdl_syntax.hh"
22 
23 #include "vhdl_target.h"
24 #include "support.hh"
25 
26 #include <cassert>
27 #include <iostream>
28 
cast(const vhdl_type * to)29 vhdl_expr *vhdl_expr::cast(const vhdl_type *to)
30 {
31 #if 0
32    std::cout << "Cast: from=" << type_->get_string()
33              << " (" << type_->get_width() << ") "
34              << " to=" << to->get_string() << " ("
35              << to->get_width() << ")" << std::endl;
36 #endif
37 
38    // If this expression hasn't been given a type then
39    // we can't generate any type conversion code
40    if (NULL == type_)
41       return this;
42 
43    if (to->get_name() == type_->get_name()) {
44       if (to->get_width() == type_->get_width())
45          return this;  // Identical
46       else
47          return resize(to->get_width());
48    }
49    else {
50       switch (to->get_name()) {
51       case VHDL_TYPE_BOOLEAN:
52          return to_boolean();
53       case VHDL_TYPE_INTEGER:
54          return to_integer();
55       case VHDL_TYPE_UNSIGNED:
56       case VHDL_TYPE_SIGNED:
57       case VHDL_TYPE_STD_LOGIC_VECTOR:
58          return to_vector(to->get_name(), to->get_width());
59       case VHDL_TYPE_STD_LOGIC:
60          return to_std_logic();
61       case VHDL_TYPE_STD_ULOGIC:
62          return to_std_ulogic();
63       case VHDL_TYPE_STRING:
64          return to_string();
65       default:
66          assert(false);
67       }
68    }
69    assert(false);
70    return NULL;
71 }
72 
73 /*
74  * Generate code to cast an expression to a vector type (std_logic_vector,
75  * signed, unsigned).
76  */
to_vector(vhdl_type_name_t name,int w)77 vhdl_expr *vhdl_expr::to_vector(vhdl_type_name_t name, int w)
78 {
79    if (type_->get_name() == VHDL_TYPE_STD_LOGIC) {
80       vhdl_expr *others = w == 1 ? NULL : new vhdl_const_bit('0');
81       vhdl_bit_spec_expr *bs =
82          new vhdl_bit_spec_expr(new vhdl_type(name, w - 1, 0), others);
83       bs->add_bit(0, this);
84 
85       return bs;
86    }
87    else {
88       // We have to cast the expression before resizing or the
89       // wrong sign bit may be extended (i.e. when casting between
90       // signed/unsigned *and* resizing)
91       vhdl_type *t = new vhdl_type(name, w - 1, 0);
92       vhdl_fcall *conv = new vhdl_fcall(t->get_string().c_str(), t);
93       conv->add_expr(this);
94 
95       if (w != type_->get_width())
96          return conv->resize(w);
97       else
98          return conv;
99    }
100 }
101 
102 /*
103  * Convert a generic expression to an Integer.
104  */
to_integer()105 vhdl_expr *vhdl_expr::to_integer()
106 {
107    vhdl_fcall *conv;
108    if (type_->get_name() == VHDL_TYPE_STD_LOGIC) {
109       require_support_function(SF_LOGIC_TO_INTEGER);
110       conv = new vhdl_fcall(support_function::function_name(SF_LOGIC_TO_INTEGER),
111                             vhdl_type::integer());
112    }
113    else
114       conv = new vhdl_fcall("To_Integer", vhdl_type::integer());
115 
116    conv->add_expr(this);
117 
118    return conv;
119 }
120 
to_string()121 vhdl_expr *vhdl_expr::to_string()
122 {
123    bool numeric = type_->get_name() == VHDL_TYPE_UNSIGNED
124       || type_->get_name() == VHDL_TYPE_SIGNED;
125 
126    if (numeric) {
127       vhdl_fcall *image = new vhdl_fcall("integer'image", vhdl_type::string());
128       image->add_expr(this->cast(vhdl_type::integer()));
129       return image;
130    }
131    else {
132       // Assume type'image exists
133       vhdl_fcall *image = new vhdl_fcall(type_->get_string() + "'image",
134                                          vhdl_type::string());
135       image->add_expr(this);
136       return image;
137    }
138 }
139 
140 /*
141  * Convert a generic expression to a Boolean.
142  */
to_boolean()143 vhdl_expr *vhdl_expr::to_boolean()
144 {
145    if (type_->get_name() == VHDL_TYPE_STD_LOGIC) {
146       // '1' is true all else are false
147       vhdl_const_bit *one = new vhdl_const_bit('1');
148       return new vhdl_binop_expr
149          (this, VHDL_BINOP_EQ, one, vhdl_type::boolean());
150    }
151    else if (type_->get_name() == VHDL_TYPE_UNSIGNED) {
152       // Need to use a support function for this conversion
153       require_support_function(SF_UNSIGNED_TO_BOOLEAN);
154 
155       vhdl_fcall *conv =
156          new vhdl_fcall(support_function::function_name(SF_UNSIGNED_TO_BOOLEAN),
157                         vhdl_type::boolean());
158       conv->add_expr(this);
159       return conv;
160    }
161    else if (type_->get_name() == VHDL_TYPE_SIGNED) {
162       require_support_function(SF_SIGNED_TO_BOOLEAN);
163 
164       vhdl_fcall *conv =
165          new vhdl_fcall(support_function::function_name(SF_SIGNED_TO_BOOLEAN),
166                         vhdl_type::boolean());
167       conv->add_expr(this);
168       return conv;
169    }
170    assert(false);
171    return NULL;
172 }
173 
174 /*
175  * Generate code to convert and expression to std_logic.
176  */
to_std_logic()177 vhdl_expr *vhdl_expr::to_std_logic()
178 {
179    if (type_->get_name() == VHDL_TYPE_BOOLEAN) {
180       require_support_function(SF_BOOLEAN_TO_LOGIC);
181 
182       vhdl_fcall *ah =
183          new vhdl_fcall(support_function::function_name(SF_BOOLEAN_TO_LOGIC),
184                         vhdl_type::std_logic());
185       ah->add_expr(this);
186 
187       return ah;
188    }
189    else if (type_->get_name() == VHDL_TYPE_SIGNED) {
190       require_support_function(SF_SIGNED_TO_LOGIC);
191 
192       vhdl_fcall *ah =
193          new vhdl_fcall(support_function::function_name(SF_SIGNED_TO_LOGIC),
194                         vhdl_type::std_logic());
195       ah->add_expr(this);
196 
197       return ah;
198    }
199    else if (type_->get_name() == VHDL_TYPE_UNSIGNED) {
200       require_support_function(SF_UNSIGNED_TO_LOGIC);
201 
202       vhdl_fcall *ah =
203          new vhdl_fcall(support_function::function_name(SF_UNSIGNED_TO_LOGIC),
204                         vhdl_type::std_logic());
205       ah->add_expr(this);
206 
207       return ah;
208    }
209    assert(false);
210    return NULL;
211 }
212 
to_std_ulogic()213 vhdl_expr *vhdl_expr::to_std_ulogic()
214 {
215    if (type_->get_name() == VHDL_TYPE_STD_LOGIC) {
216       vhdl_fcall *f = new vhdl_fcall("std_logic", vhdl_type::std_logic());
217       f->add_expr(this);
218       return f;
219    }
220    else
221       assert(false);
222    return NULL;
223 }
224 
225 /*
226  * Change the width of a signed/unsigned type.
227  */
resize(int newwidth)228 vhdl_expr *vhdl_expr::resize(int newwidth)
229 {
230    vhdl_type *rtype;
231    assert(type_);
232    if (type_->get_name() == VHDL_TYPE_SIGNED)
233       rtype = vhdl_type::nsigned(newwidth);
234    else if (type_->get_name() == VHDL_TYPE_UNSIGNED)
235       rtype = vhdl_type::nunsigned(newwidth);
236    else if (type_->get_name() == VHDL_TYPE_STD_LOGIC) {
237       // Pad it with zeros
238       vhdl_expr* zeros = new vhdl_const_bits(string(newwidth - 1, '0').c_str(),
239                                              newwidth - 1, false, true);
240 
241       vhdl_binop_expr* concat =
242          new vhdl_binop_expr(zeros, VHDL_BINOP_CONCAT, this,
243                              vhdl_type::nunsigned(newwidth));
244       return concat;
245    }
246    else
247       return this;   // Doesn't make sense to resize non-vector type
248 
249    vhdl_fcall *resizef = new vhdl_fcall("Resize", rtype);
250    resizef->add_expr(this);
251    resizef->add_expr(new vhdl_const_int(newwidth));
252 
253    return resizef;
254 }
255 
to_vector(vhdl_type_name_t name,int w)256 vhdl_expr *vhdl_const_int::to_vector(vhdl_type_name_t name, int w)
257 {
258    if (name == VHDL_TYPE_SIGNED || name == VHDL_TYPE_UNSIGNED) {
259 
260       const char *fname = name == VHDL_TYPE_SIGNED
261          ? "To_Signed" : "To_Unsigned";
262       vhdl_fcall *conv = new vhdl_fcall(fname, new vhdl_type(name, w - 1, 0));
263       conv->add_expr(this);
264       conv->add_expr(new vhdl_const_int(w));
265 
266       return conv;
267    }
268    else
269       return vhdl_expr::to_vector(name, w);
270 }
271 
bits_to_int() const272 int64_t vhdl_const_bits::bits_to_int() const
273 {
274    char msb = value_[value_.size() - 1];
275    int64_t result = 0, bit;
276    for (int i = sizeof(int64_t)*8 - 1; i >= 0; i--) {
277       if (i > (int)value_.size() - 1)
278          bit = (msb == '1' && signed_) ? 1 : 0;
279       else
280          bit = value_[i] == '1' ? 1 : 0;
281       result = (result << 1) | bit;
282    }
283 
284    return result;
285 }
286 
to_std_logic()287 vhdl_expr *vhdl_const_bits::to_std_logic()
288 {
289    // VHDL won't let us cast directly between a vector and
290    // a scalar type
291    // But we don't need to here as we have the bits available
292 
293    // Take the least significant bit
294    char lsb = value_[0];
295 
296    return new vhdl_const_bit(lsb);
297 }
298 
sign_bit() const299 char vhdl_const_bits::sign_bit() const
300 {
301    return signed_ ? value_[value_.length()-1] : '0';
302 }
303 
to_vector(vhdl_type_name_t name,int w)304 vhdl_expr *vhdl_const_bits::to_vector(vhdl_type_name_t name, int w)
305 {
306    if (name == VHDL_TYPE_STD_LOGIC_VECTOR) {
307       // Don't need to do anything
308       return this;
309    }
310    else if (name == VHDL_TYPE_SIGNED || name == VHDL_TYPE_UNSIGNED) {
311       // Extend with sign bit
312       value_.resize(w, sign_bit());
313       return this;
314    }
315    assert(false);
316    return NULL;
317 }
318 
to_integer()319 vhdl_expr *vhdl_const_bits::to_integer()
320 {
321    return new vhdl_const_int(bits_to_int());
322 }
323 
resize(int w)324 vhdl_expr *vhdl_const_bits::resize(int w)
325 {
326    // Rather than generating a call to Resize, when can just sign-extend
327    // the bits here. As well as looking better, this avoids any ambiguity
328    // between which of the signed/unsigned versions of Resize to use.
329 
330    value_.resize(w, sign_bit());
331    return this;
332 }
333 
to_integer()334 vhdl_expr *vhdl_const_bit::to_integer()
335 {
336    return new vhdl_const_int(bit_ == '1' ? 1 : 0);
337 }
338 
to_boolean()339 vhdl_expr *vhdl_const_bit::to_boolean()
340 {
341    return new vhdl_const_bool(bit_ == '1');
342 }
343 
to_std_ulogic()344 vhdl_expr *vhdl_const_bit::to_std_ulogic()
345 {
346    return this;
347 }
348 
to_vector(vhdl_type_name_t name,int w)349 vhdl_expr *vhdl_const_bit::to_vector(vhdl_type_name_t name, int w)
350 {
351    // Zero-extend this bit to the correct width
352    return (new vhdl_const_bits(&bit_, 1, name == VHDL_TYPE_SIGNED))->resize(w);
353 }
354