diff --git a/tgt-vlog95/Makefile.in b/tgt-vlog95/Makefile.in index 63fa02cfc..0c027f6b0 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 +O = vlog95.o expr.o scope.o stmt.o all: dep vlog95.tgt diff --git a/tgt-vlog95/expr.c b/tgt-vlog95/expr.c new file mode 100644 index 000000000..ca8c26704 --- /dev/null +++ b/tgt-vlog95/expr.c @@ -0,0 +1,274 @@ +/* + * 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 "config.h" +# include "vlog95_priv.h" + +// HERE: Do we need to use scope and wid? + +static void emit_expr_binary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) +{ + char *oper = ""; + switch(ivl_expr_opcode(expr)) { + case '+': oper = "+"; break; + case '-': oper = "-"; break; + case '*': oper = "*"; break; + case '/': oper = "/"; break; + case '%': oper = "%"; break; + case 'p': oper = "**"; break; + case 'E': oper = "==="; break; + case 'e': oper = "=="; break; + case 'N': oper = "!=="; break; + case 'n': oper = "!="; break; + case '<': oper = "<"; break; + case 'L': oper = "<="; break; + case '>': oper = ">"; break; + case 'G': oper = ">="; break; + case '&': oper = "&"; break; + case '|': oper = "|"; break; + case '^': oper = "^"; break; + case 'A': oper = "&"; break; + case 'O': oper = "|"; break; + case 'X': oper = "~^"; break; + case 'a': oper = "&&"; break; + case 'o': oper = "||"; break; + case 'l': oper = "<<"; break; + case 'r': oper = ">>"; break; + case 'R': oper = ">>>"; break; + } + + fprintf(vlog_out, "("); + switch(ivl_expr_opcode(expr)) { + case '+': + case '-': + case '*': + case '/': + case '%': + case 'E': + case 'e': + case 'N': + case 'n': + case '<': + case 'L': + case '>': + case 'G': + case '&': + case '|': + case '^': + case 'X': +// HERE: Do these two need to be done differently (wid = 0)? + case 'a': + case 'o': +// HERE: Do these two need to be done differently (r-value wid = 0)? + case 'l': + case 'r': + case 'R': + emit_expr(scope, ivl_expr_oper1(expr), wid); + fprintf(vlog_out, " %s ", oper); + emit_expr(scope, ivl_expr_oper2(expr), wid); + break; + case 'A': + case 'O': + fprintf(vlog_out, "~("); + emit_expr(scope, ivl_expr_oper1(expr), wid); + fprintf(vlog_out, " %s ", oper); + emit_expr(scope, ivl_expr_oper2(expr), wid); + fprintf(vlog_out, ")"); + break; + case 'p': +// HERE: We can convert 2 ** to 1 << + emit_expr(scope, ivl_expr_oper1(expr), wid); + fprintf(vlog_out, " ** "); + emit_expr(scope, ivl_expr_oper2(expr), 0); + fprintf(stderr, "%s:%u: vlog95 error: Power operator is not " + "supported.\n", + ivl_expr_file(expr), ivl_expr_lineno(expr)); + vlog_errors += 1; + break; + default: + emit_expr(scope, ivl_expr_oper1(expr), wid); + fprintf(vlog_out, ""); + emit_expr(scope, ivl_expr_oper2(expr), wid); + fprintf(stderr, "%s:%u: vlog95 error: Unknown expression " + "operator (%c).\n", + ivl_expr_file(expr), + ivl_expr_lineno(expr), + ivl_expr_opcode(expr)); + vlog_errors += 1; + break; + } + fprintf(vlog_out, ")"); +} + +static void emit_expr_number(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) +{ + const char *bits = ivl_expr_bits(expr); + unsigned nbits = ivl_expr_width(expr); + /* A signed value can only be 32 bits long since it can only be + * represented as an integer. We can trim any matching MSB bits + * to make it fit. We do not support undefined bits. */ + if (ivl_expr_signed(expr)) { + unsigned trim_wid = nbits - 1; + const char msb = bits[trim_wid]; + for (/* none */; trim_wid > 0; trim_wid -= 1) { + if (msb != bits[trim_wid]) { + trim_wid += 1; + break; + } + } + trim_wid += 1; + if (trim_wid > 32U) { + fprintf(vlog_out, ""); + fprintf(stderr, "%s:%u: vlog95 error: Signed number is " + "greater than 32 bits (%u) and cannot be safely " + "represented.\n", + ivl_expr_file(expr), ivl_expr_lineno(expr), trim_wid); + vlog_errors += 1; + return; + } else { + unsigned idx; + int32_t value = 0; + for (idx = 0; idx < trim_wid; idx += 1) { + if (bits[idx] == '1') value |= 1U << idx; + else if (bits[idx] != '0') { + fprintf(vlog_out, ""); + fprintf(stderr, "%s:%u: vlog95 error: Signed " + "number has an undefined bit " + "and cannot be represented.\n", + ivl_expr_file(expr), + ivl_expr_lineno(expr)); + vlog_errors += 1; + return; + } + } + /* Sign extend as needed. */ + if (msb == '1' && trim_wid < 32U) { + value |= ~((1U << trim_wid) - 1U); + } + fprintf(vlog_out, "%"PRId32, value); + } + /* An unsigned number is always represented in binary form to + * preserve all the information. */ + } else { + int idx; + fprintf(vlog_out, "%u'b", nbits); + for (idx = (int)nbits-1; idx >= 0; idx -= 1) { + fprintf(vlog_out, "%c", bits[idx]); + } + } +} + +static void emit_expr_select(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) +{ + if (ivl_expr_oper2(expr)) { + } else { + emit_expr(scope, ivl_expr_oper1(expr), wid); + } +} + +static void emit_expr_signal(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) +{ + ivl_signal_t sig = ivl_expr_signal(expr); +// HERE: Need support for an array select and an out of scope reference. +// Also pad a signed value to the given width. + fprintf(vlog_out, "%s", ivl_signal_basename(sig)); +} + +static void emit_expr_unary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) +{ + char *oper = "invalid"; + switch (ivl_expr_opcode(expr)) { + case '-': oper = "-"; break; + case '~': oper = "~"; break; + case '&': oper = "&"; break; + case '|': oper = "|"; break; + case '^': oper = "^"; break; + case 'A': oper = "~&"; break; + case 'N': oper = "~|"; break; + case 'X': oper = "~^"; break; + case '!': oper = "!"; break; + } + switch (ivl_expr_opcode(expr)) { + case '-': + case '~': + case '&': + case '|': + case '^': + case 'A': + case 'N': + case 'X': + fprintf(vlog_out, "%s", oper); + emit_expr(scope, ivl_expr_oper1(expr), wid); + break; + case '2': + case 'i': + case 'r': + /* A cast is a noop. */ + break; + default: + fprintf(vlog_out, ""); + emit_expr(scope, ivl_expr_oper1(expr), wid); + fprintf(stderr, "%s:%u: vlog95 error: Unknown unary " + "operator (%c).\n", + ivl_expr_file(expr), + ivl_expr_lineno(expr), + ivl_expr_opcode(expr)); + break; + } +} + +void emit_expr(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) +{ + switch(ivl_expr_type(expr)) { + case IVL_EX_BINARY: + emit_expr_binary(scope, expr, wid); + break; + case IVL_EX_NUMBER: + emit_expr_number(scope, expr, wid); + break; + case IVL_EX_REALNUM: + fprintf(vlog_out, "%#g", ivl_expr_dvalue(expr)); + break; + case IVL_EX_SELECT: + emit_expr_select(scope, expr, wid); + break; + case IVL_EX_SIGNAL: + emit_expr_signal(scope, expr, wid); + break; + case IVL_EX_STRING: + fprintf(vlog_out, "\"%s\"", ivl_expr_string(expr)); + break; + case IVL_EX_UNARY: + emit_expr_unary(scope, expr, wid); + break; + default: + fprintf(vlog_out, ""); + fprintf(stderr, "%s:%u: vlog95 error: Unknown expression " + "type (%d).\n", + ivl_expr_file(expr), + ivl_expr_lineno(expr), + (int)ivl_expr_type(expr)); + vlog_errors += 1; + break; + } +} diff --git a/tgt-vlog95/scope.c b/tgt-vlog95/scope.c new file mode 100644 index 000000000..2c6820878 --- /dev/null +++ b/tgt-vlog95/scope.c @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2010-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 +# include "config.h" +# include "vlog95_priv.h" + +static char*get_time_const(int time_value) +{ + switch (time_value) { + case 2: return "100s"; + case 1: return "10s"; + case 0: return "1s"; + case -1: return "100ms"; + case -2: return "10ms"; + case -3: return "1ms"; + case -4: return "100us"; + case -5: return "10us"; + case -6: return "1us"; + case -7: return "100ns"; + case -8: return "10ns"; + case -9: return "1ns"; + case -10: return "100ps"; + case -11: return "10ps"; + case -12: return "1ps"; + case -13: return "100fs"; + case -14: return "10fs"; + case -15: return "1fs"; + default: + fprintf(stderr, "Invalid time constant %d\n", time_value); + assert(0); + return "N/A"; + } +} + +void emit_func_return(ivl_signal_t sig) +{ + if (ivl_signal_dimensions(sig) > 0) { + fprintf(stderr, "%s:%u: vlog95 error: A function cannot return " + "an array.\n", ivl_signal_file(sig), + ivl_signal_lineno(sig)); + vlog_errors += 1; + } else if (ivl_signal_integer(sig)) { + fprintf(vlog_out, " integer"); + } else if (ivl_signal_data_type(sig) == IVL_VT_REAL) { + fprintf(vlog_out, " real"); + } else { + int msb = ivl_signal_msb(sig); + int lsb = ivl_signal_lsb(sig); + if (msb != 0 || lsb != 0) fprintf(vlog_out, " [%d:%d]", msb, lsb); + } +} + +void emit_var_def(ivl_signal_t sig) +{ + fprintf(vlog_out, "%*c", indent, ' '); + if (ivl_signal_integer(sig)) { + fprintf(vlog_out, "integer %s;\n", ivl_signal_basename(sig)); + if (ivl_signal_dimensions(sig) > 0) { + fprintf(stderr, "%s:%u: vlog95 error: Integer arrays are " + "not supported.\n", ivl_signal_file(sig), + ivl_signal_lineno(sig)); + vlog_errors += 1; + } + } else if (ivl_signal_data_type(sig) == IVL_VT_REAL) { + fprintf(vlog_out, "real %s;\n", ivl_signal_basename(sig)); + if (ivl_signal_dimensions(sig) > 0) { + fprintf(stderr, "%s:%u: vlog95 error: Real arrays are " + "not supported.\n", ivl_signal_file(sig), + ivl_signal_lineno(sig)); + vlog_errors += 1; + } + } else { + int msb = ivl_signal_msb(sig); + int lsb = ivl_signal_lsb(sig); + fprintf(vlog_out, "reg"); + if (msb != 0 || lsb != 0) fprintf(vlog_out, " [%d:%d]", msb, lsb); + fprintf(vlog_out, " %s", ivl_signal_basename(sig)); + if (ivl_signal_dimensions(sig) > 0) { + unsigned wd_count = ivl_signal_array_count(sig); + int first = ivl_signal_array_base(sig); + int last = first + wd_count - 1; + if (ivl_signal_array_addr_swapped(sig)) { + fprintf(vlog_out, " [%d:%d]", last, first); + } else { + fprintf(vlog_out, " [%d:%d]", first, last); + } + } + fprintf(vlog_out, ";\n"); + if (ivl_signal_signed(sig)) { + fprintf(stderr, "%s:%u: vlog95 error: Signed registers are " + "not supported.\n", ivl_signal_file(sig), + ivl_signal_lineno(sig)); + vlog_errors += 1; + } + } +} + +void emit_net_def(ivl_signal_t sig) +{ + fprintf(vlog_out, "%*c", indent, ' '); + int msb = ivl_signal_msb(sig); + int lsb = ivl_signal_lsb(sig); + if (ivl_signal_data_type(sig) == IVL_VT_REAL){ + fprintf(vlog_out, "wire %s;\n", ivl_signal_basename(sig)); + fprintf(stderr, "%s:%u: vlog95 error: Real nets are " + "not supported.\n", ivl_signal_file(sig), + ivl_signal_lineno(sig)); + vlog_errors += 1; + } else if (ivl_signal_signed(sig)) { + fprintf(vlog_out, "wire"); + if (msb != 0 || lsb != 0) fprintf(vlog_out, " [%d:%d]", msb, lsb); + fprintf(vlog_out, " %s;\n", ivl_signal_basename(sig)); + fprintf(stderr, "%s:%u: vlog95 error: Signed nets are " + "not supported.\n", ivl_signal_file(sig), + ivl_signal_lineno(sig)); + vlog_errors += 1; + } else if (ivl_signal_dimensions(sig) > 0) { + fprintf(vlog_out, "wire"); + if (msb != 0 || lsb != 0) fprintf(vlog_out, " [%d:%d]", msb, lsb); + fprintf(vlog_out, " %s;\n", ivl_signal_basename(sig)); + fprintf(stderr, "%s:%u: vlog95 error: Array nets are " + "not supported.\n", ivl_signal_file(sig), + ivl_signal_lineno(sig)); + vlog_errors += 1; + } else { + switch(ivl_signal_type(sig)) { + case IVL_SIT_TRI: + case IVL_SIT_UWIRE: +// HERE: Need to add support for supply nets. Probably supply strength +// with a constant 0/1 driver for all the bits. + fprintf(vlog_out, "wire"); + break; + case IVL_SIT_TRI0: + fprintf(vlog_out, "tri0"); + break; + case IVL_SIT_TRI1: + fprintf(vlog_out, "tri1"); + break; + case IVL_SIT_TRIAND: + fprintf(vlog_out, "wand"); + break; + case IVL_SIT_TRIOR: + fprintf(vlog_out, "wor"); + break; + default: + fprintf(vlog_out, ""); + fprintf(stderr, "%s:%u: vlog95 error: Unknown net type " + "(%d).\n", ivl_signal_file(sig), + ivl_signal_lineno(sig), (int)ivl_signal_type(sig)); + vlog_errors += 1; + break; + } + if (msb != 0 || lsb != 0) fprintf(vlog_out, " [%d:%d]", msb, lsb); + fprintf(vlog_out, " %s;\n", ivl_signal_basename(sig)); + } +} + +int emit_scope(ivl_scope_t scope, ivl_scope_t parent) +{ + ivl_scope_type_t sc_type = ivl_scope_type(scope); + unsigned is_auto = ivl_scope_is_auto(scope); + unsigned idx, count, start = 0; + + /* Output the scope definition. */ + switch (sc_type) { + case IVL_SCT_MODULE: + assert(!is_auto); + assert(indent == 0); + /* Set the time scale for this scope. */ + fprintf(vlog_out, "\n`timescale %s/%s\n", + get_time_const(ivl_scope_time_units(scope)), + get_time_const(ivl_scope_time_precision(scope))); + fprintf(vlog_out, "module %s", ivl_scope_tname(scope)); +// HERE: Still need to add port information. + break; + case IVL_SCT_FUNCTION: + fprintf(vlog_out, "\n%*cfunction", indent, ' '); + assert(ivl_scope_ports(scope) >= 2); + /* The function return information is the zero port. */ + emit_func_return(ivl_scope_port(scope, 0)); + start = 1; + fprintf(vlog_out, " %s", ivl_scope_tname(scope)); + if (is_auto) { + fprintf(stderr, "%s:%u: vlog95 error: Automatic function is " + "not supported.\n", ivl_scope_file(scope), + ivl_scope_lineno(scope)); + vlog_errors += 1; + } + break; + case IVL_SCT_TASK: + fprintf(vlog_out, "\n%*ctask %s", indent, ' ', + ivl_scope_tname(scope)); + if (is_auto) { + fprintf(stderr, "%s:%u: vlog95 error: Automatic task is " + "not supported.\n", ivl_scope_file(scope), + ivl_scope_lineno(scope)); + vlog_errors += 1; + } + break; + case IVL_SCT_BEGIN: + case IVL_SCT_FORK: + return 0; /* A named begin/fork is handled in line. */ + default: + fprintf(stderr, "%s:%u: vlog95 error: Unsupported scope type " + "(%d) named: %s.\n", ivl_scope_file(scope), + ivl_scope_lineno(scope), sc_type, + ivl_scope_tname(scope)); + vlog_errors += 1; + return 0; + } + fprintf(vlog_out, ";\n"); + indent += indent_incr; + + /* Output the scope ports. */ + count = ivl_scope_ports(scope); + for (idx = start; idx < count; idx += 1) { + fprintf(vlog_out, "%*c", indent, ' '); + ivl_signal_t port = ivl_scope_port(scope, idx); + switch (ivl_signal_port(port)) { + case IVL_SIP_INPUT: + fprintf(vlog_out, "input"); + break; + case IVL_SIP_OUTPUT: + fprintf(vlog_out, "output"); + break; + case IVL_SIP_INOUT: + fprintf(vlog_out, "inout"); + break; + default: + fprintf(vlog_out, ""); + fprintf(stderr, "%s:%u: vlog95 error: Unknown port " + "direction (%d).\n", ivl_signal_file(port), + ivl_signal_lineno(port), + (int)ivl_signal_port(port)); + vlog_errors += 1; + break; + } + fprintf(vlog_out, " %s;\n", ivl_signal_basename(port)); + } + if (count) fprintf(vlog_out, "\n"); + + /* Output the scope parameters. */ + count = ivl_scope_params(scope); + for (idx = 0; idx < count; idx += 1) { + ivl_parameter_t par = ivl_scope_param(scope, idx); + ivl_expr_t pex = ivl_parameter_expr(par); + fprintf(vlog_out, "%*cparameter %s = ", indent, ' ', + ivl_parameter_basename(par)); + emit_expr(scope, pex, 0); + fprintf(vlog_out, ";\n"); + } + if (count) fprintf(vlog_out, "\n"); + + /* Output the scope signals. */ + count = ivl_scope_sigs(scope); + for (idx = 0; idx < count; idx += 1) { + ivl_signal_t sig = ivl_scope_sig(scope, idx); + if (ivl_signal_type(sig) == IVL_SIT_REG) { + /* Do not output the implicit function return register. */ + if (sc_type == IVL_SCT_FUNCTION && + strcmp(ivl_signal_basename(sig), + ivl_scope_tname(scope)) == 0) continue; + emit_var_def(sig); + } else { + emit_net_def(sig); + } + } + if (count) fprintf(vlog_out, "\n"); + + /* Output the function/task body. */ + if (sc_type == IVL_SCT_TASK || sc_type == IVL_SCT_FUNCTION) { + emit_stmt(scope, ivl_scope_def(scope)); + } + + /* Now print out any sub-scopes. */ + ivl_scope_children(scope, (ivl_scope_f*) emit_scope, scope); + + /* Output the scope ending. */ + assert(indent >= indent_incr); + indent -= indent_incr; + switch (sc_type) { + case IVL_SCT_MODULE: + assert(indent == 0); + fprintf(vlog_out, "endmodule\n"); + break; + case IVL_SCT_FUNCTION: + fprintf(vlog_out, "%*cendfunction\n", indent, ' '); + break; + case IVL_SCT_TASK: + fprintf(vlog_out, "%*cendtask\n", indent, ' '); + break; + default: + assert(0); + break; + } + return 0; +} diff --git a/tgt-vlog95/stmt.c b/tgt-vlog95/stmt.c new file mode 100644 index 000000000..48e05af17 --- /dev/null +++ b/tgt-vlog95/stmt.c @@ -0,0 +1,180 @@ +/* + * 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 +# include "config.h" +# include "vlog95_priv.h" + +static unsigned indent_save = 0; + +static void emit_stmt_lval(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. +} + +static void emit_stmt_assign(ivl_scope_t scope, 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); + 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); + } + fprintf(vlog_out, "}"); + } else { + ivl_lval_t lval = ivl_stmt_lval(stmt, 0); + wid = ivl_lval_width(lval); + emit_stmt_lval(lval); + } + fprintf(vlog_out, " = "); + 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, ' '); +} + +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, ' ', + ivl_scope_basename(my_scope)); + 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 /* %s */\n", indent, ' ', + ivl_scope_basename(my_scope)); +} + +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; + 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)); + if (count != 0) { + fprintf(vlog_out, "("); + for (idx = 0; idx < count; idx += 1) { + emit_expr(scope, ivl_stmt_parm(stmt, idx), 0); + } + fprintf(vlog_out, ")"); + } + fprintf(vlog_out, ";\n"); +} + +void emit_stmt(ivl_scope_t scope, ivl_statement_t stmt) +{ + switch(ivl_statement_type(stmt)) { + case IVL_ST_NOOP: + fprintf(vlog_out, ";\n"); + break; + case IVL_ST_ASSIGN: + emit_stmt_assign(scope, stmt); + break; + case IVL_ST_BLOCK: + if (ivl_stmt_block_scope(stmt)) { + emit_stmt_block_named(scope, stmt); + } else { + emit_stmt_block(scope, stmt); + } + break; + case IVL_ST_DELAY: + emit_stmt_delay(scope, stmt); + break; + case IVL_ST_STASK: + emit_stmt_stask(scope, stmt); + break; + default: + fprintf(vlog_out, "%*c;\n", indent, ' '); + fprintf(stderr, "%s:%u: vlog95 error: Unknown statement " + "type (%d).\n", + ivl_stmt_file(stmt), + ivl_stmt_lineno(stmt), + (int)ivl_statement_type(stmt)); + vlog_errors += 1; + break; + } +} diff --git a/tgt-vlog95/vlog95.c b/tgt-vlog95/vlog95.c index 0eb676711..4aa38a4c5 100644 --- a/tgt-vlog95/vlog95.c +++ b/tgt-vlog95/vlog95.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2010-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 @@ -29,7 +29,7 @@ static const char*version_string = "Icarus Verilog VLOG95 Code Generator " VERSION " (" VERSION_TAG ")\n\n" -"Copyright (C) 2010 Cary R. (cygcary@yahoo.com)\n\n" +"Copyright (C) 2010-2011 Cary R. (cygcary@yahoo.com)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" @@ -45,9 +45,14 @@ static const char*version_string = FILE*vlog_out; int vlog_errors = 0; +int sim_precision = 0; +unsigned indent = 0; +unsigned indent_incr = 2; int target_design(ivl_design_t des) { + ivl_scope_t *roots; + unsigned nroots, idx; const char*path = ivl_design_flag(des, "-o"); assert(path); @@ -67,6 +72,12 @@ int target_design(ivl_design_t des) fprintf(vlog_out, " * Version: " VERSION " (" VERSION_TAG ")\n"); fprintf(vlog_out, " */\n"); + sim_precision = ivl_design_time_precision(des); + + /* Get all the root modules and then convert each one. */ + ivl_design_roots(des, &roots, &nroots); + for (idx = 0; idx < nroots; idx += 1) emit_scope(roots[idx], 0); + fclose(vlog_out); return vlog_errors; diff --git a/tgt-vlog95/vlog95_priv.h b/tgt-vlog95/vlog95_priv.h index 7c9e90c1d..f60c146b3 100644 --- a/tgt-vlog95/vlog95_priv.h +++ b/tgt-vlog95/vlog95_priv.h @@ -1,7 +1,7 @@ #ifndef __vlog95_priv_H #define __vlog95_priv_H /* - * Copyright (C) 2010 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2010-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 @@ -38,4 +38,30 @@ extern FILE*vlog_out; */ extern int vlog_errors; +/* + * Keep the simulation time precision so that we can scale delays. + */ +extern int sim_precision; + +/* + * Keep the current indent level. + */ +extern unsigned indent; +extern unsigned indent_incr; + +/* + * Emit the Verilog code for the given scope. + */ +extern int emit_scope(ivl_scope_t scope, ivl_scope_t parent); + +/* + * Emit a Verilog statement. + */ +extern void emit_stmt(ivl_scope_t scope, ivl_statement_t stmt); + +/* + * Emit a Verilog expression. + */ +extern void emit_expr(ivl_scope_t scope, ivl_expr_t expr, unsigned width); + #endif /* __vlog95_priv_H */