mirror of
https://github.com/steveicarus/iverilog.git
synced 2026-08-22 22:06:47 +02:00
Compare commits
480
Commits
performance
...
gsoc-end
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb1d4fd278 | ||
|
|
e1deba51ab | ||
|
|
d53014a07f | ||
|
|
7865264de0 | ||
|
|
86661c1538 | ||
|
|
e2dd7425bd | ||
|
|
e01b16e586 | ||
|
|
6adc3c9f13 | ||
|
|
2c1426a44d | ||
|
|
1f5b11246e | ||
|
|
d3caa547ba | ||
|
|
026d941734 | ||
|
|
a577ee447b | ||
|
|
d7b85c42a0 | ||
|
|
dea54df71b | ||
|
|
a3929330b0 | ||
|
|
c404b761b7 | ||
|
|
9b1f2d5971 | ||
|
|
9d7e4ac15f | ||
|
|
d55a3a073a | ||
|
|
01bf741983 | ||
|
|
bf3734110e | ||
|
|
6dcf936807 | ||
|
|
8e0bf3ebff | ||
|
|
380e3a8121 | ||
|
|
1f8ff7ff8d | ||
|
|
b1f1c11441 | ||
|
|
c918cf4a46 | ||
|
|
f835c7569e | ||
|
|
5aecd044c5 | ||
|
|
2a389a9abe | ||
|
|
6527262348 | ||
|
|
79e1273814 | ||
|
|
709c850e69 | ||
|
|
7ed8c0915d | ||
|
|
eef1c968dc | ||
|
|
0985158090 | ||
|
|
090ae5fa56 | ||
|
|
13cb81f4bb | ||
|
|
ef66ca6498 | ||
|
|
d43452f88d | ||
|
|
e719dc250a | ||
|
|
bb0efda526 | ||
|
|
e4d0a92d7c | ||
|
|
28d782e13c | ||
|
|
6f5f700cb9 | ||
|
|
a17924f819 | ||
|
|
893aae2ca4 | ||
|
|
4cbec1c817 | ||
|
|
d84771428a | ||
|
|
484d3ea36b | ||
|
|
c71e930ffa | ||
|
|
15584a4f81 | ||
|
|
f86f454956 | ||
|
|
e01e038cf9 | ||
|
|
c849dfeec4 | ||
|
|
8d7b03576c | ||
|
|
72019959a8 | ||
|
|
9565ea1034 | ||
|
|
49a2693357 | ||
|
|
10a5ca199d | ||
|
|
c2f622327f | ||
|
|
0e2628a3fb | ||
|
|
45dfa28dba | ||
|
|
c8cbac58f5 | ||
|
|
c1b5424ca6 | ||
|
|
c706c94e38 | ||
|
|
fad8abee34 | ||
|
|
9448c5939c | ||
|
|
752a90dc2f | ||
|
|
a60216ec15 | ||
|
|
5d0df8d880 | ||
|
|
1cd13ecdbd | ||
|
|
aeec93a322 | ||
|
|
61930c3b6e | ||
|
|
296f1bacc1 | ||
|
|
bb80b432e6 | ||
|
|
be551a6b68 | ||
|
|
e0834f7f38 | ||
|
|
a26d91557b | ||
|
|
3f73c9bb54 | ||
|
|
09f3eb4a36 | ||
|
|
33885ed891 | ||
|
|
d21277f1b9 | ||
|
|
7b0f675785 | ||
|
|
db339b8fc3 | ||
|
|
4ca7f2a9bd | ||
|
|
3191b3efcb | ||
|
|
1a41ac3145 | ||
|
|
6d4dd5ae3b | ||
|
|
6cb3d86d15 | ||
|
|
7a4f85d382 | ||
|
|
5207be0778 | ||
|
|
2ceb0539af | ||
|
|
f9c67e21b2 | ||
|
|
baa2363e85 | ||
|
|
e5b8abfb23 | ||
|
|
9f6f711f8d | ||
|
|
9a5b7bb0b0 | ||
|
|
eaf1cc9120 | ||
|
|
3bcd42dc8f | ||
|
|
744fbed783 | ||
|
|
c9454b346e | ||
|
|
5ec2c37e7e | ||
|
|
25602e487d | ||
|
|
e037ffd952 | ||
|
|
48c1a7982c | ||
|
|
a842b327c7 | ||
|
|
f8034d69ef | ||
|
|
d94dac28a8 | ||
|
|
680c6f0503 | ||
|
|
39717989a8 | ||
|
|
c26b7ce675 | ||
|
|
c6f6ea7358 | ||
|
|
c0c838f1bc | ||
|
|
1a45e9164f | ||
|
|
8a5f129e56 | ||
|
|
65c2ceb89d | ||
|
|
7a2e9c02cd | ||
|
|
f88415b1d7 | ||
|
|
1250010696 | ||
|
|
506a0ba7d6 | ||
|
|
1d4914c590 | ||
|
|
3e28a10676 | ||
|
|
78028a3310 | ||
|
|
b9cecbef64 | ||
|
|
8b32096e2a | ||
|
|
ba462eb8b7 | ||
|
|
5a09819729 | ||
|
|
d3296d4895 | ||
|
|
39c9c54760 | ||
|
|
8bee5b1108 | ||
|
|
e4c2400eb2 | ||
|
|
1409207def | ||
|
|
30fdadc525 | ||
|
|
a5db0297b0 | ||
|
|
e25f946ac0 | ||
|
|
2f4f075005 | ||
|
|
3ca85491ee | ||
|
|
d8351ec1b2 | ||
|
|
77508b9afa | ||
|
|
38de6ebf3a | ||
|
|
af3ee49f57 | ||
|
|
0cb6ea34d7 | ||
|
|
b6df73d3b9 | ||
|
|
2d79e1a2e0 | ||
|
|
7b311b6adb | ||
|
|
df4a380e42 | ||
|
|
6ff80e80a4 | ||
|
|
8b6b111541 | ||
|
|
fd8f01e317 | ||
|
|
00317dd47f | ||
|
|
e9637f6d11 | ||
|
|
1f9ed2c5ec | ||
|
|
7677b59650 | ||
|
|
9cf4792d53 | ||
|
|
4d9f029000 | ||
|
|
9916686c24 | ||
|
|
c86377790f | ||
|
|
2a791bfb38 | ||
|
|
1d3ac6bc1f | ||
|
|
3fa5a04947 | ||
|
|
7c5b0f737c | ||
|
|
c116808fdb | ||
|
|
553f3d77a9 | ||
|
|
642fbe9fc5 | ||
|
|
395a2248d8 | ||
|
|
be67cae29f | ||
|
|
f62a00bedb | ||
|
|
646a6056a2 | ||
|
|
4504c2bceb | ||
|
|
d343db34fd | ||
|
|
b5e12077b2 | ||
|
|
f3753ea9ad | ||
|
|
45e289d32d | ||
|
|
40cabff44f | ||
|
|
a9c98ad5f2 | ||
|
|
b8e758edf0 | ||
|
|
0b48f69b4e | ||
|
|
d1e7e325b7 | ||
|
|
75b1db0add | ||
|
|
6e965523a1 | ||
|
|
8589c0691b | ||
|
|
99ef8ec4f1 | ||
|
|
d22c9a8b05 | ||
|
|
f84f50842c | ||
|
|
65720f49fe | ||
|
|
6243736481 | ||
|
|
e331e4831b | ||
|
|
78ee61558d | ||
|
|
07c4ff7ea7 | ||
|
|
e5422dddd2 | ||
|
|
6af201ea03 | ||
|
|
27a40cfdcd | ||
|
|
3bd480a375 | ||
|
|
aa951af2b7 | ||
|
|
55747bf79d | ||
|
|
1cd7689d03 | ||
|
|
bd5cc96956 | ||
|
|
a0dbb1aa5d | ||
|
|
4777966b4c | ||
|
|
860a74ddd8 | ||
|
|
2bb645e0bc | ||
|
|
47db80315c | ||
|
|
0348664512 | ||
|
|
3987e0753d | ||
|
|
6b73cc39a5 | ||
|
|
b0de1a8d7e | ||
|
|
37fe6e4219 | ||
|
|
89cdbf63be | ||
|
|
7f955cc070 | ||
|
|
4db5b9d7ed | ||
|
|
dadd145d09 | ||
|
|
bdf5ee7ab7 | ||
|
|
ebaa4c7d5d | ||
|
|
c33600bcc3 | ||
|
|
85d2cc78d6 | ||
|
|
18071562ba | ||
|
|
5aeff6d47d | ||
|
|
a298b03735 | ||
|
|
3d0a2b55ce | ||
|
|
19871efd5a | ||
|
|
1410c339de | ||
|
|
96d32b29c9 | ||
|
|
88816e150a | ||
|
|
409fc4dc19 | ||
|
|
c54b36c902 | ||
|
|
19cbab78b2 | ||
|
|
1736cd9bc8 | ||
|
|
a5264e9995 | ||
|
|
6868127ba3 | ||
|
|
dbbadbc309 | ||
|
|
fb08164cbc | ||
|
|
7e999c5496 | ||
|
|
4fac825457 | ||
|
|
35c66744db | ||
|
|
930e04f6c7 | ||
|
|
37756b8d06 | ||
|
|
596c93ce7e | ||
|
|
edfae1abfb | ||
|
|
624943b3ca | ||
|
|
ef89a760d6 | ||
|
|
050aa277ae | ||
|
|
f03dfb50ad | ||
|
|
6e8474f584 | ||
|
|
4e73b1b133 | ||
|
|
e08e29c8b4 | ||
|
|
b82ca28190 | ||
|
|
e02d186946 | ||
|
|
d9b02657a7 | ||
|
|
d761a2273c | ||
|
|
e3d9cc30a8 | ||
|
|
1ef7994ae2 | ||
|
|
081f397460 | ||
|
|
f800298d01 | ||
|
|
301a25303f | ||
|
|
b24eb6ce88 | ||
|
|
fd60bfd3d2 | ||
|
|
da2c4b0fa1 | ||
|
|
4251979e8b | ||
|
|
12783674cb | ||
|
|
c17ffcae2c | ||
|
|
897f14e60a | ||
|
|
4163eb28d0 | ||
|
|
500442e5c5 | ||
|
|
8c54803094 | ||
|
|
2baf31dff8 | ||
|
|
d997397c38 | ||
|
|
7773000c36 | ||
|
|
042f7ccbcd | ||
|
|
44aa8a6b91 | ||
|
|
43c671cb5c | ||
|
|
a3df37b851 | ||
|
|
c01c2bd742 | ||
|
|
533ee87bb1 | ||
|
|
c810406195 | ||
|
|
3ec8a867db | ||
|
|
899a70908e | ||
|
|
bf95d77562 | ||
|
|
db992e808f | ||
|
|
f2aca68b82 | ||
|
|
12b448ef01 | ||
|
|
e77bb0157e | ||
|
|
75631bd8f1 | ||
|
|
63b1887ff2 | ||
|
|
ba36e47575 | ||
|
|
3866c4526e | ||
|
|
cb08f02de1 | ||
|
|
4188fbecee | ||
|
|
f261bf7e97 | ||
|
|
88dc9b6b63 | ||
|
|
632a265e14 | ||
|
|
449cd0a76e | ||
|
|
181a53d3ed | ||
|
|
44958409f5 | ||
|
|
f81129aa68 | ||
|
|
e5ef0d97bd | ||
|
|
469036990a | ||
|
|
d06f5c7c54 | ||
|
|
9911939576 | ||
|
|
c9ace14c40 | ||
|
|
d5cdb91d55 | ||
|
|
360be597a8 | ||
|
|
75f7c9ae0c | ||
|
|
c926454a41 | ||
|
|
5cfe7ea0aa | ||
|
|
c70fb4ba08 | ||
|
|
7cba9f3cb2 | ||
|
|
d6acb8d059 | ||
|
|
ec23b70bb7 | ||
|
|
58f2f5007d | ||
|
|
0caf4fd9d0 | ||
|
|
037ce08f72 | ||
|
|
27cdd27889 | ||
|
|
f60a6561bb | ||
|
|
204862ac3c | ||
|
|
0f50849fbb | ||
|
|
404c22ac86 | ||
|
|
fe555e4539 | ||
|
|
c87e629150 | ||
|
|
b9d6c816f1 | ||
|
|
0153a25061 | ||
|
|
476a4c7bc4 | ||
|
|
08d80b35cb | ||
|
|
6622b5fe3a | ||
|
|
d7bb5658f2 | ||
|
|
2f3627cd6d | ||
|
|
ce9fd0147f | ||
|
|
be12f56856 | ||
|
|
e0f41198d6 | ||
|
|
fb31a88c51 | ||
|
|
254ccb9ccb | ||
|
|
d2bebee9d9 | ||
|
|
b2c9352bb5 | ||
|
|
15481a9520 | ||
|
|
37723698dc | ||
|
|
af8c08e6a7 | ||
|
|
01249000c3 | ||
|
|
f6edd098a9 | ||
|
|
6321fb6a92 | ||
|
|
69ba009439 | ||
|
|
86e5762b1c | ||
|
|
49363c660c | ||
|
|
1debbc3100 | ||
|
|
ae0b09dd3a | ||
|
|
8d0afa632d | ||
|
|
561953e494 | ||
|
|
83a7796b74 | ||
|
|
ce72eb4eb4 | ||
|
|
7cde5f247e | ||
|
|
849e7cb4d5 | ||
|
|
92c823680a | ||
|
|
05f1541903 | ||
|
|
30d42e2806 | ||
|
|
ebdf4e478a | ||
|
|
f6bc1afcbb | ||
|
|
9013dcb527 | ||
|
|
dbe13aecea | ||
|
|
6521ceaf92 | ||
|
|
f497dbf01e | ||
|
|
8a9486eb49 | ||
|
|
2fb57805ea | ||
|
|
919c1d695c | ||
|
|
1530594b4b | ||
|
|
6f0d8e8dda | ||
|
|
62d7c081dc | ||
|
|
f78994b66c | ||
|
|
50a8c17cf8 | ||
|
|
6f9643df79 | ||
|
|
aeaf8e8433 | ||
|
|
7afb280990 | ||
|
|
99a46fd5af | ||
|
|
eed4ff7e2d | ||
|
|
659714f45d | ||
|
|
8f70c5ca35 | ||
|
|
0ea64ad8ab | ||
|
|
9fbb449e06 | ||
|
|
be3c4cf268 | ||
|
|
0a8fd50c4a | ||
|
|
70db096b6d | ||
|
|
005df31a0d | ||
|
|
d6193c1622 | ||
|
|
a74f2827b2 | ||
|
|
392b162024 | ||
|
|
4af4c8cca9 | ||
|
|
3c4346acb2 | ||
|
|
b8c1f9ab67 | ||
|
|
2e9970a98c | ||
|
|
4914b734dc | ||
|
|
0df3eabe26 | ||
|
|
8fe2211e2b | ||
|
|
645ee2003f | ||
|
|
d6f1162547 | ||
|
|
46991aa65c | ||
|
|
7eb41304e6 | ||
|
|
24da00bd5a | ||
|
|
34efc7db51 | ||
|
|
acf010326c | ||
|
|
d7814ed767 | ||
|
|
19e60b698f | ||
|
|
a7cfdc3a87 | ||
|
|
b010b8e3ca | ||
|
|
26a2c69c2e | ||
|
|
608bad26cf | ||
|
|
70768176f9 | ||
|
|
694a6ed4a1 | ||
|
|
dc15fa965b | ||
|
|
d78739d163 | ||
|
|
d90ce68f5d | ||
|
|
d2c52ff35b | ||
|
|
813b4cf74c | ||
|
|
a437b38270 | ||
|
|
f9d060146c | ||
|
|
7f0c6b9c21 | ||
|
|
5a7cfd8c02 | ||
|
|
babe694366 | ||
|
|
7560b29fb9 | ||
|
|
f6753a9013 | ||
|
|
647e53bcbb | ||
|
|
8439fc6c19 | ||
|
|
6f61cf2e4e | ||
|
|
191187ed1b | ||
|
|
1fb01d4d98 | ||
|
|
3106fe0ed6 | ||
|
|
e29954e03f | ||
|
|
3b5d56e087 | ||
|
|
aa91186119 | ||
|
|
d08f5af9c6 | ||
|
|
b96e471fa2 | ||
|
|
7120ab7b13 | ||
|
|
2f5dcda3b6 | ||
|
|
120b5dc80e | ||
|
|
d762253f74 | ||
|
|
1d28b935e8 | ||
|
|
4b4a1c6cac | ||
|
|
110a1b2ac7 | ||
|
|
79558910d1 | ||
|
|
fbf85398da | ||
|
|
1e4b96aa0a | ||
|
|
c064ae6bc3 | ||
|
|
39228f3495 | ||
|
|
12e2237131 | ||
|
|
066a9b7a61 | ||
|
|
cdb180e1d4 | ||
|
|
a8ecce7421 | ||
|
|
8c3461f0ff | ||
|
|
305f448d05 | ||
|
|
5f90a3e48c | ||
|
|
96cf190720 | ||
|
|
373832ba22 | ||
|
|
4f472e451e | ||
|
|
d36bbec5b5 | ||
|
|
e258058cf1 | ||
|
|
c3ac1aac8c | ||
|
|
234f73e7bf | ||
|
|
f49dd97d24 | ||
|
|
7bd1565cfb | ||
|
|
6e448da90d | ||
|
|
9f035108e1 | ||
|
|
4bf2e1669d | ||
|
|
dd30c1b39d | ||
|
|
94006cb44c | ||
|
|
2e6ec91ce0 | ||
|
|
fe80da362c | ||
|
|
82aca1b02e | ||
|
|
4211e651d0 | ||
|
|
f9e1289463 | ||
|
|
a09b4e3b92 | ||
|
|
ab6ae621cb | ||
|
|
17ae0a6a09 | ||
|
|
041925c123 | ||
|
|
9292a087e8 | ||
|
|
fef0fd82ff | ||
|
|
5cbd587833 | ||
|
|
7c9d154461 | ||
|
|
8189c4ee43 | ||
|
|
05de2f56b4 | ||
|
|
e38494a10c | ||
|
|
e178baefbd | ||
|
|
bfa2bfc8ae |
@@ -1,7 +1,7 @@
|
||||
#ifndef __HName_H
|
||||
#define __HName_H
|
||||
/*
|
||||
* Copyright (c) 2001-2007 Stephen Williams ([email protected])
|
||||
* Copyright (c) 2001-2008 Stephen Williams ([email protected])
|
||||
*
|
||||
* This source code is free software; you can redistribute it
|
||||
* and/or modify it in source code form under the terms of the GNU
|
||||
@@ -18,11 +18,9 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifdef HAVE_CVS_IDENT
|
||||
#ident "$Id: HName.h,v 1.7 2007/06/02 03:42:12 steve Exp $"
|
||||
#endif
|
||||
|
||||
# include <iostream>
|
||||
# include <list>
|
||||
# include "StringHeap.h"
|
||||
#ifdef __GNUC__
|
||||
#if __GNUC__ > 2
|
||||
@@ -57,7 +55,7 @@ class hname_t {
|
||||
|
||||
private:
|
||||
perm_string name_;
|
||||
// If the number is anything other then INT_MIN, then this is
|
||||
// If the number is anything other than INT_MIN, then this is
|
||||
// the numeric part of the name. Otherwise, it is not part of
|
||||
// the name at all.
|
||||
int number_;
|
||||
@@ -70,30 +68,16 @@ extern bool operator == (const hname_t&, const hname_t&);
|
||||
extern bool operator != (const hname_t&, const hname_t&);
|
||||
extern ostream& operator<< (ostream&, const hname_t&);
|
||||
|
||||
/*
|
||||
* $Log: HName.h,v $
|
||||
* Revision 1.7 2007/06/02 03:42:12 steve
|
||||
* Properly evaluate scope path expressions.
|
||||
*
|
||||
* Revision 1.6 2007/05/16 19:12:33 steve
|
||||
* Fix hname_t use of space for 1 perm_string.
|
||||
*
|
||||
* Revision 1.5 2007/04/26 03:06:21 steve
|
||||
* Rework hname_t to use perm_strings.
|
||||
*
|
||||
* Revision 1.4 2002/11/02 03:27:51 steve
|
||||
* Allow named events to be referenced by
|
||||
* hierarchical names.
|
||||
*
|
||||
* Revision 1.3 2002/08/12 01:34:58 steve
|
||||
* conditional ident string using autoconfig.
|
||||
*
|
||||
* Revision 1.2 2002/06/14 03:25:51 steve
|
||||
* Compiler portability.
|
||||
*
|
||||
* Revision 1.1 2001/12/03 04:47:14 steve
|
||||
* Parser and pform use hierarchical names as hname_t
|
||||
* objects instead of encoded strings.
|
||||
*
|
||||
*/
|
||||
inline ostream& operator<< (ostream&out, const list<hname_t>&ll)
|
||||
{
|
||||
list<hname_t>::const_iterator cur = ll.begin();
|
||||
out << *cur;
|
||||
cur ++;
|
||||
while (cur != ll.end()) {
|
||||
out << "." << *cur;
|
||||
cur ++;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
/* n is a permallocated string. */
|
||||
Module::Module(perm_string n)
|
||||
: PScope(n, 0)
|
||||
: PScope(n)
|
||||
{
|
||||
library_flag = false;
|
||||
default_nettype = NetNet::NONE;
|
||||
@@ -41,16 +41,6 @@ void Module::add_gate(PGate*gate)
|
||||
gates_.push_back(gate);
|
||||
}
|
||||
|
||||
void Module::add_task(perm_string name, PTask*task)
|
||||
{
|
||||
tasks_[name] = task;
|
||||
}
|
||||
|
||||
void Module::add_function(perm_string name, PFunction *func)
|
||||
{
|
||||
funcs_[name] = func;
|
||||
}
|
||||
|
||||
unsigned Module::port_count() const
|
||||
{
|
||||
return ports.count();
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
# include <list>
|
||||
# include <map>
|
||||
# include <utility>
|
||||
# include "svector.h"
|
||||
# include "StringHeap.h"
|
||||
# include "HName.h"
|
||||
@@ -102,7 +103,7 @@ class Module : public PScope, public LineInfo {
|
||||
bool signed_flag;
|
||||
// Value expression
|
||||
PExpr*expr;
|
||||
// If there are range constrants, list them here
|
||||
// If there are range constraints, list them here
|
||||
range_t*range;
|
||||
};
|
||||
map<perm_string,param_expr_t>parameters;
|
||||
@@ -118,7 +119,8 @@ class Module : public PScope, public LineInfo {
|
||||
new parameters within the module, but may be used to set
|
||||
values within this module (when instantiated) or in other
|
||||
instantiated modules. */
|
||||
map<pform_name_t,PExpr*>defparms;
|
||||
typedef pair<pform_name_t,PExpr*> named_expr_t;
|
||||
list<named_expr_t>defparms;
|
||||
|
||||
/* Parameters may be overridden at instantiation time;
|
||||
the overrides do not contain explicit parameter names,
|
||||
@@ -138,6 +140,10 @@ class Module : public PScope, public LineInfo {
|
||||
set by the `timescale directive. */
|
||||
int time_unit, time_precision;
|
||||
|
||||
/* Task definitions within this module */
|
||||
map<perm_string,PTask*> tasks;
|
||||
map<perm_string,PFunction*> funcs;
|
||||
|
||||
/* The module has a list of genvars that may be used in
|
||||
various generate schemes. */
|
||||
map<perm_string,LineInfo*> genvars;
|
||||
@@ -152,8 +158,6 @@ class Module : public PScope, public LineInfo {
|
||||
perm_string mod_name() const { return pscope_name(); }
|
||||
|
||||
void add_gate(PGate*gate);
|
||||
void add_task(perm_string name, PTask*def);
|
||||
void add_function(perm_string name, PFunction*def);
|
||||
|
||||
unsigned port_count() const;
|
||||
const svector<PEIdent*>& get_port(unsigned idx) const;
|
||||
@@ -167,14 +171,12 @@ class Module : public PScope, public LineInfo {
|
||||
bool elaborate(Design*, NetScope*scope) const;
|
||||
|
||||
typedef map<perm_string,NetExpr*> replace_t;
|
||||
bool elaborate_scope(Design*, NetScope*scope, const replace_t&rep) const;
|
||||
bool elaborate_scope(Design*, NetScope*scope, const replace_t&rep);
|
||||
|
||||
bool elaborate_sig(Design*, NetScope*scope) const;
|
||||
|
||||
private:
|
||||
list<PGate*> gates_;
|
||||
map<perm_string,PTask*> tasks_;
|
||||
map<perm_string,PFunction*> funcs_;
|
||||
|
||||
static void elaborate_parm_item_(perm_string name, const param_expr_t&cur,
|
||||
Design*des, NetScope*scope);
|
||||
|
||||
@@ -315,6 +315,7 @@ class PEIdent : public PExpr {
|
||||
|
||||
private:
|
||||
NetAssign_*elaborate_lval_net_word_(Design*, NetScope*, NetNet*) const;
|
||||
bool elaborate_lval_net_bit_(Design*, NetScope*, NetAssign_*) const;
|
||||
bool elaborate_lval_net_part_(Design*, NetScope*, NetAssign_*) const;
|
||||
bool elaborate_lval_net_idx_(Design*, NetScope*, NetAssign_*,
|
||||
index_component_t::ctype_t) const;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef __PGate_H
|
||||
#define __PGate_H
|
||||
/*
|
||||
* Copyright (c) 1998-2004 Stephen Williams ([email protected])
|
||||
* Copyright (c) 1998-2008 Stephen Williams ([email protected])
|
||||
*
|
||||
* This source code is free software; you can redistribute it
|
||||
* and/or modify it in source code form under the terms of the GNU
|
||||
@@ -18,9 +18,6 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifdef HAVE_CVS_IDENT
|
||||
#ident "$Id: PGate.h,v 1.32 2006/04/10 00:37:42 steve Exp $"
|
||||
#endif
|
||||
|
||||
# include "svector.h"
|
||||
# include "StringHeap.h"
|
||||
@@ -232,9 +229,11 @@ class PGModule : public PGate {
|
||||
PExpr*msb_;
|
||||
PExpr*lsb_;
|
||||
|
||||
friend class delayed_elaborate_scope_mod_instances;
|
||||
void elaborate_mod_(Design*, Module*mod, NetScope*scope) const;
|
||||
void elaborate_udp_(Design*, PUdp *udp, NetScope*scope) const;
|
||||
void elaborate_scope_mod_(Design*des, Module*mod, NetScope*sc) const;
|
||||
void elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*sc) const;
|
||||
bool elaborate_sig_mod_(Design*des, NetScope*scope, Module*mod) const;
|
||||
bool elaborate_sig_udp_(Design*des, NetScope*scope, PUdp*udp) const;
|
||||
|
||||
|
||||
+1
-14
@@ -27,27 +27,14 @@ PGenerate::PGenerate(unsigned id)
|
||||
: id_number(id)
|
||||
{
|
||||
parent = 0;
|
||||
lexical_scope = 0;
|
||||
}
|
||||
|
||||
PGenerate::~PGenerate()
|
||||
{
|
||||
}
|
||||
|
||||
PWire* PGenerate::get_wire(perm_string name) const
|
||||
{
|
||||
map<perm_string,PWire*>::const_iterator obj = wires.find(name);
|
||||
if (obj == wires.end())
|
||||
return 0;
|
||||
else
|
||||
return (*obj).second;
|
||||
}
|
||||
|
||||
void PGenerate::add_gate(PGate*gate)
|
||||
{
|
||||
gates.push_back(gate);
|
||||
}
|
||||
|
||||
void PGenerate::add_behavior(PProcess*proc)
|
||||
{
|
||||
behaviors.push_back(proc);
|
||||
}
|
||||
|
||||
+16
-11
@@ -1,7 +1,7 @@
|
||||
#ifndef __PGenerate_H
|
||||
#define __PGenerate_H
|
||||
/*
|
||||
* Copyright (c) 2006 Stephen Williams ([email protected])
|
||||
* Copyright (c) 2006-2008 Stephen Williams ([email protected])
|
||||
*
|
||||
* This source code is free software; you can redistribute it
|
||||
* and/or modify it in source code form under the terms of the GNU
|
||||
@@ -18,13 +18,11 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifdef HAVE_CVS_IDENT
|
||||
#ident "$Id: PGenerate.h,v 1.4 2007/06/02 03:42:12 steve Exp $"
|
||||
#endif
|
||||
|
||||
# include "LineInfo.h"
|
||||
# include "StringHeap.h"
|
||||
# include "HName.h"
|
||||
# include "PScope.h"
|
||||
# include <list>
|
||||
# include <map>
|
||||
# include "pform_types.h"
|
||||
@@ -32,7 +30,9 @@
|
||||
class Design;
|
||||
class NetScope;
|
||||
class PExpr;
|
||||
class PFunction;
|
||||
class PProcess;
|
||||
class PTask;
|
||||
class PGate;
|
||||
class PWire;
|
||||
|
||||
@@ -49,10 +49,10 @@ class PWire;
|
||||
* The parent points to the GS_CASE that contains this item.
|
||||
* the loop_test is compared with the parent->loop_test expression.
|
||||
*/
|
||||
class PGenerate : public LineInfo {
|
||||
class PGenerate : public LineInfo, public LexicalScope {
|
||||
|
||||
public:
|
||||
PGenerate(unsigned id_number);
|
||||
explicit PGenerate(unsigned id_number);
|
||||
~PGenerate();
|
||||
|
||||
// Generate schemes have an ID number, for when the scope is
|
||||
@@ -60,6 +60,10 @@ class PGenerate : public LineInfo {
|
||||
const unsigned id_number;
|
||||
perm_string scope_name;
|
||||
|
||||
// This is used during parsing to stack lexical scopes within
|
||||
// this generate scheme.
|
||||
PScope*lexical_scope;
|
||||
|
||||
enum scheme_t {GS_NONE, GS_LOOP, GS_CONDIT, GS_ELSE,
|
||||
GS_CASE, GS_CASE_ITEM};
|
||||
scheme_t scheme_type;
|
||||
@@ -71,16 +75,17 @@ class PGenerate : public LineInfo {
|
||||
PExpr*loop_test;
|
||||
PExpr*loop_step;
|
||||
|
||||
map<perm_string,PWire*>wires;
|
||||
PWire* get_wire(perm_string name) const;
|
||||
|
||||
list<PGate*> gates;
|
||||
void add_gate(PGate*);
|
||||
|
||||
list<PProcess*> behaviors;
|
||||
void add_behavior(PProcess*behave);
|
||||
|
||||
list<PGenerate*> generates;
|
||||
// Tasks instantiated within this scheme.
|
||||
map<perm_string,PTask*> tasks;
|
||||
map<perm_string,PFunction*>funcs;
|
||||
|
||||
// Generate schemes can contain further generate schemes.
|
||||
list<PGenerate*> generate_schemes;
|
||||
PGenerate*parent;
|
||||
|
||||
// This method is called by the elaboration of a module to
|
||||
|
||||
@@ -19,8 +19,13 @@
|
||||
|
||||
# include "PScope.h"
|
||||
|
||||
PScope::PScope(perm_string n, PScope*p)
|
||||
: name_(n), parent_(p)
|
||||
PScope::PScope(perm_string n, PScope*parent)
|
||||
: name_(n), parent_(parent)
|
||||
{
|
||||
}
|
||||
|
||||
PScope::PScope(perm_string n)
|
||||
: name_(n), parent_(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -28,7 +33,7 @@ PScope::~PScope()
|
||||
{
|
||||
}
|
||||
|
||||
PWire* PScope::wires_find(perm_string name)
|
||||
PWire* LexicalScope::wires_find(perm_string name)
|
||||
{
|
||||
map<perm_string,PWire*>::const_iterator cur = wires.find(name);
|
||||
if (cur == wires.end())
|
||||
|
||||
@@ -38,7 +38,22 @@ class NetScope;
|
||||
* NOTE: This is note the same concept as the "scope" of an elaborated
|
||||
* hierarchy. That is represented by NetScope objects after elaboration.
|
||||
*/
|
||||
class PScope {
|
||||
|
||||
class LexicalScope {
|
||||
|
||||
public:
|
||||
explicit LexicalScope() { }
|
||||
// A virtual destructor is so that dynamic_cast can work.
|
||||
virtual ~LexicalScope() { }
|
||||
|
||||
// Nets an variables (wires) in the scope
|
||||
map<perm_string,PWire*>wires;
|
||||
PWire* wires_find(perm_string name);
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
class PScope : public LexicalScope {
|
||||
|
||||
public:
|
||||
// When created, a scope has a name and a parent. The name is
|
||||
@@ -50,15 +65,12 @@ class PScope {
|
||||
// modules. Scopes for tasks and functions point to their
|
||||
// containing module.
|
||||
PScope(perm_string name, PScope*parent);
|
||||
PScope(perm_string name);
|
||||
virtual ~PScope();
|
||||
|
||||
perm_string pscope_name() const { return name_; }
|
||||
PScope* pscope_parent() { return parent_; }
|
||||
|
||||
// Nets an variables (wires) in the scope
|
||||
map<perm_string,PWire*>wires;
|
||||
PWire* wires_find(perm_string name);
|
||||
|
||||
// Named events in the scope.
|
||||
map<perm_string,PEvent*>events;
|
||||
|
||||
|
||||
+1
-1
@@ -92,7 +92,7 @@ PBlock::PBlock(perm_string n, PScope*parent, BL_TYPE t)
|
||||
}
|
||||
|
||||
PBlock::PBlock(BL_TYPE t)
|
||||
: PScope(perm_string(),0), bl_type_(t)
|
||||
: PScope(perm_string()), bl_type_(t)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
echo "Autoconf in root..."
|
||||
autoconf -f
|
||||
|
||||
for dir in vpip vpi vvp tgt-vvp tgt-fpga tgt-stub libveriuser cadpli
|
||||
for dir in vpip vpi vvp tgt-vvp tgt-fpga tgt-stub tgt-vhdl libveriuser cadpli
|
||||
do
|
||||
echo "Autoconf in $dir..."
|
||||
( cd ./$dir ; autoconf -f --include=.. )
|
||||
|
||||
+2
-2
@@ -122,6 +122,6 @@ AX_C_UNDERSCORES_TRAILING
|
||||
AX_CPP_IDENT
|
||||
|
||||
# XXX disable tgt-fpga for the moment
|
||||
AC_CONFIG_SUBDIRS(vvp vpi tgt-stub tgt-null tgt-vvp libveriuser cadpli)
|
||||
AC_CONFIG_SUBDIRS(vvp vpi tgt-stub tgt-null tgt-vvp tgt-vhdl libveriuser cadpli)
|
||||
|
||||
AC_OUTPUT(Makefile ivlpp/Makefile driver/Makefile driver-vpi/Makefile tgt-null/Makefile tgt-verilog/Makefile tgt-pal/Makefile)
|
||||
AC_OUTPUT(Makefile ivlpp/Makefile driver/Makefile driver-vpi/Makefile tgt-null/Makefile tgt-verilog/Makefile tgt-pal/Makefile tgt-vhdl/Makefile)
|
||||
|
||||
+26
-1
@@ -302,6 +302,22 @@ void NetArrayDq::dump_node(ostream&o, unsigned ind) const
|
||||
dump_obj_attr(o, ind+4);
|
||||
}
|
||||
|
||||
void NetCastInt::dump_node(ostream&o, unsigned ind) const
|
||||
{
|
||||
o << setw(ind) << "" << "Cast to int. (NetCastInt): " <<
|
||||
name() << " width=" << width() << endl;
|
||||
dump_node_pins(o, ind+4);
|
||||
dump_obj_attr(o, ind+4);
|
||||
}
|
||||
|
||||
void NetCastReal::dump_node(ostream&o, unsigned ind) const
|
||||
{
|
||||
o << setw(ind) << "" << "Cast to real (NetCastReal): " <<
|
||||
name() << endl;
|
||||
dump_node_pins(o, ind+4);
|
||||
dump_obj_attr(o, ind+4);
|
||||
}
|
||||
|
||||
void NetCLShift::dump_node(ostream&o, unsigned ind) const
|
||||
{
|
||||
o << setw(ind) << "" << "Combinatorial shift (NetCLShift): " <<
|
||||
@@ -1047,7 +1063,7 @@ void NetScope::dump(ostream&o) const
|
||||
|
||||
/* Dump the saved defparam assignments here. */
|
||||
{
|
||||
map<pform_name_t,NetExpr*>::const_iterator pp;
|
||||
list<pair<pform_name_t,NetExpr*> >::const_iterator pp;
|
||||
for (pp = defparams.begin()
|
||||
; pp != defparams.end() ; pp ++ ) {
|
||||
o << " defparam " << (*pp).first << " = " <<
|
||||
@@ -1055,6 +1071,15 @@ void NetScope::dump(ostream&o) const
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
list<pair<list<hname_t>,NetExpr*> >::const_iterator pp;
|
||||
for (pp = defparams_later.begin()
|
||||
; pp != defparams_later.end() ; pp ++ ) {
|
||||
o << " defparam(later) " << pp->first << " = " <<
|
||||
*(pp->second) << ";" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dump the events in this scope. */
|
||||
for (NetEvent*cur = events_ ; cur ; cur = cur->snext_) {
|
||||
o << " event " << cur->name() << "; nprobe="
|
||||
|
||||
@@ -223,6 +223,12 @@ mostly by EDIF format output. The Icarus Verilog fpga code generator
|
||||
can generate complete designs or EDIF macros that can in turn be
|
||||
imported into larger designs by other tools. The \fBfpga\fP target
|
||||
implies the synthesis \fB-S\fP flag.
|
||||
.TP 8
|
||||
.B vhdl
|
||||
This target produces a VHDL translation of the Verilog netlist. The
|
||||
output is a single file containing VHDL entities corresponding to
|
||||
the modules in the Verilog source code. Note that only a subset of
|
||||
the Verilog language is supported. See the wiki for more information.
|
||||
|
||||
.SH "WARNING TYPES"
|
||||
These are the types of warnings that can be selected by the \fB-W\fP
|
||||
|
||||
+13
-13
@@ -328,23 +328,24 @@ static int t_default(char*cmd, unsigned ncmd)
|
||||
remove(compiled_defines_path);
|
||||
}
|
||||
#ifdef __MINGW32__ /* MinGW just returns the exit status, so return it! */
|
||||
free(cmd);
|
||||
return rc;
|
||||
#else
|
||||
|
||||
int rtn = 0;
|
||||
if (rc != 0) {
|
||||
if (rc == 127) {
|
||||
fprintf(stderr, "Failed to execute: %s\n", cmd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (WIFEXITED(rc))
|
||||
return WEXITSTATUS(rc);
|
||||
|
||||
fprintf(stderr, "Command signaled: %s\n", cmd);
|
||||
return -1;
|
||||
rtn = 1;
|
||||
} else if (WIFEXITED(rc)) {
|
||||
rtn = WEXITSTATUS(rc);
|
||||
} else {
|
||||
fprintf(stderr, "Command signaled: %s\n", cmd);
|
||||
rtn = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
free(cmd);
|
||||
return rtn;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -505,7 +506,7 @@ int process_generation(const char*name)
|
||||
" 2005 -- IEEE1364-2005\n"
|
||||
"Other generation flags:\n"
|
||||
" specify | no-specify\n"
|
||||
" verilog-ams | no-verinlog-ams\n"
|
||||
" verilog-ams | no-verilog-ams\n"
|
||||
" std-include | no-std-include\n"
|
||||
" xtypes | no-xtypes\n"
|
||||
" icarus-misc | no-icarus-misc\n"
|
||||
@@ -871,6 +872,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
fprintf(stderr, "Command signaled: %s\n", cmd);
|
||||
free(cmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -889,6 +891,4 @@ int main(int argc, char **argv)
|
||||
fclose(iconfig_file);
|
||||
|
||||
return t_default(cmd, ncmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+12
-61
@@ -1182,12 +1182,12 @@ NetExpr* PEIdent::elaborate_expr_param_part_(Design*des, NetScope*scope,
|
||||
if (!flag)
|
||||
return 0;
|
||||
|
||||
// Notice that the par_msv is not used is this function other
|
||||
// then for this test. It is used to tell the direction that
|
||||
// Notice that the par_msv is not used in this function other
|
||||
// than for this test. It is used to tell the direction that
|
||||
// the bits are numbers, so that we can make sure the
|
||||
// direction matches the part select direction. After that,
|
||||
// we only need the par_lsv.
|
||||
if (msv>lsv && par_msv<par_lsv || msv<lsv && par_msv>=par_lsv) {
|
||||
if ((msv>lsv && par_msv<par_lsv) || (msv<lsv && par_msv>=par_lsv)) {
|
||||
cerr << get_fileline() << ": error: Part select "
|
||||
<< "[" << msv << ":" << lsv << "] is out of order." << endl;
|
||||
des->errors += 1;
|
||||
@@ -1373,7 +1373,7 @@ NetExpr* PEIdent::elaborate_expr_param(Design*des,
|
||||
par_mv = par_me->value().as_long();
|
||||
par_lv = par_le->value().as_long();
|
||||
}
|
||||
/* Convert the index to cannonical bit address. */
|
||||
/* Convert the index to canonical bit address. */
|
||||
long ridx = rv.as_long();
|
||||
if (par_mv >= par_lv) {
|
||||
ridx -= par_lv;
|
||||
@@ -1567,9 +1567,11 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope,
|
||||
|
||||
// If the part select covers exactly the entire
|
||||
// vector, then do not bother with it. Return the
|
||||
// signal itself.
|
||||
if (sb_lsb == 0 && wid == net->vector_width())
|
||||
// signal itself, casting to unsigned if necessary.
|
||||
if (sb_lsb == 0 && wid == net->vector_width()) {
|
||||
net->cast_signed(false);
|
||||
return net;
|
||||
}
|
||||
|
||||
// If the part select covers NONE of the vector, then return a
|
||||
// constant X.
|
||||
@@ -1580,61 +1582,10 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope,
|
||||
return tmp;
|
||||
}
|
||||
|
||||
// If the part select is entirely within the vector, then make
|
||||
// a simple part select.
|
||||
if (sb_lsb >= 0 && sb_msb < (signed)net->vector_width()) {
|
||||
NetExpr*ex = new NetEConst(verinum(sb_lsb));
|
||||
NetESelect*ss = new NetESelect(net, ex, wid);
|
||||
ss->set_line(*this);
|
||||
return ss;
|
||||
}
|
||||
|
||||
// Now the hard stuff. The part select is falling off at least
|
||||
// one end. We're going to need a NetEConcat to mix the
|
||||
// selection with overrun.
|
||||
|
||||
NetEConst*bot = 0;
|
||||
if (sb_lsb < 0) {
|
||||
bot = make_const_x( 0-sb_lsb );
|
||||
bot->set_line(*this);
|
||||
sb_lsb = 0;
|
||||
}
|
||||
NetEConst*top = 0;
|
||||
if (sb_msb >= (signed)net->vector_width()) {
|
||||
top = make_const_x( 1+sb_msb-net->vector_width() );
|
||||
top->set_line(*this);
|
||||
sb_msb = net->vector_width()-1;
|
||||
}
|
||||
|
||||
unsigned concat_count = 1;
|
||||
if (bot) concat_count += 1;
|
||||
if (top) concat_count += 1;
|
||||
|
||||
NetEConcat*concat = new NetEConcat(concat_count);
|
||||
concat->set_line(*this);
|
||||
|
||||
if (bot) {
|
||||
concat_count -= 1;
|
||||
concat->set(concat_count, bot);
|
||||
}
|
||||
if (sb_lsb == 0 && sb_msb+1 == (signed)net->vector_width()) {
|
||||
concat_count -= 1;
|
||||
concat->set(concat_count, net);
|
||||
} else {
|
||||
NetExpr*ex = new NetEConst(verinum(sb_lsb));
|
||||
ex->set_line(*this);
|
||||
NetESelect*ss = new NetESelect(net, ex, 1+sb_msb-sb_lsb);
|
||||
ss->set_line(*this);
|
||||
concat_count -= 1;
|
||||
concat->set(concat_count, ss);
|
||||
}
|
||||
if (top) {
|
||||
concat_count -= 1;
|
||||
concat->set(concat_count, top);
|
||||
}
|
||||
ivl_assert(*this, concat_count==0);
|
||||
|
||||
return concat;
|
||||
NetExpr*ex = new NetEConst(verinum(sb_lsb));
|
||||
NetESelect*ss = new NetESelect(net, ex, wid);
|
||||
ss->set_line(*this);
|
||||
return ss;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+71
-102
@@ -208,101 +208,18 @@ NetAssign_* PEIdent::elaborate_lval(Design*des,
|
||||
return 0;
|
||||
}
|
||||
|
||||
long msb, lsb;
|
||||
NetExpr*mux;
|
||||
|
||||
if (use_sel == index_component_t::SEL_BIT) {
|
||||
|
||||
const index_component_t&index_tail = name_tail.index.back();
|
||||
ivl_assert(*this, index_tail.msb != 0);
|
||||
ivl_assert(*this, index_tail.lsb == 0);
|
||||
|
||||
/* If there is only a single select expression, it is a
|
||||
bit select. Evaluate the constant value and treat it
|
||||
as a part select with a bit width of 1. If the
|
||||
expression it not constant, then return the
|
||||
expression as a mux. */
|
||||
|
||||
NetExpr*index_expr = elab_and_eval(des, scope, index_tail.msb, -1);
|
||||
|
||||
if (NetEConst*index_con = dynamic_cast<NetEConst*> (index_expr)) {
|
||||
msb = index_con->value().as_long();
|
||||
lsb = index_con->value().as_long();
|
||||
mux = 0;
|
||||
|
||||
} else {
|
||||
msb = 0;
|
||||
lsb = 0;
|
||||
mux = index_expr;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* No select expressions, so presume a part select the
|
||||
width of the register. */
|
||||
|
||||
msb = reg->msb();
|
||||
lsb = reg->lsb();
|
||||
mux = 0;
|
||||
NetAssign_*lv = new NetAssign_(reg);
|
||||
elaborate_lval_net_bit_(des, scope, lv);
|
||||
return lv;
|
||||
}
|
||||
|
||||
ivl_assert(*this, use_sel == index_component_t::SEL_NONE);
|
||||
|
||||
NetAssign_*lv;
|
||||
if (mux) {
|
||||
|
||||
/* If there is a non-constant bit select, make a
|
||||
NetAssign_ to the target reg and attach a
|
||||
bmux to select the target bit. */
|
||||
lv = new NetAssign_(reg);
|
||||
|
||||
/* Correct the mux for the range of the vector. */
|
||||
if (reg->msb() < reg->lsb())
|
||||
mux = make_sub_expr(reg->lsb(), mux);
|
||||
else if (reg->lsb() != 0)
|
||||
mux = make_add_expr(mux, - reg->lsb());
|
||||
|
||||
lv->set_part(mux, 1);
|
||||
|
||||
} else if (msb == reg->msb() && lsb == reg->lsb()) {
|
||||
|
||||
/* No bit select, and part select covers the entire
|
||||
vector. Simplest case. */
|
||||
lv = new NetAssign_(reg);
|
||||
|
||||
} else {
|
||||
|
||||
/* If the bit/part select is constant, then make the
|
||||
NetAssign_ only as wide as it needs to be and connect
|
||||
only to the selected bits of the reg. */
|
||||
unsigned loff = reg->sb_to_idx(lsb);
|
||||
unsigned moff = reg->sb_to_idx(msb);
|
||||
unsigned wid = moff - loff + 1;
|
||||
|
||||
if (moff < loff) {
|
||||
cerr << get_fileline() << ": error: part select "
|
||||
<< reg->name() << "[" << msb<<":"<<lsb<<"]"
|
||||
<< " is reversed." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If the part select extends beyond the extreme of the
|
||||
variable, then report an error. Note that loff is
|
||||
converted to normalized form so is relative the
|
||||
variable pins. */
|
||||
|
||||
if ((wid + loff) > reg->vector_width()) {
|
||||
cerr << get_fileline() << ": error: bit/part select "
|
||||
<< reg->name() << "[" << msb<<":"<<lsb<<"]"
|
||||
<< " is out of range." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
lv = new NetAssign_(reg);
|
||||
lv->set_part(new NetEConst(verinum(loff)), wid);
|
||||
}
|
||||
/* No select expressions. */
|
||||
|
||||
NetAssign_*lv = new NetAssign_(reg);
|
||||
|
||||
return lv;
|
||||
}
|
||||
@@ -374,10 +291,67 @@ NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des,
|
||||
return lv;
|
||||
}
|
||||
|
||||
bool PEIdent::elaborate_lval_net_bit_(Design*des,
|
||||
NetScope*scope,
|
||||
NetAssign_*lv) const
|
||||
{
|
||||
const name_component_t&name_tail = path_.back();
|
||||
const index_component_t&index_tail = name_tail.index.back();
|
||||
ivl_assert(*this, index_tail.msb != 0);
|
||||
ivl_assert(*this, index_tail.lsb == 0);
|
||||
|
||||
NetNet*reg = lv->sig();
|
||||
|
||||
// Bit selects have a single select expression. Evaluate the
|
||||
// constant value and treat it as a part select with a bit
|
||||
// width of 1.
|
||||
NetExpr*mux = elab_and_eval(des, scope, index_tail.msb, -1);
|
||||
long lsb = 0;
|
||||
|
||||
if (NetEConst*index_con = dynamic_cast<NetEConst*> (mux)) {
|
||||
lsb = index_con->value().as_long();
|
||||
mux = 0;
|
||||
}
|
||||
|
||||
if (mux) {
|
||||
// Non-constant bit mux. Correct the mux for the range
|
||||
// of the vector, then set the l-value part select expression.
|
||||
if (reg->msb() < reg->lsb())
|
||||
mux = make_sub_expr(reg->lsb(), mux);
|
||||
else if (reg->lsb() != 0)
|
||||
mux = make_add_expr(mux, - reg->lsb());
|
||||
|
||||
lv->set_part(mux, 1);
|
||||
|
||||
} else if (lsb == reg->msb() && lsb == reg->lsb()) {
|
||||
// Constant bit mux that happens to select the only bit
|
||||
// of the l-value. Don't bother with any select at all.
|
||||
|
||||
} else {
|
||||
// Constant bit select that does something useful.
|
||||
long loff = reg->sb_to_idx(lsb);
|
||||
|
||||
if (loff < 0 || loff >= (long)reg->vector_width()) {
|
||||
cerr << get_fileline() << ": error: bit select "
|
||||
<< reg->name() << "[" <<lsb<<"]"
|
||||
<< " is out of range." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
lv->set_part(new NetEConst(verinum(loff)), 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PEIdent::elaborate_lval_net_part_(Design*des,
|
||||
NetScope*scope,
|
||||
NetAssign_*lv) const
|
||||
{
|
||||
// The range expressions of a part select must be
|
||||
// constant. The calculate_parts_ function calculates the
|
||||
// values into msb and lsb.
|
||||
long msb, lsb;
|
||||
bool flag = calculate_parts_(des, scope, msb, lsb);
|
||||
if (!flag)
|
||||
@@ -388,17 +362,14 @@ bool PEIdent::elaborate_lval_net_part_(Design*des,
|
||||
|
||||
if (msb == reg->msb() && lsb == reg->lsb()) {
|
||||
|
||||
/* No bit select, and part select covers the entire
|
||||
vector. Simplest case. */
|
||||
/* Part select covers the entire vector. Simplest case. */
|
||||
|
||||
} else {
|
||||
|
||||
/* If the bit/part select is constant, then make the
|
||||
NetAssign_ only as wide as it needs to be and connect
|
||||
only to the selected bits of the reg. */
|
||||
unsigned loff = reg->sb_to_idx(lsb);
|
||||
unsigned moff = reg->sb_to_idx(msb);
|
||||
unsigned wid = moff - loff + 1;
|
||||
/* Get the canonical offsets into the vector. */
|
||||
long loff = reg->sb_to_idx(lsb);
|
||||
long moff = reg->sb_to_idx(msb);
|
||||
long wid = moff - loff + 1;
|
||||
|
||||
if (moff < loff) {
|
||||
cerr << get_fileline() << ": error: part select "
|
||||
@@ -408,17 +379,15 @@ bool PEIdent::elaborate_lval_net_part_(Design*des,
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If the part select extends beyond the extreme of the
|
||||
/* If the part select extends beyond the extremes of the
|
||||
variable, then report an error. Note that loff is
|
||||
converted to normalized form so is relative the
|
||||
variable pins. */
|
||||
|
||||
if ((wid + loff) > reg->vector_width()) {
|
||||
cerr << get_fileline() << ": error: bit/part select "
|
||||
if (loff < 0 || moff >= (signed)reg->vector_width()) {
|
||||
cerr << get_fileline() << ": warning: Part select "
|
||||
<< reg->name() << "[" << msb<<":"<<lsb<<"]"
|
||||
<< " is out of range." << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
lv->set_part(new NetEConst(verinum(loff)), wid);
|
||||
|
||||
+47
-16
@@ -137,7 +137,7 @@ NetNet* PEBinary::elaborate_net_add_(Design*des, NetScope*scope,
|
||||
const NetExpr* decay) const
|
||||
{
|
||||
NetNet*lsig = left_->elaborate_net(des, scope, lwidth, 0, 0, 0),
|
||||
*rsig = right_->elaborate_net(des, scope, lwidth, 0, 0, 0);
|
||||
*rsig = right_->elaborate_net(des, scope, lwidth, 0, 0, 0);
|
||||
|
||||
if (lsig == 0 || rsig == 0) return 0;
|
||||
|
||||
@@ -449,26 +449,21 @@ NetNet* PEBinary::elaborate_net_cmp_(Design*des, NetScope*scope,
|
||||
const NetExpr* fall,
|
||||
const NetExpr* decay) const
|
||||
{
|
||||
|
||||
/* Elaborate the operands of the compare first as expressions
|
||||
(so that the eval_tree method can reduce constant
|
||||
expressions, including parameters) then turn those results
|
||||
into synthesized nets. */
|
||||
NetExpr*lexp = elab_and_eval(des, scope, left_, lwidth),
|
||||
*rexp = elab_and_eval(des, scope, right_, lwidth);
|
||||
NetExpr*lexp = elab_and_eval(des, scope, left_, -1),
|
||||
*rexp = elab_and_eval(des, scope, right_, -1);
|
||||
|
||||
if (lexp == 0 || rexp == 0) return 0;
|
||||
|
||||
unsigned operand_width;
|
||||
bool real_arg = false;
|
||||
if (lexp->expr_type() == IVL_VT_REAL ||
|
||||
rexp->expr_type() == IVL_VT_REAL) {
|
||||
operand_width = 1;
|
||||
real_arg = true;
|
||||
} else {
|
||||
bool real_arg = true;
|
||||
if (lexp->expr_type() != IVL_VT_REAL &&
|
||||
rexp->expr_type() != IVL_VT_REAL) {
|
||||
/* Choose the operand width to be the width of the widest
|
||||
self-determined operand. */
|
||||
operand_width = lexp->expr_width();
|
||||
unsigned operand_width = lexp->expr_width();
|
||||
if (rexp->expr_width() > operand_width)
|
||||
operand_width = rexp->expr_width();
|
||||
|
||||
@@ -476,6 +471,8 @@ NetNet* PEBinary::elaborate_net_cmp_(Design*des, NetScope*scope,
|
||||
lexp = pad_to_width(lexp, operand_width);
|
||||
rexp->set_width(operand_width);
|
||||
rexp = pad_to_width(rexp, operand_width);
|
||||
|
||||
real_arg = false;
|
||||
}
|
||||
|
||||
NetNet*lsig = 0;
|
||||
@@ -719,6 +716,16 @@ NetNet* PEBinary::elaborate_net_div_(Design*des, NetScope*scope,
|
||||
|
||||
unsigned rwidth = lwidth;
|
||||
|
||||
// If either operand is IVL_VT_REAL, then cast the other to
|
||||
// IVL_VT_REAL so that the division can become IVL_VT_REAL.
|
||||
|
||||
if (lsig->data_type()==IVL_VT_REAL || rsig->data_type()==IVL_VT_REAL) {
|
||||
if (lsig->data_type() != IVL_VT_REAL)
|
||||
lsig = cast_to_real(des, scope, lsig);
|
||||
if (rsig->data_type() != IVL_VT_REAL)
|
||||
rsig = cast_to_real(des, scope, rsig);
|
||||
}
|
||||
|
||||
if (rwidth == 0) {
|
||||
rwidth = lsig->vector_width();
|
||||
if (rsig->vector_width() > rwidth)
|
||||
@@ -790,8 +797,8 @@ NetNet* PEBinary::elaborate_net_mod_(Design*des, NetScope*scope,
|
||||
const NetExpr* fall,
|
||||
const NetExpr* decay) const
|
||||
{
|
||||
NetNet*lsig = left_->elaborate_net(des, scope, 0, 0, 0, 0),
|
||||
*rsig = right_->elaborate_net(des, scope, 0, 0, 0, 0);
|
||||
NetNet*lsig = left_->elaborate_net(des, scope, lwidth, 0, 0, 0),
|
||||
*rsig = right_->elaborate_net(des, scope, lwidth, 0, 0, 0);
|
||||
|
||||
if (lsig == 0 || rsig == 0) return 0;
|
||||
|
||||
@@ -1787,8 +1794,32 @@ NetNet* PEIdent::elaborate_net(Design*des, NetScope*scope,
|
||||
that connects to a signal with the correct name. */
|
||||
if (par != 0) {
|
||||
|
||||
// Detect and handle the special case that we have a
|
||||
// real valued parameter. Return a NetLiteral and a
|
||||
// properly typed net.
|
||||
if (const NetECReal*pc = dynamic_cast<const NetECReal*>(par)) {
|
||||
NetLiteral*tmp = new NetLiteral(scope, scope->local_symbol(),
|
||||
pc->value());
|
||||
des->add_node(tmp);
|
||||
tmp->set_line(*par);
|
||||
sig = new NetNet(scope, scope->local_symbol(),
|
||||
NetNet::IMPLICIT);
|
||||
sig->set_line(*tmp);
|
||||
sig->data_type(tmp->data_type());
|
||||
sig->local_flag(true);
|
||||
|
||||
connect(tmp->pin(0), sig->pin(0));
|
||||
return sig;
|
||||
}
|
||||
|
||||
const NetEConst*pc = dynamic_cast<const NetEConst*>(par);
|
||||
assert(pc);
|
||||
if (pc == 0) {
|
||||
cerr << get_fileline() << ": internal error: "
|
||||
<< "Non-consant parameter value?: " << *par << endl;
|
||||
cerr << get_fileline() << ": : "
|
||||
<< "Expression type is " << par->expr_type() << endl;
|
||||
}
|
||||
ivl_assert(*this, pc);
|
||||
verinum pvalue = pc->value();
|
||||
|
||||
/* If the parameter has declared dimensions, then apply
|
||||
@@ -2296,7 +2327,7 @@ NetNet* PEIdent::elaborate_net_array_(Design*des, NetScope*scope,
|
||||
} while (0);
|
||||
#else
|
||||
if (name_tail.index.size() > sig->array_dimensions())
|
||||
tmp = process_select_(des, scope, sig);
|
||||
tmp = process_select_(des, scope, tmp);
|
||||
|
||||
#endif
|
||||
return tmp;
|
||||
|
||||
+234
-79
@@ -53,7 +53,7 @@ void Module::elaborate_parm_item_(perm_string name, const param_expr_t&cur,
|
||||
assert(ex);
|
||||
|
||||
NetExpr*val = ex->elaborate_pexpr(des, scope);
|
||||
if (val == 0) return;
|
||||
|
||||
NetExpr*msb = 0;
|
||||
NetExpr*lsb = 0;
|
||||
bool signed_flag = cur.signed_flag;
|
||||
@@ -71,19 +71,6 @@ void Module::elaborate_parm_item_(perm_string name, const param_expr_t&cur,
|
||||
assert(lsb);
|
||||
}
|
||||
|
||||
if (signed_flag) {
|
||||
/* If explicitly signed, then say so. */
|
||||
val->cast_signed(true);
|
||||
} else if (cur.msb) {
|
||||
/* If there is a range, then the signedness comes
|
||||
from the type and not the expression. */
|
||||
val->cast_signed(signed_flag);
|
||||
} else {
|
||||
/* otherwise, let the expression describe
|
||||
itself. */
|
||||
signed_flag = val->has_sign();
|
||||
}
|
||||
|
||||
NetScope::range_t*range_list = 0;
|
||||
for (Module::range_t*range = cur.range ; range ; range = range->next) {
|
||||
NetScope::range_t*tmp = new NetScope::range_t;
|
||||
@@ -118,13 +105,124 @@ void Module::elaborate_parm_item_(perm_string name, const param_expr_t&cur,
|
||||
range_list = tmp;
|
||||
}
|
||||
|
||||
val = scope->set_parameter(name, val, cur.type, msb, lsb, signed_flag, range_list, cur);
|
||||
assert(val);
|
||||
/* Set the parameter expression to 0 if the evaluation failed. */
|
||||
if (val == 0) {
|
||||
val = scope->set_parameter(name, val, cur.type, msb, lsb,
|
||||
signed_flag, range_list, cur);
|
||||
delete val;
|
||||
return;
|
||||
}
|
||||
|
||||
if (signed_flag) {
|
||||
/* If explicitly signed, then say so. */
|
||||
val->cast_signed(true);
|
||||
} else if (cur.msb) {
|
||||
/* If there is a range, then the signedness comes
|
||||
from the type and not the expression. */
|
||||
val->cast_signed(signed_flag);
|
||||
} else {
|
||||
/* otherwise, let the expression describe
|
||||
itself. */
|
||||
signed_flag = val->has_sign();
|
||||
}
|
||||
|
||||
val = scope->set_parameter(name, val, cur.type, msb, lsb, signed_flag,
|
||||
range_list, cur);
|
||||
delete val;
|
||||
}
|
||||
|
||||
static void elaborate_scope_tasks(Design*des, NetScope*scope,
|
||||
const LineInfo&loc,
|
||||
const map<perm_string,PTask*>&tasks)
|
||||
{
|
||||
typedef map<perm_string,PTask*>::const_iterator tasks_it_t;
|
||||
|
||||
for (tasks_it_t cur = tasks.begin()
|
||||
; cur != tasks.end() ; cur ++ ) {
|
||||
|
||||
hname_t use_name( (*cur).first );
|
||||
if (scope->child(use_name)) {
|
||||
cerr << loc.get_fileline() << ": error: task/scope name "
|
||||
<< use_name << " already used in this context."
|
||||
<< endl;
|
||||
des->errors += 1;
|
||||
continue;
|
||||
}
|
||||
NetScope*task_scope = new NetScope(scope, use_name,
|
||||
NetScope::TASK);
|
||||
task_scope->set_line((*cur).second);
|
||||
|
||||
if (debug_scopes)
|
||||
cerr << cur->second->get_fileline() << ": debug: "
|
||||
<< "Elaborate task scope " << scope_path(task_scope) << endl;
|
||||
(*cur).second->elaborate_scope(des, task_scope);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void elaborate_scope_funcs(Design*des, NetScope*scope,
|
||||
const LineInfo&loc,
|
||||
const map<perm_string,PFunction*>&funcs)
|
||||
{
|
||||
typedef map<perm_string,PFunction*>::const_iterator funcs_it_t;
|
||||
|
||||
for (funcs_it_t cur = funcs.begin()
|
||||
; cur != funcs.end() ; cur ++ ) {
|
||||
|
||||
hname_t use_name( (*cur).first );
|
||||
if (scope->child(use_name)) {
|
||||
cerr << loc.get_fileline() << ": error: function/scope name "
|
||||
<< use_name << " already used in this context."
|
||||
<< endl;
|
||||
des->errors += 1;
|
||||
continue;
|
||||
}
|
||||
NetScope*func_scope = new NetScope(scope, use_name,
|
||||
NetScope::FUNC);
|
||||
func_scope->set_line((*cur).second);
|
||||
|
||||
if (debug_scopes)
|
||||
cerr << cur->second->get_fileline() << ": debug: "
|
||||
<< "Elaborate function scope " << scope_path(func_scope) << endl;
|
||||
(*cur).second->elaborate_scope(des, func_scope);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class generate_schemes_work_item_t : public elaborator_work_item_t {
|
||||
public:
|
||||
generate_schemes_work_item_t(Design*des, NetScope*scope, Module*mod)
|
||||
: elaborator_work_item_t(des), scope_(scope), mod_(mod)
|
||||
{ }
|
||||
|
||||
void elaborate_runrun()
|
||||
{
|
||||
if (debug_scopes)
|
||||
cerr << mod_->get_fileline() << ": debug: "
|
||||
<< "Processing generate schemes for "
|
||||
<< scope_path(scope_) << endl;
|
||||
|
||||
// Generate schemes can create new scopes in the form of
|
||||
// generated code. Scan the generate schemes, and *generate*
|
||||
// new scopes, which is slightly different from simple
|
||||
// elaboration.
|
||||
typedef list<PGenerate*>::const_iterator generate_it_t;
|
||||
for (generate_it_t cur = mod_->generate_schemes.begin()
|
||||
; cur != mod_->generate_schemes.end() ; cur ++ ) {
|
||||
(*cur) -> generate_scope(des, scope_);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// The scope_ is the scope that contains the generate scheme
|
||||
// we are to work on. the mod_ is the Module definition for
|
||||
// that scope, and contains the parsed generate schemes.
|
||||
NetScope*scope_;
|
||||
Module*mod_;
|
||||
};
|
||||
|
||||
bool Module::elaborate_scope(Design*des, NetScope*scope,
|
||||
const replace_t&replacements) const
|
||||
const replace_t&replacements)
|
||||
{
|
||||
if (debug_scopes) {
|
||||
cerr << get_fileline() << ": debug: Elaborate scope "
|
||||
@@ -143,8 +241,6 @@ bool Module::elaborate_scope(Design*des, NetScope*scope,
|
||||
// place of the elaborated expression.
|
||||
|
||||
typedef map<perm_string,param_expr_t>::const_iterator mparm_it_t;
|
||||
typedef map<pform_name_t,PExpr*>::const_iterator pform_parm_it_t;
|
||||
|
||||
|
||||
// This loop scans the parameters in the module, and creates
|
||||
// stub parameter entries in the scope for the parameter name.
|
||||
@@ -228,15 +324,17 @@ bool Module::elaborate_scope(Design*des, NetScope*scope,
|
||||
// here because the parameter receiving the assignment may be
|
||||
// in a scope not discovered by this pass.
|
||||
|
||||
for (pform_parm_it_t cur = defparms.begin()
|
||||
; cur != defparms.end() ; cur ++ ) {
|
||||
typedef list<Module::named_expr_t>::const_iterator defparms_iter_t;
|
||||
for (defparms_iter_t cur = defparms.begin()
|
||||
; cur != defparms.end() ; cur ++) {
|
||||
|
||||
PExpr*ex = (*cur).second;
|
||||
PExpr*ex = cur->second;
|
||||
assert(ex);
|
||||
|
||||
NetExpr*val = ex->elaborate_pexpr(des, scope);
|
||||
if (val == 0) continue;
|
||||
scope->defparams[(*cur).first] = val;
|
||||
|
||||
scope->defparams.push_back(make_pair(cur->first, val));
|
||||
}
|
||||
|
||||
// Evaluate the attributes. Evaluate them in the scope of the
|
||||
@@ -249,66 +347,29 @@ bool Module::elaborate_scope(Design*des, NetScope*scope,
|
||||
|
||||
delete[]attr;
|
||||
|
||||
// Generate schemes can create new scopes in the form of
|
||||
// generated code. Scan the generate schemes, and *generate*
|
||||
// new scopes, which is slightly different from simple
|
||||
// elaboration.
|
||||
|
||||
typedef list<PGenerate*>::const_iterator generate_it_t;
|
||||
for (generate_it_t cur = generate_schemes.begin()
|
||||
; cur != generate_schemes.end() ; cur ++ ) {
|
||||
(*cur) -> generate_scope(des, scope);
|
||||
}
|
||||
// Generate schemes need to have their scopes elaborated, but
|
||||
// we cannot do that until defparams are run, so push it off
|
||||
// into an elaborate work item.
|
||||
if (debug_scopes)
|
||||
cerr << get_fileline() << ": debug: "
|
||||
<< "Schedule generates within " << scope_path(scope)
|
||||
<< " for elaboration after defparams." << endl;
|
||||
|
||||
des->elaboration_work_list.push_back(new generate_schemes_work_item_t(des, scope, this));
|
||||
|
||||
// Tasks introduce new scopes, so scan the tasks in this
|
||||
// module. Create a scope for the task and pass that to the
|
||||
// elaborate_scope method of the PTask for detailed
|
||||
// processing.
|
||||
|
||||
typedef map<perm_string,PTask*>::const_iterator tasks_it_t;
|
||||
|
||||
for (tasks_it_t cur = tasks_.begin()
|
||||
; cur != tasks_.end() ; cur ++ ) {
|
||||
|
||||
hname_t use_name( (*cur).first );
|
||||
if (scope->child(use_name)) {
|
||||
cerr << get_fileline() << ": error: task/scope name "
|
||||
<< use_name << " already used in this context."
|
||||
<< endl;
|
||||
des->errors += 1;
|
||||
continue;
|
||||
}
|
||||
NetScope*task_scope = new NetScope(scope, use_name,
|
||||
NetScope::TASK);
|
||||
task_scope->set_line((*cur).second);
|
||||
(*cur).second->elaborate_scope(des, task_scope);
|
||||
}
|
||||
elaborate_scope_tasks(des, scope, *this, tasks);
|
||||
|
||||
|
||||
// Functions are very similar to tasks, at least from the
|
||||
// perspective of scopes. So handle them exactly the same
|
||||
// way.
|
||||
|
||||
typedef map<perm_string,PFunction*>::const_iterator funcs_it_t;
|
||||
|
||||
for (funcs_it_t cur = funcs_.begin()
|
||||
; cur != funcs_.end() ; cur ++ ) {
|
||||
|
||||
hname_t use_name( (*cur).first );
|
||||
if (scope->child(use_name)) {
|
||||
cerr << get_fileline() << ": error: function/scope name "
|
||||
<< use_name << " already used in this context."
|
||||
<< endl;
|
||||
des->errors += 1;
|
||||
continue;
|
||||
}
|
||||
NetScope*func_scope = new NetScope(scope, use_name,
|
||||
NetScope::FUNC);
|
||||
func_scope->set_line((*cur).second);
|
||||
(*cur).second->elaborate_scope(des, func_scope);
|
||||
}
|
||||
|
||||
elaborate_scope_funcs(des, scope, *this, funcs);
|
||||
|
||||
// Gates include modules, which might introduce new scopes, so
|
||||
// scan all of them to create those scopes.
|
||||
@@ -422,7 +483,12 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container)
|
||||
container->genvar_tmp_val = genvar;
|
||||
NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1);
|
||||
NetEConst*test = dynamic_cast<NetEConst*>(test_ex);
|
||||
assert(test);
|
||||
if (test == 0) {
|
||||
cerr << get_fileline() << ": error: Cannot evaluate genvar"
|
||||
<< " conditional expression: " << *loop_test << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
while (test->value().as_long()) {
|
||||
|
||||
// The actual name of the scope includes the genvar so
|
||||
@@ -469,7 +535,12 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container)
|
||||
// Calculate the step for the loop variable.
|
||||
NetExpr*step_ex = elab_and_eval(des, container, loop_step, -1);
|
||||
NetEConst*step = dynamic_cast<NetEConst*>(step_ex);
|
||||
assert(step);
|
||||
if (step == 0) {
|
||||
cerr << get_fileline() << ": error: Cannot evaluate genvar"
|
||||
<< " step expression: " << *loop_step << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
if (debug_scopes)
|
||||
cerr << get_fileline() << ": debug: genvar step from "
|
||||
<< genvar << " to " << step->value().as_long() << endl;
|
||||
@@ -494,7 +565,12 @@ bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else
|
||||
{
|
||||
NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1);
|
||||
NetEConst*test = dynamic_cast<NetEConst*> (test_ex);
|
||||
assert(test);
|
||||
if (test == 0) {
|
||||
cerr << get_fileline() << ": error: Cannot evaluate genvar"
|
||||
<< " conditional expression: " << *loop_test << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the condition evaluates as false, then do not create the
|
||||
// scope.
|
||||
@@ -536,7 +612,12 @@ bool PGenerate::generate_scope_case_(Design*des, NetScope*container)
|
||||
{
|
||||
NetExpr*case_value_ex = elab_and_eval(des, container, loop_test, -1);
|
||||
NetEConst*case_value_co = dynamic_cast<NetEConst*>(case_value_ex);
|
||||
assert(case_value_co);
|
||||
if (case_value_co == 0) {
|
||||
cerr << get_fileline() << ": error: Cannot evaluate genvar case"
|
||||
<< " expression: " << *loop_test << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// The name of the scope to generate, whatever that item is.
|
||||
hname_t use_name (scope_name);
|
||||
@@ -548,8 +629,8 @@ bool PGenerate::generate_scope_case_(Design*des, NetScope*container)
|
||||
PGenerate*default_item = 0;
|
||||
|
||||
typedef list<PGenerate*>::const_iterator generator_it_t;
|
||||
generator_it_t cur = generates.begin();
|
||||
while (cur != generates.end()) {
|
||||
generator_it_t cur = generate_schemes.begin();
|
||||
while (cur != generate_schemes.end()) {
|
||||
PGenerate*item = *cur;
|
||||
assert( item->scheme_type == PGenerate::GS_CASE_ITEM );
|
||||
|
||||
@@ -578,7 +659,7 @@ bool PGenerate::generate_scope_case_(Design*des, NetScope*container)
|
||||
delete case_value_co;
|
||||
case_value_co = 0;
|
||||
|
||||
PGenerate*item = (cur == generates.end())? default_item : *cur;
|
||||
PGenerate*item = (cur == generate_schemes.end())? default_item : *cur;
|
||||
if (item == 0) {
|
||||
cerr << get_fileline() << ": debug: "
|
||||
<< "No generate items found" << endl;
|
||||
@@ -605,11 +686,14 @@ void PGenerate::elaborate_subscope_(Design*des, NetScope*scope)
|
||||
// from simple elaboration.
|
||||
|
||||
typedef list<PGenerate*>::const_iterator generate_it_t;
|
||||
for (generate_it_t cur = generates.begin()
|
||||
; cur != generates.end() ; cur ++ ) {
|
||||
for (generate_it_t cur = generate_schemes.begin()
|
||||
; cur != generate_schemes.end() ; cur ++ ) {
|
||||
(*cur) -> generate_scope(des, scope);
|
||||
}
|
||||
|
||||
elaborate_scope_tasks(des, scope, *this, tasks);
|
||||
elaborate_scope_funcs(des, scope, *this, funcs);
|
||||
|
||||
// Scan the generated scope for gates that may create
|
||||
// their own scopes.
|
||||
typedef list<PGate*>::const_iterator pgate_list_it_t;
|
||||
@@ -618,10 +702,51 @@ void PGenerate::elaborate_subscope_(Design*des, NetScope*scope)
|
||||
(*cur) ->elaborate_scope(des, scope);
|
||||
}
|
||||
|
||||
typedef list<PProcess*>::const_iterator proc_it_t;
|
||||
for (proc_it_t cur = behaviors.begin()
|
||||
; cur != behaviors.end() ; cur ++ ) {
|
||||
(*cur) -> statement() -> elaborate_scope(des, scope);
|
||||
}
|
||||
|
||||
// Save the scope that we created, for future use.
|
||||
scope_list_.push_back(scope);
|
||||
}
|
||||
|
||||
class delayed_elaborate_scope_mod_instances : public elaborator_work_item_t {
|
||||
|
||||
public:
|
||||
delayed_elaborate_scope_mod_instances(Design*des,
|
||||
const PGModule*obj,
|
||||
Module*mod,
|
||||
NetScope*sc)
|
||||
: elaborator_work_item_t(des), obj_(obj), mod_(mod), sc_(sc)
|
||||
{ }
|
||||
~delayed_elaborate_scope_mod_instances() { }
|
||||
|
||||
virtual void elaborate_runrun();
|
||||
|
||||
private:
|
||||
const PGModule*obj_;
|
||||
Module*mod_;
|
||||
NetScope*sc_;
|
||||
};
|
||||
|
||||
void delayed_elaborate_scope_mod_instances::elaborate_runrun()
|
||||
{
|
||||
if (debug_scopes)
|
||||
cerr << obj_->get_fileline() << ": debug: "
|
||||
<< "Resume scope elaboration of instances of "
|
||||
<< mod_->mod_name() << "." << endl;
|
||||
|
||||
obj_->elaborate_scope_mod_instances_(des, mod_, sc_);
|
||||
}
|
||||
|
||||
/*
|
||||
* Here we handle the elaborate scope of a module instance. The caller
|
||||
* has already figured out that this "gate" is a module, and has found
|
||||
* the module definition. The "sc" argument is the scope that will
|
||||
* contain this instance.
|
||||
*/
|
||||
void PGModule::elaborate_scope_mod_(Design*des, Module*mod, NetScope*sc) const
|
||||
{
|
||||
if (get_name() == "") {
|
||||
@@ -668,6 +793,36 @@ void PGModule::elaborate_scope_mod_(Design*des, Module*mod, NetScope*sc) const
|
||||
return;
|
||||
}
|
||||
|
||||
if (msb_ || lsb_) {
|
||||
// If there are expressions to evaluate in order to know
|
||||
// the actual number of instances that will be
|
||||
// instantiated, then we have to delay further scope
|
||||
// elaboration until after defparams (above me) are
|
||||
// run. Do that by appending a work item to the
|
||||
// elaboration work list.
|
||||
if (debug_scopes)
|
||||
cerr << get_fileline() << ": debug: delay elaborate_scope"
|
||||
<< " of array of " << get_name()
|
||||
<< " in scope " << scope_path(sc) << "." << endl;
|
||||
|
||||
elaborator_work_item_t*tmp
|
||||
= new delayed_elaborate_scope_mod_instances(des, this, mod, sc);
|
||||
des->elaboration_work_list.push_back(tmp);
|
||||
|
||||
} else {
|
||||
// If there are no expressions that need to be evaluated
|
||||
// to elaborate the scope of this next instances, then
|
||||
// get right to it.
|
||||
elaborate_scope_mod_instances_(des, mod, sc);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called to process a module instantiation after basic
|
||||
* sanity testing is already complete.
|
||||
*/
|
||||
void PGModule::elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*sc) const
|
||||
{
|
||||
NetExpr*mse = msb_ ? elab_and_eval(des, sc, msb_, -1) : 0;
|
||||
NetExpr*lse = lsb_ ? elab_and_eval(des, sc, lsb_, -1) : 0;
|
||||
NetEConst*msb = dynamic_cast<NetEConst*> (mse);
|
||||
|
||||
+61
-37
@@ -108,6 +108,42 @@ bool PScope::elaborate_sig_wires_(Design*des, NetScope*scope) const
|
||||
return flag;
|
||||
}
|
||||
|
||||
static void elaborate_sig_funcs(Design*des, NetScope*scope,
|
||||
const map<perm_string,PFunction*>&funcs)
|
||||
{
|
||||
typedef map<perm_string,PFunction*>::const_iterator mfunc_it_t;
|
||||
|
||||
for (mfunc_it_t cur = funcs.begin()
|
||||
; cur != funcs.end() ; cur ++) {
|
||||
|
||||
hname_t use_name ( (*cur).first );
|
||||
NetScope*fscope = scope->child(use_name);
|
||||
if (scope == 0) {
|
||||
cerr << (*cur).second->get_fileline() << ": internal error: "
|
||||
<< "Child scope for function " << (*cur).first
|
||||
<< " missing in " << scope_path(scope) << "." << endl;
|
||||
des->errors += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
(*cur).second->elaborate_sig(des, fscope);
|
||||
}
|
||||
}
|
||||
|
||||
static void elaborate_sig_tasks(Design*des, NetScope*scope,
|
||||
const map<perm_string,PTask*>&tasks)
|
||||
{
|
||||
typedef map<perm_string,PTask*>::const_iterator mtask_it_t;
|
||||
|
||||
for (mtask_it_t cur = tasks.begin()
|
||||
; cur != tasks.end() ; cur ++) {
|
||||
NetScope*tscope = scope->child( hname_t((*cur).first) );
|
||||
assert(tscope);
|
||||
(*cur).second->elaborate_sig(des, tscope);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Module::elaborate_sig(Design*des, NetScope*scope) const
|
||||
{
|
||||
bool flag = true;
|
||||
@@ -182,38 +218,12 @@ bool Module::elaborate_sig(Design*des, NetScope*scope) const
|
||||
flag &= (*gt)->elaborate_sig(des, scope);
|
||||
}
|
||||
|
||||
|
||||
typedef map<perm_string,PFunction*>::const_iterator mfunc_it_t;
|
||||
|
||||
for (mfunc_it_t cur = funcs_.begin()
|
||||
; cur != funcs_.end() ; cur ++) {
|
||||
|
||||
hname_t use_name ( (*cur).first );
|
||||
NetScope*fscope = scope->child(use_name);
|
||||
if (scope == 0) {
|
||||
cerr << (*cur).second->get_fileline() << ": internal error: "
|
||||
<< "Child scope for function " << (*cur).first
|
||||
<< " missing in " << scope_path(scope) << "." << endl;
|
||||
des->errors += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
(*cur).second->elaborate_sig(des, fscope);
|
||||
}
|
||||
|
||||
|
||||
// After all the wires are elaborated, we are free to
|
||||
// elaborate the ports of the tasks defined within this
|
||||
// module. Run through them now.
|
||||
|
||||
typedef map<perm_string,PTask*>::const_iterator mtask_it_t;
|
||||
|
||||
for (mtask_it_t cur = tasks_.begin()
|
||||
; cur != tasks_.end() ; cur ++) {
|
||||
NetScope*tscope = scope->child( hname_t((*cur).first) );
|
||||
assert(tscope);
|
||||
(*cur).second->elaborate_sig(des, tscope);
|
||||
}
|
||||
elaborate_sig_funcs(des, scope, funcs);
|
||||
elaborate_sig_tasks(des, scope, tasks);
|
||||
|
||||
// initial and always blocks may contain begin-end and
|
||||
// fork-join blocks that can introduce scopes. Therefore, I
|
||||
@@ -415,8 +425,8 @@ bool PGenerate::elaborate_sig(Design*des, NetScope*container) const
|
||||
<< scope_path(container) << "." << endl;
|
||||
|
||||
typedef list<PGenerate*>::const_iterator generate_it_t;
|
||||
for (generate_it_t cur = generates.begin()
|
||||
; cur != generates.end() ; cur ++) {
|
||||
for (generate_it_t cur = generate_schemes.begin()
|
||||
; cur != generate_schemes.end() ; cur ++) {
|
||||
PGenerate*item = *cur;
|
||||
if (! item->scope_list_.empty()) {
|
||||
flag &= item->elaborate_sig(des, container);
|
||||
@@ -461,9 +471,12 @@ bool PGenerate::elaborate_sig_(Design*des, NetScope*scope) const
|
||||
cur->elaborate_sig(des, scope);
|
||||
}
|
||||
|
||||
elaborate_sig_funcs(des, scope, funcs);
|
||||
elaborate_sig_tasks(des, scope, tasks);
|
||||
|
||||
typedef list<PGenerate*>::const_iterator generate_it_t;
|
||||
for (generate_it_t cur = generates.begin()
|
||||
; cur != generates.end() ; cur ++ ) {
|
||||
for (generate_it_t cur = generate_schemes.begin()
|
||||
; cur != generate_schemes.end() ; cur ++ ) {
|
||||
(*cur) -> elaborate_sig(des, scope);
|
||||
}
|
||||
|
||||
@@ -576,9 +589,17 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const
|
||||
break;
|
||||
|
||||
default:
|
||||
cerr << get_fileline() << ": internal error: I don't know how "
|
||||
<< "to deal with return type of function "
|
||||
<< scope->basename() << "." << endl;
|
||||
if (ports_) {
|
||||
cerr << get_fileline() << ": internal error: I don't know "
|
||||
<< "how to deal with return type of function "
|
||||
<< scope->basename() << "." << endl;
|
||||
} else {
|
||||
/* If we do not have any ports or a return type this
|
||||
* is probably a bad function definition. */
|
||||
cerr << get_fileline() << ": error: Bad definition for "
|
||||
<< "function " << scope->basename() << "?" << endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
svector<NetNet*>ports (ports_? ports_->count() : 0);
|
||||
@@ -990,8 +1011,11 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const
|
||||
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": debug: Create signal "
|
||||
<< wtype << " ["<<msb<<":"<<lsb<<"] " << name_
|
||||
<< " in scope " << scope_path(scope) << endl;
|
||||
<< wtype << " ["<<msb<<":"<<lsb<<"] " << name_;
|
||||
if (array_dimensions > 0) {
|
||||
cerr << " [" << array_s0 << ":" << array_e0 << "]" << endl;
|
||||
}
|
||||
cerr << " in scope " << scope_path(scope) << endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+214
-72
@@ -94,7 +94,7 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const
|
||||
assert(lval->pin_count() == 1);
|
||||
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": debug: PGassign: elaborated l-value"
|
||||
cerr << get_fileline() << ": debug: PGAssign: elaborated l-value"
|
||||
<< " width=" << lval->vector_width()
|
||||
<< ", type=" << lval->data_type() << endl;
|
||||
}
|
||||
@@ -113,14 +113,13 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const
|
||||
return;
|
||||
}
|
||||
|
||||
/* If either lval or rid are real then both must be real. */
|
||||
if ((lval->data_type() == IVL_VT_REAL ||
|
||||
rid->data_type() == IVL_VT_REAL) &&
|
||||
lval->data_type() != rid->data_type()) {
|
||||
cerr << get_fileline() << ": sorry: Both the r-value and "
|
||||
"the l-value must be real in this context." << endl;
|
||||
des->errors += 1;
|
||||
return;
|
||||
/* Cast the right side when needed. */
|
||||
if ((lval->data_type() == IVL_VT_REAL &&
|
||||
rid->data_type() != IVL_VT_REAL)) {
|
||||
rid = cast_to_real(des, scope, rid);
|
||||
} else if ((lval->data_type() != IVL_VT_REAL &&
|
||||
rid->data_type() == IVL_VT_REAL)) {
|
||||
rid = cast_to_int(des, scope, rid, lval->vector_width());
|
||||
}
|
||||
|
||||
ivl_assert(*this, rid);
|
||||
@@ -287,14 +286,13 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const
|
||||
assert(lval && rval);
|
||||
assert(rval->pin_count() == 1);
|
||||
|
||||
/* If either lval or rval are real then both must be real. */
|
||||
if ((lval->data_type() == IVL_VT_REAL ||
|
||||
rval->data_type() == IVL_VT_REAL) &&
|
||||
lval->data_type() != rval->data_type()) {
|
||||
cerr << get_fileline() << ": sorry: Both the r-value and "
|
||||
"the l-value must be real in this context." << endl;
|
||||
des->errors += 1;
|
||||
return;
|
||||
/* Cast the right side when needed. */
|
||||
if ((lval->data_type() == IVL_VT_REAL &&
|
||||
rval->data_type() != IVL_VT_REAL)) {
|
||||
rval = cast_to_real(des, scope, rval);
|
||||
} else if ((lval->data_type() != IVL_VT_REAL &&
|
||||
rval->data_type() == IVL_VT_REAL)) {
|
||||
rval = cast_to_int(des, scope, rval, lval->vector_width());
|
||||
}
|
||||
|
||||
/* If the r-value insists on being smaller then the l-value
|
||||
@@ -386,7 +384,7 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const
|
||||
high = msb.as_long();
|
||||
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": debug: PGBuiltin: Make arrray "
|
||||
cerr << get_fileline() << ": debug: PGBuiltin: Make array "
|
||||
<< "[" << high << ":" << low << "]"
|
||||
<< " of " << count << " gates for " << name << endl;
|
||||
}
|
||||
@@ -946,6 +944,17 @@ NetNet*PGModule::resize_net_to_port_(Design*des, NetScope*scope,
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static bool need_bufz_for_input_port(const svector<NetNet*>&prts)
|
||||
{
|
||||
if (prts[0]->port_type() != NetNet::PINPUT)
|
||||
return false;
|
||||
|
||||
if (prts[0]->pin(0).nexus()->drivers_present())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Instantiate a module by recursively elaborating it. Set the path of
|
||||
* the recursive elaboration so that signal names get properly
|
||||
@@ -1160,11 +1169,11 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
|
||||
|
||||
/* Input to module. elaborate the expression to
|
||||
the desired width. If this in an instance
|
||||
array, then let the net determine it's own
|
||||
array, then let the net determine its own
|
||||
width. We use that, then, to decide how to hook
|
||||
it up.
|
||||
|
||||
NOTE that this also handles the case that the
|
||||
v NOTE that this also handles the case that the
|
||||
port is actually empty on the inside. We assume
|
||||
in that case that the port is input. */
|
||||
|
||||
@@ -1178,6 +1187,21 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
|
||||
continue;
|
||||
}
|
||||
|
||||
if (need_bufz_for_input_port(prts)) {
|
||||
NetBUFZ*tmp = new NetBUFZ(scope, scope->local_symbol(),
|
||||
sig->vector_width());
|
||||
des->add_node(tmp);
|
||||
connect(tmp->pin(1), sig->pin(0));
|
||||
|
||||
NetNet*tmp2 = new NetNet(scope, scope->local_symbol(),
|
||||
NetNet::WIRE, sig->vector_width());
|
||||
tmp2->local_flag(true);
|
||||
tmp2->set_line(*this);
|
||||
tmp2->data_type(sig->data_type());
|
||||
connect(tmp->pin(0), tmp2->pin(0));
|
||||
sig = tmp2;
|
||||
}
|
||||
|
||||
} else if (prts[0]->port_type() == NetNet::PINOUT) {
|
||||
|
||||
/* Inout to/from module. This is a more
|
||||
@@ -1189,8 +1213,8 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
|
||||
identifier elaborates to the same NetNet in
|
||||
both cases so the extra elaboration has no
|
||||
effect. But if the expression passed to the
|
||||
inout port is a part select, aspecial part
|
||||
select must be created that can paqss data in
|
||||
inout port is a part select, a special part
|
||||
select must be created that can pass data in
|
||||
both directions.
|
||||
|
||||
Use the elaborate_bi_net method to handle all
|
||||
@@ -1952,7 +1976,7 @@ NetProc* PBlock::elaborate(Design*des, NetScope*scope) const
|
||||
if (nscope == 0) {
|
||||
cerr << get_fileline() << ": internal error: "
|
||||
"unable to find block scope " << scope_path(scope)
|
||||
<< "<" << pscope_name() << ">" << endl;
|
||||
<< "." << pscope_name() << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -2625,8 +2649,8 @@ NetProc* PEventStatement::elaborate_st(Design*des, NetScope*scope,
|
||||
}
|
||||
NexusSet*nset = enet->nex_input(rem_out);
|
||||
if (nset == 0) {
|
||||
cerr << get_fileline() << ": internal error: No NexusSet"
|
||||
<< " from statement." << endl;
|
||||
cerr << get_fileline() << ": error: Unable to elaborate:"
|
||||
<< endl;
|
||||
enet->dump(cerr, 6);
|
||||
des->errors += 1;
|
||||
return enet;
|
||||
@@ -2956,7 +2980,7 @@ NetForce* PForce::elaborate(Design*des, NetScope*scope) const
|
||||
dev = new NetForce(lval, rexp);
|
||||
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": debug: ELaborate force,"
|
||||
cerr << get_fileline() << ": debug: Elaborate force,"
|
||||
<< " lval width=" << lval->lwidth()
|
||||
<< " rval width=" << rexp->expr_width()
|
||||
<< " rval=" << *rexp
|
||||
@@ -3055,7 +3079,7 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const
|
||||
sig = des->find_signal(scope, id2->path());
|
||||
if (sig == 0) {
|
||||
cerr << get_fileline() << ": error: Unable to find variable "
|
||||
<< id2->path() << " in for-loop increment expressin." << endl;
|
||||
<< id2->path() << " in for-loop increment expression." << endl;
|
||||
des->errors += 1;
|
||||
return body;
|
||||
}
|
||||
@@ -3556,6 +3580,34 @@ void PSpecPath::elaborate(Design*des, NetScope*scope) const
|
||||
|
||||
}
|
||||
|
||||
static void elaborate_functions(Design*des, NetScope*scope,
|
||||
const map<perm_string,PFunction*>&funcs)
|
||||
{
|
||||
typedef map<perm_string,PFunction*>::const_iterator mfunc_it_t;
|
||||
for (mfunc_it_t cur = funcs.begin()
|
||||
; cur != funcs.end() ; cur ++) {
|
||||
|
||||
hname_t use_name ( (*cur).first );
|
||||
NetScope*fscope = scope->child(use_name);
|
||||
assert(fscope);
|
||||
(*cur).second->elaborate(des, fscope);
|
||||
}
|
||||
}
|
||||
|
||||
static void elaborate_tasks(Design*des, NetScope*scope,
|
||||
const map<perm_string,PTask*>&tasks)
|
||||
{
|
||||
typedef map<perm_string,PTask*>::const_iterator mtask_it_t;
|
||||
for (mtask_it_t cur = tasks.begin()
|
||||
; cur != tasks.end() ; cur ++) {
|
||||
|
||||
hname_t use_name ( (*cur).first );
|
||||
NetScope*tscope = scope->child(use_name);
|
||||
assert(tscope);
|
||||
(*cur).second->elaborate(des, tscope);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When a module is instantiated, it creates the scope then uses this
|
||||
* method to elaborate the contents of the module.
|
||||
@@ -3615,28 +3667,12 @@ bool Module::elaborate(Design*des, NetScope*scope) const
|
||||
}
|
||||
|
||||
// Elaborate functions.
|
||||
typedef map<perm_string,PFunction*>::const_iterator mfunc_it_t;
|
||||
for (mfunc_it_t cur = funcs_.begin()
|
||||
; cur != funcs_.end() ; cur ++) {
|
||||
|
||||
hname_t use_name ( (*cur).first );
|
||||
NetScope*fscope = scope->child(use_name);
|
||||
assert(fscope);
|
||||
(*cur).second->elaborate(des, fscope);
|
||||
}
|
||||
elaborate_functions(des, scope, funcs);
|
||||
|
||||
// Elaborate the task definitions. This is done before the
|
||||
// behaviors so that task calls may reference these, and after
|
||||
// the signals so that the tasks can reference them.
|
||||
typedef map<perm_string,PTask*>::const_iterator mtask_it_t;
|
||||
for (mtask_it_t cur = tasks_.begin()
|
||||
; cur != tasks_.end() ; cur ++) {
|
||||
|
||||
hname_t use_name ( (*cur).first );
|
||||
NetScope*tscope = scope->child(use_name);
|
||||
assert(tscope);
|
||||
(*cur).second->elaborate(des, tscope);
|
||||
}
|
||||
elaborate_tasks(des, scope, tasks);
|
||||
|
||||
// Get all the gates of the module and elaborate them by
|
||||
// connecting them to the signals. The gate may be simple or
|
||||
@@ -3681,8 +3717,8 @@ bool PGenerate::elaborate(Design*des, NetScope*container) const
|
||||
<< scope_path(container) << "." << endl;
|
||||
|
||||
typedef list<PGenerate*>::const_iterator generate_it_t;
|
||||
for (generate_it_t cur = generates.begin()
|
||||
; cur != generates.end() ; cur ++) {
|
||||
for (generate_it_t cur = generate_schemes.begin()
|
||||
; cur != generate_schemes.end() ; cur ++) {
|
||||
PGenerate*item = *cur;
|
||||
if (! item->scope_list_.empty()) {
|
||||
flag &= item->elaborate(des, container);
|
||||
@@ -3724,6 +3760,9 @@ bool PGenerate::elaborate(Design*des, NetScope*container) const
|
||||
|
||||
bool PGenerate::elaborate_(Design*des, NetScope*scope) const
|
||||
{
|
||||
elaborate_functions(des, scope, funcs);
|
||||
elaborate_tasks(des, scope, tasks);
|
||||
|
||||
typedef list<PGate*>::const_iterator gates_it_t;
|
||||
for (gates_it_t cur = gates.begin() ; cur != gates.end() ; cur ++ )
|
||||
(*cur)->elaborate(des, scope);
|
||||
@@ -3733,8 +3772,8 @@ bool PGenerate::elaborate_(Design*des, NetScope*scope) const
|
||||
(*cur)->elaborate(des, scope);
|
||||
|
||||
typedef list<PGenerate*>::const_iterator generate_it_t;
|
||||
for (generate_it_t cur = generates.begin()
|
||||
; cur != generates.end() ; cur ++ ) {
|
||||
for (generate_it_t cur = generate_schemes.begin()
|
||||
; cur != generate_schemes.end() ; cur ++ ) {
|
||||
(*cur)->elaborate(des, scope);
|
||||
}
|
||||
|
||||
@@ -3762,6 +3801,82 @@ struct root_elem {
|
||||
NetScope *scope;
|
||||
};
|
||||
|
||||
class elaborate_root_scope_t : public elaborator_work_item_t {
|
||||
public:
|
||||
elaborate_root_scope_t(Design*des, NetScope*scope, Module*rmod)
|
||||
: elaborator_work_item_t(des), scope_(scope), rmod_(rmod)
|
||||
{ }
|
||||
|
||||
~elaborate_root_scope_t() { }
|
||||
|
||||
virtual void elaborate_runrun()
|
||||
{
|
||||
Module::replace_t stub;
|
||||
if (! rmod_->elaborate_scope(des, scope_, stub))
|
||||
des->errors += 1;
|
||||
}
|
||||
|
||||
private:
|
||||
NetScope*scope_;
|
||||
Module*rmod_;
|
||||
};
|
||||
|
||||
class top_defparams : public elaborator_work_item_t {
|
||||
public:
|
||||
top_defparams(Design*des)
|
||||
: elaborator_work_item_t(des)
|
||||
{ }
|
||||
|
||||
~top_defparams() { }
|
||||
|
||||
virtual void elaborate_runrun()
|
||||
{
|
||||
// This method recurses through the scopes, looking for
|
||||
// defparam assignments to apply to the parameters in the
|
||||
// various scopes. This needs to be done after all the scopes
|
||||
// and basic parameters are taken care of because the defparam
|
||||
// can assign to a parameter declared *after* it.
|
||||
des->run_defparams();
|
||||
|
||||
// At this point, all parameter overrides are done. Scan the
|
||||
// scopes and evaluate the parameters all the way down to
|
||||
// constants.
|
||||
des->evaluate_parameters();
|
||||
}
|
||||
};
|
||||
|
||||
class later_defparams : public elaborator_work_item_t {
|
||||
public:
|
||||
later_defparams(Design*des)
|
||||
: elaborator_work_item_t(des)
|
||||
{ }
|
||||
|
||||
~later_defparams() { }
|
||||
|
||||
virtual void elaborate_runrun()
|
||||
{
|
||||
list<NetScope*>tmp_list;
|
||||
for (set<NetScope*>::iterator cur = des->defparams_later.begin()
|
||||
; cur != des->defparams_later.end() ; cur ++ )
|
||||
tmp_list.push_back(*cur);
|
||||
|
||||
des->defparams_later.clear();
|
||||
|
||||
while (! tmp_list.empty()) {
|
||||
NetScope*cur = tmp_list.front();
|
||||
tmp_list.pop_front();
|
||||
cur->run_defparams_later(des);
|
||||
}
|
||||
des->evaluate_parameters();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* This function is the root of all elaboration. The input is the list
|
||||
* of root module names. The function locates the Module definitions
|
||||
* for each root, does the whole elaboration sequence, and fills in
|
||||
* the resulting Design.
|
||||
*/
|
||||
Design* elaborate(list<perm_string>roots)
|
||||
{
|
||||
svector<root_elem*> root_elems(roots.size());
|
||||
@@ -3772,7 +3887,7 @@ Design* elaborate(list<perm_string>roots)
|
||||
// module and elaborate what I find.
|
||||
Design*des = new Design;
|
||||
|
||||
// Scan the root modules, and elaborate their scopes.
|
||||
// Scan the root modules by name, and elaborate their scopes.
|
||||
for (list<perm_string>::const_iterator root = roots.begin()
|
||||
; root != roots.end()
|
||||
; root++) {
|
||||
@@ -3791,47 +3906,74 @@ Design* elaborate(list<perm_string>roots)
|
||||
// Get the module definition for this root instance.
|
||||
Module *rmod = (*mod).second;
|
||||
|
||||
// Make the root scope.
|
||||
// Make the root scope. This makes a NetScoep object and
|
||||
// pushes it into the list of root scopes in the Design.
|
||||
NetScope*scope = des->make_root_scope(*root);
|
||||
|
||||
// Collect some basic properties of this scope from the
|
||||
// Module definition.
|
||||
scope->set_line(rmod);
|
||||
scope->time_unit(rmod->time_unit);
|
||||
scope->time_precision(rmod->time_precision);
|
||||
scope->default_nettype(rmod->default_nettype);
|
||||
des->set_precision(rmod->time_precision);
|
||||
|
||||
Module::replace_t stub;
|
||||
|
||||
// Recursively elaborate from this root scope down. This
|
||||
// does a lot of the grunt work of creating sub-scopes, etc.
|
||||
if (! rmod->elaborate_scope(des, scope, stub)) {
|
||||
delete des;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Save this scope, along with its defintion, in the
|
||||
// "root_elems" list for later passes.
|
||||
struct root_elem *r = new struct root_elem;
|
||||
r->mod = rmod;
|
||||
r->scope = scope;
|
||||
root_elems[i++] = r;
|
||||
|
||||
// Arrange for these scopes to be elaborated as root
|
||||
// scopes. Create an "elaborate_root_scope" object to
|
||||
// contain the work item, and append it to the scope
|
||||
// elaborations work list.
|
||||
elaborator_work_item_t*es = new elaborate_root_scope_t(des, scope, rmod);
|
||||
des->elaboration_work_list.push_back(es);
|
||||
}
|
||||
|
||||
// After the work items for the root scope elaboration, push a
|
||||
// work item to process the defparams.
|
||||
des->elaboration_work_list.push_back(new top_defparams(des));
|
||||
|
||||
// Run the work list of scope elaborations until the list is
|
||||
// empty. This list is initially populated above where the
|
||||
// initial root scopes are primed.
|
||||
while (! des->elaboration_work_list.empty()) {
|
||||
// Transfer the queue to a temporary queue.
|
||||
list<elaborator_work_item_t*> cur_queue;
|
||||
while (! des->elaboration_work_list.empty()) {
|
||||
cur_queue.push_back(des->elaboration_work_list.front());
|
||||
des->elaboration_work_list.pop_front();
|
||||
}
|
||||
|
||||
// Run from the temporary queue. If the temporary queue
|
||||
// items create new work queue items, they will show up
|
||||
// in the elaboration_work_list and then we get to run
|
||||
// through them in the next pass.
|
||||
while (! cur_queue.empty()) {
|
||||
elaborator_work_item_t*tmp = cur_queue.front();
|
||||
cur_queue.pop_front();
|
||||
tmp->elaborate_runrun();
|
||||
delete tmp;
|
||||
}
|
||||
|
||||
if (! des->elaboration_work_list.empty()) {
|
||||
des->elaboration_work_list.push_back(new later_defparams(des));
|
||||
}
|
||||
}
|
||||
|
||||
// Look for residual defparams (that point to a non-existent
|
||||
// scope) and clean them out.
|
||||
des->residual_defparams();
|
||||
|
||||
// Errors already? Probably missing root modules. Just give up
|
||||
// now and return nothing.
|
||||
if (des->errors > 0)
|
||||
return des;
|
||||
|
||||
// This method recurses through the scopes, looking for
|
||||
// defparam assignments to apply to the parameters in the
|
||||
// various scopes. This needs to be done after all the scopes
|
||||
// and basic parameters are taken care of because the defparam
|
||||
// can assign to a parameter declared *after* it.
|
||||
des->run_defparams();
|
||||
|
||||
|
||||
// At this point, all parameter overrides are done. Scan the
|
||||
// scopes and evaluate the parameters all the way down to
|
||||
// constants.
|
||||
des->evaluate_parameters();
|
||||
|
||||
// With the parameters evaluated down to constants, we have
|
||||
// what we need to elaborate signals and memories. This pass
|
||||
// creates all the NetNet and NetMemory objects for declared
|
||||
|
||||
@@ -72,6 +72,16 @@ bool NetCaseCmp::emit_node(struct target_t*tgt) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NetCastInt::emit_node(struct target_t*tgt) const
|
||||
{
|
||||
return tgt->lpm_cast_int(this);
|
||||
}
|
||||
|
||||
bool NetCastReal::emit_node(struct target_t*tgt) const
|
||||
{
|
||||
return tgt->lpm_cast_real(this);
|
||||
}
|
||||
|
||||
bool NetCLShift::emit_node(struct target_t*tgt) const
|
||||
{
|
||||
tgt->lpm_clshift(this);
|
||||
@@ -362,7 +372,7 @@ void NetScope::emit_scope(struct target_t*tgt) const
|
||||
/* Run the signals again, but this time to connect the
|
||||
delay paths. This is done as a second pass because
|
||||
the paths reference other signals that may be later
|
||||
in the list. We can do it here becase delay paths are
|
||||
in the list. We can do it here because delay paths are
|
||||
always connected within the scope. */
|
||||
cur = signals_->sig_next_;
|
||||
do {
|
||||
|
||||
@@ -154,7 +154,7 @@
|
||||
*
|
||||
* The POST_MAP compiler directive causes the GSR manipulations
|
||||
* included in the test bench to be compiled in, to simulate the chip
|
||||
* startup. Other then that, the test bench runs the post-map design
|
||||
* startup. Other than that, the test bench runs the post-map design
|
||||
* the same way the pre-synthesis design works.
|
||||
*
|
||||
* Run this design with the command:
|
||||
|
||||
+1
-1
@@ -459,7 +459,7 @@ NetNet* NetEBDiv::synthesize(Design*des)
|
||||
|
||||
default: {
|
||||
cerr << get_fileline() << ": internal error: "
|
||||
<< "NetEBDiv has unexpeced op() code: "
|
||||
<< "NetEBDiv has unexpected op() code: "
|
||||
<< op() << endl;
|
||||
des->errors += 1;
|
||||
|
||||
|
||||
@@ -191,6 +191,8 @@ ivl_path_source
|
||||
|
||||
ivl_process_attr_cnt
|
||||
ivl_process_attr_val
|
||||
ivl_process_file
|
||||
ivl_process_lineno
|
||||
ivl_process_scope
|
||||
ivl_process_stmt
|
||||
ivl_process_type
|
||||
|
||||
@@ -254,6 +254,8 @@ typedef enum ivl_lpm_type_e {
|
||||
IVL_LPM_ABS = 32,
|
||||
IVL_LPM_ADD = 0,
|
||||
IVL_LPM_ARRAY = 30,
|
||||
IVL_LPM_CAST_INT = 34,
|
||||
IVL_LPM_CAST_REAL = 33,
|
||||
IVL_LPM_CONCAT = 16,
|
||||
IVL_LPM_CMP_EEQ= 18, /* Case EQ (===) */
|
||||
IVL_LPM_CMP_EQ = 10,
|
||||
@@ -1693,6 +1695,9 @@ extern ivl_statement_t ivl_process_stmt(ivl_process_t net);
|
||||
extern unsigned ivl_process_attr_cnt(ivl_process_t net);
|
||||
extern ivl_attribute_t ivl_process_attr_val(ivl_process_t net, unsigned idx);
|
||||
|
||||
extern const char* ivl_process_file(ivl_process_t net);
|
||||
extern unsigned ivl_process_lineno(ivl_process_t net);
|
||||
|
||||
/*
|
||||
* These functions manage statements of various type. This includes
|
||||
* all the different kinds of statements (as enumerated in
|
||||
|
||||
+10
-6
@@ -115,7 +115,7 @@ static void ifdef_enter(void)
|
||||
struct ifdef_stack_t*cur;
|
||||
|
||||
cur = (struct ifdef_stack_t*) calloc(1, sizeof(struct ifdef_stack_t));
|
||||
cur->path = strdup(istack->path);
|
||||
if (istack->path) cur->path = strdup(istack->path);
|
||||
cur->lineno = istack->lineno;
|
||||
cur->next = ifdef_stack;
|
||||
|
||||
@@ -131,8 +131,10 @@ static void ifdef_leave(void)
|
||||
cur = ifdef_stack;
|
||||
ifdef_stack = cur->next;
|
||||
|
||||
if (strcmp(istack->path,cur->path) != 0)
|
||||
{
|
||||
/* If either path is from a non-file context e.g.(macro expansion)
|
||||
* we assume that the non-file part is from this file. */
|
||||
if (istack->path != NULL && cur->path != NULL &&
|
||||
strcmp(istack->path,cur->path) != 0) {
|
||||
fprintf
|
||||
(
|
||||
stderr,
|
||||
@@ -856,6 +858,7 @@ static int define_continue_flag = 0;
|
||||
static char *find_arg(char*ptr, char*head, char*arg)
|
||||
{
|
||||
char *cp = ptr;
|
||||
size_t len = strlen(arg);
|
||||
|
||||
while (1) {
|
||||
/* Look for a candidate match, just return if none is found. */
|
||||
@@ -866,7 +869,8 @@ static char *find_arg(char*ptr, char*head, char*arg)
|
||||
* match is not in the middle of another identifier.
|
||||
*/
|
||||
if (cp != head &&
|
||||
(isalnum(*(cp-1)) || *(cp-1) == '_' || *(cp-1) == '$')) {
|
||||
(isalnum(*(cp-1)) || *(cp-1) == '_' || *(cp-1) == '$' ||
|
||||
isalnum(*(cp+len)) || *(cp+len) == '_' || *(cp+len) == '$')) {
|
||||
cp++;
|
||||
continue;
|
||||
}
|
||||
@@ -941,7 +945,7 @@ static void do_define()
|
||||
}
|
||||
|
||||
/* Detect the continuation sequence. If I find it, remove it
|
||||
* and the white space that preceeds it, then replace all that
|
||||
* and the white space that precedes it, then replace all that
|
||||
* with a single newline.
|
||||
*/
|
||||
if ((cp > yytext) && (cp[-1] == '\\'))
|
||||
@@ -1468,7 +1472,7 @@ static void do_include()
|
||||
fprintf(depend_file, "%s\n", standby->path);
|
||||
|
||||
if (line_direct_flag)
|
||||
fprintf(yyout, "\n`line %u \"%s\" 1\n", istack->lineno+1, standby->path);
|
||||
fprintf(yyout, "\n`line 1 \"%s\" 1\n", standby->path);
|
||||
|
||||
standby->next = istack;
|
||||
standby->stringify_flag = 0;
|
||||
|
||||
@@ -843,7 +843,7 @@ static verinum*make_unsized_dec(const char*ptr)
|
||||
if (ptr[0] == '\'') {
|
||||
/* The number has decorations of the form 'sd<digits>,
|
||||
possibly with space between the d and the <digits>.
|
||||
Also, the 's' is optional, and markes the number as
|
||||
Also, the 's' is optional, and marks the number as
|
||||
signed. */
|
||||
ptr += 1;
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ assign, GN_KEYWORDS_1364_1995, K_assign
|
||||
atan, GN_KEYWORDS_VAMS_2_3, K_atan
|
||||
atan2, GN_KEYWORDS_VAMS_2_3, K_atan2
|
||||
atanh, GN_KEYWORDS_VAMS_2_3, K_atanh
|
||||
automatic, GN_KEYWORDS_1364_2001, K_automatic
|
||||
begin, GN_KEYWORDS_1364_1995, K_begin
|
||||
bool, GN_KEYWORDS_ICARUS, K_bool
|
||||
buf, GN_KEYWORDS_1364_1995, K_buf
|
||||
|
||||
@@ -54,6 +54,7 @@ const char NOTICE[] =
|
||||
#endif
|
||||
# include "pform.h"
|
||||
# include "parse_api.h"
|
||||
# include "PGenerate.h"
|
||||
# include "netlist.h"
|
||||
# include "target.h"
|
||||
# include "compiler.h"
|
||||
@@ -272,6 +273,9 @@ static void parm_to_flagmap(const string&flag)
|
||||
flags[key] = value;
|
||||
}
|
||||
|
||||
static void find_module_mention(map<perm_string,bool>&check_map, Module*m);
|
||||
static void find_module_mention(map<perm_string,bool>&check_map, PGenerate*s);
|
||||
|
||||
/*
|
||||
* Read the contents of a config file. This file is a temporary
|
||||
* configuration file made by the compiler driver to carry the bulky
|
||||
@@ -729,15 +733,7 @@ int main(int argc, char*argv[])
|
||||
for (mod = pform_modules.begin()
|
||||
; mod != pform_modules.end()
|
||||
; mod++) {
|
||||
list<PGate*> gates = (*mod).second->get_gates();
|
||||
list<PGate*>::const_iterator gate;
|
||||
for (gate = gates.begin(); gate != gates.end(); gate++) {
|
||||
PGModule *mod = dynamic_cast<PGModule*>(*gate);
|
||||
if (mod) {
|
||||
// Note that this module has been instantiated
|
||||
mentioned_p[mod->get_type()] = true;
|
||||
}
|
||||
}
|
||||
find_module_mention(mentioned_p, mod->second);
|
||||
}
|
||||
|
||||
for (mod = pform_modules.begin()
|
||||
@@ -907,3 +903,40 @@ int main(int argc, char*argv[])
|
||||
|
||||
return des? des->errors : 1;
|
||||
}
|
||||
|
||||
static void find_module_mention(map<perm_string,bool>&check_map, Module*mod)
|
||||
{
|
||||
list<PGate*> gates = mod->get_gates();
|
||||
list<PGate*>::const_iterator gate;
|
||||
for (gate = gates.begin(); gate != gates.end(); gate++) {
|
||||
PGModule*tmp = dynamic_cast<PGModule*>(*gate);
|
||||
if (tmp) {
|
||||
// Note that this module has been instantiated
|
||||
check_map[tmp->get_type()] = true;
|
||||
}
|
||||
}
|
||||
|
||||
list<PGenerate*>::const_iterator cur;
|
||||
for (cur = mod->generate_schemes.begin()
|
||||
; cur != mod->generate_schemes.end() ; cur ++) {
|
||||
find_module_mention(check_map, *cur);
|
||||
}
|
||||
}
|
||||
|
||||
static void find_module_mention(map<perm_string,bool>&check_map, PGenerate*schm)
|
||||
{
|
||||
list<PGate*>::const_iterator gate;
|
||||
for (gate = schm->gates.begin(); gate != schm->gates.end(); gate++) {
|
||||
PGModule*tmp = dynamic_cast<PGModule*>(*gate);
|
||||
if (tmp) {
|
||||
// Note that this module has been instantiated
|
||||
check_map[tmp->get_type()] = true;
|
||||
}
|
||||
}
|
||||
|
||||
list<PGenerate*>::const_iterator cur;
|
||||
for (cur = schm->generate_schemes.begin()
|
||||
; cur != schm->generate_schemes.end() ; cur ++) {
|
||||
find_module_mention(check_map, *cur);
|
||||
}
|
||||
}
|
||||
|
||||
+118
-9
@@ -20,6 +20,7 @@
|
||||
# include "config.h"
|
||||
|
||||
# include <iostream>
|
||||
# include <set>
|
||||
# include <cstdlib>
|
||||
|
||||
/*
|
||||
@@ -208,10 +209,12 @@ void NetScope::run_defparams(Design*des)
|
||||
}
|
||||
}
|
||||
|
||||
map<pform_name_t,NetExpr*>::const_iterator pp;
|
||||
for (pp = defparams.begin() ; pp != defparams.end() ; pp ++ ) {
|
||||
NetExpr*val = (*pp).second;
|
||||
pform_name_t path = (*pp).first;
|
||||
while (! defparams.empty()) {
|
||||
pair<pform_name_t,NetExpr*> pp = defparams.front();
|
||||
defparams.pop_front();
|
||||
|
||||
pform_name_t path = pp.first;
|
||||
NetExpr*val = pp.second;
|
||||
|
||||
perm_string perm_name = peek_tail_name(path);
|
||||
path.pop_back();
|
||||
@@ -222,11 +225,20 @@ void NetScope::run_defparams(Design*des)
|
||||
is the current scope. */
|
||||
NetScope*targ_scope = des->find_scope(this, eval_path);
|
||||
if (targ_scope == 0) {
|
||||
cerr << val->get_fileline() << ": warning: scope of " <<
|
||||
path << "." << perm_name << " not found." << endl;
|
||||
|
||||
// Push the defparam onto a list for retry
|
||||
// later. It is possible for the scope lookup to
|
||||
// fail if the scope being defparam'd into is
|
||||
// generated by an index array for generate.
|
||||
eval_path.push_back(hname_t(perm_name));
|
||||
defparams_later.push_back(make_pair(eval_path,val));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Once placed in the parameter map, the expression may
|
||||
// be deleted when evaluated, so give it a copy of this
|
||||
// expression, not the original.
|
||||
val = val->dup_expr();
|
||||
bool flag = targ_scope->replace_parameter(perm_name, val);
|
||||
if (! flag) {
|
||||
cerr << val->get_fileline() << ": warning: parameter "
|
||||
@@ -235,6 +247,66 @@ void NetScope::run_defparams(Design*des)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If some of the defparams didn't find a scope in the name,
|
||||
// then try again later. It may be that the target scope is
|
||||
// created later by generate scheme or instance array.
|
||||
if (! defparams_later.empty())
|
||||
des->defparams_later.insert(this);
|
||||
}
|
||||
|
||||
void NetScope::run_defparams_later(Design*des)
|
||||
{
|
||||
set<NetScope*> target_scopes;
|
||||
list<pair<list<hname_t>,NetExpr*> > defparams_even_later;
|
||||
|
||||
while (! defparams_later.empty()) {
|
||||
pair<list<hname_t>,NetExpr*> cur = defparams_later.front();
|
||||
defparams_later.pop_front();
|
||||
|
||||
list<hname_t>eval_path = cur.first;
|
||||
perm_string name = eval_path.back().peek_name();
|
||||
eval_path.pop_back();
|
||||
|
||||
NetExpr*val = cur.second;
|
||||
|
||||
NetScope*targ_scope = des->find_scope(this, eval_path);
|
||||
if (targ_scope == 0) {
|
||||
// If a scope in the target path is not found,
|
||||
// then push this defparam for handling even
|
||||
// later. Maybe a later generate scheme or
|
||||
// instance array will create the scope.
|
||||
defparams_even_later.push_back(cur);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Once placed in the parameter map, the expression may
|
||||
// be deleted when evaluated, so give it a copy of this
|
||||
// expression, not the original.
|
||||
val = val->dup_expr();
|
||||
bool flag = targ_scope->replace_parameter(name, val);
|
||||
if (! flag) {
|
||||
cerr << val->get_fileline() << ": warning: parameter "
|
||||
<< name << " not found in "
|
||||
<< scope_path(targ_scope) << "." << endl;
|
||||
}
|
||||
|
||||
// We'll need to re-evaluate parameters in this scope
|
||||
target_scopes.insert(targ_scope);
|
||||
}
|
||||
|
||||
// All the scopes that this defparam set touched should have
|
||||
// their parameters re-evaluated.
|
||||
for (set<NetScope*>::iterator cur = target_scopes.begin()
|
||||
; cur != target_scopes.end() ; cur ++ )
|
||||
(*cur)->evaluate_parameters(des);
|
||||
|
||||
// If there are some scopes that still have missing scopes,
|
||||
// then sav them back into the defparams_later list for a
|
||||
// later pass.
|
||||
defparams_later = defparams_even_later;
|
||||
if (! defparams_later.empty())
|
||||
des->defparams_later.insert(this);
|
||||
}
|
||||
|
||||
void Design::evaluate_parameters()
|
||||
@@ -285,11 +357,11 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur)
|
||||
|
||||
/* Evaluate the parameter expression, if necessary. */
|
||||
NetExpr*expr = (*cur).second.expr;
|
||||
assert(expr);
|
||||
if (expr == NULL) return; // This is an invalid parameter so return.
|
||||
|
||||
eval_expr(expr);
|
||||
|
||||
/* The eval_expr may delete any replace the expr pointer, so the
|
||||
/* The eval_expr may delete and replace the expr pointer, so the
|
||||
second.expr value cannot be relied on. Might as well replace
|
||||
it now with the expression that we evaluated. */
|
||||
(*cur).second.expr = expr;
|
||||
@@ -335,6 +407,14 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur)
|
||||
unsigned long wid = (msb >= lsb)? msb - lsb : lsb - msb;
|
||||
wid += 1;
|
||||
|
||||
/* If we have a real value convert it to an integer. */
|
||||
if(NetECReal*tmp = dynamic_cast<NetECReal*>(expr)) {
|
||||
verinum nval(tmp->value().as_long64());
|
||||
expr = new NetEConst(nval);
|
||||
expr->set_line(*((*cur).second.expr));
|
||||
(*cur).second.expr = expr;
|
||||
}
|
||||
|
||||
NetEConst*val = dynamic_cast<NetEConst*>(expr);
|
||||
assert(val);
|
||||
|
||||
@@ -348,7 +428,7 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur)
|
||||
tmp.has_sign ( (*cur).second.signed_flag );
|
||||
delete val;
|
||||
val = new NetEConst(tmp);
|
||||
expr = val;
|
||||
(*cur).second.expr = expr = val;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,6 +586,9 @@ void NetScope::evaluate_parameters(Design*des)
|
||||
cur = cur->sib_;
|
||||
}
|
||||
|
||||
if (debug_scopes)
|
||||
cerr << ":0" << ": debug: "
|
||||
<< "Evaluate parameters in " << scope_path(this) << endl;
|
||||
|
||||
// Evaluate the parameter values. The parameter expressions
|
||||
// have already been elaborated and replaced by the scope
|
||||
@@ -540,6 +623,32 @@ void NetScope::evaluate_parameters(Design*des)
|
||||
|
||||
}
|
||||
|
||||
void Design::residual_defparams()
|
||||
{
|
||||
for (list<NetScope*>::const_iterator scope = root_scopes_.begin();
|
||||
scope != root_scopes_.end(); scope++)
|
||||
(*scope)->residual_defparams(this);
|
||||
}
|
||||
|
||||
void NetScope::residual_defparams(Design*des)
|
||||
{
|
||||
// Clean out the list of defparams that never managed to match
|
||||
// a scope. Print a warning for each.
|
||||
while (! defparams_later.empty()) {
|
||||
pair<list<hname_t>,NetExpr*> cur = defparams_later.front();
|
||||
defparams_later.pop_front();
|
||||
|
||||
cerr << cur.second->get_fileline() << ": warning: "
|
||||
<< "Scope of " << cur.first << " not found." << endl;
|
||||
}
|
||||
|
||||
NetScope*cur = sub_;
|
||||
while (cur) {
|
||||
cur->residual_defparams(des);
|
||||
cur = cur->sib_;
|
||||
}
|
||||
}
|
||||
|
||||
const char* Design::get_flag(const string&key) const
|
||||
{
|
||||
map<string,const char*>::const_iterator tmp = flags_.find(key);
|
||||
|
||||
+35
@@ -247,6 +247,41 @@ verinum::V Nexus::get_init() const
|
||||
return verinum::Vz;
|
||||
}
|
||||
|
||||
bool Nexus::drivers_present() const
|
||||
{
|
||||
assert(list_);
|
||||
for (Link*cur = list_ ; cur ; cur = cur->next_) {
|
||||
if (cur->get_dir() == Link::OUTPUT)
|
||||
return true;
|
||||
|
||||
if (cur->get_dir() == Link::INPUT)
|
||||
continue;
|
||||
|
||||
// Must be PASSIVE, so if it is some kind of net, see if
|
||||
// it is the sort that might drive the nexus.
|
||||
const NetObj*obj;
|
||||
unsigned pin;
|
||||
cur->cur_link(obj, pin);
|
||||
if (const NetNet*net = dynamic_cast<const NetNet*>(obj))
|
||||
switch (net->type()) {
|
||||
case NetNet::SUPPLY0:
|
||||
case NetNet::SUPPLY1:
|
||||
case NetNet::TRI0:
|
||||
case NetNet::TRI1:
|
||||
case NetNet::WAND:
|
||||
case NetNet::WOR:
|
||||
case NetNet::TRIAND:
|
||||
case NetNet::TRIOR:
|
||||
case NetNet::REG:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Nexus::drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay)
|
||||
{
|
||||
for (Link*cur = list_ ; cur ; cur = cur->next_) {
|
||||
|
||||
+10
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2007 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 2002-2008 Stephen Williams (steve@icarus.com)
|
||||
*
|
||||
* This source code is free software; you can redistribute it
|
||||
* and/or modify it in source code form under the terms of the GNU
|
||||
@@ -53,8 +53,13 @@ NexusSet* NetEBinary::nex_input(bool rem_out)
|
||||
|
||||
NexusSet* NetEConcat::nex_input(bool rem_out)
|
||||
{
|
||||
if (parms_[0] == NULL) return NULL;
|
||||
NexusSet*result = parms_[0]->nex_input(rem_out);
|
||||
for (unsigned idx = 1 ; idx < parms_.count() ; idx += 1) {
|
||||
if (parms_[idx] == NULL) {
|
||||
delete result;
|
||||
return NULL;
|
||||
}
|
||||
NexusSet*tmp = parms_[idx]->nex_input(rem_out);
|
||||
result->add(*tmp);
|
||||
delete tmp;
|
||||
@@ -98,6 +103,10 @@ NexusSet* NetESelect::nex_input(bool rem_out)
|
||||
{
|
||||
NexusSet*result = base_? base_->nex_input(rem_out) : new NexusSet();
|
||||
NexusSet*tmp = expr_->nex_input(rem_out);
|
||||
if (tmp == NULL) {
|
||||
delete result;
|
||||
return NULL;
|
||||
}
|
||||
result->add(*tmp);
|
||||
delete tmp;
|
||||
return result;
|
||||
@@ -381,4 +390,3 @@ NexusSet* NetWhile::nex_input(bool rem_out)
|
||||
delete tmp;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -166,7 +166,7 @@ bool NetScope::replace_parameter(perm_string key, NetExpr*expr)
|
||||
|
||||
/*
|
||||
* This is not really complete (msb, lsb, sign). It is currently only
|
||||
* used to add a genver to the local parameter list.
|
||||
* used to add a genvar to the local parameter list.
|
||||
*/
|
||||
NetExpr* NetScope::set_localparam(perm_string key, NetExpr*expr,
|
||||
const LineInfo&file_line)
|
||||
@@ -275,7 +275,7 @@ const NetFuncDef* NetScope::func_def() const
|
||||
void NetScope::set_module_name(perm_string n)
|
||||
{
|
||||
assert(type_ == MODULE);
|
||||
module_name_ = n; /* NOTE: n mus have been permallocated. */
|
||||
module_name_ = n; /* NOTE: n must have been permallocated. */
|
||||
}
|
||||
|
||||
perm_string NetScope::module_name() const
|
||||
|
||||
+3
-3
@@ -114,7 +114,7 @@ void join_island(NetObj*obj)
|
||||
|
||||
// If that is an uncommitted branch, then save
|
||||
// it. When I finally choose an island for self,
|
||||
// these branches will be scanned so tha they join
|
||||
// these branches will be scanned so that they join
|
||||
// this island as well.
|
||||
if (tmp_branch->island == 0) {
|
||||
uncommitted_neighbors.push_back(tmp);
|
||||
@@ -128,7 +128,7 @@ void join_island(NetObj*obj)
|
||||
if (branch->island == 0) {
|
||||
if (debug_elaborate)
|
||||
cerr << obj->get_fileline() << ": debug: "
|
||||
<< "Join brach to existing island." << endl;
|
||||
<< "Join branch to existing island." << endl;
|
||||
branch->island = tmp_branch->island;
|
||||
|
||||
} else if (branch->island != tmp_branch->island) {
|
||||
@@ -139,7 +139,7 @@ void join_island(NetObj*obj)
|
||||
}
|
||||
}
|
||||
|
||||
// If after all that we did not find an idland to join, then
|
||||
// If after all that we did not find an island to join, then
|
||||
// start the island not and join it.
|
||||
if (branch->island == 0) {
|
||||
branch->island = new ivl_island_s;
|
||||
|
||||
+23
@@ -849,6 +849,24 @@ const NetScope* NetProcTop::scope() const
|
||||
return scope_;
|
||||
}
|
||||
|
||||
NetCastInt::NetCastInt(NetScope*scope, perm_string n, unsigned width)
|
||||
: NetNode(scope, n, 2), width_(width)
|
||||
{
|
||||
pin(0).set_dir(Link::OUTPUT);
|
||||
pin(0).set_name(perm_string::literal("O"), 0);
|
||||
pin(1).set_dir(Link::INPUT);
|
||||
pin(1).set_name(perm_string::literal("I"), 0);
|
||||
}
|
||||
|
||||
NetCastReal::NetCastReal(NetScope*scope, perm_string n, bool signed_flag)
|
||||
: NetNode(scope, n, 2), signed_flag_(signed_flag)
|
||||
{
|
||||
pin(0).set_dir(Link::OUTPUT);
|
||||
pin(0).set_name(perm_string::literal("O"), 0);
|
||||
pin(1).set_dir(Link::INPUT);
|
||||
pin(1).set_name(perm_string::literal("I"), 0);
|
||||
}
|
||||
|
||||
NetConcat::NetConcat(NetScope*scope, perm_string n, unsigned wid, unsigned cnt)
|
||||
: NetNode(scope, n, cnt+1), width_(wid)
|
||||
{
|
||||
@@ -1496,6 +1514,11 @@ NetLiteral::~NetLiteral()
|
||||
{
|
||||
}
|
||||
|
||||
ivl_variable_type_t NetLiteral::data_type() const
|
||||
{
|
||||
return IVL_VT_REAL;
|
||||
}
|
||||
|
||||
const verireal& NetLiteral::value_real() const
|
||||
{
|
||||
return real_;
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
# include <map>
|
||||
# include <list>
|
||||
# include <vector>
|
||||
# include <set>
|
||||
# include <utility>
|
||||
# include "ivl_target.h"
|
||||
# include "pform_types.h"
|
||||
# include "config.h"
|
||||
@@ -288,6 +290,10 @@ class Nexus {
|
||||
|
||||
NetNet* pick_any_net();
|
||||
|
||||
/* This method returns true if there are any drivers
|
||||
(including variables) attached to this nexus. */
|
||||
bool drivers_present() const;
|
||||
|
||||
/* This method returns true if all the possible drivers of
|
||||
this nexus are constant. It will also return true if there
|
||||
are no drivers at all. */
|
||||
@@ -718,8 +724,13 @@ class NetScope : public Attrib {
|
||||
const hname_t& fullname() const { return name_; }
|
||||
|
||||
void run_defparams(class Design*);
|
||||
void run_defparams_later(class Design*);
|
||||
|
||||
void evaluate_parameters(class Design*);
|
||||
|
||||
// Look for defparams that never matched, and print warnings.
|
||||
void residual_defparams(class Design*);
|
||||
|
||||
/* This method generates a non-hierarchical name that is
|
||||
guaranteed to be unique within this scope. */
|
||||
perm_string local_symbol();
|
||||
@@ -737,7 +748,8 @@ class NetScope : public Attrib {
|
||||
assignments from the scope pass to the parameter evaluation
|
||||
step. After that, it is not used. */
|
||||
|
||||
map<pform_name_t,NetExpr*>defparams;
|
||||
list<pair<pform_name_t,NetExpr*> > defparams;
|
||||
list<pair<list<hname_t>,NetExpr*> > defparams_later;
|
||||
|
||||
public:
|
||||
struct range_t {
|
||||
@@ -827,7 +839,7 @@ class NetScope : public Attrib {
|
||||
|
||||
/*
|
||||
* This class implements the LPM_ABS component. The node has a single
|
||||
* input, a signe expression, that it converts to the absolute
|
||||
* input, a signed expression, that it converts to the absolute
|
||||
* value. The gate is simple: pin(0) is the output and pin(1) is the input.
|
||||
*/
|
||||
class NetAbs : public NetNode {
|
||||
@@ -916,6 +928,44 @@ class NetArrayDq : public NetNode {
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* Convert an IVL_VT_REAL input to a logical value with the
|
||||
* given width. The input is pin(1) and the output is pin(0).
|
||||
*/
|
||||
class NetCastInt : public NetNode {
|
||||
|
||||
public:
|
||||
NetCastInt(NetScope*s, perm_string n, unsigned width);
|
||||
|
||||
unsigned width() const { return width_; }
|
||||
|
||||
virtual void dump_node(ostream&, unsigned ind) const;
|
||||
virtual bool emit_node(struct target_t*) const;
|
||||
|
||||
private:
|
||||
unsigned width_;
|
||||
};
|
||||
|
||||
/*
|
||||
* Convert an input to IVL_VT_REAL. The input is pin(1), which can be
|
||||
* any vector type (VT_BOOL or VT_LOGIC) and the output is pin(0),
|
||||
* which is IVL_VT_REAL. The conversion interprets the input as an
|
||||
* unsigned value unless the signed_flag is true.
|
||||
*/
|
||||
class NetCastReal : public NetNode {
|
||||
|
||||
public:
|
||||
NetCastReal(NetScope*s, perm_string n, bool signed_flag);
|
||||
|
||||
bool signed_flag() const { return signed_flag_; }
|
||||
|
||||
virtual void dump_node(ostream&, unsigned ind) const;
|
||||
virtual bool emit_node(struct target_t*) const;
|
||||
|
||||
private:
|
||||
bool signed_flag_;
|
||||
};
|
||||
|
||||
/*
|
||||
* This type represents the LPM_CLSHIFT device.
|
||||
*/
|
||||
@@ -1379,7 +1429,7 @@ class NetSysFunc : public NetNode {
|
||||
class NetTran : public NetNode, public IslandBranch {
|
||||
|
||||
public:
|
||||
// Tran devices other then TRAN_VP
|
||||
// Tran devices other than TRAN_VP
|
||||
NetTran(NetScope*scope, perm_string n, ivl_switch_type_t type);
|
||||
// Create a TRAN_VP
|
||||
NetTran(NetScope*scope, perm_string n, unsigned wid,
|
||||
@@ -3560,6 +3610,18 @@ class NetESignal : public NetExpr {
|
||||
NetExpr*word_;
|
||||
};
|
||||
|
||||
/*
|
||||
* The Design object keeps a list of work items for processing
|
||||
* elaboration. This is the type of those work items.
|
||||
*/
|
||||
struct elaborator_work_item_t {
|
||||
explicit elaborator_work_item_t(Design*d)
|
||||
: des(d) { }
|
||||
virtual ~elaborator_work_item_t() { }
|
||||
virtual void elaborate_runrun() =0;
|
||||
protected:
|
||||
Design*des;
|
||||
};
|
||||
|
||||
/*
|
||||
* This class contains an entire design. It includes processes and a
|
||||
@@ -3609,10 +3671,23 @@ class Design {
|
||||
NetScope* find_scope(NetScope*, const std::list<hname_t>&path,
|
||||
NetScope::TYPE type = NetScope::MODULE) const;
|
||||
|
||||
/* These members help manage elaboration of scopes. When we
|
||||
get to a point in scope elaboration where we want to put
|
||||
off a scope elaboration, an object of scope_elaboration_t
|
||||
is pushed onto the scope_elaborations list. The scope
|
||||
elaborator will go through this list elaborating scopes
|
||||
until the list is empty. */
|
||||
list<elaborator_work_item_t*>elaboration_work_list;
|
||||
void run_elaboration_work(void);
|
||||
|
||||
set<NetScope*> defparams_later;
|
||||
|
||||
// PARAMETERS
|
||||
|
||||
void run_defparams();
|
||||
void evaluate_parameters();
|
||||
// Look for defparams that never matched, and print warnings.
|
||||
void residual_defparams();
|
||||
|
||||
/* This method locates a signal, starting at a given
|
||||
scope. The name parameter may be partially hierarchical, so
|
||||
|
||||
+92
-24
@@ -76,6 +76,46 @@ NetNet* add_to_net(Design*des, NetNet*sig, long val)
|
||||
#endif
|
||||
}
|
||||
|
||||
NetNet* cast_to_int(Design*des, NetScope*scope, NetNet*src, unsigned wid)
|
||||
{
|
||||
if (src->data_type() != IVL_VT_REAL)
|
||||
return src;
|
||||
|
||||
NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, wid);
|
||||
tmp->data_type(IVL_VT_LOGIC);
|
||||
tmp->set_line(*src);
|
||||
tmp->local_flag(true);
|
||||
|
||||
NetCastInt*cast = new NetCastInt(scope, scope->local_symbol(), wid);
|
||||
cast->set_line(*src);
|
||||
des->add_node(cast);
|
||||
|
||||
connect(cast->pin(0), tmp->pin(0));
|
||||
connect(cast->pin(1), src->pin(0));
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
NetNet* cast_to_real(Design*des, NetScope*scope, NetNet*src)
|
||||
{
|
||||
if (src->data_type() == IVL_VT_REAL)
|
||||
return src;
|
||||
|
||||
NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE);
|
||||
tmp->data_type(IVL_VT_REAL);
|
||||
tmp->set_line(*src);
|
||||
tmp->local_flag(true);
|
||||
|
||||
NetCastReal*cast = new NetCastReal(scope, scope->local_symbol(), src->get_signed());
|
||||
cast->set_line(*src);
|
||||
des->add_node(cast);
|
||||
|
||||
connect(cast->pin(0), tmp->pin(0));
|
||||
connect(cast->pin(1), src->pin(0));
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a signed constant to an existing expression. Generate a new
|
||||
* NetEBAdd node that has the input expression and an expression made
|
||||
@@ -204,6 +244,57 @@ bool eval_as_double(double&value, NetExpr*expr)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* At the parser level, a name component it a name with a collection
|
||||
* of expressions. For example foo[N] is the name "foo" and the index
|
||||
* expression "N". This function takes as input the name component and
|
||||
* returns the path component name. It will evaulate the index
|
||||
* expression if it is present.
|
||||
*/
|
||||
hname_t eval_path_component(Design*des, NetScope*scope,
|
||||
const name_component_t&comp)
|
||||
{
|
||||
// No index exression, so the path component is an undecorated
|
||||
// name, for example "foo".
|
||||
if (comp.index.empty())
|
||||
return hname_t(comp.name);
|
||||
|
||||
// The parser will assure that path components will have only
|
||||
// one index. For example, foo[N] is one index, foo[n][m] is two.
|
||||
assert(comp.index.size() == 1);
|
||||
|
||||
const index_component_t&index = comp.index.front();
|
||||
|
||||
// The parser will assure that path components will have only
|
||||
// bit select index expressions. For example, "foo[n]" is OK,
|
||||
// but "foo[n:m]" is not.
|
||||
assert(index.sel == index_component_t::SEL_BIT);
|
||||
|
||||
// Evaluate the bit select to get a number.
|
||||
NetExpr*tmp = elab_and_eval(des, scope, index.msb, -1);
|
||||
ivl_assert(*index.msb, tmp);
|
||||
|
||||
// Now we should have a constant value for the bit select
|
||||
// expression, and we can use it to make the final hname_t
|
||||
// value, for example "foo[5]".
|
||||
if (NetEConst*ctmp = dynamic_cast<NetEConst*>(tmp)) {
|
||||
hname_t res(comp.name, ctmp->value().as_long());
|
||||
delete ctmp;
|
||||
return res;
|
||||
}
|
||||
|
||||
// Darn, the expression doesn't evaluate to a constant. That's
|
||||
// and error to be reported. And make up a fake index value to
|
||||
// return to the caller.
|
||||
cerr << index.msb->get_fileline() << ": error: "
|
||||
<< "Scope index expression is not constant: "
|
||||
<< *index.msb << endl;
|
||||
des->errors += 1;
|
||||
|
||||
delete tmp;
|
||||
return hname_t (comp.name, 0);
|
||||
}
|
||||
|
||||
std::list<hname_t> eval_scope_path(Design*des, NetScope*scope,
|
||||
const pform_name_t&path)
|
||||
{
|
||||
@@ -213,30 +304,7 @@ std::list<hname_t> eval_scope_path(Design*des, NetScope*scope,
|
||||
|
||||
for (pform_path_it cur = path.begin() ; cur != path.end(); cur++) {
|
||||
const name_component_t&comp = *cur;
|
||||
if (comp.index.empty()) {
|
||||
res.push_back(hname_t(comp.name));
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(comp.index.size() == 1);
|
||||
const index_component_t&index = comp.index.front();
|
||||
assert(index.sel == index_component_t::SEL_BIT);
|
||||
|
||||
NetExpr*tmp = elab_and_eval(des, scope, index.msb, -1);
|
||||
ivl_assert(*index.msb, tmp);
|
||||
|
||||
if (NetEConst*ctmp = dynamic_cast<NetEConst*>(tmp)) {
|
||||
res.push_back(hname_t(comp.name, ctmp->value().as_long()));
|
||||
delete ctmp;
|
||||
continue;
|
||||
} else {
|
||||
cerr << index.msb->get_fileline() << ": error: "
|
||||
<< "Scope index expression is not constant: "
|
||||
<< *index.msb << endl;
|
||||
des->errors += 1;
|
||||
}
|
||||
|
||||
return res;
|
||||
res.push_back( eval_path_component(des,scope,comp) );
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
@@ -64,6 +64,13 @@ extern NetNet*pad_to_width(Design*des, NetNet*n, unsigned w);
|
||||
|
||||
extern NetNet*pad_to_width_signed(Design*des, NetNet*n, unsigned w);
|
||||
|
||||
/*
|
||||
* Generate the nodes necessary to cast an expression (a net) to a
|
||||
* real value.
|
||||
*/
|
||||
extern NetNet*cast_to_int(Design*des, NetScope*scope, NetNet*src, unsigned wid);
|
||||
extern NetNet*cast_to_real(Design*des, NetScope*scope, NetNet*src);
|
||||
|
||||
/*
|
||||
* Take the input expression and return a variation that assures that
|
||||
* the expression is 1-bit wide and logical. This reflects the needs
|
||||
@@ -145,6 +152,16 @@ void eval_expr(NetExpr*&expr, int prune_width =-1);
|
||||
bool eval_as_long(long&value, NetExpr*expr);
|
||||
bool eval_as_double(double&value, NetExpr*expr);
|
||||
|
||||
/*
|
||||
* Evaluate the component of a scope path to get an hname_t value. Do
|
||||
* the evaluation in the context of the given scope.
|
||||
*/
|
||||
extern hname_t eval_path_component(Design*des, NetScope*scope,
|
||||
const name_component_t&comp);
|
||||
|
||||
/*
|
||||
* Evaluate an entire scope path in the context of the given scope.
|
||||
*/
|
||||
extern std::list<hname_t> eval_scope_path(Design*des, NetScope*scope,
|
||||
const pform_name_t&path);
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2)
|
||||
%token K_PSTAR K_STARP
|
||||
%token K_LOR K_LAND K_NAND K_NOR K_NXOR K_TRIGGER
|
||||
%token K_abs K_abstol K_access K_acos K_acosh K_asin K_analog K_asinh
|
||||
%token K_atan K_atanh K_atan2
|
||||
%token K_atan K_atanh K_atan2 K_automatic
|
||||
%token K_always K_and K_assign K_begin K_bool K_buf K_bufif0 K_bufif1 K_case
|
||||
%token K_casex K_casez K_ceil K_cmos K_continuous K_cos K_cosh
|
||||
%token K_ddt_nature K_deassign K_default K_defparam K_disable K_discrete
|
||||
@@ -351,6 +351,13 @@ number : BASED_NUMBER
|
||||
based_size = 0; }
|
||||
;
|
||||
|
||||
/* real and realtime are exactly the same so save some code
|
||||
* with a common matching rule. */
|
||||
real_or_realtime
|
||||
: K_real
|
||||
| K_realtime
|
||||
;
|
||||
|
||||
/* Verilog-2001 supports attribute lists, which can be attached to a
|
||||
variety of different objects. The syntax inside the (* *) is a
|
||||
comma separated list of names or names with assigned values. */
|
||||
@@ -432,7 +439,7 @@ block_item_decl
|
||||
|
||||
/* Integer declarations are simpler in that they do not have all the
|
||||
trappings of a general variable declaration. All of that is
|
||||
implicit in the "integer" of the declaratin. */
|
||||
implicit in the "integer" of the declaration. */
|
||||
|
||||
| attribute_list_opt K_integer register_variable_list ';'
|
||||
{ pform_set_reg_integer($3);
|
||||
@@ -445,7 +452,7 @@ block_item_decl
|
||||
|
||||
/* real declarations are fairly simple as there is no range of
|
||||
signed flag in the declaration. Create the real as a NetNet::REG
|
||||
with real value. Note that real and realtime are interchangable
|
||||
with real value. Note that real and realtime are interchangeable
|
||||
in this context. */
|
||||
|
||||
| attribute_list_opt K_real real_variable_list ';'
|
||||
@@ -1186,7 +1193,7 @@ expr_primary
|
||||
}
|
||||
|
||||
/* Many of the VAMS built-in functions are available as builtin
|
||||
functions with $system_function equivilents. */
|
||||
functions with $system_function equivalents. */
|
||||
|
||||
| K_acos '(' expression ')'
|
||||
{ perm_string tn = perm_string::literal("$acos");
|
||||
@@ -2087,41 +2094,41 @@ module_item
|
||||
statements in the task body. But we continue to accept it as an
|
||||
extension. */
|
||||
|
||||
| K_task IDENTIFIER ';'
|
||||
| K_task automatic_opt IDENTIFIER ';'
|
||||
{ assert(current_task == 0);
|
||||
current_task = pform_push_task_scope($2);
|
||||
current_task = pform_push_task_scope($3);
|
||||
FILE_NAME(current_task, @1);
|
||||
}
|
||||
task_item_list_opt
|
||||
statement_or_null
|
||||
K_endtask
|
||||
{ current_task->set_ports($5);
|
||||
current_task->set_statement($6);
|
||||
{ current_task->set_ports($6);
|
||||
current_task->set_statement($7);
|
||||
pform_pop_scope();
|
||||
current_task = 0;
|
||||
delete[]$2;
|
||||
delete[]$3;
|
||||
}
|
||||
|
||||
| K_task IDENTIFIER
|
||||
| K_task automatic_opt IDENTIFIER
|
||||
{ assert(current_task == 0);
|
||||
current_task = pform_push_task_scope($2);
|
||||
current_task = pform_push_task_scope($3);
|
||||
FILE_NAME(current_task, @1);
|
||||
}
|
||||
'(' task_port_decl_list ')' ';'
|
||||
block_item_decls_opt
|
||||
statement_or_null
|
||||
K_endtask
|
||||
{ current_task->set_ports($5);
|
||||
current_task->set_statement($9);
|
||||
{ current_task->set_ports($6);
|
||||
current_task->set_statement($10);
|
||||
pform_pop_scope();
|
||||
current_task = 0;
|
||||
delete[]$2;
|
||||
delete[]$3;
|
||||
}
|
||||
| K_task IDENTIFIER error K_endtask
|
||||
| K_task automatic_opt IDENTIFIER error K_endtask
|
||||
{
|
||||
pform_pop_scope();
|
||||
current_task = 0;
|
||||
delete[]$2;
|
||||
delete[]$3;
|
||||
}
|
||||
|
||||
/* The function declaration rule matches the function declaration
|
||||
@@ -2129,42 +2136,42 @@ module_item
|
||||
definitions in the func_body to take on the scope of the function
|
||||
instead of the module. */
|
||||
|
||||
| K_function function_range_or_type_opt IDENTIFIER ';'
|
||||
| K_function automatic_opt function_range_or_type_opt IDENTIFIER ';'
|
||||
{ assert(current_function == 0);
|
||||
current_function = pform_push_function_scope($3);
|
||||
current_function = pform_push_function_scope($4);
|
||||
FILE_NAME(current_function, @1);
|
||||
}
|
||||
function_item_list statement
|
||||
K_endfunction
|
||||
{ current_function->set_ports($6);
|
||||
current_function->set_statement($7);
|
||||
current_function->set_return($2);
|
||||
{ current_function->set_ports($7);
|
||||
current_function->set_statement($8);
|
||||
current_function->set_return($3);
|
||||
pform_pop_scope();
|
||||
current_function = 0;
|
||||
delete[]$3;
|
||||
delete[]$4;
|
||||
}
|
||||
|
||||
| K_function function_range_or_type_opt IDENTIFIER
|
||||
| K_function automatic_opt function_range_or_type_opt IDENTIFIER
|
||||
{ assert(current_function == 0);
|
||||
current_function = pform_push_function_scope($3);
|
||||
current_function = pform_push_function_scope($4);
|
||||
FILE_NAME(current_function, @1);
|
||||
}
|
||||
'(' task_port_decl_list ')' ';'
|
||||
block_item_decls_opt
|
||||
statement
|
||||
K_endfunction
|
||||
{ current_function->set_ports($6);
|
||||
current_function->set_statement($10);
|
||||
current_function->set_return($2);
|
||||
{ current_function->set_ports($7);
|
||||
current_function->set_statement($11);
|
||||
current_function->set_return($3);
|
||||
pform_pop_scope();
|
||||
current_function = 0;
|
||||
delete[]$3;
|
||||
delete[]$4;
|
||||
}
|
||||
| K_function function_range_or_type_opt IDENTIFIER error K_endfunction
|
||||
| K_function automatic_opt function_range_or_type_opt IDENTIFIER error K_endfunction
|
||||
{
|
||||
pform_pop_scope();
|
||||
current_task = 0;
|
||||
delete[]$3;
|
||||
delete[]$4;
|
||||
}
|
||||
|
||||
/* A generate region can contain further module items. Actually, it
|
||||
@@ -2200,6 +2207,15 @@ module_item
|
||||
K_endcase
|
||||
{ pform_endgenerate(); }
|
||||
|
||||
/* Handle some anachronistic syntax cases. */
|
||||
| K_generate K_begin module_item_list_opt K_end K_endgenerate
|
||||
{ /* Detect and warn about anachronistic begin/end use */
|
||||
if (generation_flag > GN_VER2001) {
|
||||
warn_count += 1;
|
||||
cerr << @2 << ": warning: Anachronistic use of begin/end to surround generate schemes." << endl;
|
||||
}
|
||||
}
|
||||
|
||||
/* specify blocks are parsed but ignored. */
|
||||
|
||||
| K_specify K_endspecify
|
||||
@@ -2262,6 +2278,15 @@ module_item
|
||||
{ yyerror(@1, "error: Malformed $attribute parameter list."); }
|
||||
;
|
||||
|
||||
automatic_opt
|
||||
: K_automatic
|
||||
{ yyerror(@1, "sorry: automatic tasks/functions are not "
|
||||
"currently supported.");
|
||||
yyerrok;
|
||||
}
|
||||
| {}
|
||||
;
|
||||
|
||||
generate_if : K_if '(' expression ')' { pform_start_generate_if(@1, $3); }
|
||||
|
||||
generate_case_items
|
||||
@@ -2384,7 +2409,14 @@ parameter_assign_decl
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
| K_integer
|
||||
{ param_active_range = 0;
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum(integer_width-1, integer_width));
|
||||
(*range_stub)[0] = re;
|
||||
re = new PENumber(new verinum((uint64_t)0, integer_width));
|
||||
(*range_stub)[1] = re;
|
||||
/* The default range is [31:0] */
|
||||
param_active_range = range_stub;
|
||||
param_active_signed = true;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
@@ -2393,7 +2425,24 @@ parameter_assign_decl
|
||||
param_active_signed = false;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
| K_real
|
||||
| K_time
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum((uint64_t)63, integer_width));
|
||||
(*range_stub)[0] = re;
|
||||
re = new PENumber(new verinum((uint64_t)0, integer_width));
|
||||
(*range_stub)[1] = re;
|
||||
/* The range is [63:0] */
|
||||
param_active_range = range_stub;
|
||||
param_active_signed = false;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
parameter_assign_list
|
||||
{ param_active_range = 0;
|
||||
param_active_signed = false;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
| real_or_realtime
|
||||
{ param_active_range = 0;
|
||||
param_active_signed = true;
|
||||
param_active_type = IVL_VT_REAL;
|
||||
@@ -2476,7 +2525,7 @@ localparam_assign_decl
|
||||
localparam_assign_list
|
||||
{ param_active_range = 0;
|
||||
param_active_signed = false;
|
||||
param_active_type = IVL_VT_NO_TYPE;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
| K_signed range
|
||||
{ param_active_range = $2;
|
||||
@@ -2489,7 +2538,14 @@ localparam_assign_decl
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
| K_integer
|
||||
{ param_active_range = 0;
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum(integer_width-1, integer_width));
|
||||
(*range_stub)[0] = re;
|
||||
re = new PENumber(new verinum((uint64_t)0, integer_width));
|
||||
(*range_stub)[1] = re;
|
||||
/* The default range is [31:0] */
|
||||
param_active_range = range_stub;
|
||||
param_active_signed = true;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
@@ -2498,7 +2554,24 @@ localparam_assign_decl
|
||||
param_active_signed = false;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
| K_real
|
||||
| K_time
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum((uint64_t)63, integer_width));
|
||||
(*range_stub)[0] = re;
|
||||
re = new PENumber(new verinum((uint64_t)0, integer_width));
|
||||
(*range_stub)[1] = re;
|
||||
/* The range is [63:0] */
|
||||
param_active_range = range_stub;
|
||||
param_active_signed = false;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
localparam_assign_list
|
||||
{ param_active_range = 0;
|
||||
param_active_signed = false;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
| real_or_realtime
|
||||
{ param_active_range = 0;
|
||||
param_active_signed = true;
|
||||
param_active_type = IVL_VT_REAL;
|
||||
@@ -3657,39 +3730,43 @@ task_item
|
||||
| task_port_item { $$ = $1; }
|
||||
;
|
||||
|
||||
reg_opt
|
||||
: K_reg
|
||||
|
|
||||
;
|
||||
|
||||
task_port_item
|
||||
|
||||
: K_input signed_opt range_opt list_of_identifiers ';'
|
||||
: K_input reg_opt signed_opt range_opt list_of_identifiers ';'
|
||||
{ svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::PINPUT,
|
||||
IVL_VT_NO_TYPE, $2,
|
||||
$3, $4,
|
||||
IVL_VT_NO_TYPE, $3,
|
||||
$4, $5,
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_output signed_opt range_opt list_of_identifiers ';'
|
||||
| K_output reg_opt signed_opt range_opt list_of_identifiers ';'
|
||||
{ svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::POUTPUT,
|
||||
IVL_VT_LOGIC, $2,
|
||||
$3, $4,
|
||||
IVL_VT_LOGIC, $3,
|
||||
$4, $5,
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_inout signed_opt range_opt list_of_identifiers ';'
|
||||
| K_inout reg_opt signed_opt range_opt list_of_identifiers ';'
|
||||
{ svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::PINOUT,
|
||||
IVL_VT_LOGIC, $2,
|
||||
$3, $4,
|
||||
IVL_VT_LOGIC, $3,
|
||||
$4, $5,
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
|
||||
/* When the port is an integer, infer a signed vector of the integer
|
||||
shape. Generate a range to make it work. */
|
||||
shape. Generate a range ([31:0]) to make it work. */
|
||||
|
||||
| K_input K_integer list_of_identifiers ';'
|
||||
{ svector<PExpr*>*range_stub
|
||||
= new svector<PExpr*>(2);
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum(integer_width-1,
|
||||
integer_width));
|
||||
@@ -3704,8 +3781,7 @@ task_port_item
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_output K_integer list_of_identifiers ';'
|
||||
{ svector<PExpr*>*range_stub
|
||||
= new svector<PExpr*>(2);
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum(integer_width-1,
|
||||
integer_width));
|
||||
@@ -3720,8 +3796,7 @@ task_port_item
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_inout K_integer list_of_identifiers ';'
|
||||
{ svector<PExpr*>*range_stub
|
||||
= new svector<PExpr*>(2);
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum(integer_width-1,
|
||||
integer_width));
|
||||
@@ -3736,9 +3811,54 @@ task_port_item
|
||||
$$ = tmp;
|
||||
}
|
||||
|
||||
/* Ports can be real. */
|
||||
/* Ports can be time with a width of [63:0] (unsigned). */
|
||||
|
||||
| K_input K_real list_of_identifiers ';'
|
||||
| K_input K_time list_of_identifiers ';'
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum((uint64_t)63, integer_width));
|
||||
(*range_stub)[0] = re;
|
||||
re = new PENumber(new verinum((uint64_t)0, integer_width));
|
||||
(*range_stub)[1] = re;
|
||||
svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::PINPUT,
|
||||
IVL_VT_LOGIC, false,
|
||||
range_stub, $3,
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_output K_time list_of_identifiers ';'
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum((uint64_t)63, integer_width));
|
||||
(*range_stub)[0] = re;
|
||||
re = new PENumber(new verinum((uint64_t)0, integer_width));
|
||||
(*range_stub)[1] = re;
|
||||
svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::POUTPUT,
|
||||
IVL_VT_LOGIC, false,
|
||||
range_stub, $3,
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_inout K_time list_of_identifiers ';'
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum((uint64_t)63, integer_width));
|
||||
(*range_stub)[0] = re;
|
||||
re = new PENumber(new verinum((uint64_t)0, integer_width));
|
||||
(*range_stub)[1] = re;
|
||||
svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::PINOUT,
|
||||
IVL_VT_LOGIC, false,
|
||||
range_stub, $3,
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
|
||||
/* Ports can be real or realtime. */
|
||||
|
||||
| K_input real_or_realtime list_of_identifiers ';'
|
||||
{ svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::PINPUT,
|
||||
IVL_VT_REAL, false,
|
||||
@@ -3746,7 +3866,7 @@ task_port_item
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_output K_real list_of_identifiers ';'
|
||||
| K_output real_or_realtime list_of_identifiers ';'
|
||||
{ svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::POUTPUT,
|
||||
IVL_VT_REAL, true,
|
||||
@@ -3754,7 +3874,7 @@ task_port_item
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_inout K_real list_of_identifiers ';'
|
||||
| K_inout real_or_realtime list_of_identifiers ';'
|
||||
{ svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::PINOUT,
|
||||
IVL_VT_REAL, true,
|
||||
@@ -3784,50 +3904,51 @@ task_item_list_opt
|
||||
|
||||
task_port_decl
|
||||
|
||||
: K_input signed_opt range_opt IDENTIFIER
|
||||
: K_input reg_opt signed_opt range_opt IDENTIFIER
|
||||
{ port_declaration_context.port_type = NetNet::PINPUT;
|
||||
port_declaration_context.var_type = IVL_VT_LOGIC;
|
||||
port_declaration_context.sign_flag = $2;
|
||||
port_declaration_context.sign_flag = $3;
|
||||
delete port_declaration_context.range;
|
||||
port_declaration_context.range = copy_range($3);
|
||||
port_declaration_context.range = copy_range($4);
|
||||
svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::PINPUT,
|
||||
IVL_VT_LOGIC, $2,
|
||||
$3, list_from_identifier($4),
|
||||
IVL_VT_LOGIC, $3,
|
||||
$4, list_from_identifier($5),
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
|
||||
| K_output signed_opt range_opt IDENTIFIER
|
||||
| K_output reg_opt signed_opt range_opt IDENTIFIER
|
||||
{ port_declaration_context.port_type = NetNet::POUTPUT;
|
||||
port_declaration_context.var_type = IVL_VT_LOGIC;
|
||||
port_declaration_context.sign_flag = $2;
|
||||
port_declaration_context.sign_flag = $3;
|
||||
delete port_declaration_context.range;
|
||||
port_declaration_context.range = copy_range($3);
|
||||
port_declaration_context.range = copy_range($4);
|
||||
svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::POUTPUT,
|
||||
IVL_VT_LOGIC, $2,
|
||||
$3, list_from_identifier($4),
|
||||
IVL_VT_LOGIC, $3,
|
||||
$4, list_from_identifier($5),
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_inout signed_opt range_opt IDENTIFIER
|
||||
| K_inout reg_opt signed_opt range_opt IDENTIFIER
|
||||
{ port_declaration_context.port_type = NetNet::PINOUT;
|
||||
port_declaration_context.var_type = IVL_VT_LOGIC;
|
||||
port_declaration_context.sign_flag = $2;
|
||||
port_declaration_context.sign_flag = $3;
|
||||
delete port_declaration_context.range;
|
||||
port_declaration_context.range = copy_range($3);
|
||||
port_declaration_context.range = copy_range($4);
|
||||
svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::PINOUT,
|
||||
IVL_VT_LOGIC, $2,
|
||||
$3, list_from_identifier($4),
|
||||
IVL_VT_LOGIC, $3,
|
||||
$4, list_from_identifier($5),
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
|
||||
/* Ports can be integer with a width of [31:0]. */
|
||||
|
||||
| K_input K_integer IDENTIFIER
|
||||
{ svector<PExpr*>*range_stub
|
||||
= new svector<PExpr*>(2);
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum(integer_width-1,
|
||||
integer_width));
|
||||
@@ -3848,8 +3969,7 @@ task_port_decl
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_output K_integer IDENTIFIER
|
||||
{ svector<PExpr*>*range_stub
|
||||
= new svector<PExpr*>(2);
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum(integer_width-1,
|
||||
integer_width));
|
||||
@@ -3870,8 +3990,7 @@ task_port_decl
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_inout K_integer IDENTIFIER
|
||||
{ svector<PExpr*>*range_stub
|
||||
= new svector<PExpr*>(2);
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum(integer_width-1,
|
||||
integer_width));
|
||||
@@ -3892,9 +4011,72 @@ task_port_decl
|
||||
$$ = tmp;
|
||||
}
|
||||
|
||||
/* Ports can be real. */
|
||||
/* Ports can be time with a width of [63:0] (unsigned). */
|
||||
|
||||
| K_input K_time IDENTIFIER
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum((uint64_t)63, integer_width));
|
||||
(*range_stub)[0] = re;
|
||||
re = new PENumber(new verinum((uint64_t)0, integer_width));
|
||||
(*range_stub)[1] = re;
|
||||
port_declaration_context.port_type = NetNet::PINPUT;
|
||||
port_declaration_context.var_type = IVL_VT_LOGIC;
|
||||
port_declaration_context.sign_flag = false;
|
||||
delete port_declaration_context.range;
|
||||
port_declaration_context.range = copy_range(range_stub);
|
||||
svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::PINPUT,
|
||||
IVL_VT_LOGIC, false,
|
||||
range_stub,
|
||||
list_from_identifier($3),
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_output K_time IDENTIFIER
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum((uint64_t)63, integer_width));
|
||||
(*range_stub)[0] = re;
|
||||
re = new PENumber(new verinum((uint64_t)0, integer_width));
|
||||
(*range_stub)[1] = re;
|
||||
port_declaration_context.port_type = NetNet::POUTPUT;
|
||||
port_declaration_context.var_type = IVL_VT_LOGIC;
|
||||
port_declaration_context.sign_flag = false;
|
||||
delete port_declaration_context.range;
|
||||
port_declaration_context.range = copy_range(range_stub);
|
||||
svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::POUTPUT,
|
||||
IVL_VT_LOGIC, false,
|
||||
range_stub,
|
||||
list_from_identifier($3),
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_inout K_time IDENTIFIER
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum((uint64_t)63, integer_width));
|
||||
(*range_stub)[0] = re;
|
||||
re = new PENumber(new verinum((uint64_t)0, integer_width));
|
||||
(*range_stub)[1] = re;
|
||||
port_declaration_context.port_type = NetNet::PINOUT;
|
||||
port_declaration_context.var_type = IVL_VT_LOGIC;
|
||||
port_declaration_context.sign_flag = false;
|
||||
delete port_declaration_context.range;
|
||||
port_declaration_context.range = copy_range(range_stub);
|
||||
svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::PINOUT,
|
||||
IVL_VT_LOGIC, false,
|
||||
range_stub,
|
||||
list_from_identifier($3),
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
|
||||
| K_input K_real IDENTIFIER
|
||||
/* Ports can be real or realtime. */
|
||||
|
||||
| K_input real_or_realtime IDENTIFIER
|
||||
{ port_declaration_context.port_type = NetNet::PINPUT;
|
||||
port_declaration_context.var_type = IVL_VT_REAL;
|
||||
port_declaration_context.sign_flag = false;
|
||||
@@ -3907,7 +4089,7 @@ task_port_decl
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_output K_real IDENTIFIER
|
||||
| K_output real_or_realtime IDENTIFIER
|
||||
{ port_declaration_context.port_type = NetNet::POUTPUT;
|
||||
port_declaration_context.var_type = IVL_VT_REAL;
|
||||
port_declaration_context.sign_flag = false;
|
||||
@@ -3920,7 +4102,7 @@ task_port_decl
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_inout K_real IDENTIFIER
|
||||
| K_inout real_or_realtime IDENTIFIER
|
||||
{ port_declaration_context.port_type = NetNet::PINOUT;
|
||||
port_declaration_context.var_type = IVL_VT_REAL;
|
||||
port_declaration_context.sign_flag = false;
|
||||
|
||||
+11
-8
@@ -30,6 +30,15 @@ unsigned error_count = 0;
|
||||
unsigned warn_count = 0;
|
||||
unsigned long based_size = 0;
|
||||
|
||||
std::ostream& operator << (std::ostream&o, const YYLTYPE&loc)
|
||||
{
|
||||
if (loc.text)
|
||||
o << loc.text << ":";
|
||||
o << loc.first_line;
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
void VLerror(const char*msg)
|
||||
{
|
||||
error_count += 1;
|
||||
@@ -39,20 +48,14 @@ void VLerror(const char*msg)
|
||||
void VLerror(const YYLTYPE&loc, const char*msg)
|
||||
{
|
||||
error_count += 1;
|
||||
if (loc.text)
|
||||
cerr << loc.text << ":";
|
||||
|
||||
cerr << loc.first_line << ": " << msg << endl;
|
||||
cerr << loc << ": " << msg << endl;
|
||||
based_size = 0; /* Clear the base information if we have an error. */
|
||||
}
|
||||
|
||||
void yywarn(const YYLTYPE&loc, const char*msg)
|
||||
{
|
||||
warn_count += 1;
|
||||
if (loc.text)
|
||||
cerr << loc.text << ":";
|
||||
|
||||
cerr << loc.first_line << ": warning: " << msg << endl;
|
||||
cerr << loc << ": warning: " << msg << endl;
|
||||
}
|
||||
|
||||
int VLwrap()
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#endif
|
||||
|
||||
# include <list>
|
||||
# include <ostream>
|
||||
# include "compiler.h"
|
||||
# include "pform.h"
|
||||
|
||||
@@ -62,6 +63,8 @@ extern void VLerror(const YYLTYPE&loc, const char*msg);
|
||||
#define yywarn VLwarn
|
||||
extern void VLwarn(const YYLTYPE&loc, const char*msg);
|
||||
|
||||
extern ostream& operator << (ostream&, const YYLTYPE&loc);
|
||||
|
||||
extern unsigned error_count, warn_count;
|
||||
extern unsigned long based_size;
|
||||
|
||||
|
||||
@@ -93,40 +93,64 @@ static PScope* lexical_scope = 0;
|
||||
|
||||
void pform_pop_scope()
|
||||
{
|
||||
lexical_scope = lexical_scope->pscope_parent();
|
||||
if (pform_cur_generate) {
|
||||
assert(pform_cur_generate->lexical_scope);
|
||||
PScope*cur = pform_cur_generate->lexical_scope;
|
||||
pform_cur_generate->lexical_scope = cur->pscope_parent();
|
||||
} else {
|
||||
assert(lexical_scope);
|
||||
lexical_scope = lexical_scope->pscope_parent();
|
||||
}
|
||||
}
|
||||
|
||||
PTask* pform_push_task_scope(char*name)
|
||||
{
|
||||
perm_string task_name = lex_strings.make(name);
|
||||
PTask*task = new PTask(task_name, pform_cur_module);
|
||||
|
||||
// Add the task to the current module
|
||||
pform_cur_module->add_task(task->pscope_name(), task);
|
||||
// Make this the current lexical scope
|
||||
lexical_scope = task;
|
||||
PTask*task;
|
||||
if (pform_cur_generate) {
|
||||
task = new PTask(task_name, pform_cur_generate->lexical_scope);
|
||||
pform_cur_generate->tasks[task->pscope_name()] = task;
|
||||
pform_cur_generate->lexical_scope = task;
|
||||
} else {
|
||||
task = new PTask(task_name, lexical_scope);
|
||||
pform_cur_module->tasks[task->pscope_name()] = task;
|
||||
lexical_scope = task;
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
PFunction* pform_push_function_scope(char*name)
|
||||
{
|
||||
perm_string func_name = lex_strings.make(name);
|
||||
PFunction*func = new PFunction(func_name, lexical_scope);
|
||||
|
||||
// Add the task to the current module
|
||||
pform_cur_module->add_function(func->pscope_name(), func);
|
||||
// Make this the current lexical scope
|
||||
lexical_scope = func;
|
||||
PFunction*func;
|
||||
if (pform_cur_generate) {
|
||||
func = new PFunction(func_name, pform_cur_generate->lexical_scope);
|
||||
pform_cur_generate->funcs[func->pscope_name()] = func;
|
||||
pform_cur_generate->lexical_scope = func;
|
||||
} else {
|
||||
func = new PFunction(func_name, lexical_scope);
|
||||
pform_cur_module->funcs[func->pscope_name()] = func;
|
||||
lexical_scope = func;
|
||||
}
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
PBlock* pform_push_block_scope(char*name, PBlock::BL_TYPE bt)
|
||||
{
|
||||
perm_string block_name = lex_strings.make(name);
|
||||
PBlock*block = new PBlock(block_name, lexical_scope, bt);
|
||||
|
||||
// Make this the current lexical scope
|
||||
lexical_scope = block;
|
||||
PBlock*block;
|
||||
if (pform_cur_generate) {
|
||||
block = new PBlock(block_name, pform_cur_generate->lexical_scope, bt);
|
||||
pform_cur_generate->lexical_scope = block;
|
||||
} else {
|
||||
block = new PBlock(block_name, lexical_scope, bt);
|
||||
lexical_scope = block;
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
@@ -138,9 +162,34 @@ PWire*pform_get_wire_in_scope(perm_string name)
|
||||
cannot be within sub-scopes. Only directly in
|
||||
modules. */
|
||||
if (pform_cur_generate)
|
||||
return pform_cur_generate->get_wire(name);
|
||||
if (pform_cur_generate->lexical_scope)
|
||||
return pform_cur_generate->lexical_scope->wires_find(name);
|
||||
else
|
||||
return pform_cur_generate->wires_find(name);
|
||||
else
|
||||
return lexical_scope->wires_find(name);
|
||||
}
|
||||
|
||||
return lexical_scope->wires_find(name);
|
||||
static void pform_put_wire_in_scope(perm_string name, PWire*net)
|
||||
{
|
||||
if (pform_cur_generate)
|
||||
if (pform_cur_generate->lexical_scope)
|
||||
pform_cur_generate->lexical_scope->wires[name] = net;
|
||||
else
|
||||
pform_cur_generate->wires[name] = net;
|
||||
else
|
||||
lexical_scope->wires[name] = net;
|
||||
}
|
||||
|
||||
static void pform_put_behavior_in_scope(PProcess*pp)
|
||||
{
|
||||
if (pform_cur_generate)
|
||||
if (pform_cur_generate->lexical_scope)
|
||||
pform_cur_generate->lexical_scope->behaviors.push_back(pp);
|
||||
else
|
||||
pform_cur_generate->behaviors.push_back(pp);
|
||||
else
|
||||
lexical_scope->behaviors.push_back(pp);
|
||||
}
|
||||
|
||||
void pform_set_default_nettype(NetNet::Type type,
|
||||
@@ -508,7 +557,7 @@ void pform_endgenerate()
|
||||
if (pform_cur_generate != 0) {
|
||||
assert(cur->scheme_type == PGenerate::GS_CASE_ITEM
|
||||
|| pform_cur_generate->scheme_type != PGenerate::GS_CASE);
|
||||
pform_cur_generate->generates.push_back(cur);
|
||||
pform_cur_generate->generate_schemes.push_back(cur);
|
||||
} else {
|
||||
assert(cur->scheme_type != PGenerate::GS_CASE_ITEM);
|
||||
pform_cur_module->generate_schemes.push_back(cur);
|
||||
@@ -1236,7 +1285,7 @@ void pform_make_pgassign_list(svector<PExpr*>*alist,
|
||||
void pform_make_reginit(const struct vlltype&li,
|
||||
perm_string name, PExpr*expr)
|
||||
{
|
||||
PWire*cur = lexical_scope->wires_find(name);
|
||||
PWire*cur = pform_get_wire_in_scope(name);
|
||||
if (cur == 0) {
|
||||
VLerror(li, "internal error: reginit to non-register?");
|
||||
delete expr;
|
||||
@@ -1250,7 +1299,7 @@ void pform_make_reginit(const struct vlltype&li,
|
||||
PProcess*top = new PProcess(PProcess::PR_INITIAL, ass);
|
||||
FILE_NAME(top, li);
|
||||
|
||||
lexical_scope->behaviors.push_back(top);
|
||||
pform_put_behavior_in_scope(top);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1271,7 +1320,7 @@ void pform_module_define_port(const struct vlltype&li,
|
||||
svector<PExpr*>*range,
|
||||
svector<named_pexpr_t*>*attr)
|
||||
{
|
||||
PWire*cur = lexical_scope->wires_find(name);
|
||||
PWire*cur = pform_get_wire_in_scope(name);
|
||||
if (cur) {
|
||||
ostringstream msg;
|
||||
msg << name << " definition conflicts with "
|
||||
@@ -1305,7 +1354,7 @@ void pform_module_define_port(const struct vlltype&li,
|
||||
cur->attributes[tmp->name] = tmp->parm;
|
||||
}
|
||||
}
|
||||
lexical_scope->wires[name] = cur;
|
||||
pform_put_wire_in_scope(name, cur);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1388,12 +1437,8 @@ void pform_makewire(const vlltype&li, perm_string name,
|
||||
}
|
||||
}
|
||||
|
||||
if (new_wire_flag) {
|
||||
if (pform_cur_generate)
|
||||
pform_cur_generate->wires[name] = cur;
|
||||
else
|
||||
lexical_scope->wires[name] = cur;
|
||||
}
|
||||
if (new_wire_flag)
|
||||
pform_put_wire_in_scope(name, cur);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1469,11 +1514,11 @@ void pform_makewire(const vlltype&li,
|
||||
void pform_set_port_type(perm_string name, NetNet::PortType pt,
|
||||
const char*file, unsigned lineno)
|
||||
{
|
||||
PWire*cur = lexical_scope->wires_find(name);
|
||||
PWire*cur = pform_get_wire_in_scope(name);
|
||||
if (cur == 0) {
|
||||
cur = new PWire(name, NetNet::IMPLICIT, NetNet::PIMPLICIT, IVL_VT_NO_TYPE);
|
||||
FILE_NAME(cur, file, lineno);
|
||||
lexical_scope->wires[name] = cur;
|
||||
pform_put_wire_in_scope(name, cur);
|
||||
}
|
||||
|
||||
switch (cur->get_port_type()) {
|
||||
@@ -1555,13 +1600,13 @@ svector<PWire*>*pform_make_task_ports(NetNet::PortType pt,
|
||||
|
||||
/* Look for a preexisting wire. If it exists, set the
|
||||
port direction. If not, create it. */
|
||||
PWire*curw = lexical_scope->wires_find(name);
|
||||
PWire*curw = pform_get_wire_in_scope(name);
|
||||
if (curw) {
|
||||
curw->set_port_type(pt);
|
||||
} else {
|
||||
curw = new PWire(name, NetNet::IMPLICIT_REG, pt, vtype);
|
||||
FILE_NAME(curw, file, lineno);
|
||||
lexical_scope->wires[name] = curw;
|
||||
pform_put_wire_in_scope(name, curw);
|
||||
}
|
||||
|
||||
curw->set_signed(signed_flag);
|
||||
@@ -1622,7 +1667,7 @@ void pform_set_reg_idx(perm_string name, PExpr*l, PExpr*r)
|
||||
{
|
||||
PWire*cur = 0;
|
||||
if (pform_cur_generate) {
|
||||
cur = pform_cur_generate->get_wire(name);
|
||||
cur = pform_cur_generate->wires_find(name);
|
||||
} else {
|
||||
cur = lexical_scope->wires_find(name);
|
||||
}
|
||||
@@ -1715,7 +1760,7 @@ void pform_set_specparam(perm_string name, PExpr*expr)
|
||||
void pform_set_defparam(const pform_name_t&name, PExpr*expr)
|
||||
{
|
||||
assert(expr);
|
||||
pform_cur_module->defparms[name] = expr;
|
||||
pform_cur_module->defparms.push_back(make_pair(name,expr));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1805,13 +1850,13 @@ void pform_set_port_type(const struct vlltype&li,
|
||||
|
||||
static void pform_set_reg_integer(perm_string name)
|
||||
{
|
||||
PWire*cur = lexical_scope->wires_find(name);
|
||||
PWire*cur = pform_get_wire_in_scope(name);
|
||||
if (cur == 0) {
|
||||
cur = new PWire(name, NetNet::INTEGER,
|
||||
NetNet::NOT_A_PORT,
|
||||
IVL_VT_LOGIC);
|
||||
cur->set_signed(true);
|
||||
lexical_scope->wires[name] = cur;
|
||||
pform_put_wire_in_scope(name, cur);
|
||||
} else {
|
||||
bool rc = cur->set_wire_type(NetNet::INTEGER);
|
||||
assert(rc);
|
||||
@@ -1839,10 +1884,10 @@ void pform_set_reg_integer(list<perm_string>*names)
|
||||
|
||||
static void pform_set_reg_time(perm_string name)
|
||||
{
|
||||
PWire*cur = lexical_scope->wires_find(name);
|
||||
PWire*cur = pform_get_wire_in_scope(name);
|
||||
if (cur == 0) {
|
||||
cur = new PWire(name, NetNet::REG, NetNet::NOT_A_PORT, IVL_VT_LOGIC);
|
||||
lexical_scope->wires[name] = cur;
|
||||
pform_put_wire_in_scope(name, cur);
|
||||
} else {
|
||||
bool rc = cur->set_wire_type(NetNet::REG);
|
||||
assert(rc);
|
||||
@@ -1901,11 +1946,7 @@ PProcess* pform_make_behavior(PProcess::Type type, Statement*st,
|
||||
delete attr;
|
||||
}
|
||||
|
||||
if (pform_cur_generate)
|
||||
pform_cur_generate->add_behavior(pp);
|
||||
else
|
||||
pform_cur_module->behaviors.push_back(pp);
|
||||
|
||||
pform_put_behavior_in_scope(pp);
|
||||
return pp;
|
||||
}
|
||||
|
||||
|
||||
+7
-7
@@ -937,8 +937,8 @@ void PGenerate::dump(ostream&out, unsigned indent) const
|
||||
(*idx)->dump(out, indent+2);
|
||||
}
|
||||
|
||||
for (list<PGenerate*>::const_iterator idx = generates.begin()
|
||||
; idx != generates.end() ; idx++) {
|
||||
for (list<PGenerate*>::const_iterator idx = generate_schemes.begin()
|
||||
; idx != generate_schemes.end() ; idx++) {
|
||||
(*idx)->dump(out, indent+2);
|
||||
}
|
||||
|
||||
@@ -991,7 +991,6 @@ void Module::dump(ostream&out) const
|
||||
}
|
||||
|
||||
typedef map<perm_string,param_expr_t>::const_iterator parm_iter_t;
|
||||
typedef map<pform_name_t,PExpr*>::const_iterator parm_hiter_t;
|
||||
for (parm_iter_t cur = parameters.begin()
|
||||
; cur != parameters.end() ; cur ++) {
|
||||
out << " parameter " << (*cur).second.type << " ";
|
||||
@@ -1068,6 +1067,7 @@ void Module::dump(ostream&out) const
|
||||
<< *(*cur).second << ";" << endl;
|
||||
}
|
||||
|
||||
typedef list<Module::named_expr_t>::const_iterator parm_hiter_t;
|
||||
for (parm_hiter_t cur = defparms.begin()
|
||||
; cur != defparms.end() ; cur ++) {
|
||||
out << " defparam " << (*cur).first << " = ";
|
||||
@@ -1089,8 +1089,8 @@ void Module::dump(ostream&out) const
|
||||
|
||||
// Dump the task definitions.
|
||||
typedef map<perm_string,PTask*>::const_iterator task_iter_t;
|
||||
for (task_iter_t cur = tasks_.begin()
|
||||
; cur != tasks_.end() ; cur ++) {
|
||||
for (task_iter_t cur = tasks.begin()
|
||||
; cur != tasks.end() ; cur ++) {
|
||||
out << " task " << (*cur).first << ";" << endl;
|
||||
(*cur).second->dump(out, 6);
|
||||
out << " endtask;" << endl;
|
||||
@@ -1098,8 +1098,8 @@ void Module::dump(ostream&out) const
|
||||
|
||||
// Dump the function definitions.
|
||||
typedef map<perm_string,PFunction*>::const_iterator func_iter_t;
|
||||
for (func_iter_t cur = funcs_.begin()
|
||||
; cur != funcs_.end() ; cur ++) {
|
||||
for (func_iter_t cur = funcs.begin()
|
||||
; cur != funcs.end() ; cur ++) {
|
||||
out << " function " << (*cur).first << ";" << endl;
|
||||
(*cur).second->dump(out, 6);
|
||||
out << " endfunction;" << endl;
|
||||
|
||||
+1
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 2007-2008 Stephen Williams (steve@icarus.com)
|
||||
*
|
||||
* This source code is free software; you can redistribute it
|
||||
* and/or modify it in source code form under the terms of the GNU
|
||||
@@ -16,17 +16,6 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifdef HAVE_CVS_IDENT
|
||||
#ident "$Id: pform_types.cc,v 1.1 2007/05/24 04:07:12 steve Exp $"
|
||||
#endif
|
||||
|
||||
|
||||
# include "pform_types.h"
|
||||
|
||||
bool operator < (const name_component_t&lef, const name_component_t&rig)
|
||||
{
|
||||
if (lef.name < rig.name)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
+26
-6
@@ -1,7 +1,7 @@
|
||||
#ifndef __pform_types_H
|
||||
#define __pform_types_H
|
||||
/*
|
||||
* Copyright (c) 2007 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 2007-2008 Stephen Williams (steve@icarus.com)
|
||||
*
|
||||
* This source code is free software; you can redistribute it
|
||||
* and/or modify it in source code form under the terms of the GNU
|
||||
@@ -18,9 +18,6 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifdef HAVE_CVS_IDENT
|
||||
#ident "$Id: pform_types.h,v 1.2 2007/06/04 02:19:07 steve Exp $"
|
||||
#endif
|
||||
|
||||
// This for the perm_string type.
|
||||
# include "StringHeap.h"
|
||||
@@ -50,13 +47,36 @@ struct name_component_t {
|
||||
std::list<index_component_t>index;
|
||||
};
|
||||
|
||||
extern bool operator < (const name_component_t&lef, const name_component_t&rig);
|
||||
|
||||
/*
|
||||
* The pform_name_t is the general form for a hierarchical identifier.
|
||||
* The pform_name_t is the general form for a hierarchical
|
||||
* identifier. It is an ordered list of name components. Each name
|
||||
* component is an identifier and an optional list of bit/part
|
||||
* selects. The simplest name component is a simple identifier:
|
||||
*
|
||||
* foo
|
||||
*
|
||||
* The bit/part selects come from the source and are made part of the
|
||||
* name component. A bit select is a single number that may be a bit
|
||||
* select of a vector or a word select of an array:
|
||||
*
|
||||
* foo[5] -- a bit select/word index
|
||||
* foo[6:4] -- a part select
|
||||
*
|
||||
* The index components of a name component are collected into an
|
||||
* ordered list, so there may be many, for example:
|
||||
*
|
||||
* foo[5][6:4] -- a part select of an array word
|
||||
*
|
||||
* The pform_name_t, then, is an ordered list of these name
|
||||
* components. The list of names comes from a hierarchical name in the
|
||||
* source, like this:
|
||||
*
|
||||
* foo[5].bar[6:4] -- a part select of a vector in sub-scope foo[5].
|
||||
*/
|
||||
typedef std::list<name_component_t> pform_name_t;
|
||||
|
||||
|
||||
inline perm_string peek_head_name(const pform_name_t&that)
|
||||
{
|
||||
return that.front().name;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script manually creates a version.h file.
|
||||
#
|
||||
# It is used when creating a MinGW executable from a Cygwin
|
||||
# hosted git repository. It assumes that git is available.
|
||||
#
|
||||
# sh scripts/CREATE_VERSION.sh
|
||||
#
|
||||
|
||||
echo "Building version.h with git describe"
|
||||
tmp=`git describe | sed -e 's;\(.*\);#define VERSION_TAG "\1";'`
|
||||
echo "$tmp" > version.h
|
||||
@@ -7,7 +7,7 @@
|
||||
# sh scripts/MAKE_SNAPSHOT.sh 20080428 ~/tmp
|
||||
#
|
||||
# The above assumes that there is a tag "s20080428" at the point
|
||||
# to be snaphot. (The tag has the "s", but the argument to this
|
||||
# to be snapshot. (The tag has the "s", but the argument to this
|
||||
# script does not have the "s"). This script extracts based on the
|
||||
# tag, uses the temporary directory to stage intermediate results,
|
||||
# and finally creates a file called verilog-20080428.tar.gz that
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#
|
||||
# This is a debug conf file that the scripts/devel-stub.sh script uses
|
||||
# to control the ivl core. The contents of this file are normally written
|
||||
# to a temporary file by the driver program, but for devel purposes, where
|
||||
# the driver program is not used, this config substitutes.
|
||||
#
|
||||
# NOTE: DO NOT INSTALL THIS FILE!
|
||||
#
|
||||
generation:2005
|
||||
generation:specify
|
||||
generation:xtypes
|
||||
generation:verilog-ams
|
||||
iwidth:32
|
||||
sys_func:vpi/system.sft
|
||||
sys_func:vpi/va_math.sft
|
||||
warnings:implicit
|
||||
debug:eval_tree
|
||||
debug:elaborate
|
||||
debug:scopes
|
||||
debug:synth2
|
||||
out:a.out
|
||||
ivlpp:./ivlpp/ivlpp -D__ICARUS__ -L -Pfoo.pp
|
||||
sys_func:scripts/devel-stub.sft
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
# This is an example function table.
|
||||
$realtime vpiSysFuncReal
|
||||
|
||||
$verywide vpiSysFuncSized 128 signed
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
# This is a little developer convenience script to run the ivl core program
|
||||
# in place with the stub target. It runs the ivl core verbose, with diagnostic
|
||||
# output files enable, and without the driver program or preprocessor.
|
||||
# It is useful only for development of the ivl core program.
|
||||
#
|
||||
# Run this script in the source directory for the ivl core program so that
|
||||
# the patch to the other components is correct.
|
||||
#
|
||||
# NOTE: DO NOT INSTALL THIS FILE.
|
||||
|
||||
./ivl -v -Ctgt-stub/stub.conf -C./scripts/devel-stub.conf -Pa.pf -Na.net -fDLL=tgt-stub/stub.tgt foo.vl
|
||||
|
||||
echo "*** ivl command completed, rc=$?"
|
||||
@@ -598,7 +598,7 @@ bool NetCondit::synth_sync(Design*des, NetScope*scope, NetFF*ff,
|
||||
delete expr_input;
|
||||
|
||||
/* Detect the case that this is a *synchronous* set/reset. It
|
||||
is not asyncronous because we know the condition is not
|
||||
is not asynchronous because we know the condition is not
|
||||
included in the sensitivity list, but if the if_ case is
|
||||
constant (has no inputs) then we can model this as a
|
||||
synchronous set/reset.
|
||||
|
||||
+23
-8
@@ -886,6 +886,8 @@ extern "C" ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx)
|
||||
assert(net);
|
||||
switch (net->type) {
|
||||
case IVL_LPM_ABS:
|
||||
case IVL_LPM_CAST_INT:
|
||||
case IVL_LPM_CAST_REAL:
|
||||
assert(idx == 0);
|
||||
return net->u_.arith.a;
|
||||
|
||||
@@ -1028,20 +1030,19 @@ extern "C" ivl_nexus_t ivl_lpm_q(ivl_lpm_t net, unsigned idx)
|
||||
switch (net->type) {
|
||||
case IVL_LPM_ABS:
|
||||
case IVL_LPM_ADD:
|
||||
case IVL_LPM_DIVIDE:
|
||||
case IVL_LPM_MOD:
|
||||
case IVL_LPM_MULT:
|
||||
case IVL_LPM_POW:
|
||||
case IVL_LPM_SUB:
|
||||
assert(idx == 0);
|
||||
return net->u_.arith.q;
|
||||
|
||||
case IVL_LPM_CAST_INT:
|
||||
case IVL_LPM_CAST_REAL:
|
||||
case IVL_LPM_CMP_GE:
|
||||
case IVL_LPM_CMP_GT:
|
||||
case IVL_LPM_CMP_EQ:
|
||||
case IVL_LPM_CMP_NE:
|
||||
case IVL_LPM_CMP_EEQ:
|
||||
case IVL_LPM_CMP_NEE:
|
||||
case IVL_LPM_DIVIDE:
|
||||
case IVL_LPM_MOD:
|
||||
case IVL_LPM_MULT:
|
||||
case IVL_LPM_POW:
|
||||
case IVL_LPM_SUB:
|
||||
assert(idx == 0);
|
||||
return net->u_.arith.q;
|
||||
|
||||
@@ -1144,6 +1145,7 @@ extern "C" int ivl_lpm_signed(ivl_lpm_t net)
|
||||
return 0;
|
||||
case IVL_LPM_ABS:
|
||||
case IVL_LPM_ADD:
|
||||
case IVL_LPM_CAST_REAL:
|
||||
case IVL_LPM_CMP_EEQ:
|
||||
case IVL_LPM_CMP_EQ:
|
||||
case IVL_LPM_CMP_GE:
|
||||
@@ -1166,6 +1168,7 @@ extern "C" int ivl_lpm_signed(ivl_lpm_t net)
|
||||
case IVL_LPM_SHIFTL:
|
||||
case IVL_LPM_SHIFTR:
|
||||
return net->u_.shift.signed_flag;
|
||||
case IVL_LPM_CAST_INT:
|
||||
case IVL_LPM_SIGN_EXT: // Sign extend is always signed.
|
||||
return 1;
|
||||
case IVL_LPM_SFUNC:
|
||||
@@ -1435,6 +1438,18 @@ extern int ivl_path_source_negedge(ivl_delaypath_t net)
|
||||
return net->negedge ? 1 : 0;
|
||||
}
|
||||
|
||||
extern "C" const char*ivl_process_file(ivl_process_t net)
|
||||
{
|
||||
assert(net);
|
||||
return net->file.str();
|
||||
}
|
||||
|
||||
extern "C" unsigned ivl_process_lineno(ivl_process_t net)
|
||||
{
|
||||
assert(net);
|
||||
return net->lineno;
|
||||
}
|
||||
|
||||
extern "C" ivl_process_type_t ivl_process_type(ivl_process_t net)
|
||||
{
|
||||
return net->type_;
|
||||
|
||||
+2
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 2000-2008 Stephen Williams (steve@icarus.com)
|
||||
*
|
||||
* This source code is free software; you can redistribute it
|
||||
* and/or modify it in source code form under the terms of the GNU
|
||||
@@ -17,9 +17,6 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifdef HAVE_CVS_IDENT
|
||||
#ident "$Id: $"
|
||||
#endif
|
||||
|
||||
# include "config.h"
|
||||
|
||||
@@ -50,6 +47,7 @@ bool dll_target::process(const NetProcTop*net)
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
FILE_NAME(obj, net);
|
||||
|
||||
/* Save the scope of the process. */
|
||||
obj->scope_ = lookup_scope_(net->scope());
|
||||
@@ -745,4 +743,3 @@ void dll_target::proc_while(const NetWhile*net)
|
||||
net->emit_proc_recurse(this);
|
||||
stmt_cur_ = save_cur_;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
# include <malloc.h>
|
||||
#endif
|
||||
# include <stdlib.h>
|
||||
# include "ivl_assert.h"
|
||||
|
||||
#if defined(__WIN32__)
|
||||
|
||||
@@ -538,6 +539,13 @@ void dll_target::make_scope_param_expr(ivl_parameter_t cur_par, NetExpr*etmp)
|
||||
|
||||
}
|
||||
|
||||
if (expr_ == 0) {
|
||||
cerr << etmp->get_fileline() << ": internal error: "
|
||||
<< "Parameter expression not reduced to constant? "
|
||||
<< *etmp << endl;
|
||||
}
|
||||
ivl_assert(*etmp, expr_);
|
||||
|
||||
cur_par->value = expr_;
|
||||
expr_ = 0;
|
||||
}
|
||||
@@ -1550,6 +1558,71 @@ void dll_target::lpm_clshift(const NetCLShift*net)
|
||||
scope_add_lpm(obj->scope, obj);
|
||||
}
|
||||
|
||||
bool dll_target::lpm_cast_int(const NetCastInt*net)
|
||||
{
|
||||
ivl_lpm_t obj = new struct ivl_lpm_s;
|
||||
obj->type = IVL_LPM_CAST_INT;
|
||||
obj->name = net->name(); // NetCastInt names are permallocated
|
||||
assert(net->scope());
|
||||
obj->scope = find_scope(des_, net->scope());
|
||||
assert(obj->scope);
|
||||
|
||||
obj->width = net->width();
|
||||
|
||||
const Nexus*nex;
|
||||
|
||||
nex = net->pin(0).nexus();
|
||||
assert(nex->t_cookie());
|
||||
|
||||
obj->u_.arith.q = nex->t_cookie();
|
||||
|
||||
nex = net->pin(1).nexus();
|
||||
assert(nex->t_cookie());
|
||||
obj->u_.arith.a = nex->t_cookie();
|
||||
|
||||
nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG);
|
||||
nexus_lpm_add(obj->u_.arith.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ);
|
||||
|
||||
make_lpm_delays_(obj, net);
|
||||
|
||||
scope_add_lpm(obj->scope, obj);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dll_target::lpm_cast_real(const NetCastReal*net)
|
||||
{
|
||||
ivl_lpm_t obj = new struct ivl_lpm_s;
|
||||
obj->type = IVL_LPM_CAST_REAL;
|
||||
obj->name = net->name(); // NetCastReal names are permallocated
|
||||
assert(net->scope());
|
||||
obj->scope = find_scope(des_, net->scope());
|
||||
assert(obj->scope);
|
||||
|
||||
obj->width = 0;
|
||||
obj->u_.arith.signed_flag = net->signed_flag()? 1 : 0;
|
||||
|
||||
const Nexus*nex;
|
||||
|
||||
nex = net->pin(0).nexus();
|
||||
assert(nex->t_cookie());
|
||||
|
||||
obj->u_.arith.q = nex->t_cookie();
|
||||
|
||||
nex = net->pin(1).nexus();
|
||||
assert(nex->t_cookie());
|
||||
obj->u_.arith.a = nex->t_cookie();
|
||||
|
||||
nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG);
|
||||
nexus_lpm_add(obj->u_.arith.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ);
|
||||
|
||||
make_lpm_delays_(obj, net);
|
||||
|
||||
scope_add_lpm(obj->scope, obj);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make out of the NetCompare object an ivl_lpm_s object. The
|
||||
* comparators in ivl_target do not support < or <=, but they can be
|
||||
@@ -1881,7 +1954,7 @@ void dll_target::lpm_mux(const NetMux*net)
|
||||
{
|
||||
ivl_lpm_t obj = new struct ivl_lpm_s;
|
||||
obj->type = IVL_LPM_MUX;
|
||||
obj->name = net->name(); // The NetMux perallocates its name.
|
||||
obj->name = net->name(); // The NetMux permallocates its name.
|
||||
obj->scope = find_scope(des_, net->scope());
|
||||
assert(obj->scope);
|
||||
|
||||
@@ -2301,8 +2374,7 @@ void dll_target::signal(const NetNet*net)
|
||||
object, or creating the sigs_ array if this is the first
|
||||
signal. */
|
||||
obj->scope_ = find_scope(des_, net->scope());
|
||||
obj->file = perm_string::literal("N/A");
|
||||
obj->lineno = 0;
|
||||
FILE_NAME(obj, net);
|
||||
assert(obj->scope_);
|
||||
|
||||
if (obj->scope_->nsigs_ == 0) {
|
||||
|
||||
@@ -76,6 +76,8 @@ struct dll_target : public target_t, public expr_scan_t {
|
||||
void lpm_abs(const NetAbs*);
|
||||
void lpm_add_sub(const NetAddSub*);
|
||||
bool lpm_array_dq(const NetArrayDq*);
|
||||
bool lpm_cast_int(const NetCastInt*);
|
||||
bool lpm_cast_real(const NetCastReal*);
|
||||
void lpm_clshift(const NetCLShift*);
|
||||
void lpm_compare(const NetCompare*);
|
||||
void lpm_divide(const NetDivide*);
|
||||
@@ -537,7 +539,7 @@ struct ivl_parameter_s {
|
||||
unsigned lineno;
|
||||
};
|
||||
/*
|
||||
* All we know about a process it its type (initial or always) and the
|
||||
* All we know about a process is its type (initial or always) and the
|
||||
* single statement that is it. A process also has a scope, although
|
||||
* that generally only matters for VPI calls.
|
||||
*/
|
||||
@@ -545,6 +547,8 @@ struct ivl_process_s {
|
||||
ivl_process_type_t type_;
|
||||
ivl_scope_t scope_;
|
||||
ivl_statement_t stmt_;
|
||||
perm_string file;
|
||||
unsigned lineno;
|
||||
|
||||
struct ivl_attribute_s*attr;
|
||||
unsigned nattr;
|
||||
@@ -761,4 +765,16 @@ static inline void FILE_NAME(ivl_switch_t net, const LineInfo*info)
|
||||
net->lineno = info->get_lineno();
|
||||
}
|
||||
|
||||
static inline void FILE_NAME(ivl_process_t net, const LineInfo*info)
|
||||
{
|
||||
net->file = info->get_file();
|
||||
net->lineno = info->get_lineno();
|
||||
}
|
||||
|
||||
static inline void FILE_NAME(ivl_signal_t net, const LineInfo*info)
|
||||
{
|
||||
net->file = info->get_file();
|
||||
net->lineno = info->get_lineno();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -107,6 +107,20 @@ bool target_t::lpm_array_dq(const NetArrayDq*)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool target_t::lpm_cast_int(const NetCastInt*)
|
||||
{
|
||||
cerr << "target (" << typeid(*this).name() << "): "
|
||||
"Unhandled NetCastInt." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool target_t::lpm_cast_real(const NetCastReal*)
|
||||
{
|
||||
cerr << "target (" << typeid(*this).name() << "): "
|
||||
"Unhandled NetCastReal." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
void target_t::lpm_clshift(const NetCLShift*)
|
||||
{
|
||||
cerr << "target (" << typeid(*this).name() << "): "
|
||||
|
||||
@@ -72,6 +72,8 @@ struct target_t {
|
||||
virtual void lpm_add_sub(const NetAddSub*);
|
||||
virtual bool lpm_array_dq(const NetArrayDq*);
|
||||
virtual void lpm_clshift(const NetCLShift*);
|
||||
virtual bool lpm_cast_int(const NetCastInt*);
|
||||
virtual bool lpm_cast_real(const NetCastReal*);
|
||||
virtual void lpm_compare(const NetCompare*);
|
||||
virtual void lpm_divide(const NetDivide*);
|
||||
virtual void lpm_modulo(const NetModulo*);
|
||||
|
||||
@@ -160,7 +160,7 @@ device pins are connected.
|
||||
.SH EXAMPLES
|
||||
|
||||
.TB 8
|
||||
.I COMPILING WITH XILINX FOUNDATION/iSE
|
||||
.I COMPILING WITH XILINX FOUNDATION/ISE
|
||||
Compile a single-file design with command line tools like so:
|
||||
|
||||
.nf
|
||||
|
||||
@@ -178,6 +178,11 @@ void show_unary_expression(ivl_expr_t net, unsigned ind)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ivl_expr_opcode(net) == '!' && ivl_expr_type(net)==IVL_VT_REAL) {
|
||||
fprintf(out, "%*sERROR: Real argument to unary ! !?\n", ind,"");
|
||||
stub_errors += 1;
|
||||
}
|
||||
|
||||
fprintf(out, "%*s<unary \"%s\" width=%u, %s, type=%s>\n", ind, "",
|
||||
name, width, sign, vt);
|
||||
show_expression(ivl_expr_oper1(net), ind+4);
|
||||
|
||||
@@ -240,6 +240,56 @@ static void show_lpm_array(ivl_lpm_t net)
|
||||
}
|
||||
}
|
||||
|
||||
static void show_lpm_cast_int(ivl_lpm_t net)
|
||||
{
|
||||
unsigned width = ivl_lpm_width(net);
|
||||
|
||||
fprintf(out, " LPM_CAST_INT %s: <width=%u>\n",
|
||||
ivl_lpm_basename(net), width);
|
||||
|
||||
ivl_nexus_t q = ivl_lpm_q(net,0);
|
||||
ivl_nexus_t a = ivl_lpm_data(net,0);
|
||||
fprintf(out, " O: %s\n", ivl_nexus_name(ivl_lpm_q(net,0)));
|
||||
fprintf(out, " A: %s\n", ivl_nexus_name(ivl_lpm_data(net,0)));
|
||||
|
||||
if (type_of_nexus(q) == IVL_VT_REAL) {
|
||||
fprintf(out, " ERROR: Data type of Q is %s, expecting !real\n",
|
||||
data_type_string(type_of_nexus(q)));
|
||||
stub_errors += 1;
|
||||
}
|
||||
|
||||
if (type_of_nexus(a) != IVL_VT_REAL) {
|
||||
fprintf(out, " ERROR: Data type of A is %s, expecting real\n",
|
||||
data_type_string(type_of_nexus(a)));
|
||||
stub_errors += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void show_lpm_cast_real(ivl_lpm_t net)
|
||||
{
|
||||
unsigned width = ivl_lpm_width(net);
|
||||
|
||||
fprintf(out, " LPM_CAST_REAL %s: <width=%u>\n",
|
||||
ivl_lpm_basename(net), width);
|
||||
|
||||
ivl_nexus_t q = ivl_lpm_q(net,0);
|
||||
ivl_nexus_t a = ivl_lpm_data(net,0);
|
||||
fprintf(out, " O: %s\n", ivl_nexus_name(ivl_lpm_q(net,0)));
|
||||
fprintf(out, " A: %s\n", ivl_nexus_name(ivl_lpm_data(net,0)));
|
||||
|
||||
if (type_of_nexus(q) != IVL_VT_REAL) {
|
||||
fprintf(out, " ERROR: Data type of Q is %s, expecting real\n",
|
||||
data_type_string(type_of_nexus(q)));
|
||||
stub_errors += 1;
|
||||
}
|
||||
|
||||
if (type_of_nexus(a) == IVL_VT_REAL) {
|
||||
fprintf(out, " ERROR: Data type of A is %s, expecting !real\n",
|
||||
data_type_string(type_of_nexus(a)));
|
||||
stub_errors += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void show_lpm_divide(ivl_lpm_t net)
|
||||
{
|
||||
unsigned width = ivl_lpm_width(net);
|
||||
@@ -802,6 +852,14 @@ static void show_lpm(ivl_lpm_t net)
|
||||
show_lpm_array(net);
|
||||
break;
|
||||
|
||||
case IVL_LPM_CAST_INT:
|
||||
show_lpm_cast_int(net);
|
||||
break;
|
||||
|
||||
case IVL_LPM_CAST_REAL:
|
||||
show_lpm_cast_real(net);
|
||||
break;
|
||||
|
||||
case IVL_LPM_DIVIDE:
|
||||
show_lpm_divide(net);
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
#
|
||||
# This source code is free software; you can redistribute it
|
||||
# and/or modify it in source code form under the terms of the GNU
|
||||
# Library 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 Library General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Library General Public
|
||||
# License along with this program; if not, write to the Free
|
||||
# Software Foundation, Inc.,
|
||||
# 59 Temple Place - Suite 330
|
||||
# Boston, MA 02111-1307, USA
|
||||
#
|
||||
SHELL = /bin/sh
|
||||
|
||||
VERSION = 0.0
|
||||
|
||||
prefix = @prefix@
|
||||
prefix = @prefix@
|
||||
exec_prefix = @exec_prefix@
|
||||
srcdir = @srcdir@
|
||||
|
||||
VPATH = $(srcdir)
|
||||
|
||||
bindir = @bindir@
|
||||
libdir = @libdir@
|
||||
includedir = $(prefix)/include
|
||||
|
||||
CXX = @CXX@
|
||||
INSTALL = @INSTALL@
|
||||
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
|
||||
CPPFLAGS = @ident_support@ -I. -I$(srcdir)/.. @CPPFLAGS@ @DEFS@ @PICFLAG@
|
||||
CXXFLAGS = -Wall @CXXFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
|
||||
all: dep vhdl.tgt vhdl.conf
|
||||
|
||||
dep:
|
||||
mkdir dep
|
||||
|
||||
%.o: %.cc
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -MD -c $< -o $*.o
|
||||
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 support.o cast.o logic.o
|
||||
|
||||
ifeq (@WIN32@,yes)
|
||||
TGTLDFLAGS=-L.. -livl
|
||||
TGTDEPLIBS=../libivl.a
|
||||
else
|
||||
TGTLDFLAGS=
|
||||
TGTDEPLIBS=
|
||||
endif
|
||||
|
||||
vhdl.tgt: $O $(TGTDEPLIBS)
|
||||
$(CXX) @shared@ -o $@ $O $(TGTLDFLAGS)
|
||||
|
||||
Makefile: Makefile.in config.status
|
||||
./config.status
|
||||
|
||||
clean:
|
||||
rm -rf $(O) dep vhdl.tgt
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile config.status config.log config.cache vhdl_config.h
|
||||
|
||||
check: all
|
||||
|
||||
install: all installdirs $(libdir)/ivl/vhdl.tgt $(libdir)/ivl/vhdl.conf
|
||||
|
||||
$(libdir)/ivl/vhdl.tgt: ./vhdl.tgt
|
||||
$(INSTALL_PROGRAM) ./vhdl.tgt $(libdir)/ivl/vhdl.tgt
|
||||
|
||||
$(libdir)/ivl/vhdl.conf: vhdl.conf
|
||||
$(INSTALL_DATA) $< $(libdir)/ivl/vhdl.conf
|
||||
|
||||
installdirs: ../mkinstalldirs
|
||||
$(srcdir)/../mkinstalldirs $(libdir)/ivl
|
||||
|
||||
uninstall:
|
||||
rm -f $(libdir)/ivl/vhdl.tgt $(libdir)/ivl/vhdl.conf
|
||||
|
||||
|
||||
-include $(patsubst %.o, dep/%.d, $O)
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Generate code to convert between VHDL types.
|
||||
*
|
||||
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "vhdl_syntax.hh"
|
||||
|
||||
#include "vhdl_target.h"
|
||||
#include "support.hh"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
vhdl_expr *vhdl_expr::cast(const vhdl_type *to)
|
||||
{
|
||||
//std::cout << "Cast: from=" << type_->get_string()
|
||||
// << " (" << type_->get_width() << ") "
|
||||
// << " to=" << to->get_string() << " ("
|
||||
// << to->get_width() << ")" << std::endl;
|
||||
|
||||
if (to->get_name() == type_->get_name()) {
|
||||
if (to->get_width() == type_->get_width())
|
||||
return this; // Identical
|
||||
else
|
||||
return resize(to->get_width());
|
||||
}
|
||||
else if (to->get_name() == VHDL_TYPE_BOOLEAN) {
|
||||
if (type_->get_name() == VHDL_TYPE_STD_LOGIC) {
|
||||
// '1' is true all else are false
|
||||
vhdl_const_bit *one = new vhdl_const_bit('1');
|
||||
return new vhdl_binop_expr
|
||||
(this, VHDL_BINOP_EQ, one, vhdl_type::boolean());
|
||||
}
|
||||
else if (type_->get_name() == VHDL_TYPE_UNSIGNED) {
|
||||
// Need to use a support function for this conversion
|
||||
require_support_function(SF_UNSIGNED_TO_BOOLEAN);
|
||||
|
||||
vhdl_fcall *conv =
|
||||
new vhdl_fcall(support_function::function_name(SF_UNSIGNED_TO_BOOLEAN),
|
||||
vhdl_type::boolean());
|
||||
conv->add_expr(this);
|
||||
return conv;
|
||||
}
|
||||
else if (type_->get_name() == VHDL_TYPE_SIGNED) {
|
||||
require_support_function(SF_SIGNED_TO_BOOLEAN);
|
||||
|
||||
vhdl_fcall *conv =
|
||||
new vhdl_fcall(support_function::function_name(SF_SIGNED_TO_BOOLEAN),
|
||||
vhdl_type::boolean());
|
||||
conv->add_expr(this);
|
||||
return conv;
|
||||
}
|
||||
else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
else if (to->get_name() == VHDL_TYPE_INTEGER) {
|
||||
vhdl_fcall *conv;
|
||||
if (type_->get_name() == VHDL_TYPE_STD_LOGIC) {
|
||||
require_support_function(SF_LOGIC_TO_INTEGER);
|
||||
conv = new vhdl_fcall(support_function::function_name(SF_LOGIC_TO_INTEGER),
|
||||
vhdl_type::integer());
|
||||
}
|
||||
else
|
||||
conv = new vhdl_fcall("To_Integer", new vhdl_type(*to));
|
||||
|
||||
conv->add_expr(this);
|
||||
|
||||
return conv;
|
||||
}
|
||||
else if ((to->get_name() == VHDL_TYPE_UNSIGNED
|
||||
|| to->get_name() == VHDL_TYPE_SIGNED
|
||||
|| to->get_name() == VHDL_TYPE_STD_LOGIC_VECTOR) &&
|
||||
type_->get_name() == VHDL_TYPE_STD_LOGIC) {
|
||||
|
||||
vhdl_expr *others = to->get_width() == 1 ? NULL : new vhdl_const_bit('0');
|
||||
vhdl_bit_spec_expr *bs =
|
||||
new vhdl_bit_spec_expr(new vhdl_type(*to), others);
|
||||
bs->add_bit(0, this);
|
||||
|
||||
return bs;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
vhdl_expr *vhdl_const_int::cast(const vhdl_type *to)
|
||||
{
|
||||
if (to->get_name() == VHDL_TYPE_SIGNED
|
||||
|| to->get_name() == VHDL_TYPE_UNSIGNED) {
|
||||
|
||||
const char *fname = to->get_name() == VHDL_TYPE_SIGNED
|
||||
? "To_Signed" : "To_Unsigned";
|
||||
vhdl_fcall *conv = new vhdl_fcall(fname, new vhdl_type(*to));
|
||||
conv->add_expr(this);
|
||||
conv->add_expr(new vhdl_const_int(to->get_width()));
|
||||
|
||||
return conv;
|
||||
}
|
||||
else
|
||||
return vhdl_expr::cast(to);
|
||||
}
|
||||
|
||||
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 if (to->get_name() == VHDL_TYPE_BOOLEAN)
|
||||
return new vhdl_const_bool(bit_ == '1');
|
||||
else
|
||||
return vhdl_expr::cast(to);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
dnl Process this file with autoconf to produce a configure script.
|
||||
AC_INIT(vhdl.cc)
|
||||
AC_CONFIG_HEADER(vhdl_config.h)
|
||||
|
||||
dnl Checks for programs.
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
|
||||
AC_PROG_INSTALL
|
||||
|
||||
AC_CANONICAL_HOST
|
||||
# $host
|
||||
|
||||
# Combined check for Microsoft-related bogosities; sets WIN32 if found
|
||||
AX_WIN32
|
||||
|
||||
AC_CHECK_HEADERS(malloc.h stdint.h inttypes.h)
|
||||
|
||||
# may modify CPPFLAGS and CFLAGS
|
||||
AX_CPP_PRECOMP
|
||||
|
||||
# Compiler option for position independent code, needed when making shared objects.
|
||||
AX_C_PICFLAG
|
||||
|
||||
# linker options when building a shared library
|
||||
AX_LD_SHAREDLIB_OPTS
|
||||
|
||||
AX_CPP_IDENT
|
||||
|
||||
AC_OUTPUT(Makefile)
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* VHDL implementation of $display.
|
||||
*
|
||||
* 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 <cstring>
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <sstream>
|
||||
|
||||
static const char *DISPLAY_LINE = "Verilog_Display_Line";
|
||||
|
||||
/*
|
||||
* Write a VHDL expression into the current display line.
|
||||
*/
|
||||
static void display_write(stmt_container *container, vhdl_expr *expr)
|
||||
{
|
||||
vhdl_pcall_stmt *write = new vhdl_pcall_stmt("Write");
|
||||
vhdl_var_ref *ref =
|
||||
new vhdl_var_ref(DISPLAY_LINE, vhdl_type::line());
|
||||
write->add_expr(ref);
|
||||
|
||||
vhdl_type_name_t type = expr->get_type()->get_name();
|
||||
if (type == VHDL_TYPE_SIGNED || type == VHDL_TYPE_UNSIGNED) {
|
||||
vhdl_type integer(VHDL_TYPE_INTEGER);
|
||||
write->add_expr(expr->cast(&integer));
|
||||
}
|
||||
else if (type != VHDL_TYPE_STRING) {
|
||||
// Need to add a call to Type'Image for types not
|
||||
// supported by std.textio
|
||||
std::string name(expr->get_type()->get_string());
|
||||
name += "'Image";
|
||||
|
||||
vhdl_fcall *cast
|
||||
= new vhdl_fcall(name.c_str(), vhdl_type::string());
|
||||
cast->add_expr(expr);
|
||||
|
||||
write->add_expr(cast);
|
||||
}
|
||||
else
|
||||
write->add_expr(expr);
|
||||
|
||||
container->add_stmt(write);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the value of DISPLAY_LINE to the output.
|
||||
*/
|
||||
static void display_line(stmt_container *container)
|
||||
{
|
||||
vhdl_pcall_stmt *write_line = new vhdl_pcall_stmt("WriteLine");
|
||||
vhdl_var_ref *output_ref =
|
||||
new vhdl_var_ref("std.textio.Output", new vhdl_type(VHDL_TYPE_FILE));
|
||||
write_line->add_expr(output_ref);
|
||||
vhdl_var_ref *ref =
|
||||
new vhdl_var_ref(DISPLAY_LINE, vhdl_type::line());
|
||||
write_line->add_expr(ref);
|
||||
container->add_stmt(write_line);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse an octal escape sequence.
|
||||
*/
|
||||
static char parse_octal(const char *p)
|
||||
{
|
||||
assert(*p && *(p+1) && *(p+2));
|
||||
assert(isdigit(*p) && isdigit(*(p+1)) && isdigit(*(p+1)));
|
||||
|
||||
return (*p - '0') * 64
|
||||
+ (*(p+1) - '0') * 8
|
||||
+ (*(p+2) - '0') * 1;
|
||||
}
|
||||
|
||||
static void flush_string(std::ostringstream &ss, stmt_container *container)
|
||||
{
|
||||
display_write(container, new vhdl_const_string(ss.str().c_str()));
|
||||
|
||||
// Clear the stream
|
||||
ss.str("");
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate VHDL for the $display system task.
|
||||
* This is implemented using the functions in std.textio. Each
|
||||
* parameter is written to a line variable in the process and
|
||||
* then the line is written to the special variable `Output'
|
||||
* (which represents the console). Subsequent $displays will
|
||||
* use the same line variable.
|
||||
*
|
||||
* It's possible, although quite unlikely, that there will be
|
||||
* name collision with an existing variable called
|
||||
* `Verilog_Display_Line' -- do something about this?
|
||||
*/
|
||||
int draw_stask_display(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt, bool newline)
|
||||
{
|
||||
if (!proc->get_scope()->have_declared(DISPLAY_LINE)) {
|
||||
vhdl_var_decl *line_var =
|
||||
new vhdl_var_decl(DISPLAY_LINE, vhdl_type::line());
|
||||
line_var->set_comment("For generating $display output");
|
||||
proc->get_scope()->add_decl(line_var);
|
||||
}
|
||||
|
||||
// Write the data into the line
|
||||
int count = ivl_stmt_parm_count(stmt), i = 0;
|
||||
while (i < count) {
|
||||
// $display may have an empty parameter, in which case
|
||||
// the expression will be null
|
||||
// The behaviour here seems to be to output a space
|
||||
ivl_expr_t net = ivl_stmt_parm(stmt, i++);
|
||||
if (net == NULL) {
|
||||
display_write(container, new vhdl_const_string(" "));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ivl_expr_type(net) == IVL_EX_STRING) {
|
||||
ostringstream ss;
|
||||
for (const char *p = ivl_expr_string(net); *p; p++) {
|
||||
if (*p == '\\') {
|
||||
// Octal escape
|
||||
char ch = parse_octal(p+1);
|
||||
if (ch == '\n') {
|
||||
flush_string(ss, container);
|
||||
display_line(container);
|
||||
}
|
||||
else
|
||||
ss << ch;
|
||||
p += 3;
|
||||
}
|
||||
else if (*p == '%' && *(++p) != '%') {
|
||||
flush_string(ss, container);
|
||||
|
||||
// Skip over width for now
|
||||
while (isdigit(*p)) ++p;
|
||||
|
||||
// TODO: This needs to be re-written
|
||||
// ...it does not handle format codes at all!
|
||||
// Unfortunately, there is no printf-like
|
||||
// function in VHDL
|
||||
|
||||
assert(i < count);
|
||||
ivl_expr_t net = ivl_stmt_parm(stmt, i++);
|
||||
assert(net);
|
||||
|
||||
vhdl_expr *base = translate_expr(net);
|
||||
if (NULL == base)
|
||||
return 1;
|
||||
|
||||
display_write(container, base);
|
||||
}
|
||||
else
|
||||
ss << *p;
|
||||
}
|
||||
|
||||
display_write(container, new vhdl_const_string(ss.str().c_str()));
|
||||
}
|
||||
else {
|
||||
vhdl_expr *base = translate_expr(net);
|
||||
if (NULL == base)
|
||||
return 1;
|
||||
|
||||
display_write(container, base);
|
||||
}
|
||||
}
|
||||
|
||||
if (newline)
|
||||
display_line(container);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,560 @@
|
||||
/*
|
||||
* 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 "support.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
/*
|
||||
* Change the signedness of a vector.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* A reference to a signal in an expression. It's assumed that the
|
||||
* signal has already been defined elsewhere.
|
||||
*/
|
||||
static vhdl_var_ref *translate_signal(ivl_expr_t e)
|
||||
{
|
||||
ivl_signal_t sig = ivl_expr_signal(e);
|
||||
|
||||
const vhdl_scope *scope = find_scope_for_signal(sig);
|
||||
assert(scope);
|
||||
|
||||
const char *renamed = get_renamed_signal(sig).c_str();
|
||||
|
||||
vhdl_decl *decl = scope->get_decl(renamed);
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_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)
|
||||
{
|
||||
vhdl_expr *result;
|
||||
if (operand->get_type()->get_name() == VHDL_TYPE_STD_LOGIC)
|
||||
result = operand;
|
||||
else {
|
||||
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));
|
||||
|
||||
result = fcall;
|
||||
}
|
||||
|
||||
if (neg)
|
||||
return new vhdl_unaryop_expr(VHDL_UNARYOP_NOT, result,
|
||||
vhdl_type::std_logic());
|
||||
else
|
||||
return result;
|
||||
}
|
||||
|
||||
static vhdl_expr *translate_unary(ivl_expr_t e)
|
||||
{
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
operand = change_signedness(operand, false);
|
||||
}
|
||||
|
||||
char opcode = ivl_expr_opcode(e);
|
||||
switch (opcode) {
|
||||
case '!':
|
||||
case '~':
|
||||
return new vhdl_unaryop_expr
|
||||
(VHDL_UNARYOP_NOT, operand, new vhdl_type(*operand->get_type()));
|
||||
case '-':
|
||||
operand = change_signedness(operand, true);
|
||||
return new vhdl_unaryop_expr
|
||||
(VHDL_UNARYOP_NEG, operand, new vhdl_type(*operand->get_type()));
|
||||
case 'N': // NOR
|
||||
return translate_reduction(SF_REDUCE_OR, true, operand);
|
||||
case '|':
|
||||
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));
|
||||
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)
|
||||
{
|
||||
// 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);
|
||||
|
||||
vhdl_type *rtype = new vhdl_type(*lhs->get_type());
|
||||
return new vhdl_binop_expr(lhs, op, rhs, rtype);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
int lwidth = lhs->get_type()->get_width();
|
||||
int rwidth = rhs->get_type()->get_width();
|
||||
int result_width = ivl_expr_width(e);
|
||||
|
||||
// For === and !== we need to compare std_logic_vectors
|
||||
// rather than signeds
|
||||
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();
|
||||
bool vectorop =
|
||||
(ltype == VHDL_TYPE_SIGNED || ltype == VHDL_TYPE_UNSIGNED) &&
|
||||
(rtype == VHDL_TYPE_SIGNED || rtype == VHDL_TYPE_UNSIGNED);
|
||||
|
||||
// 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;
|
||||
switch (ivl_expr_opcode(e)) {
|
||||
case '+':
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_ADD);
|
||||
break;
|
||||
case '-':
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_SUB);
|
||||
break;
|
||||
case '*':
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_MULT);
|
||||
break;
|
||||
case '/':
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_DIV);
|
||||
break;
|
||||
case '%':
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_MOD);
|
||||
break;
|
||||
case 'e':
|
||||
result = translate_relation(lhs, rhs, VHDL_BINOP_EQ);
|
||||
break;
|
||||
case 'E':
|
||||
if (vectorop)
|
||||
result = translate_relation(lhs->cast(&std_logic_vector),
|
||||
rhs->cast(&std_logic_vector), VHDL_BINOP_EQ);
|
||||
else
|
||||
result = translate_relation(lhs, rhs, VHDL_BINOP_EQ);
|
||||
break;
|
||||
case 'n':
|
||||
result = translate_relation(lhs, rhs, VHDL_BINOP_NEQ);
|
||||
break;
|
||||
case 'N':
|
||||
if (vectorop)
|
||||
result = translate_relation(lhs->cast(&std_logic_vector),
|
||||
rhs->cast(&std_logic_vector), VHDL_BINOP_NEQ);
|
||||
else
|
||||
result = translate_relation(lhs, rhs, VHDL_BINOP_NEQ);
|
||||
break;
|
||||
case '&': // Bitwise AND
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_AND);
|
||||
break;
|
||||
case 'a': // Logical AND
|
||||
result = translate_logical(lhs, rhs, VHDL_BINOP_AND);
|
||||
break;
|
||||
case 'A': // Bitwise NAND
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_NAND);
|
||||
break;
|
||||
case 'O': // Bitwise NOR
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_NOR);
|
||||
break;
|
||||
case 'X': // Bitwise XNOR
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_XNOR);
|
||||
break;
|
||||
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;
|
||||
case '<':
|
||||
result = translate_relation(lhs, rhs, VHDL_BINOP_LT);
|
||||
break;
|
||||
case 'L':
|
||||
result = translate_relation(lhs, rhs, VHDL_BINOP_LEQ);
|
||||
break;
|
||||
case '>':
|
||||
result = translate_relation(lhs, rhs, VHDL_BINOP_GT);
|
||||
break;
|
||||
case 'G':
|
||||
result = translate_relation(lhs, rhs, VHDL_BINOP_GEQ);
|
||||
break;
|
||||
case 'l':
|
||||
result = translate_shift(lhs, rhs, VHDL_BINOP_SL);
|
||||
break;
|
||||
case 'r':
|
||||
result = translate_shift(lhs, rhs, VHDL_BINOP_SR);
|
||||
break;
|
||||
case '^':
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_XOR);
|
||||
break;
|
||||
default:
|
||||
error("No translation for binary opcode '%c'\n",
|
||||
ivl_expr_opcode(e));
|
||||
delete lhs;
|
||||
delete rhs;
|
||||
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;
|
||||
|
||||
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_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)
|
||||
{
|
||||
vhdl_var_ref *from =
|
||||
dynamic_cast<vhdl_var_ref*>(translate_expr(ivl_expr_oper1(e)));
|
||||
if (NULL == from) {
|
||||
error("Can only select from variable reference");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ivl_expr_t o2 = ivl_expr_oper2(e);
|
||||
if (o2) {
|
||||
vhdl_expr *base = translate_expr(ivl_expr_oper2(e));
|
||||
if (NULL == base)
|
||||
return NULL;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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_name(parentscope));
|
||||
assert(parent_ent);
|
||||
|
||||
const char *funcname = ivl_scope_tname(defscope);
|
||||
|
||||
vhdl_type *rettype = expr_to_vhdl_type(e);
|
||||
vhdl_fcall *fcall = new vhdl_fcall(funcname, rettype);
|
||||
|
||||
return translate_parms<vhdl_fcall>(fcall, e);
|
||||
}
|
||||
|
||||
static vhdl_expr *translate_ternary(ivl_expr_t e)
|
||||
{
|
||||
support_function_t sf;
|
||||
int width = ivl_expr_width(e);
|
||||
bool issigned = ivl_expr_signed(e) != 0;
|
||||
if (width == 1)
|
||||
sf = SF_TERNARY_LOGIC;
|
||||
else if (issigned)
|
||||
sf = SF_TERNARY_SIGNED;
|
||||
else
|
||||
sf = SF_TERNARY_UNSIGNED;
|
||||
|
||||
require_support_function(sf);
|
||||
|
||||
vhdl_expr *test = translate_expr(ivl_expr_oper1(e));
|
||||
vhdl_expr *true_part = translate_expr(ivl_expr_oper2(e));
|
||||
vhdl_expr *false_part = translate_expr(ivl_expr_oper3(e));
|
||||
if (!test || !true_part || !false_part)
|
||||
return NULL;
|
||||
|
||||
vhdl_type boolean(VHDL_TYPE_BOOLEAN);
|
||||
test = test->cast(&boolean);
|
||||
|
||||
vhdl_fcall *fcall =
|
||||
new vhdl_fcall(support_function::function_name(sf),
|
||||
vhdl_type::type_for(width, issigned));
|
||||
fcall->add_expr(test);
|
||||
fcall->add_expr(true_part);
|
||||
fcall->add_expr(false_part);
|
||||
|
||||
return fcall;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
int nrepeat = ivl_expr_repeat(e);
|
||||
while (nrepeat--)
|
||||
translate_parms<vhdl_binop_expr>(concat, e);
|
||||
|
||||
return concat;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
vhdl_expr *translate_expr(ivl_expr_t e)
|
||||
{
|
||||
assert(e);
|
||||
ivl_expr_type_t type = ivl_expr_type(e);
|
||||
|
||||
switch (type) {
|
||||
case IVL_EX_STRING:
|
||||
return translate_string(e);
|
||||
case IVL_EX_SIGNAL:
|
||||
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:
|
||||
return translate_binary(e);
|
||||
case IVL_EX_SELECT:
|
||||
return translate_select(e);
|
||||
case IVL_EX_UFUNC:
|
||||
return translate_ufunc(e);
|
||||
case IVL_EX_TERNARY:
|
||||
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);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate an expression into a time. This is achieved simply
|
||||
* by multiplying the expression by 1ns.
|
||||
*/
|
||||
vhdl_expr *translate_time_expr(ivl_expr_t e)
|
||||
{
|
||||
vhdl_expr *time = translate_expr(e);
|
||||
if (NULL == time)
|
||||
return NULL;
|
||||
|
||||
vhdl_type integer(VHDL_TYPE_INTEGER);
|
||||
time = time->cast(&integer);
|
||||
|
||||
vhdl_expr *ns1 = new vhdl_const_time(1, TIME_UNIT_NS);
|
||||
return new vhdl_binop_expr(time, VHDL_BINOP_MULT, ns1,
|
||||
vhdl_type::time());
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* VHDL code generation for logic devices.
|
||||
*
|
||||
* 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 "vhdl_element.hh"
|
||||
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
/*
|
||||
* Convert the inputs of a logic gate to a binary expression.
|
||||
*/
|
||||
static vhdl_expr *inputs_to_expr(vhdl_scope *scope, vhdl_binop_t op,
|
||||
ivl_net_logic_t log)
|
||||
{
|
||||
// Not always std_logic but this is probably OK since
|
||||
// the program has already been type checked
|
||||
vhdl_binop_expr *gate =
|
||||
new vhdl_binop_expr(op, vhdl_type::std_logic());
|
||||
|
||||
int npins = ivl_logic_pins(log);
|
||||
for (int i = 1; i < npins; i++) {
|
||||
ivl_nexus_t input = ivl_logic_pin(log, i);
|
||||
gate->add_expr(nexus_to_var_ref(scope, input));
|
||||
}
|
||||
|
||||
return gate;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a gate intput to an unary expression.
|
||||
*/
|
||||
static vhdl_expr *input_to_expr(vhdl_scope *scope, vhdl_unaryop_t op,
|
||||
ivl_net_logic_t log)
|
||||
{
|
||||
ivl_nexus_t input = ivl_logic_pin(log, 1);
|
||||
assert(input);
|
||||
|
||||
vhdl_expr *operand = nexus_to_var_ref(scope, input);
|
||||
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_var_ref(arch->get_scope(), ivl_logic_pin(log, 1));
|
||||
assert(val);
|
||||
|
||||
vhdl_expr *sel = nexus_to_var_ref(arch->get_scope(), ivl_logic_pin(log, 2));
|
||||
assert(val);
|
||||
|
||||
vhdl_expr *cmp;
|
||||
if (ivl_logic_width(log) == 1) {
|
||||
vhdl_expr *on = new vhdl_const_bit(if0 ? '0' : '1');
|
||||
cmp = new vhdl_binop_expr(sel, VHDL_BINOP_EQ, on, NULL);
|
||||
}
|
||||
else {
|
||||
vhdl_expr *zero = (new vhdl_const_int(0))->cast(sel->get_type());
|
||||
vhdl_binop_t op = if0 ? VHDL_BINOP_EQ : VHDL_BINOP_NEQ;
|
||||
cmp = new vhdl_binop_expr(sel, op, zero, 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_expr *z = new vhdl_const_bit(zbit);
|
||||
if (ivl_logic_width(log) > 1)
|
||||
z = new vhdl_bit_spec_expr(NULL, z);
|
||||
vhdl_cassign_stmt *cass = new vhdl_cassign_stmt(lhs, z);
|
||||
cass->add_condition(val, cmp);
|
||||
|
||||
arch->add_stmt(cass);
|
||||
}
|
||||
|
||||
static void comb_udp_logic(vhdl_arch *arch, ivl_net_logic_t log)
|
||||
{
|
||||
ivl_udp_t udp = ivl_logic_udp(log);
|
||||
|
||||
// As with regular case statements, the expression in a
|
||||
// `with .. select' statement must be "locally static".
|
||||
// This is achieved by first combining the inputs into
|
||||
// a temporary
|
||||
|
||||
ostringstream ss;
|
||||
ss << ivl_logic_basename(log) << "_Tmp";
|
||||
int msb = ivl_udp_nin(udp) - 1;
|
||||
vhdl_type *tmp_type = vhdl_type::std_logic_vector(msb, 0);
|
||||
vhdl_signal_decl *tmp_decl =
|
||||
new vhdl_signal_decl(ss.str().c_str(), tmp_type);
|
||||
arch->get_scope()->add_decl(tmp_decl);
|
||||
|
||||
int nin = ivl_udp_nin(udp);
|
||||
vhdl_expr *tmp_rhs;
|
||||
if (nin == 1) {
|
||||
tmp_rhs = nexus_to_var_ref(arch->get_scope(), ivl_logic_pin(log, 1));
|
||||
tmp_rhs = tmp_rhs->cast(tmp_type);
|
||||
}
|
||||
else
|
||||
tmp_rhs = inputs_to_expr(arch->get_scope(), VHDL_BINOP_CONCAT, log);
|
||||
|
||||
ss.str("");
|
||||
ss << "Input to " << ivl_logic_basename(log) << " "
|
||||
<< ivl_udp_name(udp) << " UDP";
|
||||
tmp_decl->set_comment(ss.str());
|
||||
|
||||
vhdl_var_ref *tmp_ref =
|
||||
new vhdl_var_ref(tmp_decl->get_name().c_str(), NULL);
|
||||
arch->add_stmt(new vhdl_cassign_stmt(tmp_ref, tmp_rhs));
|
||||
|
||||
// Now we can implement the UDP as a `with .. select' statement
|
||||
// by reading values out of the table
|
||||
ivl_nexus_t output_nex = ivl_logic_pin(log, 0);
|
||||
vhdl_var_ref *out = nexus_to_var_ref(arch->get_scope(), output_nex);
|
||||
vhdl_with_select_stmt *ws =
|
||||
new vhdl_with_select_stmt(new vhdl_var_ref(*tmp_ref), out);
|
||||
|
||||
int nrows = ivl_udp_rows(udp);
|
||||
for (int i = 0; i < nrows; i++) {
|
||||
const char *row = ivl_udp_row(udp, i);
|
||||
|
||||
vhdl_expr *value = new vhdl_const_bit(row[nin]);
|
||||
vhdl_expr *cond = new vhdl_const_bits(row, nin, false);
|
||||
|
||||
ivl_expr_t delay_ex = ivl_logic_delay(log, 1);
|
||||
vhdl_expr *delay = NULL;
|
||||
if (delay_ex)
|
||||
delay = translate_time_expr(delay_ex);
|
||||
|
||||
ws->add_condition(value, cond, delay);
|
||||
}
|
||||
|
||||
ss.str("");
|
||||
ss << "UDP " << ivl_udp_name(udp);
|
||||
ws->set_comment(ss.str());
|
||||
|
||||
arch->add_stmt(ws);
|
||||
}
|
||||
|
||||
static void seq_udp_logic(vhdl_arch *arch, ivl_net_logic_t log)
|
||||
{
|
||||
ivl_udp_t udp = ivl_logic_udp(log);
|
||||
|
||||
// These will be translated to a process with a single
|
||||
// case statement
|
||||
|
||||
vhdl_process *proc = new vhdl_process(ivl_logic_basename(log));
|
||||
|
||||
ostringstream ss;
|
||||
ss << "Generated from UDP " << ivl_udp_name(udp);
|
||||
proc->set_comment(ss.str().c_str());
|
||||
|
||||
// Create a variable to hold the concatenation of the inputs
|
||||
int msb = ivl_udp_nin(udp) - 1;
|
||||
vhdl_type *tmp_type = vhdl_type::std_logic_vector(msb, 0);
|
||||
proc->get_scope()->add_decl(new vhdl_var_decl("UDP_Inputs", tmp_type));
|
||||
|
||||
// Concatenate the inputs into a single expression that can be
|
||||
// used as the test in a case statement (this can't be inserted
|
||||
// directly into the case statement due to the requirement that
|
||||
// the test expression be "locally static")
|
||||
int nin = ivl_udp_nin(udp);
|
||||
vhdl_expr *tmp_rhs = NULL;
|
||||
if (nin == 1) {
|
||||
vhdl_var_ref *ref =
|
||||
nexus_to_var_ref(arch->get_scope(), ivl_logic_pin(log, 1));
|
||||
tmp_rhs = ref->cast(tmp_type);
|
||||
proc->add_sensitivity(ref->get_name());
|
||||
}
|
||||
else {
|
||||
vhdl_binop_expr *concat = new vhdl_binop_expr(VHDL_BINOP_CONCAT, NULL);
|
||||
|
||||
for (int i = 1; i < nin; i++) {
|
||||
vhdl_var_ref *ref =
|
||||
nexus_to_var_ref(arch->get_scope(), ivl_logic_pin(log, i));
|
||||
concat->add_expr(ref);
|
||||
proc->add_sensitivity(ref->get_name());
|
||||
}
|
||||
|
||||
tmp_rhs = concat;
|
||||
}
|
||||
|
||||
proc->get_container()->add_stmt
|
||||
(new vhdl_assign_stmt(new vhdl_var_ref("UDP_Inputs", NULL), tmp_rhs));
|
||||
|
||||
arch->add_stmt(proc);
|
||||
}
|
||||
|
||||
static void udp_logic(vhdl_arch *arch, ivl_net_logic_t log)
|
||||
{
|
||||
if (ivl_udp_sequ(ivl_logic_udp(log)))
|
||||
seq_udp_logic(arch, log);
|
||||
else
|
||||
comb_udp_logic(arch, log);
|
||||
}
|
||||
|
||||
static vhdl_expr *translate_logic_inputs(vhdl_scope *scope, ivl_net_logic_t log)
|
||||
{
|
||||
switch (ivl_logic_type(log)) {
|
||||
case IVL_LO_NOT:
|
||||
return input_to_expr(scope, VHDL_UNARYOP_NOT, log);
|
||||
case IVL_LO_AND:
|
||||
return inputs_to_expr(scope, VHDL_BINOP_AND, log);
|
||||
case IVL_LO_OR:
|
||||
return inputs_to_expr(scope, VHDL_BINOP_OR, log);
|
||||
case IVL_LO_NAND:
|
||||
return inputs_to_expr(scope, VHDL_BINOP_NAND, log);
|
||||
case IVL_LO_NOR:
|
||||
return inputs_to_expr(scope, VHDL_BINOP_NOR, log);
|
||||
case IVL_LO_XOR:
|
||||
return inputs_to_expr(scope, VHDL_BINOP_XOR, log);
|
||||
case IVL_LO_XNOR:
|
||||
return inputs_to_expr(scope, VHDL_BINOP_XNOR, log);
|
||||
case IVL_LO_BUF:
|
||||
case IVL_LO_BUFZ:
|
||||
return nexus_to_var_ref(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 to expression",
|
||||
ivl_logic_type(log));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void draw_logic(vhdl_arch *arch, ivl_net_logic_t log)
|
||||
{
|
||||
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;
|
||||
case IVL_LO_UDP:
|
||||
udp_logic(arch, log);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
// The output is always pin zero
|
||||
ivl_nexus_t output = ivl_logic_pin(log, 0);
|
||||
vhdl_var_ref *lhs = nexus_to_var_ref(arch->get_scope(), output);
|
||||
|
||||
vhdl_expr *rhs = translate_logic_inputs(arch->get_scope(), log);
|
||||
vhdl_cassign_stmt *ass = new vhdl_cassign_stmt(lhs, rhs);
|
||||
|
||||
ivl_expr_t delay = ivl_logic_delay(log, 1);
|
||||
if (delay)
|
||||
ass->set_after(translate_time_expr(delay));
|
||||
|
||||
arch->add_stmt(ass);
|
||||
}
|
||||
}
|
||||
}
|
||||
+330
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* VHDL code generation for LPM devices.
|
||||
*
|
||||
* 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>
|
||||
|
||||
/*
|
||||
* Return the base of a part select.
|
||||
*/
|
||||
static vhdl_expr *part_select_base(vhdl_scope *scope, ivl_lpm_t lpm)
|
||||
{
|
||||
vhdl_expr *off;
|
||||
ivl_nexus_t base = ivl_lpm_data(lpm, 1);
|
||||
if (base != NULL)
|
||||
off = nexus_to_var_ref(scope, base);
|
||||
else
|
||||
off = new vhdl_const_int(ivl_lpm_base(lpm));
|
||||
|
||||
// Array indexes must be integers
|
||||
vhdl_type integer(VHDL_TYPE_INTEGER);
|
||||
return off->cast(&integer);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Need to make sure output is std_logic rather than Boolean
|
||||
vhdl_type std_logic(VHDL_TYPE_STD_LOGIC);
|
||||
return expr->cast(&std_logic);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
selfrom->set_slice(off, ivl_lpm_width(lpm) - 1);
|
||||
return selfrom;
|
||||
}
|
||||
|
||||
|
||||
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 *ufunc_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
|
||||
{
|
||||
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));
|
||||
if (NULL == ref)
|
||||
return NULL;
|
||||
|
||||
fcall->add_expr(ref);
|
||||
}
|
||||
|
||||
return fcall;
|
||||
}
|
||||
|
||||
static vhdl_expr *reduction_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm,
|
||||
support_function_t f, bool invert)
|
||||
{
|
||||
vhdl_var_ref *ref = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
|
||||
if (NULL == ref)
|
||||
return NULL;
|
||||
|
||||
vhdl_expr *result;
|
||||
if (ref->get_type()->get_name() == VHDL_TYPE_STD_LOGIC)
|
||||
result = ref;
|
||||
else {
|
||||
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(ref->cast(&std_logic_vector));
|
||||
|
||||
result = fcall;
|
||||
}
|
||||
|
||||
if (invert)
|
||||
return new vhdl_unaryop_expr
|
||||
(VHDL_UNARYOP_NOT, result, vhdl_type::std_logic());
|
||||
else
|
||||
return result;
|
||||
}
|
||||
|
||||
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)
|
||||
return ref->resize(ivl_lpm_width(lpm));
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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 *repeat_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
|
||||
{
|
||||
vhdl_expr *in = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
|
||||
return new vhdl_bit_spec_expr(NULL, in);
|
||||
}
|
||||
|
||||
static vhdl_expr *lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
|
||||
{
|
||||
switch (ivl_lpm_type(lpm)) {
|
||||
case IVL_LPM_ADD:
|
||||
return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_ADD);
|
||||
case IVL_LPM_SUB:
|
||||
return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_SUB);
|
||||
case IVL_LPM_MULT:
|
||||
return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_MULT);
|
||||
case IVL_LPM_DIVIDE:
|
||||
return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_DIV);
|
||||
case IVL_LPM_MOD:
|
||||
return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_MOD);
|
||||
case IVL_LPM_CONCAT:
|
||||
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 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 ufunc_lpm_to_expr(scope, lpm);
|
||||
case IVL_LPM_RE_AND:
|
||||
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_AND, false);
|
||||
case IVL_LPM_RE_NAND:
|
||||
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_AND, true);
|
||||
case IVL_LPM_RE_NOR:
|
||||
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_OR, true);
|
||||
case IVL_LPM_RE_OR:
|
||||
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_OR, false);
|
||||
case IVL_LPM_RE_XOR:
|
||||
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_XOR, false);
|
||||
case IVL_LPM_RE_XNOR:
|
||||
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_XOR, true);
|
||||
case IVL_LPM_SIGN_EXT:
|
||||
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);
|
||||
case IVL_LPM_REPEAT:
|
||||
return repeat_lpm_to_expr(scope, lpm);
|
||||
default:
|
||||
error("Unsupported LPM type: %d", ivl_lpm_type(lpm));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int draw_mux_lpm(vhdl_arch *arch, ivl_lpm_t lpm)
|
||||
{
|
||||
int nselects = ivl_lpm_selects(lpm);
|
||||
|
||||
if (nselects > 1) {
|
||||
error("Only 1 LPM select bit supported at the moment");
|
||||
return 1;
|
||||
}
|
||||
|
||||
vhdl_scope *scope = arch->get_scope();
|
||||
|
||||
vhdl_expr *s0 = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
|
||||
vhdl_expr *s1 = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 1));
|
||||
|
||||
vhdl_expr *sel = nexus_to_var_ref(scope, ivl_lpm_select(lpm));
|
||||
vhdl_expr *b1 = new vhdl_const_bit('1');
|
||||
vhdl_expr *t1 =
|
||||
new vhdl_binop_expr(sel, VHDL_BINOP_EQ, b1, vhdl_type::boolean());
|
||||
|
||||
vhdl_var_ref *out = nexus_to_var_ref(scope, ivl_lpm_q(lpm, 0));
|
||||
|
||||
vhdl_cassign_stmt *s = new vhdl_cassign_stmt(out, s0);
|
||||
s->add_condition(s1, t1);
|
||||
|
||||
arch->add_stmt(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int draw_lpm(vhdl_arch *arch, ivl_lpm_t lpm)
|
||||
{
|
||||
if (ivl_lpm_type(lpm) == IVL_LPM_MUX)
|
||||
return draw_mux_lpm(arch, lpm);
|
||||
|
||||
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 (ivl_lpm_type(lpm) == IVL_LPM_PART_PV) {
|
||||
vhdl_expr *off = part_select_base(arch->get_scope(), lpm);
|
||||
assert(off);
|
||||
|
||||
out->set_slice(off, ivl_lpm_width(lpm) - 1);
|
||||
}
|
||||
|
||||
arch->add_stmt(new vhdl_cassign_stmt(out, f));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* VHDL code generation for processes.
|
||||
*
|
||||
* 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 "vhdl_element.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
|
||||
/*
|
||||
* Convert a Verilog process to VHDL and add it to the architecture
|
||||
* of the given entity.
|
||||
*/
|
||||
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
|
||||
// to add information to the architecture)
|
||||
vhdl_process *vhdl_proc = new vhdl_process();
|
||||
ent->get_arch()->add_stmt(vhdl_proc);
|
||||
|
||||
// If this is an initial process, push signal initialisation
|
||||
// into the declarations
|
||||
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;
|
||||
|
||||
// Initial processes are translated to VHDL processes with
|
||||
// no sensitivity list and and indefinite wait statement at
|
||||
// the end
|
||||
// However, if no statements were added to the container
|
||||
// by draw_stmt, don't bother adding a wait as `emit'
|
||||
// will optimise the process out of the output
|
||||
if (ivl_process_type(proc) == IVL_PR_INITIAL
|
||||
&& !vhdl_proc->get_container()->empty()) {
|
||||
vhdl_wait_stmt *wait = new vhdl_wait_stmt();
|
||||
vhdl_proc->get_container()->add_stmt(wait);
|
||||
}
|
||||
|
||||
// Add a comment indicating where it came from
|
||||
ivl_scope_t scope = ivl_process_scope(proc);
|
||||
const char *type = ivl_process_type(proc) == IVL_PR_INITIAL
|
||||
? "initial" : "always";
|
||||
std::ostringstream ss;
|
||||
ss << "Generated from " << type << " process in "
|
||||
<< ivl_scope_tname(scope) << " ("
|
||||
<< ivl_process_file(proc) << ":"
|
||||
<< ivl_process_lineno(proc) << ")";
|
||||
vhdl_proc->set_comment(ss.str());
|
||||
|
||||
set_active_entity(NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int draw_process(ivl_process_t proc, void *cd)
|
||||
{
|
||||
ivl_scope_t scope = ivl_process_scope(proc);
|
||||
|
||||
// A process should occur in a module scope, therefore it
|
||||
// should have already been assigned a VHDL entity
|
||||
assert(ivl_scope_type(scope) == IVL_SCT_MODULE);
|
||||
vhdl_entity *ent = find_entity(ivl_scope_name(scope));
|
||||
assert(ent != NULL);
|
||||
|
||||
return generate_vhdl_process(ent, proc);
|
||||
}
|
||||
@@ -0,0 +1,819 @@
|
||||
/*
|
||||
* VHDL code generation for scopes.
|
||||
*
|
||||
* 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 "vhdl_element.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
static string make_safe_name(ivl_signal_t sig);
|
||||
|
||||
static vhdl_entity *g_active_entity = NULL;
|
||||
|
||||
vhdl_entity *get_active_entity()
|
||||
{
|
||||
return g_active_entity;
|
||||
}
|
||||
|
||||
void set_active_entity(vhdl_entity *ent)
|
||||
{
|
||||
g_active_entity = ent;
|
||||
}
|
||||
|
||||
/*
|
||||
* This represents the portion of a nexus that is visible within
|
||||
* a VHDL scope. If that nexus portion does not contain a signal,
|
||||
* then `tmpname' gives the name of the temporary that will be
|
||||
* used when this nexus is used in `scope' (e.g. for LPMs that
|
||||
* appear in instantiations). The list `connect' lists all the
|
||||
* signals that should be joined together to re-create the net.
|
||||
*/
|
||||
struct scope_nexus_t {
|
||||
vhdl_scope *scope;
|
||||
ivl_signal_t sig; // A real signal
|
||||
unsigned pin; // The pin this signal is connected to
|
||||
string tmpname; // A new temporary signal
|
||||
list<ivl_signal_t> connect; // Other signals to wire together
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure is stored in the private part of each nexus.
|
||||
* It stores a scope_nexus_t for each VHDL scope which is
|
||||
* connected to that nexus. It's stored as a list so we can use
|
||||
* contained_within to allow several nested scopes to reference
|
||||
* the same signal.
|
||||
*/
|
||||
struct nexus_private_t {
|
||||
list<scope_nexus_t> signals;
|
||||
vhdl_expr *const_driver;
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns the scope_nexus_t of this nexus visible within scope.
|
||||
*/
|
||||
static scope_nexus_t *visible_nexus(nexus_private_t *priv, vhdl_scope *scope)
|
||||
{
|
||||
list<scope_nexus_t>::iterator it;
|
||||
for (it = priv->signals.begin(); it != priv->signals.end(); ++it) {
|
||||
if (scope->contained_within((*it).scope))
|
||||
return &*it;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remember that a signal in `scope' is part of this nexus. The
|
||||
* first signal passed to this function for a scope will be used
|
||||
* as the canonical representation of this nexus when we need to
|
||||
* convert it to a variable reference (e.g. in a LPM input/output).
|
||||
*/
|
||||
static void link_scope_to_nexus_signal(nexus_private_t *priv, vhdl_scope *scope,
|
||||
ivl_signal_t sig, unsigned pin)
|
||||
{
|
||||
scope_nexus_t *sn;
|
||||
if ((sn = visible_nexus(priv, scope))) {
|
||||
assert(sn->tmpname == "");
|
||||
|
||||
sn->connect.push_back(sig);
|
||||
}
|
||||
else {
|
||||
scope_nexus_t new_sn = { scope, sig, pin, "" };
|
||||
priv->signals.push_back(new_sn);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a temporary the representative of this nexus in scope.
|
||||
*/
|
||||
static void link_scope_to_nexus_tmp(nexus_private_t *priv, vhdl_scope *scope,
|
||||
const string &name)
|
||||
{
|
||||
scope_nexus_t new_sn = { scope, NULL, 0, name };
|
||||
priv->signals.push_back(new_sn);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the name of the nexus signal within this scope.
|
||||
*/
|
||||
static string visible_nexus_signal_name(nexus_private_t *priv, vhdl_scope *scope,
|
||||
unsigned *pin)
|
||||
{
|
||||
scope_nexus_t *sn = visible_nexus(priv, scope);
|
||||
assert(sn);
|
||||
|
||||
*pin = sn->pin;
|
||||
return sn->sig ? get_renamed_signal(sn->sig) : sn->tmpname;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generates VHDL code to fully represent a nexus.
|
||||
*/
|
||||
void draw_nexus(ivl_nexus_t nexus)
|
||||
{
|
||||
nexus_private_t *priv = new nexus_private_t;
|
||||
priv->const_driver = NULL;
|
||||
|
||||
int nptrs = ivl_nexus_ptrs(nexus);
|
||||
|
||||
// First pass through connect all the signals up
|
||||
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))) {
|
||||
vhdl_scope *scope = find_scope_for_signal(sig);
|
||||
unsigned pin = ivl_nexus_ptr_pin(nexus_ptr);
|
||||
link_scope_to_nexus_signal(priv, scope, sig, pin);
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass through make sure logic/LPMs have signal
|
||||
// inputs and outputs
|
||||
for (int i = 0; i < nptrs; i++) {
|
||||
ivl_nexus_ptr_t nexus_ptr = ivl_nexus_ptr(nexus, i);
|
||||
|
||||
ivl_net_logic_t log;
|
||||
ivl_lpm_t lpm;
|
||||
ivl_net_const_t con;
|
||||
if ((log = ivl_nexus_ptr_log(nexus_ptr))) {
|
||||
ivl_scope_t log_scope = ivl_logic_scope(log);
|
||||
vhdl_scope *vhdl_scope =
|
||||
find_entity(ivl_scope_name(log_scope))->get_arch()->get_scope();
|
||||
|
||||
if (visible_nexus(priv, vhdl_scope)) {
|
||||
// Already seen this signal in vhdl_scope
|
||||
}
|
||||
else {
|
||||
// Create a temporary signal to connect it to the nexus
|
||||
vhdl_type *type =
|
||||
vhdl_type::type_for(ivl_logic_width(log), false);
|
||||
|
||||
ostringstream ss;
|
||||
ss << "LO" << ivl_logic_basename(log);
|
||||
vhdl_scope->add_decl(new vhdl_signal_decl(ss.str().c_str(), type));
|
||||
|
||||
link_scope_to_nexus_tmp(priv, vhdl_scope, ss.str());
|
||||
}
|
||||
}
|
||||
else if ((lpm = ivl_nexus_ptr_lpm(nexus_ptr))) {
|
||||
ivl_scope_t lpm_scope = ivl_lpm_scope(lpm);
|
||||
vhdl_scope *vhdl_scope =
|
||||
find_entity(ivl_scope_name(lpm_scope))->get_arch()->get_scope();
|
||||
|
||||
if (visible_nexus(priv, vhdl_scope)) {
|
||||
// Already seen this signal in vhdl_scope
|
||||
}
|
||||
else {
|
||||
// Create a temporary signal to connect the nexus
|
||||
// TODO: we could avoid this for IVL_LPM_PART_PV
|
||||
vhdl_type *type = vhdl_type::type_for(ivl_lpm_width(lpm),
|
||||
ivl_lpm_signed(lpm) != 0);
|
||||
ostringstream ss;
|
||||
ss << "LPM" << ivl_lpm_basename(lpm);
|
||||
vhdl_scope->add_decl(new vhdl_signal_decl(ss.str().c_str(), type));
|
||||
|
||||
link_scope_to_nexus_tmp(priv, vhdl_scope, ss.str());
|
||||
}
|
||||
}
|
||||
else if ((con = ivl_nexus_ptr_con(nexus_ptr))) {
|
||||
if (ivl_const_width(con) == 1)
|
||||
priv->const_driver = new vhdl_const_bit(ivl_const_bits(con)[0]);
|
||||
else
|
||||
priv->const_driver =
|
||||
new vhdl_const_bits(ivl_const_bits(con), ivl_const_width(con),
|
||||
ivl_const_signed(con) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Save the private data in the nexus
|
||||
ivl_nexus_set_private(nexus, priv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that a nexus has been initialised. I.e. all the necessary
|
||||
* statements, declarations, etc. have been generated.
|
||||
*/
|
||||
static void seen_nexus(ivl_nexus_t nexus)
|
||||
{
|
||||
if (ivl_nexus_get_private(nexus) == NULL)
|
||||
draw_nexus(nexus);
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate a nexus to a variable reference. Given a nexus and a
|
||||
* scope, this function returns a reference to a signal that is
|
||||
* connected to the nexus and within the given scope. This signal
|
||||
* might not exist in the original Verilog source (even as a
|
||||
* compiler-generated temporary). If this nexus hasn't been
|
||||
* encountered before, the necessary code to connect up the nexus
|
||||
* will be generated.
|
||||
*/
|
||||
vhdl_var_ref *nexus_to_var_ref(vhdl_scope *scope, ivl_nexus_t nexus)
|
||||
{
|
||||
seen_nexus(nexus);
|
||||
|
||||
nexus_private_t *priv =
|
||||
static_cast<nexus_private_t*>(ivl_nexus_get_private(nexus));
|
||||
unsigned pin;
|
||||
string renamed(visible_nexus_signal_name(priv, scope, &pin));
|
||||
|
||||
vhdl_decl *decl = scope->get_decl(renamed);
|
||||
assert(decl);
|
||||
|
||||
vhdl_type *type = new vhdl_type(*(decl->get_type()));
|
||||
vhdl_var_ref *ref = new vhdl_var_ref(renamed.c_str(), type);
|
||||
|
||||
if (decl->get_type()->get_name() == VHDL_TYPE_ARRAY)
|
||||
ref->set_slice(new vhdl_const_int(pin), 0);
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate all the primitive logic gates into concurrent
|
||||
* signal assignments.
|
||||
*/
|
||||
static void declare_logic(vhdl_arch *arch, ivl_scope_t scope)
|
||||
{
|
||||
int nlogs = ivl_scope_logs(scope);
|
||||
for (int i = 0; i < nlogs; i++)
|
||||
draw_logic(arch, ivl_scope_log(scope, i));
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure a signal name conforms to VHDL naming rules.
|
||||
*/
|
||||
static string make_safe_name(ivl_signal_t sig)
|
||||
{
|
||||
const char *base = ivl_signal_basename(sig);
|
||||
if (base[0] == '_')
|
||||
return string("VL") + base;
|
||||
|
||||
const char *vhdl_reserved[] = {
|
||||
"in", "out", "entity", "architecture", "inout", "array",
|
||||
"is", "not", "and", "or", "bus", "bit", "line", // Etc...
|
||||
NULL
|
||||
};
|
||||
for (const char **p = vhdl_reserved; *p != NULL; p++) {
|
||||
if (strcasecmp(*p, base) == 0) {
|
||||
return string("VL_") + base;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return string(base);
|
||||
}
|
||||
|
||||
/*
|
||||
* Declare all signals and ports for a scope.
|
||||
*/
|
||||
static void declare_signals(vhdl_entity *ent, ivl_scope_t scope)
|
||||
{
|
||||
int nsigs = ivl_scope_sigs(scope);
|
||||
for (int i = 0; i < nsigs; i++) {
|
||||
ivl_signal_t sig = ivl_scope_sig(scope, i);
|
||||
remember_signal(sig, ent->get_arch()->get_scope());
|
||||
|
||||
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:
|
||||
{
|
||||
vhdl_decl *decl = new vhdl_signal_decl(name.c_str(), sig_type);
|
||||
|
||||
ostringstream ss;
|
||||
ss << "Declared at " << ivl_signal_file(sig) << ":"
|
||||
<< ivl_signal_lineno(sig);
|
||||
decl->set_comment(ss.str().c_str());
|
||||
|
||||
ent->get_arch()->get_scope()->add_decl(decl);
|
||||
}
|
||||
break;
|
||||
case IVL_SIP_INPUT:
|
||||
ent->get_scope()->add_decl
|
||||
(new vhdl_port_decl(name.c_str(), sig_type, VHDL_PORT_IN));
|
||||
break;
|
||||
case IVL_SIP_OUTPUT:
|
||||
{
|
||||
vhdl_port_decl *decl =
|
||||
new vhdl_port_decl(name.c_str(), sig_type, VHDL_PORT_OUT);
|
||||
ent->get_scope()->add_decl(decl);
|
||||
}
|
||||
|
||||
if (ivl_signal_type(sig) == IVL_SIT_REG) {
|
||||
// A registered output
|
||||
// In Verilog the output and reg can have the
|
||||
// same name: this is not valid in VHDL
|
||||
// Instead a new signal foo_Reg is created
|
||||
// which represents the register
|
||||
std::string newname(name);
|
||||
newname += "_Reg";
|
||||
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));
|
||||
|
||||
// Create a concurrent assignment statement to
|
||||
// connect the register to the output
|
||||
ent->get_arch()->add_stmt
|
||||
(new vhdl_cassign_stmt
|
||||
(new vhdl_var_ref(name.c_str(), NULL),
|
||||
new vhdl_var_ref(newname.c_str(), NULL)));
|
||||
}
|
||||
break;
|
||||
case IVL_SIP_INOUT:
|
||||
ent->get_scope()->add_decl
|
||||
(new vhdl_port_decl(name.c_str(), sig_type, VHDL_PORT_INOUT));
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate VHDL for LPM instances in a module.
|
||||
*/
|
||||
static void declare_lpm(vhdl_arch *arch, ivl_scope_t scope)
|
||||
{
|
||||
int nlpms = ivl_scope_lpms(scope);
|
||||
for (int i = 0; i < nlpms; i++) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Map two signals together in an instantiation.
|
||||
* The signals are joined by a nexus.
|
||||
*/
|
||||
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);
|
||||
seen_nexus(nexus);
|
||||
|
||||
vhdl_scope *arch_scope = parent->get_arch()->get_scope();
|
||||
|
||||
nexus_private_t *priv =
|
||||
static_cast<nexus_private_t*>(ivl_nexus_get_private(nexus));
|
||||
assert(priv);
|
||||
if (!visible_nexus(priv, arch_scope)) {
|
||||
// This nexus isn't attached to anything in the parent
|
||||
return;
|
||||
}
|
||||
|
||||
vhdl_var_ref *ref = nexus_to_var_ref(parent->get_arch()->get_scope(), nexus);
|
||||
|
||||
string name = make_safe_name(to);
|
||||
|
||||
// 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.
|
||||
// The VHDL equivalent of this is to make *both* output ports
|
||||
// a `buffer'.
|
||||
vhdl_decl *decl =
|
||||
parent->get_arch()->get_scope()->get_decl(ref->get_name());
|
||||
vhdl_port_decl *pdecl;
|
||||
if ((pdecl = dynamic_cast<vhdl_port_decl*>(decl))
|
||||
&& pdecl->get_mode() == VHDL_PORT_OUT) {
|
||||
|
||||
// First change the mode in the parent entity
|
||||
pdecl->set_mode(VHDL_PORT_BUFFER);
|
||||
|
||||
// Now change the mode in the child entity
|
||||
vhdl_port_decl *to_pdecl =
|
||||
dynamic_cast<vhdl_port_decl*>(find_scope_for_signal(to)->get_decl(name));
|
||||
assert(to_pdecl);
|
||||
to_pdecl->set_mode(VHDL_PORT_BUFFER);
|
||||
}
|
||||
|
||||
inst->map_port(name.c_str(), ref);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find all the port mappings of a module instantiation.
|
||||
*/
|
||||
static void port_map(ivl_scope_t scope, vhdl_entity *parent,
|
||||
vhdl_comp_inst *inst)
|
||||
{
|
||||
// Find all the port mappings
|
||||
int nsigs = ivl_scope_sigs(scope);
|
||||
for (int i = 0; i < nsigs; i++) {
|
||||
ivl_signal_t sig = ivl_scope_sig(scope, i);
|
||||
|
||||
ivl_signal_port_t mode = ivl_signal_port(sig);
|
||||
switch (mode) {
|
||||
case IVL_SIP_NONE:
|
||||
// Internal signals don't appear in the port map
|
||||
break;
|
||||
case IVL_SIP_INPUT:
|
||||
case IVL_SIP_OUTPUT:
|
||||
case IVL_SIP_INOUT:
|
||||
map_signal(sig, parent, inst);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a VHDL function from a Verilog function definition.
|
||||
*/
|
||||
static int draw_function(ivl_scope_t scope, ivl_scope_t parent)
|
||||
{
|
||||
assert(ivl_scope_type(scope) == IVL_SCT_FUNCTION);
|
||||
|
||||
// Find the containing entity
|
||||
vhdl_entity *ent = find_entity(ivl_scope_name(parent));
|
||||
assert(ent);
|
||||
|
||||
const char *funcname = ivl_scope_tname(scope);
|
||||
|
||||
assert(!ent->get_arch()->get_scope()->have_declared(funcname));
|
||||
|
||||
// The return type is worked out from the output port
|
||||
vhdl_function *func = new vhdl_function(funcname, NULL);
|
||||
|
||||
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 =
|
||||
vhdl_type::type_for(ivl_signal_width(sig),
|
||||
ivl_signal_signed(sig) != 0);
|
||||
|
||||
string signame(make_safe_name(sig));
|
||||
|
||||
switch (ivl_signal_port(sig)) {
|
||||
case IVL_SIP_INPUT:
|
||||
func->add_param(new vhdl_param_decl(signame.c_str(), sigtype));
|
||||
break;
|
||||
case IVL_SIP_OUTPUT:
|
||||
// The magic variable Verilog_Result holds the return value
|
||||
signame = "Verilog_Result";
|
||||
func->set_type(new vhdl_type(*sigtype));
|
||||
default:
|
||||
func->get_scope()->add_decl
|
||||
(new vhdl_var_decl(signame.c_str(), sigtype));
|
||||
}
|
||||
|
||||
remember_signal(sig, func->get_scope());
|
||||
rename_signal(sig, signame);
|
||||
}
|
||||
|
||||
// Non-blocking assignment not allowed in functions
|
||||
func->get_scope()->set_allow_signal_assignment(false);
|
||||
|
||||
set_active_entity(ent);
|
||||
{
|
||||
draw_stmt(func, func->get_container(), ivl_scope_def(scope));
|
||||
}
|
||||
set_active_entity(NULL);
|
||||
|
||||
// Add a forward declaration too in case it is called by
|
||||
// another function that has already been added
|
||||
ent->get_arch()->get_scope()->add_forward_decl
|
||||
(new vhdl_forward_fdecl(func));
|
||||
|
||||
ostringstream ss;
|
||||
ss << "Generated from function " << funcname << " at "
|
||||
<< ivl_scope_def_file(scope) << ":" << ivl_scope_def_lineno(scope);
|
||||
func->set_comment(ss.str().c_str());
|
||||
|
||||
ent->get_arch()->get_scope()->add_decl(func);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create the signals necessary to expand this task later.
|
||||
*/
|
||||
static int draw_task(ivl_scope_t scope, ivl_scope_t parent)
|
||||
{
|
||||
assert(ivl_scope_type(scope) == IVL_SCT_TASK);
|
||||
|
||||
// Find the containing entity
|
||||
vhdl_entity *ent = find_entity(ivl_scope_name(parent));
|
||||
assert(ent);
|
||||
|
||||
const char *taskname = ivl_scope_tname(scope);
|
||||
|
||||
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 =
|
||||
vhdl_type::type_for(ivl_signal_width(sig),
|
||||
ivl_signal_signed(sig) != 0);
|
||||
|
||||
string signame(make_safe_name(sig));
|
||||
|
||||
// Check this signal isn't declared in the outer scope
|
||||
if (ent->get_arch()->get_scope()->have_declared(signame)) {
|
||||
signame += "_";
|
||||
signame += taskname;
|
||||
}
|
||||
|
||||
vhdl_signal_decl *decl = new vhdl_signal_decl(signame.c_str(), sigtype);
|
||||
|
||||
ostringstream ss;
|
||||
ss << "Declared at " << ivl_signal_file(sig) << ":"
|
||||
<< ivl_signal_lineno(sig) << " (in task " << taskname << ")";
|
||||
decl->set_comment(ss.str().c_str());
|
||||
|
||||
ent->get_arch()->get_scope()->add_decl(decl);
|
||||
|
||||
remember_signal(sig, ent->get_arch()->get_scope());
|
||||
rename_signal(sig, signame);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an empty VHDL entity for a Verilog module.
|
||||
*/
|
||||
static void create_skeleton_entity_for(ivl_scope_t scope)
|
||||
{
|
||||
assert(ivl_scope_type(scope) == IVL_SCT_MODULE);
|
||||
|
||||
// The type name will become the entity name
|
||||
const char *tname = ivl_scope_tname(scope);
|
||||
|
||||
// Remember the scope name this entity was derived from so
|
||||
// the correct processes can be added later
|
||||
const char *derived_from = ivl_scope_name(scope);
|
||||
|
||||
// Verilog does not have the entity/architecture distinction
|
||||
// so we always create a pair and associate the architecture
|
||||
// with the entity for convenience (this also means that we
|
||||
// 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);
|
||||
|
||||
// Build a comment to add to the entity/architecture
|
||||
ostringstream ss;
|
||||
ss << "Generated from Verilog module " << ivl_scope_tname(scope)
|
||||
<< " (" << ivl_scope_def_file(scope) << ":"
|
||||
<< ivl_scope_def_lineno(scope) << ")";
|
||||
|
||||
arch->set_comment(ss.str());
|
||||
ent->set_comment(ss.str());
|
||||
|
||||
remember_entity(ent);
|
||||
}
|
||||
|
||||
/*
|
||||
* A first pass through the hierarchy: create VHDL entities for
|
||||
* each unique Verilog module type.
|
||||
*/
|
||||
static int draw_skeleton_scope(ivl_scope_t scope, void *_parent)
|
||||
{
|
||||
if (ivl_scope_type(scope) == IVL_SCT_MODULE)
|
||||
create_skeleton_entity_for(scope);
|
||||
|
||||
return ivl_scope_children(scope, draw_skeleton_scope, scope);
|
||||
}
|
||||
|
||||
static int draw_all_signals(ivl_scope_t scope, void *_parent)
|
||||
{
|
||||
if (ivl_scope_type(scope) == IVL_SCT_MODULE) {
|
||||
vhdl_entity *ent = find_entity(ivl_scope_name(scope));
|
||||
assert(ent);
|
||||
|
||||
declare_signals(ent, scope);
|
||||
}
|
||||
|
||||
return ivl_scope_children(scope, draw_all_signals, scope);
|
||||
}
|
||||
|
||||
/*
|
||||
* Draw all tasks and functions in the hierarchy.
|
||||
*/
|
||||
static int draw_functions(ivl_scope_t scope, void *_parent)
|
||||
{
|
||||
ivl_scope_t parent = static_cast<ivl_scope_t>(_parent);
|
||||
if (ivl_scope_type(scope) == IVL_SCT_FUNCTION) {
|
||||
if (draw_function(scope, parent) != 0)
|
||||
return 1;
|
||||
}
|
||||
else if (ivl_scope_type(scope) == IVL_SCT_TASK) {
|
||||
if (draw_task(scope, parent) != 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return ivl_scope_children(scope, draw_functions, scope);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make concurrent assignments for constants in nets. This works
|
||||
* bottom-up so that the driver is in the lowest instance it can.
|
||||
* This also has the side effect of generating all the necessary
|
||||
* nexus code.
|
||||
*/
|
||||
static int draw_constant_drivers(ivl_scope_t scope, void *_parent)
|
||||
{
|
||||
ivl_scope_children(scope, draw_constant_drivers, scope);
|
||||
|
||||
if (ivl_scope_type(scope) == IVL_SCT_MODULE) {
|
||||
vhdl_entity *ent = find_entity(ivl_scope_name(scope));
|
||||
assert(ent);
|
||||
|
||||
int nsigs = ivl_scope_sigs(scope);
|
||||
for (int i = 0; i < nsigs; i++) {
|
||||
ivl_signal_t sig = ivl_scope_sig(scope, i);
|
||||
|
||||
for (unsigned i = ivl_signal_array_base(sig);
|
||||
i < ivl_signal_array_count(sig);
|
||||
i++) {
|
||||
// Make sure the nexus code is generated
|
||||
ivl_nexus_t nex = ivl_signal_nex(sig, i);
|
||||
seen_nexus(nex);
|
||||
|
||||
nexus_private_t *priv =
|
||||
static_cast<nexus_private_t*>(ivl_nexus_get_private(nex));
|
||||
assert(priv);
|
||||
|
||||
vhdl_scope *arch_scope = ent->get_arch()->get_scope();
|
||||
|
||||
if (priv->const_driver) {
|
||||
assert(i == 0); // TODO: Make work for more words
|
||||
|
||||
vhdl_var_ref *ref = nexus_to_var_ref(arch_scope, nex);
|
||||
|
||||
ent->get_arch()->add_stmt
|
||||
(new vhdl_cassign_stmt(ref, priv->const_driver));
|
||||
priv->const_driver = NULL;
|
||||
}
|
||||
|
||||
scope_nexus_t *sn = visible_nexus(priv, arch_scope);
|
||||
for (list<ivl_signal_t>::const_iterator it = sn->connect.begin();
|
||||
it != sn->connect.end();
|
||||
++it) {
|
||||
vhdl_var_ref *rref =
|
||||
new vhdl_var_ref(get_renamed_signal(sn->sig).c_str(), NULL);
|
||||
vhdl_var_ref *lref =
|
||||
new vhdl_var_ref(get_renamed_signal(*it).c_str(), NULL);
|
||||
ent->get_arch()->add_stmt(new vhdl_cassign_stmt(lref, rref));
|
||||
}
|
||||
sn->connect.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int draw_all_logic_and_lpm(ivl_scope_t scope, void *_parent)
|
||||
{
|
||||
if (ivl_scope_type(scope) == IVL_SCT_MODULE) {
|
||||
vhdl_entity *ent = find_entity(ivl_scope_name(scope));
|
||||
assert(ent);
|
||||
|
||||
set_active_entity(ent);
|
||||
{
|
||||
declare_logic(ent->get_arch(), scope);
|
||||
declare_lpm(ent->get_arch(), scope);
|
||||
}
|
||||
set_active_entity(NULL);
|
||||
}
|
||||
|
||||
return ivl_scope_children(scope, draw_all_logic_and_lpm, scope);
|
||||
}
|
||||
|
||||
static int draw_hierarchy(ivl_scope_t scope, void *_parent)
|
||||
{
|
||||
if (ivl_scope_type(scope) == IVL_SCT_MODULE && _parent) {
|
||||
ivl_scope_t parent = static_cast<ivl_scope_t>(_parent);
|
||||
|
||||
vhdl_entity *ent = find_entity(ivl_scope_name(scope));
|
||||
assert(ent);
|
||||
|
||||
vhdl_entity *parent_ent = find_entity(ivl_scope_name(parent));
|
||||
assert(parent_ent);
|
||||
|
||||
vhdl_arch *parent_arch = parent_ent->get_arch();
|
||||
assert(parent_arch != NULL);
|
||||
|
||||
// Create a forward declaration for it
|
||||
if (!parent_arch->get_scope()->have_declared(ent->get_name())) {
|
||||
vhdl_decl *comp_decl = vhdl_component_decl::component_decl_for(ent);
|
||||
parent_arch->get_scope()->add_decl(comp_decl);
|
||||
}
|
||||
|
||||
// And an instantiation statement
|
||||
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());
|
||||
port_map(scope, parent_ent, inst);
|
||||
|
||||
ostringstream ss;
|
||||
ss << "Generated from instantiation at "
|
||||
<< ivl_scope_file(scope) << ":" << ivl_scope_lineno(scope);
|
||||
inst->set_comment(ss.str().c_str());
|
||||
|
||||
parent_arch->add_stmt(inst);
|
||||
}
|
||||
|
||||
return ivl_scope_children(scope, draw_hierarchy, scope);
|
||||
}
|
||||
|
||||
int draw_scope(ivl_scope_t scope, void *_parent)
|
||||
{
|
||||
int rc = draw_skeleton_scope(scope, _parent);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
rc = draw_all_signals(scope, _parent);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
rc = draw_all_logic_and_lpm(scope, _parent);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
rc = draw_hierarchy(scope, _parent);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
rc = draw_functions(scope, _parent);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
rc = draw_constant_drivers(scope, _parent);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,697 @@
|
||||
/*
|
||||
* VHDL code generation for statements.
|
||||
*
|
||||
* 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 <cstring>
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
#include <typeinfo>
|
||||
|
||||
/*
|
||||
* VHDL has no real equivalent of Verilog's $finish task. The
|
||||
* current solution is to use `assert false ...' to terminate
|
||||
* the simulator. This isn't great, as the simulator will
|
||||
* return a failure exit code when in fact it completed
|
||||
* successfully.
|
||||
*
|
||||
* An alternative is to use the VHPI interface supported by
|
||||
* some VHDL simulators and implement the $finish funcitonality
|
||||
* in C. This function can be enabled with the flag
|
||||
* -puse-vhpi-finish=1.
|
||||
*/
|
||||
static int draw_stask_finish(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt)
|
||||
{
|
||||
const char *use_vhpi = ivl_design_flag(get_vhdl_design(), "use-vhpi-finish");
|
||||
if (strcmp(use_vhpi, "1") == 0) {
|
||||
//get_active_entity()->requires_package("work.Verilog_Support");
|
||||
container->add_stmt(new vhdl_pcall_stmt("work.Verilog_Support.Finish"));
|
||||
}
|
||||
else {
|
||||
container->add_stmt(new vhdl_assert_stmt("SIMULATION FINISHED"));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate VHDL for system tasks (like $display). Not all of
|
||||
* these are supported.
|
||||
*/
|
||||
static int draw_stask(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt)
|
||||
{
|
||||
const char *name = ivl_stmt_name(stmt);
|
||||
|
||||
if (strcmp(name, "$display") == 0)
|
||||
return draw_stask_display(proc, container, stmt, true);
|
||||
else if (strcmp(name, "$write") == 0)
|
||||
return draw_stask_display(proc, container, stmt, false);
|
||||
else if (strcmp(name, "$finish") == 0)
|
||||
return draw_stask_finish(proc, container, stmt);
|
||||
else {
|
||||
error("No VHDL translation for system task %s", name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate VHDL for a block of Verilog statements. This doesn't
|
||||
* actually do anything, other than recursively translate the
|
||||
* block's statements and add them to the process. This is OK as
|
||||
* the stmt_container class behaves like a Verilog block.
|
||||
*/
|
||||
static int draw_block(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt, bool is_last)
|
||||
{
|
||||
int count = ivl_stmt_block_count(stmt);
|
||||
for (int i = 0; i < count; i++) {
|
||||
ivl_statement_t stmt_i = ivl_stmt_block_stmt(stmt, i);
|
||||
if (draw_stmt(proc, container, stmt_i, is_last && i == count - 1) != 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* A no-op statement. This corresponds to a `null' statement in VHDL.
|
||||
*/
|
||||
static int draw_noop(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt)
|
||||
{
|
||||
container->add_stmt(new vhdl_null_stmt());
|
||||
return 0;
|
||||
}
|
||||
|
||||
static vhdl_var_ref *make_assign_lhs(ivl_lval_t lval, vhdl_scope *scope)
|
||||
{
|
||||
ivl_signal_t sig = ivl_lval_sig(lval);
|
||||
if (!sig) {
|
||||
error("Only signals as lvals supported at the moment");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
string signame(get_renamed_signal(sig));
|
||||
vhdl_decl *decl = scope->get_decl(signame);
|
||||
assert(decl);
|
||||
|
||||
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
|
||||
lval_ref->set_slice(base, lval_width - 1);
|
||||
}
|
||||
|
||||
return lval_ref;
|
||||
}
|
||||
|
||||
static bool assignment_lvals(ivl_statement_t stmt, vhdl_procedural *proc,
|
||||
list<vhdl_var_ref*> &lvals)
|
||||
{
|
||||
int nlvals = ivl_stmt_lvals(stmt);
|
||||
for (int i = 0; i < nlvals; i++) {
|
||||
ivl_lval_t lval = ivl_stmt_lval(stmt, i);
|
||||
vhdl_var_ref *lhs = make_assign_lhs(lval, proc->get_scope());
|
||||
if (NULL == lhs)
|
||||
return false;
|
||||
|
||||
lvals.push_back(lhs);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate an assignment of type T for the Verilog statement stmt.
|
||||
*/
|
||||
template <class T>
|
||||
void make_assignment(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt, bool blocking)
|
||||
{
|
||||
list<vhdl_var_ref*> lvals;
|
||||
if (!assignment_lvals(stmt, proc, lvals))
|
||||
return;
|
||||
|
||||
vhdl_expr *rhs, *rhs2 = NULL;
|
||||
ivl_expr_t rval = ivl_stmt_rval(stmt);
|
||||
if (ivl_expr_type(rval) == IVL_EX_TERNARY) {
|
||||
rhs = translate_expr(ivl_expr_oper2(rval));
|
||||
rhs2 = translate_expr(ivl_expr_oper3(rval));
|
||||
}
|
||||
else
|
||||
rhs = translate_expr(rval);
|
||||
if (rhs == NULL)
|
||||
return;
|
||||
|
||||
if (lvals.size() == 1) {
|
||||
vhdl_var_ref *lhs = lvals.front();
|
||||
rhs = rhs->cast(lhs->get_type());
|
||||
|
||||
ivl_expr_t i_delay;
|
||||
vhdl_expr *after = NULL;
|
||||
if ((i_delay = ivl_stmt_delay_expr(stmt)) != NULL)
|
||||
after = translate_time_expr(i_delay);
|
||||
|
||||
// A small optimisation is to expand ternary RHSs into an
|
||||
// if statement (eliminates a function call and produces
|
||||
// more idiomatic code)
|
||||
if (ivl_expr_type(rval) == IVL_EX_TERNARY) {
|
||||
rhs2 = rhs2->cast(lhs->get_type());
|
||||
vhdl_var_ref *lhs2 =
|
||||
make_assign_lhs(ivl_stmt_lval(stmt, 0), proc->get_scope());
|
||||
|
||||
vhdl_expr *test = translate_expr(ivl_expr_oper1(rval));
|
||||
if (NULL == test)
|
||||
return;
|
||||
|
||||
vhdl_if_stmt *vhdif = new vhdl_if_stmt(test);
|
||||
|
||||
// True part
|
||||
{
|
||||
T *a = new T(lhs, rhs);
|
||||
if (after)
|
||||
a->set_after(after);
|
||||
vhdif->get_then_container()->add_stmt(a);
|
||||
}
|
||||
|
||||
// False part
|
||||
{
|
||||
T *a = new T(lhs2, rhs2);
|
||||
if (after)
|
||||
a->set_after(translate_time_expr(i_delay));
|
||||
vhdif->get_else_container()->add_stmt(a);
|
||||
}
|
||||
|
||||
container->add_stmt(vhdif);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
ivl_lval_t lval = ivl_stmt_lval(stmt, 0);
|
||||
vhdl_decl *decl = proc->get_scope()->get_decl(lhs->get_name());
|
||||
if (proc->get_scope()->initializing()
|
||||
&& ivl_signal_port(ivl_lval_sig(lval)) == 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);
|
||||
delete lhs;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
T *a = new T(lhs, rhs);
|
||||
container->add_stmt(a);
|
||||
|
||||
if (after != NULL)
|
||||
a->set_after(after);
|
||||
}
|
||||
else {
|
||||
// Multiple lvals are implemented by first assigning the complete
|
||||
// RHS to a temporary, and then assigning each lval in turn as
|
||||
// bit-selects of the temporary
|
||||
|
||||
static int tmp_count = 0;
|
||||
ostringstream ss;
|
||||
ss << "Verilog_Assign_Tmp_" << tmp_count++;
|
||||
string tmpname = ss.str();
|
||||
|
||||
proc->get_scope()->add_decl
|
||||
(new vhdl_var_decl(tmpname.c_str(), new vhdl_type(*rhs->get_type())));
|
||||
|
||||
vhdl_var_ref *tmp_ref =
|
||||
new vhdl_var_ref(tmpname.c_str(), new vhdl_type(*rhs->get_type()));
|
||||
container->add_stmt(new vhdl_assign_stmt(tmp_ref, rhs));
|
||||
|
||||
list<vhdl_var_ref*>::iterator it;
|
||||
int width_so_far = 0;
|
||||
for (it = lvals.begin(); it != lvals.end(); ++it) {
|
||||
vhdl_var_ref *tmp_rhs =
|
||||
new vhdl_var_ref(tmpname.c_str(), new vhdl_type(*rhs->get_type()));
|
||||
|
||||
int lval_width = (*it)->get_type()->get_width();
|
||||
vhdl_expr *slice_base = new vhdl_const_int(width_so_far);
|
||||
tmp_rhs->set_slice(slice_base, lval_width - 1);
|
||||
|
||||
ivl_expr_t i_delay;
|
||||
vhdl_expr *after = NULL;
|
||||
if ((i_delay = ivl_stmt_delay_expr(stmt)) != NULL)
|
||||
after = translate_time_expr(i_delay);
|
||||
|
||||
T *a = new T(*it, tmp_rhs);
|
||||
if (after)
|
||||
a->set_after(after);
|
||||
|
||||
container->add_stmt(a);
|
||||
|
||||
width_so_far += lval_width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A non-blocking assignment inside a process. The semantics for
|
||||
* this are essentially the same as VHDL's non-blocking signal
|
||||
* assignment.
|
||||
*/
|
||||
static int draw_nbassign(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt)
|
||||
{
|
||||
assert(proc->get_scope()->allow_signal_assignment());
|
||||
|
||||
make_assignment<vhdl_nbassign_stmt>(proc, container, stmt, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int draw_assign(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt, bool is_last)
|
||||
{
|
||||
if (proc->get_scope()->allow_signal_assignment()) {
|
||||
// Blocking assignment is implemented as non-blocking assignment
|
||||
// followed by a zero-time wait
|
||||
// This follows the Verilog semantics fairly closely.
|
||||
|
||||
make_assignment<vhdl_nbassign_stmt>(proc, container, stmt, false);
|
||||
|
||||
// Don't generate a zero-wait if this is the last statement in
|
||||
// the process
|
||||
if (!is_last)
|
||||
container->add_stmt
|
||||
(new vhdl_wait_stmt(VHDL_WAIT_FOR, new vhdl_const_time(0)));
|
||||
}
|
||||
else
|
||||
make_assignment<vhdl_assign_stmt>(proc, container, stmt, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delay statements are equivalent to the `wait for' form of the
|
||||
* VHDL wait statement.
|
||||
*/
|
||||
static int draw_delay(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt)
|
||||
{
|
||||
// This currently ignores the time units and precision
|
||||
// of the enclosing scope
|
||||
// A neat way to do this would be to make these values
|
||||
// constants in the scope (type is Time), and have the
|
||||
// VHDL wait statement compute the value from that.
|
||||
// The other solution is to add them as parameters to
|
||||
// the vhdl_process class
|
||||
vhdl_expr *time;
|
||||
if (ivl_statement_type(stmt) == IVL_ST_DELAY) {
|
||||
uint64_t value = ivl_stmt_delay_val(stmt);
|
||||
time = new vhdl_const_time(value, TIME_UNIT_NS);
|
||||
}
|
||||
else {
|
||||
time = translate_time_expr(ivl_stmt_delay_expr(stmt));
|
||||
if (NULL == time)
|
||||
return 1;
|
||||
}
|
||||
|
||||
ivl_statement_t sub_stmt = ivl_stmt_sub_stmt(stmt);
|
||||
vhdl_wait_stmt *wait =
|
||||
new vhdl_wait_stmt(VHDL_WAIT_FOR, time);
|
||||
container->add_stmt(wait);
|
||||
|
||||
// Expand the sub-statement as well
|
||||
// Often this would result in a useless `null' statement which
|
||||
// is caught here instead
|
||||
if (ivl_statement_type(sub_stmt) != IVL_ST_NOOP)
|
||||
draw_stmt(proc, container, sub_stmt);
|
||||
|
||||
// Any further assignments occur after simulation time 0
|
||||
// so they cannot be used to initialize signal declarations
|
||||
// (if this scope is an initial process)
|
||||
proc->get_scope()->set_initializing(false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* A wait statement waits for a level change on a @(..) list of
|
||||
* 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)
|
||||
{
|
||||
// Wait statements only occur in processes
|
||||
vhdl_process *proc = dynamic_cast<vhdl_process*>(_proc);
|
||||
assert(proc); // Catch not process
|
||||
|
||||
vhdl_binop_expr *test =
|
||||
new vhdl_binop_expr(VHDL_BINOP_OR, vhdl_type::boolean());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
draw_stmt(proc, container, ivl_stmt_sub_stmt(stmt), true);
|
||||
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), true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int draw_if(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt, bool is_last)
|
||||
{
|
||||
vhdl_expr *test = translate_expr(ivl_stmt_cond_expr(stmt));
|
||||
if (NULL == test)
|
||||
return 1;
|
||||
|
||||
vhdl_if_stmt *vhdif = new vhdl_if_stmt(test);
|
||||
|
||||
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, is_last);
|
||||
|
||||
ivl_statement_t cond_false_stmt = ivl_stmt_cond_false(stmt);
|
||||
if (cond_false_stmt)
|
||||
draw_stmt(proc, vhdif->get_else_container(), cond_false_stmt, is_last);
|
||||
|
||||
container->add_stmt(vhdif);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int draw_case(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt, bool is_last)
|
||||
{
|
||||
vhdl_expr *test = translate_expr(ivl_stmt_cond_expr(stmt));
|
||||
if (NULL == test)
|
||||
return 1;
|
||||
|
||||
// VHDL case expressions are required to be quite simple: variable
|
||||
// references or slices. So we may need to create a temporary
|
||||
// variable to hold the result of the expression evaluation
|
||||
if (typeid(*test) != typeid(vhdl_var_ref)) {
|
||||
const char *tmp_name = "Verilog_Case_Ex";
|
||||
vhdl_type *test_type = new vhdl_type(*test->get_type());
|
||||
|
||||
if (!proc->get_scope()->have_declared(tmp_name)) {
|
||||
proc->get_scope()->add_decl
|
||||
(new vhdl_var_decl(tmp_name, new vhdl_type(*test_type)));
|
||||
}
|
||||
|
||||
vhdl_var_ref *tmp_ref = new vhdl_var_ref(tmp_name, NULL);
|
||||
container->add_stmt(new vhdl_assign_stmt(tmp_ref, test));
|
||||
|
||||
test = new vhdl_var_ref(tmp_name, test_type);
|
||||
}
|
||||
|
||||
vhdl_case_stmt *vhdlcase = new vhdl_case_stmt(test);
|
||||
container->add_stmt(vhdlcase);
|
||||
|
||||
// VHDL is more strict than Verilog about covering every
|
||||
// possible case. So make sure we add an 'others' branch
|
||||
// if there isn't a default one.
|
||||
bool have_others = false;
|
||||
|
||||
int nbranches = ivl_stmt_case_count(stmt);
|
||||
for (int i = 0; i < nbranches; i++) {
|
||||
vhdl_expr *when;
|
||||
ivl_expr_t net = ivl_stmt_case_expr(stmt, i);
|
||||
if (net) {
|
||||
when = translate_expr(net)->cast(test->get_type());
|
||||
if (NULL == when)
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
when = new vhdl_var_ref("others", NULL);
|
||||
have_others = true;
|
||||
}
|
||||
|
||||
vhdl_case_branch *branch = new vhdl_case_branch(when);
|
||||
vhdlcase->add_branch(branch);
|
||||
|
||||
ivl_statement_t stmt_i = ivl_stmt_case_stmt(stmt, i);
|
||||
draw_stmt(proc, branch->get_container(), stmt_i, is_last);
|
||||
}
|
||||
|
||||
if (!have_others) {
|
||||
vhdl_case_branch *others =
|
||||
new vhdl_case_branch(new vhdl_var_ref("others", NULL));
|
||||
others->get_container()->add_stmt(new vhdl_null_stmt());
|
||||
vhdlcase->add_branch(others);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int draw_while(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt)
|
||||
{
|
||||
vhdl_expr *test = translate_expr(ivl_stmt_cond_expr(stmt));
|
||||
if (NULL == test)
|
||||
return 1;
|
||||
|
||||
// The test must be a Boolean (and std_logic and (un)signed types
|
||||
// must be explicitly cast unlike in Verilog)
|
||||
vhdl_type boolean(VHDL_TYPE_BOOLEAN);
|
||||
test = test->cast(&boolean);
|
||||
|
||||
vhdl_while_stmt *loop = new vhdl_while_stmt(test);
|
||||
container->add_stmt(loop);
|
||||
|
||||
draw_stmt(proc, loop->get_container(), ivl_stmt_sub_stmt(stmt));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int draw_forever(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt)
|
||||
{
|
||||
vhdl_loop_stmt *loop = new vhdl_loop_stmt;
|
||||
container->add_stmt(loop);
|
||||
|
||||
draw_stmt(proc, loop->get_container(), ivl_stmt_sub_stmt(stmt));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int draw_repeat(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt)
|
||||
{
|
||||
vhdl_expr *times = translate_expr(ivl_stmt_cond_expr(stmt));
|
||||
if (NULL == times)
|
||||
return 1;
|
||||
|
||||
vhdl_type integer(VHDL_TYPE_INTEGER);
|
||||
times = times->cast(&integer);
|
||||
|
||||
const char *it_name = "Verilog_Repeat";
|
||||
vhdl_for_stmt *loop =
|
||||
new vhdl_for_stmt(it_name, new vhdl_const_int(1), times);
|
||||
container->add_stmt(loop);
|
||||
|
||||
draw_stmt(proc, loop->get_container(), ivl_stmt_sub_stmt(stmt));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tasks are difficult to translate to VHDL since they allow things
|
||||
* not allowed by VHDL's corresponding procedures (e.g. updating
|
||||
* global variables. The solution here is to expand tasks in-line.
|
||||
*/
|
||||
int draw_utask(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt)
|
||||
{
|
||||
ivl_scope_t tscope = ivl_stmt_call(stmt);
|
||||
|
||||
// TODO: adding some comments to the output would be helpful
|
||||
|
||||
// TOOD: this completely ignores paremeters!
|
||||
draw_stmt(proc, container, ivl_scope_def(tscope), false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate VHDL statements for the given Verilog statement and
|
||||
* add them to the given VHDL process. The container is the
|
||||
* location to add statements: e.g. the process body, a branch
|
||||
* of an if statement, etc.
|
||||
*
|
||||
* The flag is_last should be set if this is the final statement
|
||||
* in a block or process. It avoids generating useless `wait for 0ns'
|
||||
* statements if the next statement would be a wait anyway.
|
||||
*/
|
||||
int draw_stmt(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt, bool is_last)
|
||||
{
|
||||
assert(stmt);
|
||||
|
||||
switch (ivl_statement_type(stmt)) {
|
||||
case IVL_ST_STASK:
|
||||
return draw_stask(proc, container, stmt);
|
||||
case IVL_ST_BLOCK:
|
||||
return draw_block(proc, container, stmt, is_last);
|
||||
case IVL_ST_NOOP:
|
||||
return draw_noop(proc, container, stmt);
|
||||
case IVL_ST_ASSIGN:
|
||||
return draw_assign(proc, container, stmt, is_last);
|
||||
case IVL_ST_ASSIGN_NB:
|
||||
return draw_nbassign(proc, container, stmt);
|
||||
case IVL_ST_DELAY:
|
||||
case IVL_ST_DELAYX:
|
||||
return draw_delay(proc, container, stmt);
|
||||
case IVL_ST_WAIT:
|
||||
return draw_wait(proc, container, stmt);
|
||||
case IVL_ST_CONDIT:
|
||||
return draw_if(proc, container, stmt, is_last);
|
||||
case IVL_ST_CASE:
|
||||
return draw_case(proc, container, stmt, is_last);
|
||||
case IVL_ST_WHILE:
|
||||
return draw_while(proc, container, stmt);
|
||||
case IVL_ST_FOREVER:
|
||||
return draw_forever(proc, container, stmt);
|
||||
case IVL_ST_REPEAT:
|
||||
return draw_repeat(proc, container, stmt);
|
||||
case IVL_ST_UTASK:
|
||||
return draw_utask(proc, container, stmt);
|
||||
case IVL_ST_FORCE:
|
||||
case IVL_ST_RELEASE:
|
||||
error("force/release statements cannot be translated to VHDL");
|
||||
return 1;
|
||||
case IVL_ST_DISABLE:
|
||||
error("disable statement cannot be translated to VHDL");
|
||||
return 1;
|
||||
case IVL_ST_CASEX:
|
||||
error("casex statement cannot be translated to VHDL");
|
||||
return 1;
|
||||
case IVL_ST_FORK:
|
||||
error("fork statement cannot be translated to VHDL");
|
||||
return 1;
|
||||
case IVL_ST_CASSIGN:
|
||||
case IVL_ST_DEASSIGN:
|
||||
error("continuous procedural assignment cannot be translated to VHDL");
|
||||
return 1;
|
||||
default:
|
||||
error("No VHDL translation for statement at %s:%d (type = %d)",
|
||||
ivl_stmt_file(stmt), ivl_stmt_lineno(stmt),
|
||||
ivl_statement_type(stmt));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Support functions for VHDL output.
|
||||
*
|
||||
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "vhdl_target.h"
|
||||
#include "support.hh"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
void require_support_function(support_function_t f)
|
||||
{
|
||||
vhdl_scope *scope = get_active_entity()->get_arch()->get_scope();
|
||||
if (!scope->have_declared(support_function::function_name(f)))
|
||||
scope->add_decl(new support_function(f));
|
||||
}
|
||||
|
||||
const char *support_function::function_name(support_function_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case SF_UNSIGNED_TO_BOOLEAN: return "Unsigned_To_Boolean";
|
||||
case SF_SIGNED_TO_BOOLEAN: return "Signed_To_Boolean";
|
||||
case SF_BOOLEAN_TO_LOGIC: return "Boolean_To_Logic";
|
||||
case SF_REDUCE_OR: return "Reduce_OR";
|
||||
case SF_REDUCE_AND: return "Reduce_AND";
|
||||
case SF_REDUCE_XOR: return "Reduce_XOR";
|
||||
case SF_TERNARY_LOGIC: return "Ternary_Logic";
|
||||
case SF_TERNARY_UNSIGNED: return "Ternary_Unsigned";
|
||||
case SF_TERNARY_SIGNED: return "Ternary_Signed";
|
||||
case SF_LOGIC_TO_INTEGER: return "Logic_To_Integer";
|
||||
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:
|
||||
case SF_TERNARY_LOGIC:
|
||||
return vhdl_type::std_logic();
|
||||
case SF_TERNARY_SIGNED:
|
||||
return new vhdl_type(VHDL_TYPE_SIGNED);
|
||||
case SF_TERNARY_UNSIGNED:
|
||||
return new vhdl_type(VHDL_TYPE_UNSIGNED);
|
||||
case SF_LOGIC_TO_INTEGER:
|
||||
return vhdl_type::integer();
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void support_function::emit_ternary(std::ostream &of, int level) const
|
||||
{
|
||||
of << nl_string(level) << "begin" << nl_string(indent(level))
|
||||
<< "if T then return X; else return Y; end if;";
|
||||
}
|
||||
|
||||
void support_function::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << nl_string(level) << "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);";
|
||||
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);";
|
||||
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;";
|
||||
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';";
|
||||
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';";
|
||||
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;";
|
||||
break;
|
||||
case SF_TERNARY_LOGIC:
|
||||
of << "(T : Boolean; X, Y : std_logic) return std_logic is";
|
||||
emit_ternary(of, level);
|
||||
break;
|
||||
case SF_TERNARY_SIGNED:
|
||||
of << "(T : Boolean; X, Y : signed) return signed is";
|
||||
emit_ternary(of, level);
|
||||
break;
|
||||
case SF_TERNARY_UNSIGNED:
|
||||
of << "(T : Boolean; X, Y : unsigned) return unsigned is";
|
||||
emit_ternary(of, level);
|
||||
break;
|
||||
case SF_LOGIC_TO_INTEGER:
|
||||
of << "(X : std_logic) return integer is" << nl_string(level)
|
||||
<< "begin" << nl_string(indent(level))
|
||||
<< "if X = '1' then return 1; else return 0; end if;";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
of << nl_string(level) << "end function;";
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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 = 0,
|
||||
SF_SIGNED_TO_BOOLEAN,
|
||||
SF_BOOLEAN_TO_LOGIC,
|
||||
SF_REDUCE_OR,
|
||||
SF_REDUCE_AND,
|
||||
SF_REDUCE_XOR,
|
||||
SF_TERNARY_LOGIC,
|
||||
SF_TERNARY_UNSIGNED,
|
||||
SF_TERNARY_SIGNED,
|
||||
SF_LOGIC_TO_INTEGER,
|
||||
};
|
||||
|
||||
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:
|
||||
void emit_ternary(std::ostream &of, int level) const;
|
||||
|
||||
support_function_t type_;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* VHDL code generator for Icarus Verilog.
|
||||
*
|
||||
* 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 "vhdl_element.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
/*
|
||||
* Maps a signal to the scope it is defined within. Also
|
||||
* provides a mechanism for renaming signals -- i.e. when
|
||||
* an output has the same name as register: valid in Verilog
|
||||
* but not in VHDL, so two separate signals need to be
|
||||
* defined.
|
||||
*/
|
||||
struct signal_defn_t {
|
||||
std::string renamed; // The name of the VHDL signal
|
||||
vhdl_scope *scope; // The scope where it is defined
|
||||
};
|
||||
|
||||
typedef std::map<ivl_signal_t, signal_defn_t> signal_defn_map_t;
|
||||
|
||||
|
||||
static int g_errors = 0; // Total number of errors encountered
|
||||
static entity_list_t g_entities; // All entities to emit
|
||||
static signal_defn_map_t g_known_signals;
|
||||
static ivl_design_t g_design;
|
||||
|
||||
|
||||
/*
|
||||
* Called when an unrecoverable problem is encountered.
|
||||
*/
|
||||
void error(const char *fmt, ...)
|
||||
{
|
||||
std::va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
std::printf("VHDL conversion error: "); // Source/line number?
|
||||
std::vprintf(fmt, args);
|
||||
std::putchar('\n');
|
||||
va_end(args);
|
||||
|
||||
g_errors++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find an entity given a scope name.
|
||||
*/
|
||||
vhdl_entity *find_entity(const std::string &sname)
|
||||
{
|
||||
entity_list_t::const_iterator it;
|
||||
for (it = g_entities.begin(); it != g_entities.end(); ++it) {
|
||||
if ((*it)->get_derived_from() == sname)
|
||||
return *it;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add an entity/architecture pair to the list of entities
|
||||
* to emit.
|
||||
*/
|
||||
void remember_entity(vhdl_entity* ent)
|
||||
{
|
||||
assert(find_entity(ent->get_derived_from()) == NULL);
|
||||
g_entities.push_back(ent);
|
||||
}
|
||||
|
||||
bool seen_signal_before(ivl_signal_t sig)
|
||||
{
|
||||
return g_known_signals.find(sig) != g_known_signals.end();
|
||||
}
|
||||
|
||||
/*
|
||||
* Remeber the association of signal to entity.
|
||||
*/
|
||||
void remember_signal(ivl_signal_t sig, vhdl_scope *scope)
|
||||
{
|
||||
assert(!seen_signal_before(sig));
|
||||
|
||||
signal_defn_t defn = { ivl_signal_basename(sig), scope };
|
||||
g_known_signals[sig] = defn;
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the VHDL name of a Verilog signal.
|
||||
*/
|
||||
void rename_signal(ivl_signal_t sig, const std::string &renamed)
|
||||
{
|
||||
assert(seen_signal_before(sig));
|
||||
|
||||
g_known_signals[sig].renamed = renamed;
|
||||
}
|
||||
|
||||
vhdl_scope *find_scope_for_signal(ivl_signal_t sig)
|
||||
{
|
||||
assert(seen_signal_before(sig));
|
||||
|
||||
return g_known_signals[sig].scope;
|
||||
}
|
||||
|
||||
const std::string &get_renamed_signal(ivl_signal_t sig)
|
||||
{
|
||||
assert(seen_signal_before(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;
|
||||
}
|
||||
|
||||
extern "C" int target_design(ivl_design_t des)
|
||||
{
|
||||
ivl_scope_t *roots;
|
||||
unsigned int nroots;
|
||||
ivl_design_roots(des, &roots, &nroots);
|
||||
|
||||
g_design = des;
|
||||
|
||||
for (unsigned int i = 0; i < nroots; i++)
|
||||
draw_scope(roots[i], NULL);
|
||||
|
||||
ivl_design_process(des, draw_process, NULL);
|
||||
|
||||
// Write the generated elements to the output file
|
||||
// only if there are no errors
|
||||
if (0 == g_errors) {
|
||||
const char *ofname = ivl_design_flag(des, "-o");
|
||||
ofstream outfile(ofname);
|
||||
|
||||
// Make sure we only emit one example of each type of entity
|
||||
set<string> seen_entities;
|
||||
|
||||
for (entity_list_t::iterator it = g_entities.begin();
|
||||
it != g_entities.end();
|
||||
++it) {
|
||||
if (seen_entities.find((*it)->get_name()) == seen_entities.end()) {
|
||||
(*it)->emit(outfile);
|
||||
seen_entities.insert((*it)->get_name());
|
||||
}
|
||||
}
|
||||
|
||||
outfile.close();
|
||||
}
|
||||
|
||||
// Clean up
|
||||
for (entity_list_t::iterator it = g_entities.begin();
|
||||
it != g_entities.end();
|
||||
++it)
|
||||
delete (*it);
|
||||
g_entities.clear();
|
||||
|
||||
return g_errors;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
functor:cprop
|
||||
functor:nodangle
|
||||
-t:dll
|
||||
flag:DLL=vhdl.tgt
|
||||
@@ -0,0 +1,49 @@
|
||||
/* vhdl_config.h.in. Generated from configure.in by autoheader. */
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 if you have the <malloc.h> header file. */
|
||||
#undef HAVE_MALLOC_H
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#undef HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#undef HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#undef HAVE_SYS_STAT_H
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#undef HAVE_SYS_TYPES_H
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#undef HAVE_UNISTD_H
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* VHDL abstract syntax elements.
|
||||
*
|
||||
* 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_element.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <typeinfo>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
static const int VHDL_INDENT = 2; // Spaces to indent
|
||||
|
||||
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.
|
||||
*/
|
||||
void newline(std::ostream &of, int level)
|
||||
{
|
||||
of << std::endl;
|
||||
while (level--)
|
||||
of << ' ';
|
||||
}
|
||||
|
||||
void blank_line(std::ostream &of, int level)
|
||||
{
|
||||
of << std::endl;
|
||||
newline(of, level);
|
||||
}
|
||||
|
||||
void vhdl_element::set_comment(std::string comment)
|
||||
{
|
||||
comment_ = comment;
|
||||
}
|
||||
|
||||
/*
|
||||
* Draw the comment for any element. The comment is either on
|
||||
* a line before the element (end_of_line is false) or at the
|
||||
* end of the line containing the element (end_of_line is true).
|
||||
*/
|
||||
void vhdl_element::emit_comment(std::ostream &of, int level,
|
||||
bool end_of_line) const
|
||||
{
|
||||
if (comment_.size() > 0) {
|
||||
if (end_of_line)
|
||||
of << " ";
|
||||
of << "-- " << comment_;
|
||||
if (!end_of_line)
|
||||
newline(of, level);
|
||||
}
|
||||
}
|
||||
|
||||
void vhdl_element::print() const
|
||||
{
|
||||
emit(std::cout, 0);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* VHDL abstract syntax elements.
|
||||
*
|
||||
* 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_VHDL_ELEMENT_HH
|
||||
#define INC_VHDL_ELEMENT_HH
|
||||
|
||||
#include <fstream>
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
typedef std::list<std::string> string_list_t;
|
||||
|
||||
/*
|
||||
* Any VHDL syntax element. Each element can also contain a comment.
|
||||
*/
|
||||
class vhdl_element {
|
||||
public:
|
||||
virtual ~vhdl_element() {}
|
||||
|
||||
virtual void emit(std::ostream &of, int level=0) const = 0;
|
||||
void print() const;
|
||||
|
||||
void set_comment(std::string comment);
|
||||
protected:
|
||||
void emit_comment(std::ostream &of, int level,
|
||||
bool end_of_line=false) const;
|
||||
private:
|
||||
std::string comment_;
|
||||
};
|
||||
|
||||
typedef std::list<vhdl_element*> element_list_t;
|
||||
|
||||
int indent(int level);
|
||||
void newline(std::ostream &of, int level);
|
||||
std::string nl_string(int level);
|
||||
void blank_line(std::ostream &of, int level);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Helper functions for VHDL syntax elements.
|
||||
*
|
||||
* 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_VHDL_HELPER_HH
|
||||
#define INC_VHDL_HELPER_HH
|
||||
|
||||
#include <fstream>
|
||||
#include <list>
|
||||
#include <cassert>
|
||||
|
||||
template <class T>
|
||||
void emit_children(std::ostream &of,
|
||||
const std::list<T*> &children,
|
||||
int level, const char *delim = "",
|
||||
bool trailing_newline = true)
|
||||
{
|
||||
// Don't indent if there are no children
|
||||
if (children.size() == 0)
|
||||
newline(of, level);
|
||||
else {
|
||||
typename std::list<T*>::const_iterator it;
|
||||
int sz = children.size();
|
||||
for (it = children.begin(); it != children.end(); ++it) {
|
||||
newline(of, indent(level));
|
||||
(*it)->emit(of, indent(level));
|
||||
if (--sz > 0)
|
||||
of << delim;
|
||||
}
|
||||
if (trailing_newline)
|
||||
newline(of, level);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void delete_children(std::list<T*> &children)
|
||||
{
|
||||
typename std::list<T*>::iterator it;
|
||||
for (it = children.begin(); it != children.end(); ++it)
|
||||
delete *it;
|
||||
children.clear();
|
||||
}
|
||||
|
||||
static inline char vl_to_vhdl_bit(char bit)
|
||||
{
|
||||
switch (bit) {
|
||||
case '0':
|
||||
case 'Z':
|
||||
case '1':
|
||||
return bit;
|
||||
case 'z':
|
||||
return 'Z';
|
||||
case 'x':
|
||||
case 'X':
|
||||
return 'U';
|
||||
case '?':
|
||||
return '-';
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,944 @@
|
||||
/*
|
||||
* VHDL abstract syntax elements.
|
||||
*
|
||||
* 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_helper.hh"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <typeinfo>
|
||||
|
||||
vhdl_scope::vhdl_scope()
|
||||
: parent_(NULL), init_(false), sig_assign_(true)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
vhdl_scope::~vhdl_scope()
|
||||
{
|
||||
delete_children<vhdl_decl>(decls_);
|
||||
}
|
||||
|
||||
void vhdl_scope::set_initializing(bool i)
|
||||
{
|
||||
init_ = i;
|
||||
if (parent_)
|
||||
parent_->set_initializing(i);
|
||||
}
|
||||
|
||||
void vhdl_scope::add_decl(vhdl_decl *decl)
|
||||
{
|
||||
decls_.push_back(decl);
|
||||
}
|
||||
|
||||
void vhdl_scope::add_forward_decl(vhdl_decl *decl)
|
||||
{
|
||||
decls_.push_front(decl);
|
||||
}
|
||||
|
||||
vhdl_decl *vhdl_scope::get_decl(const std::string &name) const
|
||||
{
|
||||
decl_list_t::const_iterator it;
|
||||
for (it = decls_.begin(); it != decls_.end(); ++it) {
|
||||
if ((*it)->get_name() == name)
|
||||
return *it;
|
||||
}
|
||||
|
||||
return parent_ ? parent_->get_decl(name) : NULL;
|
||||
}
|
||||
|
||||
bool vhdl_scope::have_declared(const std::string &name) const
|
||||
{
|
||||
return get_decl(name) != NULL;
|
||||
}
|
||||
|
||||
bool vhdl_scope::contained_within(const vhdl_scope *other) const
|
||||
{
|
||||
if (this == other)
|
||||
return true;
|
||||
else if (NULL == parent_)
|
||||
return false;
|
||||
else
|
||||
return parent_->contained_within(other);
|
||||
}
|
||||
|
||||
vhdl_scope *vhdl_scope::get_parent() const
|
||||
{
|
||||
assert(parent_);
|
||||
return parent_;
|
||||
}
|
||||
|
||||
vhdl_entity::vhdl_entity(const char *name, const char *derived_from,
|
||||
vhdl_arch *arch)
|
||||
: name_(name), arch_(arch), derived_from_(derived_from)
|
||||
{
|
||||
arch->get_scope()->set_parent(&ports_);
|
||||
}
|
||||
|
||||
vhdl_entity::~vhdl_entity()
|
||||
{
|
||||
delete arch_;
|
||||
}
|
||||
|
||||
void vhdl_entity::add_port(vhdl_port_decl *decl)
|
||||
{
|
||||
ports_.add_decl(decl);
|
||||
}
|
||||
|
||||
void vhdl_entity::emit(std::ostream &of, int level) const
|
||||
{
|
||||
// Pretty much every design will use std_logic so we
|
||||
// might as well include it by default
|
||||
of << "library ieee;" << std::endl;
|
||||
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 << std::endl;
|
||||
|
||||
emit_comment(of, level);
|
||||
of << "entity " << name_ << " is";
|
||||
|
||||
if (!ports_.empty()) {
|
||||
newline(of, indent(level));
|
||||
of << "port (";
|
||||
emit_children<vhdl_decl>(of, ports_.get_decls(), indent(level), ";");
|
||||
of << ");";
|
||||
}
|
||||
|
||||
newline(of, level);
|
||||
of << "end entity; ";
|
||||
blank_line(of, level); // Extra blank line after entities
|
||||
arch_->emit(of, level);
|
||||
}
|
||||
|
||||
vhdl_arch::~vhdl_arch()
|
||||
{
|
||||
delete_children<vhdl_conc_stmt>(stmts_);
|
||||
}
|
||||
|
||||
void vhdl_arch::add_stmt(vhdl_process *proc)
|
||||
{
|
||||
proc->get_scope()->set_parent(&scope_);
|
||||
stmts_.push_back(proc);
|
||||
}
|
||||
|
||||
void vhdl_arch::add_stmt(vhdl_conc_stmt *stmt)
|
||||
{
|
||||
stmts_.push_back(stmt);
|
||||
}
|
||||
|
||||
void vhdl_arch::emit(std::ostream &of, int level) const
|
||||
{
|
||||
emit_comment(of, level);
|
||||
of << "architecture " << name_ << " of " << entity_;
|
||||
of << " is";
|
||||
emit_children<vhdl_decl>(of, scope_.get_decls(), level);
|
||||
of << "begin";
|
||||
emit_children<vhdl_conc_stmt>(of, stmts_, level);
|
||||
of << "end architecture;";
|
||||
blank_line(of, level); // Extra blank line after architectures;
|
||||
}
|
||||
|
||||
void vhdl_process::add_sensitivity(const std::string &name)
|
||||
{
|
||||
sens_.push_back(name);
|
||||
}
|
||||
|
||||
void vhdl_process::emit(std::ostream &of, int level) const
|
||||
{
|
||||
// If there are no statements in the body, this process
|
||||
// can't possibly do anything, so don't bother to emit it
|
||||
if (stmts_.empty()) {
|
||||
of << "-- Removed one empty process";
|
||||
newline(of, level);
|
||||
return;
|
||||
}
|
||||
|
||||
newline(of, level);
|
||||
emit_comment(of, level);
|
||||
if (name_.size() > 0)
|
||||
of << name_ << ": ";
|
||||
of << "process ";
|
||||
|
||||
int num_sens = sens_.size();
|
||||
if (num_sens > 0) {
|
||||
of << "(";
|
||||
string_list_t::const_iterator it;
|
||||
for (it = sens_.begin(); it != sens_.end(); ++it) {
|
||||
of << *it;
|
||||
if (--num_sens > 0)
|
||||
of << ", ";
|
||||
}
|
||||
of << ") ";
|
||||
}
|
||||
|
||||
of << "is";
|
||||
emit_children<vhdl_decl>(of, scope_.get_decls(), level);
|
||||
of << "begin";
|
||||
stmts_.emit(of, level);
|
||||
of << "end process;";
|
||||
}
|
||||
|
||||
stmt_container::~stmt_container()
|
||||
{
|
||||
delete_children<vhdl_seq_stmt>(stmts_);
|
||||
}
|
||||
|
||||
void stmt_container::add_stmt(vhdl_seq_stmt *stmt)
|
||||
{
|
||||
stmts_.push_back(stmt);
|
||||
}
|
||||
|
||||
void stmt_container::emit(std::ostream &of, int level, bool newline) const
|
||||
{
|
||||
emit_children<vhdl_seq_stmt>(of, stmts_, level, "", newline);
|
||||
}
|
||||
|
||||
vhdl_comp_inst::vhdl_comp_inst(const char *inst_name, const char *comp_name)
|
||||
: comp_name_(comp_name), inst_name_(inst_name)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
vhdl_comp_inst::~vhdl_comp_inst()
|
||||
{
|
||||
port_map_list_t::iterator it;
|
||||
for (it = mapping_.begin(); it != mapping_.end(); ++it) {
|
||||
delete (*it).expr;
|
||||
}
|
||||
mapping_.clear();
|
||||
}
|
||||
|
||||
void vhdl_comp_inst::map_port(const char *name, vhdl_expr *expr)
|
||||
{
|
||||
port_map_t pmap = { name, expr };
|
||||
mapping_.push_back(pmap);
|
||||
}
|
||||
|
||||
void vhdl_comp_inst::emit(std::ostream &of, int level) const
|
||||
{
|
||||
newline(of, level);
|
||||
emit_comment(of, level);
|
||||
of << inst_name_ << ": " << comp_name_;
|
||||
|
||||
// If there are no ports or generics we don't need to mention them...
|
||||
if (mapping_.size() > 0) {
|
||||
newline(of, indent(level));
|
||||
of << "port map (";
|
||||
|
||||
int sz = mapping_.size();
|
||||
port_map_list_t::const_iterator it;
|
||||
for (it = mapping_.begin(); it != mapping_.end(); ++it) {
|
||||
newline(of, indent(indent(level)));
|
||||
of << (*it).name << " => ";
|
||||
(*it).expr->emit(of, level);
|
||||
if (--sz > 0)
|
||||
of << ",";
|
||||
}
|
||||
newline(of, indent(level));
|
||||
of << ")";
|
||||
}
|
||||
|
||||
of << ";";
|
||||
}
|
||||
|
||||
vhdl_component_decl::vhdl_component_decl(const char *name)
|
||||
: vhdl_decl(name)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a component declaration for the given entity.
|
||||
*/
|
||||
vhdl_component_decl *vhdl_component_decl::component_decl_for(vhdl_entity *ent)
|
||||
{
|
||||
assert(ent != NULL);
|
||||
|
||||
vhdl_component_decl *decl = new vhdl_component_decl
|
||||
(ent->get_name().c_str());
|
||||
|
||||
decl->ports_ = ent->get_scope()->get_decls();
|
||||
|
||||
return decl;
|
||||
}
|
||||
|
||||
void vhdl_component_decl::emit(std::ostream &of, int level) const
|
||||
{
|
||||
newline(of, level);
|
||||
emit_comment(of, level);
|
||||
of << "component " << name_ << " is";
|
||||
|
||||
if (ports_.size() > 0) {
|
||||
newline(of, indent(level));
|
||||
of << "port (";
|
||||
emit_children<vhdl_decl>(of, ports_, indent(level), ";");
|
||||
of << ");";
|
||||
}
|
||||
|
||||
newline(of, level);
|
||||
of << "end component;";
|
||||
}
|
||||
|
||||
vhdl_wait_stmt::~vhdl_wait_stmt()
|
||||
{
|
||||
if (expr_ != NULL)
|
||||
delete expr_;
|
||||
}
|
||||
|
||||
void vhdl_wait_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "wait";
|
||||
|
||||
switch (type_) {
|
||||
case VHDL_WAIT_INDEF:
|
||||
break;
|
||||
case VHDL_WAIT_FOR:
|
||||
assert(expr_);
|
||||
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 << ";";
|
||||
}
|
||||
|
||||
vhdl_decl::~vhdl_decl()
|
||||
{
|
||||
if (type_ != NULL)
|
||||
delete type_;
|
||||
if (initial_ != NULL)
|
||||
delete initial_;
|
||||
}
|
||||
|
||||
const vhdl_type *vhdl_decl::get_type() const
|
||||
{
|
||||
assert(type_);
|
||||
return type_;
|
||||
}
|
||||
|
||||
void vhdl_decl::set_initial(vhdl_expr *initial)
|
||||
{
|
||||
if (!has_initial_) {
|
||||
assert(initial_ == NULL);
|
||||
initial_ = initial;
|
||||
has_initial_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void vhdl_port_decl::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << name_ << " : ";
|
||||
|
||||
switch (mode_) {
|
||||
case VHDL_PORT_IN:
|
||||
of << "in ";
|
||||
break;
|
||||
case VHDL_PORT_OUT:
|
||||
of << "out ";
|
||||
break;
|
||||
case VHDL_PORT_INOUT:
|
||||
of << "inout ";
|
||||
break;
|
||||
case VHDL_PORT_BUFFER:
|
||||
of << "buffer ";
|
||||
break;
|
||||
}
|
||||
|
||||
type_->emit(of, level);
|
||||
}
|
||||
|
||||
void vhdl_var_decl::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "variable " << name_ << " : ";
|
||||
type_->emit(of, level);
|
||||
|
||||
if (initial_) {
|
||||
of << " := ";
|
||||
initial_->emit(of, level);
|
||||
}
|
||||
|
||||
of << ";";
|
||||
emit_comment(of, level, true);
|
||||
}
|
||||
|
||||
void vhdl_signal_decl::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "signal " << name_ << " : ";
|
||||
type_->emit(of, level);
|
||||
|
||||
if (initial_) {
|
||||
of << " := ";
|
||||
initial_->emit(of, level);
|
||||
}
|
||||
|
||||
of << ";";
|
||||
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_;
|
||||
}
|
||||
|
||||
void vhdl_expr_list::add_expr(vhdl_expr *e)
|
||||
{
|
||||
exprs_.push_back(e);
|
||||
}
|
||||
|
||||
vhdl_expr_list::~vhdl_expr_list()
|
||||
{
|
||||
delete_children<vhdl_expr>(exprs_);
|
||||
}
|
||||
|
||||
void vhdl_expr_list::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "(";
|
||||
|
||||
int size = exprs_.size();
|
||||
std::list<vhdl_expr*>::const_iterator it;
|
||||
for (it = exprs_.begin(); it != exprs_.end(); ++it) {
|
||||
(*it)->emit(of, level);
|
||||
if (--size > 0)
|
||||
of << ", ";
|
||||
}
|
||||
|
||||
of << ")";
|
||||
}
|
||||
|
||||
void vhdl_pcall_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << name_;
|
||||
if (!exprs_.empty())
|
||||
exprs_.emit(of, level);
|
||||
of << ";";
|
||||
}
|
||||
|
||||
vhdl_var_ref::~vhdl_var_ref()
|
||||
{
|
||||
if (slice_)
|
||||
delete slice_;
|
||||
}
|
||||
|
||||
void vhdl_var_ref::set_slice(vhdl_expr *s, int w)
|
||||
{
|
||||
assert(type_);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
void vhdl_var_ref::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << name_;
|
||||
if (slice_) {
|
||||
of << "(";
|
||||
if (slice_width_ > 0) {
|
||||
slice_->emit(of, level);
|
||||
of << " + " << slice_width_ << " downto ";
|
||||
}
|
||||
slice_->emit(of, level);
|
||||
of << ")";
|
||||
}
|
||||
}
|
||||
|
||||
void vhdl_const_string::emit(std::ostream &of, int level) const
|
||||
{
|
||||
// In some instances a string literal can be ambiguous between
|
||||
// a String type and some other types (e.g. std_logic_vector)
|
||||
// The explicit cast to String removes this ambiguity (although
|
||||
// isn't always strictly necessary)
|
||||
of << "String'(\"" << value_ << "\")";
|
||||
}
|
||||
|
||||
void vhdl_null_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "null;";
|
||||
}
|
||||
|
||||
void vhdl_fcall::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << name_;
|
||||
exprs_.emit(of, level);
|
||||
}
|
||||
|
||||
vhdl_abstract_assign_stmt::~vhdl_abstract_assign_stmt()
|
||||
{
|
||||
delete lhs_;
|
||||
delete rhs_;
|
||||
if (after_)
|
||||
delete after_;
|
||||
}
|
||||
|
||||
void vhdl_nbassign_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
lhs_->emit(of, level);
|
||||
of << " <= ";
|
||||
rhs_->emit(of, level);
|
||||
|
||||
if (after_) {
|
||||
of << " after ";
|
||||
after_->emit(of, level);
|
||||
}
|
||||
|
||||
of << ";";
|
||||
}
|
||||
|
||||
void vhdl_assign_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
lhs_->emit(of, level);
|
||||
of << " := ";
|
||||
rhs_->emit(of, level);
|
||||
of << ";";
|
||||
}
|
||||
|
||||
vhdl_const_bits::vhdl_const_bits(const char *value, int width, bool issigned)
|
||||
: vhdl_expr(issigned ? vhdl_type::nsigned(width)
|
||||
: vhdl_type::nunsigned(width), true),
|
||||
qualified_(false),
|
||||
signed_(issigned)
|
||||
{
|
||||
// Can't rely on value being NULL-terminated
|
||||
while (width--)
|
||||
value_.push_back(*value++);
|
||||
}
|
||||
|
||||
void vhdl_const_bits::emit(std::ostream &of, int level) const
|
||||
{
|
||||
if (qualified_)
|
||||
of << (signed_ ? "signed" : "unsigned") << "'(\"";
|
||||
else
|
||||
of << "\"";
|
||||
|
||||
// The bits appear to be in reverse order
|
||||
std::string::const_reverse_iterator it;
|
||||
for (it = value_.rbegin(); it != value_.rend(); ++it)
|
||||
of << vl_to_vhdl_bit(*it);
|
||||
|
||||
of << (qualified_ ? "\")" : "\"");
|
||||
}
|
||||
|
||||
void vhdl_const_bit::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "'" << vl_to_vhdl_bit(bit_) << "'";
|
||||
}
|
||||
|
||||
void vhdl_const_int::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << value_;
|
||||
}
|
||||
|
||||
void vhdl_const_bool::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << (value_ ? "True" : "False");
|
||||
}
|
||||
|
||||
void vhdl_const_time::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << value_;
|
||||
switch (units_) {
|
||||
case TIME_UNIT_NS:
|
||||
of << " ns";
|
||||
}
|
||||
}
|
||||
|
||||
vhdl_cassign_stmt::~vhdl_cassign_stmt()
|
||||
{
|
||||
delete lhs_;
|
||||
delete rhs_;
|
||||
|
||||
for (std::list<when_part_t>::const_iterator it = whens_.begin();
|
||||
it != whens_.end();
|
||||
++it) {
|
||||
delete (*it).value;
|
||||
delete (*it).cond;
|
||||
}
|
||||
}
|
||||
|
||||
void vhdl_cassign_stmt::add_condition(vhdl_expr *value, vhdl_expr *cond)
|
||||
{
|
||||
when_part_t when = { value, cond, NULL };
|
||||
whens_.push_back(when);
|
||||
}
|
||||
|
||||
void vhdl_cassign_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
lhs_->emit(of, level);
|
||||
of << " <= ";
|
||||
if (!whens_.empty()) {
|
||||
for (std::list<when_part_t>::const_iterator it = whens_.begin();
|
||||
it != whens_.end();
|
||||
++it) {
|
||||
(*it).value->emit(of, level);
|
||||
of << " when ";
|
||||
(*it).cond->emit(of, level);
|
||||
of << " ";
|
||||
}
|
||||
of << "else ";
|
||||
}
|
||||
rhs_->emit(of, level);
|
||||
|
||||
if (after_) {
|
||||
of << " after ";
|
||||
after_->emit(of, level);
|
||||
}
|
||||
|
||||
of << ";";
|
||||
}
|
||||
|
||||
void vhdl_assert_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "assert false"; // TODO: Allow arbitrary expression
|
||||
of << " report \"" << reason_ << "\" severity failure;";
|
||||
}
|
||||
|
||||
vhdl_if_stmt::vhdl_if_stmt(vhdl_expr *test)
|
||||
{
|
||||
// Need to ensure that the expression is Boolean
|
||||
vhdl_type boolean(VHDL_TYPE_BOOLEAN);
|
||||
test_ = test->cast(&boolean);
|
||||
}
|
||||
|
||||
vhdl_if_stmt::~vhdl_if_stmt()
|
||||
{
|
||||
delete test_;
|
||||
}
|
||||
|
||||
void vhdl_if_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "if ";
|
||||
test_->emit(of, level);
|
||||
of << " then";
|
||||
then_part_.emit(of, level);
|
||||
if (!else_part_.empty()) {
|
||||
of << "else";
|
||||
else_part_.emit(of, level);
|
||||
}
|
||||
of << "end if;";
|
||||
}
|
||||
|
||||
vhdl_unaryop_expr::~vhdl_unaryop_expr()
|
||||
{
|
||||
delete operand_;
|
||||
}
|
||||
|
||||
void vhdl_unaryop_expr::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "(";
|
||||
switch (op_) {
|
||||
case VHDL_UNARYOP_NOT:
|
||||
of << "not ";
|
||||
break;
|
||||
case VHDL_UNARYOP_NEG:
|
||||
of << "-";
|
||||
break;
|
||||
}
|
||||
operand_->emit(of, level);
|
||||
of << ")";
|
||||
}
|
||||
|
||||
vhdl_binop_expr::vhdl_binop_expr(vhdl_expr *left, vhdl_binop_t op,
|
||||
vhdl_expr *right, vhdl_type *type)
|
||||
: vhdl_expr(type), op_(op)
|
||||
{
|
||||
add_expr(left);
|
||||
add_expr(right);
|
||||
}
|
||||
|
||||
vhdl_binop_expr::~vhdl_binop_expr()
|
||||
{
|
||||
delete_children<vhdl_expr>(operands_);
|
||||
}
|
||||
|
||||
void vhdl_binop_expr::add_expr(vhdl_expr *e)
|
||||
{
|
||||
operands_.push_back(e);
|
||||
}
|
||||
|
||||
void vhdl_binop_expr::emit(std::ostream &of, int level) const
|
||||
{
|
||||
// Expressions are fully parenthesized to remove any
|
||||
// ambiguity in the output
|
||||
|
||||
of << "(";
|
||||
|
||||
assert(operands_.size() > 0);
|
||||
std::list<vhdl_expr*>::const_iterator it = operands_.begin();
|
||||
|
||||
(*it)->emit(of, level);
|
||||
while (++it != operands_.end()) {
|
||||
const char* ops[] = {
|
||||
"and", "or", "=", "/=", "+", "-", "*", "<",
|
||||
">", "<=", ">=", "sll", "srl", "xor", "&",
|
||||
"nand", "nor", "xnor", "/", "mod", NULL
|
||||
};
|
||||
|
||||
of << " " << ops[op_] << " ";
|
||||
|
||||
(*it)->emit(of, level);
|
||||
}
|
||||
|
||||
of << ")";
|
||||
}
|
||||
|
||||
vhdl_bit_spec_expr::~vhdl_bit_spec_expr()
|
||||
{
|
||||
if (others_)
|
||||
delete others_;
|
||||
|
||||
std::list<bit_map>::iterator it;
|
||||
for (it = bits_.begin(); it != bits_.end(); ++it)
|
||||
delete (*it).e;
|
||||
}
|
||||
|
||||
void vhdl_bit_spec_expr::add_bit(int bit, vhdl_expr *e)
|
||||
{
|
||||
bit_map bm = { bit, e };
|
||||
bits_.push_back(bm);
|
||||
}
|
||||
|
||||
void vhdl_bit_spec_expr::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "(";
|
||||
|
||||
std::list<bit_map>::const_iterator it;
|
||||
it = bits_.begin();
|
||||
while (it != bits_.end()) {
|
||||
of << (*it).bit << " => ";
|
||||
(*it).e->emit(of, level);
|
||||
if (++it != bits_.end())
|
||||
of << ", ";
|
||||
}
|
||||
|
||||
if (others_) {
|
||||
of << (bits_.empty() ? "" : ", ") << "others => ";
|
||||
others_->emit(of, level);
|
||||
}
|
||||
|
||||
of << ")";
|
||||
}
|
||||
|
||||
vhdl_case_branch::~vhdl_case_branch()
|
||||
{
|
||||
delete when_;
|
||||
}
|
||||
|
||||
void vhdl_case_branch::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "when ";
|
||||
when_->emit(of, level);
|
||||
of << " =>";
|
||||
stmts_.emit(of, indent(level), false);
|
||||
}
|
||||
|
||||
vhdl_case_stmt::~vhdl_case_stmt()
|
||||
{
|
||||
delete test_;
|
||||
}
|
||||
|
||||
void vhdl_case_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "case ";
|
||||
test_->emit(of, level);
|
||||
of << " is";
|
||||
newline(of, indent(level));
|
||||
|
||||
case_branch_list_t::const_iterator it;
|
||||
int n = branches_.size();
|
||||
for (it = branches_.begin(); it != branches_.end(); ++it) {
|
||||
(*it)->emit(of, level);
|
||||
if (--n > 0)
|
||||
newline(of, indent(level));
|
||||
else
|
||||
newline(of, level);
|
||||
}
|
||||
|
||||
of << "end case;";
|
||||
}
|
||||
|
||||
vhdl_while_stmt::~vhdl_while_stmt()
|
||||
{
|
||||
delete test_;
|
||||
}
|
||||
|
||||
void vhdl_while_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "while ";
|
||||
test_->emit(of, level);
|
||||
of << " ";
|
||||
vhdl_loop_stmt::emit(of, level);
|
||||
}
|
||||
|
||||
void vhdl_loop_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "loop";
|
||||
stmts_.emit(of, level);
|
||||
of << "end loop;";
|
||||
}
|
||||
|
||||
vhdl_for_stmt::~vhdl_for_stmt()
|
||||
{
|
||||
delete from_;
|
||||
delete to_;
|
||||
}
|
||||
|
||||
void vhdl_for_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "for " << lname_ << " in ";
|
||||
from_->emit(of, level);
|
||||
of << " to ";
|
||||
to_->emit(of, level);
|
||||
of << " ";
|
||||
vhdl_loop_stmt::emit(of, level);
|
||||
}
|
||||
|
||||
vhdl_function::vhdl_function(const char *name, vhdl_type *ret_type)
|
||||
: vhdl_decl(name, ret_type)
|
||||
{
|
||||
// A function contains two scopes:
|
||||
// scope_ = The paramters
|
||||
// variables_ = Local variables
|
||||
// A call to get_scope returns variables_ whose parent is scope_
|
||||
variables_.set_parent(&scope_);
|
||||
}
|
||||
|
||||
void vhdl_function::emit(std::ostream &of, int level) const
|
||||
{
|
||||
newline(of, level);
|
||||
emit_comment(of, level);
|
||||
of << "function " << name_ << " (";
|
||||
emit_children<vhdl_decl>(of, scope_.get_decls(), level, ";");
|
||||
of << ") ";
|
||||
newline(of, level);
|
||||
of << "return " << type_->get_string() << " is";
|
||||
emit_children<vhdl_decl>(of, variables_.get_decls(), level);
|
||||
of << "begin";
|
||||
stmts_.emit(of, level);
|
||||
of << " return Verilog_Result;";
|
||||
newline(of, level);
|
||||
of << "end function;";
|
||||
}
|
||||
|
||||
void vhdl_forward_fdecl::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "function " << f_->get_name() << " (";
|
||||
emit_children<vhdl_decl>(of, f_->scope_.get_decls(), level, ";");
|
||||
of << ") ";
|
||||
newline(of, level);
|
||||
of << "return " << f_->type_->get_string() << ";";
|
||||
newline(of, level);
|
||||
}
|
||||
|
||||
void vhdl_param_decl::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << name_ << " : ";
|
||||
type_->emit(of, level);
|
||||
}
|
||||
|
||||
vhdl_with_select_stmt::~vhdl_with_select_stmt()
|
||||
{
|
||||
delete test_;
|
||||
delete out_;
|
||||
|
||||
for (when_list_t::const_iterator it = whens_.begin();
|
||||
it != whens_.end();
|
||||
++it) {
|
||||
delete (*it).value;
|
||||
delete (*it).cond;
|
||||
if ((*it).delay)
|
||||
delete (*it).delay;
|
||||
}
|
||||
}
|
||||
|
||||
void vhdl_with_select_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "with ";
|
||||
test_->emit(of, level);
|
||||
of << " select";
|
||||
emit_comment(of, level, true);
|
||||
newline(of, indent(level));
|
||||
|
||||
out_->emit(of, level);
|
||||
of << " <= ";
|
||||
|
||||
when_list_t::const_iterator it = whens_.begin();
|
||||
while (it != whens_.end()) {
|
||||
(*it).value->emit(of, level);
|
||||
if ((*it).delay) {
|
||||
of << " after ";
|
||||
(*it).delay->emit(of, level);
|
||||
}
|
||||
of << " when ";
|
||||
(*it).cond->emit(of, level);
|
||||
|
||||
if (++it != whens_.end()) {
|
||||
of << ",";
|
||||
newline(of, indent(level));
|
||||
}
|
||||
else
|
||||
of << ";";
|
||||
}
|
||||
}
|
||||
|
||||
void vhdl_with_select_stmt::add_condition(vhdl_expr *value, vhdl_expr *cond, vhdl_expr *delay)
|
||||
{
|
||||
when_part_t when = { value, cond, delay };
|
||||
whens_.push_back(when);
|
||||
}
|
||||
@@ -0,0 +1,782 @@
|
||||
/*
|
||||
* VHDL abstract syntax elements.
|
||||
*
|
||||
* 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_VHDL_SYNTAX_HH
|
||||
#define INC_VHDL_SYNTAX_HH
|
||||
|
||||
#include "vhdl_element.hh"
|
||||
#include "vhdl_type.hh"
|
||||
|
||||
class vhdl_scope;
|
||||
class vhdl_entity;
|
||||
class vhdl_arch;
|
||||
|
||||
class vhdl_expr : public vhdl_element {
|
||||
public:
|
||||
vhdl_expr(vhdl_type* type, bool isconst=false)
|
||||
: type_(type), isconst_(isconst) {}
|
||||
virtual ~vhdl_expr();
|
||||
|
||||
const vhdl_type *get_type() const { return type_; }
|
||||
bool constant() const { return isconst_; }
|
||||
virtual vhdl_expr *cast(const vhdl_type *to);
|
||||
virtual vhdl_expr *resize(int newwidth);
|
||||
protected:
|
||||
vhdl_type *type_;
|
||||
bool isconst_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A scalar or array variable reference.
|
||||
*/
|
||||
class vhdl_var_ref : public vhdl_expr {
|
||||
public:
|
||||
vhdl_var_ref(const char *name, vhdl_type *type,
|
||||
vhdl_expr *slice = NULL)
|
||||
: vhdl_expr(type), name_(name), slice_(slice) {}
|
||||
~vhdl_var_ref();
|
||||
|
||||
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_;
|
||||
vhdl_expr *slice_;
|
||||
unsigned slice_width_;
|
||||
};
|
||||
|
||||
|
||||
enum vhdl_binop_t {
|
||||
VHDL_BINOP_AND = 0,
|
||||
VHDL_BINOP_OR,
|
||||
VHDL_BINOP_EQ,
|
||||
VHDL_BINOP_NEQ,
|
||||
VHDL_BINOP_ADD,
|
||||
VHDL_BINOP_SUB,
|
||||
VHDL_BINOP_MULT,
|
||||
VHDL_BINOP_LT,
|
||||
VHDL_BINOP_GT,
|
||||
VHDL_BINOP_LEQ,
|
||||
VHDL_BINOP_GEQ,
|
||||
VHDL_BINOP_SL,
|
||||
VHDL_BINOP_SR,
|
||||
VHDL_BINOP_XOR,
|
||||
VHDL_BINOP_CONCAT,
|
||||
VHDL_BINOP_NAND,
|
||||
VHDL_BINOP_NOR,
|
||||
VHDL_BINOP_XNOR,
|
||||
VHDL_BINOP_DIV,
|
||||
VHDL_BINOP_MOD,
|
||||
};
|
||||
|
||||
/*
|
||||
* A binary expression contains a list of operands rather
|
||||
* than just two: this is to model n-input gates and the
|
||||
* like. A second constructor is provided to handle the
|
||||
* common case of a true binary expression.
|
||||
*/
|
||||
class vhdl_binop_expr : public vhdl_expr {
|
||||
public:
|
||||
vhdl_binop_expr(vhdl_binop_t op, vhdl_type *type)
|
||||
: vhdl_expr(type), op_(op) {}
|
||||
vhdl_binop_expr(vhdl_expr *left, vhdl_binop_t op,
|
||||
vhdl_expr *right, vhdl_type *type);
|
||||
~vhdl_binop_expr();
|
||||
|
||||
void add_expr(vhdl_expr *e);
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
std::list<vhdl_expr*> operands_;
|
||||
vhdl_binop_t op_;
|
||||
};
|
||||
|
||||
|
||||
enum vhdl_unaryop_t {
|
||||
VHDL_UNARYOP_NOT,
|
||||
VHDL_UNARYOP_NEG,
|
||||
};
|
||||
|
||||
class vhdl_unaryop_expr : public vhdl_expr {
|
||||
public:
|
||||
vhdl_unaryop_expr(vhdl_unaryop_t op, vhdl_expr *operand,
|
||||
vhdl_type *type)
|
||||
: vhdl_expr(type), op_(op), operand_(operand) {}
|
||||
~vhdl_unaryop_expr();
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
vhdl_unaryop_t op_;
|
||||
vhdl_expr *operand_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* An expression like (0 => '1', 2 => '0', others => 'Z')
|
||||
*/
|
||||
class vhdl_bit_spec_expr : public vhdl_expr {
|
||||
public:
|
||||
vhdl_bit_spec_expr(vhdl_type *type, vhdl_expr *others)
|
||||
: vhdl_expr(type), others_(others) {}
|
||||
~vhdl_bit_spec_expr();
|
||||
|
||||
void add_bit(int bit, vhdl_expr *e);
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
vhdl_expr *others_;
|
||||
struct bit_map {
|
||||
int bit;
|
||||
vhdl_expr *e;
|
||||
};
|
||||
std::list<bit_map> bits_;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_const_string : public vhdl_expr {
|
||||
public:
|
||||
vhdl_const_string(const char *value)
|
||||
: vhdl_expr(vhdl_type::string(), true), value_(value) {}
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
std::string value_;
|
||||
};
|
||||
|
||||
class vhdl_const_bits : public vhdl_expr {
|
||||
public:
|
||||
vhdl_const_bits(const char *value, int width, bool issigned);
|
||||
void emit(std::ostream &of, int level) const;
|
||||
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_;
|
||||
};
|
||||
|
||||
class vhdl_const_bit : public vhdl_expr {
|
||||
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_;
|
||||
};
|
||||
|
||||
enum time_unit_t {
|
||||
TIME_UNIT_NS,
|
||||
};
|
||||
|
||||
class vhdl_const_time : public vhdl_expr {
|
||||
public:
|
||||
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:
|
||||
int64_t value_;
|
||||
time_unit_t units_;
|
||||
};
|
||||
|
||||
class vhdl_const_int : public vhdl_expr {
|
||||
public:
|
||||
vhdl_const_int(int64_t value)
|
||||
: vhdl_expr(vhdl_type::integer(), true), value_(value) {}
|
||||
void emit(std::ostream &of, int level) const;
|
||||
vhdl_expr *cast(const vhdl_type *to);
|
||||
private:
|
||||
int64_t value_;
|
||||
};
|
||||
|
||||
class vhdl_const_bool : public vhdl_expr {
|
||||
public:
|
||||
vhdl_const_bool(bool value)
|
||||
: vhdl_expr(vhdl_type::boolean(), true), value_(value) {}
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
bool value_;
|
||||
};
|
||||
|
||||
class vhdl_expr_list : public vhdl_element {
|
||||
public:
|
||||
~vhdl_expr_list();
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
bool empty() const { return exprs_.empty(); }
|
||||
void add_expr(vhdl_expr *e);
|
||||
private:
|
||||
std::list<vhdl_expr*> exprs_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A function call within an expression.
|
||||
*/
|
||||
class vhdl_fcall : public vhdl_expr {
|
||||
public:
|
||||
vhdl_fcall(const char *name, vhdl_type *rtype)
|
||||
: vhdl_expr(rtype), name_(name) {};
|
||||
~vhdl_fcall() {}
|
||||
|
||||
void add_expr(vhdl_expr *e) { exprs_.add_expr(e); }
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
std::string name_;
|
||||
vhdl_expr_list exprs_;
|
||||
};
|
||||
|
||||
/*
|
||||
* A concurrent statement appears in architecture bodies/
|
||||
*/
|
||||
class vhdl_conc_stmt : public vhdl_element {
|
||||
public:
|
||||
virtual ~vhdl_conc_stmt() {}
|
||||
};
|
||||
|
||||
typedef std::list<vhdl_conc_stmt*> conc_stmt_list_t;
|
||||
|
||||
/*
|
||||
* A '<value> when <cond>' clause that appears in several
|
||||
* statement types.
|
||||
*/
|
||||
struct when_part_t {
|
||||
vhdl_expr *value, *cond, *delay;
|
||||
};
|
||||
typedef std::list<when_part_t> when_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:
|
||||
vhdl_cassign_stmt(vhdl_var_ref *lhs, vhdl_expr *rhs)
|
||||
: lhs_(lhs), rhs_(rhs), after_(NULL) {}
|
||||
~vhdl_cassign_stmt();
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
void add_condition(vhdl_expr *value, vhdl_expr *cond);
|
||||
void set_after(vhdl_expr *a) { after_ = a; }
|
||||
private:
|
||||
vhdl_var_ref *lhs_;
|
||||
vhdl_expr *rhs_;
|
||||
vhdl_expr *after_;
|
||||
when_list_t whens_;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_with_select_stmt : public vhdl_conc_stmt {
|
||||
public:
|
||||
vhdl_with_select_stmt(vhdl_expr *test, vhdl_var_ref *out)
|
||||
: test_(test), out_(out) {}
|
||||
~vhdl_with_select_stmt();
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
void add_condition(vhdl_expr *value, vhdl_expr *cond, vhdl_expr *delay=NULL);
|
||||
private:
|
||||
vhdl_expr *test_;
|
||||
vhdl_var_ref *out_;
|
||||
when_list_t whens_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Any sequential statement in a process.
|
||||
*/
|
||||
class vhdl_seq_stmt : public vhdl_element {
|
||||
public:
|
||||
virtual ~vhdl_seq_stmt() {}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A list of sequential statements. For example inside a
|
||||
* process, loop, or if statement.
|
||||
*/
|
||||
class stmt_container {
|
||||
public:
|
||||
~stmt_container();
|
||||
|
||||
void add_stmt(vhdl_seq_stmt *stmt);
|
||||
void emit(std::ostream &of, int level, bool newline=true) const;
|
||||
bool empty() const { return stmts_.empty(); }
|
||||
private:
|
||||
std::list<vhdl_seq_stmt*> stmts_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Shared between blocking and non-blocking assignment.
|
||||
*/
|
||||
class vhdl_abstract_assign_stmt : public vhdl_seq_stmt {
|
||||
public:
|
||||
vhdl_abstract_assign_stmt(vhdl_var_ref *lhs, vhdl_expr *rhs)
|
||||
: lhs_(lhs), rhs_(rhs), after_(NULL) {}
|
||||
virtual ~vhdl_abstract_assign_stmt();
|
||||
|
||||
void set_after(vhdl_expr *after) { after_ = after; }
|
||||
protected:
|
||||
vhdl_var_ref *lhs_;
|
||||
vhdl_expr *rhs_, *after_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Similar to Verilog non-blocking assignment, except the LHS
|
||||
* must be a signal not a variable.
|
||||
*/
|
||||
class vhdl_nbassign_stmt : public vhdl_abstract_assign_stmt {
|
||||
public:
|
||||
vhdl_nbassign_stmt(vhdl_var_ref *lhs, vhdl_expr *rhs)
|
||||
: vhdl_abstract_assign_stmt(lhs, rhs) {}
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_assign_stmt : public vhdl_abstract_assign_stmt {
|
||||
public:
|
||||
vhdl_assign_stmt(vhdl_var_ref *lhs, vhdl_expr *rhs)
|
||||
: vhdl_abstract_assign_stmt(lhs, rhs) {}
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
};
|
||||
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
/*
|
||||
* Delay simulation indefinitely, until an event, or for a
|
||||
* specified time.
|
||||
*/
|
||||
class vhdl_wait_stmt : public vhdl_seq_stmt {
|
||||
public:
|
||||
vhdl_wait_stmt(vhdl_wait_type_t type = VHDL_WAIT_INDEF,
|
||||
vhdl_expr *expr = NULL)
|
||||
: type_(type), expr_(expr) {}
|
||||
~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_;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_null_stmt : public vhdl_seq_stmt {
|
||||
public:
|
||||
void emit(std::ostream &of, int level) const;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_assert_stmt : public vhdl_seq_stmt {
|
||||
public:
|
||||
vhdl_assert_stmt(const char *reason)
|
||||
: reason_(reason) {}
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
std::string reason_;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_if_stmt : public vhdl_seq_stmt {
|
||||
public:
|
||||
vhdl_if_stmt(vhdl_expr *test);
|
||||
~vhdl_if_stmt();
|
||||
|
||||
stmt_container *get_then_container() { return &then_part_; }
|
||||
stmt_container *get_else_container() { return &else_part_; }
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
vhdl_expr *test_;
|
||||
stmt_container then_part_, else_part_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A single branch in a case statement consisting of an
|
||||
* expression part and a statement container.
|
||||
*/
|
||||
class vhdl_case_branch : public vhdl_element {
|
||||
public:
|
||||
vhdl_case_branch(vhdl_expr *when) : when_(when) {}
|
||||
~vhdl_case_branch();
|
||||
|
||||
stmt_container *get_container() { return &stmts_; }
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
vhdl_expr *when_;
|
||||
stmt_container stmts_;
|
||||
};
|
||||
|
||||
typedef std::list<vhdl_case_branch*> case_branch_list_t;
|
||||
|
||||
class vhdl_case_stmt : public vhdl_seq_stmt {
|
||||
public:
|
||||
vhdl_case_stmt(vhdl_expr *test) : test_(test) {}
|
||||
~vhdl_case_stmt();
|
||||
|
||||
void add_branch(vhdl_case_branch *b) { branches_.push_back(b); }
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
vhdl_expr *test_;
|
||||
case_branch_list_t branches_;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_loop_stmt : public vhdl_seq_stmt {
|
||||
public:
|
||||
virtual ~vhdl_loop_stmt() {}
|
||||
|
||||
stmt_container *get_container() { return &stmts_; }
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
stmt_container stmts_;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_while_stmt : public vhdl_loop_stmt {
|
||||
public:
|
||||
vhdl_while_stmt(vhdl_expr *test) : test_(test) {}
|
||||
~vhdl_while_stmt();
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
vhdl_expr *test_;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_for_stmt : public vhdl_loop_stmt {
|
||||
public:
|
||||
vhdl_for_stmt(const char *lname, vhdl_expr *from, vhdl_expr *to)
|
||||
: lname_(lname), from_(from), to_(to) {}
|
||||
~vhdl_for_stmt();
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
const char *lname_;
|
||||
vhdl_expr *from_, *to_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A procedure call. Which is a statement, unlike a function
|
||||
* call which is an expression.
|
||||
*/
|
||||
class vhdl_pcall_stmt : public vhdl_seq_stmt {
|
||||
public:
|
||||
vhdl_pcall_stmt(const char *name) : name_(name) {}
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
void add_expr(vhdl_expr *e) { exprs_.add_expr(e); }
|
||||
private:
|
||||
std::string name_;
|
||||
vhdl_expr_list exprs_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A declaration of some sort (variable, component, etc.).
|
||||
* Declarations have names, which is the identifier of the variable,
|
||||
* constant, etc. not the type.
|
||||
*/
|
||||
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),
|
||||
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 has_initial_; }
|
||||
protected:
|
||||
std::string name_;
|
||||
vhdl_type *type_;
|
||||
vhdl_expr *initial_;
|
||||
bool has_initial_;
|
||||
};
|
||||
|
||||
typedef std::list<vhdl_decl*> decl_list_t;
|
||||
|
||||
|
||||
/*
|
||||
* A forward declaration of a component. At the moment it is assumed
|
||||
* that components declarations will only ever be for entities
|
||||
* generated by this code generator. This is enforced by making the
|
||||
* constructor private (use component_decl_for instead).
|
||||
*/
|
||||
class vhdl_component_decl : public vhdl_decl {
|
||||
public:
|
||||
static vhdl_component_decl *component_decl_for(vhdl_entity *ent);
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
vhdl_component_decl(const char *name);
|
||||
|
||||
decl_list_t ports_;
|
||||
};
|
||||
|
||||
|
||||
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).
|
||||
*/
|
||||
class vhdl_var_decl : public vhdl_decl {
|
||||
public:
|
||||
vhdl_var_decl(const char *name, vhdl_type *type)
|
||||
: vhdl_decl(name, type) {}
|
||||
void emit(std::ostream &of, int level) const;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A signal declaration in architecture.
|
||||
*/
|
||||
class vhdl_signal_decl : public vhdl_decl {
|
||||
public:
|
||||
vhdl_signal_decl(const char *name, vhdl_type *type)
|
||||
: vhdl_decl(name, type) {}
|
||||
virtual void emit(std::ostream &of, int level) const;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A parameter to a function.
|
||||
*/
|
||||
class vhdl_param_decl : public vhdl_decl {
|
||||
public:
|
||||
vhdl_param_decl(const char *name, vhdl_type *type)
|
||||
: vhdl_decl(name, type) {}
|
||||
void emit(std::ostream &of, int level) const;
|
||||
};
|
||||
|
||||
enum vhdl_port_mode_t {
|
||||
VHDL_PORT_IN,
|
||||
VHDL_PORT_OUT,
|
||||
VHDL_PORT_INOUT,
|
||||
VHDL_PORT_BUFFER,
|
||||
};
|
||||
|
||||
/*
|
||||
* A port declaration is like a signal declaration except
|
||||
* it has a direction and appears in the entity rather than
|
||||
* the architecture.
|
||||
*/
|
||||
class vhdl_port_decl : public vhdl_decl {
|
||||
public:
|
||||
vhdl_port_decl(const char *name, vhdl_type *type,
|
||||
vhdl_port_mode_t mode)
|
||||
: vhdl_decl(name, type), mode_(mode) {}
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
vhdl_port_mode_t get_mode() const { return mode_; }
|
||||
void set_mode(vhdl_port_mode_t m) { mode_ = m; }
|
||||
private:
|
||||
vhdl_port_mode_t mode_;
|
||||
};
|
||||
|
||||
/*
|
||||
* A mapping from port name to an expression.
|
||||
*/
|
||||
struct port_map_t {
|
||||
std::string name;
|
||||
vhdl_expr *expr;
|
||||
};
|
||||
|
||||
typedef std::list<port_map_t> port_map_list_t;
|
||||
|
||||
/*
|
||||
* Instantiation of component. This is really only a placeholder
|
||||
* at the moment until the port mappings are worked out.
|
||||
*/
|
||||
class vhdl_comp_inst : public vhdl_conc_stmt {
|
||||
public:
|
||||
vhdl_comp_inst(const char *inst_name, const char *comp_name);
|
||||
~vhdl_comp_inst();
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
void map_port(const char *name, vhdl_expr *expr);
|
||||
|
||||
const std::string &get_comp_name() const { return comp_name_; }
|
||||
const std::string &get_inst_name() const { return inst_name_; }
|
||||
private:
|
||||
std::string comp_name_, inst_name_;
|
||||
port_map_list_t mapping_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Contains a list of declarations in a hierarchy.
|
||||
* A scope can be `initializing' where assignments automatically
|
||||
* create initial values for declarations.
|
||||
*/
|
||||
class vhdl_scope {
|
||||
public:
|
||||
vhdl_scope();
|
||||
~vhdl_scope();
|
||||
|
||||
void add_decl(vhdl_decl *decl);
|
||||
void add_forward_decl(vhdl_decl *decl);
|
||||
vhdl_decl *get_decl(const std::string &name) const;
|
||||
bool have_declared(const std::string &name) const;
|
||||
bool contained_within(const vhdl_scope *other) const;
|
||||
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);
|
||||
|
||||
void set_allow_signal_assignment(bool b) { sig_assign_ = b; }
|
||||
bool allow_signal_assignment() const { return sig_assign_; }
|
||||
private:
|
||||
decl_list_t decls_;
|
||||
vhdl_scope *parent_;
|
||||
bool init_, sig_assign_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Any sort of procedural element: process, function, or
|
||||
* procedure. Roughly these map onto Verilog's processes,
|
||||
* functions, and tasks.
|
||||
*/
|
||||
class vhdl_procedural {
|
||||
public:
|
||||
virtual ~vhdl_procedural() {}
|
||||
|
||||
virtual stmt_container *get_container() { return &stmts_; }
|
||||
virtual vhdl_scope *get_scope() { return &scope_; }
|
||||
protected:
|
||||
stmt_container stmts_;
|
||||
vhdl_scope scope_;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_function : public vhdl_decl, public vhdl_procedural {
|
||||
friend class vhdl_forward_fdecl;
|
||||
public:
|
||||
vhdl_function(const char *name, vhdl_type *ret_type);
|
||||
|
||||
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:
|
||||
vhdl_scope variables_;
|
||||
};
|
||||
|
||||
class vhdl_forward_fdecl : public vhdl_decl {
|
||||
public:
|
||||
vhdl_forward_fdecl(const vhdl_function *f)
|
||||
: vhdl_decl((f->get_name() + "_Forward").c_str()), f_(f) {}
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
const vhdl_function *f_;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_process : public vhdl_conc_stmt, public vhdl_procedural {
|
||||
public:
|
||||
vhdl_process(const char *name = "") : name_(name) {}
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
void add_sensitivity(const std::string &name);
|
||||
private:
|
||||
std::string name_;
|
||||
string_list_t sens_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* An architecture which implements an entity.
|
||||
*/
|
||||
class vhdl_arch : public vhdl_element {
|
||||
public:
|
||||
vhdl_arch(const char *entity, const char *name)
|
||||
: name_(name), entity_(entity) {}
|
||||
virtual ~vhdl_arch();
|
||||
|
||||
void emit(std::ostream &of, int level=0) const;
|
||||
void add_stmt(vhdl_process *proc);
|
||||
void add_stmt(vhdl_conc_stmt *stmt);
|
||||
vhdl_scope *get_scope() { return &scope_; }
|
||||
private:
|
||||
conc_stmt_list_t stmts_;
|
||||
vhdl_scope scope_;
|
||||
std::string name_, entity_;
|
||||
};
|
||||
|
||||
/*
|
||||
* An entity defines the ports, parameters, etc. of a module. Each
|
||||
* entity is associated with a single architecture (although
|
||||
* technically this need not be the case). Entities are `derived'
|
||||
* from instantiations of Verilog module scopes in the hierarchy.
|
||||
*/
|
||||
class vhdl_entity : public vhdl_element {
|
||||
public:
|
||||
vhdl_entity(const char *name, const char *derived_from,
|
||||
vhdl_arch *arch);
|
||||
virtual ~vhdl_entity();
|
||||
|
||||
void emit(std::ostream &of, int level=0) const;
|
||||
void add_port(vhdl_port_decl *decl);
|
||||
vhdl_arch *get_arch() const { return arch_; }
|
||||
const std::string &get_name() const { return name_; }
|
||||
const std::string &get_derived_from() const { return derived_from_; }
|
||||
|
||||
vhdl_scope *get_scope() { return &ports_; }
|
||||
private:
|
||||
std::string name_;
|
||||
vhdl_arch *arch_; // Entity may only have a single architecture
|
||||
std::string derived_from_;
|
||||
vhdl_scope ports_;
|
||||
};
|
||||
|
||||
typedef std::list<vhdl_entity*> entity_list_t;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef INC_VHDL_TARGET_H
|
||||
#define INC_VHDL_TARGET_H
|
||||
|
||||
#include "vhdl_config.h"
|
||||
#include "ivl_target.h"
|
||||
|
||||
#include "vhdl_syntax.hh"
|
||||
#include "vhdl_type.hh"
|
||||
|
||||
#include "support.hh"
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
void error(const char *fmt, ...);
|
||||
|
||||
int draw_scope(ivl_scope_t scope, void *_parent);
|
||||
int draw_process(ivl_process_t net, void *cd);
|
||||
int draw_stmt(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt, bool is_last = false);
|
||||
int draw_lpm(vhdl_arch *arch, ivl_lpm_t lpm);
|
||||
void draw_logic(vhdl_arch *arch, ivl_net_logic_t log);
|
||||
|
||||
vhdl_expr *translate_expr(ivl_expr_t e);
|
||||
vhdl_expr *translate_time_expr(ivl_expr_t e);
|
||||
|
||||
void remember_entity(vhdl_entity *ent);
|
||||
vhdl_entity *find_entity(const string &sname);
|
||||
|
||||
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, vhdl_scope *scope);
|
||||
void rename_signal(ivl_signal_t sig, const string &renamed);
|
||||
vhdl_scope *find_scope_for_signal(ivl_signal_t sig);
|
||||
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 */
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* VHDL variable and signal 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_type.hh"
|
||||
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
vhdl_type *vhdl_type::std_logic()
|
||||
{
|
||||
return new vhdl_type(VHDL_TYPE_STD_LOGIC);
|
||||
}
|
||||
|
||||
vhdl_type *vhdl_type::string()
|
||||
{
|
||||
return new vhdl_type(VHDL_TYPE_STRING);
|
||||
}
|
||||
|
||||
vhdl_type *vhdl_type::line()
|
||||
{
|
||||
return new vhdl_type(VHDL_TYPE_LINE);
|
||||
}
|
||||
|
||||
vhdl_type *vhdl_type::boolean()
|
||||
{
|
||||
return new vhdl_type(VHDL_TYPE_BOOLEAN);
|
||||
}
|
||||
|
||||
vhdl_type *vhdl_type::integer()
|
||||
{
|
||||
return new vhdl_type(VHDL_TYPE_INTEGER);
|
||||
}
|
||||
|
||||
vhdl_type *vhdl_type::nunsigned(int width, int lsb)
|
||||
{
|
||||
return new vhdl_type(VHDL_TYPE_UNSIGNED, width-1+lsb, lsb);
|
||||
}
|
||||
|
||||
vhdl_type *vhdl_type::nsigned(int width, int lsb)
|
||||
{
|
||||
return new vhdl_type(VHDL_TYPE_SIGNED, width-1+lsb, lsb);
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
std::string vhdl_type::get_string() const
|
||||
{
|
||||
switch (name_) {
|
||||
case VHDL_TYPE_STD_LOGIC:
|
||||
return std::string("std_logic");
|
||||
case VHDL_TYPE_STD_LOGIC_VECTOR:
|
||||
return std::string("std_logic_vector");
|
||||
case VHDL_TYPE_STRING:
|
||||
return std::string("String");
|
||||
case VHDL_TYPE_LINE:
|
||||
return std::string("Line");
|
||||
case VHDL_TYPE_FILE:
|
||||
return std::string("File");
|
||||
case VHDL_TYPE_INTEGER:
|
||||
return std::string("Integer");
|
||||
case VHDL_TYPE_BOOLEAN:
|
||||
return std::string("Boolean");
|
||||
case VHDL_TYPE_SIGNED:
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The is the qualified name of the type.
|
||||
*/
|
||||
std::string vhdl_type::get_decl_string() const
|
||||
{
|
||||
switch (name_) {
|
||||
case VHDL_TYPE_STD_LOGIC_VECTOR:
|
||||
case VHDL_TYPE_UNSIGNED:
|
||||
case VHDL_TYPE_SIGNED:
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << get_string() << "(" << msb_;
|
||||
ss << " downto " << lsb_ << ")";
|
||||
return ss.str();
|
||||
}
|
||||
default:
|
||||
return get_string();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
vhdl_type *vhdl_type::type_for(int width, bool issigned, int lsb)
|
||||
{
|
||||
if (width == 1)
|
||||
return vhdl_type::std_logic();
|
||||
else if (issigned)
|
||||
return vhdl_type::nsigned(width, 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);
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* VHDL variable and signal 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.
|
||||
*/
|
||||
|
||||
#ifndef INC_VHDL_TYPE_HH
|
||||
#define INC_VHDL_TYPE_HH
|
||||
|
||||
#include "vhdl_element.hh"
|
||||
|
||||
enum vhdl_type_name_t {
|
||||
VHDL_TYPE_STD_LOGIC,
|
||||
VHDL_TYPE_STD_LOGIC_VECTOR,
|
||||
VHDL_TYPE_STRING,
|
||||
VHDL_TYPE_LINE,
|
||||
VHDL_TYPE_FILE,
|
||||
VHDL_TYPE_INTEGER,
|
||||
VHDL_TYPE_BOOLEAN,
|
||||
VHDL_TYPE_SIGNED,
|
||||
VHDL_TYPE_UNSIGNED,
|
||||
VHDL_TYPE_TIME,
|
||||
VHDL_TYPE_ARRAY,
|
||||
};
|
||||
|
||||
/*
|
||||
* A type at the moment is just a name. It shouldn't get
|
||||
* too much more complex, as Verilog's type system is much
|
||||
* simpler than VHDL's.
|
||||
*/
|
||||
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), 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_; }
|
||||
|
||||
// Common types
|
||||
static vhdl_type *std_logic();
|
||||
static vhdl_type *string();
|
||||
static vhdl_type *line();
|
||||
static vhdl_type *std_logic_vector(int msb, int lsb);
|
||||
static vhdl_type *nunsigned(int width, int lsb=0);
|
||||
static vhdl_type *nsigned(int width, int lsb=0);
|
||||
static vhdl_type *integer();
|
||||
static vhdl_type *boolean();
|
||||
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
|
||||
@@ -0,0 +1,6 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
void finish(void)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
+3
-3
@@ -47,9 +47,9 @@ static void draw_lpm_mux_ab(ivl_lpm_t net, const char*muxz)
|
||||
|
||||
const char*dly = "";
|
||||
if (d_rise != 0) {
|
||||
assert(number_is_immediate(d_rise, 64));
|
||||
assert(number_is_immediate(d_fall, 64));
|
||||
assert(number_is_immediate(d_decay, 64));
|
||||
assert(number_is_immediate(d_rise, 64, 0));
|
||||
assert(number_is_immediate(d_fall, 64, 0));
|
||||
assert(number_is_immediate(d_decay, 64, 0));
|
||||
dly = "/d";
|
||||
fprintf(vvp_out, "L_%p .delay (%lu,%lu,%lu) L_%p/d;\n",
|
||||
net, get_number_immediate(d_rise),
|
||||
|
||||
@@ -315,7 +315,7 @@ static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr)
|
||||
because it may be an array of reg vectors. */
|
||||
snprintf(tmp, sizeof tmp, "v%p_%u", sptr, nptr_pin);
|
||||
|
||||
if (ivl_signal_array_count(sptr) > 1) {
|
||||
if (ivl_signal_dimensions(sptr) > 0) {
|
||||
fprintf(vvp_out, "v%p_%u .array/port v%p, %u;\n",
|
||||
sptr, nptr_pin, sptr, nptr_pin);
|
||||
}
|
||||
@@ -359,9 +359,9 @@ static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr)
|
||||
|
||||
/* We have a delayed constant, so we need to build some code. */
|
||||
if (d_rise != 0) {
|
||||
assert(number_is_immediate(d_rise, 64));
|
||||
assert(number_is_immediate(d_fall, 64));
|
||||
assert(number_is_immediate(d_decay, 64));
|
||||
assert(number_is_immediate(d_rise, 64, 0));
|
||||
assert(number_is_immediate(d_fall, 64, 0));
|
||||
assert(number_is_immediate(d_decay, 64, 0));
|
||||
|
||||
fprintf(vvp_out, "L_%p/d .functor BUFZ 1, %s, "
|
||||
"C4<0>, C4<0>, C4<0>;\n", cptr, result);
|
||||
@@ -387,6 +387,8 @@ static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr)
|
||||
case IVL_LPM_ABS:
|
||||
case IVL_LPM_ADD:
|
||||
case IVL_LPM_ARRAY:
|
||||
case IVL_LPM_CAST_INT:
|
||||
case IVL_LPM_CAST_REAL:
|
||||
case IVL_LPM_CONCAT:
|
||||
case IVL_LPM_CMP_EEQ:
|
||||
case IVL_LPM_CMP_EQ:
|
||||
|
||||
@@ -30,7 +30,7 @@ static void function_argument_logic(ivl_signal_t port, ivl_expr_t exp)
|
||||
struct vector_info res;
|
||||
|
||||
/* ports cannot be arrays. */
|
||||
assert(ivl_signal_array_count(port) == 1);
|
||||
assert(ivl_signal_dimensions(port) == 0);
|
||||
|
||||
res = draw_eval_expr_wid(exp, ivl_signal_width(port), 0);
|
||||
/* We could have extra bits so only select the ones we need. */
|
||||
@@ -46,7 +46,7 @@ static void function_argument_real(ivl_signal_t port, ivl_expr_t exp)
|
||||
int res = draw_eval_real(exp);
|
||||
|
||||
/* ports cannot be arrays. */
|
||||
assert(ivl_signal_array_count(port) == 1);
|
||||
assert(ivl_signal_dimensions(port) == 0);
|
||||
|
||||
fprintf(vvp_out, " %%set/wr v%p_0, %d;\n", port, res);
|
||||
clr_word(res);
|
||||
@@ -126,7 +126,7 @@ struct vector_info draw_ufunc_expr(ivl_expr_t exp, unsigned wid)
|
||||
if (load_wid > ivl_signal_width(retval))
|
||||
load_wid = ivl_signal_width(retval);
|
||||
|
||||
assert(ivl_signal_array_count(retval) == 1);
|
||||
assert(ivl_signal_dimensions(retval) == 0);
|
||||
fprintf(vvp_out, " %%load/v %u, v%p_0, %u;\n",
|
||||
res.base, retval, load_wid);
|
||||
|
||||
@@ -157,7 +157,7 @@ int draw_ufunc_real(ivl_expr_t exp)
|
||||
fprintf(vvp_out, " %%join;\n");
|
||||
|
||||
/* Return value signal cannot be an array. */
|
||||
assert(ivl_signal_array_count(retval) == 1);
|
||||
assert(ivl_signal_dimensions(retval) == 0);
|
||||
|
||||
/* Load the result into a word. */
|
||||
res = allocate_word();
|
||||
|
||||
+20
-83
@@ -29,82 +29,6 @@
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Check to see if the expression (number) can be correctly represented
|
||||
* with a long variable.
|
||||
*/
|
||||
static int is_constant_number(ivl_expr_t ex)
|
||||
{
|
||||
/* Make sure this matches the return type of constant_number(). */
|
||||
unsigned lim_wid = 8*sizeof(long);
|
||||
const char*bits;
|
||||
char pad_bit = '0';
|
||||
unsigned idx;
|
||||
unsigned nbits = ivl_expr_width(ex);
|
||||
|
||||
if (ivl_expr_type(ex) != IVL_EX_NUMBER
|
||||
&& ivl_expr_type(ex) != IVL_EX_ULONG)
|
||||
return 0;
|
||||
|
||||
bits = ivl_expr_bits(ex);
|
||||
|
||||
/* For unsigned values the effective MSB and on must be '0'. */
|
||||
if (!ivl_expr_signed(ex)) lim_wid -= 1;
|
||||
|
||||
/* For negative values the pad bit is '1'. */
|
||||
if (ivl_expr_signed(ex) && bits[nbits-1]=='1') {
|
||||
pad_bit = '1';
|
||||
}
|
||||
|
||||
/* For the number to fit in the variable all the upper bits must
|
||||
* match the pad bits. */
|
||||
for (idx = lim_wid ; idx < nbits ; idx += 1) {
|
||||
if (bits[idx] != pad_bit) return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the expression (number) to a long value.
|
||||
*/
|
||||
static long get_constant_number(ivl_expr_t ex)
|
||||
{
|
||||
long rtn = 0;
|
||||
|
||||
switch (ivl_expr_type(ex)) {
|
||||
case IVL_EX_ULONG:
|
||||
rtn = (signed)ivl_expr_value(ex);
|
||||
break;
|
||||
case IVL_EX_NUMBER: {
|
||||
unsigned idx;
|
||||
const char*bits = ivl_expr_bits(ex);
|
||||
unsigned nbits = ivl_expr_width(ex);
|
||||
char pad_bit = bits[nbits-1];
|
||||
/* Define all the bits in the long (negative numbers). */
|
||||
for (idx = 0 ; idx < 8*sizeof(long) ; idx += 1) {
|
||||
char bit;
|
||||
if (idx < nbits) bit = bits[idx];
|
||||
else bit = pad_bit;
|
||||
switch (bit) {
|
||||
case '0':
|
||||
break;
|
||||
case '1':
|
||||
rtn |= 1 << idx;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
return rtn;
|
||||
}
|
||||
|
||||
static const char* magic_sfuncs[] = {
|
||||
"$time",
|
||||
"$stime",
|
||||
@@ -138,7 +62,7 @@ static int is_fixed_memory_word(ivl_expr_t net)
|
||||
if (ivl_signal_type(sig) == IVL_SIT_REG)
|
||||
return 0;
|
||||
|
||||
if (number_is_immediate(ivl_expr_oper1(net), 8*sizeof(unsigned)))
|
||||
if (number_is_immediate(ivl_expr_oper1(net), 8*sizeof(unsigned), 0))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
@@ -258,7 +182,7 @@ static void draw_vpi_taskfunc_args(const char*call_string,
|
||||
ivl_expr_t word_ex = ivl_expr_oper1(expr);
|
||||
if (word_ex) {
|
||||
/* Some array select have been evaluated. */
|
||||
if (number_is_immediate(word_ex, 8*sizeof(unsigned))) {
|
||||
if (number_is_immediate(word_ex, 8*sizeof(unsigned), 0)) {
|
||||
use_word = get_number_immediate(word_ex);
|
||||
word_ex = 0;
|
||||
}
|
||||
@@ -280,16 +204,22 @@ static void draw_vpi_taskfunc_args(const char*call_string,
|
||||
ivl_expr_t word_ex = ivl_expr_oper1(expr);
|
||||
if (word_ex) {
|
||||
/* Some array select have been evaluated. */
|
||||
if (number_is_immediate(word_ex, 8*sizeof(unsigned))) {
|
||||
if (number_is_immediate(word_ex, 8*sizeof(unsigned), 0)) {
|
||||
use_word = get_number_immediate(word_ex);
|
||||
word_ex = 0;
|
||||
}
|
||||
}
|
||||
if (word_ex) {
|
||||
if (word_ex && ivl_expr_type(word_ex)==IVL_EX_SIGNAL) {
|
||||
/* Special case: the index is a signal. */
|
||||
snprintf(buffer, sizeof buffer,
|
||||
"&A<v%p, v%p_0 >", sig,
|
||||
ivl_expr_signal(word_ex));
|
||||
} else if (word_ex) {
|
||||
/* Fallback case: evaluate expression. */
|
||||
struct vector_info av;
|
||||
av = draw_eval_expr(word_ex, STUFF_OK_XZ);
|
||||
snprintf(buffer, sizeof buffer,
|
||||
"&A<v%p, T<%u,%u,u>>", sig, av.base, av.wid);
|
||||
"&A<v%p, %u %u>", sig, av.base, av.wid);
|
||||
args[idx].vec = av;
|
||||
args[idx].vec_flag = 1;
|
||||
} else {
|
||||
@@ -315,13 +245,20 @@ static void draw_vpi_taskfunc_args(const char*call_string,
|
||||
assert(bexpr);
|
||||
|
||||
/* This is a constant bit/part select. */
|
||||
if (is_constant_number(bexpr)) {
|
||||
if (number_is_immediate(bexpr, 64, 1)) {
|
||||
snprintf(buffer, sizeof buffer, "&PV<v%p_0, %ld, %u>",
|
||||
ivl_expr_signal(vexpr),
|
||||
get_constant_number(bexpr),
|
||||
get_number_immediate(bexpr),
|
||||
ivl_expr_width(expr));
|
||||
/* This is an indexed bit/part select. */
|
||||
} else if (ivl_expr_type(bexpr) == IVL_EX_SIGNAL) {
|
||||
/* Sepcial case: the base is a signal. */
|
||||
snprintf(buffer, sizeof buffer, "&PV<v%p_0, v%p_0, %u>",
|
||||
ivl_expr_signal(vexpr),
|
||||
ivl_expr_signal(bexpr),
|
||||
ivl_expr_width(expr));
|
||||
} else {
|
||||
/* Fallback case: evaluate the expression. */
|
||||
struct vector_info rv;
|
||||
rv = draw_eval_expr(bexpr, STUFF_OK_XZ);
|
||||
snprintf(buffer, sizeof buffer, "&PV<v%p_0, %u %u, %u>",
|
||||
|
||||
+84
-83
@@ -28,7 +28,7 @@
|
||||
static void draw_eval_expr_dest(ivl_expr_t exp, struct vector_info dest,
|
||||
int ok_flags);
|
||||
static void draw_signal_dest(ivl_expr_t exp, struct vector_info res,
|
||||
int add_index, unsigned long immediate);
|
||||
int add_index, long immediate);
|
||||
|
||||
int number_is_unknown(ivl_expr_t ex)
|
||||
{
|
||||
@@ -54,9 +54,11 @@ int number_is_unknown(ivl_expr_t ex)
|
||||
* above bitX. The maximum size of the immediate may vary, so use
|
||||
* lim_wid at the width limit to use.
|
||||
*/
|
||||
int number_is_immediate(ivl_expr_t ex, unsigned lim_wid)
|
||||
int number_is_immediate(ivl_expr_t ex, unsigned lim_wid, int negative_ok_flag)
|
||||
{
|
||||
const char*bits;
|
||||
unsigned nbits = ivl_expr_width(ex);
|
||||
char pad_bit = '0';
|
||||
unsigned idx;
|
||||
|
||||
if (ivl_expr_type(ex) != IVL_EX_NUMBER
|
||||
@@ -64,20 +66,23 @@ int number_is_immediate(ivl_expr_t ex, unsigned lim_wid)
|
||||
return 0;
|
||||
|
||||
bits = ivl_expr_bits(ex);
|
||||
for (idx = lim_wid ; idx < ivl_expr_width(ex) ; idx += 1)
|
||||
if (bits[idx] != '0')
|
||||
return 0;
|
||||
|
||||
/* Negative numbers are not "immediate". */
|
||||
if (ivl_expr_signed(ex) && bits[ivl_expr_width(ex)-1]=='1')
|
||||
if (ivl_expr_signed(ex) && bits[nbits-1]=='1')
|
||||
pad_bit = '1';
|
||||
|
||||
if (pad_bit == '1' && !negative_ok_flag)
|
||||
return 0;
|
||||
|
||||
for (idx = lim_wid ; idx < nbits ; idx += 1)
|
||||
if (bits[idx] != pad_bit)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned long get_number_immediate(ivl_expr_t ex)
|
||||
long get_number_immediate(ivl_expr_t ex)
|
||||
{
|
||||
unsigned long imm = 0;
|
||||
long imm = 0;
|
||||
unsigned idx;
|
||||
|
||||
switch (ivl_expr_type(ex)) {
|
||||
@@ -93,11 +98,13 @@ unsigned long get_number_immediate(ivl_expr_t ex)
|
||||
break;
|
||||
case '1':
|
||||
assert(idx < 8*sizeof(imm));
|
||||
imm |= 1UL << idx;
|
||||
imm |= 1L << idx;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
if (ivl_expr_signed(ex) && bits[nbits-1]=='1' && nbits < 8*sizeof(imm))
|
||||
imm |= -1L << nbits;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -112,30 +119,18 @@ static void eval_logic_into_integer(ivl_expr_t expr, unsigned ix)
|
||||
{
|
||||
switch (ivl_expr_type(expr)) {
|
||||
|
||||
case IVL_EX_NUMBER: {
|
||||
unsigned value = 0;
|
||||
unsigned idx, nbits = ivl_expr_width(expr);
|
||||
const char*bits = ivl_expr_bits(expr);
|
||||
|
||||
for (idx = 0 ; idx < nbits ; idx += 1) switch (bits[idx]) {
|
||||
|
||||
case '0':
|
||||
break;
|
||||
case '1':
|
||||
assert(idx < (8*sizeof value));
|
||||
value |= 1 << idx;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
fprintf(vvp_out, " %%ix/load %u, %u;\n", ix, value);
|
||||
break;
|
||||
}
|
||||
|
||||
case IVL_EX_NUMBER:
|
||||
case IVL_EX_ULONG:
|
||||
fprintf(vvp_out, " %%ix/load %u, %lu;\n", ix, ivl_expr_uvalue(expr));
|
||||
break;
|
||||
{
|
||||
long imm = get_number_immediate(expr);
|
||||
if (imm >= 0) {
|
||||
fprintf(vvp_out, " %%ix/load %u, %ld;\n", ix, imm);
|
||||
} else {
|
||||
fprintf(vvp_out, " %%ix/load %u, 0; loading %ld\n", ix, imm);
|
||||
fprintf(vvp_out, " %%ix/sub %u, %ld;\n", ix, -imm);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case IVL_EX_SIGNAL: {
|
||||
ivl_signal_t sig = ivl_expr_signal(expr);
|
||||
@@ -156,7 +151,7 @@ static void eval_logic_into_integer(ivl_expr_t expr, unsigned ix)
|
||||
}
|
||||
|
||||
ivl_expr_t ixe = ivl_expr_oper1(expr);
|
||||
if (number_is_immediate(ixe, 8*sizeof(unsigned long)))
|
||||
if (number_is_immediate(ixe, 8*sizeof(unsigned long), 0))
|
||||
word = get_number_immediate(ixe);
|
||||
else {
|
||||
struct vector_info rv;
|
||||
@@ -384,7 +379,7 @@ static struct vector_info draw_binary_expr_eq(ivl_expr_t exp,
|
||||
return draw_binary_expr_eq_real(exp);
|
||||
}
|
||||
|
||||
if (number_is_immediate(re,16) && !number_is_unknown(re))
|
||||
if (number_is_immediate(re,16,0) && !number_is_unknown(re))
|
||||
return draw_eq_immediate(exp, ewid, le, re, stuff_ok_flag);
|
||||
|
||||
assert(ivl_expr_value(le) == IVL_VT_LOGIC
|
||||
@@ -815,7 +810,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t exp,
|
||||
switch (ivl_expr_opcode(exp)) {
|
||||
case 'G':
|
||||
rv = draw_eval_expr_wid(re, owid, STUFF_OK_XZ);
|
||||
if (number_is_immediate(le,16) && !number_is_unknown(le)) {
|
||||
if (number_is_immediate(le,16,0) && !number_is_unknown(le)) {
|
||||
unsigned imm = get_number_immediate(le);
|
||||
assert(imm >= 0);
|
||||
fprintf(vvp_out, " %%cmpi/%c %u, %u, %u;\n", s_flag,
|
||||
@@ -831,7 +826,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t exp,
|
||||
|
||||
case 'L':
|
||||
lv = draw_eval_expr_wid(le, owid, STUFF_OK_XZ);
|
||||
if (number_is_immediate(re,16) && !number_is_unknown(re)) {
|
||||
if (number_is_immediate(re,16,0) && !number_is_unknown(re)) {
|
||||
unsigned imm = get_number_immediate(re);
|
||||
assert(imm >= 0);
|
||||
fprintf(vvp_out, " %%cmpi/%c %u, %u, %u;\n", s_flag,
|
||||
@@ -847,7 +842,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t exp,
|
||||
|
||||
case '<':
|
||||
lv = draw_eval_expr_wid(le, owid, STUFF_OK_XZ);
|
||||
if (number_is_immediate(re,16) && !number_is_unknown(re)) {
|
||||
if (number_is_immediate(re,16,0) && !number_is_unknown(re)) {
|
||||
unsigned imm = get_number_immediate(re);
|
||||
assert(imm >= 0);
|
||||
fprintf(vvp_out, " %%cmpi/%c %u, %u, %u;\n", s_flag,
|
||||
@@ -862,7 +857,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t exp,
|
||||
|
||||
case '>':
|
||||
rv = draw_eval_expr_wid(re, owid, STUFF_OK_XZ);
|
||||
if (number_is_immediate(le,16) && !number_is_unknown(le)) {
|
||||
if (number_is_immediate(le,16,0) && !number_is_unknown(le)) {
|
||||
unsigned imm = get_number_immediate(le);
|
||||
assert(imm >= 0);
|
||||
fprintf(vvp_out, " %%cmpi/%c %u, %u, %u;\n", s_flag,
|
||||
@@ -941,9 +936,9 @@ static struct vector_info draw_binary_expr_logic(ivl_expr_t exp,
|
||||
ivl_expr_t re = ivl_expr_oper2(exp);
|
||||
|
||||
if (ivl_expr_opcode(exp) == '&') {
|
||||
if (number_is_immediate(re, IMM_WID) && !number_is_unknown(re))
|
||||
if (number_is_immediate(re, IMM_WID, 0) && !number_is_unknown(re))
|
||||
return draw_logic_immediate(exp, le, re, wid);
|
||||
if (number_is_immediate(le, IMM_WID) && !number_is_unknown(le))
|
||||
if (number_is_immediate(le, IMM_WID, 0) && !number_is_unknown(le))
|
||||
return draw_logic_immediate(exp, re, le, wid);
|
||||
}
|
||||
|
||||
@@ -1163,12 +1158,11 @@ static struct vector_info draw_binary_expr_lrs(ivl_expr_t exp, unsigned wid)
|
||||
|
||||
static struct vector_info draw_load_add_immediate(ivl_expr_t le,
|
||||
ivl_expr_t re,
|
||||
unsigned wid)
|
||||
unsigned wid,
|
||||
int signed_flag)
|
||||
{
|
||||
struct vector_info lv;
|
||||
unsigned long imm;
|
||||
|
||||
imm = get_number_immediate(re);
|
||||
long imm = get_number_immediate(re);
|
||||
lv.base = allocate_vector(wid);
|
||||
lv.wid = wid;
|
||||
if (lv.base == 0) {
|
||||
@@ -1181,7 +1175,7 @@ static struct vector_info draw_load_add_immediate(ivl_expr_t le,
|
||||
|
||||
/* Load the signal value with a %load that adds the index
|
||||
register to the value being loaded. */
|
||||
draw_signal_dest(le, lv, 0, imm);
|
||||
draw_signal_dest(le, lv, signed_flag, imm);
|
||||
|
||||
return lv;
|
||||
}
|
||||
@@ -1324,25 +1318,27 @@ static struct vector_info draw_binary_expr_arith(ivl_expr_t exp, unsigned wid)
|
||||
|
||||
const char*sign_string = ivl_expr_signed(le) && ivl_expr_signed(re)? "/s" : "";
|
||||
|
||||
int signed_flag = ivl_expr_signed(exp)? 1 : 0;
|
||||
|
||||
if ((ivl_expr_opcode(exp) == '+')
|
||||
&& (ivl_expr_type(le) == IVL_EX_SIGNAL)
|
||||
&& (ivl_expr_type(re) == IVL_EX_ULONG))
|
||||
return draw_load_add_immediate(le, re, wid);
|
||||
return draw_load_add_immediate(le, re, wid, signed_flag);
|
||||
|
||||
if ((ivl_expr_opcode(exp) == '+')
|
||||
&& (ivl_expr_type(le) == IVL_EX_SIGNAL)
|
||||
&& (ivl_expr_type(re) == IVL_EX_NUMBER))
|
||||
return draw_load_add_immediate(le, re, wid);
|
||||
return draw_load_add_immediate(le, re, wid, signed_flag);
|
||||
|
||||
if ((ivl_expr_opcode(exp) == '+')
|
||||
&& (ivl_expr_type(re) == IVL_EX_SIGNAL)
|
||||
&& (ivl_expr_type(le) == IVL_EX_ULONG))
|
||||
return draw_load_add_immediate(re, le, wid);
|
||||
return draw_load_add_immediate(re, le, wid, signed_flag);
|
||||
|
||||
if ((ivl_expr_opcode(exp) == '+')
|
||||
&& (ivl_expr_type(re) == IVL_EX_SIGNAL)
|
||||
&& (ivl_expr_type(le) == IVL_EX_NUMBER))
|
||||
return draw_load_add_immediate(re, le, wid);
|
||||
return draw_load_add_immediate(re, le, wid, signed_flag);
|
||||
|
||||
if ((ivl_expr_opcode(exp) == '+')
|
||||
&& (ivl_expr_type(re) == IVL_EX_ULONG))
|
||||
@@ -1351,7 +1347,7 @@ static struct vector_info draw_binary_expr_arith(ivl_expr_t exp, unsigned wid)
|
||||
if ((ivl_expr_opcode(exp) == '+')
|
||||
&& (ivl_expr_type(re) == IVL_EX_NUMBER)
|
||||
&& (! number_is_unknown(re))
|
||||
&& number_is_immediate(re, 8*sizeof(unsigned long)))
|
||||
&& number_is_immediate(re, 8*sizeof(unsigned long), 0))
|
||||
return draw_add_immediate(le, re, wid);
|
||||
|
||||
if ((ivl_expr_opcode(exp) == '-')
|
||||
@@ -1361,13 +1357,13 @@ static struct vector_info draw_binary_expr_arith(ivl_expr_t exp, unsigned wid)
|
||||
if ((ivl_expr_opcode(exp) == '-')
|
||||
&& (ivl_expr_type(re) == IVL_EX_NUMBER)
|
||||
&& (! number_is_unknown(re))
|
||||
&& number_is_immediate(re, IMM_WID))
|
||||
&& number_is_immediate(re, IMM_WID, 0))
|
||||
return draw_sub_immediate(le, re, wid);
|
||||
|
||||
if ((ivl_expr_opcode(exp) == '*')
|
||||
&& (ivl_expr_type(re) == IVL_EX_NUMBER)
|
||||
&& (! number_is_unknown(re))
|
||||
&& number_is_immediate(re, IMM_WID))
|
||||
&& number_is_immediate(re, IMM_WID, 0))
|
||||
return draw_mul_immediate(le, re, wid);
|
||||
|
||||
lv = draw_eval_expr_wid(le, wid, STUFF_OK_XZ);
|
||||
@@ -1674,7 +1670,7 @@ static struct vector_info draw_number_expr(ivl_expr_t exp, unsigned wid)
|
||||
vvp_errors += 1;
|
||||
}
|
||||
|
||||
if ((!number_is_unknown(exp)) && number_is_immediate(exp, IMM_WID)) {
|
||||
if ((!number_is_unknown(exp)) && number_is_immediate(exp, IMM_WID,0)) {
|
||||
unsigned long val = get_number_immediate(exp);
|
||||
fprintf(vvp_out, " %%movi %u, %lu, %u;\n", res.base, val, wid);
|
||||
return res;
|
||||
@@ -1968,11 +1964,13 @@ void pad_expr_in_place(ivl_expr_t exp, struct vector_info res, unsigned swid)
|
||||
* offsetting the read from the lsi (least significant index) of the
|
||||
* signal.
|
||||
*
|
||||
* If the add_index is >=0, then generate a %load/vp0 to add the
|
||||
* word0 value to the loaded value before storing it into the destination.
|
||||
* If the add_index is 0, then generate a %load/vp0 to add the
|
||||
* word0 value to the loaded value before storing it into the
|
||||
* destination. If the add_index is 1, then generate a %load/vp0/s to
|
||||
* do a signed load.
|
||||
*/
|
||||
static void draw_signal_dest(ivl_expr_t exp, struct vector_info res,
|
||||
int add_index, unsigned long immediate)
|
||||
int add_index, long immediate)
|
||||
{
|
||||
unsigned swid = ivl_expr_width(exp);
|
||||
ivl_signal_t sig = ivl_expr_signal(exp);
|
||||
@@ -1989,17 +1987,17 @@ static void draw_signal_dest(ivl_expr_t exp, struct vector_info res,
|
||||
|
||||
draw_eval_expr_into_integer(ix, 3);
|
||||
if (add_index < 0) {
|
||||
fprintf(vvp_out, " %%load/av %u, v%p, %u;\n",
|
||||
fprintf(vvp_out, " %%load/av %u, v%p, %u;\n",
|
||||
res.base, sig, swid);
|
||||
pad_expr_in_place(exp, res, swid);
|
||||
} else {
|
||||
assert(add_index == 0);
|
||||
const char*sign_flag = (add_index>0)? "/s" : "";
|
||||
|
||||
/* Add an immediate value to an array value. */
|
||||
fprintf(vvp_out, " %%ix/load 0, %lu;\n", immediate);
|
||||
fprintf(vvp_out, " %%load/avp0 %u, v%p, %u;\n",
|
||||
res.base, sig, swid);
|
||||
fprintf(vvp_out, " %%ix/load 0, %lu;\n", immediate);
|
||||
fprintf(vvp_out, " %%load/avp0%s %u, v%p, %u;\n",
|
||||
sign_flag, res.base, sig, res.wid);
|
||||
}
|
||||
pad_expr_in_place(exp, res, swid);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2014,13 +2012,17 @@ static void draw_signal_dest(ivl_expr_t exp, struct vector_info res,
|
||||
|
||||
} else if (add_index >= 0) {
|
||||
|
||||
assert(add_index == 0);
|
||||
const char*sign_flag = add_index==1? "/s" : "";
|
||||
|
||||
/* If this is a REG (a variable) then I can do a vector read. */
|
||||
fprintf(vvp_out, " %%ix/load 0, %lu;\n", immediate);
|
||||
fprintf(vvp_out, " %%ix/load 2, %u;\n", res.wid);
|
||||
fprintf(vvp_out, " %%load/vp0 %u, v%p_%u, %u;\n",
|
||||
res.base, sig, word, swid);
|
||||
if (immediate >= 0) {
|
||||
fprintf(vvp_out, " %%ix/load 0, %lu;\n", immediate);
|
||||
} else {
|
||||
fprintf(vvp_out, " %%ix/load 0, 0; immediate=%ld\n", immediate);
|
||||
fprintf(vvp_out, " %%ix/sub 0, %ld;\n", -immediate);
|
||||
}
|
||||
fprintf(vvp_out, " %%load/vp0%s %u, v%p_%u, %u;\n", sign_flag,
|
||||
res.base, sig,word, res.wid);
|
||||
swid = res.wid;
|
||||
|
||||
} else {
|
||||
@@ -2109,7 +2111,6 @@ static struct vector_info draw_select_signal(ivl_expr_t sube,
|
||||
{
|
||||
ivl_signal_t sig = ivl_expr_signal(sube);
|
||||
struct vector_info res;
|
||||
unsigned idx;
|
||||
|
||||
/* Use this word of the signal. */
|
||||
unsigned use_word = 0;
|
||||
@@ -2124,7 +2125,7 @@ static struct vector_info draw_select_signal(ivl_expr_t sube,
|
||||
ivl_expr_t ix = ivl_expr_oper1(sube);
|
||||
|
||||
if (ivl_signal_type(sig)==IVL_SIT_REG
|
||||
|| !number_is_immediate(ix, 8*sizeof(unsigned long)))
|
||||
|| !number_is_immediate(ix, 8*sizeof(unsigned long),0))
|
||||
return draw_select_array(sube, bit_idx, bit_wid, wid);
|
||||
|
||||
/* The index is constant, so we can return to direct
|
||||
@@ -2135,7 +2136,7 @@ static struct vector_info draw_select_signal(ivl_expr_t sube,
|
||||
/* Try the special case that the part is at the beginning of
|
||||
the signal (or the entire width). Just load the early bits
|
||||
in one go. */
|
||||
if (number_is_immediate(bit_idx, 32)
|
||||
if (number_is_immediate(bit_idx, 32, 0)
|
||||
&& get_number_immediate(bit_idx) == 0
|
||||
&& (ivl_expr_width(sube) >= bit_wid)) {
|
||||
|
||||
@@ -2153,23 +2154,23 @@ static struct vector_info draw_select_signal(ivl_expr_t sube,
|
||||
return res;
|
||||
}
|
||||
|
||||
draw_eval_expr_into_integer(bit_idx, 0);
|
||||
|
||||
/* Alas, do it the hard way. */
|
||||
|
||||
draw_eval_expr_into_integer(bit_idx, 1);
|
||||
|
||||
res.base = allocate_vector(wid);
|
||||
res.wid = wid;
|
||||
assert(res.base);
|
||||
|
||||
for (idx = 0 ; idx < res.wid ; idx += 1) {
|
||||
if (idx >= bit_wid) {
|
||||
fprintf(vvp_out, " %%movi %u, 0, %u; Pad from %u to %u\n",
|
||||
res.base+idx, res.wid-idx,
|
||||
ivl_expr_width(sube), wid);
|
||||
break;
|
||||
}
|
||||
fprintf(vvp_out, " %%load/x.p %u, v%p_%u, 0;\n",
|
||||
res.base+idx, sig, use_word);
|
||||
}
|
||||
unsigned use_wid = res.wid;
|
||||
if (use_wid > bit_wid)
|
||||
use_wid = bit_wid;
|
||||
|
||||
fprintf(vvp_out, " %%load/x1p %u, v%p_%u, %u;\n",
|
||||
res.base, sig, use_word, use_wid);
|
||||
if (use_wid < res.wid)
|
||||
fprintf(vvp_out, " %%movi %u, 0, %u;\n",
|
||||
res.base + use_wid, res.wid - use_wid);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
+19
-4
@@ -326,9 +326,9 @@ static int draw_signal_real_real(ivl_expr_t exp)
|
||||
int res = allocate_word();
|
||||
unsigned long word = 0;
|
||||
|
||||
if (ivl_signal_array_count(sig) > 1) {
|
||||
if (ivl_signal_dimensions(sig) > 0) {
|
||||
ivl_expr_t ix = ivl_expr_oper1(exp);
|
||||
if (!number_is_immediate(ix, 8*sizeof(word))) {
|
||||
if (!number_is_immediate(ix, 8*sizeof(word), 0)) {
|
||||
/* XXXX Need to generate a %load/ar instruction. */
|
||||
assert(0);
|
||||
return res;
|
||||
@@ -441,7 +441,22 @@ static int draw_unary_real(ivl_expr_t exp)
|
||||
return res;
|
||||
}
|
||||
|
||||
if (ivl_expr_opcode(exp) == '!') {
|
||||
struct vector_info vi;
|
||||
vi = draw_eval_expr(exp, STUFF_OK_XZ);
|
||||
int res = allocate_word();
|
||||
const char*sign_flag = ivl_expr_signed(exp)? "/s" : "";
|
||||
fprintf(vvp_out, " %%ix/get%s %d, %u, %u;\n",
|
||||
sign_flag, res, vi.base, vi.wid);
|
||||
|
||||
fprintf(vvp_out, " %%cvt/ri %d, %d;\n", res, res);
|
||||
|
||||
clr_vector(vi);
|
||||
return res;
|
||||
}
|
||||
|
||||
ivl_expr_t sube = ivl_expr_oper1(exp);
|
||||
|
||||
int sub = draw_eval_real(sube);
|
||||
|
||||
if (ivl_expr_opcode(exp) == '+')
|
||||
@@ -461,8 +476,8 @@ static int draw_unary_real(ivl_expr_t exp)
|
||||
return sub;
|
||||
}
|
||||
|
||||
fprintf(vvp_out, "; XXXX unary (%c)\n", ivl_expr_opcode(exp));
|
||||
fprintf(stderr, "XXXX evaluate unary (%c)\n", ivl_expr_opcode(exp));
|
||||
fprintf(vvp_out, "; XXXX unary (%c) on sube in %d\n", ivl_expr_opcode(exp), sub);
|
||||
fprintf(stderr, "XXXX evaluate unary (%c) on sube in %d\n", ivl_expr_opcode(exp), sub);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -94,7 +94,7 @@ extern void pad_expr_in_place(ivl_expr_t exp, struct vector_info res, unsigned s
|
||||
* draw_modpath arranges for a .modpath record to be written out.
|
||||
*
|
||||
* cleanup_modpath() cleans up any pending .modpath records that may
|
||||
* have been scheduled by draw_modpath() but not yet writte.
|
||||
* have been scheduled by draw_modpath() but not yet written.
|
||||
*
|
||||
* Note: draw_modpath drive_label must be malloc'ed by the
|
||||
* caller. This function will free the string sometime in the future.
|
||||
@@ -269,8 +269,8 @@ extern unsigned allocate_vector_exp(ivl_expr_t exp, unsigned wid,
|
||||
int exclusive_flag);
|
||||
|
||||
extern int number_is_unknown(ivl_expr_t ex);
|
||||
extern int number_is_immediate(ivl_expr_t ex, unsigned lim_wid);
|
||||
extern unsigned long get_number_immediate(ivl_expr_t ex);
|
||||
extern int number_is_immediate(ivl_expr_t ex, unsigned lim_wid, int negative_is_ok);
|
||||
extern long get_number_immediate(ivl_expr_t ex);
|
||||
|
||||
/*
|
||||
* draw_eval_real evaluates real value expressions. The return code
|
||||
|
||||
+21
-21
@@ -83,7 +83,7 @@ static void set_to_lvariable(ivl_lval_t lval,
|
||||
|
||||
if (part_off_ex == 0) {
|
||||
part_off = 0;
|
||||
} else if (number_is_immediate(part_off_ex, 64)) {
|
||||
} else if (number_is_immediate(part_off_ex, 64, 0)) {
|
||||
part_off = get_number_immediate(part_off_ex);
|
||||
part_off_ex = 0;
|
||||
}
|
||||
@@ -91,7 +91,7 @@ static void set_to_lvariable(ivl_lval_t lval,
|
||||
/* If the word index is a constant expression, then evaluate
|
||||
it to select the word, and pay no further heed to the
|
||||
expression itself. */
|
||||
if (word_ix && number_is_immediate(word_ix, 8*sizeof(use_word))) {
|
||||
if (word_ix && number_is_immediate(word_ix, 8*sizeof(use_word), 0)) {
|
||||
use_word = get_number_immediate(word_ix);
|
||||
word_ix = 0;
|
||||
}
|
||||
@@ -227,15 +227,15 @@ static void assign_to_array_word(ivl_signal_t lsig, ivl_expr_t word_ix,
|
||||
unsigned part_off = 0;
|
||||
if (part_off_ex == 0) {
|
||||
part_off = 0;
|
||||
} else if (number_is_immediate(part_off_ex, 64)) {
|
||||
} else if (number_is_immediate(part_off_ex, 64, 0)) {
|
||||
part_off = get_number_immediate(part_off_ex);
|
||||
part_off_ex = 0;
|
||||
}
|
||||
|
||||
if (dexp == 0) {
|
||||
/* Constant delay... */
|
||||
if (number_is_immediate(word_ix, 64)) {
|
||||
fprintf(vvp_out, " %%ix/load 3, %lu; address\n",
|
||||
if (number_is_immediate(word_ix, 64, 0)) {
|
||||
fprintf(vvp_out, " %%ix/load 3, %lu; address\n",
|
||||
get_number_immediate(word_ix));
|
||||
} else {
|
||||
/* Calculate array word index into index register 3 */
|
||||
@@ -259,7 +259,7 @@ static void assign_to_array_word(ivl_signal_t lsig, ivl_expr_t word_ix,
|
||||
/* Calculated delay... */
|
||||
int delay_index = allocate_word();
|
||||
draw_eval_expr_into_integer(dexp, delay_index);
|
||||
if (number_is_immediate(word_ix, 64)) {
|
||||
if (number_is_immediate(word_ix, 64, 0)) {
|
||||
fprintf(vvp_out, " %%ix/load 3, %lu; address\n",
|
||||
get_number_immediate(word_ix));
|
||||
} else {
|
||||
@@ -298,7 +298,7 @@ static void assign_to_lvector(ivl_lval_t lval, unsigned bit,
|
||||
ivl_expr_t word_ix = ivl_lval_idx(lval);
|
||||
const unsigned long use_word = 0;
|
||||
|
||||
if (ivl_signal_array_count(sig) > 1) {
|
||||
if (ivl_signal_dimensions(sig) > 0) {
|
||||
assert(word_ix);
|
||||
assign_to_array_word(sig, word_ix, bit, delay, dexp, part_off_ex, width);
|
||||
return;
|
||||
@@ -306,7 +306,7 @@ static void assign_to_lvector(ivl_lval_t lval, unsigned bit,
|
||||
|
||||
if (part_off_ex == 0) {
|
||||
part_off = 0;
|
||||
} else if (number_is_immediate(part_off_ex, 64)) {
|
||||
} else if (number_is_immediate(part_off_ex, 64, 0)) {
|
||||
part_off = get_number_immediate(part_off_ex);
|
||||
part_off_ex = 0;
|
||||
}
|
||||
@@ -480,7 +480,7 @@ static int show_stmt_assign_sig_real(ivl_statement_t net)
|
||||
var = ivl_lval_sig(lval);
|
||||
assert(var != 0);
|
||||
|
||||
assert(ivl_signal_array_count(var) == 1);
|
||||
assert(ivl_signal_dimensions(var) == 0);
|
||||
|
||||
fprintf(vvp_out, " %%set/wr v%p_0, %d;\n", var, res);
|
||||
|
||||
@@ -540,10 +540,10 @@ static int show_stmt_assign_nb_real(ivl_statement_t net)
|
||||
sig = ivl_lval_sig(lval);
|
||||
assert(sig);
|
||||
|
||||
if (ivl_signal_array_count(sig) > 1) {
|
||||
if (ivl_signal_dimensions(sig) > 0) {
|
||||
word_ix = ivl_lval_idx(lval);
|
||||
assert(word_ix);
|
||||
assert(number_is_immediate(word_ix, 8*sizeof(use_word)));
|
||||
assert(number_is_immediate(word_ix, 8*sizeof(use_word), 0));
|
||||
use_word = get_number_immediate(word_ix);
|
||||
}
|
||||
|
||||
@@ -700,7 +700,7 @@ static int show_stmt_case(ivl_statement_t net, ivl_scope_t sscope)
|
||||
if ((ivl_statement_type(net) == IVL_ST_CASE)
|
||||
&& (ivl_expr_type(cex) == IVL_EX_NUMBER)
|
||||
&& (! number_is_unknown(cex))
|
||||
&& number_is_immediate(cex, 16)) {
|
||||
&& number_is_immediate(cex, 16, 0)) {
|
||||
|
||||
unsigned long imm = get_number_immediate(cex);
|
||||
|
||||
@@ -926,12 +926,12 @@ static void force_vector_to_lval(ivl_statement_t net, struct vector_info rvec)
|
||||
if (part_off_ex == 0) {
|
||||
part_off = 0;
|
||||
} else {
|
||||
assert(number_is_immediate(part_off_ex, 64));
|
||||
assert(number_is_immediate(part_off_ex, 64, 0));
|
||||
part_off = get_number_immediate(part_off_ex);
|
||||
}
|
||||
|
||||
if (word_idx != 0) {
|
||||
assert(number_is_immediate(word_idx, 8*sizeof(unsigned long)));
|
||||
assert(number_is_immediate(word_idx, 8*sizeof(unsigned long), 0));
|
||||
use_word = get_number_immediate(word_idx);
|
||||
}
|
||||
|
||||
@@ -1004,16 +1004,16 @@ static void force_link_rval(ivl_statement_t net, ivl_expr_t rval)
|
||||
|
||||
/* At least for now, only handle force to fixed words of an array. */
|
||||
if ((lword_idx = ivl_lval_idx(lval)) != 0) {
|
||||
assert(number_is_immediate(lword_idx, 8*sizeof(unsigned long)));
|
||||
assert(number_is_immediate(lword_idx, 8*sizeof(unsigned long), 0));
|
||||
use_lword = get_number_immediate(lword_idx);
|
||||
}
|
||||
|
||||
if ((rword_idx = ivl_expr_oper1(rval)) != 0) {
|
||||
assert(number_is_immediate(rword_idx, 8*sizeof(unsigned long)));
|
||||
assert(number_is_immediate(rword_idx, 8*sizeof(unsigned long), 0));
|
||||
use_rword = get_number_immediate(rword_idx);
|
||||
}
|
||||
|
||||
assert(ivl_signal_array_count(rsig) == 1);
|
||||
assert(ivl_signal_dimensions(rsig) == 0);
|
||||
use_rword = 0;
|
||||
|
||||
fprintf(vvp_out, " %s/link", command_name);
|
||||
@@ -1087,12 +1087,12 @@ static int show_stmt_deassign(ivl_statement_t net)
|
||||
ivl_expr_t part_off_ex = ivl_lval_part_off(lval);
|
||||
unsigned part_off = 0;
|
||||
if (part_off_ex != 0) {
|
||||
assert(number_is_immediate(part_off_ex, 64));
|
||||
assert(number_is_immediate(part_off_ex, 64, 0));
|
||||
part_off = get_number_immediate(part_off_ex);
|
||||
}
|
||||
|
||||
if (word_idx != 0) {
|
||||
assert(number_is_immediate(word_idx, 8*sizeof(use_word)));
|
||||
assert(number_is_immediate(word_idx, 8*sizeof(use_word), 0));
|
||||
use_word = get_number_immediate(word_idx);
|
||||
}
|
||||
|
||||
@@ -1369,7 +1369,7 @@ static int show_stmt_release(ivl_statement_t net)
|
||||
ivl_expr_t part_off_ex = ivl_lval_part_off(lval);
|
||||
unsigned part_off = 0;
|
||||
if (part_off_ex != 0) {
|
||||
assert(number_is_immediate(part_off_ex, 64));
|
||||
assert(number_is_immediate(part_off_ex, 64, 0));
|
||||
part_off = get_number_immediate(part_off_ex);
|
||||
}
|
||||
|
||||
@@ -1383,7 +1383,7 @@ static int show_stmt_release(ivl_statement_t net)
|
||||
}
|
||||
|
||||
if (word_idx != 0) {
|
||||
assert(number_is_immediate(word_idx, 8*sizeof(use_word)));
|
||||
assert(number_is_immediate(word_idx, 8*sizeof(use_word), 0));
|
||||
use_word = get_number_immediate(word_idx);
|
||||
}
|
||||
|
||||
|
||||
+89
-56
@@ -546,9 +546,9 @@ static void draw_delay(ivl_net_logic_t lptr)
|
||||
return;
|
||||
|
||||
/* FIXME: Assume that the expression is a constant */
|
||||
assert(number_is_immediate(d0, 64));
|
||||
assert(number_is_immediate(d1, 64));
|
||||
assert(number_is_immediate(d2, 64));
|
||||
assert(number_is_immediate(d0, 64, 0));
|
||||
assert(number_is_immediate(d1, 64, 0));
|
||||
assert(number_is_immediate(d2, 64, 0));
|
||||
|
||||
if (d0 == d1 && d1 == d2)
|
||||
fprintf(vvp_out, " (%lu)", get_number_immediate(d0));
|
||||
@@ -862,9 +862,9 @@ static void draw_logic_in_scope(ivl_net_logic_t lptr)
|
||||
ivl_expr_t fall_exp = ivl_logic_delay(lptr,1);
|
||||
ivl_expr_t decay_exp = ivl_logic_delay(lptr,2);
|
||||
|
||||
if (number_is_immediate(rise_exp,64)
|
||||
&& number_is_immediate(fall_exp,64)
|
||||
&& number_is_immediate(decay_exp,64)) {
|
||||
if (number_is_immediate(rise_exp,64,0)
|
||||
&& number_is_immediate(fall_exp,64,0)
|
||||
&& number_is_immediate(decay_exp,64,0)) {
|
||||
|
||||
fprintf(vvp_out, "L_%p .delay (%lu,%lu,%lu) L_%p/d;\n",
|
||||
lptr, get_number_immediate(rise_exp),
|
||||
@@ -879,15 +879,15 @@ static void draw_logic_in_scope(ivl_net_logic_t lptr)
|
||||
fprintf(vvp_out, "L_%p .delay L_%p/d", lptr, lptr);
|
||||
|
||||
sig = ivl_expr_signal(rise_exp);
|
||||
assert(ivl_signal_array_count(sig) == 1);
|
||||
assert(ivl_signal_dimensions(sig) == 0);
|
||||
fprintf(vvp_out, ", v%p_0", sig);
|
||||
|
||||
sig = ivl_expr_signal(fall_exp);
|
||||
assert(ivl_signal_array_count(sig) == 1);
|
||||
assert(ivl_signal_dimensions(sig) == 0);
|
||||
fprintf(vvp_out, ", v%p_0", sig);
|
||||
|
||||
sig = ivl_expr_signal(decay_exp);
|
||||
assert(ivl_signal_array_count(sig) == 1);
|
||||
assert(ivl_signal_dimensions(sig) == 0);
|
||||
fprintf(vvp_out, ", v%p_0;\n", sig);
|
||||
}
|
||||
}
|
||||
@@ -895,6 +895,10 @@ static void draw_logic_in_scope(ivl_net_logic_t lptr)
|
||||
|
||||
static void draw_event_in_scope(ivl_event_t obj)
|
||||
{
|
||||
char tmp[4][32];
|
||||
|
||||
const unsigned ntmp = sizeof(tmp) / sizeof(tmp[0]);
|
||||
|
||||
unsigned nany = ivl_event_nany(obj);
|
||||
unsigned nneg = ivl_event_nneg(obj);
|
||||
unsigned npos = ivl_event_npos(obj);
|
||||
@@ -903,13 +907,13 @@ static void draw_event_in_scope(ivl_event_t obj)
|
||||
|
||||
/* Figure out how many probe functors are needed. */
|
||||
if (nany > 0)
|
||||
cnt += (nany+3) / 4;
|
||||
cnt += (nany+ntmp-1) / ntmp;
|
||||
|
||||
if (nneg > 0)
|
||||
cnt += (nneg+3) / 4;
|
||||
cnt += (nneg+ntmp-1) / ntmp;
|
||||
|
||||
if (npos > 0)
|
||||
cnt += (npos+3) / 4;
|
||||
cnt += (npos+ntmp-1) / ntmp;
|
||||
|
||||
if (cnt == 0) {
|
||||
/* If none are needed, then this is a named event. The
|
||||
@@ -923,48 +927,57 @@ static void draw_event_in_scope(ivl_event_t obj)
|
||||
unsigned idx;
|
||||
unsigned ecnt = 0;
|
||||
|
||||
for (idx = 0 ; idx < nany ; idx += 4, ecnt += 1) {
|
||||
for (idx = 0 ; idx < nany ; idx += ntmp, ecnt += 1) {
|
||||
unsigned sub, top;
|
||||
|
||||
fprintf(vvp_out, "E_%p/%u .event edge", obj, ecnt);
|
||||
|
||||
top = idx + 4;
|
||||
top = idx + ntmp;
|
||||
if (nany < top)
|
||||
top = nany;
|
||||
for (sub = idx ; sub < top ; sub += 1) {
|
||||
ivl_nexus_t nex = ivl_event_any(obj, sub);
|
||||
fprintf(vvp_out, ", %s", draw_input_from_net(nex));
|
||||
strncpy(tmp[sub-idx], draw_input_from_net(nex), sizeof(tmp[0]));
|
||||
}
|
||||
|
||||
fprintf(vvp_out, "E_%p/%u .event edge", obj, ecnt);
|
||||
for (sub = idx ; sub < top ; sub += 1)
|
||||
fprintf(vvp_out, ", %s", tmp[sub-idx]);
|
||||
|
||||
fprintf(vvp_out, ";\n");
|
||||
}
|
||||
|
||||
for (idx = 0 ; idx < nneg ; idx += 4, ecnt += 1) {
|
||||
for (idx = 0 ; idx < nneg ; idx += ntmp, ecnt += 1) {
|
||||
unsigned sub, top;
|
||||
|
||||
fprintf(vvp_out, "E_%p/%u .event negedge", obj, ecnt);
|
||||
|
||||
top = idx + 4;
|
||||
top = idx + ntmp;
|
||||
if (nneg < top)
|
||||
top = nneg;
|
||||
for (sub = idx ; sub < top ; sub += 1) {
|
||||
ivl_nexus_t nex = ivl_event_neg(obj, sub);
|
||||
fprintf(vvp_out, ", %s", draw_input_from_net(nex));
|
||||
strncpy(tmp[sub-idx], draw_input_from_net(nex), sizeof(tmp[0]));
|
||||
}
|
||||
|
||||
fprintf(vvp_out, "E_%p/%u .event negedge", obj, ecnt);
|
||||
for (sub = idx ; sub < top ; sub += 1)
|
||||
fprintf(vvp_out, ", %s", tmp[sub-idx]);
|
||||
|
||||
fprintf(vvp_out, ";\n");
|
||||
}
|
||||
|
||||
for (idx = 0 ; idx < npos ; idx += 4, ecnt += 1) {
|
||||
for (idx = 0 ; idx < npos ; idx += ntmp, ecnt += 1) {
|
||||
unsigned sub, top;
|
||||
|
||||
fprintf(vvp_out, "E_%p/%u .event posedge", obj, ecnt);
|
||||
|
||||
top = idx + 4;
|
||||
top = idx + ntmp;
|
||||
if (npos < top)
|
||||
top = npos;
|
||||
for (sub = idx ; sub < top ; sub += 1) {
|
||||
ivl_nexus_t nex = ivl_event_pos(obj, sub);
|
||||
fprintf(vvp_out, ", %s", draw_input_from_net(nex));
|
||||
strncpy(tmp[sub-idx], draw_input_from_net(nex), sizeof(tmp[0]));
|
||||
}
|
||||
|
||||
fprintf(vvp_out, "E_%p/%u .event posedge", obj, ecnt);
|
||||
for (sub = idx ; sub < top ; sub += 1)
|
||||
fprintf(vvp_out, ", %s", tmp[sub-idx]);
|
||||
|
||||
fprintf(vvp_out, ";\n");
|
||||
}
|
||||
|
||||
@@ -981,20 +994,17 @@ static void draw_event_in_scope(ivl_event_t obj)
|
||||
} else {
|
||||
unsigned num_input_strings = nany + nneg + npos;
|
||||
unsigned idx;
|
||||
ivl_nexus_t input_nexa[4];
|
||||
const char*edge = 0;
|
||||
|
||||
assert(num_input_strings <= 4);
|
||||
assert(num_input_strings <= ntmp);
|
||||
|
||||
if (nany > 0) {
|
||||
assert((nneg + npos) == 0);
|
||||
assert(nany <= 4);
|
||||
|
||||
edge = "edge";
|
||||
|
||||
for (idx = 0 ; idx < nany ; idx += 1) {
|
||||
ivl_nexus_t nex = ivl_event_any(obj, idx);
|
||||
input_nexa[idx] = nex;
|
||||
strncpy(tmp[idx], draw_input_from_net(nex), sizeof(tmp[0]));
|
||||
}
|
||||
|
||||
} else if (nneg > 0) {
|
||||
@@ -1003,7 +1013,7 @@ static void draw_event_in_scope(ivl_event_t obj)
|
||||
|
||||
for (idx = 0 ; idx < nneg ; idx += 1) {
|
||||
ivl_nexus_t nex = ivl_event_neg(obj, idx);
|
||||
input_nexa[idx] = nex;
|
||||
strncpy(tmp[idx], draw_input_from_net(nex), sizeof(tmp[0]));
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -1012,14 +1022,14 @@ static void draw_event_in_scope(ivl_event_t obj)
|
||||
|
||||
for (idx = 0 ; idx < npos ; idx += 1) {
|
||||
ivl_nexus_t nex = ivl_event_pos(obj, idx);
|
||||
input_nexa[idx] = nex;
|
||||
strncpy(tmp[idx], draw_input_from_net(nex), sizeof(tmp[0]));
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(vvp_out, "E_%p .event %s", obj, edge);
|
||||
for (idx = 0 ; idx < num_input_strings ; idx += 1)
|
||||
fprintf(vvp_out, ", %s", draw_input_from_net(input_nexa[idx]));
|
||||
|
||||
for (idx = 0 ; idx < num_input_strings ; idx += 1) {
|
||||
fprintf(vvp_out, ", %s", tmp[idx]);
|
||||
}
|
||||
fprintf(vvp_out, ";\n");
|
||||
}
|
||||
}
|
||||
@@ -1055,9 +1065,9 @@ static const char* draw_lpm_output_delay(ivl_lpm_t net)
|
||||
|
||||
const char*dly = "";
|
||||
if (d_rise != 0) {
|
||||
assert(number_is_immediate(d_rise, 64));
|
||||
assert(number_is_immediate(d_fall, 64));
|
||||
assert(number_is_immediate(d_decay, 64));
|
||||
assert(number_is_immediate(d_rise, 64, 0));
|
||||
assert(number_is_immediate(d_fall, 64, 0));
|
||||
assert(number_is_immediate(d_decay, 64, 0));
|
||||
dly = "/d";
|
||||
fprintf(vvp_out, "L_%p .delay (%lu,%lu,%lu) L_%p/d;\n",
|
||||
net, get_number_immediate(d_rise),
|
||||
@@ -1079,6 +1089,31 @@ static void draw_lpm_abs(ivl_lpm_t net)
|
||||
net, dly, src_table[0]);
|
||||
}
|
||||
|
||||
static void draw_lpm_cast_int(ivl_lpm_t net)
|
||||
{
|
||||
const char*src_table[1];
|
||||
draw_lpm_data_inputs(net, 0, 1, src_table);
|
||||
|
||||
const char*dly = draw_lpm_output_delay(net);
|
||||
|
||||
fprintf(vvp_out, "L_%p%s .cast/int %u, %s;\n",
|
||||
net, dly, ivl_lpm_width(net), src_table[0]);
|
||||
}
|
||||
|
||||
static void draw_lpm_cast_real(ivl_lpm_t net)
|
||||
{
|
||||
const char*src_table[1];
|
||||
draw_lpm_data_inputs(net, 0, 1, src_table);
|
||||
|
||||
const char*dly = draw_lpm_output_delay(net);
|
||||
|
||||
const char*is_signed = "";
|
||||
if (ivl_lpm_signed(net)) is_signed = ".s";
|
||||
|
||||
fprintf(vvp_out, "L_%p%s .cast/real%s %s;\n",
|
||||
net, dly, is_signed, src_table[0]);
|
||||
}
|
||||
|
||||
static void draw_lpm_add(ivl_lpm_t net)
|
||||
{
|
||||
const char*src_table[2];
|
||||
@@ -1129,20 +1164,9 @@ static void draw_lpm_add(ivl_lpm_t net)
|
||||
case IVL_LPM_POW:
|
||||
if (dto == IVL_VT_REAL)
|
||||
type = "pow.r";
|
||||
else if (ivl_lpm_signed(net)) {
|
||||
else if (ivl_lpm_signed(net))
|
||||
type = "pow.s";
|
||||
if (width > 8*sizeof(long)) {
|
||||
fprintf(stderr, "%s:%u: sorry (vvp-tgt): Signed power "
|
||||
#ifdef __MINGW32__ /* MinGW does not know about z. */
|
||||
"result must be no more than %u bits.\n",
|
||||
#else
|
||||
"result must be no more than %zu bits.\n",
|
||||
#endif
|
||||
ivl_lpm_file(net), ivl_lpm_lineno(net),
|
||||
8*sizeof(long));
|
||||
exit(1);
|
||||
}
|
||||
} else
|
||||
else
|
||||
type = "pow";
|
||||
break;
|
||||
default:
|
||||
@@ -1550,7 +1574,7 @@ static void draw_lpm_ufunc(ivl_lpm_t net)
|
||||
else
|
||||
fprintf(vvp_out, ", ");
|
||||
|
||||
assert(ivl_signal_array_count(psig) == 1);
|
||||
assert(ivl_signal_dimensions(psig) == 0);
|
||||
fprintf(vvp_out, "v%p_0", psig);
|
||||
}
|
||||
|
||||
@@ -1560,7 +1584,7 @@ static void draw_lpm_ufunc(ivl_lpm_t net)
|
||||
result is collected. */
|
||||
{ ivl_signal_t psig = ivl_scope_port(def, 0);
|
||||
assert(ivl_lpm_width(net) == ivl_signal_width(psig));
|
||||
assert(ivl_signal_array_count(psig) == 1);
|
||||
assert(ivl_signal_dimensions(psig) == 0);
|
||||
|
||||
fprintf(vvp_out, " v%p_0", psig);
|
||||
}
|
||||
@@ -1588,9 +1612,10 @@ static void draw_lpm_part(ivl_lpm_t net)
|
||||
net, dly, draw_net_input(ivl_lpm_data(net, 0)));
|
||||
fprintf(vvp_out, ", %u, %u;\n", base, width);
|
||||
} else {
|
||||
const char*sel_symbol = draw_net_input(sel);
|
||||
fprintf(vvp_out, "L_%p%s .part/v %s",
|
||||
net, dly, draw_net_input(ivl_lpm_data(net,0)));
|
||||
fprintf(vvp_out, ", %s", draw_net_input(sel));
|
||||
fprintf(vvp_out, ", %s", sel_symbol);
|
||||
fprintf(vvp_out, ", %u;\n", width);
|
||||
}
|
||||
}
|
||||
@@ -1644,6 +1669,14 @@ static void draw_lpm_in_scope(ivl_lpm_t net)
|
||||
draw_lpm_abs(net);
|
||||
return;
|
||||
|
||||
case IVL_LPM_CAST_INT:
|
||||
draw_lpm_cast_int(net);
|
||||
return;
|
||||
|
||||
case IVL_LPM_CAST_REAL:
|
||||
draw_lpm_cast_real(net);
|
||||
return;
|
||||
|
||||
case IVL_LPM_ADD:
|
||||
case IVL_LPM_SUB:
|
||||
case IVL_LPM_MULT:
|
||||
|
||||
+2
-2
@@ -205,7 +205,7 @@ verinum::verinum(int64_t that)
|
||||
bits_ = new V[nbits_];
|
||||
for (unsigned idx = 0 ; idx < nbits_ ; idx += 1) {
|
||||
bits_[idx] = (that & 1)? V1 : V0;
|
||||
that /= 2;
|
||||
that >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -508,7 +508,7 @@ verinum trim_vnum(const verinum&that)
|
||||
|
||||
/* Now top is the index of the highest non-zero bit. If
|
||||
that turns out to the highest bit in the vector, then
|
||||
tere is no trimming possible. */
|
||||
there is no trimming possible. */
|
||||
if (top+1 == that.len())
|
||||
return that;
|
||||
|
||||
|
||||
+1
-1
@@ -139,7 +139,7 @@ static int lookup_keyword(const char*text)
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a string witout the leading and trailing quotes.
|
||||
* Create a string without the leading and trailing quotes.
|
||||
*/
|
||||
static void process_quoted_string(void)
|
||||
{
|
||||
|
||||
+4
-3
@@ -267,7 +267,7 @@ static void format_time(unsigned mcd, int fsize,
|
||||
/* Fill the leading characters to make up the desired
|
||||
width. This may require a '0' if the last character
|
||||
written was the decimal point. This may also require a '0'
|
||||
if there are no other characters at all in the ouput. */
|
||||
if there are no other characters at all in the output. */
|
||||
if (fusize > 0) {
|
||||
while (bp > start_address) {
|
||||
if (*bp == '.' || strcmp(bp, timeformat_info.suff) == 0)
|
||||
@@ -1119,6 +1119,7 @@ static PLI_INT32 sys_monitor_calltf(PLI_BYTE8*name)
|
||||
case vpiReg:
|
||||
case vpiIntegerVar:
|
||||
case vpiRealVar:
|
||||
case vpiPartSelect:
|
||||
/* Monitoring reg and net values involves setting
|
||||
a callback for value changes. Pass the storage
|
||||
pointer for the callback itself as user_data so
|
||||
@@ -1414,7 +1415,7 @@ static unsigned int get_format_char(char **rtn, int ljust, int plus,
|
||||
vpi_printf("WARNING: incompatible value for %s%s.\n", info->name,
|
||||
fmtb);
|
||||
} else {
|
||||
/* If a width was not giveni, use a width of zero. */
|
||||
/* If a width was not given, use a width of zero. */
|
||||
if (width == -1) width = 0;
|
||||
if (ljust == 0) sprintf(result, "%*c", width,
|
||||
value.value.str[strlen(value.value.str)-1]);
|
||||
@@ -1658,7 +1659,7 @@ static unsigned int get_format_char(char **rtn, int ljust, int plus,
|
||||
/* If a width was not given use a width of zero. */
|
||||
if (width == -1) width = 0;
|
||||
nbits = vpi_get(vpiSize, info->items[*idx]);
|
||||
/* This is 4 chars for all but the last bit (strenght + "_")
|
||||
/* This is 4 chars for all but the last bit (strength + "_")
|
||||
* which only needs three chars (strength), but then you need
|
||||
* space for the EOS '\0', so it is just number of bits * 4. */
|
||||
rsize = nbits*4;
|
||||
|
||||
+525
-584
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -766,7 +766,7 @@ void sys_lxt_register()
|
||||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpfile";
|
||||
tf_data.calltf = sys_dumpfile_calltf;
|
||||
tf_data.compiletf = sys_dumpfile_compiletf;
|
||||
tf_data.compiletf = sys_one_string_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpfile";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
+1
-1
@@ -781,7 +781,7 @@ void sys_lxt2_register()
|
||||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpfile";
|
||||
tf_data.calltf = sys_dumpfile_calltf;
|
||||
tf_data.compiletf = sys_dumpfile_compiletf;
|
||||
tf_data.compiletf = sys_one_string_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpfile";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user