Fix for GitHub issue #96 - support mixed constant/variable delays in vvp.

If all three rise/fall/decay delay values are constant, we can use
the vvp .delay statement variant that takes three literal numbers.
If not, we have to use the variant that takes three net inputs. If
some of the delay values are constant, we need to create constant
drivers for those delay inputs.
This commit is contained in:
Martin Whitaker 2016-04-02 19:49:38 +01:00
parent 4521be4510
commit 20104c92c8
8 changed files with 160 additions and 183 deletions

View File

@ -769,7 +769,7 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const
if (check_delay_count(des)) return;
NetExpr* rise_time, *fall_time, *decay_time;
eval_delays(des, scope, rise_time, fall_time, decay_time);
eval_delays(des, scope, rise_time, fall_time, decay_time, true);
struct attrib_list_t*attrib_list;
unsigned attrib_list_n = 0;
@ -1977,7 +1977,7 @@ void PGModule::elaborate_udp_(Design*des, PUdp*udp, NetScope*scope) const
PDelays tmp_del;
tmp_del.set_delays(overrides_, false);
tmp_del.eval_delays(des, scope, rise_expr, fall_expr,
decay_expr);
decay_expr, true);
}
}

View File

@ -47,8 +47,8 @@ CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@
CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@
LDFLAGS = @LDFLAGS@
O = vvp.o draw_class.o draw_enum.o draw_mux.o draw_substitute.o draw_net_input.o \
draw_switch.o draw_ufunc.o draw_vpi.o \
O = vvp.o draw_class.o draw_delay.o draw_enum.o draw_mux.o draw_net_input.o \
draw_substitute.o draw_switch.o draw_ufunc.o draw_vpi.o \
eval_bool.o \
eval_condit.o \
eval_expr.o eval_object.o eval_real.o eval_string.o \

127
tgt-vvp/draw_delay.c Normal file
View File

@ -0,0 +1,127 @@
/*
* Copyright (c) 2016 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
* General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
# include "vvp_priv.h"
# include <assert.h>
# include <stdlib.h>
# include <string.h>
/*
* This function draws a BUFT to drive a constant delay value.
*/
static char* draw_const_net(void*ptr, char*suffix, uint64_t value)
{
char tmp[64];
char c4_value[69];
unsigned idx;
c4_value[0] = 'C';
c4_value[1] = '4';
c4_value[2] = '<';
for (idx = 0; idx < 64; idx += 1) {
c4_value[66-idx] = (value & 1) ? '1' : '0';
value >>= 1;
}
c4_value[67] = '>';
c4_value[68] = 0;
/* Make the constant an argument to a BUFT, which is
what we use to drive the value. */
fprintf(vvp_out, "L_%p/%s .functor BUFT 1, %s, C4<0>, C4<0>, C4<0>;\n",
ptr, suffix, c4_value);
snprintf(tmp, sizeof tmp, "L_%p/%s", ptr, suffix);
return strdup(tmp);
}
/*
* Draw the appropriate delay statement.
*/
void draw_delay(void*ptr, unsigned wid, const char*input, ivl_expr_t rise_exp,
ivl_expr_t fall_exp, ivl_expr_t decay_exp)
{
char tmp[64];
if (input == 0) {
snprintf(tmp, sizeof tmp, "L_%p/d", ptr);
input = tmp;
}
/* If the delays are all constants then process them here. */
if (number_is_immediate(rise_exp, 64, 0) &&
number_is_immediate(fall_exp, 64, 0) &&
number_is_immediate(decay_exp, 64, 0)) {
assert(! number_is_unknown(rise_exp));
assert(! number_is_unknown(fall_exp));
assert(! number_is_unknown(decay_exp));
fprintf(vvp_out, "L_%p .delay %u "
"(%" PRIu64 ",%" PRIu64 ",%" PRIu64 ") %s;\n",
ptr, wid,
get_number_immediate64(rise_exp),
get_number_immediate64(fall_exp),
get_number_immediate64(decay_exp),
input);
/* For a variable delay we indicate only two delays by setting the
* decay time to zero. */
} else {
char*rise_const = 0;
char*fall_const = 0;
char*decay_const = 0;
const char*rise_str;
const char*fall_str;
const char*decay_str;
if (number_is_immediate(rise_exp, 64, 0)) {
uint64_t value = get_number_immediate64(rise_exp);
rise_str = rise_const = draw_const_net(ptr, "tr", value);
} else {
ivl_signal_t sig = ivl_expr_signal(rise_exp);
assert(sig && ivl_signal_dimensions(sig) == 0);
rise_str = draw_net_input(ivl_signal_nex(sig,0));
}
if (number_is_immediate(fall_exp, 64, 0)) {
uint64_t value = get_number_immediate64(fall_exp);
fall_str = fall_const = draw_const_net(ptr, "tf", value);
} else {
ivl_signal_t sig = ivl_expr_signal(fall_exp);
assert(sig && ivl_signal_dimensions(sig) == 0);
fall_str = draw_net_input(ivl_signal_nex(sig,0));
}
if (decay_exp == 0) {
decay_str = "0";
} else if (number_is_immediate(decay_exp, 64, 0)) {
uint64_t value = get_number_immediate64(decay_exp);
decay_str = decay_const = draw_const_net(ptr, "td", value);
} else {
ivl_signal_t sig = ivl_expr_signal(decay_exp);
assert(sig && ivl_signal_dimensions(sig) == 0);
decay_str = draw_net_input(ivl_signal_nex(sig,0));
}
fprintf(vvp_out, "L_%p .delay %u %s, %s, %s, %s;\n",
ptr, wid, input, rise_str, fall_str, decay_str);
free(rise_const);
free(fall_const);
free(decay_const);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002-2015 Stephen Williams (steve@icarus.com)
* Copyright (c) 2002-2016 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -51,44 +51,8 @@ static void draw_lpm_mux_ab(ivl_lpm_t net, const char*muxz)
if (data_type_of_nexus(ivl_lpm_q(net)) == IVL_VT_REAL)
dly_width = 0;
draw_delay(net, dly_width, 0, d_rise, d_fall, d_decay);
dly = "/d";
if (number_is_immediate(d_rise, 64, 0) &&
number_is_immediate(d_fall, 64, 0) &&
number_is_immediate(d_decay, 64, 0)) {
assert( ! number_is_unknown(d_rise));
assert( ! number_is_unknown(d_fall));
assert( ! number_is_unknown(d_decay));
fprintf(vvp_out, "L_%p .delay %u (%" PRIu64 ",%" PRIu64 ",%" PRIu64 ") L_%p/d;\n",
net, dly_width,
get_number_immediate64(d_rise),
get_number_immediate64(d_fall),
get_number_immediate64(d_decay), net);
} else {
ivl_signal_t sig;
// We do not currently support calculating the decay from
// the rise and fall variable delays.
assert(d_decay != 0);
assert(ivl_expr_type(d_rise) == IVL_EX_SIGNAL);
assert(ivl_expr_type(d_fall) == IVL_EX_SIGNAL);
assert(ivl_expr_type(d_decay) == IVL_EX_SIGNAL);
fprintf(vvp_out, "L_%p .delay %u L_%p/d",
net, dly_width, net);
sig = ivl_expr_signal(d_rise);
assert(ivl_signal_dimensions(sig) == 0);
fprintf(vvp_out, ", v%p_0", sig);
sig = ivl_expr_signal(d_fall);
assert(ivl_signal_dimensions(sig) == 0);
fprintf(vvp_out, ", v%p_0", sig);
sig = ivl_expr_signal(d_decay);
assert(ivl_signal_dimensions(sig) == 0);
fprintf(vvp_out, ", v%p_0;\n", sig);
}
}
input[0] = draw_net_input(ivl_lpm_data(net,0));

View File

@ -330,9 +330,11 @@ static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr)
cptr = ivl_nexus_ptr_con(nptr);
if (cptr) {
char tmp[64];
char *result = 0;
ivl_expr_t d_rise, d_fall, d_decay;
unsigned dly_width = 0;
char *dly;
/* Constants should have exactly 1 pin, with a literal value. */
assert(nptr_pin == 0);
@ -368,68 +370,17 @@ static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr)
d_fall = ivl_const_delay(cptr, 1);
d_decay = ivl_const_delay(cptr, 2);
/* We have a delayed constant, so we need to build some code. */
dly = "";
if (d_rise != 0) {
char tmp[128];
fprintf(vvp_out, "L_%p/d .functor BUFT 1, %s, "
"C4<0>, C4<0>, C4<0>;\n", cptr, result);
free(result);
/* Is this a fixed or variable delay? */
if (number_is_immediate(d_rise, 64, 0) &&
number_is_immediate(d_fall, 64, 0) &&
number_is_immediate(d_decay, 64, 0)) {
assert(! number_is_unknown(d_rise));
assert(! number_is_unknown(d_fall));
assert(! number_is_unknown(d_decay));
fprintf(vvp_out, "L_%p .delay %u "
"(%" PRIu64 ",%" PRIu64 ",%" PRIu64 ") L_%p/d;\n",
cptr, dly_width,
get_number_immediate64(d_rise),
get_number_immediate64(d_fall),
get_number_immediate64(d_decay), cptr);
} else {
ivl_signal_t sig;
// We do not currently support calculating the decay
// from the rise and fall variable delays.
assert(d_decay != 0);
assert(ivl_expr_type(d_rise) == IVL_EX_SIGNAL);
assert(ivl_expr_type(d_fall) == IVL_EX_SIGNAL);
assert(ivl_expr_type(d_decay) == IVL_EX_SIGNAL);
fprintf(vvp_out, "L_%p .delay %u L_%p/d",
cptr, dly_width, cptr);
sig = ivl_expr_signal(d_rise);
assert(ivl_signal_dimensions(sig) == 0);
fprintf(vvp_out, ", v%p_0", sig);
sig = ivl_expr_signal(d_fall);
assert(ivl_signal_dimensions(sig) == 0);
fprintf(vvp_out, ", v%p_0", sig);
sig = ivl_expr_signal(d_decay);
assert(ivl_signal_dimensions(sig) == 0);
fprintf(vvp_out, ", v%p_0;\n", sig);
}
snprintf(tmp, sizeof tmp, "L_%p", cptr);
result = strdup(tmp);
} else {
char tmp[64];
fprintf(vvp_out, "L_%p .functor BUFT 1, %s, "
"C4<0>, C4<0>, C4<0>;\n", cptr, result);
free(result);
snprintf(tmp, sizeof tmp, "L_%p", cptr);
result = strdup(tmp);
draw_delay(cptr, dly_width, 0, d_rise, d_fall, d_decay);
dly = "/d";
}
fprintf(vvp_out, "L_%p%s .functor BUFT 1, %s, C4<0>, C4<0>, C4<0>;\n",
cptr, dly, result);
free(result);
return result;
snprintf(tmp, sizeof tmp, "L_%p", cptr);
return strdup(tmp);
}
lpm = ivl_nexus_ptr_lpm(nptr);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2010,2012 Stephen Williams (steve@icarus.com)
* Copyright (c) 2008-2016 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -38,18 +38,6 @@ void draw_switch_in_scope(ivl_switch_t sw)
ivl_expr_t fall_exp = ivl_switch_delay(sw, 1);
ivl_expr_t decay_exp= ivl_switch_delay(sw, 2);
if ((rise_exp || fall_exp || decay_exp) &&
(!number_is_immediate(rise_exp, 64, 0) ||
number_is_unknown(rise_exp) ||
!number_is_immediate(fall_exp, 64, 0) ||
number_is_unknown(fall_exp) ||
!number_is_immediate(decay_exp, 64, 0) ||
number_is_unknown(decay_exp))) {
fprintf(stderr, "%s:%u: error: Invalid tranif delay expression.\n",
ivl_switch_file(sw), ivl_switch_lineno(sw));
vvp_errors += 1;
}
island = ivl_switch_island(sw);
if (ivl_island_flag_test(island, 0) == 0)
draw_tran_island(island);
@ -67,24 +55,18 @@ void draw_switch_in_scope(ivl_switch_t sw)
char str_e_buf[4 + 2*sizeof(void*)];
if (enable && rise_exp) {
assert(fall_exp && decay_exp);
/* If the enable has a delay, then generate a .delay
node to delay the input by the specified amount. Do
the delay outside of the island so that the island
processing doesn't have to deal with it. */
const char*raw = draw_net_input(enable);
draw_delay(sw, 1, raw, rise_exp, fall_exp, decay_exp);
snprintf(str_e_buf, sizeof str_e_buf, "p%p", sw);
str_e = str_e_buf;
fprintf(vvp_out, "%s/d .delay 1 "
"(%" PRIu64 ",%" PRIu64 ",%" PRIu64 ") %s;\n",
str_e, get_number_immediate64(rise_exp),
get_number_immediate64(fall_exp),
get_number_immediate64(decay_exp), raw);
fprintf(vvp_out, "%s .import I%p, %s/d;\n", str_e, island, str_e);
fprintf(vvp_out, "%s .import I%p, L_%p;\n", str_e, island, sw);
} else if (enable) {
str_e = draw_island_net_input(island, enable);

View File

@ -261,6 +261,13 @@ extern void show_stmt_file_line(ivl_statement_t net, const char*desc);
extern int test_immediate_vec4_ok(ivl_expr_t expr);
extern void draw_immediate_vec4(ivl_expr_t expr, const char*opcode);
/*
* Draw a delay statement.
*/
extern void draw_delay(void*ptr, unsigned wid, const char*input,
ivl_expr_t rise_exp, ivl_expr_t fall_exp,
ivl_expr_t decay_exp);
/*
* These functions manage word register allocation.
*/

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com)
* Copyright (c) 2001-2016 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -754,7 +754,7 @@ static unsigned need_delay(ivl_net_logic_t lptr)
/*
* Draw the appropriate delay statement. Returns zero if there is not a delay.
*/
static void draw_delay(ivl_net_logic_t lptr)
static void draw_logic_delay(ivl_net_logic_t lptr)
{
ivl_expr_t rise_exp = ivl_logic_delay(lptr, 0);
ivl_expr_t fall_exp = ivl_logic_delay(lptr, 1);
@ -767,49 +767,7 @@ static void draw_delay(ivl_net_logic_t lptr)
delay_wid = 0;
}
/* If the delays are all constants then process them here. */
if (number_is_immediate(rise_exp, 64, 0) &&
number_is_immediate(fall_exp, 64, 0) &&
number_is_immediate(decay_exp, 64, 0)) {
assert(! number_is_unknown(rise_exp));
assert(! number_is_unknown(fall_exp));
assert(! number_is_unknown(decay_exp));
fprintf(vvp_out, "L_%p .delay %u "
"(%" PRIu64 ",%" PRIu64 ",%" PRIu64 ") L_%p/d;\n",
lptr, delay_wid,
get_number_immediate64(rise_exp),
get_number_immediate64(fall_exp),
get_number_immediate64(decay_exp), lptr);
/* For a variable delay we indicate only two delays by setting the
* decay time to zero. */
} else {
ivl_signal_t sig;
assert(ivl_expr_type(rise_exp) == IVL_EX_SIGNAL);
assert(ivl_expr_type(fall_exp) == IVL_EX_SIGNAL);
assert((decay_exp == 0) ||
(ivl_expr_type(decay_exp) == IVL_EX_SIGNAL));
fprintf(vvp_out, "L_%p .delay %u L_%p/d", lptr, delay_wid, lptr);
sig = ivl_expr_signal(rise_exp);
assert(ivl_signal_dimensions(sig) == 0);
fprintf(vvp_out, ", %s", draw_net_input(ivl_signal_nex(sig,0)));
sig = ivl_expr_signal(fall_exp);
assert(ivl_signal_dimensions(sig) == 0);
fprintf(vvp_out, ", %s", draw_net_input(ivl_signal_nex(sig,0)));
if (decay_exp) {
sig = ivl_expr_signal(decay_exp);
assert(ivl_signal_dimensions(sig) == 0);
fprintf(vvp_out, ", %s;\n",
draw_net_input(ivl_signal_nex(sig,0)));
} else {
fprintf(vvp_out, ", 0;\n");
}
}
draw_delay(lptr, delay_wid, 0, rise_exp, fall_exp, decay_exp);
}
static void draw_udp_def(ivl_udp_t udp)
@ -945,7 +903,7 @@ static void draw_udp_in_scope(ivl_net_logic_t lptr)
fprintf(vvp_out, ";\n");
/* Generate a delay when needed. */
if (need_delay_flag) draw_delay(lptr);
if (need_delay_flag) draw_logic_delay(lptr);
}
static void draw_logic_in_scope(ivl_net_logic_t lptr)
@ -1149,7 +1107,7 @@ static void draw_logic_in_scope(ivl_net_logic_t lptr)
free(input_strings);
/* Generate a delay when needed. */
if (need_delay_flag) draw_delay(lptr);
if (need_delay_flag) draw_logic_delay(lptr);
}
static void draw_event_in_scope(ivl_event_t obj)
@ -1349,20 +1307,8 @@ static const char* draw_lpm_output_delay(ivl_lpm_t net, ivl_variable_type_t dt)
const char*dly = "";
if (d_rise != 0) {
assert(number_is_immediate(d_rise, 64, 0));
assert(number_is_immediate(d_fall, 64, 0));
assert(number_is_immediate(d_decay, 64, 0));
assert(! number_is_unknown(d_rise));
assert(! number_is_unknown(d_fall));
assert(! number_is_unknown(d_decay));
draw_delay(net, width, 0, d_rise, d_fall, d_decay);
dly = "/d";
fprintf(vvp_out, "L_%p .delay %u (%" PRIu64 ",%" PRIu64 ",%" PRIu64 ")"
" L_%p/d;\n", net, width,
get_number_immediate64(d_rise),
get_number_immediate64(d_fall),
get_number_immediate64(d_decay), net);
}
return dly;