From dc7bb9149eb7e1e27eb8300e3893a5e66d7250c6 Mon Sep 17 00:00:00 2001 From: Cary R Date: Thu, 13 Jan 2011 14:11:41 -0800 Subject: [PATCH] vlog95: Add support for more statements and a delay expression This patch adds the ability to print a delay expression (it is rescaled to the module timescale) and adds support the following statement types: nonblocking assignment with a delay (event control is still missing) condition (if/else) forever repeat while (Icarus translated for loops to while loops) fork procedural continuous assign deassign force release --- tgt-vlog95/Makefile.in | 2 +- tgt-vlog95/expr.c | 10 +- tgt-vlog95/misc.c | 57 ++++++++ tgt-vlog95/scope.c | 8 +- tgt-vlog95/stmt.c | 284 ++++++++++++++++++++++++++++++--------- tgt-vlog95/vlog95_priv.h | 6 + 6 files changed, 301 insertions(+), 66 deletions(-) create mode 100644 tgt-vlog95/misc.c diff --git a/tgt-vlog95/Makefile.in b/tgt-vlog95/Makefile.in index 0c027f6b0..e1108ace9 100644 --- a/tgt-vlog95/Makefile.in +++ b/tgt-vlog95/Makefile.in @@ -44,7 +44,7 @@ CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@ CFLAGS = @WARNING_FLAGS@ @CFLAGS@ LDFLAGS = @LDFLAGS@ -O = vlog95.o expr.o scope.o stmt.o +O = vlog95.o expr.o misc.o scope.o stmt.o all: dep vlog95.tgt diff --git a/tgt-vlog95/expr.c b/tgt-vlog95/expr.c index 4020dd21b..10a185137 100644 --- a/tgt-vlog95/expr.c +++ b/tgt-vlog95/expr.c @@ -24,7 +24,7 @@ # include "config.h" # include "vlog95_priv.h" -// HERE: Do we need to use scope and wid? +// HERE: Do we need to use wid? static void emit_expr_binary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { @@ -140,6 +140,11 @@ static void emit_expr_concat(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) if (repeat != 1) fprintf(vlog_out, "}"); } +static void emit_expr_delay(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) +{ + emit_scaled_delay(scope, ivl_expr_delay_val(expr)); +} + static void emit_expr_number(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { const char *bits = ivl_expr_bits(expr); @@ -293,6 +298,9 @@ void emit_expr(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) case IVL_EX_CONCAT: emit_expr_concat(scope, expr, wid); break; + case IVL_EX_DELAY: + emit_expr_delay(scope, expr, wid); + break; case IVL_EX_NUMBER: emit_expr_number(scope, expr, wid); break; diff --git a/tgt-vlog95/misc.c b/tgt-vlog95/misc.c new file mode 100644 index 000000000..133a92c75 --- /dev/null +++ b/tgt-vlog95/misc.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 Cary R. (cygcary@yahoo.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * + * This is the vlog95 target module. It generates a 1364-1995 compliant + * netlist from the input netlist. The generated netlist is expected to + * be simulation equivalent to the original. + */ + +# include +# include "config.h" +# include "vlog95_priv.h" + +void emit_scaled_delay(ivl_scope_t scope, uint64_t delay) +{ + int scale = ivl_scope_time_units(scope) - sim_precision; + int pre = ivl_scope_time_units(scope) - ivl_scope_time_precision(scope); + char *frac; + uint8_t real_dly = 0; + assert(scale >= 0); + assert(pre >= 0); + assert(scale >= pre); + frac = (char *)malloc(pre+1); + frac[pre] = 0; + for (/* none */; scale > 0; scale -= 1) { + if (scale > pre) { + assert((delay % 10) == 0); + } else { + frac[scale-1] = (delay % 10) + '0'; + if (frac[scale-1] != '0') { + real_dly = 1; + } else if (!real_dly) { + frac[scale-1] = 0; + } + } + delay /= 10; + } + fprintf(vlog_out, "%"PRIu64, delay); + if (real_dly) { + fprintf(vlog_out, ".%s", frac); + } + free(frac); +} diff --git a/tgt-vlog95/scope.c b/tgt-vlog95/scope.c index d937c06d6..9ab071c5d 100644 --- a/tgt-vlog95/scope.c +++ b/tgt-vlog95/scope.c @@ -305,16 +305,18 @@ int emit_scope(ivl_scope_t scope, ivl_scope_t parent) switch (sc_type) { case IVL_SCT_MODULE: assert(indent == 0); - fprintf(vlog_out, "endmodule\n"); + fprintf(vlog_out, "endmodule /* %s */\n", ivl_scope_tname(scope)); if (ivl_scope_is_cell(scope)) { fprintf(vlog_out, "`endcelldefine\n"); } break; case IVL_SCT_FUNCTION: - fprintf(vlog_out, "%*cendfunction\n", indent, ' '); + fprintf(vlog_out, "%*cendfunction /* %s */\n", indent, ' ', + ivl_scope_tname(scope)); break; case IVL_SCT_TASK: - fprintf(vlog_out, "%*cendtask\n", indent, ' '); + fprintf(vlog_out, "%*cendtask /* %s */\n", indent, ' ', + ivl_scope_tname(scope)); break; default: assert(0); diff --git a/tgt-vlog95/stmt.c b/tgt-vlog95/stmt.c index 48e05af17..a39db1b8f 100644 --- a/tgt-vlog95/stmt.c +++ b/tgt-vlog95/stmt.c @@ -21,120 +21,232 @@ * be simulation equivalent to the original. */ -# include -# include # include "config.h" # include "vlog95_priv.h" -static unsigned indent_save = 0; +static unsigned single_indent = 0; -static void emit_stmt_lval(ivl_lval_t lval) +static unsigned get_indent() +{ + if (single_indent) { + single_indent = 0; + return single_indent; + } + return indent; +} + +static void emit_stmt_block_body(ivl_scope_t scope, ivl_statement_t stmt) +{ + unsigned idx, count = ivl_stmt_block_count(stmt); + indent += indent_incr; + for (idx = 0; idx < count; idx += 1) { + emit_stmt(scope, ivl_stmt_block_stmt(stmt, idx)); + } + assert(indent >= indent_incr); + indent -= indent_incr; +} + +static void emit_stmt_inter_delay(ivl_scope_t scope, ivl_statement_t stmt) +{ + ivl_expr_t delay = ivl_stmt_delay_expr(stmt); + unsigned nevents = ivl_stmt_nevent(stmt); +// HERE: No support for event based delays/ + if (nevents) { + assert(delay == 0); + assert(0); + } + if (delay) { + assert(nevents == 0); + fprintf(vlog_out, "#"); + emit_expr(scope, delay, 0); + fprintf(vlog_out, " "); + } +} + +static void emit_stmt_lval_piece(ivl_lval_t lval) { ivl_signal_t sig = ivl_lval_sig(lval); fprintf(vlog_out, "%s", ivl_signal_basename(sig)); // HERE: Need support for bit, part or array word. // ivl_lval_width and ivl_lval_part_off is used for part select. // If the lval width is less than the signal width this is a zero based PS. -// ivl_lval_idx is used for an array select. +// ivl_lval_idx is used for an array select. Handle non-zero based and +// different endian accesses. } -static void emit_stmt_assign(ivl_scope_t scope, ivl_statement_t stmt) +static unsigned emit_stmt_lval(ivl_statement_t stmt) { unsigned idx; unsigned count = ivl_stmt_lvals(stmt); unsigned wid = 0; - fprintf(vlog_out, "%*c", indent, ' '); if (count > 1) { ivl_lval_t lval = ivl_stmt_lval(stmt, 0); wid += ivl_lval_width(lval); fprintf(vlog_out, "{"); - emit_stmt_lval(lval); + emit_stmt_lval_piece(lval); for (idx = 1; idx < count; idx += 1) { fprintf(vlog_out, ", "); lval = ivl_stmt_lval(stmt, idx); wid += ivl_lval_width(lval); - emit_stmt_lval(lval); + emit_stmt_lval_piece(lval); } fprintf(vlog_out, "}"); } else { ivl_lval_t lval = ivl_stmt_lval(stmt, 0); wid = ivl_lval_width(lval); - emit_stmt_lval(lval); + emit_stmt_lval_piece(lval); } + return wid; +} + +static void emit_stmt_assign(ivl_scope_t scope, ivl_statement_t stmt) +{ + unsigned wid; + fprintf(vlog_out, "%*c", get_indent(), ' '); +// HERE: Do we need to calculate the width? The compiler should have already +// done this for us. + wid = emit_stmt_lval(stmt); fprintf(vlog_out, " = "); emit_expr(scope, ivl_stmt_rval(stmt), wid); fprintf(vlog_out, ";\n"); } +static void emit_stmt_assign_nb(ivl_scope_t scope, ivl_statement_t stmt) +{ + unsigned wid; + fprintf(vlog_out, "%*c", get_indent(), ' '); +// HERE: Do we need to calculate the width? The compiler should have already +// done this for us. + wid = emit_stmt_lval(stmt); + fprintf(vlog_out, " <= "); + emit_stmt_inter_delay(scope, stmt); + emit_expr(scope, ivl_stmt_rval(stmt), wid); + fprintf(vlog_out, ";\n"); +} + void emit_stmt_block(ivl_scope_t scope, ivl_statement_t stmt) { - unsigned idx, count = ivl_stmt_block_count(stmt); - fprintf(vlog_out, "%*cbegin\n", indent, ' '); - indent += indent_incr; - for (idx = 0; idx < count; idx += 1) { - emit_stmt(scope, ivl_stmt_block_stmt(stmt, idx)); - } - assert(indent >= indent_incr); - indent -= indent_incr; - fprintf(vlog_out, "%*cend\n", indent, ' '); + fprintf(vlog_out, "%*cbegin\n", get_indent(), ' '); + emit_stmt_block_body(scope, stmt); + fprintf(vlog_out, "%*cend\n", get_indent(), ' '); } void emit_stmt_block_named(ivl_scope_t scope, ivl_statement_t stmt) { ivl_scope_t my_scope = ivl_stmt_block_scope(stmt); - unsigned idx, count = ivl_stmt_block_count(stmt); - fprintf(vlog_out, "%*cbegin: %s\n", indent, ' ', + fprintf(vlog_out, "%*cbegin: %s\n", get_indent(), ' ', ivl_scope_basename(my_scope)); - indent += indent_incr; - for (idx = 0; idx < count; idx += 1) { - emit_stmt(scope, ivl_stmt_block_stmt(stmt, idx)); + emit_stmt_block_body(scope, stmt); + fprintf(vlog_out, "%*cend /* %s */\n", get_indent(), ' ', + ivl_scope_basename(my_scope)); +} + +static void emit_stmt_case(ivl_scope_t scope, ivl_statement_t stmt) +{ + assert(0); +} + +static void emit_stmt_cassign(ivl_scope_t scope, ivl_statement_t stmt) +{ + unsigned wid; + fprintf(vlog_out, "%*cassign ", get_indent(), ' '); +// HERE: Do we need to calculate the width? The compiler should have already +// done this for us. + wid = emit_stmt_lval(stmt); + fprintf(vlog_out, " = "); + emit_expr(scope, ivl_stmt_rval(stmt), wid); + fprintf(vlog_out, ";\n"); +} + +static void emit_stmt_condit(ivl_scope_t scope, ivl_statement_t stmt) +{ + fprintf(vlog_out, "%*cif (", get_indent(), ' '); + emit_expr(scope, ivl_stmt_cond_expr(stmt), 0); + fprintf(vlog_out, ")"); + if (ivl_stmt_cond_true(stmt)) { + single_indent = 1; + emit_stmt(scope, ivl_stmt_cond_true(stmt)); + } else { + fprintf(vlog_out, ";\n"); } - assert(indent >= indent_incr); - indent -= indent_incr; - fprintf(vlog_out, "%*cend /* %s */\n", indent, ' ', - ivl_scope_basename(my_scope)); + if (ivl_stmt_cond_false(stmt)) { + fprintf(vlog_out, "%*celse", get_indent(), ' '); + single_indent = 1; + emit_stmt(scope, ivl_stmt_cond_false(stmt)); + } +} + +static void emit_stmt_deassign(ivl_scope_t scope, ivl_statement_t stmt) +{ + fprintf(vlog_out, "%*cdeassign ", get_indent(), ' '); + (void) emit_stmt_lval(stmt); + fprintf(vlog_out, ";\n"); } static void emit_stmt_delay(ivl_scope_t scope, ivl_statement_t stmt) { - uint64_t delay = ivl_stmt_delay_val(stmt); - int scale = ivl_scope_time_units(scope) - sim_precision; - int pre = ivl_scope_time_units(scope) - ivl_scope_time_precision(scope); - char *frac; - uint8_t real_dly = 0; - assert(scale >= 0); - assert(pre >= 0); - assert(scale >= pre); - frac = (char *)malloc(pre+1); - frac[pre] = 0; - for (/* none */; scale > 0; scale -= 1) { - if (scale > pre) { - assert((delay % 10) == 0); - } else { - frac[scale-1] = (delay % 10) + '0'; - if (frac[scale-1] != '0') { - real_dly = 1; - } else if (!real_dly) { - frac[scale-1] = 0; - } - } - delay /= 10; - } - fprintf(vlog_out, "%*c#%"PRIu64, indent, ' ', delay); - if (real_dly) { - fprintf(vlog_out, ".%s", frac); - } - free(frac); - indent_save = indent; - indent = 1; + fprintf(vlog_out, "%*c#", get_indent(), ' '); + emit_scaled_delay(scope, ivl_stmt_delay_val(stmt)); + single_indent = 1; + emit_stmt(scope, ivl_stmt_sub_stmt(stmt)); +} + +static void emit_stmt_force(ivl_scope_t scope, ivl_statement_t stmt) +{ + unsigned wid; + fprintf(vlog_out, "%*cforce ", get_indent(), ' '); +// HERE: Do we need to calculate the width? The compiler should have already +// done this for us. + wid = emit_stmt_lval(stmt); + fprintf(vlog_out, " = "); + emit_expr(scope, ivl_stmt_rval(stmt), wid); + fprintf(vlog_out, ";\n"); +} + +static void emit_stmt_forever(ivl_scope_t scope, ivl_statement_t stmt) +{ + fprintf(vlog_out, "%*cforever", get_indent(), ' '); + single_indent = 1; + emit_stmt(scope, ivl_stmt_sub_stmt(stmt)); +} + +static void emit_stmt_fork(ivl_scope_t scope, ivl_statement_t stmt) +{ + fprintf(vlog_out, "%*cfork\n", get_indent(), ' '); + emit_stmt_block_body(scope, stmt); + fprintf(vlog_out, "%*cjoin\n", get_indent(), ' '); +} + +static void emit_stmt_fork_named(ivl_scope_t scope, ivl_statement_t stmt) +{ + ivl_scope_t my_scope = ivl_stmt_block_scope(stmt); + fprintf(vlog_out, "%*cfork: %s\n", get_indent(), ' ', + ivl_scope_basename(my_scope)); + emit_stmt_block_body(scope, stmt); + fprintf(vlog_out, "%*cjoin /* %s */\n", get_indent(), ' ', + ivl_scope_basename(my_scope)); +} + +static void emit_stmt_release(ivl_scope_t scope, ivl_statement_t stmt) +{ + fprintf(vlog_out, "%*crelease ", get_indent(), ' '); + (void) emit_stmt_lval(stmt); + fprintf(vlog_out, ";\n"); +} + +static void emit_stmt_repeat(ivl_scope_t scope, ivl_statement_t stmt) +{ + fprintf(vlog_out, "%*crepeat (", get_indent(), ' '); + emit_expr(scope, ivl_stmt_cond_expr(stmt), 0); + fprintf(vlog_out, ")"); + single_indent = 1; emit_stmt(scope, ivl_stmt_sub_stmt(stmt)); - indent = indent_save; } static void emit_stmt_stask(ivl_scope_t scope, ivl_statement_t stmt) { unsigned idx, count = ivl_stmt_parm_count(stmt); - fprintf(vlog_out, "%*c%s", indent, ' ', ivl_stmt_name(stmt)); + fprintf(vlog_out, "%*c%s", get_indent(), ' ', ivl_stmt_name(stmt)); if (count != 0) { fprintf(vlog_out, "("); for (idx = 0; idx < count; idx += 1) { @@ -145,15 +257,28 @@ static void emit_stmt_stask(ivl_scope_t scope, ivl_statement_t stmt) fprintf(vlog_out, ";\n"); } +static void emit_stmt_while(ivl_scope_t scope, ivl_statement_t stmt) +{ + fprintf(vlog_out, "%*cwhile (", get_indent(), ' '); + emit_expr(scope, ivl_stmt_cond_expr(stmt), 0); + fprintf(vlog_out, ")"); + single_indent = 1; + emit_stmt(scope, ivl_stmt_sub_stmt(stmt)); +} + void emit_stmt(ivl_scope_t scope, ivl_statement_t stmt) { switch(ivl_statement_type(stmt)) { case IVL_ST_NOOP: + single_indent = 0; fprintf(vlog_out, ";\n"); break; case IVL_ST_ASSIGN: emit_stmt_assign(scope, stmt); break; + case IVL_ST_ASSIGN_NB: + emit_stmt_assign_nb(scope, stmt); + break; case IVL_ST_BLOCK: if (ivl_stmt_block_scope(stmt)) { emit_stmt_block_named(scope, stmt); @@ -161,14 +286,51 @@ void emit_stmt(ivl_scope_t scope, ivl_statement_t stmt) emit_stmt_block(scope, stmt); } break; + case IVL_ST_CASE: + case IVL_ST_CASER: + case IVL_ST_CASEX: + case IVL_ST_CASEZ: + emit_stmt_case(scope, stmt); + break; + case IVL_ST_CASSIGN: + emit_stmt_cassign(scope, stmt); + break; + case IVL_ST_CONDIT: + emit_stmt_condit(scope, stmt); + break; + case IVL_ST_DEASSIGN: + emit_stmt_deassign(scope, stmt); + break; case IVL_ST_DELAY: emit_stmt_delay(scope, stmt); break; + case IVL_ST_FORCE: + emit_stmt_force(scope, stmt); + break; + case IVL_ST_FOREVER: + emit_stmt_forever(scope, stmt); + break; + case IVL_ST_FORK: + if (ivl_stmt_block_scope(stmt)) { + emit_stmt_fork_named(scope, stmt); + } else { + emit_stmt_fork(scope, stmt); + } + break; + case IVL_ST_RELEASE: + emit_stmt_release(scope, stmt); + break; + case IVL_ST_REPEAT: + emit_stmt_repeat(scope, stmt); + break; case IVL_ST_STASK: emit_stmt_stask(scope, stmt); break; + case IVL_ST_WHILE: + emit_stmt_while(scope, stmt); + break; default: - fprintf(vlog_out, "%*c;\n", indent, ' '); + fprintf(vlog_out, "%*c;\n", get_indent(), ' '); fprintf(stderr, "%s:%u: vlog95 error: Unknown statement " "type (%d).\n", ivl_stmt_file(stmt), diff --git a/tgt-vlog95/vlog95_priv.h b/tgt-vlog95/vlog95_priv.h index f60c146b3..8d623007b 100644 --- a/tgt-vlog95/vlog95_priv.h +++ b/tgt-vlog95/vlog95_priv.h @@ -25,6 +25,7 @@ # include "config.h" # include "ivl_target.h" +# include # include # include @@ -64,4 +65,9 @@ extern void emit_stmt(ivl_scope_t scope, ivl_statement_t stmt); */ extern void emit_expr(ivl_scope_t scope, ivl_expr_t expr, unsigned width); +/* + * Emit a delay scaled to the current timescale (units and precision). + */ +extern void emit_scaled_delay(ivl_scope_t scope, uint64_t delay); + #endif /* __vlog95_priv_H */