2001-03-19 02:20:46 +01:00
|
|
|
/*
|
2013-04-15 20:53:07 +02:00
|
|
|
* Copyright (c) 2001-2013 Stephen Williams (steve@icarus.com)
|
2001-03-19 02:20:46 +01:00
|
|
|
*
|
|
|
|
|
* 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
|
2012-08-29 03:41:23 +02:00
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
2001-03-19 02:20:46 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
# include "vvp_priv.h"
|
2001-04-18 07:12:03 +02:00
|
|
|
# include <string.h>
|
2001-03-21 02:49:43 +01:00
|
|
|
# include <assert.h>
|
2001-09-15 20:27:04 +02:00
|
|
|
# include <stdlib.h>
|
2001-03-21 02:49:43 +01:00
|
|
|
|
2008-09-17 19:44:29 +02:00
|
|
|
#ifdef __MINGW32__ /* MinGW has inconsistent %p output. */
|
|
|
|
|
#define snprintf _snprintf
|
|
|
|
|
#endif
|
|
|
|
|
|
2001-04-18 07:12:03 +02:00
|
|
|
static int show_statement(ivl_statement_t net, ivl_scope_t sscope);
|
2001-03-19 02:20:46 +01:00
|
|
|
|
2001-05-17 06:37:02 +02:00
|
|
|
unsigned local_count = 0;
|
|
|
|
|
unsigned thread_count = 0;
|
2001-03-22 06:06:21 +01:00
|
|
|
|
2011-11-29 00:29:53 +01:00
|
|
|
unsigned transient_id = 0;
|
2002-05-27 02:08:45 +02:00
|
|
|
|
2001-03-19 02:20:46 +01:00
|
|
|
/*
|
|
|
|
|
* This file includes the code needed to generate VVP code for
|
|
|
|
|
* processes. Scopes are already declared, we generate here the
|
|
|
|
|
* executable code for the processes.
|
|
|
|
|
*/
|
|
|
|
|
|
2012-10-23 02:20:43 +02:00
|
|
|
/* Support a non-blocking assignment to a real array word. The real
|
|
|
|
|
value to be written is already in the top of the stack. */
|
2009-09-03 05:04:34 +02:00
|
|
|
static void assign_to_array_r_word(ivl_signal_t lsig, ivl_expr_t word_ix,
|
2012-10-23 02:20:43 +02:00
|
|
|
uint64_t delay,
|
2009-09-03 05:04:34 +02:00
|
|
|
ivl_expr_t dexp, unsigned nevents)
|
|
|
|
|
{
|
|
|
|
|
unsigned skip_assign = transient_id++;
|
|
|
|
|
|
|
|
|
|
/* This code is common to all the different types of array delays. */
|
|
|
|
|
if (number_is_immediate(word_ix, IMM_WID, 0)) {
|
|
|
|
|
assert(! number_is_unknown(word_ix));
|
|
|
|
|
fprintf(vvp_out, " %%ix/load 3, %lu, 0; address\n",
|
|
|
|
|
get_number_immediate(word_ix));
|
|
|
|
|
} else {
|
|
|
|
|
/* Calculate array word index into index register 3 */
|
|
|
|
|
draw_eval_expr_into_integer(word_ix, 3);
|
|
|
|
|
/* Skip assignment if word expression is not defined. */
|
|
|
|
|
fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_assign);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dexp != 0) {
|
|
|
|
|
/* Calculated delay... */
|
|
|
|
|
int delay_index = allocate_word();
|
|
|
|
|
draw_eval_expr_into_integer(dexp, delay_index);
|
2012-10-23 02:20:43 +02:00
|
|
|
fprintf(vvp_out, " %%assign/ar/d v%p, %d;\n", lsig,
|
|
|
|
|
delay_index);
|
2009-09-03 05:04:34 +02:00
|
|
|
clr_word(delay_index);
|
|
|
|
|
} else if (nevents != 0) {
|
|
|
|
|
/* Event control delay... */
|
2012-10-23 02:20:43 +02:00
|
|
|
fprintf(vvp_out, " %%assign/ar/e v%p;\n", lsig);
|
2009-09-03 05:04:34 +02:00
|
|
|
} else {
|
|
|
|
|
/* Constant delay... */
|
|
|
|
|
unsigned long low_d = delay % UINT64_C(0x100000000);
|
|
|
|
|
unsigned long hig_d = delay / UINT64_C(0x100000000);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The %assign can only take a 32 bit delay. For a larger
|
|
|
|
|
* delay we need to put it into an index register.
|
|
|
|
|
*/
|
|
|
|
|
if (hig_d != 0) {
|
|
|
|
|
int delay_index = allocate_word();
|
|
|
|
|
fprintf(vvp_out, " %%ix/load %d, %lu, %lu;\n",
|
|
|
|
|
delay_index, low_d, hig_d);
|
2012-10-23 02:20:43 +02:00
|
|
|
fprintf(vvp_out, " %%assign/ar/d v%p, %d;\n", lsig,
|
|
|
|
|
delay_index);
|
2009-09-03 05:04:34 +02:00
|
|
|
clr_word(delay_index);
|
|
|
|
|
} else {
|
2012-10-23 02:20:43 +02:00
|
|
|
fprintf(vvp_out, " %%assign/ar v%p, %lu;\n",
|
|
|
|
|
lsig, low_d);
|
2009-09-03 05:04:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, "t_%u ;\n", skip_assign);
|
|
|
|
|
if (nevents != 0) fprintf(vvp_out, " %%evctl/c;\n");
|
|
|
|
|
|
|
|
|
|
clear_expression_lookaside();
|
|
|
|
|
}
|
|
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
static void assign_to_array_word(ivl_signal_t lsig, ivl_expr_t word_ix,
|
2009-02-17 01:44:52 +01:00
|
|
|
unsigned bit, uint64_t delay, ivl_expr_t dexp,
|
2008-09-18 22:44:54 +02:00
|
|
|
ivl_expr_t part_off_ex, unsigned width,
|
|
|
|
|
unsigned nevents)
|
2007-01-16 06:44:14 +01:00
|
|
|
{
|
|
|
|
|
unsigned skip_assign = transient_id++;
|
|
|
|
|
|
2009-02-17 01:44:52 +01:00
|
|
|
unsigned long part_off = 0;
|
2008-05-20 20:51:17 +02:00
|
|
|
if (part_off_ex == 0) {
|
|
|
|
|
part_off = 0;
|
2009-02-17 01:44:52 +01:00
|
|
|
} else if (number_is_immediate(part_off_ex, IMM_WID, 0)) {
|
2009-08-25 19:24:50 +02:00
|
|
|
assert(! number_is_unknown(part_off_ex));
|
2008-05-20 20:51:17 +02:00
|
|
|
part_off = get_number_immediate(part_off_ex);
|
|
|
|
|
part_off_ex = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-18 22:44:54 +02:00
|
|
|
/* This code is common to all the different types of array delays. */
|
2009-02-17 01:44:52 +01:00
|
|
|
if (number_is_immediate(word_ix, IMM_WID, 0)) {
|
2009-08-25 19:24:50 +02:00
|
|
|
assert(! number_is_unknown(word_ix));
|
2009-02-17 01:44:52 +01:00
|
|
|
fprintf(vvp_out, " %%ix/load 3, %lu, 0; address\n",
|
2008-09-18 22:44:54 +02:00
|
|
|
get_number_immediate(word_ix));
|
|
|
|
|
} else {
|
|
|
|
|
/* Calculate array word index into index register 3 */
|
|
|
|
|
draw_eval_expr_into_integer(word_ix, 3);
|
|
|
|
|
/* Skip assignment if word expression is not defined. */
|
|
|
|
|
fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_assign);
|
|
|
|
|
}
|
|
|
|
|
/* Store expression width into index word 0 */
|
2009-02-17 01:44:52 +01:00
|
|
|
fprintf(vvp_out, " %%ix/load 0, %u, 0; word width\n", width);
|
2008-09-18 22:44:54 +02:00
|
|
|
if (part_off_ex) {
|
|
|
|
|
draw_eval_expr_into_integer(part_off_ex, 1);
|
|
|
|
|
/* If the index expression has XZ bits, skip the assign. */
|
|
|
|
|
fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_assign);
|
2008-04-08 00:20:16 +02:00
|
|
|
} else {
|
2008-09-18 22:44:54 +02:00
|
|
|
/* Store word part select into index 1 */
|
2009-02-17 01:44:52 +01:00
|
|
|
fprintf(vvp_out, " %%ix/load 1, %lu, 0; part off\n", part_off);
|
2008-09-18 22:44:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dexp != 0) {
|
2008-04-08 00:20:16 +02:00
|
|
|
/* Calculated delay... */
|
|
|
|
|
int delay_index = allocate_word();
|
|
|
|
|
draw_eval_expr_into_integer(dexp, delay_index);
|
|
|
|
|
fprintf(vvp_out, " %%assign/av/d v%p, %d, %u;\n", lsig,
|
2008-09-18 22:44:54 +02:00
|
|
|
delay_index, bit);
|
|
|
|
|
clr_word(delay_index);
|
|
|
|
|
} else if (nevents != 0) {
|
|
|
|
|
/* Event control delay... */
|
|
|
|
|
fprintf(vvp_out, " %%assign/av/e v%p, %u;\n", lsig, bit);
|
|
|
|
|
} else {
|
|
|
|
|
/* Constant delay... */
|
2009-02-17 01:44:52 +01:00
|
|
|
unsigned long low_d = delay % UINT64_C(0x100000000);
|
|
|
|
|
unsigned long hig_d = delay / UINT64_C(0x100000000);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The %assign can only take a 32 bit delay. For a larger
|
|
|
|
|
* delay we need to put it into an index register.
|
|
|
|
|
*/
|
|
|
|
|
if (hig_d != 0) {
|
|
|
|
|
int delay_index = allocate_word();
|
|
|
|
|
fprintf(vvp_out, " %%ix/load %d, %lu, %lu;\n",
|
|
|
|
|
delay_index, low_d, hig_d);
|
|
|
|
|
fprintf(vvp_out, " %%assign/av/d v%p, %d, %u;\n", lsig,
|
|
|
|
|
delay_index, bit);
|
|
|
|
|
clr_word(delay_index);
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(vvp_out, " %%assign/av v%p, %lu, %u;\n",
|
|
|
|
|
lsig, low_d, bit);
|
|
|
|
|
}
|
2008-04-08 00:20:16 +02:00
|
|
|
}
|
2007-01-16 06:44:14 +01:00
|
|
|
|
2008-05-20 20:51:17 +02:00
|
|
|
fprintf(vvp_out, "t_%u ;\n", skip_assign);
|
2008-09-18 22:44:54 +02:00
|
|
|
if (nevents != 0) fprintf(vvp_out, " %%evctl/c;\n");
|
2008-05-20 20:51:17 +02:00
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
clear_expression_lookaside();
|
|
|
|
|
}
|
|
|
|
|
|
2005-06-14 03:44:09 +02:00
|
|
|
static void assign_to_lvector(ivl_lval_t lval, unsigned bit,
|
2009-02-17 01:44:52 +01:00
|
|
|
uint64_t delay, ivl_expr_t dexp,
|
2008-09-18 01:47:20 +02:00
|
|
|
unsigned width, unsigned nevents)
|
2002-11-08 06:00:31 +01:00
|
|
|
{
|
|
|
|
|
ivl_signal_t sig = ivl_lval_sig(lval);
|
2006-02-02 03:43:57 +01:00
|
|
|
ivl_expr_t part_off_ex = ivl_lval_part_off(lval);
|
2009-02-17 01:44:52 +01:00
|
|
|
unsigned long part_off = 0;
|
2006-02-02 03:43:57 +01:00
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
ivl_expr_t word_ix = ivl_lval_idx(lval);
|
2008-05-17 01:33:04 +02:00
|
|
|
const unsigned long use_word = 0;
|
2007-01-16 06:44:14 +01:00
|
|
|
|
2008-07-17 00:49:33 +02:00
|
|
|
if (ivl_signal_dimensions(sig) > 0) {
|
2007-01-16 06:44:14 +01:00
|
|
|
assert(word_ix);
|
2008-09-18 22:44:54 +02:00
|
|
|
assign_to_array_word(sig, word_ix, bit, delay, dexp, part_off_ex,
|
|
|
|
|
width, nevents);
|
2008-05-17 01:33:04 +02:00
|
|
|
return;
|
2007-01-16 06:44:14 +01:00
|
|
|
}
|
|
|
|
|
|
2006-02-02 03:43:57 +01:00
|
|
|
if (part_off_ex == 0) {
|
|
|
|
|
part_off = 0;
|
2009-02-17 01:44:52 +01:00
|
|
|
} else if (number_is_immediate(part_off_ex, IMM_WID, 0)) {
|
2009-08-25 19:24:50 +02:00
|
|
|
assert(! number_is_unknown(part_off_ex));
|
2006-02-02 03:43:57 +01:00
|
|
|
part_off = get_number_immediate(part_off_ex);
|
2006-04-16 02:15:43 +02:00
|
|
|
part_off_ex = 0;
|
2006-02-02 03:43:57 +01:00
|
|
|
}
|
2002-11-08 06:00:31 +01:00
|
|
|
|
2006-04-16 02:15:43 +02:00
|
|
|
if (ivl_lval_mux(lval))
|
|
|
|
|
part_off_ex = ivl_lval_mux(lval);
|
|
|
|
|
|
2009-02-17 01:44:52 +01:00
|
|
|
unsigned long low_d = delay % UINT64_C(0x100000000);
|
|
|
|
|
unsigned long hig_d = delay / UINT64_C(0x100000000);
|
|
|
|
|
|
2006-04-16 02:15:43 +02:00
|
|
|
if (part_off_ex) {
|
2005-05-09 02:38:12 +02:00
|
|
|
unsigned skip_assign = transient_id++;
|
2008-09-18 01:47:20 +02:00
|
|
|
if (dexp != 0) {
|
|
|
|
|
/* Calculated delay... */
|
|
|
|
|
int delay_index = allocate_word();
|
|
|
|
|
draw_eval_expr_into_integer(dexp, delay_index);
|
2008-01-16 00:25:27 +01:00
|
|
|
draw_eval_expr_into_integer(part_off_ex, 1);
|
|
|
|
|
/* If the index expression has XZ bits, skip the assign. */
|
|
|
|
|
fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_assign);
|
2009-02-17 01:44:52 +01:00
|
|
|
fprintf(vvp_out, " %%ix/load 0, %u, 0;\n", width);
|
|
|
|
|
fprintf(vvp_out, " %%assign/v0/x1/d v%p_%lu, %d, %u;\n",
|
2008-09-18 01:47:20 +02:00
|
|
|
sig, use_word, delay_index, bit);
|
|
|
|
|
fprintf(vvp_out, "t_%u ;\n", skip_assign);
|
|
|
|
|
clr_word(delay_index);
|
|
|
|
|
} else if (nevents != 0) {
|
|
|
|
|
/* Event control delay... */
|
|
|
|
|
draw_eval_expr_into_integer(part_off_ex, 1);
|
|
|
|
|
/* If the index expression has XZ bits, skip the assign. */
|
|
|
|
|
fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_assign);
|
2009-02-17 01:44:52 +01:00
|
|
|
fprintf(vvp_out, " %%ix/load 0, %u, 0;\n", width);
|
2008-09-18 01:47:20 +02:00
|
|
|
fprintf(vvp_out, " %%assign/v0/x1/e v%p_%lu, %u;\n",
|
|
|
|
|
sig, use_word, bit);
|
2008-01-16 00:25:27 +01:00
|
|
|
fprintf(vvp_out, "t_%u ;\n", skip_assign);
|
2008-09-18 02:49:45 +02:00
|
|
|
fprintf(vvp_out, " %%evctl/c;\n");
|
2008-01-16 00:25:27 +01:00
|
|
|
} else {
|
2008-09-18 01:47:20 +02:00
|
|
|
/* Constant delay... */
|
2008-01-16 00:25:27 +01:00
|
|
|
draw_eval_expr_into_integer(part_off_ex, 1);
|
|
|
|
|
/* If the index expression has XZ bits, skip the assign. */
|
|
|
|
|
fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_assign);
|
2009-02-17 01:44:52 +01:00
|
|
|
fprintf(vvp_out, " %%ix/load 0, %u, 0;\n", width);
|
|
|
|
|
/*
|
|
|
|
|
* The %assign can only take a 32 bit delay. For a larger
|
|
|
|
|
* delay we need to put it into an index register.
|
|
|
|
|
*/
|
|
|
|
|
if (hig_d != 0) {
|
|
|
|
|
int delay_index = allocate_word();
|
|
|
|
|
fprintf(vvp_out, " %%ix/load %d, %lu, %lu;\n",
|
|
|
|
|
delay_index, low_d, hig_d);
|
|
|
|
|
fprintf(vvp_out,
|
|
|
|
|
" %%assign/v0/x1/d v%p_%lu, %d, %u;\n",
|
|
|
|
|
sig, use_word, delay_index, bit);
|
|
|
|
|
clr_word(delay_index);
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(vvp_out,
|
|
|
|
|
" %%assign/v0/x1 v%p_%lu, %lu, %u;\n",
|
|
|
|
|
sig, use_word, low_d, bit);
|
|
|
|
|
}
|
2008-01-16 00:25:27 +01:00
|
|
|
fprintf(vvp_out, "t_%u ;\n", skip_assign);
|
|
|
|
|
}
|
2005-05-09 02:38:12 +02:00
|
|
|
|
2005-05-24 04:31:18 +02:00
|
|
|
} else if (part_off>0 || ivl_lval_width(lval)!=ivl_signal_width(sig)) {
|
|
|
|
|
/* There is no mux expression, but a constant part
|
|
|
|
|
offset. Load that into index x1 and generate a
|
|
|
|
|
single-bit set instruction. */
|
|
|
|
|
assert(ivl_lval_width(lval) == width);
|
|
|
|
|
|
2008-09-18 01:47:20 +02:00
|
|
|
if (dexp != 0) {
|
2006-10-05 03:23:53 +02:00
|
|
|
/* Calculated delay... */
|
|
|
|
|
int delay_index = allocate_word();
|
|
|
|
|
draw_eval_expr_into_integer(dexp, delay_index);
|
2009-02-17 01:44:52 +01:00
|
|
|
fprintf(vvp_out, " %%ix/load 0, %u, 0;\n", width);
|
|
|
|
|
fprintf(vvp_out, " %%ix/load 1, %lu, 0;\n", part_off);
|
2008-04-08 00:20:16 +02:00
|
|
|
fprintf(vvp_out, " %%assign/v0/x1/d v%p_%lu, %d, %u;\n",
|
2007-01-16 06:44:14 +01:00
|
|
|
sig, use_word, delay_index, bit);
|
2006-10-05 03:23:53 +02:00
|
|
|
clr_word(delay_index);
|
2008-09-18 01:47:20 +02:00
|
|
|
} else if (nevents != 0) {
|
|
|
|
|
/* Event control delay... */
|
2009-02-17 01:44:52 +01:00
|
|
|
fprintf(vvp_out, " %%ix/load 0, %u, 0;\n", width);
|
|
|
|
|
fprintf(vvp_out, " %%ix/load 1, %lu, 0;\n", part_off);
|
2008-09-18 01:47:20 +02:00
|
|
|
fprintf(vvp_out, " %%assign/v0/x1/e v%p_%lu, %u;\n",
|
|
|
|
|
sig, use_word, bit);
|
|
|
|
|
} else {
|
|
|
|
|
/* Constant delay... */
|
2009-02-17 01:44:52 +01:00
|
|
|
fprintf(vvp_out, " %%ix/load 0, %u, 0;\n", width);
|
|
|
|
|
fprintf(vvp_out, " %%ix/load 1, %lu, 0;\n", part_off);
|
|
|
|
|
/*
|
|
|
|
|
* The %assign can only take a 32 bit delay. For a larger
|
|
|
|
|
* delay we need to put it into an index register.
|
|
|
|
|
*/
|
|
|
|
|
if (hig_d != 0) {
|
|
|
|
|
int delay_index = allocate_word();
|
|
|
|
|
fprintf(vvp_out, " %%ix/load %d, %lu, %lu;\n",
|
|
|
|
|
delay_index, low_d, hig_d);
|
|
|
|
|
fprintf(vvp_out,
|
|
|
|
|
" %%assign/v0/x1/d v%p_%lu, %d, %u;\n",
|
|
|
|
|
sig, use_word, delay_index, bit);
|
|
|
|
|
clr_word(delay_index);
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(vvp_out,
|
|
|
|
|
" %%assign/v0/x1 v%p_%lu, %lu, %u;\n",
|
|
|
|
|
sig, use_word, low_d, bit);
|
|
|
|
|
}
|
2006-10-05 03:23:53 +02:00
|
|
|
}
|
2005-05-24 04:31:18 +02:00
|
|
|
|
2005-06-14 03:44:09 +02:00
|
|
|
} else if (dexp != 0) {
|
2008-09-18 01:47:20 +02:00
|
|
|
/* Calculated delay... */
|
2009-02-17 01:44:52 +01:00
|
|
|
int delay_index = allocate_word();
|
|
|
|
|
draw_eval_expr_into_integer(dexp, delay_index);
|
|
|
|
|
fprintf(vvp_out, " %%ix/load 0, %u, 0;\n", width);
|
|
|
|
|
fprintf(vvp_out, " %%assign/v0/d v%p_%lu, %d, %u;\n",
|
|
|
|
|
sig, use_word, delay_index, bit);
|
|
|
|
|
clr_word(delay_index);
|
2008-09-18 01:47:20 +02:00
|
|
|
} else if (nevents != 0) {
|
|
|
|
|
/* Event control delay... */
|
2009-02-17 01:44:52 +01:00
|
|
|
fprintf(vvp_out, " %%ix/load 0, %u, 0;\n", width);
|
2008-09-18 01:47:20 +02:00
|
|
|
fprintf(vvp_out, " %%assign/v0/e v%p_%lu, %u;\n",
|
|
|
|
|
sig, use_word, bit);
|
2005-05-07 05:16:31 +02:00
|
|
|
} else {
|
2008-09-18 01:47:20 +02:00
|
|
|
/* Constant delay... */
|
2009-02-17 01:44:52 +01:00
|
|
|
fprintf(vvp_out, " %%ix/load 0, %u, 0;\n", width);
|
|
|
|
|
/*
|
|
|
|
|
* The %assign can only take a 32 bit delay. For a larger
|
|
|
|
|
* delay we need to put it into an index register.
|
|
|
|
|
*/
|
|
|
|
|
if (hig_d != 0) {
|
|
|
|
|
int delay_index = allocate_word();
|
|
|
|
|
fprintf(vvp_out, " %%ix/load %d, %lu, %lu;\n",
|
|
|
|
|
delay_index, low_d, hig_d);
|
|
|
|
|
fprintf(vvp_out, " %%assign/v0/d v%p_%lu, %d, %u;\n",
|
|
|
|
|
sig, use_word, delay_index, bit);
|
|
|
|
|
clr_word(delay_index);
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(vvp_out, " %%assign/v0 v%p_%lu, %lu, %u;\n",
|
|
|
|
|
sig, use_word, low_d, bit);
|
|
|
|
|
}
|
2005-05-07 05:16:31 +02:00
|
|
|
}
|
2002-11-08 06:00:31 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-28 04:20:16 +01:00
|
|
|
/*
|
|
|
|
|
* Routine to insert statement tracing information into the output stream
|
|
|
|
|
* when requested by the user (compiler).
|
|
|
|
|
*/
|
2011-11-29 00:29:53 +01:00
|
|
|
void show_stmt_file_line(ivl_statement_t net, const char* desc)
|
2011-02-28 04:20:16 +01:00
|
|
|
{
|
|
|
|
|
if (show_file_line) {
|
|
|
|
|
/* If the line number is not zero then the file should also
|
|
|
|
|
* be set. It's safe to skip the assert during debug, but
|
|
|
|
|
* the assert represents missing file/line information that
|
|
|
|
|
* should be reported/fixed. */
|
|
|
|
|
unsigned lineno = ivl_stmt_lineno(net);
|
|
|
|
|
assert(lineno);
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, " %%file_line %d %u \"%s\";\n",
|
2011-02-28 04:20:16 +01:00
|
|
|
ivl_file_table_index(ivl_stmt_file(net)), lineno, desc);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-27 01:54:13 +02:00
|
|
|
static int show_stmt_alloc(ivl_statement_t net)
|
|
|
|
|
{
|
|
|
|
|
ivl_scope_t scope = ivl_stmt_call(net);
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, " %%alloc S_%p;\n", scope);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2004-05-19 05:25:42 +02:00
|
|
|
/*
|
|
|
|
|
* This function handles the case of non-blocking assign to word
|
|
|
|
|
* variables such as real, i.e:
|
|
|
|
|
*
|
2005-01-28 20:39:03 +01:00
|
|
|
* real foo;
|
2004-05-19 05:25:42 +02:00
|
|
|
* foo <= 1.0;
|
|
|
|
|
*
|
|
|
|
|
* In this case we know (by Verilog syntax) that there is only exactly
|
|
|
|
|
* 1 l-value, the target identifier, so it should be relatively easy.
|
|
|
|
|
*/
|
2005-07-11 18:56:50 +02:00
|
|
|
static int show_stmt_assign_nb_real(ivl_statement_t net)
|
2004-05-19 05:25:42 +02:00
|
|
|
{
|
|
|
|
|
ivl_lval_t lval;
|
2005-07-11 18:56:50 +02:00
|
|
|
ivl_signal_t sig;
|
2004-05-19 05:25:42 +02:00
|
|
|
ivl_expr_t rval = ivl_stmt_rval(net);
|
|
|
|
|
ivl_expr_t del = ivl_stmt_delay_expr(net);
|
2007-01-16 06:44:14 +01:00
|
|
|
/* variables for the selection of word from an array. */
|
|
|
|
|
unsigned long use_word = 0;
|
2009-02-17 01:44:52 +01:00
|
|
|
uint64_t delay = 0;
|
2008-09-11 04:37:11 +02:00
|
|
|
unsigned nevents = ivl_stmt_nevent(net);
|
2004-05-19 05:25:42 +02:00
|
|
|
|
|
|
|
|
/* Must be exactly 1 l-value. */
|
|
|
|
|
assert(ivl_stmt_lvals(net) == 1);
|
|
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
lval = ivl_stmt_lval(net, 0);
|
|
|
|
|
sig = ivl_lval_sig(lval);
|
|
|
|
|
assert(sig);
|
2008-09-04 18:41:51 +02:00
|
|
|
|
2009-02-17 01:44:52 +01:00
|
|
|
if (del && (ivl_expr_type(del) == IVL_EX_DELAY)) {
|
|
|
|
|
assert(number_is_immediate(del, 64, 0));
|
|
|
|
|
delay = ivl_expr_delay_val(del);
|
2004-05-19 05:25:42 +02:00
|
|
|
del = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Evaluate the r-value */
|
2012-10-23 02:20:43 +02:00
|
|
|
draw_eval_real(rval);
|
2004-05-19 05:25:42 +02:00
|
|
|
|
2009-09-03 05:04:34 +02:00
|
|
|
if (ivl_signal_dimensions(sig) > 0) {
|
2010-04-14 06:29:15 +02:00
|
|
|
ivl_expr_t word_ix = ivl_lval_idx(lval);
|
2009-09-03 05:04:34 +02:00
|
|
|
assert(word_ix);
|
2012-10-23 02:20:43 +02:00
|
|
|
assign_to_array_r_word(sig, word_ix, delay, del, nevents);
|
2009-09-03 05:04:34 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-09 04:49:52 +02:00
|
|
|
/* We need to calculate the delay expression. */
|
|
|
|
|
if (del) {
|
2008-09-18 01:47:20 +02:00
|
|
|
assert(nevents == 0);
|
2008-09-09 04:49:52 +02:00
|
|
|
int delay_index = allocate_word();
|
|
|
|
|
draw_eval_expr_into_integer(del, delay_index);
|
2012-10-23 02:20:43 +02:00
|
|
|
fprintf(vvp_out, " %%assign/wr/d v%p_%lu, %d;\n",
|
|
|
|
|
sig, use_word, delay_index);
|
2008-09-09 04:49:52 +02:00
|
|
|
clr_word(delay_index);
|
2008-09-11 04:37:11 +02:00
|
|
|
} else if (nevents) {
|
2012-10-23 02:20:43 +02:00
|
|
|
fprintf(vvp_out, " %%assign/wr/e v%p_%lu;\n",
|
|
|
|
|
sig, use_word);
|
2008-09-09 04:49:52 +02:00
|
|
|
} else {
|
2009-02-17 01:44:52 +01:00
|
|
|
unsigned long low_d = delay % UINT64_C(0x100000000);
|
|
|
|
|
unsigned long hig_d = delay / UINT64_C(0x100000000);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The %assign can only take a 32 bit delay. For a larger
|
|
|
|
|
* delay we need to put it into an index register.
|
|
|
|
|
*/
|
|
|
|
|
if (hig_d != 0) {
|
|
|
|
|
int delay_index = allocate_word();
|
|
|
|
|
fprintf(vvp_out, " %%ix/load %d, %lu, %lu;\n",
|
|
|
|
|
delay_index, low_d, hig_d);
|
2012-10-23 02:20:43 +02:00
|
|
|
fprintf(vvp_out, " %%assign/wr/d v%p_%lu, %d;\n",
|
|
|
|
|
sig, use_word, delay_index);
|
2009-02-17 01:44:52 +01:00
|
|
|
clr_word(delay_index);
|
|
|
|
|
} else {
|
2012-10-23 02:20:43 +02:00
|
|
|
fprintf(vvp_out, " %%assign/wr v%p_%lu, %lu;\n",
|
|
|
|
|
sig, use_word, low_d);
|
2009-02-17 01:44:52 +01:00
|
|
|
}
|
2008-09-09 04:49:52 +02:00
|
|
|
}
|
2004-05-19 05:25:42 +02:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-03 06:50:37 +02:00
|
|
|
static int show_stmt_assign_nb(ivl_statement_t net)
|
|
|
|
|
{
|
|
|
|
|
ivl_lval_t lval;
|
|
|
|
|
ivl_expr_t rval = ivl_stmt_rval(net);
|
2001-04-15 04:58:11 +02:00
|
|
|
ivl_expr_t del = ivl_stmt_delay_expr(net);
|
2005-07-11 18:56:50 +02:00
|
|
|
ivl_signal_t sig;
|
2008-09-11 04:37:11 +02:00
|
|
|
unsigned nevents = ivl_stmt_nevent(net);
|
|
|
|
|
|
2011-02-28 04:20:16 +01:00
|
|
|
show_stmt_file_line(net, "Nonblocking assignment.");
|
|
|
|
|
|
2008-09-30 03:06:47 +02:00
|
|
|
/* If we have an event control build the control structure. */
|
2008-09-11 04:37:11 +02:00
|
|
|
if (nevents) {
|
|
|
|
|
assert(del == 0);
|
|
|
|
|
|
|
|
|
|
ivl_expr_t cnt = ivl_stmt_cond_expr(net);
|
|
|
|
|
unsigned long count = 1;
|
|
|
|
|
if (cnt && (ivl_expr_type(cnt) == IVL_EX_ULONG)) {
|
2008-10-07 01:22:34 +02:00
|
|
|
assert(number_is_immediate(cnt, IMM_WID, 0));
|
2008-09-11 04:37:11 +02:00
|
|
|
count = ivl_expr_uvalue(cnt);
|
|
|
|
|
cnt = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char name[256];
|
|
|
|
|
if (nevents == 1) {
|
|
|
|
|
ivl_event_t ev = ivl_stmt_events(net, 0);
|
|
|
|
|
snprintf(name, sizeof(name), "E_%p", ev);
|
|
|
|
|
} else {
|
|
|
|
|
unsigned idx;
|
|
|
|
|
static unsigned int cascade_counter = 0;
|
|
|
|
|
ivl_event_t ev = ivl_stmt_events(net, 0);
|
|
|
|
|
fprintf(vvp_out, "Eassign_%u .event/or E_%p",
|
|
|
|
|
cascade_counter, ev);
|
|
|
|
|
|
|
|
|
|
for (idx = 1; idx < nevents; idx += 1) {
|
|
|
|
|
ev = ivl_stmt_events(net, idx);
|
|
|
|
|
fprintf(vvp_out, ", E_%p", ev);
|
|
|
|
|
}
|
|
|
|
|
snprintf(name, sizeof(name), "Eassign_%u", cascade_counter);
|
|
|
|
|
cascade_counter += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cnt) {
|
|
|
|
|
int count_index = allocate_word();
|
2010-09-27 20:03:43 +02:00
|
|
|
const char*type = ivl_expr_signed(cnt) ? "/s" : "";
|
2008-09-11 04:37:11 +02:00
|
|
|
draw_eval_expr_into_integer(cnt, count_index);
|
|
|
|
|
fprintf(vvp_out, " %%evctl%s %s, %d;\n", type, name,
|
|
|
|
|
count_index);
|
|
|
|
|
clr_word(count_index);
|
2010-07-24 04:24:10 +02:00
|
|
|
} else {
|
2008-09-11 04:37:11 +02:00
|
|
|
fprintf(vvp_out, " %%evctl/i %s, %lu;\n", name, count);
|
|
|
|
|
}
|
2008-09-18 01:47:20 +02:00
|
|
|
} else {
|
|
|
|
|
assert(ivl_stmt_cond_expr(net) == 0);
|
2008-09-11 04:37:11 +02:00
|
|
|
}
|
2001-04-15 04:58:11 +02:00
|
|
|
|
2009-02-17 01:44:52 +01:00
|
|
|
uint64_t delay = 0;
|
2004-05-19 05:25:42 +02:00
|
|
|
|
2005-07-11 18:56:50 +02:00
|
|
|
/* Detect special cases that are handled elsewhere. */
|
|
|
|
|
lval = ivl_stmt_lval(net,0);
|
|
|
|
|
if ((sig = ivl_lval_sig(lval))) {
|
|
|
|
|
switch (ivl_signal_data_type(sig)) {
|
|
|
|
|
case IVL_VT_REAL:
|
|
|
|
|
return show_stmt_assign_nb_real(net);
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2004-05-19 05:25:42 +02:00
|
|
|
}
|
|
|
|
|
|
2009-02-17 01:44:52 +01:00
|
|
|
if (del && (ivl_expr_type(del) == IVL_EX_DELAY)) {
|
|
|
|
|
assert(number_is_immediate(del, 64, 0));
|
|
|
|
|
delay = ivl_expr_delay_val(del);
|
2002-04-22 00:31:02 +02:00
|
|
|
del = 0;
|
2001-04-15 04:58:11 +02:00
|
|
|
}
|
2001-04-03 06:50:37 +02:00
|
|
|
|
2001-08-26 01:50:02 +02:00
|
|
|
|
2010-03-13 23:27:20 +01:00
|
|
|
{ struct vector_info res;
|
|
|
|
|
unsigned wid;
|
2001-08-26 01:50:02 +02:00
|
|
|
unsigned lidx;
|
|
|
|
|
unsigned cur_rbit = 0;
|
2010-03-13 23:27:20 +01:00
|
|
|
/* Handle the special case that the expression is a real
|
|
|
|
|
value. Evaluate the real expression, then convert the
|
2010-03-14 00:27:48 +01:00
|
|
|
result to a vector. */
|
2010-03-13 23:27:20 +01:00
|
|
|
if (ivl_expr_value(rval) == IVL_VT_REAL) {
|
2012-10-23 02:20:43 +02:00
|
|
|
draw_eval_real(rval);
|
2010-03-13 23:27:20 +01:00
|
|
|
/* This is the accumulated with of the l-value of the
|
|
|
|
|
assignment. */
|
|
|
|
|
wid = ivl_stmt_lwidth(net);
|
|
|
|
|
|
|
|
|
|
res.base = allocate_vector(wid);
|
|
|
|
|
res.wid = wid;
|
|
|
|
|
|
|
|
|
|
if (res.base == 0) {
|
|
|
|
|
fprintf(stderr, "%s:%u: vvp.tgt error: "
|
|
|
|
|
"Unable to allocate %u thread bits for "
|
|
|
|
|
"r-value expression.\n", ivl_expr_file(rval),
|
|
|
|
|
ivl_expr_lineno(rval), wid);
|
|
|
|
|
vvp_errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-23 02:20:43 +02:00
|
|
|
fprintf(vvp_out, " %%cvt/vr %u, %u;\n",
|
|
|
|
|
res.base, res.wid);
|
2010-03-13 23:27:20 +01:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
res = draw_eval_expr(rval, 0);
|
|
|
|
|
wid = res.wid;
|
|
|
|
|
}
|
2001-08-26 01:50:02 +02:00
|
|
|
|
|
|
|
|
for (lidx = 0 ; lidx < ivl_stmt_lvals(net) ; lidx += 1) {
|
|
|
|
|
unsigned bit_limit = wid - cur_rbit;
|
2008-09-30 03:06:47 +02:00
|
|
|
unsigned bidx;
|
|
|
|
|
|
2001-08-26 01:50:02 +02:00
|
|
|
lval = ivl_stmt_lval(net, lidx);
|
|
|
|
|
|
2004-12-11 03:31:25 +01:00
|
|
|
if (bit_limit > ivl_lval_width(lval))
|
|
|
|
|
bit_limit = ivl_lval_width(lval);
|
2001-08-26 01:50:02 +02:00
|
|
|
|
2007-01-17 05:39:18 +01:00
|
|
|
bidx = res.base < 4? res.base : (res.base+cur_rbit);
|
2008-09-18 01:47:20 +02:00
|
|
|
assign_to_lvector(lval, bidx, delay, del, bit_limit, nevents);
|
2005-03-06 18:07:48 +01:00
|
|
|
|
2005-06-15 03:33:33 +02:00
|
|
|
cur_rbit += bit_limit;
|
2002-09-01 02:19:35 +02:00
|
|
|
|
2001-05-09 01:59:33 +02:00
|
|
|
}
|
2001-04-03 06:50:37 +02:00
|
|
|
|
2002-08-31 05:48:50 +02:00
|
|
|
if (res.base > 3)
|
|
|
|
|
clr_vector(res);
|
2001-04-03 06:50:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-21 02:55:46 +02:00
|
|
|
static int show_stmt_block(ivl_statement_t net, ivl_scope_t sscope)
|
|
|
|
|
{
|
|
|
|
|
int rc = 0;
|
|
|
|
|
unsigned idx;
|
|
|
|
|
unsigned cnt = ivl_stmt_block_count(net);
|
|
|
|
|
|
|
|
|
|
for (idx = 0 ; idx < cnt ; idx += 1) {
|
|
|
|
|
rc += show_statement(ivl_stmt_block_stmt(net, idx), sscope);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2002-05-27 02:08:45 +02:00
|
|
|
/*
|
|
|
|
|
* This draws an invocation of a named block. This is a little
|
|
|
|
|
* different because a subscope is created. We do that by creating
|
|
|
|
|
* a thread to deal with this.
|
|
|
|
|
*/
|
|
|
|
|
static int show_stmt_block_named(ivl_statement_t net, ivl_scope_t scope)
|
|
|
|
|
{
|
|
|
|
|
int rc;
|
2012-08-31 21:35:26 +02:00
|
|
|
unsigned out_id, sub_id;
|
2002-05-27 02:08:45 +02:00
|
|
|
ivl_scope_t subscope = ivl_stmt_block_scope(net);
|
|
|
|
|
|
|
|
|
|
out_id = transient_id++;
|
|
|
|
|
sub_id = transient_id++;
|
|
|
|
|
|
2003-03-25 03:15:48 +01:00
|
|
|
fprintf(vvp_out, " %%fork t_%u, S_%p;\n",
|
|
|
|
|
sub_id, subscope);
|
2002-05-27 02:08:45 +02:00
|
|
|
fprintf(vvp_out, " %%jmp t_%u;\n", out_id);
|
2010-03-17 01:11:56 +01:00
|
|
|
/* Change the compiling scope to be the named blocks scope. */
|
|
|
|
|
fprintf(vvp_out, " .scope S_%p;\n", subscope);
|
2002-05-27 02:08:45 +02:00
|
|
|
fprintf(vvp_out, "t_%u ;\n", sub_id);
|
|
|
|
|
|
2007-02-02 05:48:49 +01:00
|
|
|
/* The statement within the fork is in a new thread, so no
|
|
|
|
|
expression lookaside is valid. */
|
|
|
|
|
clear_expression_lookaside();
|
|
|
|
|
|
2002-05-27 02:08:45 +02:00
|
|
|
rc = show_stmt_block(net, subscope);
|
|
|
|
|
fprintf(vvp_out, " %%end;\n");
|
2010-03-17 01:11:56 +01:00
|
|
|
/* Return to the previous scope. */
|
|
|
|
|
fprintf(vvp_out, " .scope S_%p;\n", scope);
|
2002-05-27 02:08:45 +02:00
|
|
|
|
|
|
|
|
fprintf(vvp_out, "t_%u %%join;\n", out_id);
|
2002-09-27 18:33:34 +02:00
|
|
|
clear_expression_lookaside();
|
2002-05-27 02:08:45 +02:00
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2001-04-18 07:12:03 +02:00
|
|
|
static int show_stmt_case(ivl_statement_t net, ivl_scope_t sscope)
|
2001-03-31 19:36:38 +02:00
|
|
|
{
|
2008-04-16 06:51:03 +02:00
|
|
|
int rc = 0;
|
2010-05-28 05:09:32 +02:00
|
|
|
ivl_expr_t expr = ivl_stmt_cond_expr(net);
|
|
|
|
|
struct vector_info cond = draw_eval_expr(expr, 0);
|
2001-03-31 19:36:38 +02:00
|
|
|
unsigned count = ivl_stmt_case_count(net);
|
|
|
|
|
|
|
|
|
|
unsigned local_base = local_count;
|
|
|
|
|
|
|
|
|
|
unsigned idx, default_case;
|
|
|
|
|
|
2011-02-28 04:20:16 +01:00
|
|
|
show_stmt_file_line(net, "Case statement.");
|
|
|
|
|
|
2001-03-31 19:36:38 +02:00
|
|
|
local_count += count + 1;
|
|
|
|
|
|
|
|
|
|
/* First draw the branch table. All the non-default cases
|
|
|
|
|
generate a branch out of here, to the code that implements
|
|
|
|
|
the case. The default will fall through all the tests. */
|
|
|
|
|
default_case = count;
|
|
|
|
|
|
|
|
|
|
for (idx = 0 ; idx < count ; idx += 1) {
|
|
|
|
|
ivl_expr_t cex = ivl_stmt_case_expr(net, idx);
|
|
|
|
|
struct vector_info cvec;
|
|
|
|
|
|
|
|
|
|
if (cex == 0) {
|
|
|
|
|
default_case = idx;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2002-06-02 20:57:17 +02:00
|
|
|
/* Is the guard expression something I can pass to a
|
|
|
|
|
%cmpi/u instruction? If so, use that instead. */
|
|
|
|
|
|
|
|
|
|
if ((ivl_statement_type(net) == IVL_ST_CASE)
|
|
|
|
|
&& (ivl_expr_type(cex) == IVL_EX_NUMBER)
|
|
|
|
|
&& (! number_is_unknown(cex))
|
2008-06-13 22:32:06 +02:00
|
|
|
&& number_is_immediate(cex, 16, 0)) {
|
2002-06-02 20:57:17 +02:00
|
|
|
|
|
|
|
|
unsigned long imm = get_number_immediate(cex);
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, " %%cmpi/u %u, %lu, %u;\n",
|
|
|
|
|
cond.base, imm, cond.wid);
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, " %%jmp/1 T_%u.%u, 6;\n",
|
2002-06-02 20:57:17 +02:00
|
|
|
thread_count, local_base+idx);
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Oh well, do this case the hard way. */
|
|
|
|
|
|
2005-09-17 03:01:00 +02:00
|
|
|
cvec = draw_eval_expr_wid(cex, cond.wid, STUFF_OK_RO);
|
2001-03-31 21:02:13 +02:00
|
|
|
assert(cvec.wid == cond.wid);
|
2001-03-31 19:36:38 +02:00
|
|
|
|
2001-04-01 06:34:59 +02:00
|
|
|
switch (ivl_statement_type(net)) {
|
2001-03-31 19:36:38 +02:00
|
|
|
|
2001-04-01 06:34:59 +02:00
|
|
|
case IVL_ST_CASE:
|
|
|
|
|
fprintf(vvp_out, " %%cmp/u %u, %u, %u;\n",
|
|
|
|
|
cond.base, cvec.base, cond.wid);
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, " %%jmp/1 T_%u.%u, 6;\n",
|
2001-04-01 06:34:59 +02:00
|
|
|
thread_count, local_base+idx);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IVL_ST_CASEX:
|
|
|
|
|
fprintf(vvp_out, " %%cmp/x %u, %u, %u;\n",
|
|
|
|
|
cond.base, cvec.base, cond.wid);
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, " %%jmp/1 T_%u.%u, 4;\n",
|
2001-04-01 06:34:59 +02:00
|
|
|
thread_count, local_base+idx);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IVL_ST_CASEZ:
|
|
|
|
|
fprintf(vvp_out, " %%cmp/z %u, %u, %u;\n",
|
|
|
|
|
cond.base, cvec.base, cond.wid);
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, " %%jmp/1 T_%u.%u, 4;\n",
|
2001-04-01 06:34:59 +02:00
|
|
|
thread_count, local_base+idx);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-03-31 19:36:38 +02:00
|
|
|
/* Done with the case expression */
|
|
|
|
|
clr_vector(cvec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Done with the condition expression */
|
|
|
|
|
clr_vector(cond);
|
|
|
|
|
|
|
|
|
|
/* Emit code for the default case. */
|
|
|
|
|
if (default_case < count) {
|
|
|
|
|
ivl_statement_t cst = ivl_stmt_case_stmt(net, default_case);
|
2008-04-16 06:51:03 +02:00
|
|
|
rc += show_statement(cst, sscope);
|
2001-03-31 19:36:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Jump to the out of the case. */
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count,
|
2001-03-31 19:36:38 +02:00
|
|
|
local_base+count);
|
|
|
|
|
|
|
|
|
|
for (idx = 0 ; idx < count ; idx += 1) {
|
|
|
|
|
ivl_statement_t cst = ivl_stmt_case_stmt(net, idx);
|
|
|
|
|
|
|
|
|
|
if (idx == default_case)
|
|
|
|
|
continue;
|
|
|
|
|
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, "T_%u.%u ;\n", thread_count, local_base+idx);
|
2002-09-27 22:24:42 +02:00
|
|
|
clear_expression_lookaside();
|
2008-04-16 06:51:03 +02:00
|
|
|
rc += show_statement(cst, sscope);
|
2001-03-31 19:36:38 +02:00
|
|
|
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count,
|
2001-03-31 19:36:38 +02:00
|
|
|
local_base+count);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2002-09-27 18:33:34 +02:00
|
|
|
|
2001-03-31 19:36:38 +02:00
|
|
|
/* The out of the case. */
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, "T_%u.%u ;\n", thread_count, local_base+count);
|
2002-09-27 22:24:42 +02:00
|
|
|
clear_expression_lookaside();
|
2001-03-31 19:36:38 +02:00
|
|
|
|
2008-04-16 06:51:03 +02:00
|
|
|
return rc;
|
2001-03-31 19:36:38 +02:00
|
|
|
}
|
|
|
|
|
|
2003-05-14 07:26:41 +02:00
|
|
|
static int show_stmt_case_r(ivl_statement_t net, ivl_scope_t sscope)
|
|
|
|
|
{
|
2008-04-16 06:51:03 +02:00
|
|
|
int rc = 0;
|
2010-05-28 05:09:32 +02:00
|
|
|
ivl_expr_t expr = ivl_stmt_cond_expr(net);
|
2003-05-14 07:26:41 +02:00
|
|
|
unsigned count = ivl_stmt_case_count(net);
|
|
|
|
|
|
|
|
|
|
unsigned local_base = local_count;
|
|
|
|
|
|
|
|
|
|
unsigned idx, default_case;
|
|
|
|
|
|
2011-02-28 04:20:16 +01:00
|
|
|
show_stmt_file_line(net, "Case statement.");
|
2003-05-14 07:26:41 +02:00
|
|
|
|
2012-10-23 02:20:43 +02:00
|
|
|
/* Build the reference value into the top of the stack. All
|
|
|
|
|
the case comparisons will make duplicates of this value in
|
|
|
|
|
order to do their tests. */
|
|
|
|
|
draw_eval_real(expr);
|
|
|
|
|
|
2011-02-28 04:20:16 +01:00
|
|
|
local_count += count + 1;
|
2003-05-14 07:26:41 +02:00
|
|
|
|
|
|
|
|
/* First draw the branch table. All the non-default cases
|
|
|
|
|
generate a branch out of here, to the code that implements
|
|
|
|
|
the case. The default will fall through all the tests. */
|
|
|
|
|
default_case = count;
|
|
|
|
|
|
|
|
|
|
for (idx = 0 ; idx < count ; idx += 1) {
|
|
|
|
|
ivl_expr_t cex = ivl_stmt_case_expr(net, idx);
|
|
|
|
|
|
|
|
|
|
if (cex == 0) {
|
|
|
|
|
default_case = idx;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-23 02:20:43 +02:00
|
|
|
/* The referene value... */
|
|
|
|
|
fprintf(vvp_out, " %%dup/real;\n");
|
|
|
|
|
/* The guard value... */
|
|
|
|
|
draw_eval_real(cex);
|
|
|
|
|
/* The comparison. */
|
|
|
|
|
fprintf(vvp_out, " %%cmp/wr;\n");
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, " %%jmp/1 T_%u.%u, 4;\n",
|
2003-05-14 07:26:41 +02:00
|
|
|
thread_count, local_base+idx);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Emit code for the case default. The above jump table will
|
|
|
|
|
fall through to this statement. */
|
|
|
|
|
if (default_case < count) {
|
|
|
|
|
ivl_statement_t cst = ivl_stmt_case_stmt(net, default_case);
|
2008-04-16 06:51:03 +02:00
|
|
|
rc += show_statement(cst, sscope);
|
2003-05-14 07:26:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Jump to the out of the case. */
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count,
|
2003-05-14 07:26:41 +02:00
|
|
|
local_base+count);
|
|
|
|
|
|
|
|
|
|
for (idx = 0 ; idx < count ; idx += 1) {
|
|
|
|
|
ivl_statement_t cst = ivl_stmt_case_stmt(net, idx);
|
|
|
|
|
|
|
|
|
|
if (idx == default_case)
|
|
|
|
|
continue;
|
|
|
|
|
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, "T_%u.%u ;\n", thread_count, local_base+idx);
|
2003-05-14 07:26:41 +02:00
|
|
|
clear_expression_lookaside();
|
2008-04-16 06:51:03 +02:00
|
|
|
rc += show_statement(cst, sscope);
|
2003-05-14 07:26:41 +02:00
|
|
|
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count,
|
2003-05-14 07:26:41 +02:00
|
|
|
local_base+count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The out of the case. */
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, "T_%u.%u ;\n", thread_count, local_base+count);
|
2012-10-23 02:20:43 +02:00
|
|
|
fprintf(vvp_out, " %%pop/real 1;\n");
|
2003-05-14 07:26:41 +02:00
|
|
|
|
2010-04-14 06:29:15 +02:00
|
|
|
return rc;
|
2003-05-14 07:26:41 +02:00
|
|
|
}
|
|
|
|
|
|
2012-10-23 02:20:43 +02:00
|
|
|
/*
|
|
|
|
|
* The real value is already pushed to the top of the real value stack.
|
|
|
|
|
*/
|
|
|
|
|
static void force_real_to_lval(ivl_statement_t net)
|
2008-04-17 01:11:23 +02:00
|
|
|
{
|
|
|
|
|
const char*command_name;
|
2008-09-30 03:06:47 +02:00
|
|
|
ivl_lval_t lval;
|
|
|
|
|
ivl_signal_t lsig;
|
2008-04-17 01:11:23 +02:00
|
|
|
|
|
|
|
|
switch (ivl_statement_type(net)) {
|
|
|
|
|
case IVL_ST_CASSIGN:
|
|
|
|
|
command_name = "%cassign/wr";
|
|
|
|
|
break;
|
|
|
|
|
case IVL_ST_FORCE:
|
|
|
|
|
command_name = "%force/wr";
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
command_name = "ERROR";
|
|
|
|
|
assert(0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(ivl_stmt_lvals(net) == 1);
|
2008-09-30 03:06:47 +02:00
|
|
|
lval = ivl_stmt_lval(net, 0);
|
|
|
|
|
lsig = ivl_lval_sig(lval);
|
2008-04-17 01:11:23 +02:00
|
|
|
|
|
|
|
|
assert(ivl_lval_width(lval) == 1);
|
|
|
|
|
assert(ivl_lval_part_off(lval) == 0);
|
|
|
|
|
assert(ivl_lval_idx(lval) == 0);
|
|
|
|
|
/* L-Value must be a signal: reg or wire */
|
|
|
|
|
assert(lsig != 0);
|
|
|
|
|
|
2012-10-23 02:20:43 +02:00
|
|
|
fprintf(vvp_out, " %s v%p_0;\n", command_name, lsig);
|
2008-04-17 01:11:23 +02:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2004-12-17 05:46:40 +01:00
|
|
|
static void force_vector_to_lval(ivl_statement_t net, struct vector_info rvec)
|
2001-11-01 05:26:57 +01:00
|
|
|
{
|
2004-12-17 05:46:40 +01:00
|
|
|
unsigned lidx;
|
2004-12-11 06:43:30 +01:00
|
|
|
unsigned roff = 0;
|
2001-11-01 05:26:57 +01:00
|
|
|
|
2004-12-17 05:46:40 +01:00
|
|
|
const char*command_name;
|
2005-11-26 18:23:17 +01:00
|
|
|
const char*command_name_x0;
|
2001-11-01 05:26:57 +01:00
|
|
|
|
2004-12-17 05:46:40 +01:00
|
|
|
switch (ivl_statement_type(net)) {
|
|
|
|
|
case IVL_ST_CASSIGN:
|
|
|
|
|
command_name = "%cassign/v";
|
2008-04-10 03:56:42 +02:00
|
|
|
command_name_x0 = "%cassign/x0";
|
2004-12-17 05:46:40 +01:00
|
|
|
break;
|
|
|
|
|
case IVL_ST_FORCE:
|
|
|
|
|
command_name = "%force/v";
|
2005-11-26 18:23:17 +01:00
|
|
|
command_name_x0 = "%force/x0";
|
2004-12-17 05:46:40 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
command_name = "ERROR";
|
2008-01-03 03:16:00 +01:00
|
|
|
command_name_x0 = "ERROR";
|
2004-12-17 05:46:40 +01:00
|
|
|
assert(0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2001-11-01 05:26:57 +01:00
|
|
|
|
2004-12-11 06:43:30 +01:00
|
|
|
for (lidx = 0 ; lidx < ivl_stmt_lvals(net) ; lidx += 1) {
|
|
|
|
|
ivl_lval_t lval = ivl_stmt_lval(net, lidx);
|
|
|
|
|
ivl_signal_t lsig = ivl_lval_sig(lval);
|
2005-11-26 18:23:17 +01:00
|
|
|
|
|
|
|
|
unsigned use_wid = ivl_lval_width(lval);
|
2006-02-02 03:43:57 +01:00
|
|
|
ivl_expr_t part_off_ex = ivl_lval_part_off(lval);
|
2009-02-17 01:44:52 +01:00
|
|
|
unsigned long part_off;
|
2007-01-16 06:44:14 +01:00
|
|
|
ivl_expr_t word_idx = ivl_lval_idx(lval);
|
|
|
|
|
unsigned long use_word = 0;
|
2006-02-02 03:43:57 +01:00
|
|
|
|
|
|
|
|
if (part_off_ex == 0) {
|
|
|
|
|
part_off = 0;
|
|
|
|
|
} else {
|
2009-02-17 01:44:52 +01:00
|
|
|
assert(number_is_immediate(part_off_ex, IMM_WID, 0));
|
2009-08-25 19:24:50 +02:00
|
|
|
assert(! number_is_unknown(part_off_ex));
|
2006-02-02 03:43:57 +01:00
|
|
|
part_off = get_number_immediate(part_off_ex);
|
|
|
|
|
}
|
2004-12-11 03:31:25 +01:00
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
if (word_idx != 0) {
|
2008-10-07 01:22:34 +02:00
|
|
|
assert(number_is_immediate(word_idx, IMM_WID, 0));
|
2009-08-25 19:24:50 +02:00
|
|
|
assert(! number_is_unknown(word_idx));
|
2007-01-16 06:44:14 +01:00
|
|
|
use_word = get_number_immediate(word_idx);
|
|
|
|
|
}
|
|
|
|
|
|
2005-10-12 19:26:01 +02:00
|
|
|
/* L-Value must be a signal: reg or wire */
|
2004-12-11 06:43:30 +01:00
|
|
|
assert(lsig != 0);
|
2005-10-12 19:26:01 +02:00
|
|
|
|
2005-11-26 18:23:17 +01:00
|
|
|
if (part_off != 0 || use_wid != ivl_signal_width(lsig)) {
|
|
|
|
|
|
|
|
|
|
command_name = command_name_x0;
|
2009-02-17 01:44:52 +01:00
|
|
|
fprintf(vvp_out, " %%ix/load 0, %lu, 0;\n", part_off);
|
2004-12-11 03:31:25 +01:00
|
|
|
|
2005-11-26 18:23:17 +01:00
|
|
|
} else {
|
|
|
|
|
/* Do not support bit or part selects of l-values yet. */
|
|
|
|
|
assert(ivl_lval_mux(lval) == 0);
|
|
|
|
|
assert(ivl_lval_width(lval) == ivl_signal_width(lsig));
|
|
|
|
|
|
|
|
|
|
assert((roff + use_wid) <= rvec.wid);
|
|
|
|
|
}
|
2004-12-11 03:31:25 +01:00
|
|
|
|
2008-03-12 00:04:00 +01:00
|
|
|
fprintf(vvp_out, " %s v%p_%lu, %u, %u;\n", command_name,
|
2007-01-16 06:44:14 +01:00
|
|
|
lsig, use_word, rvec.base+roff, use_wid);
|
2004-12-11 06:43:30 +01:00
|
|
|
|
|
|
|
|
if (rvec.base >= 4)
|
|
|
|
|
roff += use_wid;
|
2001-11-01 05:26:57 +01:00
|
|
|
}
|
2004-12-17 05:46:40 +01:00
|
|
|
}
|
|
|
|
|
|
2005-06-02 18:03:47 +02:00
|
|
|
static void force_link_rval(ivl_statement_t net, ivl_expr_t rval)
|
|
|
|
|
{
|
2008-09-30 03:06:47 +02:00
|
|
|
ivl_signal_t rsig;
|
2005-06-02 18:03:47 +02:00
|
|
|
ivl_lval_t lval;
|
|
|
|
|
ivl_signal_t lsig;
|
|
|
|
|
const char*command_name;
|
2008-09-30 03:06:47 +02:00
|
|
|
ivl_expr_t part_off_ex;
|
2005-06-02 18:03:47 +02:00
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
ivl_expr_t lword_idx, rword_idx;
|
|
|
|
|
unsigned long use_lword = 0, use_rword = 0;
|
|
|
|
|
|
2005-06-02 18:03:47 +02:00
|
|
|
if (ivl_expr_type(rval) != IVL_EX_SIGNAL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
switch (ivl_statement_type(net)) {
|
|
|
|
|
case IVL_ST_CASSIGN:
|
|
|
|
|
command_name = "%cassign";
|
|
|
|
|
break;
|
|
|
|
|
case IVL_ST_FORCE:
|
|
|
|
|
command_name = "%force";
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
command_name = "ERROR";
|
|
|
|
|
assert(0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rsig = ivl_expr_signal(rval);
|
|
|
|
|
assert(ivl_stmt_lvals(net) == 1);
|
|
|
|
|
lval = ivl_stmt_lval(net, 0);
|
|
|
|
|
lsig = ivl_lval_sig(lval);
|
|
|
|
|
|
2008-03-12 00:04:00 +01:00
|
|
|
/* We do not currently support driving a signal to a bit or
|
|
|
|
|
* part select (this could give us multiple drivers). */
|
2008-09-30 03:06:47 +02:00
|
|
|
part_off_ex = ivl_lval_part_off(lval);
|
2009-09-18 04:06:40 +02:00
|
|
|
/* This should be verified in force_vector_to_lval() which is called
|
|
|
|
|
* before this procedure. */
|
|
|
|
|
if (part_off_ex) {
|
|
|
|
|
assert(number_is_immediate(part_off_ex, IMM_WID, 0));
|
|
|
|
|
assert(! number_is_unknown(part_off_ex));
|
|
|
|
|
}
|
2008-03-12 00:04:00 +01:00
|
|
|
if (ivl_signal_width(lsig) > ivl_signal_width(rsig) ||
|
|
|
|
|
(part_off_ex && get_number_immediate(part_off_ex) != 0)) {
|
2013-04-18 19:49:56 +02:00
|
|
|
/* Normalize the bit/part select. This also needs to be
|
|
|
|
|
* reworked to support packed arrays. */
|
|
|
|
|
long real_msb = ivl_signal_packed_msb(lsig, 0);
|
|
|
|
|
long real_lsb = ivl_signal_packed_lsb(lsig, 0);
|
2011-10-26 04:00:43 +02:00
|
|
|
long use_wid = ivl_signal_width(rsig);
|
|
|
|
|
long use_lsb, use_msb;
|
2013-04-18 19:49:56 +02:00
|
|
|
if (ivl_signal_packed_dimensions(lsig) > 1) {
|
|
|
|
|
fprintf(stderr, "%s:%u: tgt-vvp sorry: cannot %s part of a "
|
|
|
|
|
"packed array.\n",
|
|
|
|
|
ivl_stmt_file(net), ivl_stmt_lineno(net),
|
|
|
|
|
command_name);
|
|
|
|
|
}
|
2011-10-26 04:00:43 +02:00
|
|
|
if (real_msb >= real_lsb) {
|
|
|
|
|
use_lsb = get_number_immediate(part_off_ex);
|
|
|
|
|
use_lsb += real_lsb;
|
|
|
|
|
use_msb = use_lsb + use_wid - 1;
|
|
|
|
|
} else {
|
|
|
|
|
use_lsb = real_lsb;
|
|
|
|
|
use_lsb -= get_number_immediate(part_off_ex);
|
|
|
|
|
use_msb = use_lsb;
|
|
|
|
|
use_msb -= use_wid - 1;
|
|
|
|
|
}
|
|
|
|
|
fprintf(stderr, "%s:%u: tgt-vvp sorry: cannot %s signal to a ",
|
|
|
|
|
ivl_stmt_file(net), ivl_stmt_lineno(net), command_name);
|
|
|
|
|
if (use_wid > 1) {
|
|
|
|
|
fprintf(stderr, "part select (%s[%lu:%lu]).\n",
|
|
|
|
|
ivl_signal_basename(lsig), use_msb, use_lsb);
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(stderr, "bit select (%s[%lu]).\n",
|
|
|
|
|
ivl_signal_basename(lsig), use_lsb);
|
|
|
|
|
}
|
|
|
|
|
vvp_errors += 1;
|
2008-03-12 00:04:00 +01:00
|
|
|
}
|
|
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
/* At least for now, only handle force to fixed words of an array. */
|
|
|
|
|
if ((lword_idx = ivl_lval_idx(lval)) != 0) {
|
2008-10-07 01:22:34 +02:00
|
|
|
assert(number_is_immediate(lword_idx, IMM_WID, 0));
|
2009-08-25 19:24:50 +02:00
|
|
|
assert(! number_is_unknown(lword_idx));
|
2007-01-16 06:44:14 +01:00
|
|
|
use_lword = get_number_immediate(lword_idx);
|
2011-10-26 04:00:43 +02:00
|
|
|
/* We do not currently support using a word from a variable
|
|
|
|
|
* array as the L-value (Icarus extension). */
|
|
|
|
|
if (ivl_signal_type(lsig) == IVL_SIT_REG) {
|
|
|
|
|
/* Normalize the array access. */
|
|
|
|
|
long real_word = use_lword;
|
|
|
|
|
real_word += ivl_signal_array_base(lsig);
|
|
|
|
|
fprintf(stderr, "%s:%u: tgt-vvp sorry: cannot %s to the "
|
|
|
|
|
"word of a variable array (%s[%ld]).\n",
|
|
|
|
|
ivl_stmt_file(net), ivl_stmt_lineno(net),
|
|
|
|
|
command_name, ivl_signal_basename(lsig), real_word);
|
|
|
|
|
vvp_errors += 1;
|
|
|
|
|
}
|
2007-01-16 06:44:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((rword_idx = ivl_expr_oper1(rval)) != 0) {
|
2011-10-26 04:00:43 +02:00
|
|
|
assert(ivl_signal_dimensions(rsig) != 0);
|
2008-10-07 01:22:34 +02:00
|
|
|
assert(number_is_immediate(rword_idx, IMM_WID, 0));
|
2009-08-25 19:24:50 +02:00
|
|
|
assert(! number_is_unknown(rword_idx));
|
2007-01-16 06:44:14 +01:00
|
|
|
use_rword = get_number_immediate(rword_idx);
|
2011-10-26 04:00:43 +02:00
|
|
|
/* We do not currently support using a word from a variable
|
|
|
|
|
* array as the R-value. */
|
|
|
|
|
if (ivl_signal_type(rsig) == IVL_SIT_REG) {
|
|
|
|
|
/* Normalize the array access. */
|
|
|
|
|
long real_word = use_rword;
|
|
|
|
|
real_word += ivl_signal_array_base(rsig);
|
|
|
|
|
fprintf(stderr, "%s:%u: tgt-vvp sorry: cannot %s from the "
|
|
|
|
|
"word of a variable array (%s[%ld]).\n",
|
|
|
|
|
ivl_expr_file(rval), ivl_expr_lineno(rval),
|
|
|
|
|
command_name, ivl_signal_basename(rsig), real_word);
|
|
|
|
|
vvp_errors += 1;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
assert(ivl_signal_dimensions(rsig) == 0);
|
|
|
|
|
use_rword = 0;
|
2007-01-16 06:44:14 +01:00
|
|
|
}
|
|
|
|
|
|
2008-03-12 00:04:00 +01:00
|
|
|
fprintf(vvp_out, " %s/link", command_name);
|
2007-01-16 06:44:14 +01:00
|
|
|
fprintf(vvp_out, " v%p_%lu", lsig, use_lword);
|
|
|
|
|
fprintf(vvp_out, ", v%p_%lu;\n", rsig, use_rword);
|
2005-06-02 18:03:47 +02:00
|
|
|
}
|
|
|
|
|
|
2004-12-17 05:46:40 +01:00
|
|
|
static int show_stmt_cassign(ivl_statement_t net)
|
|
|
|
|
{
|
|
|
|
|
ivl_expr_t rval;
|
2008-04-17 01:11:23 +02:00
|
|
|
ivl_signal_t sig;
|
2004-12-17 05:46:40 +01:00
|
|
|
|
2011-02-28 04:20:16 +01:00
|
|
|
show_stmt_file_line(net, "Assign statement.");
|
|
|
|
|
|
2004-12-17 05:46:40 +01:00
|
|
|
rval = ivl_stmt_rval(net);
|
|
|
|
|
assert(rval);
|
|
|
|
|
|
2008-04-17 01:11:23 +02:00
|
|
|
sig = ivl_lval_sig(ivl_stmt_lval(net, 0));
|
|
|
|
|
if (sig && ivl_signal_data_type(sig) == IVL_VT_REAL) {
|
2001-11-01 05:26:57 +01:00
|
|
|
|
2012-10-23 02:20:43 +02:00
|
|
|
draw_eval_real(ivl_stmt_rval(net));
|
|
|
|
|
force_real_to_lval(net);
|
2008-04-17 01:11:23 +02:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
struct vector_info rvec;
|
|
|
|
|
|
|
|
|
|
rvec = draw_eval_expr(rval, STUFF_OK_47);
|
|
|
|
|
|
|
|
|
|
/* Write out initial continuous assign instructions to assign
|
|
|
|
|
the expression value to the l-value. */
|
|
|
|
|
force_vector_to_lval(net, rvec);
|
|
|
|
|
}
|
2004-12-17 05:46:40 +01:00
|
|
|
|
2005-06-02 18:03:47 +02:00
|
|
|
force_link_rval(net, rval);
|
2004-12-11 06:43:30 +01:00
|
|
|
|
2001-11-01 05:26:57 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2004-12-11 06:43:30 +01:00
|
|
|
/*
|
|
|
|
|
* Handle the deassign similar to cassign. The lvals must all be
|
|
|
|
|
* vectors without bit or part selects. Simply call %deassign for all
|
|
|
|
|
* the values.
|
|
|
|
|
*/
|
2001-11-01 05:26:57 +01:00
|
|
|
static int show_stmt_deassign(ivl_statement_t net)
|
|
|
|
|
{
|
2008-04-17 01:11:23 +02:00
|
|
|
ivl_signal_t sig = ivl_lval_sig(ivl_stmt_lval(net, 0));
|
2008-09-30 03:06:47 +02:00
|
|
|
unsigned lidx;
|
|
|
|
|
|
2011-02-28 04:20:16 +01:00
|
|
|
show_stmt_file_line(net, "Deassign statement.");
|
|
|
|
|
|
2008-04-17 01:11:23 +02:00
|
|
|
if (sig && ivl_signal_data_type(sig) == IVL_VT_REAL) {
|
2008-09-30 03:06:47 +02:00
|
|
|
ivl_lval_t lval;
|
|
|
|
|
|
2008-04-17 01:11:23 +02:00
|
|
|
assert(ivl_stmt_lvals(net) == 1);
|
2008-09-30 03:06:47 +02:00
|
|
|
lval = ivl_stmt_lval(net, 0);
|
2008-04-17 01:11:23 +02:00
|
|
|
assert(ivl_lval_width(lval) == 1);
|
|
|
|
|
assert(ivl_lval_part_off(lval) == 0);
|
|
|
|
|
assert(ivl_lval_idx(lval) == 0);
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, " %%deassign/wr v%p_0;\n", sig);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2004-12-11 06:43:30 +01:00
|
|
|
for (lidx = 0 ; lidx < ivl_stmt_lvals(net) ; lidx += 1) {
|
|
|
|
|
ivl_lval_t lval = ivl_stmt_lval(net, lidx);
|
|
|
|
|
ivl_signal_t lsig = ivl_lval_sig(lval);
|
2001-11-01 05:26:57 +01:00
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
ivl_expr_t word_idx = ivl_lval_idx(lval);
|
|
|
|
|
unsigned long use_word = 0;
|
2008-09-30 03:06:47 +02:00
|
|
|
unsigned use_wid;
|
|
|
|
|
ivl_expr_t part_off_ex;
|
|
|
|
|
unsigned part_off;
|
2007-01-16 06:44:14 +01:00
|
|
|
|
2004-12-11 06:43:30 +01:00
|
|
|
assert(lsig != 0);
|
|
|
|
|
assert(ivl_lval_mux(lval) == 0);
|
2008-04-07 20:16:15 +02:00
|
|
|
|
2008-09-30 03:06:47 +02:00
|
|
|
use_wid = ivl_lval_width(lval);
|
|
|
|
|
part_off_ex = ivl_lval_part_off(lval);
|
|
|
|
|
part_off = 0;
|
2008-04-10 03:56:42 +02:00
|
|
|
if (part_off_ex != 0) {
|
2008-06-13 22:32:06 +02:00
|
|
|
assert(number_is_immediate(part_off_ex, 64, 0));
|
2009-08-25 19:24:50 +02:00
|
|
|
assert(! number_is_unknown(part_off_ex));
|
2008-04-10 03:56:42 +02:00
|
|
|
part_off = get_number_immediate(part_off_ex);
|
2008-04-07 20:16:15 +02:00
|
|
|
}
|
2001-11-01 05:26:57 +01:00
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
if (word_idx != 0) {
|
2008-10-07 01:22:34 +02:00
|
|
|
assert(number_is_immediate(word_idx, IMM_WID, 0));
|
2009-08-25 19:24:50 +02:00
|
|
|
assert(! number_is_unknown(word_idx));
|
2007-01-16 06:44:14 +01:00
|
|
|
use_word = get_number_immediate(word_idx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2008-04-10 03:56:42 +02:00
|
|
|
fprintf(vvp_out, " %%deassign v%p_%lu, %u, %u;\n",
|
|
|
|
|
lsig, use_word, part_off, use_wid);
|
2004-12-11 06:43:30 +01:00
|
|
|
}
|
2004-12-11 03:31:25 +01:00
|
|
|
|
2001-11-01 05:26:57 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-18 07:12:03 +02:00
|
|
|
static int show_stmt_condit(ivl_statement_t net, ivl_scope_t sscope)
|
2001-03-22 06:06:21 +01:00
|
|
|
{
|
2001-03-27 05:31:06 +02:00
|
|
|
int rc = 0;
|
2001-03-22 06:06:21 +01:00
|
|
|
unsigned lab_false, lab_out;
|
2010-05-28 05:09:32 +02:00
|
|
|
ivl_expr_t expr = ivl_stmt_cond_expr(net);
|
2011-02-28 04:20:16 +01:00
|
|
|
struct vector_info cond;
|
|
|
|
|
|
|
|
|
|
show_stmt_file_line(net, "If statement.");
|
|
|
|
|
|
|
|
|
|
cond = draw_eval_expr(expr, STUFF_OK_XZ|STUFF_OK_47|STUFF_OK_RO);
|
2001-03-22 06:06:21 +01:00
|
|
|
|
|
|
|
|
assert(cond.wid == 1);
|
|
|
|
|
|
|
|
|
|
lab_false = local_count++;
|
|
|
|
|
lab_out = local_count++;
|
|
|
|
|
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, " %%jmp/0xz T_%u.%u, %u;\n",
|
2001-03-22 06:06:21 +01:00
|
|
|
thread_count, lab_false, cond.base);
|
|
|
|
|
|
2001-03-31 21:02:13 +02:00
|
|
|
/* Done with the condition expression. */
|
2002-09-24 06:20:32 +02:00
|
|
|
if (cond.base >= 8)
|
|
|
|
|
clr_vector(cond);
|
2001-03-31 21:02:13 +02:00
|
|
|
|
2002-04-14 21:19:21 +02:00
|
|
|
if (ivl_stmt_cond_true(net))
|
|
|
|
|
rc += show_statement(ivl_stmt_cond_true(net), sscope);
|
2001-03-22 06:06:21 +01:00
|
|
|
|
2002-09-27 18:33:34 +02:00
|
|
|
|
2001-03-22 06:06:21 +01:00
|
|
|
if (ivl_stmt_cond_false(net)) {
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_out);
|
|
|
|
|
fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_false);
|
2002-09-27 22:24:42 +02:00
|
|
|
clear_expression_lookaside();
|
2001-03-22 06:06:21 +01:00
|
|
|
|
2001-04-18 07:12:03 +02:00
|
|
|
rc += show_statement(ivl_stmt_cond_false(net), sscope);
|
2001-03-22 06:06:21 +01:00
|
|
|
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_out);
|
2002-09-27 22:24:42 +02:00
|
|
|
clear_expression_lookaside();
|
2001-03-22 06:06:21 +01:00
|
|
|
|
|
|
|
|
} else {
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_false);
|
2002-09-27 22:24:42 +02:00
|
|
|
clear_expression_lookaside();
|
2001-03-22 06:06:21 +01:00
|
|
|
}
|
2001-03-27 05:31:06 +02:00
|
|
|
|
|
|
|
|
return rc;
|
2001-03-22 06:06:21 +01:00
|
|
|
}
|
|
|
|
|
|
2001-03-21 02:49:43 +01:00
|
|
|
/*
|
|
|
|
|
* The delay statement is easy. Simply write a ``%delay <n>''
|
|
|
|
|
* instruction to delay the thread, then draw the included statement.
|
2008-01-29 21:19:59 +01:00
|
|
|
* The delay statement comes from Verilog code like this:
|
2001-03-21 02:49:43 +01:00
|
|
|
*
|
|
|
|
|
* ...
|
|
|
|
|
* #<delay> <stmt>;
|
|
|
|
|
*/
|
2001-04-18 07:12:03 +02:00
|
|
|
static int show_stmt_delay(ivl_statement_t net, ivl_scope_t sscope)
|
2001-03-21 02:49:43 +01:00
|
|
|
{
|
2001-03-27 05:31:06 +02:00
|
|
|
int rc = 0;
|
2006-08-08 07:11:37 +02:00
|
|
|
uint64_t delay = ivl_stmt_delay_val(net);
|
2001-03-21 02:49:43 +01:00
|
|
|
ivl_statement_t stmt = ivl_stmt_sub_stmt(net);
|
|
|
|
|
|
2006-08-08 07:11:37 +02:00
|
|
|
unsigned long low = delay % UINT64_C(0x100000000);
|
|
|
|
|
unsigned long hig = delay / UINT64_C(0x100000000);
|
|
|
|
|
|
2011-02-28 04:20:16 +01:00
|
|
|
show_stmt_file_line(net, "Delay statement.");
|
|
|
|
|
|
2006-08-08 07:11:37 +02:00
|
|
|
fprintf(vvp_out, " %%delay %lu, %lu;\n", low, hig);
|
2002-09-27 18:33:34 +02:00
|
|
|
/* Lots of things can happen during a delay. */
|
|
|
|
|
clear_expression_lookaside();
|
|
|
|
|
|
2001-04-18 07:12:03 +02:00
|
|
|
rc += show_statement(stmt, sscope);
|
2001-03-27 05:31:06 +02:00
|
|
|
|
|
|
|
|
return rc;
|
2001-03-21 02:49:43 +01:00
|
|
|
}
|
|
|
|
|
|
2001-07-19 06:55:06 +02:00
|
|
|
/*
|
|
|
|
|
* The delayx statement is slightly more complex in that it is
|
|
|
|
|
* necessary to calculate the delay first. Load the calculated delay
|
|
|
|
|
* into and index register and use the %delayx instruction to do the
|
|
|
|
|
* actual delay.
|
|
|
|
|
*/
|
|
|
|
|
static int show_stmt_delayx(ivl_statement_t net, ivl_scope_t sscope)
|
|
|
|
|
{
|
|
|
|
|
int rc = 0;
|
2010-05-28 05:09:32 +02:00
|
|
|
ivl_expr_t expr = ivl_stmt_delay_expr(net);
|
2001-07-19 06:55:06 +02:00
|
|
|
ivl_statement_t stmt = ivl_stmt_sub_stmt(net);
|
|
|
|
|
|
2011-02-28 04:20:16 +01:00
|
|
|
show_stmt_file_line(net, "Delay statement.");
|
|
|
|
|
|
2010-05-28 05:09:32 +02:00
|
|
|
switch (ivl_expr_value(expr)) {
|
2003-01-26 22:15:58 +01:00
|
|
|
|
2005-09-14 04:53:13 +02:00
|
|
|
case IVL_VT_BOOL:
|
|
|
|
|
case IVL_VT_LOGIC: {
|
2010-05-28 05:09:32 +02:00
|
|
|
struct vector_info del = draw_eval_expr(expr, 0);
|
2003-01-26 22:15:58 +01:00
|
|
|
fprintf(vvp_out, " %%ix/get 0, %u, %u;\n",
|
|
|
|
|
del.base, del.wid);
|
|
|
|
|
clr_vector(del);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case IVL_VT_REAL: {
|
2012-10-23 02:20:43 +02:00
|
|
|
draw_eval_real(expr);
|
|
|
|
|
fprintf(vvp_out, " %%cvt/ur 0;\n");
|
2003-01-26 22:15:58 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
assert(0);
|
2001-07-19 06:55:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, " %%delayx 0;\n");
|
2002-09-27 18:33:34 +02:00
|
|
|
/* Lots of things can happen during a delay. */
|
|
|
|
|
clear_expression_lookaside();
|
2001-07-19 06:55:06 +02:00
|
|
|
|
|
|
|
|
rc += show_statement(stmt, sscope);
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-21 02:55:46 +02:00
|
|
|
static int show_stmt_disable(ivl_statement_t net, ivl_scope_t sscope)
|
|
|
|
|
{
|
|
|
|
|
int rc = 0;
|
|
|
|
|
ivl_scope_t target = ivl_stmt_call(net);
|
2011-02-28 04:20:16 +01:00
|
|
|
|
|
|
|
|
show_stmt_file_line(net, "Disable statement.");
|
|
|
|
|
|
2003-03-25 03:15:48 +01:00
|
|
|
fprintf(vvp_out, " %%disable S_%p;\n", target);
|
2001-04-21 02:55:46 +02:00
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-14 04:28:49 +01:00
|
|
|
static int show_stmt_force(ivl_statement_t net)
|
|
|
|
|
{
|
2004-12-17 05:46:40 +01:00
|
|
|
ivl_expr_t rval;
|
2008-04-17 01:11:23 +02:00
|
|
|
ivl_signal_t sig;
|
2001-11-18 02:28:18 +01:00
|
|
|
|
2011-02-28 04:20:16 +01:00
|
|
|
show_stmt_file_line(net, "Force statement.");
|
|
|
|
|
|
2004-12-17 05:46:40 +01:00
|
|
|
rval = ivl_stmt_rval(net);
|
|
|
|
|
assert(rval);
|
2001-11-18 02:28:18 +01:00
|
|
|
|
2008-04-17 01:11:23 +02:00
|
|
|
sig = ivl_lval_sig(ivl_stmt_lval(net, 0));
|
|
|
|
|
if (sig && ivl_signal_data_type(sig) == IVL_VT_REAL) {
|
|
|
|
|
|
2012-10-23 02:20:43 +02:00
|
|
|
draw_eval_real(ivl_stmt_rval(net));
|
|
|
|
|
force_real_to_lval(net);
|
2004-12-17 05:46:40 +01:00
|
|
|
|
2008-04-17 01:11:23 +02:00
|
|
|
} else {
|
|
|
|
|
struct vector_info rvec;
|
|
|
|
|
|
|
|
|
|
rvec = draw_eval_expr(rval, STUFF_OK_47);
|
|
|
|
|
|
|
|
|
|
/* Write out initial continuous assign instructions to assign
|
|
|
|
|
the expression value to the l-value. */
|
|
|
|
|
force_vector_to_lval(net, rvec);
|
|
|
|
|
}
|
2004-12-17 05:46:40 +01:00
|
|
|
|
2005-06-02 18:03:47 +02:00
|
|
|
force_link_rval(net, rval);
|
2001-11-18 02:28:18 +01:00
|
|
|
|
2001-11-14 04:28:49 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-18 07:12:03 +02:00
|
|
|
static int show_stmt_forever(ivl_statement_t net, ivl_scope_t sscope)
|
2001-04-04 06:50:35 +02:00
|
|
|
{
|
|
|
|
|
int rc = 0;
|
|
|
|
|
ivl_statement_t stmt = ivl_stmt_sub_stmt(net);
|
|
|
|
|
unsigned lab_top = local_count++;
|
|
|
|
|
|
2011-02-28 04:20:16 +01:00
|
|
|
show_stmt_file_line(net, "Forever statement.");
|
|
|
|
|
|
2001-04-04 06:50:35 +02:00
|
|
|
fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_top);
|
2001-04-18 07:12:03 +02:00
|
|
|
rc += show_statement(stmt, sscope);
|
2001-04-04 06:50:35 +02:00
|
|
|
fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_top);
|
|
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-18 07:12:03 +02:00
|
|
|
static int show_stmt_fork(ivl_statement_t net, ivl_scope_t sscope)
|
2001-03-30 07:49:52 +02:00
|
|
|
{
|
|
|
|
|
unsigned idx;
|
|
|
|
|
int rc = 0;
|
2012-05-20 20:39:52 +02:00
|
|
|
unsigned join_count = ivl_stmt_block_count(net);
|
|
|
|
|
unsigned join_detach_count = 0;
|
2003-05-17 06:38:19 +02:00
|
|
|
ivl_scope_t scope = ivl_stmt_block_scope(net);
|
2012-05-20 20:39:52 +02:00
|
|
|
int is_named = (scope != 0);
|
|
|
|
|
/* This is TRUE if it is allowed to embed one of the threads
|
|
|
|
|
into this thread. */
|
|
|
|
|
int is_embeddable = 1;
|
2001-03-30 07:49:52 +02:00
|
|
|
|
2002-05-27 02:08:45 +02:00
|
|
|
unsigned out = transient_id++;
|
2002-08-27 07:39:57 +02:00
|
|
|
unsigned id_base = transient_id;
|
2003-07-29 07:12:10 +02:00
|
|
|
|
2012-05-20 20:39:52 +02:00
|
|
|
/* Children are certainly not embeddable if they are going
|
|
|
|
|
into a new scope. */
|
|
|
|
|
if (is_named)
|
|
|
|
|
is_embeddable = 0;
|
|
|
|
|
|
|
|
|
|
switch (ivl_statement_type(net)) {
|
|
|
|
|
case IVL_ST_FORK:
|
|
|
|
|
break;
|
|
|
|
|
case IVL_ST_FORK_JOIN_ANY:
|
|
|
|
|
if (join_count < 2)
|
|
|
|
|
break;
|
|
|
|
|
is_embeddable = 0;
|
|
|
|
|
join_detach_count = join_count - 1;
|
|
|
|
|
join_count = 1;
|
|
|
|
|
break;
|
|
|
|
|
case IVL_ST_FORK_JOIN_NONE:
|
|
|
|
|
is_embeddable = 0;
|
|
|
|
|
join_detach_count = join_count;
|
|
|
|
|
join_count = 0;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
|
|
2003-07-29 07:12:10 +02:00
|
|
|
/* cnt is the number of sub-threads. If the fork-join has no
|
|
|
|
|
name, then we can put one of the sub-threads in the current
|
2010-03-17 01:11:56 +01:00
|
|
|
thread, so decrement the count by one and use the current
|
|
|
|
|
scope for all the threads. */
|
2012-05-20 20:39:52 +02:00
|
|
|
if (is_embeddable)
|
|
|
|
|
join_count -= 1;
|
|
|
|
|
if (scope==0)
|
2003-07-29 07:12:10 +02:00
|
|
|
scope = sscope;
|
|
|
|
|
|
2012-05-20 20:39:52 +02:00
|
|
|
transient_id += join_count;
|
2002-08-27 07:39:57 +02:00
|
|
|
|
2001-03-30 07:49:52 +02:00
|
|
|
/* Draw a fork statement for all but one of the threads of the
|
|
|
|
|
fork/join. Send the threads off to a bit of code where they
|
|
|
|
|
are implemented. */
|
2012-05-20 20:39:52 +02:00
|
|
|
for (idx = 0 ; idx < (join_count+join_detach_count) ; idx += 1) {
|
2003-03-25 03:15:48 +01:00
|
|
|
fprintf(vvp_out, " %%fork t_%u, S_%p;\n",
|
2003-05-17 06:38:19 +02:00
|
|
|
id_base+idx, scope);
|
2001-03-30 07:49:52 +02:00
|
|
|
}
|
|
|
|
|
|
2003-07-29 07:12:10 +02:00
|
|
|
/* If we are putting one sub-thread into the current thread,
|
|
|
|
|
then draw its code here. */
|
2012-05-20 20:39:52 +02:00
|
|
|
if (is_embeddable)
|
|
|
|
|
rc += show_statement(ivl_stmt_block_stmt(net, join_count), scope);
|
2001-03-30 07:49:52 +02:00
|
|
|
|
2003-07-29 07:12:10 +02:00
|
|
|
|
|
|
|
|
/* Generate enough joins to collect all the sub-threads. */
|
2012-05-20 20:39:52 +02:00
|
|
|
for (idx = 0 ; idx < join_count ; idx += 1)
|
2001-03-30 07:49:52 +02:00
|
|
|
fprintf(vvp_out, " %%join;\n");
|
2012-05-20 20:39:52 +02:00
|
|
|
if (join_detach_count > 0)
|
|
|
|
|
fprintf(vvp_out, " %%join/detach %u;\n", join_detach_count);
|
|
|
|
|
/* Jump around all the threads that I'm creating. */
|
2001-03-30 07:49:52 +02:00
|
|
|
fprintf(vvp_out, " %%jmp t_%u;\n", out);
|
|
|
|
|
|
2010-03-17 01:11:56 +01:00
|
|
|
/* Change the compiling scope to be the named forks scope. */
|
|
|
|
|
if (is_named) fprintf(vvp_out, " .scope S_%p;\n", scope);
|
2003-07-29 07:12:10 +02:00
|
|
|
/* Generate the sub-threads themselves. */
|
2012-05-20 20:39:52 +02:00
|
|
|
for (idx = 0 ; idx < (join_count + join_detach_count) ; idx += 1) {
|
2002-08-27 07:39:57 +02:00
|
|
|
fprintf(vvp_out, "t_%u ;\n", id_base+idx);
|
2002-09-27 18:33:34 +02:00
|
|
|
clear_expression_lookaside();
|
2003-05-17 06:38:19 +02:00
|
|
|
rc += show_statement(ivl_stmt_block_stmt(net, idx), scope);
|
2001-03-30 07:49:52 +02:00
|
|
|
fprintf(vvp_out, " %%end;\n");
|
|
|
|
|
}
|
2010-03-17 01:11:56 +01:00
|
|
|
/* Return to the previous scope. */
|
2012-05-20 20:39:52 +02:00
|
|
|
if (sscope) fprintf(vvp_out, " .scope S_%p;\n", sscope);
|
2001-03-30 07:49:52 +02:00
|
|
|
|
2001-04-18 07:12:03 +02:00
|
|
|
/* This is the label for the out. Use this to branch around
|
2001-03-30 07:49:52 +02:00
|
|
|
the implementations of all the child threads. */
|
2002-09-27 18:33:34 +02:00
|
|
|
clear_expression_lookaside();
|
2001-10-20 01:52:36 +02:00
|
|
|
fprintf(vvp_out, "t_%u ;\n", out);
|
2001-03-30 07:49:52 +02:00
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-27 01:54:13 +02:00
|
|
|
static int show_stmt_free(ivl_statement_t net)
|
|
|
|
|
{
|
|
|
|
|
ivl_scope_t scope = ivl_stmt_call(net);
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, " %%free S_%p;\n", scope);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-21 02:49:43 +01:00
|
|
|
/*
|
|
|
|
|
* noop statements are implemented by doing nothing.
|
|
|
|
|
*/
|
2001-03-27 05:31:06 +02:00
|
|
|
static int show_stmt_noop(ivl_statement_t net)
|
2001-03-21 02:49:43 +01:00
|
|
|
{
|
2001-03-27 05:31:06 +02:00
|
|
|
return 0;
|
2001-03-21 02:49:43 +01:00
|
|
|
}
|
2001-03-19 02:20:46 +01:00
|
|
|
|
2001-11-14 04:28:49 +01:00
|
|
|
static int show_stmt_release(ivl_statement_t net)
|
|
|
|
|
{
|
2008-04-17 01:11:23 +02:00
|
|
|
ivl_signal_t sig = ivl_lval_sig(ivl_stmt_lval(net, 0));
|
2008-09-30 03:06:47 +02:00
|
|
|
unsigned lidx;
|
|
|
|
|
|
2011-02-28 04:20:16 +01:00
|
|
|
show_stmt_file_line(net, "Release statement.");
|
|
|
|
|
|
2008-04-17 01:11:23 +02:00
|
|
|
if (sig && ivl_signal_data_type(sig) == IVL_VT_REAL) {
|
|
|
|
|
unsigned type = 0;
|
2008-09-30 03:06:47 +02:00
|
|
|
ivl_lval_t lval;
|
2008-04-17 01:11:23 +02:00
|
|
|
|
|
|
|
|
assert(ivl_stmt_lvals(net) == 1);
|
2008-09-30 03:06:47 +02:00
|
|
|
lval = ivl_stmt_lval(net, 0);
|
2008-04-17 01:11:23 +02:00
|
|
|
assert(ivl_lval_width(lval) == 1);
|
|
|
|
|
assert(ivl_lval_part_off(lval) == 0);
|
|
|
|
|
assert(ivl_lval_idx(lval) == 0);
|
|
|
|
|
|
|
|
|
|
if (ivl_signal_type(sig) == IVL_SIT_REG) type = 1;
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, " %%release/wr v%p_0, %u;\n", sig, type);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2004-12-17 05:46:40 +01:00
|
|
|
for (lidx = 0 ; lidx < ivl_stmt_lvals(net) ; lidx += 1) {
|
|
|
|
|
ivl_lval_t lval = ivl_stmt_lval(net, lidx);
|
|
|
|
|
ivl_signal_t lsig = ivl_lval_sig(lval);
|
|
|
|
|
const char*opcode = 0;
|
2001-11-18 02:28:18 +01:00
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
ivl_expr_t word_idx = ivl_lval_idx(lval);
|
|
|
|
|
unsigned long use_word = 0;
|
2008-09-30 03:06:47 +02:00
|
|
|
unsigned use_wid;
|
|
|
|
|
ivl_expr_t part_off_ex;
|
|
|
|
|
unsigned part_off;
|
|
|
|
|
|
2004-12-17 05:46:40 +01:00
|
|
|
assert(lsig != 0);
|
|
|
|
|
assert(ivl_lval_mux(lval) == 0);
|
2008-03-12 00:04:00 +01:00
|
|
|
|
2008-09-30 03:06:47 +02:00
|
|
|
use_wid = ivl_lval_width(lval);
|
|
|
|
|
part_off_ex = ivl_lval_part_off(lval);
|
|
|
|
|
part_off = 0;
|
2008-03-12 00:04:00 +01:00
|
|
|
if (part_off_ex != 0) {
|
2008-06-13 22:32:06 +02:00
|
|
|
assert(number_is_immediate(part_off_ex, 64, 0));
|
2009-08-25 19:24:50 +02:00
|
|
|
assert(! number_is_unknown(part_off_ex));
|
2008-03-12 00:04:00 +01:00
|
|
|
part_off = get_number_immediate(part_off_ex);
|
2008-02-13 18:18:25 +01:00
|
|
|
}
|
2004-12-17 05:46:40 +01:00
|
|
|
|
|
|
|
|
switch (ivl_signal_type(lsig)) {
|
|
|
|
|
case IVL_SIT_REG:
|
|
|
|
|
opcode = "reg";
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
opcode = "net";
|
|
|
|
|
break;
|
2002-08-07 02:54:39 +02:00
|
|
|
}
|
2004-12-17 05:46:40 +01:00
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
if (word_idx != 0) {
|
2008-10-07 01:22:34 +02:00
|
|
|
assert(number_is_immediate(word_idx, IMM_WID, 0));
|
2009-08-25 19:24:50 +02:00
|
|
|
assert(! number_is_unknown(word_idx));
|
2007-01-16 06:44:14 +01:00
|
|
|
use_word = get_number_immediate(word_idx);
|
|
|
|
|
}
|
|
|
|
|
|
2004-12-17 05:46:40 +01:00
|
|
|
/* Generate the appropriate release statement for this
|
|
|
|
|
l-value. */
|
2008-03-12 00:04:00 +01:00
|
|
|
fprintf(vvp_out, " %%release/%s v%p_%lu, %u, %u;\n",
|
|
|
|
|
opcode, lsig, use_word, part_off, use_wid);
|
2001-11-18 02:28:18 +01:00
|
|
|
}
|
2004-12-17 05:46:40 +01:00
|
|
|
|
2001-11-14 04:28:49 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-18 07:12:03 +02:00
|
|
|
static int show_stmt_repeat(ivl_statement_t net, ivl_scope_t sscope)
|
2001-04-05 05:20:57 +02:00
|
|
|
{
|
|
|
|
|
int rc = 0;
|
|
|
|
|
unsigned lab_top = local_count++, lab_out = local_count++;
|
2010-05-28 05:09:32 +02:00
|
|
|
ivl_expr_t expr = ivl_stmt_cond_expr(net);
|
2011-02-28 04:20:16 +01:00
|
|
|
struct vector_info cnt;
|
2010-09-27 20:03:43 +02:00
|
|
|
const char *sign = ivl_expr_signed(expr) ? "s" : "u";
|
2001-04-05 05:20:57 +02:00
|
|
|
|
2011-02-28 04:20:16 +01:00
|
|
|
show_stmt_file_line(net, "Repeat statement.");
|
|
|
|
|
|
|
|
|
|
cnt = draw_eval_expr(expr, 0);
|
|
|
|
|
|
2001-04-05 05:20:57 +02:00
|
|
|
/* Test that 0 < expr */
|
2008-09-04 01:05:12 +02:00
|
|
|
fprintf(vvp_out, "T_%u.%u %%cmp/%s 0, %u, %u;\n", thread_count,
|
|
|
|
|
lab_top, sign, cnt.base, cnt.wid);
|
2002-09-27 22:24:42 +02:00
|
|
|
clear_expression_lookaside();
|
2001-04-05 05:20:57 +02:00
|
|
|
fprintf(vvp_out, " %%jmp/0xz T_%u.%u, 5;\n", thread_count, lab_out);
|
|
|
|
|
/* This adds -1 (all ones in 2's complement) to the count. */
|
|
|
|
|
fprintf(vvp_out, " %%add %u, 1, %u;\n", cnt.base, cnt.wid);
|
|
|
|
|
|
2001-04-18 07:12:03 +02:00
|
|
|
rc += show_statement(ivl_stmt_sub_stmt(net), sscope);
|
2001-04-05 05:20:57 +02:00
|
|
|
|
|
|
|
|
fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_top);
|
|
|
|
|
fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_out);
|
2002-09-27 22:24:42 +02:00
|
|
|
clear_expression_lookaside();
|
2001-04-05 05:20:57 +02:00
|
|
|
|
|
|
|
|
clr_vector(cnt);
|
|
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2004-12-18 19:53:26 +01:00
|
|
|
/*
|
|
|
|
|
* The trigger statement is straight forward. All we have to do is
|
|
|
|
|
* write a single bit of fake data to the event object.
|
|
|
|
|
*/
|
2001-03-29 05:47:38 +02:00
|
|
|
static int show_stmt_trigger(ivl_statement_t net)
|
|
|
|
|
{
|
2003-12-03 03:46:23 +01:00
|
|
|
ivl_event_t ev = ivl_stmt_events(net, 0);
|
2001-03-29 05:47:38 +02:00
|
|
|
assert(ev);
|
2011-02-28 04:20:16 +01:00
|
|
|
|
|
|
|
|
show_stmt_file_line(net, "Event trigger statement.");
|
|
|
|
|
|
2004-12-18 19:53:26 +01:00
|
|
|
fprintf(vvp_out, " %%set/v E_%p, 0,1;\n", ev);
|
2001-03-29 05:47:38 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-02 04:28:12 +02:00
|
|
|
static int show_stmt_utask(ivl_statement_t net)
|
|
|
|
|
{
|
|
|
|
|
ivl_scope_t task = ivl_stmt_call(net);
|
|
|
|
|
|
2011-02-28 04:20:16 +01:00
|
|
|
show_stmt_file_line(net, "User task call.");
|
|
|
|
|
|
2001-06-18 05:10:34 +02:00
|
|
|
fprintf(vvp_out, " %%fork TD_%s",
|
|
|
|
|
vvp_mangle_id(ivl_scope_name(task)));
|
2003-03-25 03:15:48 +01:00
|
|
|
fprintf(vvp_out, ", S_%p;\n", task);
|
2001-04-02 04:28:12 +02:00
|
|
|
fprintf(vvp_out, " %%join;\n");
|
2002-09-27 18:33:34 +02:00
|
|
|
clear_expression_lookaside();
|
2001-04-02 04:28:12 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-18 07:12:03 +02:00
|
|
|
static int show_stmt_wait(ivl_statement_t net, ivl_scope_t sscope)
|
2001-03-27 08:27:40 +02:00
|
|
|
{
|
2011-02-28 04:20:16 +01:00
|
|
|
show_stmt_file_line(net, "Event wait (@) statement.");
|
|
|
|
|
|
2003-12-03 03:46:23 +01:00
|
|
|
if (ivl_stmt_nevent(net) == 1) {
|
|
|
|
|
ivl_event_t ev = ivl_stmt_events(net, 0);
|
|
|
|
|
fprintf(vvp_out, " %%wait E_%p;\n", ev);
|
2001-03-27 08:27:40 +02:00
|
|
|
|
2003-12-03 03:46:23 +01:00
|
|
|
} else {
|
|
|
|
|
unsigned idx;
|
|
|
|
|
static unsigned int cascade_counter = 0;
|
|
|
|
|
ivl_event_t ev = ivl_stmt_events(net, 0);
|
|
|
|
|
fprintf(vvp_out, "Ewait_%u .event/or E_%p", cascade_counter, ev);
|
|
|
|
|
|
|
|
|
|
for (idx = 1 ; idx < ivl_stmt_nevent(net) ; idx += 1) {
|
|
|
|
|
ev = ivl_stmt_events(net, idx);
|
|
|
|
|
fprintf(vvp_out, ", E_%p", ev);
|
|
|
|
|
}
|
|
|
|
|
fprintf(vvp_out, ";\n %%wait Ewait_%u;\n", cascade_counter);
|
|
|
|
|
cascade_counter += 1;
|
|
|
|
|
}
|
2002-09-27 22:24:42 +02:00
|
|
|
/* Always clear the expression lookaside after a
|
|
|
|
|
%wait. Anything can happen while the thread is waiting. */
|
2002-09-27 18:33:34 +02:00
|
|
|
clear_expression_lookaside();
|
|
|
|
|
|
2001-04-18 07:12:03 +02:00
|
|
|
return show_statement(ivl_stmt_sub_stmt(net), sscope);
|
2001-03-27 08:27:40 +02:00
|
|
|
}
|
|
|
|
|
|
2002-04-22 04:41:30 +02:00
|
|
|
static struct vector_info reduction_or(struct vector_info cvec)
|
|
|
|
|
{
|
|
|
|
|
struct vector_info result;
|
|
|
|
|
|
|
|
|
|
switch (cvec.base) {
|
|
|
|
|
case 0:
|
|
|
|
|
result.base = 0;
|
|
|
|
|
result.wid = 1;
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
result.base = 1;
|
|
|
|
|
result.wid = 1;
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
case 3:
|
|
|
|
|
result.base = 0;
|
|
|
|
|
result.wid = 1;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
clr_vector(cvec);
|
|
|
|
|
result.base = allocate_vector(1);
|
|
|
|
|
result.wid = 1;
|
2008-04-16 06:51:03 +02:00
|
|
|
assert(result.base);
|
2002-04-22 04:41:30 +02:00
|
|
|
fprintf(vvp_out, " %%or/r %u, %u, %u;\n", result.base,
|
|
|
|
|
cvec.base, cvec.wid);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-18 07:12:03 +02:00
|
|
|
static int show_stmt_while(ivl_statement_t net, ivl_scope_t sscope)
|
2001-04-01 08:49:04 +02:00
|
|
|
{
|
|
|
|
|
int rc = 0;
|
|
|
|
|
struct vector_info cvec;
|
|
|
|
|
|
|
|
|
|
unsigned top_label = local_count++;
|
|
|
|
|
unsigned out_label = local_count++;
|
|
|
|
|
|
2011-02-28 04:20:16 +01:00
|
|
|
show_stmt_file_line(net, "While statement.");
|
|
|
|
|
|
2002-09-27 22:24:42 +02:00
|
|
|
/* Start the loop. The top of the loop starts a basic block
|
|
|
|
|
because it can be entered from above or from the bottom of
|
|
|
|
|
the loop. */
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, "T_%u.%u ;\n", thread_count, top_label);
|
2002-09-27 22:24:42 +02:00
|
|
|
clear_expression_lookaside();
|
2001-04-01 08:49:04 +02:00
|
|
|
|
|
|
|
|
/* Draw the evaluation of the condition expression, and test
|
|
|
|
|
the result. If the expression evaluates to false, then
|
|
|
|
|
branch to the out label. */
|
2002-09-24 06:20:32 +02:00
|
|
|
cvec = draw_eval_expr(ivl_stmt_cond_expr(net), STUFF_OK_XZ|STUFF_OK_47);
|
2002-04-22 04:41:30 +02:00
|
|
|
if (cvec.wid > 1)
|
|
|
|
|
cvec = reduction_or(cvec);
|
|
|
|
|
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, " %%jmp/0xz T_%u.%u, %u;\n",
|
2001-04-01 08:49:04 +02:00
|
|
|
thread_count, out_label, cvec.base);
|
2002-09-24 06:20:32 +02:00
|
|
|
if (cvec.base >= 8)
|
|
|
|
|
clr_vector(cvec);
|
2001-04-01 08:49:04 +02:00
|
|
|
|
|
|
|
|
/* Draw the body of the loop. */
|
2001-04-18 07:12:03 +02:00
|
|
|
rc += show_statement(ivl_stmt_sub_stmt(net), sscope);
|
2001-04-01 08:49:04 +02:00
|
|
|
|
|
|
|
|
/* This is the bottom of the loop. branch to the top where the
|
2007-02-26 20:49:48 +01:00
|
|
|
test is repeated, and also draw the out label. */
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, top_label);
|
|
|
|
|
fprintf(vvp_out, "T_%u.%u ;\n", thread_count, out_label);
|
2002-09-27 22:24:42 +02:00
|
|
|
clear_expression_lookaside();
|
2001-04-01 08:49:04 +02:00
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-17 03:15:29 +02:00
|
|
|
static int show_delete_method(ivl_statement_t net)
|
|
|
|
|
{
|
|
|
|
|
show_stmt_file_line(net, "Delete object");
|
|
|
|
|
|
|
|
|
|
unsigned parm_count = ivl_stmt_parm_count(net);
|
|
|
|
|
if (parm_count < 1)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
ivl_expr_t parm = ivl_stmt_parm(net, 0);
|
|
|
|
|
assert(ivl_expr_type(parm) == IVL_EX_SIGNAL);
|
|
|
|
|
ivl_signal_t var = ivl_expr_signal(parm);
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, " %%delete/obj v%p_0;\n", var);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-27 05:31:06 +02:00
|
|
|
static int show_system_task_call(ivl_statement_t net)
|
2001-03-19 02:20:46 +01:00
|
|
|
{
|
2012-07-17 03:15:29 +02:00
|
|
|
const char*stmt_name = ivl_stmt_name(net);
|
|
|
|
|
|
|
|
|
|
if (strcmp(stmt_name,"$ivl_darray_method$delete") == 0)
|
|
|
|
|
return show_delete_method(net);
|
|
|
|
|
|
2011-02-28 04:20:16 +01:00
|
|
|
show_stmt_file_line(net, "System task call.");
|
|
|
|
|
|
2003-03-15 05:45:18 +01:00
|
|
|
draw_vpi_task_call(net);
|
2001-05-10 02:26:53 +02:00
|
|
|
|
2002-09-27 22:24:42 +02:00
|
|
|
/* VPI calls can manipulate anything, so clear the expression
|
|
|
|
|
lookahead table after the call. */
|
2002-09-27 18:33:34 +02:00
|
|
|
clear_expression_lookaside();
|
|
|
|
|
|
2001-03-27 05:31:06 +02:00
|
|
|
return 0;
|
2001-03-19 02:20:46 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-28 04:20:16 +01:00
|
|
|
/*
|
|
|
|
|
* Icarus translated <var> = <delay or event> <value> into
|
|
|
|
|
* begin
|
|
|
|
|
* <tmp> = <value>;
|
|
|
|
|
* <delay or event> <var> = <tmp>;
|
|
|
|
|
* end
|
|
|
|
|
* This routine looks for this pattern so we only emit one %file_line opcode.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static unsigned is_delayed_or_event_assign(ivl_scope_t scope,
|
|
|
|
|
ivl_statement_t stmt)
|
|
|
|
|
{
|
|
|
|
|
ivl_statement_t assign, delay, delayed_assign;
|
|
|
|
|
ivl_statement_type_t delay_type;
|
|
|
|
|
ivl_lval_t lval;
|
|
|
|
|
ivl_expr_t rval;
|
|
|
|
|
ivl_signal_t lsig, rsig;
|
|
|
|
|
|
|
|
|
|
/* We must have two block elements. */
|
|
|
|
|
if (ivl_stmt_block_count(stmt) != 2) return 0;
|
|
|
|
|
/* The first must be an assign. */
|
|
|
|
|
assign = ivl_stmt_block_stmt(stmt, 0);
|
|
|
|
|
if (ivl_statement_type(assign) != IVL_ST_ASSIGN) return 0;
|
|
|
|
|
/* The second must be a delayx. */
|
|
|
|
|
delay = ivl_stmt_block_stmt(stmt, 1);
|
|
|
|
|
delay_type = ivl_statement_type(delay);
|
|
|
|
|
if ((delay_type != IVL_ST_DELAYX) &&
|
|
|
|
|
(delay_type != IVL_ST_WAIT)) return 0;
|
|
|
|
|
/* The statement for the delayx must be an assign. */
|
|
|
|
|
delayed_assign = ivl_stmt_sub_stmt(delay);
|
|
|
|
|
if (ivl_statement_type(delayed_assign) != IVL_ST_ASSIGN) return 0;
|
|
|
|
|
/* The L-value must be a single signal. */
|
|
|
|
|
if (ivl_stmt_lvals(assign) != 1) return 0;
|
|
|
|
|
lval = ivl_stmt_lval(assign, 0);
|
|
|
|
|
/* It must not have an array select. */
|
|
|
|
|
if (ivl_lval_idx(lval)) return 0;
|
|
|
|
|
/* It must not have a non-zero base. */
|
|
|
|
|
if (ivl_lval_part_off(lval)) return 0;
|
|
|
|
|
lsig = ivl_lval_sig(lval);
|
|
|
|
|
/* It must not be part of the signal. */
|
|
|
|
|
if (ivl_lval_width(lval) != ivl_signal_width(lsig)) return 0;
|
|
|
|
|
/* The R-value must be a single signal. */
|
|
|
|
|
rval = ivl_stmt_rval(delayed_assign);
|
|
|
|
|
if (ivl_expr_type(rval) != IVL_EX_SIGNAL) return 0;
|
|
|
|
|
/* It must not be an array word. */
|
|
|
|
|
if (ivl_expr_oper1(rval)) return 0;
|
|
|
|
|
rsig = ivl_expr_signal(rval);
|
|
|
|
|
/* The two signals must be the same. */
|
|
|
|
|
if (lsig != rsig) return 0;
|
|
|
|
|
/* And finally the three statements must have the same line number
|
|
|
|
|
* as the block. */
|
|
|
|
|
if ((ivl_stmt_lineno(stmt) != ivl_stmt_lineno(assign)) ||
|
|
|
|
|
(ivl_stmt_lineno(stmt) != ivl_stmt_lineno(delay)) ||
|
|
|
|
|
(ivl_stmt_lineno(stmt) != ivl_stmt_lineno(delayed_assign))) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
/* The pattern matched so this block represents a blocking
|
|
|
|
|
* assignment with an inter-assignment delay or event. */
|
|
|
|
|
if (delay_type == IVL_ST_DELAYX) {
|
|
|
|
|
show_stmt_file_line(stmt, "Blocking assignment (delay).");
|
|
|
|
|
} else {
|
|
|
|
|
show_stmt_file_line(stmt, "Blocking assignment (event).");
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Icarus translated <var> = repeat(<count>) <event> <value> into
|
|
|
|
|
* begin
|
|
|
|
|
* <tmp> = <value>;
|
|
|
|
|
* repeat(<count>) <event>;
|
|
|
|
|
* <var> = <tmp>;
|
|
|
|
|
* end
|
|
|
|
|
* This routine looks for this pattern so we only emit one %file_line opcode.
|
|
|
|
|
*/
|
|
|
|
|
static unsigned is_repeat_event_assign(ivl_scope_t scope,
|
|
|
|
|
ivl_statement_t stmt)
|
|
|
|
|
{
|
|
|
|
|
ivl_statement_t assign, event, event_assign, repeat;
|
|
|
|
|
ivl_lval_t lval;
|
|
|
|
|
ivl_expr_t rval;
|
|
|
|
|
ivl_signal_t lsig, rsig;
|
|
|
|
|
|
|
|
|
|
/* We must have three block elements. */
|
|
|
|
|
if (ivl_stmt_block_count(stmt) != 3) return 0;
|
|
|
|
|
/* The first must be an assign. */
|
|
|
|
|
assign = ivl_stmt_block_stmt(stmt, 0);
|
|
|
|
|
if (ivl_statement_type(assign) != IVL_ST_ASSIGN) return 0;
|
|
|
|
|
/* The second must be a repeat with an event or an event. */
|
|
|
|
|
repeat = ivl_stmt_block_stmt(stmt, 1);
|
|
|
|
|
if (ivl_statement_type(repeat) != IVL_ST_REPEAT) return 0;
|
|
|
|
|
/* The repeat must have an event statement. */
|
|
|
|
|
event = ivl_stmt_sub_stmt(repeat);
|
|
|
|
|
if (ivl_statement_type(event) != IVL_ST_WAIT) return 0;
|
|
|
|
|
/* The third must be an assign. */
|
|
|
|
|
event_assign = ivl_stmt_block_stmt(stmt, 2);
|
|
|
|
|
if (ivl_statement_type(event_assign) != IVL_ST_ASSIGN) return 0;
|
|
|
|
|
/* The L-value must be a single signal. */
|
|
|
|
|
if (ivl_stmt_lvals(assign) != 1) return 0;
|
|
|
|
|
lval = ivl_stmt_lval(assign, 0);
|
|
|
|
|
/* It must not have an array select. */
|
|
|
|
|
if (ivl_lval_idx(lval)) return 0;
|
|
|
|
|
/* It must not have a non-zero base. */
|
|
|
|
|
if (ivl_lval_part_off(lval)) return 0;
|
|
|
|
|
lsig = ivl_lval_sig(lval);
|
|
|
|
|
/* It must not be part of the signal. */
|
|
|
|
|
if (ivl_lval_width(lval) != ivl_signal_width(lsig)) return 0;
|
|
|
|
|
/* The R-value must be a single signal. */
|
|
|
|
|
rval = ivl_stmt_rval(event_assign);
|
|
|
|
|
if (ivl_expr_type(rval) != IVL_EX_SIGNAL) return 0;
|
|
|
|
|
/* It must not be an array word. */
|
|
|
|
|
if (ivl_expr_oper1(rval)) return 0;
|
|
|
|
|
rsig = ivl_expr_signal(rval);
|
|
|
|
|
/* The two signals must be the same. */
|
|
|
|
|
if (lsig != rsig) return 0;
|
|
|
|
|
/* And finally the four statements must have the same line number
|
|
|
|
|
* as the block. */
|
|
|
|
|
if ((ivl_stmt_lineno(stmt) != ivl_stmt_lineno(assign)) ||
|
|
|
|
|
(ivl_stmt_lineno(stmt) != ivl_stmt_lineno(repeat)) ||
|
|
|
|
|
(ivl_stmt_lineno(stmt) != ivl_stmt_lineno(event)) ||
|
|
|
|
|
(ivl_stmt_lineno(stmt) != ivl_stmt_lineno(event_assign))) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The pattern matched so this block represents a blocking
|
|
|
|
|
* assignment with an inter-assignment repeat event. */
|
|
|
|
|
show_stmt_file_line(stmt, "Blocking assignment (repeat event).");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Icarus translated wait(<expr) <stmt> into
|
|
|
|
|
* begin
|
|
|
|
|
* while (<expr> !== 1'b1) @(<expr sensitivities>);
|
|
|
|
|
* <stmt>
|
|
|
|
|
* end
|
|
|
|
|
* This routine looks for this pattern and turns it back into a
|
|
|
|
|
* wait statement.
|
|
|
|
|
*/
|
|
|
|
|
static unsigned is_wait(ivl_scope_t scope, ivl_statement_t stmt)
|
|
|
|
|
{
|
|
|
|
|
ivl_statement_t while_wait, wait, wait_stmt;
|
|
|
|
|
ivl_expr_t while_expr, expr;
|
|
|
|
|
const char *bits;
|
|
|
|
|
/* We must have two block elements. */
|
|
|
|
|
if (ivl_stmt_block_count(stmt) != 2) return 0;
|
|
|
|
|
/* The first must be a while. */
|
|
|
|
|
while_wait = ivl_stmt_block_stmt(stmt, 0);
|
|
|
|
|
if (ivl_statement_type(while_wait) != IVL_ST_WHILE) return 0;
|
|
|
|
|
/* That has a wait with a NOOP statement. */
|
|
|
|
|
wait = ivl_stmt_sub_stmt(while_wait);
|
|
|
|
|
if (ivl_statement_type(wait) != IVL_ST_WAIT) return 0;
|
|
|
|
|
wait_stmt = ivl_stmt_sub_stmt(wait);
|
|
|
|
|
if (ivl_statement_type(wait_stmt) != IVL_ST_NOOP) return 0;
|
|
|
|
|
/* Check that the while condition has the correct form. */
|
|
|
|
|
while_expr = ivl_stmt_cond_expr(while_wait);
|
|
|
|
|
if (ivl_expr_type(while_expr) != IVL_EX_BINARY) return 0;
|
|
|
|
|
if (ivl_expr_opcode(while_expr) != 'N') return 0;
|
|
|
|
|
/* Has a second operator that is a constant 1'b1. */
|
|
|
|
|
expr = ivl_expr_oper2(while_expr);
|
|
|
|
|
if (ivl_expr_type(expr) != IVL_EX_NUMBER) return 0;
|
|
|
|
|
if (ivl_expr_width(expr) != 1) return 0;
|
|
|
|
|
bits = ivl_expr_bits(expr);
|
|
|
|
|
if (*bits != '1') return 0;
|
|
|
|
|
/* There is no easy way to verify that the @ sensitivity list
|
|
|
|
|
* matches the first expression so that is not currently checked. */
|
|
|
|
|
/* And finally the two statements that represent the wait must
|
|
|
|
|
* have the same line number as the block. */
|
|
|
|
|
if ((ivl_stmt_lineno(stmt) != ivl_stmt_lineno(while_wait)) ||
|
|
|
|
|
(ivl_stmt_lineno(stmt) != ivl_stmt_lineno(wait))) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The pattern matched so this block represents a wait statement. */
|
|
|
|
|
show_stmt_file_line(stmt, "Wait statement.");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check to see if the statement L-value is a port in the given scope.
|
|
|
|
|
* If it is return the zero based port number.
|
|
|
|
|
*/
|
|
|
|
|
static unsigned utask_in_port_idx(ivl_scope_t scope, ivl_statement_t stmt)
|
|
|
|
|
{
|
|
|
|
|
unsigned idx, ports = ivl_scope_ports(scope);
|
|
|
|
|
ivl_lval_t lval = ivl_stmt_lval(stmt, 0);
|
|
|
|
|
ivl_signal_t lsig = ivl_lval_sig(lval);
|
|
|
|
|
const char *sig_name;
|
|
|
|
|
/* The L-value must be a single signal. */
|
|
|
|
|
if (ivl_stmt_lvals(stmt) != 1) return ports;
|
|
|
|
|
/* It must not have an array select. */
|
|
|
|
|
if (ivl_lval_idx(lval)) return ports;
|
|
|
|
|
/* It must not have a non-zero base. */
|
|
|
|
|
if (ivl_lval_part_off(lval)) return ports;
|
|
|
|
|
/* It must not be part of the signal. */
|
|
|
|
|
if (ivl_lval_width(lval) != ivl_signal_width(lsig)) return ports;
|
|
|
|
|
/* It must have the same scope as the task. */
|
|
|
|
|
if (scope != ivl_signal_scope(lsig)) return ports;
|
|
|
|
|
/* It must be an input or inout port of the task. */
|
|
|
|
|
sig_name = ivl_signal_basename(lsig);
|
|
|
|
|
for (idx = 0; idx < ports; idx += 1) {
|
|
|
|
|
ivl_signal_t port = ivl_scope_port(scope, idx);
|
|
|
|
|
ivl_signal_port_t port_type = ivl_signal_port(port);
|
|
|
|
|
if ((port_type != IVL_SIP_INPUT) &&
|
|
|
|
|
(port_type != IVL_SIP_INOUT)) continue;
|
|
|
|
|
if (strcmp(sig_name, ivl_signal_basename(port)) == 0) break;
|
|
|
|
|
}
|
|
|
|
|
return idx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check to see if the statement R-value is a port in the given scope.
|
|
|
|
|
* If it is return the zero based port number.
|
|
|
|
|
*/
|
|
|
|
|
static unsigned utask_out_port_idx(ivl_scope_t scope, ivl_statement_t stmt)
|
|
|
|
|
{
|
|
|
|
|
unsigned idx, ports = ivl_scope_ports(scope);
|
|
|
|
|
ivl_expr_t rval = ivl_stmt_rval(stmt);
|
|
|
|
|
ivl_signal_t rsig = 0;
|
|
|
|
|
ivl_expr_type_t expr_type = ivl_expr_type(rval);
|
|
|
|
|
const char *sig_name;
|
|
|
|
|
/* We can have a simple signal. */
|
|
|
|
|
if (expr_type == IVL_EX_SIGNAL) {
|
|
|
|
|
rsig = ivl_expr_signal(rval);
|
|
|
|
|
/* Or a simple select of a simple signal. */
|
|
|
|
|
} else if (expr_type == IVL_EX_SELECT) {
|
|
|
|
|
ivl_expr_t expr = ivl_expr_oper1(rval);
|
|
|
|
|
/* We must have a zero select base. */
|
|
|
|
|
if (ivl_expr_oper2(rval)) return ports;
|
|
|
|
|
/* We must be selecting a signal. */
|
|
|
|
|
if (ivl_expr_type(expr) != IVL_EX_SIGNAL) return ports;
|
|
|
|
|
rsig = ivl_expr_signal(expr);
|
|
|
|
|
} else return ports;
|
|
|
|
|
/* The R-value must have the same scope as the task. */
|
|
|
|
|
if (scope != ivl_signal_scope(rsig)) return ports;
|
|
|
|
|
/* It must not be an array element. */
|
|
|
|
|
if (ivl_signal_dimensions(rsig)) return ports;
|
|
|
|
|
/* It must be an output or inout port of the task. */
|
|
|
|
|
sig_name = ivl_signal_basename(rsig);
|
|
|
|
|
for (idx = 0; idx < ports; idx += 1) {
|
|
|
|
|
ivl_signal_t port = ivl_scope_port(scope, idx);
|
|
|
|
|
ivl_signal_port_t port_type = ivl_signal_port(port);
|
|
|
|
|
if ((port_type != IVL_SIP_OUTPUT) &&
|
|
|
|
|
(port_type != IVL_SIP_INOUT)) continue;
|
|
|
|
|
if (strcmp(sig_name, ivl_signal_basename(port)) == 0) break;
|
|
|
|
|
}
|
|
|
|
|
return idx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Structure to hold the port information as we extract it from the block.
|
|
|
|
|
*/
|
|
|
|
|
typedef struct port_expr_s {
|
|
|
|
|
ivl_signal_port_t type;
|
|
|
|
|
union {
|
|
|
|
|
ivl_statement_t lval;
|
|
|
|
|
ivl_expr_t rval;
|
2011-03-04 01:23:44 +01:00
|
|
|
} expr;
|
2011-02-28 04:20:16 +01:00
|
|
|
} *port_expr_t;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Icarus encodes a user task call with arguments as:
|
|
|
|
|
* begin
|
|
|
|
|
* <input 1> = <arg>
|
|
|
|
|
* ...
|
|
|
|
|
* <input n> = <arg>
|
|
|
|
|
* <task_call>
|
|
|
|
|
* <arg> = <output 1>
|
|
|
|
|
* ...
|
|
|
|
|
* <arg> = <output n>
|
|
|
|
|
* end
|
|
|
|
|
* This routine looks for that pattern and translates it into the
|
|
|
|
|
* appropriate task call. It returns true (1) if it successfully
|
|
|
|
|
* translated the block to a task call, otherwise it returns false
|
|
|
|
|
* (0) to indicate the block needs to be emitted.
|
|
|
|
|
*/
|
|
|
|
|
static unsigned is_utask_call_with_args(ivl_scope_t scope,
|
|
|
|
|
ivl_statement_t stmt)
|
|
|
|
|
{
|
|
|
|
|
unsigned idx, ports, task_idx = 0;
|
|
|
|
|
unsigned count = ivl_stmt_block_count(stmt);
|
|
|
|
|
unsigned lineno = ivl_stmt_lineno(stmt);
|
|
|
|
|
ivl_scope_t task_scope = 0;
|
|
|
|
|
port_expr_t port_exprs;
|
|
|
|
|
/* Check to see if the block is of the basic form first. */
|
|
|
|
|
for (idx = 0; idx < count; idx += 1) {
|
|
|
|
|
ivl_statement_t tmp = ivl_stmt_block_stmt(stmt, idx);
|
|
|
|
|
if (ivl_statement_type(tmp) == IVL_ST_ASSIGN) continue;
|
|
|
|
|
if (ivl_statement_type(tmp) == IVL_ST_UTASK && !task_scope) {
|
|
|
|
|
task_idx = idx;
|
|
|
|
|
task_scope = ivl_stmt_call(tmp);
|
|
|
|
|
assert(ivl_scope_type(task_scope) == IVL_SCT_TASK);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
/* If there is no task call or it takes no argument then return. */
|
|
|
|
|
if (!task_scope) return 0;
|
|
|
|
|
ports = ivl_scope_ports(task_scope);
|
|
|
|
|
if (ports == 0) return 0;
|
|
|
|
|
|
|
|
|
|
/* Allocate space to save the port information and initialize it. */
|
|
|
|
|
port_exprs = (port_expr_t) malloc(sizeof(struct port_expr_s)*ports);
|
|
|
|
|
for (idx = 0; idx < ports; idx += 1) {
|
|
|
|
|
port_exprs[idx].type = IVL_SIP_NONE;
|
2011-03-04 01:23:44 +01:00
|
|
|
port_exprs[idx].expr.rval = 0;
|
2011-02-28 04:20:16 +01:00
|
|
|
}
|
|
|
|
|
/* Check that the input arguments are correct. */
|
|
|
|
|
for (idx = 0; idx < task_idx; idx += 1) {
|
|
|
|
|
ivl_statement_t assign = ivl_stmt_block_stmt(stmt, idx);
|
|
|
|
|
unsigned port = utask_in_port_idx(task_scope, assign);
|
|
|
|
|
if ((port == ports) || (lineno != ivl_stmt_lineno(assign))) {
|
|
|
|
|
free(port_exprs);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
port_exprs[port].type = IVL_SIP_INPUT;
|
2011-03-04 01:23:44 +01:00
|
|
|
port_exprs[port].expr.rval = ivl_stmt_rval(assign);
|
2011-02-28 04:20:16 +01:00
|
|
|
}
|
|
|
|
|
/* Check that the output arguments are correct. */
|
|
|
|
|
for (idx = task_idx + 1; idx < count; idx += 1) {
|
|
|
|
|
ivl_statement_t assign = ivl_stmt_block_stmt(stmt, idx);
|
|
|
|
|
unsigned port = utask_out_port_idx(task_scope, assign);
|
|
|
|
|
if ((port == ports) || (lineno != ivl_stmt_lineno(assign))) {
|
|
|
|
|
free(port_exprs);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (port_exprs[port].type == IVL_SIP_INPUT) {
|
|
|
|
|
/* We probably should verify that the current R-value
|
|
|
|
|
* matches the new L-value. */
|
|
|
|
|
port_exprs[port].type = IVL_SIP_INOUT;
|
|
|
|
|
} else {
|
|
|
|
|
port_exprs[port].type = IVL_SIP_OUTPUT;
|
|
|
|
|
}
|
2011-03-04 01:23:44 +01:00
|
|
|
port_exprs[port].expr.lval = assign;
|
2011-02-28 04:20:16 +01:00
|
|
|
}
|
|
|
|
|
/* Check that the task call has the correct line number. */
|
|
|
|
|
if (lineno != ivl_stmt_lineno(ivl_stmt_block_stmt(stmt, task_idx))) {
|
|
|
|
|
free(port_exprs);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Verify that all the ports were defined. */
|
|
|
|
|
for (idx = 0; idx < ports; idx += 1) {
|
|
|
|
|
if (port_exprs[idx].type == IVL_SIP_NONE) {
|
|
|
|
|
free(port_exprs);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The pattern matched so this block represents a call to a user
|
|
|
|
|
* defined task with arguments. */
|
|
|
|
|
show_stmt_file_line(stmt, "User task call (with arguments).");
|
2011-04-18 20:12:38 +02:00
|
|
|
free(port_exprs);
|
2011-02-28 04:20:16 +01:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-19 02:20:46 +01:00
|
|
|
/*
|
|
|
|
|
* This function draws a statement as vvp assembly. It basically
|
|
|
|
|
* switches on the statement type and draws code based on the type and
|
|
|
|
|
* further specifics.
|
|
|
|
|
*/
|
2001-04-18 07:12:03 +02:00
|
|
|
static int show_statement(ivl_statement_t net, ivl_scope_t sscope)
|
2001-03-19 02:20:46 +01:00
|
|
|
{
|
|
|
|
|
const ivl_statement_type_t code = ivl_statement_type(net);
|
2001-03-27 05:31:06 +02:00
|
|
|
int rc = 0;
|
2001-03-19 02:20:46 +01:00
|
|
|
|
|
|
|
|
switch (code) {
|
|
|
|
|
|
2008-09-27 01:54:13 +02:00
|
|
|
case IVL_ST_ALLOC:
|
|
|
|
|
rc += show_stmt_alloc(net);
|
|
|
|
|
break;
|
|
|
|
|
|
2001-03-21 02:49:43 +01:00
|
|
|
case IVL_ST_ASSIGN:
|
2001-03-27 05:31:06 +02:00
|
|
|
rc += show_stmt_assign(net);
|
2001-03-21 02:49:43 +01:00
|
|
|
break;
|
|
|
|
|
|
2001-04-03 06:50:37 +02:00
|
|
|
case IVL_ST_ASSIGN_NB:
|
|
|
|
|
rc += show_stmt_assign_nb(net);
|
|
|
|
|
break;
|
|
|
|
|
|
2001-04-21 02:55:46 +02:00
|
|
|
case IVL_ST_BLOCK:
|
2002-05-27 02:08:45 +02:00
|
|
|
if (ivl_stmt_block_scope(net))
|
|
|
|
|
rc += show_stmt_block_named(net, sscope);
|
2011-02-28 04:20:16 +01:00
|
|
|
else {
|
2013-04-15 20:53:07 +02:00
|
|
|
unsigned saved_file_line = 0;
|
2011-02-28 04:20:16 +01:00
|
|
|
/* This block could really represent a single statement.
|
|
|
|
|
* If so only emit a single %file_line opcode. */
|
|
|
|
|
if (show_file_line) {
|
|
|
|
|
if (is_delayed_or_event_assign(sscope, net) ||
|
|
|
|
|
is_repeat_event_assign(sscope, net) ||
|
|
|
|
|
is_wait(sscope, net) ||
|
|
|
|
|
is_utask_call_with_args(sscope, net)) {
|
|
|
|
|
saved_file_line = show_file_line;
|
|
|
|
|
show_file_line = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-05-27 02:08:45 +02:00
|
|
|
rc += show_stmt_block(net, sscope);
|
2011-02-28 04:20:16 +01:00
|
|
|
if (saved_file_line) show_file_line = 1;
|
|
|
|
|
}
|
2001-04-21 02:55:46 +02:00
|
|
|
break;
|
2001-03-19 02:20:46 +01:00
|
|
|
|
2001-03-31 19:36:38 +02:00
|
|
|
case IVL_ST_CASE:
|
2001-04-01 06:34:59 +02:00
|
|
|
case IVL_ST_CASEX:
|
|
|
|
|
case IVL_ST_CASEZ:
|
2001-04-18 07:12:03 +02:00
|
|
|
rc += show_stmt_case(net, sscope);
|
2001-03-31 19:36:38 +02:00
|
|
|
break;
|
|
|
|
|
|
2003-05-14 07:26:41 +02:00
|
|
|
case IVL_ST_CASER:
|
|
|
|
|
rc += show_stmt_case_r(net, sscope);
|
|
|
|
|
break;
|
|
|
|
|
|
2001-11-01 05:26:57 +01:00
|
|
|
case IVL_ST_CASSIGN:
|
|
|
|
|
rc += show_stmt_cassign(net);
|
|
|
|
|
break;
|
|
|
|
|
|
2001-03-22 06:06:21 +01:00
|
|
|
case IVL_ST_CONDIT:
|
2001-04-18 07:12:03 +02:00
|
|
|
rc += show_stmt_condit(net, sscope);
|
2001-03-22 06:06:21 +01:00
|
|
|
break;
|
|
|
|
|
|
2001-11-01 05:26:57 +01:00
|
|
|
case IVL_ST_DEASSIGN:
|
|
|
|
|
rc += show_stmt_deassign(net);
|
|
|
|
|
break;
|
|
|
|
|
|
2001-03-21 02:49:43 +01:00
|
|
|
case IVL_ST_DELAY:
|
2001-04-18 07:12:03 +02:00
|
|
|
rc += show_stmt_delay(net, sscope);
|
2001-03-21 02:49:43 +01:00
|
|
|
break;
|
|
|
|
|
|
2001-07-19 06:55:06 +02:00
|
|
|
case IVL_ST_DELAYX:
|
|
|
|
|
rc += show_stmt_delayx(net, sscope);
|
|
|
|
|
break;
|
|
|
|
|
|
2001-04-21 02:55:46 +02:00
|
|
|
case IVL_ST_DISABLE:
|
|
|
|
|
rc += show_stmt_disable(net, sscope);
|
|
|
|
|
break;
|
|
|
|
|
|
2001-11-14 04:28:49 +01:00
|
|
|
case IVL_ST_FORCE:
|
|
|
|
|
rc += show_stmt_force(net);
|
|
|
|
|
break;
|
|
|
|
|
|
2001-04-04 06:50:35 +02:00
|
|
|
case IVL_ST_FOREVER:
|
2001-04-18 07:12:03 +02:00
|
|
|
rc += show_stmt_forever(net, sscope);
|
2001-04-04 06:50:35 +02:00
|
|
|
break;
|
|
|
|
|
|
2001-03-30 07:49:52 +02:00
|
|
|
case IVL_ST_FORK:
|
2012-05-20 20:39:52 +02:00
|
|
|
case IVL_ST_FORK_JOIN_ANY:
|
|
|
|
|
case IVL_ST_FORK_JOIN_NONE:
|
2001-04-18 07:12:03 +02:00
|
|
|
rc += show_stmt_fork(net, sscope);
|
2001-03-30 07:49:52 +02:00
|
|
|
break;
|
|
|
|
|
|
2008-09-27 01:54:13 +02:00
|
|
|
case IVL_ST_FREE:
|
|
|
|
|
rc += show_stmt_free(net);
|
|
|
|
|
break;
|
|
|
|
|
|
2001-03-21 02:49:43 +01:00
|
|
|
case IVL_ST_NOOP:
|
2001-03-27 05:31:06 +02:00
|
|
|
rc += show_stmt_noop(net);
|
2001-03-21 02:49:43 +01:00
|
|
|
break;
|
|
|
|
|
|
2001-11-14 04:28:49 +01:00
|
|
|
case IVL_ST_RELEASE:
|
|
|
|
|
rc += show_stmt_release(net);
|
|
|
|
|
break;
|
|
|
|
|
|
2001-04-05 05:20:57 +02:00
|
|
|
case IVL_ST_REPEAT:
|
2001-04-18 07:12:03 +02:00
|
|
|
rc += show_stmt_repeat(net, sscope);
|
2001-04-05 05:20:57 +02:00
|
|
|
break;
|
|
|
|
|
|
2001-03-19 02:20:46 +01:00
|
|
|
case IVL_ST_STASK:
|
2001-03-27 05:31:06 +02:00
|
|
|
rc += show_system_task_call(net);
|
2001-03-19 02:20:46 +01:00
|
|
|
break;
|
|
|
|
|
|
2001-03-29 05:47:38 +02:00
|
|
|
case IVL_ST_TRIGGER:
|
|
|
|
|
rc += show_stmt_trigger(net);
|
|
|
|
|
break;
|
|
|
|
|
|
2001-04-02 04:28:12 +02:00
|
|
|
case IVL_ST_UTASK:
|
|
|
|
|
rc += show_stmt_utask(net);
|
|
|
|
|
break;
|
|
|
|
|
|
2001-03-27 08:27:40 +02:00
|
|
|
case IVL_ST_WAIT:
|
2001-04-18 07:12:03 +02:00
|
|
|
rc += show_stmt_wait(net, sscope);
|
2001-03-27 08:27:40 +02:00
|
|
|
break;
|
|
|
|
|
|
2001-04-01 08:49:04 +02:00
|
|
|
case IVL_ST_WHILE:
|
2001-04-18 07:12:03 +02:00
|
|
|
rc += show_stmt_while(net, sscope);
|
2001-04-01 08:49:04 +02:00
|
|
|
break;
|
|
|
|
|
|
2001-03-19 02:20:46 +01:00
|
|
|
default:
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(stderr, "vvp.tgt: Unable to draw statement type %d\n",
|
2001-03-19 02:20:46 +01:00
|
|
|
code);
|
2001-03-27 05:31:06 +02:00
|
|
|
rc += 1;
|
2001-03-19 02:20:46 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2001-03-27 05:31:06 +02:00
|
|
|
|
|
|
|
|
return rc;
|
2001-03-19 02:20:46 +01:00
|
|
|
}
|
2001-03-27 08:27:40 +02:00
|
|
|
|
|
|
|
|
|
2001-03-19 02:20:46 +01:00
|
|
|
/*
|
|
|
|
|
* The process as a whole is surrounded by this code. We generate a
|
|
|
|
|
* start label that the .thread statement can use, and we generate
|
|
|
|
|
* code to terminate the thread.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int draw_process(ivl_process_t net, void*x)
|
|
|
|
|
{
|
2001-03-27 05:31:06 +02:00
|
|
|
int rc = 0;
|
2003-09-04 22:28:05 +02:00
|
|
|
unsigned idx;
|
2001-03-20 02:44:13 +01:00
|
|
|
ivl_scope_t scope = ivl_process_scope(net);
|
2001-03-27 08:27:40 +02:00
|
|
|
ivl_statement_t stmt = ivl_process_stmt(net);
|
2001-03-20 02:44:13 +01:00
|
|
|
|
2003-09-04 22:28:05 +02:00
|
|
|
int push_flag = 0;
|
|
|
|
|
|
|
|
|
|
for (idx = 0 ; idx < ivl_process_attr_cnt(net) ; idx += 1) {
|
|
|
|
|
|
|
|
|
|
ivl_attribute_t attr = ivl_process_attr_val(net, idx);
|
|
|
|
|
|
|
|
|
|
if (strcmp(attr->key, "_ivl_schedule_push") == 0) {
|
|
|
|
|
|
|
|
|
|
push_flag = 1;
|
|
|
|
|
|
|
|
|
|
} else if (strcmp(attr->key, "ivl_combinational") == 0) {
|
|
|
|
|
|
|
|
|
|
push_flag = 1;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-22 06:06:21 +01:00
|
|
|
local_count = 0;
|
2003-03-25 03:15:48 +01:00
|
|
|
fprintf(vvp_out, " .scope S_%p;\n", scope);
|
2001-03-30 07:49:52 +02:00
|
|
|
|
2001-03-19 02:20:46 +01:00
|
|
|
/* Generate the entry label. Just give the thread a number so
|
|
|
|
|
that we ar certain the label is unique. */
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, "T_%u ;\n", thread_count);
|
2002-09-27 18:33:34 +02:00
|
|
|
clear_expression_lookaside();
|
2001-03-19 02:20:46 +01:00
|
|
|
|
|
|
|
|
/* Draw the contents of the thread. */
|
2001-04-18 07:12:03 +02:00
|
|
|
rc += show_statement(stmt, scope);
|
2001-03-19 02:20:46 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Terminate the thread with either an %end instruction (initial
|
|
|
|
|
statements) or a %jmp back to the beginning of the thread. */
|
|
|
|
|
|
|
|
|
|
switch (ivl_process_type(net)) {
|
|
|
|
|
|
|
|
|
|
case IVL_PR_INITIAL:
|
2011-03-30 08:42:26 +02:00
|
|
|
case IVL_PR_FINAL:
|
2001-03-19 02:20:46 +01:00
|
|
|
fprintf(vvp_out, " %%end;\n");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IVL_PR_ALWAYS:
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, " %%jmp T_%u;\n", thread_count);
|
2001-03-19 02:20:46 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-30 08:42:26 +02:00
|
|
|
/* Now write out the directive that tells vvp where the thread
|
|
|
|
|
starts. */
|
|
|
|
|
switch (ivl_process_type(net)) {
|
2001-03-19 02:20:46 +01:00
|
|
|
|
2011-03-30 08:42:26 +02:00
|
|
|
case IVL_PR_INITIAL:
|
|
|
|
|
case IVL_PR_ALWAYS:
|
|
|
|
|
if (push_flag) {
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, " .thread T_%u, $push;\n", thread_count);
|
2011-03-30 08:42:26 +02:00
|
|
|
} else {
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, " .thread T_%u;\n", thread_count);
|
2011-03-30 08:42:26 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IVL_PR_FINAL:
|
2012-08-31 21:35:26 +02:00
|
|
|
fprintf(vvp_out, " .thread T_%u, $final;\n", thread_count);
|
2011-03-30 08:42:26 +02:00
|
|
|
break;
|
2003-09-04 22:28:05 +02:00
|
|
|
}
|
2001-03-19 02:20:46 +01:00
|
|
|
|
|
|
|
|
thread_count += 1;
|
2001-03-27 05:31:06 +02:00
|
|
|
return rc;
|
2001-03-19 02:20:46 +01:00
|
|
|
}
|
|
|
|
|
|
2001-04-02 04:28:12 +02:00
|
|
|
int draw_task_definition(ivl_scope_t scope)
|
|
|
|
|
{
|
|
|
|
|
int rc = 0;
|
|
|
|
|
ivl_statement_t def = ivl_scope_def(scope);
|
|
|
|
|
|
2001-06-18 05:10:34 +02:00
|
|
|
fprintf(vvp_out, "TD_%s ;\n", vvp_mangle_id(ivl_scope_name(scope)));
|
2002-09-27 18:33:34 +02:00
|
|
|
clear_expression_lookaside();
|
2001-04-02 04:28:12 +02:00
|
|
|
|
|
|
|
|
assert(def);
|
2001-04-18 07:12:03 +02:00
|
|
|
rc += show_statement(def, scope);
|
2001-04-02 04:28:12 +02:00
|
|
|
|
|
|
|
|
fprintf(vvp_out, " %%end;\n");
|
|
|
|
|
|
|
|
|
|
thread_count += 1;
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-06 04:28:02 +02:00
|
|
|
int draw_func_definition(ivl_scope_t scope)
|
|
|
|
|
{
|
|
|
|
|
int rc = 0;
|
|
|
|
|
ivl_statement_t def = ivl_scope_def(scope);
|
|
|
|
|
|
2001-06-18 05:10:34 +02:00
|
|
|
fprintf(vvp_out, "TD_%s ;\n", vvp_mangle_id(ivl_scope_name(scope)));
|
2002-09-27 18:33:34 +02:00
|
|
|
clear_expression_lookaside();
|
2001-04-06 04:28:02 +02:00
|
|
|
|
|
|
|
|
assert(def);
|
2001-04-18 07:12:03 +02:00
|
|
|
rc += show_statement(def, scope);
|
2001-04-06 04:28:02 +02:00
|
|
|
|
|
|
|
|
fprintf(vvp_out, " %%end;\n");
|
|
|
|
|
|
|
|
|
|
thread_count += 1;
|
|
|
|
|
return rc;
|
|
|
|
|
}
|