diff --git a/tgt-vhdl/Makefile.in b/tgt-vhdl/Makefile.in index ea04ee9a1..7638cd4e7 100644 --- a/tgt-vhdl/Makefile.in +++ b/tgt-vhdl/Makefile.in @@ -50,7 +50,7 @@ dep: mv $*.d dep O = vhdl.o state.o vhdl_element.o vhdl_type.o vhdl_syntax.o scope.o process.o \ - stmt.o expr.o lpm.o display.o support.o cast.o logic.o + stmt.o expr.o lpm.o support.o cast.o logic.o ifeq (@WIN32@,yes) TGTLDFLAGS=-L.. -livl diff --git a/tgt-vhdl/cast.cc b/tgt-vhdl/cast.cc index 07540f71d..8e34673a3 100644 --- a/tgt-vhdl/cast.cc +++ b/tgt-vhdl/cast.cc @@ -60,6 +60,8 @@ vhdl_expr *vhdl_expr::cast(const vhdl_type *to) return to_std_logic(); case VHDL_TYPE_STD_ULOGIC: return to_std_ulogic(); + case VHDL_TYPE_STRING: + return to_string(); default: assert(false); } @@ -116,6 +118,25 @@ vhdl_expr *vhdl_expr::to_integer() return conv; } +vhdl_expr *vhdl_expr::to_string() +{ + bool numeric = type_->get_name() == VHDL_TYPE_UNSIGNED + || type_->get_name() == VHDL_TYPE_SIGNED; + + if (numeric) { + vhdl_fcall *image = new vhdl_fcall("integer'image", vhdl_type::string()); + image->add_expr(this->cast(vhdl_type::integer())); + return image; + } + else { + // Assume type'image exists + vhdl_fcall *image = new vhdl_fcall(type_->get_string() + "'image", + vhdl_type::string()); + image->add_expr(this); + return image; + } +} + /* * Convert a generic expression to a Boolean. */ diff --git a/tgt-vhdl/display.cc b/tgt-vhdl/display.cc deleted file mode 100644 index 1f95365ec..000000000 --- a/tgt-vhdl/display.cc +++ /dev/null @@ -1,200 +0,0 @@ -/* - * VHDL implementation of $display. - * - * Copyright (C) 2008-2009 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); - - vhdl_type_name_t type = expr->get_type()->get_name(); - if (type == VHDL_TYPE_SIGNED || type == VHDL_TYPE_UNSIGNED) { - vhdl_type integer(VHDL_TYPE_INTEGER); - write->add_expr(expr->cast(&integer)); - } - else if (type != VHDL_TYPE_STRING) { - // Need to add a call to Type'Image for types not - // supported by std.textio - 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; -} - -static void flush_string(std::ostringstream &ss, stmt_container *container) -{ - display_write(container, new vhdl_const_string(ss.str().c_str())); - - // Clear the stream - ss.str(""); -} - -// This should display the hierarchical module name, but we don't support -// this in VHDL. So just emit a warning. -static void display_m(stmt_container* container) -{ - cerr << "Warning: no VHDL translation for %m format code" << endl; -} - -/* - * 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_procedural *proc, stmt_container *container, - ivl_statement_t stmt, bool newline) -{ - if (!proc->get_scope()->have_declared(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->get_scope()->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 == NULL) { - display_write(container, new vhdl_const_string(" ")); - continue; - } - - if (ivl_expr_type(net) == IVL_EX_STRING) { - 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') { - flush_string(ss, container); - display_line(container); - } - else - ss << ch; - p += 3; - } - else if (*p == '%' && *(++p) != '%') { - flush_string(ss, container); - - // Skip over width for now - while (isdigit(*p)) ++p; - - switch (*p) { - case 'm': - display_m(container); - break; - default: - { - assert(i < count); - ivl_expr_t netp = ivl_stmt_parm(stmt, i++); - assert(netp); - - vhdl_expr *base = translate_expr(netp); - if (NULL == base) - return 1; - - display_write(container, base); - } - } - } - else - ss << *p; - } - - // Call Write on any non-empty string data left in the buffer - if (!ss.str().empty()) - 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); - } - } - - if (newline) - display_line(container); - - return 0; -} diff --git a/tgt-vhdl/stmt.cc b/tgt-vhdl/stmt.cc index 678f66f90..2c013104f 100644 --- a/tgt-vhdl/stmt.cc +++ b/tgt-vhdl/stmt.cc @@ -30,6 +30,9 @@ #include #include +static void emit_wait_for_0(vhdl_procedural *proc, stmt_container *container, + ivl_statement_t stmt, vhdl_expr *expr); + /* * VHDL has no real equivalent of Verilog's $finish task. The * current solution is to use `assert false ...' to terminate @@ -57,6 +60,106 @@ static int draw_stask_finish(vhdl_procedural *proc, stmt_container *container, return 0; } +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 report statements for Verilog $display/$write +static int draw_stask_display(vhdl_procedural *proc, + stmt_container *container, + ivl_statement_t stmt) +{ + vhdl_binop_expr *text = new vhdl_binop_expr(VHDL_BINOP_CONCAT, + vhdl_type::string()); + + const int count = ivl_stmt_parm_count(stmt); + int 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 == NULL) { + text->add_expr(new vhdl_const_string(" ")); + continue; + } + + if (ivl_expr_type(net) == IVL_EX_STRING) { + 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') { + // Is there a better way of handling newlines? + // Maybe generate another report statement + } + else + ss << ch; + p += 3; + } + else if (*p == '%' && *(++p) != '%') { + // Flush the output string up to this point + text->add_expr(new vhdl_const_string(ss.str())); + ss.str(""); + + // Skip over width for now + while (isdigit(*p)) ++p; + + switch (*p) { + case 'm': + // TOOD: we can get the module name via attributes + cerr << "Warning: no VHDL translation for %m format code" + << endl; + break; + default: + { + assert(i < count); + ivl_expr_t netp = ivl_stmt_parm(stmt, i++); + assert(netp); + + vhdl_expr *base = translate_expr(netp); + if (NULL == base) + return 1; + + emit_wait_for_0(proc, container, stmt, base); + + text->add_expr(base->cast(text->get_type())); + } + } + } + else + ss << *p; + } + + // Emit any non-empty string data left in the buffer + if (!ss.str().empty()) + text->add_expr(new vhdl_const_string(ss.str())); + } + else { + vhdl_expr *base = translate_expr(net); + if (NULL == base) + return 1; + + emit_wait_for_0(proc, container, stmt, base); + + text->add_expr(base->cast(text->get_type())); + } + } + + if (count == 0) + text->add_expr(new vhdl_const_string("")); + + container->add_stmt(new vhdl_report_stmt(text)); + return 0; +} + /* * Generate VHDL for system tasks (like $display). Not all of * these are supported. @@ -67,9 +170,9 @@ static int draw_stask(vhdl_procedural *proc, stmt_container *container, const char *name = ivl_stmt_name(stmt); if (strcmp(name, "$display") == 0) - return draw_stask_display(proc, container, stmt, true); + return draw_stask_display(proc, container, stmt); else if (strcmp(name, "$write") == 0) - return draw_stask_display(proc, container, stmt, false); + return draw_stask_display(proc, container, stmt); else if (strcmp(name, "$finish") == 0) return draw_stask_finish(proc, container, stmt); else { diff --git a/tgt-vhdl/vhdl_syntax.cc b/tgt-vhdl/vhdl_syntax.cc index ba881f9fb..b634fa918 100644 --- a/tgt-vhdl/vhdl_syntax.cc +++ b/tgt-vhdl/vhdl_syntax.cc @@ -135,7 +135,6 @@ void vhdl_entity::emit(std::ostream &of, int level) const of << "library ieee;" << std::endl; of << "use ieee.std_logic_1164.all;" << std::endl; of << "use ieee.numeric_std.all;" << std::endl; - of << "use std.textio.all;" << std::endl; of << std::endl; emit_comment(of, level); @@ -620,11 +619,7 @@ void vhdl_var_ref::emit(std::ostream &of, int level) const void vhdl_const_string::emit(std::ostream &of, int level) const { - // In some instances a string literal can be ambiguous between - // a String type and some other types (e.g. std_logic_vector) - // The explicit cast to String removes this ambiguity (although - // isn't always strictly necessary) - of << "String'(\"" << value_ << "\")"; + of << "\"" << value_ << "\""; } void vhdl_null_stmt::emit(std::ostream &of, int level) const diff --git a/tgt-vhdl/vhdl_syntax.hh b/tgt-vhdl/vhdl_syntax.hh index 8e7bcd694..a27c45bac 100644 --- a/tgt-vhdl/vhdl_syntax.hh +++ b/tgt-vhdl/vhdl_syntax.hh @@ -52,6 +52,7 @@ public: virtual vhdl_expr *to_std_logic(); virtual vhdl_expr *to_std_ulogic(); virtual vhdl_expr *to_vector(vhdl_type_name_t name, int w); + virtual vhdl_expr *to_string(); virtual void find_vars(vhdl_var_set_t& read) {} protected: @@ -176,7 +177,7 @@ private: class vhdl_const_string : public vhdl_expr { public: - vhdl_const_string(const char *value) + vhdl_const_string(const string& value) : vhdl_expr(vhdl_type::string(), true), value_(value) {} void emit(std::ostream &of, int level) const; @@ -270,7 +271,7 @@ private: */ class vhdl_fcall : public vhdl_expr { public: - vhdl_fcall(const char *name, vhdl_type *rtype) + vhdl_fcall(const string& name, vhdl_type *rtype) : vhdl_expr(rtype), name_(name) {}; ~vhdl_fcall() {} diff --git a/tgt-vhdl/vhdl_target.h b/tgt-vhdl/vhdl_target.h index 4bbafac7e..abc09266b 100644 --- a/tgt-vhdl/vhdl_target.h +++ b/tgt-vhdl/vhdl_target.h @@ -1,3 +1,4 @@ +// -*- mode: c++ -*- #ifndef INC_VHDL_TARGET_H #define INC_VHDL_TARGET_H @@ -28,9 +29,6 @@ ivl_design_t get_vhdl_design(); vhdl_var_ref *nexus_to_var_ref(vhdl_scope *arch_scope, ivl_nexus_t nexus); vhdl_var_ref* readable_ref(vhdl_scope* scope, ivl_nexus_t nex); string make_safe_name(ivl_signal_t sig); - -int draw_stask_display(vhdl_procedural *proc, stmt_container *container, - ivl_statement_t stmt, bool newline = true); void require_support_function(support_function_t f); #endif /* #ifndef INC_VHDL_TARGET_H */