mirror of
https://github.com/steveicarus/iverilog.git
synced 2026-08-23 06:16:47 +02:00
Compare commits
535
Commits
performance
...
s20080830
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0a4b7c670 | ||
|
|
319b886118 | ||
|
|
fc476aa281 | ||
|
|
49c4f2d106 | ||
|
|
97e662d667 | ||
|
|
f900b6d541 | ||
|
|
0804eccfec | ||
|
|
2e97b28185 | ||
|
|
2d3cd7cb9a | ||
|
|
1ca8241b88 | ||
|
|
8d21c0390e | ||
|
|
3b778b1a06 | ||
|
|
e322cce650 | ||
|
|
ed929a37bb | ||
|
|
8c38872b4b | ||
|
|
d15e2a2f73 | ||
|
|
150d51c19b | ||
|
|
b455f3af5d | ||
|
|
0e458501b3 | ||
|
|
3b8dc81a97 | ||
|
|
0ae8f744c4 | ||
|
|
370ff9719f | ||
|
|
3ed0c4e809 | ||
|
|
0121c41942 | ||
|
|
091a546387 | ||
|
|
8323d5d01d | ||
|
|
b5e65ac9ed | ||
|
|
bccb762e9f | ||
|
|
924ddb787a | ||
|
|
be3d7b435d | ||
|
|
92dd13a225 | ||
|
|
8e023d1227 | ||
|
|
d21b3258e3 | ||
|
|
331a51e842 | ||
|
|
63a1e25129 | ||
|
|
fae7ab2418 | ||
|
|
535ef6be38 | ||
|
|
d3b7d7a13d | ||
|
|
b92b62a8fb | ||
|
|
7eb34013dd | ||
|
|
4ebe09bb72 | ||
|
|
a07dc968e7 | ||
|
|
28991d30f4 | ||
|
|
11109f519c | ||
|
|
5e512e6570 | ||
|
|
c032d28aaa | ||
|
|
768633e464 | ||
|
|
e501bbdd27 | ||
|
|
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 | ||
|
|
b292a5fc05 | ||
|
|
72019959a8 | ||
|
|
9565ea1034 | ||
|
|
49a2693357 | ||
|
|
10a5ca199d | ||
|
|
c2f622327f | ||
|
|
0e2628a3fb | ||
|
|
45dfa28dba | ||
|
|
c8cbac58f5 | ||
|
|
c1b5424ca6 | ||
|
|
c706c94e38 | ||
|
|
fad8abee34 | ||
|
|
9448c5939c | ||
|
|
752a90dc2f | ||
|
|
a60216ec15 | ||
|
|
5d0df8d880 | ||
|
|
1cd13ecdbd | ||
|
|
c14987aa18 | ||
|
|
aeec93a322 | ||
|
|
61930c3b6e | ||
|
|
296f1bacc1 | ||
|
|
bb80b432e6 | ||
|
|
be551a6b68 | ||
|
|
e0834f7f38 | ||
|
|
a26d91557b | ||
|
|
3f73c9bb54 | ||
|
|
09f3eb4a36 | ||
|
|
33885ed891 | ||
|
|
d21277f1b9 | ||
|
|
7b0f675785 | ||
|
|
db339b8fc3 | ||
|
|
eca8a46b40 | ||
|
|
4ca7f2a9bd | ||
|
|
221c63b766 | ||
|
|
9f04641fc7 | ||
|
|
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 | ||
|
|
25a27f9dd9 | ||
|
|
03e306c805 | ||
|
|
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 |
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
# include "config.h"
|
||||
|
||||
# include "AStatement.h"
|
||||
|
||||
AStatement::~AStatement()
|
||||
{
|
||||
}
|
||||
|
||||
AContrib::AContrib(PExpr*lv, PExpr*rv)
|
||||
: lval_(lv), rval_(rv)
|
||||
{
|
||||
}
|
||||
|
||||
AContrib::~AContrib()
|
||||
{
|
||||
delete lval_;
|
||||
delete rval_;
|
||||
}
|
||||
|
||||
AProcess::~AProcess()
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
#ifndef __AStatement_H
|
||||
#define __AStatement_H
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
# include <map>
|
||||
# include "StringHeap.h"
|
||||
# include "LineInfo.h"
|
||||
|
||||
class PExpr;
|
||||
|
||||
class AStatement : public LineInfo {
|
||||
|
||||
public:
|
||||
AStatement() { }
|
||||
virtual ~AStatement() =0;
|
||||
|
||||
virtual void dump(ostream&out, unsigned ind) const;
|
||||
|
||||
private: // not implemented
|
||||
AStatement(const AStatement&);
|
||||
AStatement& operator= (const AStatement&);
|
||||
};
|
||||
|
||||
/*
|
||||
* A contribution statement is like an assignment: there is an l-value
|
||||
* expression and an r-value expression. The l-value is a branch probe
|
||||
* expression.
|
||||
*/
|
||||
class AContrib : public AStatement {
|
||||
|
||||
public:
|
||||
AContrib(PExpr*lval, PExpr*rval);
|
||||
~AContrib();
|
||||
|
||||
virtual void dump(ostream&out, unsigned ind) const;
|
||||
|
||||
private:
|
||||
PExpr*lval_;
|
||||
PExpr*rval_;
|
||||
};
|
||||
|
||||
/*
|
||||
* An analog process is not a statement, but contains an analog
|
||||
* statement. The process is where we attach process characteristics
|
||||
* such as initial vs. always, attributes....
|
||||
*/
|
||||
class AProcess : public LineInfo {
|
||||
|
||||
public:
|
||||
enum Type { PR_INITIAL, PR_ALWAYS };
|
||||
|
||||
AProcess(Type t, AStatement*st)
|
||||
: type_(t), statement_(st) { }
|
||||
|
||||
~AProcess();
|
||||
|
||||
map<perm_string,PExpr*> attributes;
|
||||
|
||||
// Dump the analog process
|
||||
void dump(ostream&out, unsigned ind) const;
|
||||
|
||||
private:
|
||||
Type type_;
|
||||
AStatement*statement_;
|
||||
|
||||
private: // not implemented
|
||||
AProcess(const AProcess&);
|
||||
AProcess& operator= (const AProcess&);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -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
|
||||
|
||||
+5
-4
@@ -107,12 +107,13 @@ load_module.o netlist.o netmisc.o net_assign.o \
|
||||
net_design.o net_event.o net_expr.o net_force.o net_func.o \
|
||||
net_link.o net_modulo.o net_nex_input.o net_nex_output.o \
|
||||
net_proc.o net_scope.o net_tran.o net_udp.o pad_to_width.o \
|
||||
parse.o parse_misc.o pform.o pform_disciplines.o pform_dump.o pform_types.o \
|
||||
parse.o parse_misc.o pform.o pform_analog.o pform_disciplines.o \
|
||||
pform_dump.o pform_types.o \
|
||||
set_width.o symbol_search.o sync.o sys_funcs.o \
|
||||
verinum.o verireal.o target.o targets.o \
|
||||
Attrib.o HName.o LineInfo.o Module.o PDelays.o PEvent.o \
|
||||
PExpr.o PGate.o PGenerate.o PScope.o PSpec.o \
|
||||
PTask.o PUdp.o PFunction.o PWire.o Statement.o StringHeap.o \
|
||||
PTask.o PUdp.o PFunction.o PWire.o Statement.o AStatement.o StringHeap.o \
|
||||
$(FF) $(TT)
|
||||
|
||||
Makefile: Makefile.in config.h.in config.status
|
||||
@@ -243,10 +244,10 @@ $(libdir)/ivl/ivl@EXEEXT@: ./ivl@EXEEXT@
|
||||
$(INSTALL_PROGRAM) ./ivl@EXEEXT@ $(DESTDIR)$(libdir)/ivl/ivl@EXEEXT@
|
||||
|
||||
$(libdir)/ivl/include/constants.vams: $(srcdir)/constants.vams
|
||||
$(INSTALL_DATA) $(srcdir)/constants.vams $@
|
||||
$(INSTALL_DATA) $(srcdir)/constants.vams $(DESTDIR)$(libdir)/ivl/include/constants.vams
|
||||
|
||||
$(libdir)/ivl/include/disciplines.vams: $(srcdir)/disciplines.vams
|
||||
$(INSTALL_DATA) $(srcdir)/disciplines.vams $@
|
||||
$(INSTALL_DATA) $(srcdir)/disciplines.vams $(DESTDIR)$(libdir)/ivl/include/disciplines.vams
|
||||
|
||||
$(libdir)/ivl/xnf-s.conf: $(srcdir)/xnf-s.conf
|
||||
$(INSTALL_DATA) $(srcdir)/xnf-s.conf $(DESTDIR)$(libdir)/ivl/xnf-s.conf
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998-2007 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
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
# include <iostream>
|
||||
|
||||
# include "compiler.h"
|
||||
# include "PExpr.h"
|
||||
# include "Module.h"
|
||||
# include <typeinfo>
|
||||
@@ -90,7 +91,7 @@ PEBShift::~PEBShift()
|
||||
{
|
||||
}
|
||||
|
||||
PECallFunction::PECallFunction(const pform_name_t&n, const svector<PExpr *> &parms)
|
||||
PECallFunction::PECallFunction(const pform_name_t&n, const vector<PExpr *> &parms)
|
||||
: path_(n), parms_(parms)
|
||||
{
|
||||
}
|
||||
@@ -103,7 +104,7 @@ static pform_name_t pn_from_ps(perm_string n)
|
||||
return tmp;
|
||||
}
|
||||
|
||||
PECallFunction::PECallFunction(perm_string n, const svector<PExpr*>&parms)
|
||||
PECallFunction::PECallFunction(perm_string n, const vector<PExpr*>&parms)
|
||||
: path_(pn_from_ps(n)), parms_(parms)
|
||||
{
|
||||
}
|
||||
@@ -113,10 +114,99 @@ PECallFunction::PECallFunction(perm_string n)
|
||||
{
|
||||
}
|
||||
|
||||
// NOTE: Anachronism. Try to work all use of svector out.
|
||||
PECallFunction::PECallFunction(const pform_name_t&n, const svector<PExpr *> &parms)
|
||||
: path_(n), parms_(vector_from_svector(parms))
|
||||
{
|
||||
}
|
||||
|
||||
PECallFunction::PECallFunction(perm_string n, const svector<PExpr*>&parms)
|
||||
: path_(pn_from_ps(n)), parms_(vector_from_svector(parms))
|
||||
{
|
||||
}
|
||||
|
||||
PECallFunction::~PECallFunction()
|
||||
{
|
||||
}
|
||||
|
||||
bool PECallFunction::is_constant(Module*mod) const
|
||||
{
|
||||
/* Only $clog2 and the builtin mathematical functions can
|
||||
* be a constant system function. */
|
||||
perm_string name = peek_tail_name(path_);
|
||||
if (name[0] == '$' && (generation_flag >= GN_VER2005 ||
|
||||
gn_icarus_misc_flag || gn_verilog_ams_flag)) {
|
||||
if (name == "$clog2" ||
|
||||
name == "$ln" ||
|
||||
name == "$log10" ||
|
||||
name == "$exp" ||
|
||||
name == "$sqrt" ||
|
||||
name == "$floor" ||
|
||||
name == "$ceil" ||
|
||||
name == "$sin" ||
|
||||
name == "$cos" ||
|
||||
name == "$tan" ||
|
||||
name == "$asin" ||
|
||||
name == "$acos" ||
|
||||
name == "$atan" ||
|
||||
name == "$sinh" ||
|
||||
name == "$cosh" ||
|
||||
name == "$tanh" ||
|
||||
name == "$asinh" ||
|
||||
name == "$acosh" ||
|
||||
name == "$atanh") {
|
||||
if (parms_.size() != 1 || parms_[0] == 0) {
|
||||
cerr << get_fileline() << ": error: " << name
|
||||
<< " takes a single argument." << endl;
|
||||
return false;
|
||||
}
|
||||
/* If the argument is constant the function is constant. */
|
||||
return parms_[0]->is_constant(mod);
|
||||
}
|
||||
|
||||
if (name == "$pow" ||
|
||||
name == "$atan2" ||
|
||||
name == "$hypot") {
|
||||
if (parms_.size() != 2 || parms_[0] == 0 || parms_[1] == 0) {
|
||||
cerr << get_fileline() << ": error: " << name
|
||||
<< " takes two arguments." << endl;
|
||||
return false;
|
||||
/* If the arguments are constant the function is constant. */
|
||||
return parms_[0]->is_constant(mod) &&
|
||||
parms_[1]->is_constant(mod);
|
||||
}
|
||||
}
|
||||
|
||||
/* These are only available with verilog-ams or icarus-misc. */
|
||||
if ((gn_icarus_misc_flag || gn_verilog_ams_flag) &&
|
||||
(name == "$log" || name == "$abs")) {
|
||||
if (parms_.size() != 1 || parms_[0] == 0) {
|
||||
cerr << get_fileline() << ": error: " << name
|
||||
<< " takes a single argument." << endl;
|
||||
return false;
|
||||
}
|
||||
/* If the argument is constant the function is constant. */
|
||||
return parms_[0]->is_constant(mod);
|
||||
}
|
||||
if ((gn_icarus_misc_flag || gn_verilog_ams_flag) &&
|
||||
(name == "$min" || name == "$max")) {
|
||||
if (parms_.size() != 2 || parms_[0] == 0 || parms_[1] == 0) {
|
||||
cerr << get_fileline() << ": error: " << name
|
||||
<< " takes two arguments." << endl;
|
||||
return false;
|
||||
/* If the arguments are constant the function is constant. */
|
||||
return parms_[0]->is_constant(mod) &&
|
||||
parms_[1]->is_constant(mod);
|
||||
}
|
||||
}
|
||||
|
||||
return false; /* The other system functions are not constant. */
|
||||
}
|
||||
|
||||
/* Checking for constant user functions goes here. */
|
||||
return false;
|
||||
}
|
||||
|
||||
PEConcat::PEConcat(const svector<PExpr*>&p, PExpr*r)
|
||||
: parms_(p), repeat_(r)
|
||||
{
|
||||
@@ -299,4 +389,3 @@ bool PEUnary::is_constant(Module*m) const
|
||||
{
|
||||
return expr_->is_constant(m);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -696,12 +697,19 @@ class PETernary : public PExpr {
|
||||
*/
|
||||
class PECallFunction : public PExpr {
|
||||
public:
|
||||
explicit PECallFunction(const pform_name_t&n, const svector<PExpr *> &parms);
|
||||
explicit PECallFunction(const pform_name_t&n, const vector<PExpr *> &parms);
|
||||
// Call of system function (name is not hierarchical)
|
||||
explicit PECallFunction(perm_string n, const svector<PExpr *> &parms);
|
||||
explicit PECallFunction(perm_string n, const vector<PExpr *> &parms);
|
||||
explicit PECallFunction(perm_string n);
|
||||
|
||||
// svector versions. Should be removed!
|
||||
explicit PECallFunction(const pform_name_t&n, const svector<PExpr *> &parms);
|
||||
explicit PECallFunction(perm_string n, const svector<PExpr *> &parms);
|
||||
|
||||
~PECallFunction();
|
||||
|
||||
virtual bool is_constant(Module*) const;
|
||||
|
||||
virtual void dump(ostream &) const;
|
||||
|
||||
virtual NetNet* elaborate_net(Design*des, NetScope*scope,
|
||||
@@ -713,6 +721,7 @@ class PECallFunction : public PExpr {
|
||||
Link::strength_t drive1) const;
|
||||
virtual NetExpr*elaborate_expr(Design*des, NetScope*scope,
|
||||
int expr_wid, bool sys_task_arg) const;
|
||||
virtual NetExpr*elaborate_pexpr(Design*des, NetScope*sc) const;
|
||||
|
||||
virtual unsigned test_width(Design*des, NetScope*scope,
|
||||
unsigned min, unsigned lval,
|
||||
@@ -720,11 +729,12 @@ class PECallFunction : public PExpr {
|
||||
|
||||
private:
|
||||
pform_name_t path_;
|
||||
svector<PExpr *> parms_;
|
||||
vector<PExpr *> parms_;
|
||||
|
||||
bool check_call_matches_definition_(Design*des, NetScope*dscope) const;
|
||||
|
||||
NetExpr* elaborate_sfunc_(Design*des, NetScope*scope, int expr_wid) const;
|
||||
NetExpr* elaborate_access_func_(Design*des, NetScope*scope, int expr_wid) const;
|
||||
NetNet* elaborate_net_sfunc_(Design*des, NetScope*scope,
|
||||
unsigned width,
|
||||
const NetExpr* rise,
|
||||
|
||||
+2
-1
@@ -21,9 +21,10 @@
|
||||
|
||||
#include "PTask.h"
|
||||
|
||||
PFunction::PFunction(perm_string name, PScope*parent)
|
||||
PFunction::PFunction(perm_string name, PScope*parent, bool is_auto)
|
||||
: PScope(name, parent), ports_(0), statement_(0)
|
||||
{
|
||||
is_auto_ = is_auto;
|
||||
return_type_.type = PTF_NONE;
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
# include <map>
|
||||
|
||||
class PEvent;
|
||||
class AProcess;
|
||||
class PProcess;
|
||||
class PWire;
|
||||
|
||||
@@ -38,7 +39,26 @@ 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);
|
||||
|
||||
// Behaviors (processes) in this scope
|
||||
list<PProcess*> behaviors;
|
||||
list<AProcess*> analog_behaviors;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
class PScope : public LexicalScope {
|
||||
|
||||
public:
|
||||
// When created, a scope has a name and a parent. The name is
|
||||
@@ -50,21 +70,15 @@ 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;
|
||||
|
||||
// Behaviors (processes) in this scope
|
||||
list<PProcess*> behaviors;
|
||||
|
||||
protected:
|
||||
void dump_wires_(ostream&out, unsigned indent) const;
|
||||
|
||||
|
||||
@@ -21,9 +21,10 @@
|
||||
|
||||
# include "PTask.h"
|
||||
|
||||
PTask::PTask(perm_string name, PScope*parent)
|
||||
PTask::PTask(perm_string name, PScope*parent, bool is_auto)
|
||||
: PScope(name, parent), ports_(0), statement_(0)
|
||||
{
|
||||
is_auto_ = is_auto;
|
||||
}
|
||||
|
||||
PTask::~PTask()
|
||||
@@ -41,31 +42,3 @@ void PTask::set_statement(Statement*s)
|
||||
assert(statement_ == 0);
|
||||
statement_ = s;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* $Log: PTask.cc,v $
|
||||
* Revision 1.7 2002/08/12 01:34:58 steve
|
||||
* conditional ident string using autoconfig.
|
||||
*
|
||||
* Revision 1.6 2001/07/25 03:10:48 steve
|
||||
* Create a config.h.in file to hold all the config
|
||||
* junk, and support gcc 3.0. (Stephan Boettcher)
|
||||
*
|
||||
* Revision 1.5 2001/04/19 03:04:47 steve
|
||||
* Spurious assert of empty statemnt.
|
||||
*
|
||||
* Revision 1.4 2001/01/13 22:20:08 steve
|
||||
* Parse parameters within nested scopes.
|
||||
*
|
||||
* Revision 1.3 2000/02/23 02:56:53 steve
|
||||
* Macintosh compilers do not support ident.
|
||||
*
|
||||
* Revision 1.2 1999/07/24 02:11:19 steve
|
||||
* Elaborate task input ports.
|
||||
*
|
||||
* Revision 1.1 1999/07/03 02:12:51 steve
|
||||
* Elaborate user defined tasks.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ struct PTaskFuncArg {
|
||||
class PTask : public PScope, public LineInfo {
|
||||
|
||||
public:
|
||||
explicit PTask(perm_string name, PScope*parent);
|
||||
explicit PTask(perm_string name, PScope*parent, bool is_auto);
|
||||
~PTask();
|
||||
|
||||
void set_ports(svector<PWire *>*p);
|
||||
@@ -69,11 +69,14 @@ class PTask : public PScope, public LineInfo {
|
||||
// Elaborate the statement to finish off the task definition.
|
||||
void elaborate(Design*des, NetScope*scope) const;
|
||||
|
||||
bool is_auto() const { return is_auto_; };
|
||||
|
||||
void dump(ostream&, unsigned) const;
|
||||
|
||||
private:
|
||||
svector<PWire*>*ports_;
|
||||
Statement*statement_;
|
||||
bool is_auto_;
|
||||
|
||||
private: // Not implemented
|
||||
PTask(const PTask&);
|
||||
@@ -90,7 +93,7 @@ class PTask : public PScope, public LineInfo {
|
||||
class PFunction : public PScope, public LineInfo {
|
||||
|
||||
public:
|
||||
explicit PFunction(perm_string name, PScope*parent);
|
||||
explicit PFunction(perm_string name, PScope*parent, bool is_auto);
|
||||
~PFunction();
|
||||
|
||||
void set_ports(svector<PWire *>*p);
|
||||
@@ -105,12 +108,15 @@ class PFunction : public PScope, public LineInfo {
|
||||
/* Elaborate the behavioral statement. */
|
||||
void elaborate(Design *des, NetScope*) const;
|
||||
|
||||
bool is_auto() const { return is_auto_; };
|
||||
|
||||
void dump(ostream&, unsigned) const;
|
||||
|
||||
private:
|
||||
PTaskFuncArg return_type_;
|
||||
svector<PWire *> *ports_;
|
||||
Statement *statement_;
|
||||
bool is_auto_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+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=.. )
|
||||
|
||||
+3
-3
@@ -76,13 +76,13 @@ distclean: clean
|
||||
install: all installdirs $(vpidir)/cadpli.vpl $(INSTALL32)
|
||||
|
||||
$(vpidir)/cadpli.vpl: ./cadpli.vpl
|
||||
$(INSTALL_PROGRAM) ./cadpli.vpl $(vpidir)/cadpli.vpl
|
||||
$(INSTALL_PROGRAM) ./cadpli.vpl $(DESTDIR)$(vpidir)/cadpli.vpl
|
||||
|
||||
installdirs: ../mkinstalldirs
|
||||
$(srcdir)/../mkinstalldirs $(vpidir)
|
||||
$(srcdir)/../mkinstalldirs $(DESTDIR)$(vpidir)
|
||||
|
||||
uninstall: $(UNINSTALL32)
|
||||
rm -f $(vpidir)/cadpli.vpl
|
||||
rm -f $(DESTDIR)$(vpidir)/cadpli.vpl
|
||||
|
||||
uninstall32:
|
||||
|
||||
|
||||
+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)
|
||||
|
||||
@@ -897,7 +897,7 @@ void cprop_dc_functor::lpm_const(Design*des, NetConst*obj)
|
||||
for (Link*clnk = nex->first_nlink()
|
||||
; clnk ; clnk = clnk->next_nlink()) {
|
||||
|
||||
NetObj*cur;
|
||||
NetPins*cur;
|
||||
unsigned pin;
|
||||
clnk->cur_link(cur, pin);
|
||||
|
||||
@@ -923,7 +923,7 @@ void cprop_dc_functor::lpm_const(Design*des, NetConst*obj)
|
||||
for (Link*clnk = nex->first_nlink()
|
||||
; clnk ; clnk = clnk->next_nlink()) {
|
||||
|
||||
NetObj*cur;
|
||||
NetPins*cur;
|
||||
unsigned pin;
|
||||
clnk->cur_link(cur, pin);
|
||||
|
||||
|
||||
+47
-2
@@ -27,6 +27,7 @@
|
||||
# include <iomanip>
|
||||
# include "netlist.h"
|
||||
# include "compiler.h"
|
||||
# include "discipline.h"
|
||||
# include "ivl_assert.h"
|
||||
|
||||
static ostream& operator<< (ostream&o, NetBlock::Type t)
|
||||
@@ -187,6 +188,10 @@ void NetNet::dump_net(ostream&o, unsigned ind) const
|
||||
o << " inout";
|
||||
break;
|
||||
}
|
||||
|
||||
if (discipline_t*dis = get_discipline())
|
||||
o << " discipline=" << dis->name();
|
||||
|
||||
o << " (eref=" << peek_eref() << ", lref=" << peek_lref() << ")";
|
||||
if (scope())
|
||||
o << " scope=" << scope_path(scope());
|
||||
@@ -236,7 +241,7 @@ void NetNode::dump_node(ostream&o, unsigned ind) const
|
||||
/* This is the generic dumping of all the signals connected to each
|
||||
pin of the object. The "this" object is not printed, only the
|
||||
signals connected to this. */
|
||||
void NetObj::dump_node_pins(ostream&o, unsigned ind) const
|
||||
void NetPins::dump_node_pins(ostream&o, unsigned ind) const
|
||||
{
|
||||
for (unsigned idx = 0 ; idx < pin_count() ; idx += 1) {
|
||||
o << setw(ind) << "" << idx << " " << pin(idx).get_name()
|
||||
@@ -302,6 +307,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): " <<
|
||||
@@ -982,6 +1003,7 @@ void NetScope::dump(ostream&o) const
|
||||
o << " generate block";
|
||||
break;
|
||||
}
|
||||
if (is_auto()) o << " (automatic)";
|
||||
o << endl;
|
||||
|
||||
for (unsigned idx = 0 ; idx < attr_cnt() ; idx += 1)
|
||||
@@ -998,6 +1020,8 @@ void NetScope::dump(ostream&o) const
|
||||
; pp != parameters.end() ; pp ++) {
|
||||
o << " parameter ";
|
||||
|
||||
o << pp->second.type << " ";
|
||||
|
||||
if ((*pp).second.signed_flag)
|
||||
o << "signed ";
|
||||
|
||||
@@ -1047,7 +1071,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 +1079,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="
|
||||
@@ -1157,6 +1190,18 @@ void NetExpr::dump(ostream&o) const
|
||||
o << "(?" << typeid(*this).name() << "?)";
|
||||
}
|
||||
|
||||
void NetEAccess::dump(ostream&o) const
|
||||
{
|
||||
o << nature_->name() << "." << nature_->access() << "(";
|
||||
assert(branch_);
|
||||
if (branch_->pin(0).is_linked())
|
||||
o << branch_->pin(0).nexus()->name();
|
||||
o << ", ";
|
||||
if (branch_->pin(1).is_linked())
|
||||
o << branch_->pin(1).nexus()->name();
|
||||
o << ")";
|
||||
}
|
||||
|
||||
void NetEBinary::dump(ostream&o) const
|
||||
{
|
||||
if (op_ == 'm' || op_ == 'M') {
|
||||
|
||||
@@ -71,5 +71,7 @@ class discipline_t : public LineInfo {
|
||||
|
||||
extern map<perm_string,nature_t*> natures;
|
||||
extern map<perm_string,discipline_t*> disciplines;
|
||||
// Map access function name to the nature that it accesses.
|
||||
extern map<perm_string,nature_t*> access_function_nature;
|
||||
|
||||
#endif
|
||||
|
||||
+5
-5
@@ -100,17 +100,17 @@ endif
|
||||
install: all installdirs $(bindir)/iverilog@EXEEXT@ $(INSTALL_DOC)
|
||||
|
||||
$(bindir)/iverilog@EXEEXT@: ./iverilog@EXEEXT@
|
||||
$(INSTALL_PROGRAM) ./iverilog@EXEEXT@ $(bindir)/iverilog@EXEEXT@
|
||||
$(INSTALL_PROGRAM) ./iverilog@EXEEXT@ $(DESTDIR)$(bindir)/iverilog@EXEEXT@
|
||||
|
||||
$(mandir)/man1/iverilog.1: $(srcdir)/iverilog.man
|
||||
$(INSTALL_DATA) $(srcdir)/iverilog.man $(mandir)/man1/iverilog.1
|
||||
$(INSTALL_DATA) $(srcdir)/iverilog.man $(DESTDIR)$(mandir)/man1/iverilog.1
|
||||
|
||||
$(prefix)/iverilog.pdf: iverilog.pdf
|
||||
$(INSTALL_DATA) iverilog.pdf $(prefix)/iverilog.pdf
|
||||
|
||||
installdirs: ../mkinstalldirs
|
||||
$(srcdir)/../mkinstalldirs $(bindir) $(INSTALL_DOCDIR)
|
||||
$(srcdir)/../mkinstalldirs $(DESTDIR)$(bindir) $(DESTDIR)$(INSTALL_DOCDIR)
|
||||
|
||||
uninstall:
|
||||
rm -f $(bindir)/iverilog@EXEEXT@
|
||||
rm -f $(mandir)/man1/iverilog.1 $(prefix)/iverilog.pdf
|
||||
rm -f $(DESTDIR)$(bindir)/iverilog@EXEEXT@
|
||||
rm -f $(DESTDIR)$(mandir)/man1/iverilog.1 $(DESTDIR)$(prefix)/iverilog.pdf
|
||||
|
||||
@@ -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
|
||||
|
||||
+26
-17
@@ -106,7 +106,7 @@ const char*npath = 0;
|
||||
const char*targ = "vvp";
|
||||
const char*depfile = 0;
|
||||
|
||||
const char*generation = "2x";
|
||||
const char*generation = "2005";
|
||||
const char*gen_specify = "specify";
|
||||
const char*gen_xtypes = "xtypes";
|
||||
const char*gen_icarus = "icarus-misc";
|
||||
@@ -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"
|
||||
@@ -764,9 +765,18 @@ int main(int argc, char **argv)
|
||||
how to handle them. */
|
||||
fprintf(iconfig_file, "sys_func:%s%csystem.sft\n", base, sep);
|
||||
|
||||
/* If verilog-ams is enabled, then include the va_math module
|
||||
as well. */
|
||||
if (strcmp(gen_verilog_ams,"verilog-ams") == 0) {
|
||||
/* If verilog-2005 is enabled or icarus-misc or verilog-ams,
|
||||
* then include the v2005_math library. */
|
||||
if (strcmp(generation, "2005") == 0 ||
|
||||
strcmp(gen_icarus, "icarus-misc") == 0 ||
|
||||
strcmp(gen_verilog_ams, "verilog-ams") == 0) {
|
||||
fprintf(iconfig_file, "sys_func:%s%cv2005_math.sft\n", base, sep);
|
||||
fprintf(iconfig_file, "module:v2005_math\n");
|
||||
}
|
||||
/* If verilog-ams or icarus_misc is enabled, then include the
|
||||
* va_math module as well. */
|
||||
if (strcmp(gen_verilog_ams,"verilog-ams") == 0 ||
|
||||
strcmp(gen_icarus, "icarus-misc") == 0) {
|
||||
fprintf(iconfig_file, "sys_func:%s%cva_math.sft\n", base, sep);
|
||||
fprintf(iconfig_file, "module:va_math\n");
|
||||
}
|
||||
@@ -871,6 +881,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
fprintf(stderr, "Command signaled: %s\n", cmd);
|
||||
free(cmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -889,6 +900,4 @@ int main(int argc, char **argv)
|
||||
fclose(iconfig_file);
|
||||
|
||||
return t_default(cmd, ncmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,15 @@
|
||||
|
||||
# include "netlist.h"
|
||||
# include <cassert>
|
||||
# include "ivl_assert.h"
|
||||
|
||||
NetEAccess* NetEAccess::dup_expr() const
|
||||
{
|
||||
NetEAccess*tmp = new NetEAccess(branch_, nature_);
|
||||
ivl_assert(*this, tmp);
|
||||
tmp->set_line(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
NetEBComp* NetEBComp::dup_expr() const
|
||||
{
|
||||
|
||||
+78
-71
@@ -25,6 +25,7 @@
|
||||
|
||||
# include "pform.h"
|
||||
# include "netlist.h"
|
||||
# include "discipline.h"
|
||||
# include "netmisc.h"
|
||||
# include "util.h"
|
||||
# include "ivl_assert.h"
|
||||
@@ -449,7 +450,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w
|
||||
and makes it into a signed expression. No bits are changed,
|
||||
it just changes the interpretation. */
|
||||
if (strcmp(peek_tail_name(path_), "$signed") == 0) {
|
||||
if ((parms_.count() != 1) || (parms_[0] == 0)) {
|
||||
if ((parms_.size() != 1) || (parms_[0] == 0)) {
|
||||
cerr << get_fileline() << ": error: The $signed() function "
|
||||
<< "takes exactly one(1) argument." << endl;
|
||||
des->errors += 1;
|
||||
@@ -463,7 +464,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w
|
||||
}
|
||||
/* add $unsigned to match $signed */
|
||||
if (strcmp(peek_tail_name(path_), "$unsigned") == 0) {
|
||||
if ((parms_.count() != 1) || (parms_[0] == 0)) {
|
||||
if ((parms_.size() != 1) || (parms_[0] == 0)) {
|
||||
cerr << get_fileline() << ": error: The $unsigned() function "
|
||||
<< "takes exactly one(1) argument." << endl;
|
||||
des->errors += 1;
|
||||
@@ -486,7 +487,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w
|
||||
deleted. */
|
||||
if ((strcmp(peek_tail_name(path_), "$sizeof") == 0)
|
||||
|| (strcmp(peek_tail_name(path_), "$bits") == 0)) {
|
||||
if ((parms_.count() != 1) || (parms_[0] == 0)) {
|
||||
if ((parms_.size() != 1) || (parms_[0] == 0)) {
|
||||
cerr << get_fileline() << ": error: The $bits() function "
|
||||
<< "takes exactly one(1) argument." << endl;
|
||||
des->errors += 1;
|
||||
@@ -510,7 +511,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w
|
||||
delete tmp;
|
||||
}
|
||||
|
||||
verinum val (sub_expr_width, 8*sizeof(unsigned));
|
||||
verinum val ( (uint64_t)sub_expr_width, 8*sizeof(unsigned));
|
||||
NetEConst*sub = new NetEConst(val);
|
||||
sub->set_line(*this);
|
||||
|
||||
@@ -522,7 +523,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w
|
||||
otherwise. The subexpression is elaborated but not
|
||||
evaluated. */
|
||||
if (strcmp(peek_tail_name(path_), "$is_signed") == 0) {
|
||||
if ((parms_.count() != 1) || (parms_[0] == 0)) {
|
||||
if ((parms_.size() != 1) || (parms_[0] == 0)) {
|
||||
cerr << get_fileline() << ": error: The $is_signed() function "
|
||||
<< "takes exactly one(1) argument." << endl;
|
||||
des->errors += 1;
|
||||
@@ -558,7 +559,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w
|
||||
Functions cannot really take empty parameters, but the
|
||||
case ``func()'' is the same as no parameters at all. So
|
||||
catch that special case here. */
|
||||
unsigned nparms = parms_.count();
|
||||
unsigned nparms = parms_.size();
|
||||
if ((nparms == 1) && (parms_[0] == 0))
|
||||
nparms = 0;
|
||||
|
||||
@@ -601,6 +602,55 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w
|
||||
return fun;
|
||||
}
|
||||
|
||||
NetExpr* PECallFunction::elaborate_access_func_(Design*des, NetScope*scope,
|
||||
int expr_wid) const
|
||||
{
|
||||
// Hierarchical names cannot be access functions.
|
||||
if (path_.size() != 1)
|
||||
return 0;
|
||||
|
||||
perm_string access_name = peek_tail_name(path_);
|
||||
nature_t*nature = access_function_nature[access_name];
|
||||
|
||||
// If the name doesn't match any access functions, then give up.
|
||||
if (nature == 0)
|
||||
return 0;
|
||||
|
||||
// An access function must have 1 or 2 arguments.
|
||||
ivl_assert(*this, parms_.size()==2 || parms_.size()==1);
|
||||
|
||||
NetBranch*branch = 0;
|
||||
|
||||
if (parms_.size() == 1) {
|
||||
PExpr*arg1 = parms_[0];
|
||||
PEIdent*arg_ident = dynamic_cast<PEIdent*> (arg1);
|
||||
ivl_assert(*this, arg_ident);
|
||||
|
||||
const pform_name_t&path = arg_ident->path();
|
||||
ivl_assert(*this, path.size()==1);
|
||||
perm_string name = peek_tail_name(path);
|
||||
|
||||
NetNet*sig = scope->find_signal(name);
|
||||
ivl_assert(*this, sig);
|
||||
|
||||
discipline_t*dis = sig->get_discipline();
|
||||
ivl_assert(*this, dis);
|
||||
ivl_assert(*this, nature == dis->potential() || nature == dis->flow());
|
||||
|
||||
branch = new NetBranch(dis);
|
||||
branch->set_line(*this);
|
||||
connect(branch->pin(0), sig->pin(0));
|
||||
|
||||
} else {
|
||||
ivl_assert(*this, 0);
|
||||
}
|
||||
|
||||
NetEAccess*tmp = new NetEAccess(branch, nature);
|
||||
tmp->set_line(*this);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope,
|
||||
int expr_wid, bool) const
|
||||
{
|
||||
@@ -609,20 +659,26 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope,
|
||||
|
||||
NetFuncDef*def = des->find_function(scope, path_);
|
||||
if (def == 0) {
|
||||
// Not a user defined function. Maybe it is an access
|
||||
// function for a nature? If so then elaborate it that way.
|
||||
NetExpr*tmp = elaborate_access_func_(des, scope, expr_wid);
|
||||
if (tmp != 0)
|
||||
return tmp;
|
||||
|
||||
cerr << get_fileline() << ": error: No function " << path_ <<
|
||||
" in this context (" << scope_path(scope) << ")." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
assert(def);
|
||||
ivl_assert(*this, def);
|
||||
|
||||
NetScope*dscope = def->scope();
|
||||
assert(dscope);
|
||||
ivl_assert(*this, dscope);
|
||||
|
||||
if (! check_call_matches_definition_(des, dscope))
|
||||
return 0;
|
||||
|
||||
unsigned parms_count = parms_.count();
|
||||
unsigned parms_count = parms_.size();
|
||||
if ((parms_count == 1) && (parms_[0] == 0))
|
||||
parms_count = 0;
|
||||
|
||||
@@ -1182,12 +1238,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 +1429,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;
|
||||
@@ -1494,7 +1550,7 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope,
|
||||
// Recalculate the constant address with the adjusted base.
|
||||
unsigned use_addr = net->array_index_to_address(addr);
|
||||
if (addr < 0 || use_addr != (unsigned long)addr) {
|
||||
verinum val (use_addr, 8*sizeof(use_addr));
|
||||
verinum val ( (uint64_t)use_addr, 8*sizeof(use_addr));
|
||||
NetEConst*tmp = new NetEConst(val);
|
||||
tmp->set_line(*this);
|
||||
delete word_index;
|
||||
@@ -1567,9 +1623,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 +1638,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);
|
||||
|
||||
+51
-20
@@ -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;
|
||||
|
||||
@@ -1459,7 +1466,7 @@ NetNet* PECallFunction::elaborate_net_sfunc_(Design*des, NetScope*scope,
|
||||
forces it to be a signed result. Otherwise, it is as if the
|
||||
$signed did not exist. */
|
||||
if (strcmp(name, "$signed") == 0) {
|
||||
if ((parms_.count() != 1) || (parms_[0] == 0)) {
|
||||
if ((parms_.size() != 1) || (parms_[0] == 0)) {
|
||||
cerr << get_fileline() << ": error: The $signed() function "
|
||||
<< "takes exactly one(1) argument." << endl;
|
||||
des->errors += 1;
|
||||
@@ -1475,7 +1482,7 @@ NetNet* PECallFunction::elaborate_net_sfunc_(Design*des, NetScope*scope,
|
||||
|
||||
/* handle $unsigned like $signed */
|
||||
if (strcmp(name, "$unsigned") == 0) {
|
||||
if ((parms_.count() != 1) || (parms_[0] == 0)) {
|
||||
if ((parms_.size() != 1) || (parms_[0] == 0)) {
|
||||
cerr << get_fileline() << ": error: The $unsigned() function "
|
||||
<< "takes exactly one(1) argument." << endl;
|
||||
des->errors += 1;
|
||||
@@ -1507,7 +1514,7 @@ NetNet* PECallFunction::elaborate_net_sfunc_(Design*des, NetScope*scope,
|
||||
}
|
||||
|
||||
NetSysFunc*net = new NetSysFunc(scope, scope->local_symbol(),
|
||||
def, 1+parms_.count());
|
||||
def, 1+parms_.size());
|
||||
net->set_line(*this);
|
||||
net->rise_time(rise);
|
||||
net->fall_time(fall);
|
||||
@@ -1524,7 +1531,7 @@ NetNet* PECallFunction::elaborate_net_sfunc_(Design*des, NetScope*scope,
|
||||
connect(net->pin(0), osig->pin(0));
|
||||
|
||||
unsigned errors = 0;
|
||||
for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) {
|
||||
for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) {
|
||||
NetNet*tmp = parms_[idx]->elaborate_net(des, scope, 0,
|
||||
0, 0, 0,
|
||||
Link::STRONG, Link::STRONG);
|
||||
@@ -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;
|
||||
|
||||
+137
@@ -267,3 +267,140 @@ NetExpr*PEUnary::elaborate_pexpr (Design*des, NetScope*scope) const
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/* Reuse these routines from eval_tree.cc. */
|
||||
NetExpr* evaluate_clog2(NetExpr*arg);
|
||||
NetExpr* evaluate_math_one_arg(NetExpr*arg, const char*name);
|
||||
NetExpr* evaluate_math_two_args(NetExpr*arg0, NetExpr*arg1, const char*name);
|
||||
NetExpr* evaluate_abs(NetExpr*arg);
|
||||
NetExpr* evaluate_min_max(NetExpr*arg0, NetExpr*arg1, const char*name);
|
||||
|
||||
NetExpr* PECallFunction::elaborate_pexpr(Design*des, NetScope*scope) const
|
||||
{
|
||||
/* Only $clog2 and the builtin mathematical functions can
|
||||
* be a constant system function. */
|
||||
perm_string name = peek_tail_name(path_);
|
||||
if (name[0] == '$' && (generation_flag >= GN_VER2005 ||
|
||||
gn_icarus_misc_flag || gn_verilog_ams_flag)) {
|
||||
if (name == "$clog2" ||
|
||||
name == "$ln" ||
|
||||
name == "$log10" ||
|
||||
name == "$exp" ||
|
||||
name == "$sqrt" ||
|
||||
name == "$floor" ||
|
||||
name == "$ceil" ||
|
||||
name == "$sin" ||
|
||||
name == "$cos" ||
|
||||
name == "$tan" ||
|
||||
name == "$asin" ||
|
||||
name == "$acos" ||
|
||||
name == "$atan" ||
|
||||
name == "$sinh" ||
|
||||
name == "$cosh" ||
|
||||
name == "$tanh" ||
|
||||
name == "$asinh" ||
|
||||
name == "$acosh" ||
|
||||
name == "$atanh") {
|
||||
if (parms_.size() != 1 || parms_[0] == 0) {
|
||||
cerr << get_fileline() << ": error: " << name
|
||||
<< " takes a single argument." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
NetExpr*arg = parms_[0]->elaborate_pexpr(des, scope);
|
||||
if (arg == 0) return 0;
|
||||
eval_expr(arg);
|
||||
NetExpr*rtn;
|
||||
if (peek_tail_name(path_) == "$clog2") {
|
||||
rtn = evaluate_clog2(arg);
|
||||
} else {
|
||||
rtn = evaluate_math_one_arg(arg, name.str());
|
||||
}
|
||||
delete arg;
|
||||
if (rtn != 0) {
|
||||
rtn->set_line(*this);
|
||||
return rtn;
|
||||
}
|
||||
}
|
||||
|
||||
if (name == "$pow" ||
|
||||
name == "$atan2" ||
|
||||
name == "$hypot") {
|
||||
if (parms_.size() != 2 || parms_[0] == 0 || parms_[1] == 0) {
|
||||
cerr << get_fileline() << ": error: " << name
|
||||
<< " takes two arguments." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
NetExpr*arg0 = parms_[0]->elaborate_pexpr(des, scope);
|
||||
NetExpr*arg1 = parms_[1]->elaborate_pexpr(des, scope);
|
||||
if (arg0 == 0 || arg1 == 0) return 0;
|
||||
eval_expr(arg0);
|
||||
eval_expr(arg1);
|
||||
NetExpr*rtn = evaluate_math_two_args(arg0, arg1, name.str());
|
||||
delete arg0;
|
||||
delete arg1;
|
||||
if (rtn != 0) {
|
||||
rtn->set_line(*this);
|
||||
return rtn;
|
||||
}
|
||||
}
|
||||
|
||||
/* These are only available with verilog-ams or icarus-misc. */
|
||||
if ((gn_icarus_misc_flag || gn_verilog_ams_flag) &&
|
||||
(name == "$log" || name == "$abs")) {
|
||||
if (parms_.size() != 1 || parms_[0] == 0) {
|
||||
cerr << get_fileline() << ": error: " << name
|
||||
<< " takes a single argument." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
NetExpr*arg = parms_[0]->elaborate_pexpr(des, scope);
|
||||
if (arg == 0) return 0;
|
||||
eval_expr(arg);
|
||||
NetExpr*rtn;
|
||||
if (peek_tail_name(path_) == "$log") {
|
||||
rtn = evaluate_math_one_arg(arg, name.str());
|
||||
} else {
|
||||
rtn = evaluate_abs(arg);
|
||||
}
|
||||
delete arg;
|
||||
if (rtn != 0) {
|
||||
rtn->set_line(*this);
|
||||
return rtn;
|
||||
}
|
||||
}
|
||||
if ((gn_icarus_misc_flag || gn_verilog_ams_flag) &&
|
||||
(name == "$min" || name == "$max")) {
|
||||
if (parms_.size() != 2 || parms_[0] == 0 || parms_[1] == 0) {
|
||||
cerr << get_fileline() << ": error: " << name
|
||||
<< " takes two arguments." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
NetExpr*arg0 = parms_[0]->elaborate_pexpr(des, scope);
|
||||
NetExpr*arg1 = parms_[1]->elaborate_pexpr(des, scope);
|
||||
if (arg0 == 0 || arg1 == 0) return 0;
|
||||
eval_expr(arg0);
|
||||
eval_expr(arg1);
|
||||
NetExpr*rtn = evaluate_min_max(arg0, arg1, name.str());
|
||||
delete arg0;
|
||||
delete arg1;
|
||||
if (rtn != 0) {
|
||||
rtn->set_line(*this);
|
||||
return rtn;
|
||||
}
|
||||
}
|
||||
|
||||
cerr << get_fileline() << ": error: this is not a constant "
|
||||
"system function (" << *this << ")." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Constant user function code goes here. */
|
||||
cerr << get_fileline() << ": sorry: constant user functions are not "
|
||||
"currently supported." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
+236
-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,126 @@ 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->is_auto((*cur).second->is_auto());
|
||||
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->is_auto((*cur).second->is_auto());
|
||||
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 +243,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 +326,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 +349,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 +485,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 +537,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 +567,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 +614,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 +631,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 +661,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 +688,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 +704,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 +795,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);
|
||||
|
||||
+65
-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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1016,6 +1040,10 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const
|
||||
sig->set_signed(get_signed());
|
||||
sig->set_isint(get_isint());
|
||||
|
||||
if (discipline_t*dis = get_discipline()) {
|
||||
sig->set_discipline(dis);
|
||||
}
|
||||
|
||||
if (pull)
|
||||
connect(sig->pin(0), pull->pin(0));
|
||||
|
||||
|
||||
+252
-96
@@ -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;
|
||||
}
|
||||
@@ -3357,29 +3381,6 @@ bool PProcess::elaborate(Design*des, NetScope*scope) const
|
||||
verinum(1));
|
||||
} while (0);
|
||||
|
||||
/* If this is an always block and we have no or zero delay then
|
||||
* a runtime infinite loop will happen. If we possible have some
|
||||
* delay then print a warning that an infinite loop is possible.
|
||||
*/
|
||||
if (type() == PProcess::PR_ALWAYS) {
|
||||
DelayType dly_type = top->statement()->delay_type();
|
||||
|
||||
if (dly_type == NO_DELAY || dly_type == ZERO_DELAY) {
|
||||
cerr << get_fileline() << ": error: always statement"
|
||||
<< " does not have any delay." << endl;
|
||||
cerr << get_fileline() << ": : A runtime infinite"
|
||||
<< " loop will occur." << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
|
||||
} else if (dly_type == POSSIBLE_DELAY && warn_inf_loop) {
|
||||
cerr << get_fileline() << ": warning: always statement"
|
||||
<< " may not have any delay." << endl;
|
||||
cerr << get_fileline() << ": : A runtime infinite"
|
||||
<< " loop may be possible." << endl;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3556,6 +3557,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 +3644,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 +3694,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 +3737,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 +3749,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 +3778,113 @@ 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();
|
||||
}
|
||||
};
|
||||
|
||||
bool Design::check_always_delay() const
|
||||
{
|
||||
bool result_flag = true;
|
||||
|
||||
for (const NetProcTop*pr = procs_ ; pr ; pr = pr->next_) {
|
||||
/* If this is an always block and we have no or zero delay then
|
||||
* a runtime infinite loop will happen. If we possible have some
|
||||
* delay then print a warning that an infinite loop is possible.
|
||||
*/
|
||||
if (pr->type() == NetProcTop::KALWAYS) {
|
||||
DelayType dly_type = pr->statement()->delay_type();
|
||||
|
||||
if (dly_type == NO_DELAY || dly_type == ZERO_DELAY) {
|
||||
cerr << pr->get_fileline() << ": error: always"
|
||||
<< " statement does not have any delay." << endl;
|
||||
cerr << pr->get_fileline() << ": : A runtime"
|
||||
<< " infinite loop will occur." << endl;
|
||||
result_flag = false;
|
||||
|
||||
} else if (dly_type == POSSIBLE_DELAY && warn_inf_loop) {
|
||||
cerr << pr->get_fileline() << ": warning: always"
|
||||
<< " statement may not have any delay." << endl;
|
||||
cerr << pr->get_fileline() << ": : A runtime"
|
||||
<< " infinite loop may be possible." << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result_flag;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 +3895,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 +3914,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 NetScope 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 definition, 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
|
||||
@@ -3855,8 +4005,14 @@ Design* elaborate(list<perm_string>roots)
|
||||
rc &= rmod->elaborate(des, scope);
|
||||
}
|
||||
|
||||
|
||||
if (rc == false) {
|
||||
delete des;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Now that everything is fully elaborated verify that we do
|
||||
// not have an always block with no delay (an infinite loop).
|
||||
if (des->check_always_delay() == false) {
|
||||
delete des;
|
||||
des = 0;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -188,8 +198,7 @@ bool NetProc::emit_proc(struct target_t*tgt) const
|
||||
|
||||
bool NetAssign::emit_proc(struct target_t*tgt) const
|
||||
{
|
||||
tgt->proc_assign(this);
|
||||
return true;
|
||||
return tgt->proc_assign(this);
|
||||
}
|
||||
|
||||
bool NetAssignNB::emit_proc(struct target_t*tgt) const
|
||||
@@ -362,7 +371,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 {
|
||||
@@ -451,6 +460,11 @@ int Design::emit(struct target_t*tgt) const
|
||||
return rc;
|
||||
}
|
||||
|
||||
void NetEAccess::expr_scan(struct expr_scan_t*tgt) const
|
||||
{
|
||||
tgt->expr_access_func(this);
|
||||
}
|
||||
|
||||
void NetEBinary::expr_scan(struct expr_scan_t*tgt) const
|
||||
{
|
||||
tgt->expr_binary(this);
|
||||
|
||||
+352
-41
@@ -22,6 +22,8 @@
|
||||
|
||||
# include <iostream>
|
||||
# include <cstdlib>
|
||||
# include <cstring>
|
||||
# include <math.h>
|
||||
|
||||
# include "netlist.h"
|
||||
# include "ivl_assert.h"
|
||||
@@ -338,19 +340,15 @@ NetEConst* NetEBComp::eval_leeq_real_(NetExpr*le, NetExpr*ri, bool eq_flag)
|
||||
switch (le->expr_type()) {
|
||||
case IVL_VT_REAL:
|
||||
rtmp = dynamic_cast<NetECReal*> (le);
|
||||
if (rtmp == 0)
|
||||
return 0;
|
||||
|
||||
if (rtmp == 0) return 0;
|
||||
lv = rtmp->value().as_double();
|
||||
break;
|
||||
|
||||
case IVL_VT_LOGIC:
|
||||
case IVL_VT_BOOL:
|
||||
vtmp = dynamic_cast<NetEConst*> (le);
|
||||
if (vtmp == 0)
|
||||
return 0;
|
||||
|
||||
lv = vtmp->value().as_long();
|
||||
if (vtmp == 0) return 0;
|
||||
lv = vtmp->value().as_double();
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -359,23 +357,18 @@ NetEConst* NetEBComp::eval_leeq_real_(NetExpr*le, NetExpr*ri, bool eq_flag)
|
||||
assert(0);
|
||||
}
|
||||
|
||||
|
||||
switch (ri->expr_type()) {
|
||||
case IVL_VT_REAL:
|
||||
rtmp = dynamic_cast<NetECReal*> (ri);
|
||||
if (rtmp == 0)
|
||||
return 0;
|
||||
|
||||
if (rtmp == 0) return 0;
|
||||
rv = rtmp->value().as_double();
|
||||
break;
|
||||
|
||||
case IVL_VT_LOGIC:
|
||||
case IVL_VT_BOOL:
|
||||
vtmp = dynamic_cast<NetEConst*> (ri);
|
||||
if (vtmp == 0)
|
||||
return 0;
|
||||
|
||||
rv = vtmp->value().as_long();
|
||||
if (vtmp == 0) return 0;
|
||||
rv = vtmp->value().as_double();
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -479,19 +472,6 @@ NetEConst* NetEBComp::eval_gt_()
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/* Compare with a real value. Do it as double precision. */
|
||||
if (right_->expr_type() == IVL_VT_REAL) {
|
||||
NetECReal*tmp = dynamic_cast<NetECReal*>(right_);
|
||||
if (tmp == 0)
|
||||
return 0;
|
||||
|
||||
double rr = tmp->value().as_double();
|
||||
double ll = lv.has_sign()? lv.as_long() : lv.as_ulong();
|
||||
|
||||
verinum result ((ll > rr)? verinum::V1 : verinum::V0, 1, true);
|
||||
return new NetEConst(result);
|
||||
}
|
||||
|
||||
/* Now go on to the normal test of the values. */
|
||||
NetEConst*r = dynamic_cast<NetEConst*>(right_);
|
||||
if (r == 0) return 0;
|
||||
@@ -530,19 +510,6 @@ NetEConst* NetEBComp::eval_gteq_()
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/* Compare with a real value. Do it as double precision. */
|
||||
if (right_->expr_type() == IVL_VT_REAL) {
|
||||
NetECReal*tmp = dynamic_cast<NetECReal*>(right_);
|
||||
if (tmp == 0)
|
||||
return 0;
|
||||
|
||||
double rr = tmp->value().as_double();
|
||||
double ll = lv.has_sign()? lv.as_long() : lv.as_ulong();
|
||||
|
||||
verinum result ((ll >= rr)? verinum::V1 : verinum::V0, 1, true);
|
||||
return new NetEConst(result);
|
||||
}
|
||||
|
||||
/* Now go on to the normal test of the values. */
|
||||
NetEConst*r = dynamic_cast<NetEConst*>(right_);
|
||||
if (r == 0) return 0;
|
||||
@@ -561,8 +528,66 @@ NetEConst* NetEBComp::eval_gteq_()
|
||||
}
|
||||
}
|
||||
|
||||
NetEConst* NetEBComp::eval_eqeq_real_(NetExpr*le, NetExpr*ri, bool ne_flag)
|
||||
{
|
||||
NetEConst*vtmp;
|
||||
NetECReal*rtmp;
|
||||
double lv, rv;
|
||||
|
||||
switch (le->expr_type()) {
|
||||
case IVL_VT_REAL:
|
||||
rtmp = dynamic_cast<NetECReal*> (le);
|
||||
if (rtmp == 0) return 0;
|
||||
lv = rtmp->value().as_double();
|
||||
break;
|
||||
|
||||
case IVL_VT_LOGIC:
|
||||
case IVL_VT_BOOL:
|
||||
vtmp = dynamic_cast<NetEConst*> (le);
|
||||
if (vtmp == 0) return 0;
|
||||
lv = vtmp->value().as_double();
|
||||
break;
|
||||
|
||||
default:
|
||||
cerr << get_fileline() << ": internal error: "
|
||||
<< "Unexpected expression type? " << le->expr_type() << endl;
|
||||
assert(0);
|
||||
}
|
||||
|
||||
switch (ri->expr_type()) {
|
||||
case IVL_VT_REAL:
|
||||
rtmp = dynamic_cast<NetECReal*> (ri);
|
||||
if (rtmp == 0) return 0;
|
||||
rv = rtmp->value().as_double();
|
||||
break;
|
||||
|
||||
case IVL_VT_LOGIC:
|
||||
case IVL_VT_BOOL:
|
||||
vtmp = dynamic_cast<NetEConst*> (ri);
|
||||
if (vtmp == 0) return 0;
|
||||
rv = vtmp->value().as_double();
|
||||
break;
|
||||
|
||||
default:
|
||||
cerr << get_fileline() << ": internal error: "
|
||||
<< "Unexpected expression type? " << ri->expr_type() << endl;
|
||||
assert(0);
|
||||
}
|
||||
|
||||
verinum result((lv == rv ^ ne_flag) ? verinum::V1 : verinum::V0, 1);
|
||||
vtmp = new NetEConst(result);
|
||||
vtmp->set_line(*this);
|
||||
|
||||
return vtmp;
|
||||
}
|
||||
|
||||
NetEConst* NetEBComp::eval_eqeq_(bool ne_flag)
|
||||
{
|
||||
if (right_->expr_type() == IVL_VT_REAL)
|
||||
return eval_eqeq_real_(right_, left_, ne_flag);
|
||||
if (left_->expr_type() == IVL_VT_REAL)
|
||||
return eval_eqeq_real_(right_, left_, ne_flag);
|
||||
|
||||
NetEConst*l = dynamic_cast<NetEConst*>(left_);
|
||||
if (l == 0) return 0;
|
||||
NetEConst*r = dynamic_cast<NetEConst*>(right_);
|
||||
@@ -1607,3 +1632,289 @@ NetEConst* NetEUReduce::eval_tree(int prune_to_width)
|
||||
|
||||
return new NetEConst(verinum(res, 1));
|
||||
}
|
||||
|
||||
NetExpr* evaluate_clog2(NetExpr*arg)
|
||||
{
|
||||
NetEConst*tmpi = dynamic_cast<NetEConst *>(arg);
|
||||
NetECReal*tmpr = dynamic_cast<NetECReal *>(arg);
|
||||
if (tmpi || tmpr) {
|
||||
verinum arg;
|
||||
if (tmpi) {
|
||||
arg = tmpi->value();
|
||||
} else {
|
||||
arg = verinum(tmpr->value().as_double(), true);
|
||||
}
|
||||
|
||||
/* If we have an x in the verinum we return 32'bx. */
|
||||
if (!arg.is_defined()) {
|
||||
verinum tmp (verinum::Vx, 32);
|
||||
tmp.has_sign(true);
|
||||
NetEConst*rtn = new NetEConst(tmp);
|
||||
return rtn;
|
||||
}
|
||||
|
||||
bool is_neg = false;
|
||||
uint64_t res = 0;
|
||||
if (arg.is_negative()) {
|
||||
is_neg = true;
|
||||
// If the length is not defined, then work with
|
||||
// the trimmed version of the number.
|
||||
if (! arg.has_len())
|
||||
arg = trim_vnum(arg);
|
||||
}
|
||||
arg.has_sign(false); // $unsigned()
|
||||
|
||||
if (!arg.is_zero()) {
|
||||
arg = arg - verinum((uint64_t)1, 1);
|
||||
while (!arg.is_zero()) {
|
||||
res += 1;
|
||||
arg = arg >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_neg && res < integer_width)
|
||||
res = integer_width;
|
||||
|
||||
verinum tmp (res, 32);
|
||||
NetEConst*rtn = new NetEConst(tmp);
|
||||
return rtn;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
NetExpr* evaluate_math_one_arg(NetExpr*arg, const char*name)
|
||||
{
|
||||
NetEConst*tmpi = dynamic_cast<NetEConst *>(arg);
|
||||
NetECReal*tmpr = dynamic_cast<NetECReal *>(arg);
|
||||
if (tmpi || tmpr) {
|
||||
double arg;
|
||||
if (tmpi) {
|
||||
arg = tmpi->value().as_double();
|
||||
} else {
|
||||
arg = tmpr->value().as_double();
|
||||
}
|
||||
|
||||
if (strcmp(name, "$ln") == 0) {
|
||||
return new NetECReal(verireal(log(arg)));
|
||||
} else if (strcmp(name, "$log") == 0) {
|
||||
return new NetECReal(verireal(log10(arg)));
|
||||
} else if (strcmp(name, "$log10") == 0) {
|
||||
return new NetECReal(verireal(log10(arg)));
|
||||
} else if (strcmp(name, "$exp") == 0) {
|
||||
return new NetECReal(verireal(exp(arg)));
|
||||
} else if (strcmp(name, "$sqrt") == 0) {
|
||||
return new NetECReal(verireal(sqrt(arg)));
|
||||
} else if (strcmp(name, "$floor") == 0) {
|
||||
return new NetECReal(verireal(floor(arg)));
|
||||
} else if (strcmp(name, "$ceil") == 0) {
|
||||
return new NetECReal(verireal(ceil(arg)));
|
||||
} else if (strcmp(name, "$sin") == 0) {
|
||||
return new NetECReal(verireal(sin(arg)));
|
||||
} else if (strcmp(name, "$cos") == 0) {
|
||||
return new NetECReal(verireal(cos(arg)));
|
||||
} else if (strcmp(name, "$tan") == 0) {
|
||||
return new NetECReal(verireal(tan(arg)));
|
||||
} else if (strcmp(name, "$asin") == 0) {
|
||||
return new NetECReal(verireal(asin(arg)));
|
||||
} else if (strcmp(name, "$acos") == 0) {
|
||||
return new NetECReal(verireal(acos(arg)));
|
||||
} else if (strcmp(name, "$atan") == 0) {
|
||||
return new NetECReal(verireal(atan(arg)));
|
||||
} else if (strcmp(name, "$sinh") == 0) {
|
||||
return new NetECReal(verireal(sinh(arg)));
|
||||
} else if (strcmp(name, "$cosh") == 0) {
|
||||
return new NetECReal(verireal(cosh(arg)));
|
||||
} else if (strcmp(name, "$tanh") == 0) {
|
||||
return new NetECReal(verireal(tanh(arg)));
|
||||
} else if (strcmp(name, "$asinh") == 0) {
|
||||
return new NetECReal(verireal(asinh(arg)));
|
||||
} else if (strcmp(name, "$acosh") == 0) {
|
||||
return new NetECReal(verireal(acosh(arg)));
|
||||
} else if (strcmp(name, "$atanh") == 0) {
|
||||
return new NetECReal(verireal(atanh(arg)));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
NetExpr* evaluate_math_two_args(NetExpr*arg0, NetExpr*arg1, const char*name)
|
||||
{
|
||||
NetEConst*tmpi0 = dynamic_cast<NetEConst *>(arg0);
|
||||
NetECReal*tmpr0 = dynamic_cast<NetECReal *>(arg0);
|
||||
NetEConst*tmpi1 = dynamic_cast<NetEConst *>(arg1);
|
||||
NetECReal*tmpr1 = dynamic_cast<NetECReal *>(arg1);
|
||||
if ((tmpi0 || tmpr0) && (tmpi1 || tmpr1)) {
|
||||
double arg0, arg1;
|
||||
if (tmpi0) {
|
||||
arg0 = tmpi0->value().as_double();
|
||||
} else {
|
||||
arg0 = tmpr0->value().as_double();
|
||||
}
|
||||
if (tmpi1) {
|
||||
arg1 = tmpi1->value().as_double();
|
||||
} else {
|
||||
arg1 = tmpr1->value().as_double();
|
||||
}
|
||||
|
||||
if (strcmp(name, "$pow") == 0) {
|
||||
return new NetECReal(verireal(pow(arg0, arg1)));
|
||||
} else if (strcmp(name, "$atan2") == 0) {
|
||||
return new NetECReal(verireal(atan2(arg0, arg1)));
|
||||
} else if (strcmp(name, "$hypot") == 0) {
|
||||
return new NetECReal(verireal(hypot(arg0, arg1)));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
NetExpr* evaluate_abs(NetExpr*arg)
|
||||
{
|
||||
NetEConst*tmpi = dynamic_cast<NetEConst *>(arg);
|
||||
if (tmpi) {
|
||||
verinum arg = tmpi->value();
|
||||
if (arg.is_negative()) {
|
||||
arg = v_not(arg) + verinum(1);
|
||||
}
|
||||
return new NetEConst(arg);
|
||||
}
|
||||
|
||||
NetECReal*tmpr = dynamic_cast<NetECReal *>(arg);
|
||||
if (tmpr) {
|
||||
double arg = tmpr->value().as_double();
|
||||
return new NetECReal(verireal(fabs(arg)));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
NetExpr* evaluate_min_max(NetExpr*arg0, NetExpr*arg1, const char*name)
|
||||
{
|
||||
NetEConst*tmpi0 = dynamic_cast<NetEConst *>(arg0);
|
||||
NetECReal*tmpr0 = dynamic_cast<NetECReal *>(arg0);
|
||||
NetEConst*tmpi1 = dynamic_cast<NetEConst *>(arg1);
|
||||
NetECReal*tmpr1 = dynamic_cast<NetECReal *>(arg1);
|
||||
if (tmpi0 && tmpi1) {
|
||||
verinum arg0 = tmpi0->value();
|
||||
verinum arg1 = tmpi1->value();
|
||||
if (strcmp(name, "$min") == 0) {
|
||||
return new NetEConst( arg0 < arg1 ? arg0 : arg1);
|
||||
} else if (strcmp(name, "$max") == 0) {
|
||||
return new NetEConst( arg0 < arg1 ? arg1 : arg0);
|
||||
}
|
||||
}
|
||||
|
||||
if ((tmpi0 || tmpr0) && (tmpi1 || tmpr1)) {
|
||||
double arg0, arg1;
|
||||
if (tmpi0) {
|
||||
arg0 = tmpi0->value().as_double();
|
||||
} else {
|
||||
arg0 = tmpr0->value().as_double();
|
||||
}
|
||||
if (tmpi1) {
|
||||
arg1 = tmpi1->value().as_double();
|
||||
} else {
|
||||
arg1 = tmpr1->value().as_double();
|
||||
}
|
||||
if (strcmp(name, "$min") == 0) {
|
||||
return new NetECReal(verireal(arg0 < arg1 ? arg0 : arg1));
|
||||
} else if (strcmp(name, "$max") == 0) {
|
||||
return new NetECReal(verireal(arg0 < arg1 ? arg1 : arg0));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
NetExpr* NetESFunc::eval_tree(int prune_to_width)
|
||||
{
|
||||
/* If we are not targeting at least Verilog-2005, Verilog-AMS
|
||||
* or using the Icarus misc flag then we do not support these
|
||||
* functions as constant. */
|
||||
if (generation_flag < GN_VER2005 &&
|
||||
!gn_icarus_misc_flag && !gn_verilog_ams_flag) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char*nm = name();
|
||||
NetExpr*rtn = 0;
|
||||
/* Only $clog2 and the builtin mathematical functions can
|
||||
* be a constant system function. */
|
||||
if (strcmp(nm, "$clog2") == 0 ||
|
||||
strcmp(nm, "$ln") == 0 ||
|
||||
strcmp(nm, "$log10") == 0 ||
|
||||
strcmp(nm, "$exp") == 0 ||
|
||||
strcmp(nm, "$sqrt") == 0 ||
|
||||
strcmp(nm, "$floor") == 0 ||
|
||||
strcmp(nm, "$ceil") == 0 ||
|
||||
strcmp(nm, "$sin") == 0 ||
|
||||
strcmp(nm, "$cos") == 0 ||
|
||||
strcmp(nm, "$tan") == 0 ||
|
||||
strcmp(nm, "$asin") == 0 ||
|
||||
strcmp(nm, "$acos") == 0 ||
|
||||
strcmp(nm, "$atan") == 0 ||
|
||||
strcmp(nm, "$sinh") == 0 ||
|
||||
strcmp(nm, "$cosh") == 0 ||
|
||||
strcmp(nm, "$tanh") == 0 ||
|
||||
strcmp(nm, "$asinh") == 0 ||
|
||||
strcmp(nm, "$acosh") == 0 ||
|
||||
strcmp(nm, "$atanh") == 0) {
|
||||
if (nparms() != 1 || parm(0) == 0) {
|
||||
cerr << get_fileline() << ": error: " << nm
|
||||
<< " takes a single argument." << endl;
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(nm, "$clog2") == 0) {
|
||||
rtn = evaluate_clog2(parm(0));
|
||||
} else {
|
||||
rtn = evaluate_math_one_arg(parm(0), nm);
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(nm, "$pow") == 0 ||
|
||||
strcmp(nm, "$atan2") == 0 ||
|
||||
strcmp(nm, "$hypot") == 0) {
|
||||
if (nparms() != 2 || parm(0) == 0 || parm(1) == 0) {
|
||||
cerr << get_fileline() << ": error: " << nm
|
||||
<< " takes two arguments." << endl;
|
||||
return 0;
|
||||
}
|
||||
rtn = evaluate_math_two_args(parm(0), parm(1), nm);
|
||||
}
|
||||
|
||||
if ((gn_icarus_misc_flag || gn_verilog_ams_flag) &&
|
||||
(strcmp(nm, "$log") == 0 || strcmp(nm, "$abs") == 0)) {
|
||||
if (nparms() != 1 || parm(0) == 0) {
|
||||
cerr << get_fileline() << ": error: " << nm
|
||||
<< " takes a single argument." << endl;
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(nm, "$log") == 0) {
|
||||
rtn = evaluate_math_one_arg(parm(0), nm);
|
||||
} else {
|
||||
rtn = evaluate_abs(parm(0));
|
||||
}
|
||||
}
|
||||
|
||||
if ((gn_icarus_misc_flag || gn_verilog_ams_flag) &&
|
||||
(strcmp(nm, "$min") == 0 || strcmp(nm, "$max") == 0)) {
|
||||
if (nparms() != 2 || parm(0) == 0 || parm(1) == 0) {
|
||||
cerr << get_fileline() << ": error: " << nm
|
||||
<< " takes two arguments." << endl;
|
||||
return 0;
|
||||
}
|
||||
rtn = evaluate_min_max(parm(0), parm(1), nm);
|
||||
}
|
||||
|
||||
if (rtn != 0) {
|
||||
rtn->set_line(*this);
|
||||
if (debug_eval_tree) {
|
||||
cerr << get_fileline() << ": debug: Evaluate constant "
|
||||
<< nm << "." << endl;
|
||||
}
|
||||
}
|
||||
|
||||
return rtn;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -143,6 +143,7 @@ ivl_scope_def_lineno
|
||||
ivl_scope_event
|
||||
ivl_scope_events
|
||||
ivl_scope_file
|
||||
ivl_scope_is_auto
|
||||
ivl_scope_lineno
|
||||
ivl_scope_logs
|
||||
ivl_scope_log
|
||||
@@ -191,6 +192,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
|
||||
|
||||
@@ -193,6 +193,7 @@ typedef enum ivl_drive_e {
|
||||
typedef enum ivl_expr_type_e {
|
||||
IVL_EX_NONE = 0,
|
||||
IVL_EX_ARRAY = 18,
|
||||
IVL_EX_BACCESS= 19,
|
||||
IVL_EX_BINARY = 2,
|
||||
IVL_EX_CONCAT = 3,
|
||||
IVL_EX_EVENT = 17,
|
||||
@@ -254,6 +255,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,
|
||||
@@ -1445,6 +1448,9 @@ extern unsigned ivl_parameter_lineno(ivl_parameter_t net);
|
||||
* ivl_scope_lineno
|
||||
* Returns the instantiation file and line for this scope.
|
||||
*
|
||||
* ivl_scope_is_auto
|
||||
* Is the task or function declared to be automatic?
|
||||
*
|
||||
* ivl_scope_var
|
||||
* ivl_scope_vars
|
||||
* REMOVED
|
||||
@@ -1521,6 +1527,7 @@ extern unsigned ivl_scope_def_lineno(ivl_scope_t net);
|
||||
extern unsigned ivl_scope_events(ivl_scope_t net);
|
||||
extern ivl_event_t ivl_scope_event(ivl_scope_t net, unsigned idx);
|
||||
extern const char* ivl_scope_file(ivl_scope_t net);
|
||||
extern unsigned ivl_scope_is_auto(ivl_scope_t net);
|
||||
extern unsigned ivl_scope_lineno(ivl_scope_t net);
|
||||
extern unsigned ivl_scope_logs(ivl_scope_t net);
|
||||
extern ivl_net_logic_t ivl_scope_log(ivl_scope_t net, unsigned idx);
|
||||
@@ -1693,6 +1700,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
|
||||
|
||||
+3
-3
@@ -63,13 +63,13 @@ lexor.c: lexor.lex
|
||||
install: all installdirs $(libdir)/ivl/ivlpp@EXEEXT@
|
||||
|
||||
$(libdir)/ivl/ivlpp@EXEEXT@: ivlpp@EXEEXT@
|
||||
$(INSTALL_PROGRAM) ./ivlpp@EXEEXT@ $(libdir)/ivl/ivlpp@EXEEXT@
|
||||
$(INSTALL_PROGRAM) ./ivlpp@EXEEXT@ $(DESTDIR)$(libdir)/ivl/ivlpp@EXEEXT@
|
||||
|
||||
installdirs: ../mkinstalldirs
|
||||
$(srcdir)/../mkinstalldirs $(libdir)/ivl
|
||||
$(srcdir)/../mkinstalldirs $(DESTDIR)$(libdir)/ivl
|
||||
|
||||
uninstall:
|
||||
rm -f $(libdir)/ivl/ivlpp@EXEEXT@
|
||||
rm -f $(DESTDIR)$(libdir)/ivl/ivlpp@EXEEXT@
|
||||
|
||||
lexor.o: lexor.c globals.h
|
||||
main.o: main.c globals.h
|
||||
|
||||
+16
-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;
|
||||
@@ -1584,6 +1588,12 @@ static int load_next_input()
|
||||
if(depend_file)
|
||||
fprintf(depend_file, "%s\n", istack->path);
|
||||
|
||||
/* This works around an issue in flex yyrestart() where it
|
||||
* uses yyin to create a new buffer when one does not exist.
|
||||
* I would have assumed that it would use the file argument.
|
||||
* The problem is that we have deleted the buffer and freed
|
||||
* yyin (isp->file) above. */
|
||||
yyin = 0;
|
||||
yyrestart(istack->file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -89,12 +89,12 @@ distclean: clean
|
||||
install:: all installdirs $(libdir)/libveriuser.a $(INSTALL32)
|
||||
|
||||
$(libdir)/libveriuser.a: ./libveriuser.a
|
||||
$(INSTALL_DATA) ./libveriuser.a $(libdir)/libveriuser.a
|
||||
$(INSTALL_DATA) ./libveriuser.a $(DESTDIR)$(libdir)/libveriuser.a
|
||||
|
||||
installdirs: mkinstalldirs
|
||||
$(srcdir)/mkinstalldirs $(includedir) $(libdir)
|
||||
$(srcdir)/mkinstalldirs $(DESTDIR)$(includedir) $(DESTDIR)$(libdir)
|
||||
|
||||
uninstall::
|
||||
rm -f $(libdir)/libveriuser.a
|
||||
rm -f $(DESTDIR)$(libdir)/libveriuser.a
|
||||
|
||||
-include $(patsubst %.o, dep/%.d, $O)
|
||||
|
||||
+3
-2
@@ -60,8 +60,9 @@ bool Nexus::drivers_constant() const
|
||||
|
||||
if (cur_dir == Link::PASSIVE) {
|
||||
|
||||
const NetObj*obj = cur->get_obj();
|
||||
if (obj->scope()->parent() != 0)
|
||||
const NetPins*obj = cur->get_obj();
|
||||
const NetObj*as_obj = dynamic_cast<const NetObj*>(obj);
|
||||
if (as_obj == 0 || as_obj->scope()->parent() != 0)
|
||||
continue;
|
||||
|
||||
sig = dynamic_cast<const NetNet*>(cur->get_obj());
|
||||
|
||||
@@ -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);
|
||||
|
||||
+1
-1
@@ -299,7 +299,7 @@ void NetEvProbe::find_similar_probes(list<NetEvProbe*>&plist)
|
||||
Nexus*nex = pin(0).nexus();
|
||||
|
||||
for (Link*lcur = nex->first_nlink(); lcur; lcur = lcur->next_nlink()) {
|
||||
NetObj*obj = lcur->get_obj();
|
||||
NetPins*obj = lcur->get_obj();
|
||||
if (obj->pin_count() != pin_count())
|
||||
continue;
|
||||
|
||||
|
||||
+14
@@ -588,3 +588,17 @@ ivl_variable_type_t NetESFunc::expr_type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
NetEAccess::NetEAccess(NetBranch*br, nature_t*nat)
|
||||
: branch_(br), nature_(nat)
|
||||
{
|
||||
}
|
||||
|
||||
NetEAccess::~NetEAccess()
|
||||
{
|
||||
}
|
||||
|
||||
ivl_variable_type_t NetEAccess::expr_type() const
|
||||
{
|
||||
return IVL_VT_REAL;
|
||||
}
|
||||
|
||||
+1
-1
@@ -104,7 +104,7 @@ bool PECallFunction::check_call_matches_definition_(Design*des, NetScope*dscope)
|
||||
1 nil pointer. This is how the parser tells me of no
|
||||
parameter. In other words, ``func()'' is 1 nil parameter. */
|
||||
|
||||
unsigned parms_count = parms_.count();
|
||||
unsigned parms_count = parms_.size();
|
||||
if ((parms_count == 1) && (parms_[0] == 0))
|
||||
parms_count = 0;
|
||||
|
||||
|
||||
+44
-6
@@ -134,13 +134,13 @@ verinum::V Link::get_init() const
|
||||
}
|
||||
|
||||
|
||||
void Link::cur_link(NetObj*&net, unsigned &pin)
|
||||
void Link::cur_link(NetPins*&net, unsigned &pin)
|
||||
{
|
||||
net = node_;
|
||||
pin = pin_;
|
||||
}
|
||||
|
||||
void Link::cur_link(const NetObj*&net, unsigned &pin) const
|
||||
void Link::cur_link(const NetPins*&net, unsigned &pin) const
|
||||
{
|
||||
net = node_;
|
||||
pin = pin_;
|
||||
@@ -186,12 +186,12 @@ const Link* Link::next_nlink() const
|
||||
return next_;
|
||||
}
|
||||
|
||||
const NetObj*Link::get_obj() const
|
||||
const NetPins*Link::get_obj() const
|
||||
{
|
||||
return node_;
|
||||
}
|
||||
|
||||
NetObj*Link::get_obj()
|
||||
NetPins*Link::get_obj()
|
||||
{
|
||||
return node_;
|
||||
}
|
||||
@@ -247,13 +247,51 @@ 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 NetPins*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_) {
|
||||
if (cur->get_dir() != Link::OUTPUT)
|
||||
continue;
|
||||
|
||||
NetObj*obj = cur->get_obj();
|
||||
NetObj*obj = dynamic_cast<NetObj*>(cur->get_obj());
|
||||
if (obj == 0)
|
||||
continue;
|
||||
|
||||
obj->rise_time(rise);
|
||||
obj->fall_time(fall);
|
||||
obj->decay_time(decay);
|
||||
@@ -397,7 +435,7 @@ const char* Nexus::name() const
|
||||
|
||||
if (sig == 0) {
|
||||
const Link*lnk = first_nlink();
|
||||
const NetObj*obj = lnk->get_obj();
|
||||
const NetObj*obj = dynamic_cast<const NetObj*>(lnk->get_obj());
|
||||
pin = lnk->get_pin();
|
||||
cerr << "internal error: No signal for nexus of " <<
|
||||
obj->name() << " pin " << pin << "(" <<
|
||||
|
||||
+15
-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;
|
||||
@@ -62,6 +67,11 @@ NexusSet* NetEConcat::nex_input(bool rem_out)
|
||||
return result;
|
||||
}
|
||||
|
||||
NexusSet* NetEAccess::nex_input(bool rem_out)
|
||||
{
|
||||
return new NexusSet;
|
||||
}
|
||||
|
||||
/*
|
||||
* A constant has not inputs, so always return an empty set.
|
||||
*/
|
||||
@@ -98,6 +108,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 +395,3 @@ NexusSet* NetWhile::nex_input(bool rem_out)
|
||||
delete tmp;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -41,6 +41,7 @@ NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t)
|
||||
signals_ = 0;
|
||||
events_ = 0;
|
||||
lcounter_ = 0;
|
||||
is_auto_ = false;
|
||||
|
||||
if (up) {
|
||||
default_nettype_ = up->default_nettype();
|
||||
@@ -166,7 +167,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)
|
||||
@@ -261,6 +262,7 @@ NetFuncDef* NetScope::func_def()
|
||||
assert( type_ == FUNC );
|
||||
return func_;
|
||||
}
|
||||
|
||||
bool NetScope::in_func()
|
||||
{
|
||||
return (type_ == FUNC) ? true : false;
|
||||
@@ -275,7 +277,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
|
||||
|
||||
+9
-5
@@ -100,8 +100,12 @@ void join_island(NetObj*obj)
|
||||
Nexus*nex = obj->pin(idx).nexus();
|
||||
for (Link*cur = nex->first_nlink() ; cur ; cur = cur->next_nlink()) {
|
||||
unsigned pin;
|
||||
NetObj*tmp;
|
||||
cur->cur_link(tmp, pin);
|
||||
NetPins*tmp_pins;
|
||||
cur->cur_link(tmp_pins, pin);
|
||||
|
||||
NetObj*tmp = dynamic_cast<NetObj*> (tmp_pins);
|
||||
if (tmp == 0)
|
||||
continue;
|
||||
|
||||
// Skip self.
|
||||
if (tmp == obj)
|
||||
@@ -114,7 +118,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 +132,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 +143,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;
|
||||
|
||||
+83
-23
@@ -91,7 +91,7 @@ unsigned count_inputs(const Link&pin)
|
||||
const Nexus*nex = pin.nexus();
|
||||
for (const Link*clnk = nex->first_nlink()
|
||||
; clnk ; clnk = clnk->next_nlink()) {
|
||||
const NetObj*cur;
|
||||
const NetPins*cur;
|
||||
unsigned cpin;
|
||||
clnk->cur_link(cur, cpin);
|
||||
if (cur->pin(cpin).get_dir() == Link::INPUT)
|
||||
@@ -108,7 +108,7 @@ unsigned count_outputs(const Link&pin)
|
||||
const Nexus*nex = pin.nexus();
|
||||
for (const Link*clnk = nex->first_nlink()
|
||||
; clnk ; clnk = clnk->next_nlink()) {
|
||||
const NetObj*cur;
|
||||
const NetPins*cur;
|
||||
unsigned cpin;
|
||||
clnk->cur_link(cur, cpin);
|
||||
if (cur->pin(cpin).get_dir() == Link::OUTPUT)
|
||||
@@ -125,7 +125,7 @@ unsigned count_signals(const Link&pin)
|
||||
const Nexus*nex = pin.nexus();
|
||||
for (const Link*clnk = nex->first_nlink()
|
||||
; clnk ; clnk = clnk->next_nlink()) {
|
||||
const NetObj*cur;
|
||||
const NetPins*cur;
|
||||
unsigned cpin;
|
||||
clnk->cur_link(cur, cpin);
|
||||
if (dynamic_cast<const NetNet*>(cur))
|
||||
@@ -142,7 +142,7 @@ const NetNet* find_link_signal(const NetObj*net, unsigned pin, unsigned&bidx)
|
||||
for (const Link*clnk = nex->first_nlink()
|
||||
; clnk ; clnk = clnk->next_nlink()) {
|
||||
|
||||
const NetObj*cur;
|
||||
const NetPins*cur;
|
||||
unsigned cpin;
|
||||
clnk->cur_link(cur, cpin);
|
||||
|
||||
@@ -171,8 +171,8 @@ Link* find_next_output(Link*lnk)
|
||||
return 0;
|
||||
}
|
||||
|
||||
NetObj::NetObj(NetScope*s, perm_string n, unsigned np)
|
||||
: scope_(s), name_(n), npins_(np), delay1_(0), delay2_(0), delay3_(0)
|
||||
NetPins::NetPins(unsigned npins)
|
||||
: npins_(npins)
|
||||
{
|
||||
pins_ = new Link[npins_];
|
||||
for (unsigned idx = 0 ; idx < npins_ ; idx += 1) {
|
||||
@@ -181,22 +181,12 @@ NetObj::NetObj(NetScope*s, perm_string n, unsigned np)
|
||||
}
|
||||
}
|
||||
|
||||
NetObj::~NetObj()
|
||||
NetPins::~NetPins()
|
||||
{
|
||||
delete[]pins_;
|
||||
}
|
||||
|
||||
NetScope* NetObj::scope()
|
||||
{
|
||||
return scope_;
|
||||
}
|
||||
|
||||
const NetScope* NetObj::scope() const
|
||||
{
|
||||
return scope_;
|
||||
}
|
||||
|
||||
Link& NetObj::pin(unsigned idx)
|
||||
Link& NetPins::pin(unsigned idx)
|
||||
{
|
||||
if (idx >= npins_) {
|
||||
cerr << get_fileline() << ": internal error: pin("<<idx<<")"
|
||||
@@ -209,12 +199,31 @@ Link& NetObj::pin(unsigned idx)
|
||||
return pins_[idx];
|
||||
}
|
||||
|
||||
const Link& NetObj::pin(unsigned idx) const
|
||||
const Link& NetPins::pin(unsigned idx) const
|
||||
{
|
||||
assert(idx < npins_);
|
||||
return pins_[idx];
|
||||
}
|
||||
|
||||
NetObj::NetObj(NetScope*s, perm_string n, unsigned np)
|
||||
: NetPins(np), scope_(s), name_(n), delay1_(0), delay2_(0), delay3_(0)
|
||||
{
|
||||
}
|
||||
|
||||
NetObj::~NetObj()
|
||||
{
|
||||
}
|
||||
|
||||
NetScope* NetObj::scope()
|
||||
{
|
||||
return scope_;
|
||||
}
|
||||
|
||||
const NetScope* NetObj::scope() const
|
||||
{
|
||||
return scope_;
|
||||
}
|
||||
|
||||
NetNode::NetNode(NetScope*s, perm_string n, unsigned npins)
|
||||
: NetObj(s, n, npins), node_next_(0), node_prev_(0), design_(0)
|
||||
{
|
||||
@@ -226,6 +235,19 @@ NetNode::~NetNode()
|
||||
design_->del_node(this);
|
||||
}
|
||||
|
||||
NetBranch::NetBranch(discipline_t*dis)
|
||||
: NetPins(2), discipline_(dis)
|
||||
{
|
||||
pin(0).set_name(perm_string::literal("A"), 0);
|
||||
pin(0).set_dir(Link::PASSIVE);
|
||||
pin(1).set_name(perm_string::literal("B"), 0);
|
||||
pin(1).set_dir(Link::PASSIVE);
|
||||
}
|
||||
|
||||
NetBranch::~NetBranch()
|
||||
{
|
||||
}
|
||||
|
||||
NetBus::NetBus(NetScope*s, unsigned pin_count)
|
||||
: NetObj(s, perm_string::literal(""), pin_count)
|
||||
{
|
||||
@@ -404,7 +426,8 @@ const Link& NetDelaySrc::condit_pin() const
|
||||
NetNet::NetNet(NetScope*s, perm_string n, Type t, unsigned npins)
|
||||
: NetObj(s, n, 1), sig_next_(0), sig_prev_(0),
|
||||
type_(t), port_type_(NOT_A_PORT), data_type_(IVL_VT_NO_TYPE),
|
||||
signed_(false), isint_(false), msb_(npins-1), lsb_(0), dimensions_(0),
|
||||
signed_(false), isint_(false), discipline_(0), msb_(npins-1), lsb_(0),
|
||||
dimensions_(0),
|
||||
s0_(0), e0_(0), local_flag_(false), eref_count_(0), lref_count_(0)
|
||||
{
|
||||
assert(s);
|
||||
@@ -444,7 +467,7 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t,
|
||||
: NetObj(s, n, 1),
|
||||
sig_next_(0), sig_prev_(0), type_(t),
|
||||
port_type_(NOT_A_PORT), data_type_(IVL_VT_NO_TYPE), signed_(false),
|
||||
isint_(false), msb_(ms), lsb_(ls), dimensions_(0), s0_(0), e0_(0),
|
||||
isint_(false), discipline_(0), msb_(ms), lsb_(ls), dimensions_(0), s0_(0), e0_(0),
|
||||
local_flag_(false), eref_count_(0), lref_count_(0)
|
||||
{
|
||||
assert(s);
|
||||
@@ -492,7 +515,7 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t,
|
||||
: NetObj(s, n, calculate_count(array_s, array_e)),
|
||||
sig_next_(0), sig_prev_(0), type_(t), port_type_(NOT_A_PORT),
|
||||
data_type_(IVL_VT_NO_TYPE), signed_(false), isint_(false),
|
||||
msb_(ms), lsb_(ls), dimensions_(1), s0_(array_s), e0_(array_e),
|
||||
discipline_(0), msb_(ms), lsb_(ls), dimensions_(1), s0_(array_s), e0_(array_e),
|
||||
local_flag_(false), eref_count_(0), lref_count_(0)
|
||||
{
|
||||
assert(s);
|
||||
@@ -622,6 +645,17 @@ void NetNet::set_isint(bool flag)
|
||||
isint_ = flag;
|
||||
}
|
||||
|
||||
discipline_t* NetNet::get_discipline() const
|
||||
{
|
||||
return discipline_;
|
||||
}
|
||||
|
||||
void NetNet::set_discipline(discipline_t*dis)
|
||||
{
|
||||
ivl_assert(*this, discipline_ == 0);
|
||||
discipline_ = dis;
|
||||
}
|
||||
|
||||
long NetNet::lsb() const
|
||||
{
|
||||
return lsb_;
|
||||
@@ -849,6 +883,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 +1548,11 @@ NetLiteral::~NetLiteral()
|
||||
{
|
||||
}
|
||||
|
||||
ivl_variable_type_t NetLiteral::data_type() const
|
||||
{
|
||||
return IVL_VT_REAL;
|
||||
}
|
||||
|
||||
const verireal& NetLiteral::value_real() const
|
||||
{
|
||||
return real_;
|
||||
@@ -2552,6 +2609,7 @@ DelayType NetBlock::delay_type() const
|
||||
for (const NetProc*cur = proc_first(); cur; cur = proc_next(cur)) {
|
||||
DelayType dt = cur->delay_type();
|
||||
if (dt > result) result = dt;
|
||||
if (dt == DEFINITE_DELAY) break;
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -2586,7 +2644,9 @@ DelayType NetCondit::delay_type() const
|
||||
if (else_) {
|
||||
result = combine_delays(if_->delay_type(), else_->delay_type());
|
||||
} else {
|
||||
result = if_->delay_type();
|
||||
/* Because of the indeterminate conditional value the
|
||||
* best we can have for this case is a possible delay. */
|
||||
result = combine_delays(if_->delay_type(), NO_DELAY);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -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"
|
||||
@@ -66,6 +68,8 @@ class NetTaskDef;
|
||||
class NetEvTrig;
|
||||
class NetEvWait;
|
||||
|
||||
class nature_t;
|
||||
class discipline_t;
|
||||
|
||||
struct target;
|
||||
struct functor_t;
|
||||
@@ -74,6 +78,24 @@ ostream& operator << (ostream&o, ivl_variable_type_t val);
|
||||
|
||||
extern void join_island(NetObj*obj);
|
||||
|
||||
class NetPins : public LineInfo {
|
||||
|
||||
public:
|
||||
explicit NetPins(unsigned npins);
|
||||
virtual ~NetPins();
|
||||
|
||||
unsigned pin_count() const { return npins_; }
|
||||
|
||||
Link&pin(unsigned idx);
|
||||
const Link&pin(unsigned idx) const;
|
||||
|
||||
void dump_node_pins(ostream&, unsigned) const;
|
||||
|
||||
private:
|
||||
Link*pins_;
|
||||
const unsigned npins_;
|
||||
};
|
||||
|
||||
/* =========
|
||||
* A NetObj is anything that has any kind of behavior in the
|
||||
* netlist. Nodes can be gates, registers, etc. and are linked
|
||||
@@ -94,7 +116,7 @@ extern void join_island(NetObj*obj);
|
||||
* interpretation of the rise/fall/decay times is typically left to
|
||||
* the target to properly interpret.
|
||||
*/
|
||||
class NetObj : public Attrib, public virtual LineInfo {
|
||||
class NetObj : public NetPins, public Attrib {
|
||||
|
||||
public:
|
||||
public:
|
||||
@@ -108,8 +130,6 @@ class NetObj : public Attrib, public virtual LineInfo {
|
||||
|
||||
perm_string name() const { return name_; }
|
||||
|
||||
unsigned pin_count() const { return npins_; }
|
||||
|
||||
const NetExpr* rise_time() const { return delay1_; }
|
||||
const NetExpr* fall_time() const { return delay2_; }
|
||||
const NetExpr* decay_time() const { return delay3_; }
|
||||
@@ -118,17 +138,11 @@ class NetObj : public Attrib, public virtual LineInfo {
|
||||
void fall_time(const NetExpr* d) { delay2_ = d; }
|
||||
void decay_time(const NetExpr* d) { delay3_ = d; }
|
||||
|
||||
Link&pin(unsigned idx);
|
||||
const Link&pin(unsigned idx) const;
|
||||
|
||||
void dump_node_pins(ostream&, unsigned) const;
|
||||
void dump_obj_attr(ostream&, unsigned) const;
|
||||
|
||||
private:
|
||||
NetScope*scope_;
|
||||
perm_string name_;
|
||||
Link*pins_;
|
||||
const unsigned npins_;
|
||||
const NetExpr* delay1_;
|
||||
const NetExpr* delay2_;
|
||||
const NetExpr* delay3_;
|
||||
@@ -148,11 +162,31 @@ class IslandBranch {
|
||||
struct ivl_island_s* island;
|
||||
};
|
||||
|
||||
/*
|
||||
* A NetBranch is a construct of Verilog-A that is a branch between
|
||||
* two nodes. The branch has exactly 2 pins and a discipline.
|
||||
*
|
||||
* pin(0) is the source of flow through a branch and the plus side of
|
||||
* potential. Pin(1) is the sink of flow and the minus (or ground) of
|
||||
* potential.
|
||||
*/
|
||||
class NetBranch : public NetPins {
|
||||
|
||||
public:
|
||||
explicit NetBranch(discipline_t*dis);
|
||||
explicit NetBranch(discipline_t*dis, perm_string name);
|
||||
~NetBranch();
|
||||
|
||||
private:
|
||||
discipline_t*discipline_;
|
||||
perm_string name_;
|
||||
};
|
||||
|
||||
class Link {
|
||||
|
||||
friend void connect(Link&, Link&);
|
||||
friend void connect(Nexus*, Link&);
|
||||
friend class NetObj;
|
||||
friend class NetPins;
|
||||
friend class Nexus;
|
||||
|
||||
public:
|
||||
@@ -186,8 +220,8 @@ class Link {
|
||||
void set_init(verinum::V val);
|
||||
verinum::V get_init() const;
|
||||
|
||||
void cur_link(NetObj*&net, unsigned &pin);
|
||||
void cur_link(const NetObj*&net, unsigned &pin) const;
|
||||
void cur_link(NetPins*&net, unsigned &pin);
|
||||
void cur_link(const NetPins*&net, unsigned &pin) const;
|
||||
|
||||
// Get a pointer to the nexus that represents all the links
|
||||
// connected to me.
|
||||
@@ -214,8 +248,8 @@ class Link {
|
||||
|
||||
// Return information about the object that this link is
|
||||
// a part of.
|
||||
const NetObj*get_obj() const;
|
||||
NetObj*get_obj();
|
||||
const NetPins*get_obj() const;
|
||||
NetPins*get_obj();
|
||||
unsigned get_pin() const;
|
||||
|
||||
// A link of an object (sometimes called a "pin") has a
|
||||
@@ -228,7 +262,7 @@ class Link {
|
||||
private:
|
||||
// The NetNode manages these. They point back to the
|
||||
// NetNode so that following the links can get me here.
|
||||
NetObj *node_;
|
||||
NetPins *node_;
|
||||
unsigned pin_;
|
||||
|
||||
DIR dir_;
|
||||
@@ -288,6 +322,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. */
|
||||
@@ -518,6 +556,10 @@ class NetNet : public NetObj {
|
||||
bool get_isint() const;
|
||||
void set_isint(bool);
|
||||
|
||||
/* Attach a discipline to the net. */
|
||||
discipline_t* get_discipline() const;
|
||||
void set_discipline(discipline_t*dis);
|
||||
|
||||
/* These methods return the msb and lsb indices for the most
|
||||
significant and least significant bits. These are signed
|
||||
longs, and may be different from pin numbers. For example,
|
||||
@@ -585,6 +627,7 @@ class NetNet : public NetObj {
|
||||
ivl_variable_type_t data_type_;
|
||||
bool signed_;
|
||||
bool isint_; // original type of integer
|
||||
discipline_t*discipline_;
|
||||
|
||||
long msb_, lsb_;
|
||||
const unsigned dimensions_;
|
||||
@@ -686,6 +729,9 @@ class NetScope : public Attrib {
|
||||
unsigned get_def_lineno() const { return def_lineno_; };
|
||||
|
||||
bool in_func();
|
||||
/* Is the task or function automatic. */
|
||||
void is_auto(bool is_auto) { is_auto_ = is_auto; };
|
||||
bool is_auto() const { return is_auto_; };
|
||||
|
||||
const NetTaskDef* task_def() const;
|
||||
const NetFuncDef* func_def() const;
|
||||
@@ -718,8 +764,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 +788,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 {
|
||||
@@ -823,11 +875,12 @@ class NetScope : public Attrib {
|
||||
NetScope*sub_;
|
||||
|
||||
unsigned lcounter_;
|
||||
bool is_auto_;
|
||||
};
|
||||
|
||||
/*
|
||||
* 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 +969,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 +1470,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,
|
||||
@@ -2827,6 +2918,27 @@ class NetEUFunc : public NetExpr {
|
||||
NetEUFunc& operator= (const NetEUFunc&);
|
||||
};
|
||||
|
||||
/*
|
||||
* A call to a nature access function for a branch.
|
||||
*/
|
||||
class NetEAccess : public NetExpr {
|
||||
|
||||
public:
|
||||
explicit NetEAccess(NetBranch*br, nature_t*nat);
|
||||
~NetEAccess();
|
||||
|
||||
virtual ivl_variable_type_t expr_type() const;
|
||||
virtual void dump(ostream&) const;
|
||||
|
||||
virtual void expr_scan(struct expr_scan_t*) const;
|
||||
virtual NetEAccess*dup_expr() const;
|
||||
virtual NexusSet* nex_input(bool rem_out = true);
|
||||
|
||||
private:
|
||||
NetBranch*branch_;
|
||||
nature_t*nature_;
|
||||
};
|
||||
|
||||
/*
|
||||
* A call to a user defined task is elaborated into this object. This
|
||||
* contains a pointer to the elaborated task definition, but is a
|
||||
@@ -3094,6 +3206,7 @@ class NetEBComp : public NetEBinary {
|
||||
NetEConst* must_be_leeq_(NetExpr*le, const verinum&rv, bool eq_flag);
|
||||
|
||||
NetEConst*eval_eqeq_(bool ne_flag);
|
||||
NetEConst*eval_eqeq_real_(NetExpr*le, NetExpr*ri, bool ne_flag);
|
||||
NetEConst*eval_less_();
|
||||
NetEConst*eval_leeq_();
|
||||
NetEConst*eval_leeq_real_(NetExpr*le, NetExpr*ri, bool eq_flag);
|
||||
@@ -3392,6 +3505,8 @@ class NetESFunc : public NetExpr {
|
||||
NetExpr* parm(unsigned idx);
|
||||
const NetExpr* parm(unsigned idx) const;
|
||||
|
||||
virtual NetExpr* eval_tree(int prune_to_width = -1);
|
||||
|
||||
virtual ivl_variable_type_t expr_type() const;
|
||||
virtual NexusSet* nex_input(bool rem_out = true);
|
||||
virtual bool set_width(unsigned, bool last_chance);
|
||||
@@ -3560,6 +3675,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 +3736,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
|
||||
@@ -3634,6 +3774,7 @@ class Design {
|
||||
// PROCESSES
|
||||
void add_process(NetProcTop*);
|
||||
void delete_process(NetProcTop*);
|
||||
bool check_always_delay() const;
|
||||
|
||||
// Iterate over the design...
|
||||
void dump(ostream&) const;
|
||||
|
||||
+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 is 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 evaluate the index
|
||||
* expression if it is present.
|
||||
*/
|
||||
hname_t eval_path_component(Design*des, NetScope*scope,
|
||||
const name_component_t&comp)
|
||||
{
|
||||
// No index expression, 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
|
||||
// an 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);
|
||||
|
||||
|
||||
@@ -180,6 +180,7 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2)
|
||||
PEventStatement*event_statement;
|
||||
Statement*statement;
|
||||
svector<Statement*>*statement_list;
|
||||
AStatement*astatement;
|
||||
|
||||
PTaskFuncArg function_type;
|
||||
|
||||
@@ -205,7 +206,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
|
||||
@@ -239,7 +240,7 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2)
|
||||
|
||||
%type <flag> from_exclude
|
||||
%type <number> number
|
||||
%type <flag> signed_opt udp_reg_opt edge_operator
|
||||
%type <flag> signed_opt udp_reg_opt edge_operator automatic_opt
|
||||
%type <drive> drive_strength drive_strength_opt dr_strength0 dr_strength1
|
||||
%type <letter> udp_input_sym udp_output_sym
|
||||
%type <text> udp_input_list udp_sequ_entry udp_comb_entry
|
||||
@@ -282,6 +283,7 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2)
|
||||
%type <pform_name> hierarchy_identifier
|
||||
%type <expr> expression expr_primary expr_mintypmax
|
||||
%type <expr> lpvalue
|
||||
%type <expr> branch_probe_expression
|
||||
%type <expr> delay_value delay_value_simple
|
||||
%type <exprs> delay1 delay3 delay3_opt delay_value_list
|
||||
%type <exprs> expression_list_with_nuls expression_list_proper
|
||||
@@ -302,6 +304,8 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2)
|
||||
%type <statement> statement statement_or_null
|
||||
%type <statement_list> statement_list
|
||||
|
||||
%type <astatement> analog_statement
|
||||
|
||||
%type <letter> spec_polarity
|
||||
%type <perm_strings> specify_path_identifiers
|
||||
|
||||
@@ -351,6 +355,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 +443,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 +456,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 ';'
|
||||
@@ -851,7 +862,9 @@ event_expression
|
||||
function name really is a nature attribute identifier. */
|
||||
branch_probe_expression
|
||||
: IDENTIFIER '(' IDENTIFIER ',' IDENTIFIER ')'
|
||||
{ $$ = pform_make_branch_probe_expression(@1, $1, $3, $5); }
|
||||
| IDENTIFIER '(' IDENTIFIER ')'
|
||||
{ $$ = pform_make_branch_probe_expression(@1, $1, $3); }
|
||||
;
|
||||
|
||||
expression
|
||||
@@ -1186,7 +1199,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");
|
||||
@@ -1495,6 +1508,32 @@ gate_instance
|
||||
delete rng;
|
||||
$$ = tmp;
|
||||
}
|
||||
|
||||
| IDENTIFIER '(' error ')'
|
||||
{ lgate*tmp = new lgate;
|
||||
tmp->name = $1;
|
||||
tmp->parms = 0;
|
||||
tmp->parms_by_name = 0;
|
||||
tmp->file = @1.text;
|
||||
tmp->lineno = @1.first_line;
|
||||
yyerror(@2, "error: Syntax error in instance port "
|
||||
"expression(s).");
|
||||
delete[]$1;
|
||||
$$ = tmp;
|
||||
}
|
||||
|
||||
| IDENTIFIER range '(' error ')'
|
||||
{ lgate*tmp = new lgate;
|
||||
tmp->name = $1;
|
||||
tmp->parms = 0;
|
||||
tmp->parms_by_name = 0;
|
||||
tmp->file = @1.text;
|
||||
tmp->lineno = @1.first_line;
|
||||
yyerror(@3, "error: Syntax error in instance port "
|
||||
"expression(s).");
|
||||
delete[]$1;
|
||||
$$ = tmp;
|
||||
}
|
||||
;
|
||||
|
||||
gate_instance_list
|
||||
@@ -2078,6 +2117,7 @@ module_item
|
||||
}
|
||||
|
||||
| attribute_list_opt K_analog analog_statement
|
||||
{ pform_make_analog_behavior(@2, AProcess::PR_ALWAYS, $3); }
|
||||
|
||||
/* The task declaration rule matches the task declaration
|
||||
header, then pushes the function scope. This causes the
|
||||
@@ -2087,41 +2127,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, $2);
|
||||
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, $2);
|
||||
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 +2169,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, $2);
|
||||
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, $2);
|
||||
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 +2240,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 +2311,11 @@ module_item
|
||||
{ yyerror(@1, "error: Malformed $attribute parameter list."); }
|
||||
;
|
||||
|
||||
automatic_opt
|
||||
: K_automatic { $$ = true; }
|
||||
| { $$ = false;}
|
||||
;
|
||||
|
||||
generate_if : K_if '(' expression ')' { pform_start_generate_if(@1, $3); }
|
||||
|
||||
generate_case_items
|
||||
@@ -2384,7 +2438,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 +2454,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 +2554,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 +2567,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 +2583,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;
|
||||
@@ -3647,7 +3749,7 @@ statement_or_null
|
||||
|
||||
analog_statement
|
||||
: branch_probe_expression K_CONTRIBUTE expression ';'
|
||||
{ yyerror(@1, "sorry: Analog contribution statements not supported."); }
|
||||
{ $$ = pform_contribution_statement(@2, $1, $3); }
|
||||
;
|
||||
|
||||
/* Task items are, other than the statement, task port items and
|
||||
@@ -3657,39 +3759,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 +3810,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 +3825,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 +3840,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 +3895,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 +3903,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 +3933,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 +3998,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 +4019,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 +4040,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 +4118,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 +4131,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,66 @@ 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)
|
||||
PTask* pform_push_task_scope(char*name, bool is_auto)
|
||||
{
|
||||
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,
|
||||
is_auto);
|
||||
pform_cur_generate->tasks[task->pscope_name()] = task;
|
||||
pform_cur_generate->lexical_scope = task;
|
||||
} else {
|
||||
task = new PTask(task_name, lexical_scope, is_auto);
|
||||
pform_cur_module->tasks[task->pscope_name()] = task;
|
||||
lexical_scope = task;
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
PFunction* pform_push_function_scope(char*name)
|
||||
PFunction* pform_push_function_scope(char*name, bool is_auto)
|
||||
{
|
||||
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,
|
||||
is_auto);
|
||||
pform_cur_generate->funcs[func->pscope_name()] = func;
|
||||
pform_cur_generate->lexical_scope = func;
|
||||
} else {
|
||||
func = new PFunction(func_name, lexical_scope, is_auto);
|
||||
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 +164,45 @@ 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_put_behavior_in_scope(AProcess*pp)
|
||||
{
|
||||
if (pform_cur_generate)
|
||||
if (pform_cur_generate->lexical_scope)
|
||||
pform_cur_generate->lexical_scope->analog_behaviors.push_back(pp);
|
||||
else
|
||||
pform_cur_generate->analog_behaviors.push_back(pp);
|
||||
else
|
||||
lexical_scope->analog_behaviors.push_back(pp);
|
||||
}
|
||||
|
||||
void pform_set_default_nettype(NetNet::Type type,
|
||||
@@ -508,7 +570,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 +1298,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 +1312,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 +1333,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 +1367,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 +1450,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 +1527,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 +1613,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 +1680,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 +1773,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 +1863,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 +1897,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 +1959,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
# include "named.h"
|
||||
# include "Module.h"
|
||||
# include "Statement.h"
|
||||
# include "AStatement.h"
|
||||
# include "PGate.h"
|
||||
# include "PExpr.h"
|
||||
# include "PTask.h"
|
||||
@@ -175,10 +176,11 @@ extern void pform_make_udp(perm_string name,
|
||||
*/
|
||||
extern void pform_pop_scope();
|
||||
|
||||
extern PTask*pform_push_task_scope(char*name);
|
||||
extern PFunction*pform_push_function_scope(char*name);
|
||||
extern PTask*pform_push_task_scope(char*name, bool is_auto);
|
||||
extern PFunction*pform_push_function_scope(char*name, bool is_auto);
|
||||
extern PBlock*pform_push_block_scope(char*name, PBlock::BL_TYPE tt);
|
||||
|
||||
extern void pform_put_behavior_in_scope(AProcess*proc);
|
||||
|
||||
extern verinum* pform_verinum_with_size(verinum*s, verinum*val,
|
||||
const char*file, unsigned lineno);
|
||||
@@ -389,4 +391,17 @@ extern void pform_attach_discipline(const struct vlltype&loc,
|
||||
extern void pform_dump(ostream&out, const nature_t*);
|
||||
extern void pform_dump(ostream&out, const discipline_t*);
|
||||
|
||||
/* ** pform_analog.cc
|
||||
*/
|
||||
extern void pform_make_analog_behavior(const struct vlltype&loc,
|
||||
AProcess::Type type, AStatement*st);
|
||||
|
||||
extern AStatement*pform_contribution_statement(const struct vlltype&loc,
|
||||
PExpr*lval, PExpr*rval);
|
||||
|
||||
extern PExpr* pform_make_branch_probe_expression(const struct vlltype&loc,
|
||||
char*name, char*n1, char*n2);
|
||||
|
||||
extern PExpr* pform_make_branch_probe_expression(const struct vlltype&loc,
|
||||
char*name, char*branch);
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
# include "config.h"
|
||||
# include "compiler.h"
|
||||
# include "pform.h"
|
||||
# include "parse_misc.h"
|
||||
# include "AStatement.h"
|
||||
|
||||
AStatement* pform_contribution_statement(const struct vlltype&loc,
|
||||
PExpr*lval, PExpr*rval)
|
||||
{
|
||||
AContrib*tmp = new AContrib(lval, rval);
|
||||
FILE_NAME(tmp, loc);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void pform_make_analog_behavior(const struct vlltype&loc, AProcess::Type pt,
|
||||
AStatement*statement)
|
||||
{
|
||||
AProcess*proc = new AProcess(pt, statement);
|
||||
FILE_NAME(proc, loc);
|
||||
|
||||
pform_put_behavior_in_scope(proc);
|
||||
}
|
||||
|
||||
PExpr* pform_make_branch_probe_expression(const struct vlltype&loc,
|
||||
char*name, char*n1, char*n2)
|
||||
{
|
||||
vector<PExpr*> parms (2);
|
||||
parms[0] = new PEIdent(lex_strings.make(n1));
|
||||
FILE_NAME(parms[0], loc);
|
||||
|
||||
parms[1] = new PEIdent(lex_strings.make(n2));
|
||||
FILE_NAME(parms[1], loc);
|
||||
|
||||
PECallFunction*res = new PECallFunction(lex_strings.make(name), parms);
|
||||
FILE_NAME(res, loc);
|
||||
return res;
|
||||
}
|
||||
|
||||
PExpr* pform_make_branch_probe_expression(const struct vlltype&loc,
|
||||
char*name, char*branch_name)
|
||||
{
|
||||
vector<PExpr*> parms (1);
|
||||
parms[0] = new PEIdent(lex_strings.make(branch_name));
|
||||
FILE_NAME(parms[0], loc);
|
||||
|
||||
PECallFunction*res = new PECallFunction(lex_strings.make(name), parms);
|
||||
FILE_NAME(res, loc);
|
||||
|
||||
return res;
|
||||
}
|
||||
+18
-1
@@ -25,6 +25,7 @@
|
||||
|
||||
map<perm_string,nature_t*> natures;
|
||||
map<perm_string,discipline_t*> disciplines;
|
||||
map<perm_string,nature_t*> access_function_nature;
|
||||
|
||||
static perm_string nature_name = perm_string::perm_string();
|
||||
static perm_string nature_access = perm_string::perm_string();
|
||||
@@ -62,9 +63,24 @@ void pform_end_nature(const struct vlltype&loc)
|
||||
}
|
||||
|
||||
nature_t*tmp = new nature_t(nature_name, nature_access);
|
||||
FILE_NAME(tmp, loc);
|
||||
|
||||
natures[nature_name] = tmp;
|
||||
|
||||
FILE_NAME(tmp, loc);
|
||||
// Make sure the access function is not used by multiple
|
||||
// different natures.
|
||||
if (nature_t*dup_access_nat = access_function_nature[nature_access]) {
|
||||
cerr << tmp->get_fileline() << ": error: "
|
||||
<< "Access function name " << nature_access
|
||||
<< " is already used by nature " << dup_access_nat->name()
|
||||
<< " declared at " << dup_access_nat->get_fileline()
|
||||
<< "." << endl;
|
||||
error_count += 1;
|
||||
}
|
||||
|
||||
// Map the access functio back to the nature so that
|
||||
// expressions that use the access function can find it.
|
||||
access_function_nature[nature_access] = tmp;
|
||||
|
||||
nature_name = perm_string::perm_string();
|
||||
nature_access = perm_string::perm_string();
|
||||
@@ -189,6 +205,7 @@ void pform_attach_discipline(const struct vlltype&loc,
|
||||
error_count += 1;
|
||||
|
||||
} else {
|
||||
cur_net->set_data_type(IVL_VT_REAL);
|
||||
cur_net->set_discipline(discipline);
|
||||
}
|
||||
}
|
||||
|
||||
+70
-9
@@ -180,9 +180,9 @@ void PECallFunction::dump(ostream &out) const
|
||||
{
|
||||
out << path_ << "(";
|
||||
|
||||
if (parms_.count() > 0) {
|
||||
if (parms_.size() > 0) {
|
||||
if (parms_[0]) parms_[0]->dump(out);
|
||||
for (unsigned idx = 1; idx < parms_.count(); ++idx) {
|
||||
for (unsigned idx = 1; idx < parms_.size(); ++idx) {
|
||||
out << ", ";
|
||||
if (parms_[idx]) parms_[idx]->dump(out);
|
||||
}
|
||||
@@ -280,6 +280,9 @@ void PEBinary::dump(ostream&out) const
|
||||
case 'N':
|
||||
out << "!==";
|
||||
break;
|
||||
case 'p':
|
||||
out << "**";
|
||||
break;
|
||||
case 'R':
|
||||
out << ">>>";
|
||||
break;
|
||||
@@ -531,6 +534,24 @@ void Statement::dump(ostream&out, unsigned ind) const
|
||||
<< " */ ;" << endl;
|
||||
}
|
||||
|
||||
void AStatement::dump(ostream&out, unsigned ind) const
|
||||
{
|
||||
/* I give up. I don't know what type this statement is,
|
||||
so just print the C++ typeid and let the user figure
|
||||
it out. */
|
||||
out << setw(ind) << "";
|
||||
out << "/* " << get_fileline() << ": " << typeid(*this).name()
|
||||
<< " */ ;" << endl;
|
||||
}
|
||||
|
||||
void AContrib::dump(ostream&out, unsigned ind) const
|
||||
{
|
||||
out << setw(ind) << "";
|
||||
out << *lval_ << " <+ " << *rval_
|
||||
<< "; /* " << get_fileline() << " */"
|
||||
<< endl;
|
||||
}
|
||||
|
||||
void PAssign::dump(ostream&out, unsigned ind) const
|
||||
{
|
||||
out << setw(ind) << "";
|
||||
@@ -715,6 +736,7 @@ void PForStatement::dump(ostream&out, unsigned ind) const
|
||||
void PFunction::dump(ostream&out, unsigned ind) const
|
||||
{
|
||||
out << setw(ind) << "" << "function ";
|
||||
if (is_auto_) cout << "automatic ";
|
||||
switch (return_type_.type) {
|
||||
case PTF_NONE:
|
||||
out << "?none? ";
|
||||
@@ -775,6 +797,9 @@ void PRepeat::dump(ostream&out, unsigned ind) const
|
||||
|
||||
void PTask::dump(ostream&out, unsigned ind) const
|
||||
{
|
||||
out << setw(ind) << "" << "task ";
|
||||
if (is_auto_) cout << "automatic ";
|
||||
out << pscope_name() << ";" << endl;
|
||||
if (ports_)
|
||||
for (unsigned idx = 0 ; idx < ports_->count() ; idx += 1) {
|
||||
out << setw(ind) << "";
|
||||
@@ -840,6 +865,32 @@ void PProcess::dump(ostream&out, unsigned ind) const
|
||||
statement_->dump(out, ind+2);
|
||||
}
|
||||
|
||||
void AProcess::dump(ostream&out, unsigned ind) const
|
||||
{
|
||||
switch (type_) {
|
||||
case AProcess::PR_INITIAL:
|
||||
out << setw(ind) << "" << "analog initial";
|
||||
break;
|
||||
case AProcess::PR_ALWAYS:
|
||||
out << setw(ind) << "" << "analog";
|
||||
break;
|
||||
}
|
||||
|
||||
out << " /* " << get_fileline() << " */" << endl;
|
||||
|
||||
for (map<perm_string,PExpr*>::const_iterator idx = attributes.begin()
|
||||
; idx != attributes.end() ; idx++ ) {
|
||||
|
||||
out << setw(ind+2) << "" << "(* " << (*idx).first;
|
||||
if ((*idx).second) {
|
||||
out << " = " << *(*idx).second;
|
||||
}
|
||||
out << " *)" << endl;
|
||||
}
|
||||
|
||||
statement_->dump(out, ind+2);
|
||||
}
|
||||
|
||||
void PSpecPath::dump(std::ostream&out, unsigned ind) const
|
||||
{
|
||||
out << setw(ind) << "" << "specify path ";
|
||||
@@ -937,8 +988,13 @@ 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<AProcess*>::const_iterator idx = analog_behaviors.begin()
|
||||
; idx != analog_behaviors.end() ; idx++) {
|
||||
(*idx)->dump(out, indent+2);
|
||||
}
|
||||
|
||||
for (list<PGenerate*>::const_iterator idx = generate_schemes.begin()
|
||||
; idx != generate_schemes.end() ; idx++) {
|
||||
(*idx)->dump(out, indent+2);
|
||||
}
|
||||
|
||||
@@ -991,7 +1047,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 +1123,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 +1145,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 +1154,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;
|
||||
@@ -1122,6 +1178,11 @@ void Module::dump(ostream&out) const
|
||||
(*behav)->dump(out, 4);
|
||||
}
|
||||
|
||||
for (list<AProcess*>::const_iterator idx = analog_behaviors.begin()
|
||||
; idx != analog_behaviors.end() ; idx++) {
|
||||
(*idx)->dump(out, 4);
|
||||
}
|
||||
|
||||
for (list<PSpecPath*>::const_iterator spec = specify_paths.begin()
|
||||
; spec != specify_paths.end()
|
||||
; spec ++ ) {
|
||||
|
||||
+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=$?"
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef __svector_H
|
||||
#define __svector_H
|
||||
/*
|
||||
* Copyright (c) 1999 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 1999-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
|
||||
@@ -20,12 +20,10 @@
|
||||
* 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: svector.h,v 1.11 2007/03/22 16:08:17 steve Exp $"
|
||||
#endif
|
||||
|
||||
# include "config.h"
|
||||
# include <string>
|
||||
# include <vector>
|
||||
# include <assert.h>
|
||||
|
||||
/*
|
||||
@@ -106,41 +104,18 @@ template <> inline svector<std::string>::svector(unsigned size)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* $Log: svector.h,v $
|
||||
* Revision 1.11 2007/03/22 16:08:17 steve
|
||||
* Spelling fixes from Larry
|
||||
*
|
||||
* Revision 1.10 2005/06/14 19:13:43 steve
|
||||
* gcc3/4 compile errors.
|
||||
*
|
||||
* Revision 1.9 2003/07/23 02:35:44 steve
|
||||
* Inline the svector<string> constructor.
|
||||
*
|
||||
* Revision 1.8 2003/07/16 00:54:07 steve
|
||||
* Needs the config.h header.
|
||||
*
|
||||
* Revision 1.7 2003/07/15 05:07:13 steve
|
||||
* Move PUdp constructor into compiled file.
|
||||
*
|
||||
* Revision 1.6 2002/08/12 01:35:00 steve
|
||||
* conditional ident string using autoconfig.
|
||||
*
|
||||
* Revision 1.5 2000/02/23 02:56:55 steve
|
||||
* Macintosh compilers do not support ident.
|
||||
*
|
||||
* Revision 1.4 1999/06/15 03:44:53 steve
|
||||
* Get rid of the STL vector template.
|
||||
*
|
||||
* Revision 1.3 1999/05/06 04:37:17 steve
|
||||
* Get rid of list<lgate> types.
|
||||
*
|
||||
* Revision 1.2 1999/05/01 02:57:53 steve
|
||||
* Handle much more complex event expressions.
|
||||
*
|
||||
* Revision 1.1 1999/04/29 02:16:26 steve
|
||||
* Parse OR of event expressions.
|
||||
*
|
||||
*/
|
||||
* This is a convenience function that converts an svector to a
|
||||
* vector. This is to ease the transition from svector to vector so
|
||||
* that the svector class can be gradually removed.
|
||||
*/
|
||||
template <class T> inline std::vector<T> vector_from_svector(const svector<T>&that)
|
||||
{
|
||||
std::vector<T> res (that.count());
|
||||
for (unsigned idx = 0 ; idx < that.count() ; idx += 1)
|
||||
res[idx] = that[idx];
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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.
|
||||
|
||||
+30
-8
@@ -354,6 +354,7 @@ extern "C" ivl_expr_t ivl_expr_oper3(ivl_expr_t net)
|
||||
|
||||
extern "C" ivl_parameter_t ivl_expr_parameter(ivl_expr_t net)
|
||||
{
|
||||
assert(net);
|
||||
switch (net->type_) {
|
||||
case IVL_EX_NUMBER:
|
||||
return net->u_.number_.parameter;
|
||||
@@ -886,6 +887,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 +1031,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 +1146,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 +1169,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 +1439,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_;
|
||||
@@ -1532,6 +1548,12 @@ extern "C" const char*ivl_scope_file(ivl_scope_t net)
|
||||
return net->file.str();
|
||||
}
|
||||
|
||||
extern "C" unsigned ivl_scope_is_auto(ivl_scope_t net)
|
||||
{
|
||||
assert(net);
|
||||
return net->is_auto;
|
||||
}
|
||||
|
||||
extern "C" unsigned ivl_scope_lineno(ivl_scope_t net)
|
||||
{
|
||||
assert(net);
|
||||
|
||||
@@ -149,6 +149,19 @@ ivl_expr_t dll_target::expr_from_value_(const verinum&val)
|
||||
return expr;
|
||||
}
|
||||
|
||||
void dll_target::expr_access_func(const NetEAccess*net)
|
||||
{
|
||||
assert(expr_ == 0);
|
||||
// Make a stub Branch Access Function expression node.
|
||||
expr_ = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s));
|
||||
expr_->type_ = IVL_EX_BACCESS;
|
||||
expr_->value_ = IVL_VT_REAL;
|
||||
expr_->file = net->get_file();
|
||||
expr_->lineno = net->get_lineno();
|
||||
expr_->width_ = 1;
|
||||
expr_->signed_= 1;
|
||||
}
|
||||
|
||||
void dll_target::expr_binary(const NetEBinary*net)
|
||||
{
|
||||
assert(expr_ == 0);
|
||||
|
||||
+16
-11
@@ -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"
|
||||
|
||||
@@ -37,6 +34,8 @@
|
||||
|
||||
bool dll_target::process(const NetProcTop*net)
|
||||
{
|
||||
bool rc_flag = true;
|
||||
|
||||
ivl_process_t obj = (struct ivl_process_s*)
|
||||
calloc(1, sizeof(struct ivl_process_s));
|
||||
|
||||
@@ -50,6 +49,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());
|
||||
@@ -70,7 +70,7 @@ bool dll_target::process(const NetProcTop*net)
|
||||
assert(stmt_cur_ == 0);
|
||||
stmt_cur_ = (struct ivl_statement_s*)calloc(1, sizeof*stmt_cur_);
|
||||
assert(stmt_cur_);
|
||||
net->statement()->emit_proc(this);
|
||||
rc_flag = net->statement()->emit_proc(this) && rc_flag;
|
||||
|
||||
assert(stmt_cur_);
|
||||
obj->stmt_ = stmt_cur_;
|
||||
@@ -80,7 +80,7 @@ bool dll_target::process(const NetProcTop*net)
|
||||
obj->next_ = des_.threads_;
|
||||
des_.threads_ = obj;
|
||||
|
||||
return true;
|
||||
return rc_flag;
|
||||
}
|
||||
|
||||
void dll_target::task_def(const NetScope*net)
|
||||
@@ -190,7 +190,7 @@ void dll_target::make_assign_lvals_(const NetAssignBase*net)
|
||||
|
||||
/*
|
||||
*/
|
||||
void dll_target::proc_assign(const NetAssign*net)
|
||||
bool dll_target::proc_assign(const NetAssign*net)
|
||||
{
|
||||
assert(stmt_cur_);
|
||||
assert(stmt_cur_->type_ == IVL_ST_NONE);
|
||||
@@ -214,6 +214,8 @@ void dll_target::proc_assign(const NetAssign*net)
|
||||
stmt_cur_->u_.assign_.delay = expr_;
|
||||
expr_ = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -399,6 +401,8 @@ bool dll_target::proc_cassign(const NetCAssign*net)
|
||||
|
||||
bool dll_target::proc_condit(const NetCondit*net)
|
||||
{
|
||||
bool rc_flag = true;
|
||||
|
||||
assert(stmt_cur_);
|
||||
assert(stmt_cur_->type_ == IVL_ST_NONE);
|
||||
FILE_NAME(stmt_cur_, net);
|
||||
@@ -410,18 +414,20 @@ bool dll_target::proc_condit(const NetCondit*net)
|
||||
assert(expr_ == 0);
|
||||
net->expr()->expr_scan(this);
|
||||
stmt_cur_->u_.condit_.cond_ = expr_;
|
||||
if (expr_ == 0)
|
||||
rc_flag = false;
|
||||
expr_ = 0;
|
||||
|
||||
ivl_statement_t save_cur_ = stmt_cur_;
|
||||
|
||||
stmt_cur_ = save_cur_->u_.condit_.stmt_+0;
|
||||
bool flag = net->emit_recurse_if(this);
|
||||
rc_flag = net->emit_recurse_if(this) && rc_flag;
|
||||
|
||||
stmt_cur_ = save_cur_->u_.condit_.stmt_+1;
|
||||
flag = flag && net->emit_recurse_else(this);
|
||||
rc_flag = net->emit_recurse_else(this) && rc_flag;
|
||||
|
||||
stmt_cur_ = save_cur_;
|
||||
return flag;
|
||||
return rc_flag;
|
||||
}
|
||||
|
||||
bool dll_target::proc_deassign(const NetDeassign*net)
|
||||
@@ -745,4 +751,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;
|
||||
}
|
||||
@@ -567,6 +575,7 @@ void dll_target::add_root(ivl_design_s &des_, const NetScope *s)
|
||||
root_->time_units = s->time_unit();
|
||||
root_->nattr = s->attr_cnt();
|
||||
root_->attr = fill_in_attributes(s);
|
||||
root_->is_auto = 0;
|
||||
|
||||
des_.nroots_++;
|
||||
if (des_.roots_)
|
||||
@@ -1550,6 +1559,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 +1955,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);
|
||||
|
||||
@@ -2246,7 +2320,8 @@ void dll_target::scope(const NetScope*net)
|
||||
scope->time_precision = net->time_precision();
|
||||
scope->time_units = net->time_unit();
|
||||
scope->nattr = net->attr_cnt();
|
||||
scope->attr = fill_in_attributes(net);
|
||||
scope->attr = fill_in_attributes(net);
|
||||
scope->is_auto = net->is_auto();
|
||||
|
||||
switch (net->type()) {
|
||||
case NetScope::MODULE:
|
||||
@@ -2301,8 +2376,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*);
|
||||
@@ -109,7 +111,7 @@ struct dll_target : public target_t, public expr_scan_t {
|
||||
/* These methods and members are used for forming the
|
||||
statements of a thread. */
|
||||
struct ivl_statement_s*stmt_cur_;
|
||||
void proc_assign(const NetAssign*);
|
||||
bool proc_assign(const NetAssign*);
|
||||
void proc_assign_nb(const NetAssignNB*);
|
||||
bool proc_block(const NetBlock*);
|
||||
void proc_case(const NetCase*);
|
||||
@@ -132,6 +134,7 @@ struct dll_target : public target_t, public expr_scan_t {
|
||||
void task_def(const NetScope*);
|
||||
|
||||
struct ivl_expr_s*expr_;
|
||||
void expr_access_func(const NetEAccess*);
|
||||
void expr_binary(const NetEBinary*);
|
||||
void expr_concat(const NetEConcat*);
|
||||
void expr_const(const NetEConst*);
|
||||
@@ -537,7 +540,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 +548,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;
|
||||
@@ -586,6 +591,7 @@ struct ivl_scope_s {
|
||||
|
||||
/* Scopes that are tasks/functions have a definition. */
|
||||
ivl_statement_t def;
|
||||
unsigned is_auto;
|
||||
|
||||
unsigned ports;
|
||||
ivl_signal_t*port;
|
||||
@@ -761,4 +767,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() << "): "
|
||||
@@ -229,10 +243,11 @@ bool target_t::process(const NetProcTop*top)
|
||||
return top->statement()->emit_proc(this);
|
||||
}
|
||||
|
||||
void target_t::proc_assign(const NetAssign*)
|
||||
bool target_t::proc_assign(const NetAssign*)
|
||||
{
|
||||
cerr << "target (" << typeid(*this).name() << "): "
|
||||
"Unhandled procedural assignment." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
void target_t::proc_assign_nb(const NetAssignNB*)
|
||||
@@ -363,6 +378,12 @@ expr_scan_t::~expr_scan_t()
|
||||
{
|
||||
}
|
||||
|
||||
void expr_scan_t::expr_access_func(const NetEAccess*)
|
||||
{
|
||||
cerr << "expr_scan_t (" << typeid(*this).name() << "): "
|
||||
"unhandled expr_access_func." << endl;
|
||||
}
|
||||
|
||||
void expr_scan_t::expr_const(const NetEConst*)
|
||||
{
|
||||
cerr << "expr_scan_t (" << 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*);
|
||||
@@ -103,7 +105,7 @@ struct target_t {
|
||||
virtual bool process(const NetProcTop*);
|
||||
|
||||
/* Various kinds of process nodes are dispatched through these. */
|
||||
virtual void proc_assign(const NetAssign*);
|
||||
virtual bool proc_assign(const NetAssign*);
|
||||
virtual void proc_assign_nb(const NetAssignNB*);
|
||||
virtual bool proc_block(const NetBlock*);
|
||||
virtual void proc_case(const NetCase*);
|
||||
@@ -131,6 +133,7 @@ struct target_t {
|
||||
of expressions. */
|
||||
struct expr_scan_t {
|
||||
virtual ~expr_scan_t();
|
||||
virtual void expr_access_func(const NetEAccess*);
|
||||
virtual void expr_const(const NetEConst*);
|
||||
virtual void expr_param(const NetEConstParam*);
|
||||
virtual void expr_rparam(const NetECRealParam*);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -75,22 +75,22 @@ check: all
|
||||
install: all installdirs $(libdir)/ivl/null.tgt $(INSTALL_DOC) $(libdir)/ivl/null.conf $(libdir)/ivl/null-s.conf
|
||||
|
||||
$(libdir)/ivl/null.tgt: ./null.tgt
|
||||
$(INSTALL_PROGRAM) ./null.tgt $(libdir)/ivl/null.tgt
|
||||
$(INSTALL_PROGRAM) ./null.tgt $(DESTDIR)$(libdir)/ivl/null.tgt
|
||||
|
||||
$(libdir)/ivl/null.conf: $(srcdir)/null.conf
|
||||
$(INSTALL_DATA) $(srcdir)/null.conf $(libdir)/ivl/null.conf
|
||||
$(INSTALL_DATA) $(srcdir)/null.conf $(DESTDIR)$(libdir)/ivl/null.conf
|
||||
|
||||
$(libdir)/ivl/null-s.conf: $(srcdir)/null-s.conf
|
||||
$(INSTALL_DATA) $(srcdir)/null-s.conf $(libdir)/ivl/null-s.conf
|
||||
$(INSTALL_DATA) $(srcdir)/null-s.conf $(DESTDIR)$(libdir)/ivl/null-s.conf
|
||||
|
||||
|
||||
installdirs: ../mkinstalldirs
|
||||
$(srcdir)/../mkinstalldirs $(includedir) $(bindir) $(libdir)/ivl
|
||||
$(srcdir)/../mkinstalldirs $(DESTDIR)$(includedir) $(DESTDIR)$(bindir) $(DESTDIR)$(libdir)/ivl
|
||||
|
||||
uninstall:
|
||||
rm -f $(libdir)/ivl/null.tgt
|
||||
rm -f $(libdir)/ivl/null.conf
|
||||
rm -f $(libdir)/ivl/null-s.conf
|
||||
rm -f $(DESTDIR)$(libdir)/ivl/null.tgt
|
||||
rm -f $(DESTDIR)$(libdir)/ivl/null.conf
|
||||
rm -f $(DESTDIR)$(libdir)/ivl/null-s.conf
|
||||
|
||||
|
||||
-include $(patsubst %.o, dep/%.d, $O)
|
||||
|
||||
@@ -76,21 +76,21 @@ install: all installdirs $(libdir)/ivl/stub.tgt \
|
||||
$(libdir)/ivl/stub.conf $(libdir)/ivl/stub-s.conf
|
||||
|
||||
$(libdir)/ivl/stub.tgt: ./stub.tgt
|
||||
$(INSTALL_PROGRAM) ./stub.tgt $(libdir)/ivl/stub.tgt
|
||||
$(INSTALL_PROGRAM) ./stub.tgt $(DESTDIR)$(libdir)/ivl/stub.tgt
|
||||
|
||||
$(libdir)/ivl/stub.conf: stub.conf
|
||||
$(INSTALL_DATA) $< $(libdir)/ivl/stub.conf
|
||||
$(INSTALL_DATA) $< $(DESTDIR)$(libdir)/ivl/stub.conf
|
||||
|
||||
$(libdir)/ivl/stub-s.conf: stub-s.conf
|
||||
$(INSTALL_DATA) $< $(libdir)/ivl/stub-s.conf
|
||||
$(INSTALL_DATA) $< $(DESTDIR)$(libdir)/ivl/stub-s.conf
|
||||
|
||||
installdirs: ../mkinstalldirs
|
||||
$(srcdir)/../mkinstalldirs $(includedir) $(bindir) $(libdir)/ivl
|
||||
$(srcdir)/../mkinstalldirs $(DESTDIR)$(includedir) $(DESTDIR)$(bindir) $(DESTDIR)$(libdir)/ivl
|
||||
|
||||
uninstall:
|
||||
rm -f $(libdir)/ivl/stub.tgt
|
||||
rm -f $(libdir)/ivl/stub.conf
|
||||
rm -f $(libdir)/ivl/stub-s.conf
|
||||
rm -f $(DESTDIR)$(libdir)/ivl/stub.tgt
|
||||
rm -f $(DESTDIR)$(libdir)/ivl/stub.conf
|
||||
rm -f $(DESTDIR)$(libdir)/ivl/stub-s.conf
|
||||
|
||||
|
||||
-include $(patsubst %.o, dep/%.d, $O)
|
||||
|
||||
+35
-2
@@ -43,16 +43,40 @@ static void show_array_expression(ivl_expr_t net, unsigned ind)
|
||||
ivl_signal_dimensions(sig), width, vt);
|
||||
}
|
||||
|
||||
static void show_branch_access_expression(ivl_expr_t net, unsigned ind)
|
||||
{
|
||||
fprintf(out, "%*s<Branch Access>\n", ind, "");
|
||||
|
||||
if (ivl_expr_value(net) != IVL_VT_REAL) {
|
||||
fprintf(out, "%*sERROR: Expecting type IVL_VT_REAL, got %s\n",
|
||||
ind, "", vt_type_string(net));
|
||||
stub_errors += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void show_binary_expression(ivl_expr_t net, unsigned ind)
|
||||
{
|
||||
unsigned width = ivl_expr_width(net);
|
||||
const char*sign = ivl_expr_signed(net)? "signed" : "unsigned";
|
||||
const char*vt = vt_type_string(net);
|
||||
|
||||
ivl_expr_t oper1 = ivl_expr_oper1(net);
|
||||
ivl_expr_t oper2 = ivl_expr_oper2(net);
|
||||
|
||||
fprintf(out, "%*s<\"%c\" width=%u, %s, type=%s>\n", ind, "",
|
||||
ivl_expr_opcode(net), width, sign, vt);
|
||||
show_expression(ivl_expr_oper1(net), ind+3);
|
||||
show_expression(ivl_expr_oper2(net), ind+3);
|
||||
if (oper1) {
|
||||
show_expression(oper1, ind+3);
|
||||
} else {
|
||||
fprintf(out, "%*sERROR: Missing operand 1\n", ind+3, "");
|
||||
stub_errors += 1;
|
||||
}
|
||||
if (oper2) {
|
||||
show_expression(oper2, ind+3);
|
||||
} else {
|
||||
fprintf(out, "%*sERROR: Missing operand 2\n", ind+3, "");
|
||||
stub_errors += 1;
|
||||
}
|
||||
|
||||
switch (ivl_expr_opcode(net)) {
|
||||
|
||||
@@ -178,6 +202,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);
|
||||
@@ -198,6 +227,10 @@ void show_expression(ivl_expr_t net, unsigned ind)
|
||||
show_array_expression(net, ind);
|
||||
break;
|
||||
|
||||
case IVL_EX_BACCESS:
|
||||
show_branch_access_expression(net, ind);
|
||||
break;
|
||||
|
||||
case IVL_EX_BINARY:
|
||||
show_binary_expression(net, ind);
|
||||
break;
|
||||
|
||||
@@ -264,7 +264,12 @@ void show_statement(ivl_statement_t net, unsigned ind)
|
||||
ivl_statement_t f = ivl_stmt_cond_false(net);
|
||||
|
||||
fprintf(out, "%*sif (...)\n", ind, "");
|
||||
show_expression(ex, ind+4);
|
||||
if (ex) {
|
||||
show_expression(ex, ind+4);
|
||||
} else {
|
||||
fprintf(out, "%*sERROR: Condition expression is NIL;\n", ind+4, "");
|
||||
stub_errors += 1;
|
||||
}
|
||||
if (t)
|
||||
show_statement(t, ind+4);
|
||||
else
|
||||
|
||||
+61
-2
@@ -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;
|
||||
@@ -1390,12 +1448,13 @@ static int show_scope(ivl_scope_t net, void*x)
|
||||
ivl_scope_name(net), ivl_scope_params(net),
|
||||
ivl_scope_sigs(net), ivl_scope_logs(net));
|
||||
|
||||
char *is_auto = ivl_scope_is_auto(net) ? "automatic " : "";
|
||||
switch (ivl_scope_type(net)) {
|
||||
case IVL_SCT_MODULE:
|
||||
fprintf(out, " module %s", ivl_scope_tname(net));
|
||||
break;
|
||||
case IVL_SCT_FUNCTION:
|
||||
fprintf(out, " function %s", ivl_scope_tname(net));
|
||||
fprintf(out, " function %s%s", is_auto, ivl_scope_tname(net));
|
||||
break;
|
||||
case IVL_SCT_BEGIN:
|
||||
fprintf(out, " begin : %s", ivl_scope_tname(net));
|
||||
@@ -1404,7 +1463,7 @@ static int show_scope(ivl_scope_t net, void*x)
|
||||
fprintf(out, " fork : %s", ivl_scope_tname(net));
|
||||
break;
|
||||
case IVL_SCT_TASK:
|
||||
fprintf(out, " task %s", ivl_scope_tname(net));
|
||||
fprintf(out, " task %s%s", is_auto, ivl_scope_tname(net));
|
||||
break;
|
||||
default:
|
||||
fprintf(out, " type(%u) %s", ivl_scope_type(net),
|
||||
|
||||
@@ -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 $(DESTDIR)$(libdir)/ivl/vhdl.tgt
|
||||
|
||||
$(libdir)/ivl/vhdl.conf: vhdl.conf
|
||||
$(INSTALL_DATA) $< $(DESTDIR)$(libdir)/ivl/vhdl.conf
|
||||
|
||||
installdirs: ../mkinstalldirs
|
||||
$(srcdir)/../mkinstalldirs $(DESTDIR)$(libdir)/ivl
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(libdir)/ivl/vhdl.tgt $(DESTDIR)$(libdir)/ivl/vhdl.conf
|
||||
|
||||
|
||||
-include $(patsubst %.o, dep/%.d, $O)
|
||||
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* 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 {
|
||||
switch (to->get_name()) {
|
||||
case VHDL_TYPE_BOOLEAN:
|
||||
return to_boolean();
|
||||
case VHDL_TYPE_INTEGER:
|
||||
return to_integer();
|
||||
case VHDL_TYPE_UNSIGNED:
|
||||
case VHDL_TYPE_SIGNED:
|
||||
case VHDL_TYPE_STD_LOGIC_VECTOR:
|
||||
return to_vector(to->get_name(), to->get_width());
|
||||
case VHDL_TYPE_STD_LOGIC:
|
||||
return to_std_logic();
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate code to cast an expression to a vector type (std_logic_vector,
|
||||
* signed, unsigned).
|
||||
*/
|
||||
vhdl_expr *vhdl_expr::to_vector(vhdl_type_name_t name, int w)
|
||||
{
|
||||
if (type_->get_name() == VHDL_TYPE_STD_LOGIC) {
|
||||
vhdl_expr *others = w == 1 ? NULL : new vhdl_const_bit('0');
|
||||
vhdl_bit_spec_expr *bs =
|
||||
new vhdl_bit_spec_expr(new vhdl_type(name, w - 1, 0), others);
|
||||
bs->add_bit(0, this);
|
||||
|
||||
return bs;
|
||||
}
|
||||
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_type *t = new vhdl_type(name, w - 1, 0);
|
||||
vhdl_fcall *conv = new vhdl_fcall(t->get_string().c_str(), t);
|
||||
conv->add_expr(this);
|
||||
|
||||
if (w != type_->get_width())
|
||||
return conv->resize(w);
|
||||
else
|
||||
return conv;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a generic expression to an Integer.
|
||||
*/
|
||||
vhdl_expr *vhdl_expr::to_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", vhdl_type::integer());
|
||||
|
||||
conv->add_expr(this);
|
||||
|
||||
return conv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a generic expression to a Boolean.
|
||||
*/
|
||||
vhdl_expr *vhdl_expr::to_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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate code to convert and expression to std_logic.
|
||||
*/
|
||||
vhdl_expr *vhdl_expr::to_std_logic()
|
||||
{
|
||||
if (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 if (type_->get_name() == VHDL_TYPE_SIGNED) {
|
||||
require_support_function(SF_SIGNED_TO_LOGIC);
|
||||
|
||||
vhdl_fcall *ah =
|
||||
new vhdl_fcall(support_function::function_name(SF_SIGNED_TO_LOGIC),
|
||||
vhdl_type::std_logic());
|
||||
ah->add_expr(this);
|
||||
|
||||
return ah;
|
||||
}
|
||||
else if (type_->get_name() == VHDL_TYPE_UNSIGNED) {
|
||||
require_support_function(SF_UNSIGNED_TO_LOGIC);
|
||||
|
||||
vhdl_fcall *ah =
|
||||
new vhdl_fcall(support_function::function_name(SF_UNSIGNED_TO_LOGIC),
|
||||
vhdl_type::std_logic());
|
||||
ah->add_expr(this);
|
||||
|
||||
return ah;
|
||||
}
|
||||
else
|
||||
assert(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the width of a signed/unsigned type.
|
||||
*/
|
||||
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::to_vector(vhdl_type_name_t name, int w)
|
||||
{
|
||||
if (name == VHDL_TYPE_SIGNED || name == VHDL_TYPE_UNSIGNED) {
|
||||
|
||||
const char *fname = name == VHDL_TYPE_SIGNED
|
||||
? "To_Signed" : "To_Unsigned";
|
||||
vhdl_fcall *conv = new vhdl_fcall(fname, new vhdl_type(name, w - 1, 0));
|
||||
conv->add_expr(this);
|
||||
conv->add_expr(new vhdl_const_int(w));
|
||||
|
||||
return conv;
|
||||
}
|
||||
else
|
||||
return vhdl_expr::to_vector(name, w);
|
||||
}
|
||||
|
||||
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::to_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);
|
||||
}
|
||||
|
||||
vhdl_expr *vhdl_const_bits::to_vector(vhdl_type_name_t name, int w)
|
||||
{
|
||||
if (name == VHDL_TYPE_STD_LOGIC_VECTOR) {
|
||||
// Don't need to do anything
|
||||
return this;
|
||||
}
|
||||
else if (name == VHDL_TYPE_SIGNED || name == VHDL_TYPE_UNSIGNED) {
|
||||
// Extend with sign bit
|
||||
value_.resize(w, value_[0]);
|
||||
return this;
|
||||
}
|
||||
else
|
||||
assert(false);
|
||||
}
|
||||
|
||||
vhdl_expr *vhdl_const_bits::to_integer()
|
||||
{
|
||||
return new vhdl_const_int(bits_to_int());
|
||||
}
|
||||
|
||||
vhdl_expr *vhdl_const_bit::to_integer()
|
||||
{
|
||||
return new vhdl_const_int(bit_ == '1' ? 1 : 0);
|
||||
}
|
||||
|
||||
vhdl_expr *vhdl_const_bit::to_boolean()
|
||||
{
|
||||
return new vhdl_const_bool(bit_ == '1');
|
||||
}
|
||||
@@ -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,577 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate code to ensure that the VHDL expression vhd_e has the
|
||||
* same signedness as the Verilog expression vl_e.
|
||||
*/
|
||||
static vhdl_expr *correct_signedness(vhdl_expr *vhd_e, ivl_expr_t vl_e)
|
||||
{
|
||||
bool should_be_signed = ivl_expr_signed(vl_e) != 0;
|
||||
|
||||
if (vhd_e->get_type()->get_name() == VHDL_TYPE_UNSIGNED
|
||||
&& should_be_signed) {
|
||||
//operand->print();
|
||||
//std::cout << "^ should be signed but is not" << std::endl;
|
||||
|
||||
return change_signedness(vhd_e, true);
|
||||
}
|
||||
else if (vhd_e->get_type()->get_name() == VHDL_TYPE_SIGNED
|
||||
&& !should_be_signed) {
|
||||
//operand->print();
|
||||
//std::cout << "^ should be unsigned but is not" << std::endl;
|
||||
|
||||
return change_signedness(vhd_e, false);
|
||||
}
|
||||
else
|
||||
return vhd_e;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
operand = correct_signedness(operand, e);
|
||||
|
||||
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;
|
||||
if (op == VHDL_BINOP_MULT)
|
||||
rtype = new vhdl_type(lhs->get_type()->get_name(),
|
||||
(lhs->get_type()->get_width()*2) - 1, 0);
|
||||
else
|
||||
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 or change the
|
||||
// signedness
|
||||
if (vectorop) {
|
||||
if (lwidth < rwidth)
|
||||
lhs = lhs->resize(rwidth);
|
||||
else if (rwidth < lwidth)
|
||||
rhs = rhs->resize(lwidth);
|
||||
|
||||
lhs = correct_signedness(lhs, ivl_expr_oper1(e));
|
||||
rhs = correct_signedness(rhs, ivl_expr_oper2(e));
|
||||
}
|
||||
|
||||
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) {
|
||||
result = correct_signedness(result, e);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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 =
|
||||
vhdl_type::type_for(ivl_expr_width(e), ivl_expr_signed(e) != 0);
|
||||
vhdl_fcall *fcall = new vhdl_fcall(funcname, rettype);
|
||||
|
||||
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;
|
||||
|
||||
// Ensure the parameter has the correct VHDL type
|
||||
ivl_signal_t param_sig = ivl_scope_sig(defscope, i);
|
||||
vhdl_type *param_type =
|
||||
vhdl_type::type_for(ivl_signal_width(param_sig),
|
||||
ivl_signal_signed(param_sig) != 0);
|
||||
|
||||
fcall->add_expr(param->cast(param_type));
|
||||
delete param_type;
|
||||
}
|
||||
|
||||
return fcall;
|
||||
}
|
||||
|
||||
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 =
|
||||
vhdl_type::type_for(ivl_expr_width(e), ivl_expr_signed(e) != 0);
|
||||
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,821 @@
|
||||
/*
|
||||
* 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 <funcname>_Result holds the return value
|
||||
signame = funcname;
|
||||
signame += "_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
|
||||
vhdl_scope *parent_scope = parent_arch->get_scope();
|
||||
if (!parent_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() || parent_scope->have_declared(inst_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,700 @@
|
||||
/*
|
||||
* 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_CASEZ:
|
||||
error("casez 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,171 @@
|
||||
/*
|
||||
* 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";
|
||||
case SF_SIGNED_TO_LOGIC: return "Signed_To_Logic";
|
||||
case SF_UNSIGNED_TO_LOGIC: return "Unsigned_To_Logic";
|
||||
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:
|
||||
case SF_SIGNED_TO_LOGIC:
|
||||
case SF_UNSIGNED_TO_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_UNSIGNED_TO_LOGIC:
|
||||
of << "(X : unsigned) return std_logic is" << nl_string(level)
|
||||
<< "begin" << nl_string(indent(level))
|
||||
<< "return X(0);";
|
||||
break;
|
||||
case SF_SIGNED_TO_LOGIC:
|
||||
of << "(X : signed) return std_logic is" << nl_string(level)
|
||||
<< "begin" << nl_string(indent(level))
|
||||
<< "return X(0);";
|
||||
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;";
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user