forked from steveicarus/iverilog
-
Notifications
You must be signed in to change notification settings - Fork 1
/
stmt.cc
1528 lines (1312 loc) · 50.9 KB
/
stmt.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* VHDL code generation for statements.
*
* Copyright (C) 2008-2009 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 "state.hh"
#include <iostream>
#include <cstring>
#include <cassert>
#include <sstream>
#include <typeinfo>
#include <limits>
#include <set>
#include <algorithm>
/*
* 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 functionality
* 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 {
vhdl_seq_stmt *result = new vhdl_null_stmt();
ostringstream ss;
ss << "Unsupported system task " << name << " omitted here ("
<< ivl_stmt_file(stmt) << ":" << ivl_stmt_lineno(stmt) << ")";
result->set_comment(ss.str());
container->add_stmt(result);
cerr << "Warning: no VHDL translation for system task " << name << endl;
return 0;
}
}
/*
* Generate VHDL for a block of Verilog statements. If this block
* doesn't have its own scope then this function does nothing, 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.
*
* If this block has its own scope with local variables then these
* are added to the process as local variables and the statements
* are generated as above.
*/
static int draw_block(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt, bool is_last)
{
ivl_scope_t block_scope = ivl_stmt_block_scope(stmt);
if (block_scope) {
int nsigs = ivl_scope_sigs(block_scope);
for (int i = 0; i < nsigs; i++) {
ivl_signal_t sig = ivl_scope_sig(block_scope, i);
remember_signal(sig, proc->get_scope());
vhdl_type* type = vhdl_type::type_for(ivl_signal_width(sig),
ivl_signal_signed(sig));
proc->get_scope()->add_decl
(new vhdl_var_decl(make_safe_name(sig), type));
}
}
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;
}
/*
* The VHDL code generator inserts `wait for 0 ns' after each
* not-last-in-block blocking assignment.
* If this is immediately followed by another `wait for ...' then
* we might as well not emit the first zero-time wait.
*/
void prune_wait_for_0(stmt_container *container)
{
vhdl_wait_stmt *wait0;
stmt_container::stmt_list_t &stmts = container->get_stmts();
while (stmts.size() > 0
&& (wait0 = dynamic_cast<vhdl_wait_stmt*>(stmts.back()))) {
if (wait0->get_type() == VHDL_WAIT_FOR0) {
delete wait0;
stmts.pop_back();
}
else
break;
}
}
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);
// Verilog allows assignments to elements that are constant in VHDL:
// function parameters, for example
// To work around this we generate a local variable to shadow the
// constant and assign to that
if (decl->assignment_type() == vhdl_decl::ASSIGN_CONST) {
const string shadow_name = signame + "_Shadow";
vhdl_var_decl* shadow_decl =
new vhdl_var_decl(shadow_name, decl->get_type());
shadow_decl->set_initial
(new vhdl_var_ref(signame, decl->get_type()));
scope->add_decl(shadow_decl);
// Make sure all future references to this signal use the
// shadow variable
rename_signal(sig, shadow_name);
// ...and use this new variable as the assignment LHS
decl = shadow_decl;
}
vhdl_type *ltype = new vhdl_type(*decl->get_type());
vhdl_var_ref *lval_ref = new vhdl_var_ref(decl->get_name(), ltype);
if (base) {
if (decl->get_type()->get_name() == VHDL_TYPE_ARRAY)
lval_ref->set_slice(base, 0);
else if (ivl_signal_width(sig) > 1)
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 the right sort of assignment statement for assigning
* `lhs' to `rhs'.
*/
static vhdl_abstract_assign_stmt *
assign_for(vhdl_decl::assign_type_t atype, vhdl_var_ref *lhs, vhdl_expr *rhs)
{
switch (atype) {
case vhdl_decl::ASSIGN_BLOCK:
case vhdl_decl::ASSIGN_CONST:
return new vhdl_assign_stmt(lhs, rhs);
case vhdl_decl::ASSIGN_NONBLOCK:
return new vhdl_nbassign_stmt(lhs, rhs);
default:
assert(false);
}
}
/*
* Check that this assignment type is valid within the context of `proc'.
* For example, a <= assignment is not valid within a function.
*/
bool check_valid_assignment(vhdl_decl::assign_type_t atype, vhdl_procedural *proc,
ivl_statement_t stmt)
{
if (atype == vhdl_decl::ASSIGN_NONBLOCK &&
!proc->get_scope()->allow_signal_assignment()) {
error("Unable to translate assignment at %s:%d\n"
" Translating this would require generating a non-blocking (<=)\n"
" assignment in a VHDL context where this is disallowed (e.g.\n"
" a function).", ivl_stmt_file(stmt), ivl_stmt_lineno(stmt));
return false;
}
else
return true;
}
// Generate an assignment of type T for the Verilog statement stmt.
// If a statement was generated then `assign_type' will contain the
// type of assignment that was generated; this should be initialised
// to some sensible default.
void make_assignment(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt, bool blocking,
vhdl_decl::assign_type_t& assign_type)
{
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);
// Find the declaration of the LHS so we know what type
// of assignment statement to generate (is it a signal,
// a variable, etc?)
vhdl_decl *decl = proc->get_scope()->get_decl(lhs->get_name());
assign_type = decl->assignment_type();
// 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;
if (!check_valid_assignment(decl->assignment_type(), proc, stmt))
return;
vhdl_if_stmt *vhdif = new vhdl_if_stmt(test);
// True part
{
vhdl_abstract_assign_stmt *a =
assign_for(decl->assignment_type(), lhs, rhs);
if (after)
a->set_after(after);
vhdif->get_then_container()->add_stmt(a);
}
// False part
{
vhdl_abstract_assign_stmt *a =
assign_for(decl->assignment_type(), 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 initialisers. This optimisation is only
// performed on assignments of constant values to prevent
// ordering problems.
// This also has another application: If this is an `initial'
// 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 activated before an `initial' process at time 0. The
// `always' process may then use the uninitialised 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);
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;
}
}
if (!check_valid_assignment(decl->assignment_type(), proc, stmt))
return;
vhdl_abstract_assign_stmt *a =
assign_for(decl->assignment_type(), 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++;
vhdl_decl* tmp_decl = new vhdl_var_decl(ss.str(), rhs->get_type());
proc->get_scope()->add_decl(tmp_decl);
container->add_stmt(new vhdl_assign_stmt(tmp_decl->make_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 = tmp_decl->make_ref();
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);
// Find the declaration of the LHS so we know what type
// of assignment statement to generate (is it a signal,
// a variable, etc?)
vhdl_decl *decl = proc->get_scope()->get_decl((*it)->get_name());
assign_type = decl->assignment_type();
if (!check_valid_assignment(decl->assignment_type(), proc, stmt))
return;
vhdl_abstract_assign_stmt *a =
assign_for(decl->assignment_type(), *it, tmp_rhs);
if (after)
a->set_after(after);
container->add_stmt(a);
width_so_far += lval_width;
}
}
return;
}
/*
* 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());
vhdl_decl::assign_type_t ignored;
make_assignment(proc, container, stmt, false, ignored);
return 0;
}
static int draw_assign(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt, bool is_last)
{
vhdl_decl::assign_type_t assign_type = vhdl_decl::ASSIGN_NONBLOCK;
if (proc->get_scope()->allow_signal_assignment()) {
// Blocking assignment is implemented as non-blocking assignment
// followed by a zero-time wait
// This follows the Verilog semantics fairly closely.
make_assignment(proc, container, stmt, false, assign_type);
// Don't generate a zero-wait if either:
// a) this is the last statement in the process
// c) a blocking assignment was generated
if (!is_last && assign_type == vhdl_decl::ASSIGN_NONBLOCK) {
prune_wait_for_0(container);
container->add_stmt
(new vhdl_wait_stmt(VHDL_WAIT_FOR0));
proc->added_wait_stmt();
}
}
else
make_assignment(proc, container, stmt, true, assign_type);
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 = scale_time(get_active_entity(), value);
}
else {
time = translate_time_expr(ivl_stmt_delay_expr(stmt));
if (NULL == time)
return 1;
}
prune_wait_for_0(container);
ivl_statement_t sub_stmt = ivl_stmt_sub_stmt(stmt);
vhdl_wait_stmt *wait =
new vhdl_wait_stmt(VHDL_WAIT_FOR, time);
// Remember that we needed a wait statement so if this is
// a process it cannot have a sensitivity list
proc->added_wait_stmt();
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 initialise signal declarations
// (if this scope is an initial process)
proc->get_scope()->set_initializing(false);
return 0;
}
/*
* Build a set of all the nexuses referenced by signals in `expr'.
*/
static void get_nexuses_from_expr(ivl_expr_t expr, set<ivl_nexus_t> &out)
{
switch (ivl_expr_type(expr)) {
case IVL_EX_SIGNAL:
out.insert(ivl_signal_nex(ivl_expr_signal(expr), 0));
break;
case IVL_EX_TERNARY:
get_nexuses_from_expr(ivl_expr_oper3(expr), out);
case IVL_EX_BINARY:
get_nexuses_from_expr(ivl_expr_oper2(expr), out);
case IVL_EX_UNARY:
get_nexuses_from_expr(ivl_expr_oper1(expr), out);
break;
default:
break;
}
}
/*
* Attempt to identify common forms of wait statements and produce
* more idiomatic VHDL than would be produced by the generic
* draw_wait function. The main application of this is a input to
* synthesis tools that don't synthesise the full VHDL language.
* If none of these patterns are matched, the function returns false
* and the default draw_wait is used.
*
* Current patterns:
* always @(posedge A or posedge B)
* if (A)
* ...
* else
* ...
*
* This is assumed to be the template for a FF with asynchronous
* reset. A is assumed to be the reset as it is dominant. This will
* produce the following VHDL:
*
* process (A, B) is
* begin
* if A = '1' then
* ...
* else if rising_edge(B) then
* ...
* end if;
* end process;
*/
static bool draw_synthesisable_wait(vhdl_process *proc, stmt_container *container,
ivl_statement_t stmt)
{
// At the moment this only detects FFs with an asynchronous reset
// All other code will fall back on the default draw_wait
// Store a set of the edge triggered signals
// The second item is true if this is positive-edge
set<ivl_nexus_t> edge_triggered;
const int nevents = ivl_stmt_nevent(stmt);
for (int i = 0; i < nevents; i++) {
ivl_event_t event = ivl_stmt_events(stmt, i);
if (ivl_event_nany(event) > 0)
return false;
int npos = ivl_event_npos(event);
for (int j = 0; j < npos; j++)
edge_triggered.insert(ivl_event_pos(event, j));
int nneg = ivl_event_nneg(event);
for (int j = 0; j < nneg; j++)
edge_triggered.insert(ivl_event_neg(event, j));
}
// If we're edge-sensitive to less than two signals this doesn't
// match the expected template, so use the default draw_wait
if (edge_triggered.size() < 2)
return false;
// Now check to see if the immediately embedded statement is an `if'
ivl_statement_t sub_stmt = ivl_stmt_sub_stmt(stmt);
if (ivl_statement_type(sub_stmt) != IVL_ST_CONDIT)
return false;
// The if should have two branches: one is the reset branch and
// one is the clocked branch
if (ivl_stmt_cond_false(sub_stmt) == NULL)
return false;
// Check the first branch of the if statement
// If it matches exactly one of the edge-triggered signals then assume
// this is the (dominant) reset branch
set<ivl_nexus_t> test_nexuses;
get_nexuses_from_expr(ivl_stmt_cond_expr(sub_stmt), test_nexuses);
// Now subtracting this set from the set of edge triggered events
// should leave just one nexus, which is hopefully the clock.
// If not, then we fall back on the default draw_wait
set<ivl_nexus_t> clock_net;
set_difference(edge_triggered.begin(), edge_triggered.end(),
test_nexuses.begin(), test_nexuses.end(),
inserter(clock_net, clock_net.begin()));
if (clock_net.size() != 1)
return false;
// Build a VHDL `if' statement to model this
vhdl_expr *reset_test = translate_expr(ivl_stmt_cond_expr(sub_stmt));
vhdl_if_stmt *body = new vhdl_if_stmt(reset_test);
// Draw the reset branch
draw_stmt(proc, body->get_then_container(), ivl_stmt_cond_true(sub_stmt));
// Build a test for the clock event
vhdl_fcall *edge = NULL;
ivl_nexus_t the_clock_net = *clock_net.begin();
for (int i = 0; i < nevents; i++) {
ivl_event_t event = ivl_stmt_events(stmt, i);
const unsigned npos = ivl_event_npos(event);
for (unsigned j = 0; j < npos; j++) {
if (ivl_event_pos(event, j) == the_clock_net)
edge = new vhdl_fcall("rising_edge", vhdl_type::boolean());
}
const unsigned nneg = ivl_event_nneg(event);
for (unsigned j = 0; j < nneg; j++)
if (ivl_event_neg(event, j) == the_clock_net)
edge = new vhdl_fcall("falling_edge", vhdl_type::boolean());
}
assert(edge);
edge->add_expr(nexus_to_var_ref(proc->get_scope(), *clock_net.begin()));
// Draw the clocked branch
// For an asynchronous reset we just want this around the else branch,
stmt_container *else_container = body->add_elsif(edge);
draw_stmt(proc, else_container, ivl_stmt_cond_false(sub_stmt));
if (proc->contains_wait_stmt()) {
// Expanding the body produced a `wait' statement which can't
// be included in a sensitised process so undo all this work
// and fall back on the default draw_wait
delete body;
return false;
}
else
container->add_stmt(body);
// Add all the edge triggered signals to the sensitivity list
for (set<ivl_nexus_t>::const_iterator it = edge_triggered.begin();
it != edge_triggered.end(); ++it) {
// Get the signal that represents this nexus in this scope
vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope(), *it);
proc->add_sensitivity(ref->get_name());
// Don't need the reference any more
delete ref;
}
// Don't bother with the default draw_wait
return true;
}
/*
* A wait statement waits for a level change on a @(..) list of
* signals. The logic here might seem a little bit convoluted,
* it attempts to always produce something that will simulate
* correctly, and tries to produce something that will also
* synthesise correctly (although not at the expense of simulation
* accuracy).
*
* The difficulty stems from VHDL's restriction that a process with
* a sensitivity list may not contain any `wait' statements: we need
* to generate these to accurately model some Verilog statements.
*
* The steps followed are:
* 1) Determine whether this is the top-level statement in the process
* 2) If this is top-level, call draw_synthesisable_wait to see if the
* process and wait statement match any templates for which we know
* how to produce good, idiomatic synthesisable VHDL (e.g. FF with
* async reset)
* 3) Determine whether the process is combinatorial (purely level
* sensitive), or sequential (edge sensitive)
* 4) Draw all of the statements in the body
* 5) One of the following will be true:
* A) The process is combinatorial, top-level, and there are
* no `wait' statements in the body: add all the level-sensitive
* signals to the VHDL sensitivity list
* B) The process is combinatorial, and there *are* `wait'
* statements in the body or it is not top-level: generate
* a VHDL `wait-on' statement at the end of the body containing
* the level-sensitive signals
* C) The process is sequential, top-level, and there are
* no `wait' statements in the body: build an `if' statement
* with the edge-detecting expression and wrap the process
* in it.
* D) The process is sequential, there *are* `wait' statements
* in the body, or it is not top-level: generate a VHDL
* `wait-until' with the edge-detecting expression and add
* it before the body of the wait event.
*/
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
// If this container is the top-level statement (i.e. it is the
// first thing inside a process) then we can extract these
// events out into the sensitivity list
bool is_top_level = container == proc->get_container()
&& container->empty();
// See if this can be implemented in a more idomatic way before we
// fall back on the generic translation
if (is_top_level && draw_synthesisable_wait(proc, container, stmt))
return 0;
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) {
// If the process has no wait statement in its body then
// add all the events to the sensitivity list, otherwise
// build a wait-on statement at the end of the process
draw_stmt(proc, container, ivl_stmt_sub_stmt(stmt), true);
vhdl_wait_stmt *wait = NULL;
if (proc->contains_wait_stmt() || !is_top_level)
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 j = 0; j < nany; j++) {
ivl_nexus_t nexus = ivl_event_any(event, j);
vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope(), nexus);
if (wait)
wait->add_sensitivity(ref->get_name());
else
proc->add_sensitivity(ref->get_name());
delete ref;
}
}
if (wait)
container->add_stmt(wait);
}
else {
// Build a test expression to represent the edge event
// If this process contains no `wait' statements and this
// is the top-level container then we
// wrap it in an `if' statement with this test and add the
// edge triggered signals to the sensitivity, otherwise
// build a `wait until' statement at the top of the process
vhdl_binop_expr *test =
new vhdl_binop_expr(VHDL_BINOP_OR, vhdl_type::boolean());
stmt_container tmp_container;
draw_stmt(proc, &tmp_container, ivl_stmt_sub_stmt(stmt), true);
for (int i = 0; i < nevents; i++) {
ivl_event_t event = ivl_stmt_events(stmt, i);
int nany = ivl_event_nany(event);
for (int j = 0; j < nany; j++) {
ivl_nexus_t nexus = ivl_event_any(event, j);
vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope(), nexus);
ref->set_name(ref->get_name() + "'Event");
test->add_expr(ref);
if (!proc->contains_wait_stmt() && is_top_level)
proc->add_sensitivity(ref->get_name());
}
int nneg = ivl_event_nneg(event);
for (int j = 0; j < nneg; j++) {
ivl_nexus_t nexus = ivl_event_neg(event, j);
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);
if (!proc->contains_wait_stmt() && is_top_level)
proc->add_sensitivity(ref->get_name());
}
int npos = ivl_event_npos(event);
for (int j = 0; j < npos; j++) {
ivl_nexus_t nexus = ivl_event_pos(event, j);
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);
if (!proc->contains_wait_stmt() && is_top_level)
proc->add_sensitivity(ref->get_name());
}
}
if (proc->contains_wait_stmt() || !is_top_level) {
container->add_stmt(new vhdl_wait_stmt(VHDL_WAIT_UNTIL, test));
container->move_stmts_from(&tmp_container);
}
else {
// Wrap the whole process body in an `if' statement to detect
// the edge event
vhdl_if_stmt *edge_detect = new vhdl_if_stmt(test);
// Move all the statements from the process body into the `if'
// statement
edge_detect->get_then_container()->move_stmts_from(&tmp_container);
container->add_stmt(edge_detect);
}
}
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 vhdl_var_ref *draw_case_test(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt)
{
vhdl_expr *test = translate_expr(ivl_stmt_cond_expr(stmt));
if (NULL == test)
return NULL;
// 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));
return new vhdl_var_ref(tmp_name, test_type);
}
else
return dynamic_cast<vhdl_var_ref*>(test);
}
static int draw_case(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt, bool is_last)
{
vhdl_var_ref *test = draw_case_test(proc, container, stmt);
if (NULL == test)
return 1;
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;
}
/*
* Check to see if the given number (expression) can be represented
* accurately in a long value.
*/
static bool number_is_long(ivl_expr_t expr)
{
ivl_expr_type_t type = ivl_expr_type(expr);
assert(type == IVL_EX_NUMBER || type == IVL_EX_ULONG);
// Make sure the ULONG can be represented correctly in a long.
if (type == IVL_EX_ULONG) {
unsigned long val = ivl_expr_uvalue(expr);
if (val > static_cast<unsigned>(numeric_limits<long>::max())) {
return false;
}
return true;
}
// Check to see if the number actually fits in a long.
unsigned nbits = ivl_expr_width(expr);
if (nbits >= 8*sizeof(long)) {
const char*bits = ivl_expr_bits(expr);
char pad_bit = bits[nbits-1];
for (unsigned idx = 8*sizeof(long); idx < nbits; idx++) {
if (bits[idx] != pad_bit) return false;
}
}
return true;
}
/*