diff --git a/tgt-vhdl/Makefile.in b/tgt-vhdl/Makefile.in index 0827b5f5c..1b2f15a1c 100644 --- a/tgt-vhdl/Makefile.in +++ b/tgt-vhdl/Makefile.in @@ -50,7 +50,7 @@ dep: mv $*.d dep O = vhdl.o vhdl_element.o vhdl_type.o vhdl_syntax.o scope.o process.o \ - stmt.o expr.o lpm.o + stmt.o expr.o lpm.o display.o ifeq (@WIN32@,yes) TGTLDFLAGS=-L.. -livl diff --git a/tgt-vhdl/display.cc b/tgt-vhdl/display.cc new file mode 100644 index 000000000..998666a0e --- /dev/null +++ b/tgt-vhdl/display.cc @@ -0,0 +1,159 @@ +/* + * VHDL implementation of $display. + * + * Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "vhdl_target.h" + +#include +#include +#include +#include +#include + +static const char *DISPLAY_LINE = "Verilog_Display_Line"; + +/* + * Write a VHDL expression into the current display line. + */ +static void display_write(stmt_container *container, vhdl_expr *expr) +{ + vhdl_pcall_stmt *write = new vhdl_pcall_stmt("Write"); + vhdl_var_ref *ref = + new vhdl_var_ref(DISPLAY_LINE, vhdl_type::line()); + write->add_expr(ref); + + // Need to add a call to Type'Image for types not + // supported by std.textio + if (expr->get_type()->get_name() != VHDL_TYPE_STRING) { + std::string name(expr->get_type()->get_string()); + name += "'Image"; + + vhdl_fcall *cast + = new vhdl_fcall(name.c_str(), vhdl_type::string()); + cast->add_expr(expr); + + write->add_expr(cast); + } + else + write->add_expr(expr); + + container->add_stmt(write); +} + +/* + * Write the value of DISPLAY_LINE to the output. + */ +static void display_line(stmt_container *container) +{ + vhdl_pcall_stmt *write_line = new vhdl_pcall_stmt("WriteLine"); + vhdl_var_ref *output_ref = + new vhdl_var_ref("std.textio.Output", new vhdl_type(VHDL_TYPE_FILE)); + write_line->add_expr(output_ref); + vhdl_var_ref *ref = + new vhdl_var_ref(DISPLAY_LINE, vhdl_type::line()); + write_line->add_expr(ref); + container->add_stmt(write_line); +} + +/* + * Parse an octal escape sequence. + */ +static char parse_octal(const char *p) +{ + assert(*p && *(p+1) && *(p+2)); + assert(isdigit(*p) && isdigit(*(p+1)) && isdigit(*(p+1))); + + return (*p - '0') * 64 + + (*(p+1) - '0') * 8 + + (*(p+2) - '0') * 1; +} + +/* + * Generate VHDL for the $display system task. + * This is implemented using the functions in std.textio. Each + * parameter is written to a line variable in the process and + * then the line is written to the special variable `Output' + * (which represents the console). Subsequent $displays will + * use the same line variable. + * + * It's possible, although quite unlikely, that there will be + * name collision with an existing variable called + * `Verilog_Display_Line' -- do something about this? + */ +int draw_stask_display(vhdl_process *proc, stmt_container *container, + ivl_statement_t stmt, bool newline) +{ + // Add the package requirement to the containing entity + proc->get_parent()->get_parent()->requires_package("std.textio"); + + if (!proc->have_declared_var(DISPLAY_LINE)) { + vhdl_var_decl *line_var = + new vhdl_var_decl(DISPLAY_LINE, vhdl_type::line()); + line_var->set_comment("For generating $display output"); + proc->add_decl(line_var); + } + + // Write the data into the line + int count = ivl_stmt_parm_count(stmt), i = 0; + while (i < count) { + // $display may have an empty parameter, in which case + // the expression will be null + // The behaviour here seems to be to output a space + ivl_expr_t net = ivl_stmt_parm(stmt, i++); + if (net) { + if (ivl_expr_type(net) == IVL_EX_STRING) { + std::ostringstream ss; + for (const char *p = ivl_expr_string(net); *p; p++) { + if (*p == '\\') { + // Octal escape + char ch = parse_octal(p+1); + if (ch == '\n') { + display_write(container, + new vhdl_const_string(ss.str().c_str())); + display_line(container); + + // Clear the stream + ss.str(""); + } + else + ss << ch; + p += 4; + } + else + ss << *p; + } + + display_write(container, new vhdl_const_string(ss.str().c_str())); + } + else { + vhdl_expr *base = translate_expr(net); + if (NULL == base) + return 1; + + display_write(container, base); + } + } + else + display_write(container, new vhdl_const_string(" ")); + } + + display_line(container); + + return 0; +} diff --git a/tgt-vhdl/stmt.cc b/tgt-vhdl/stmt.cc index 9ae5d7711..ab8accd68 100644 --- a/tgt-vhdl/stmt.cc +++ b/tgt-vhdl/stmt.cc @@ -25,85 +25,6 @@ #include #include -/* - * Generate VHDL for the $display system task. - * This is implemented using the functions in std.textio. Each - * parameter is written to a line variable in the process and - * then the line is written to the special variable `Output' - * (which represents the console). Subsequent $displays will - * use the same line variable. - * - * It's possible, although quite unlikely, that there will be - * name collision with an existing variable called - * `Verilog_Display_Line' -- do something about this? - */ -static int draw_stask_display(vhdl_process *proc, stmt_container *container, - ivl_statement_t stmt) -{ - // Add the package requirement to the containing entity - proc->get_parent()->get_parent()->requires_package("std.textio"); - - const char *display_line = "Verilog_Display_Line"; - - if (!proc->have_declared_var(display_line)) { - vhdl_var_decl *line_var = - new vhdl_var_decl(display_line, vhdl_type::line()); - line_var->set_comment("For generating $display output"); - proc->add_decl(line_var); - } - - // Write the data into the line - int count = ivl_stmt_parm_count(stmt); - for (int i = 0; i < count; i++) { - // $display may have an empty parameter, in which case - // the expression will be null - // The behaviour here seems to be to output a space - ivl_expr_t net = ivl_stmt_parm(stmt, i); - vhdl_expr *e = NULL; - if (net) { - vhdl_expr *base = translate_expr(net); - if (NULL == base) - return 1; - - // Need to add a call to Type'Image for types not - // supported by std.textio - if (base->get_type()->get_name() != VHDL_TYPE_STRING) { - std::string name(base->get_type()->get_string()); - name += "'Image"; - - vhdl_fcall *cast - = new vhdl_fcall(name.c_str(), vhdl_type::string()); - cast->add_expr(base); - e = cast; - } - else - e = base; - } - else - e = new vhdl_const_string(" "); - - vhdl_pcall_stmt *write = new vhdl_pcall_stmt("Write"); - vhdl_var_ref *ref = - new vhdl_var_ref(display_line, vhdl_type::line()); - write->add_expr(ref); - write->add_expr(e); - - container->add_stmt(write); - } - - // WriteLine(Output, Verilog_Display_Line) - vhdl_pcall_stmt *write_line = new vhdl_pcall_stmt("WriteLine"); - vhdl_var_ref *output_ref = - new vhdl_var_ref("std.textio.Output", new vhdl_type(VHDL_TYPE_FILE)); - write_line->add_expr(output_ref); - vhdl_var_ref *ref = - new vhdl_var_ref(display_line, vhdl_type::line()); - write_line->add_expr(ref); - container->add_stmt(write_line); - - return 0; -} - /* * VHDL has no real equivalent of Verilog's $finish task. The * current solution is to use `assert false ...' to terminate diff --git a/tgt-vhdl/vhdl_target.h b/tgt-vhdl/vhdl_target.h index cfad1dc13..ee51d2a78 100644 --- a/tgt-vhdl/vhdl_target.h +++ b/tgt-vhdl/vhdl_target.h @@ -35,5 +35,8 @@ void blocking_assign_to(vhdl_process *proc, ivl_signal_t sig); std::string strip_var(const std::string &str); void draw_blocking_assigns(vhdl_process *proc); +int draw_stask_display(vhdl_process *proc, stmt_container *container, + ivl_statement_t stmt, bool newline = true); + #endif /* #ifndef INC_VHDL_TARGET_H */