From 2187f302074f83f423c995708503d24cb05bb5a0 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Tue, 5 Oct 2010 19:59:25 +0100 Subject: [PATCH 1/6] Reduce number of 0 ns waits in generated VHDL Previous we generated a "wait for 0 ns" statement after every blocking assignment that wasn't the last statement in the process. While this implements the Verilog semantics, it generates excessive waits, and cannot usually be synthesised. This patch only generates "wait for 0 ns" statements when it cannot be avoid (e.g. when the target of a blocking assignment is read in the same process). An example: begin x = 5; if (x == 2) y = 7; end Becomes: x <= 5; wait for 0 ns; -- Required to implement assignment semantics if x = 2 then y <= 7; -- No need for wait here, not read -- wait for 0 ns (previously) end if; Conflicts: tgt-vhdl/process.cc tgt-vhdl/stmt.cc tgt-vhdl/vhdl_target.h --- tgt-vhdl/process.cc | 15 ++-- tgt-vhdl/stmt.cc | 165 +++++++++++++++++++++++++--------------- tgt-vhdl/vhdl_syntax.cc | 28 ++++++- tgt-vhdl/vhdl_syntax.hh | 14 +++- tgt-vhdl/vhdl_target.h | 1 - 5 files changed, 149 insertions(+), 74 deletions(-) diff --git a/tgt-vhdl/process.cc b/tgt-vhdl/process.cc index 4660ea98b..d19eac572 100644 --- a/tgt-vhdl/process.cc +++ b/tgt-vhdl/process.cc @@ -57,15 +57,12 @@ static int generate_vhdl_process(vhdl_entity *ent, ivl_process_t proc) // However, if no statements were added to the container // by draw_stmt, don't bother adding a wait as `emit' // will optimise the process out of the output - if (ivl_process_type(proc) == IVL_PR_INITIAL) { - // Get rid of any useless `wait for 0 ns's at the end of the process - prune_wait_for_0(vhdl_proc->get_container()); - - // The above pruning might have removed all logic from the process - if (!vhdl_proc->get_container()->empty()) { - vhdl_wait_stmt *wait = new vhdl_wait_stmt(); - vhdl_proc->get_container()->add_stmt(wait); - } + bool is_initial = ivl_process_type(proc) == IVL_PR_INITIAL; + bool is_empty = vhdl_proc->get_container()->empty(); + + if (is_initial && !is_empty) { + vhdl_wait_stmt *wait = new vhdl_wait_stmt(); + vhdl_proc->get_container()->add_stmt(wait); } // Add a comment indicating where it came from diff --git a/tgt-vhdl/stmt.cc b/tgt-vhdl/stmt.cc index 7d217eb09..678f66f90 100644 --- a/tgt-vhdl/stmt.cc +++ b/tgt-vhdl/stmt.cc @@ -1,7 +1,7 @@ /* * VHDL code generation for statements. * - * Copyright (C) 2008-2009 Nick Gasson (nick@nickg.me.uk) + * Copyright (C) 2008-2010 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 @@ -131,27 +131,6 @@ static int draw_noop(vhdl_procedural *proc, stmt_container *container, return 0; } -/* - * The VHDL code generator inserts `wait for 0 ns' after each - * not-last-in-block blocking assignment. - * If this is immediately followed by another `wait for ...' then - * we might as well not emit the first zero-time wait. - */ -void prune_wait_for_0(stmt_container *container) -{ - vhdl_wait_stmt *wait0; - stmt_container::stmt_list_t &stmts = container->get_stmts(); - while (stmts.size() > 0 - && (wait0 = dynamic_cast(stmts.back()))) { - if (wait0->get_type() == VHDL_WAIT_FOR0) { - delete wait0; - stmts.pop_back(); - } - else - break; - } -} - static vhdl_var_ref *make_assign_lhs(ivl_lval_t lval, vhdl_scope *scope) { ivl_signal_t sig = ivl_lval_sig(lval); @@ -263,12 +242,68 @@ bool check_valid_assignment(vhdl_decl::assign_type_t atype, vhdl_procedural *pro return true; } +// Generate a "wait for 0 ns" statement to emulate the behaviour of +// Verilog blocking assignment using VHDL signals. This is only generated +// if we read from the target of a blocking assignment in the same +// process (i.e. it is only generated when required, not for every +// blocking assignment). An example: +// +// begin +// x = 5; +// if (x == 2) +// y = 7; +// end +// +// Becomes: +// +// x <= 5; +// wait for 0 ns; -- Required to implement assignment semantics +// if x = 2 then +// y <= 7; -- No need for wait here, not read +// end if; +// +static void emit_wait_for_0(vhdl_procedural *proc, + stmt_container *container, + ivl_statement_t stmt, + vhdl_expr *expr) +{ + vhdl_var_set_t read; + expr->find_vars(read); + + bool need_wait_for_0 = false; + for (vhdl_var_set_t::const_iterator it = read.begin(); + it != read.end(); ++it) { + if (proc->is_blocking_target(*it)) + need_wait_for_0 = true; + } + + stmt_container::stmt_list_t &stmts = container->get_stmts(); + bool last_was_wait = + !stmts.empty() && dynamic_cast(stmts.back()); + + if (need_wait_for_0 && !last_was_wait) { + debug_msg("Generated wait-for-0 for %s:%d", + ivl_stmt_file(stmt), ivl_stmt_lineno(stmt)); + + vhdl_seq_stmt *wait = new vhdl_wait_stmt(VHDL_WAIT_FOR0); + + ostringstream ss; + ss << "Read target of blocking assignment (" + << ivl_stmt_file(stmt) + << ":" << ivl_stmt_lineno(stmt) << ")"; + wait->set_comment(ss.str()); + + container->add_stmt(wait); + proc->added_wait_stmt(); + } +} + // Generate an assignment of type T for the Verilog statement stmt. // If a statement was generated then `assign_type' will contain the // type of assignment that was generated; this should be initialised // to some sensible default. void make_assignment(vhdl_procedural *proc, stmt_container *container, - ivl_statement_t stmt, bool blocking, + ivl_statement_t stmt, bool emul_blocking, vhdl_decl::assign_type_t& assign_type) { list lvals; @@ -286,14 +321,23 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container, if (rhs == NULL) return; + emit_wait_for_0(proc, container, stmt, rhs); + if (rhs2) + emit_wait_for_0(proc, container, stmt, rhs2); + if (lvals.size() == 1) { vhdl_var_ref *lhs = lvals.front(); rhs = rhs->cast(lhs->get_type()); ivl_expr_t i_delay; vhdl_expr *after = NULL; - if ((i_delay = ivl_stmt_delay_expr(stmt)) != NULL) + if ((i_delay = ivl_stmt_delay_expr(stmt)) != NULL) { after = translate_time_expr(i_delay); + if (after == NULL) + return; + + emit_wait_for_0(proc, container, stmt, after); + } // Find the declaration of the LHS so we know what type // of assignment statement to generate (is it a signal, @@ -301,6 +345,9 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container, vhdl_decl *decl = proc->get_scope()->get_decl(lhs->get_name()); assign_type = decl->assignment_type(); + if (assign_type == vhdl_decl::ASSIGN_NONBLOCK && emul_blocking) + proc->add_blocking_target(lhs); + // A small optimisation is to expand ternary RHSs into an // if statement (eliminates a function call and produces // more idiomatic code) @@ -313,6 +360,8 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container, if (NULL == test) return; + emit_wait_for_0(proc, container, stmt, test); + if (!check_valid_assignment(decl->assignment_type(), proc, stmt)) return; @@ -368,6 +417,7 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container, decl->set_initial(NULL); // Default initial value else { decl->set_initial(rhs); + proc->get_scope()->hoisted_initialiser(true); delete lhs; return; } @@ -380,8 +430,7 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container, assign_for(decl->assignment_type(), lhs, rhs); container->add_stmt(a); - if (after != NULL) - a->set_after(after); + a->set_after(after); } else { // Multiple lvals are implemented by first assigning the complete @@ -408,8 +457,13 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container, ivl_expr_t i_delay; vhdl_expr *after = NULL; - if ((i_delay = ivl_stmt_delay_expr(stmt)) != NULL) + if ((i_delay = ivl_stmt_delay_expr(stmt)) != NULL) { after = translate_time_expr(i_delay); + if (after == NULL) + return; + + emit_wait_for_0(proc, container, stmt, after); + } // Find the declaration of the LHS so we know what type // of assignment statement to generate (is it a signal, @@ -428,10 +482,11 @@ void make_assignment(vhdl_procedural *proc, stmt_container *container, container->add_stmt(a); width_so_far += lval_width; + + if (assign_type == vhdl_decl::ASSIGN_NONBLOCK && emul_blocking) + proc->add_blocking_target(*it); } } - - return; } /* @@ -454,26 +509,10 @@ static int draw_assign(vhdl_procedural *proc, stmt_container *container, ivl_statement_t stmt, bool is_last) { vhdl_decl::assign_type_t assign_type = vhdl_decl::ASSIGN_NONBLOCK; - if (proc->get_scope()->allow_signal_assignment()) { - // Blocking assignment is implemented as non-blocking assignment - // followed by a zero-time wait - // This follows the Verilog semantics fairly closely. - - make_assignment(proc, container, stmt, false, assign_type); - - // Don't generate a zero-wait if either: - // a) this is the last statement in the process - // c) a blocking assignment was generated - if (!is_last && assign_type == vhdl_decl::ASSIGN_NONBLOCK) { - prune_wait_for_0(container); - container->add_stmt - (new vhdl_wait_stmt(VHDL_WAIT_FOR0)); - proc->added_wait_stmt(); - } - } - else - make_assignment(proc, container, stmt, true, assign_type); - + bool emulate_blocking = proc->get_scope()->allow_signal_assignment(); + + make_assignment(proc, container, stmt, emulate_blocking, assign_type); + return 0; } @@ -501,9 +540,7 @@ static int draw_delay(vhdl_procedural *proc, stmt_container *container, if (NULL == time) return 1; } - - prune_wait_for_0(container); - + ivl_statement_t sub_stmt = ivl_stmt_sub_stmt(stmt); vhdl_wait_stmt *wait = new vhdl_wait_stmt(VHDL_WAIT_FOR, time); @@ -523,8 +560,8 @@ static int draw_delay(vhdl_procedural *proc, stmt_container *container, // Any further assignments occur after simulation time 0 // so they cannot be used to initialise signal declarations // (if this scope is an initial process) - proc->get_scope()->set_initializing(false); - + proc->get_scope()->set_initializing(false); + return 0; } @@ -743,9 +780,12 @@ static int draw_wait(vhdl_procedural *_proc, stmt_container *container, // If this container is the top-level statement (i.e. it is the // first thing inside a process) then we can extract these - // events out into the sensitivity list - bool is_top_level = container == proc->get_container() - && container->empty(); + // events out into the sensitivity list as long as we haven't + // promoted any preceding assignments to initialisers + bool is_top_level = + container == proc->get_container() + && container->empty() + && !proc->get_scope()->hoisted_initialiser(); // See if this can be implemented in a more idiomatic way before we // fall back on the generic translation @@ -876,8 +916,11 @@ static int draw_if(vhdl_procedural *proc, stmt_container *container, if (NULL == test) return 1; + emit_wait_for_0(proc, container, stmt, test); + vhdl_if_stmt *vhdif = new vhdl_if_stmt(test); - + container->add_stmt(vhdif); + ivl_statement_t cond_true_stmt = ivl_stmt_cond_true(stmt); if (cond_true_stmt) draw_stmt(proc, vhdif->get_then_container(), cond_true_stmt, is_last); @@ -886,8 +929,6 @@ static int draw_if(vhdl_procedural *proc, stmt_container *container, if (cond_false_stmt) draw_stmt(proc, vhdif->get_else_container(), cond_false_stmt, is_last); - container->add_stmt(vhdif); - return 0; } @@ -1403,9 +1444,13 @@ int draw_while(vhdl_procedural *proc, stmt_container *container, vhdl_type boolean(VHDL_TYPE_BOOLEAN); test = test->cast(&boolean); + emit_wait_for_0(proc, container, stmt, test); + vhdl_while_stmt *loop = new vhdl_while_stmt(test); draw_stmt(proc, loop->get_container(), ivl_stmt_sub_stmt(stmt)); + emit_wait_for_0(proc, loop->get_container(), stmt, test); + container->add_stmt(loop); return 0; } diff --git a/tgt-vhdl/vhdl_syntax.cc b/tgt-vhdl/vhdl_syntax.cc index 363a04736..44f112fd2 100644 --- a/tgt-vhdl/vhdl_syntax.cc +++ b/tgt-vhdl/vhdl_syntax.cc @@ -1,7 +1,7 @@ /* * VHDL abstract syntax elements. * - * Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk) + * Copyright (C) 2008-2010 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 @@ -31,7 +31,8 @@ using namespace std; vhdl_scope::vhdl_scope() - : parent_(NULL), init_(false), sig_assign_(true) + : parent_(NULL), init_(false), sig_assign_(true), + hoisted_init_(false) { } @@ -100,6 +101,16 @@ vhdl_scope *vhdl_scope::get_parent() const return parent_; } +bool vhdl_scope::hoisted_initialiser() const +{ + return hoisted_init_; +} + +void vhdl_scope::hoisted_initialiser(bool h) +{ + hoisted_init_ = h; +} + vhdl_entity::vhdl_entity(const string& name, vhdl_arch *arch, int depth__) : depth(depth__), name_(name), arch_(arch), time_unit_(TIME_UNIT_NS) @@ -193,6 +204,16 @@ void vhdl_arch::emit(std::ostream &of, int level) const blank_line(of, level); // Extra blank line after architectures; } +void vhdl_procedural::add_blocking_target(vhdl_var_ref* ref) +{ + blocking_targets_.insert(ref->get_name()); +} + +bool vhdl_procedural::is_blocking_target(vhdl_var_ref* ref) const +{ + return blocking_targets_.find(ref->get_name()) != blocking_targets_.end(); +} + void vhdl_process::add_sensitivity(const std::string &name) { sens_.push_back(name); @@ -399,6 +420,7 @@ void vhdl_wait_stmt::emit(std::ostream &of, int level) const } of << ";"; + emit_comment(of, level, true); } vhdl_decl::~vhdl_decl() @@ -630,7 +652,7 @@ vhdl_abstract_assign_stmt::~vhdl_abstract_assign_stmt() void vhdl_abstract_assign_stmt::find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write) { - write.insert(lhs_); + lhs_->find_vars(write); rhs_->find_vars(read); } diff --git a/tgt-vhdl/vhdl_syntax.hh b/tgt-vhdl/vhdl_syntax.hh index ea7b7de3f..bfacd2ed4 100644 --- a/tgt-vhdl/vhdl_syntax.hh +++ b/tgt-vhdl/vhdl_syntax.hh @@ -52,7 +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 void find_vars(vhdl_var_set_t& read) const {} + virtual void find_vars(vhdl_var_set_t& read) {} protected: static void open_parens(ostream& of); @@ -770,6 +770,8 @@ public: bool initializing() const { return init_; } void set_initializing(bool i); + bool hoisted_initialiser() const; + void hoisted_initialiser(bool h); void set_allow_signal_assignment(bool b) { sig_assign_ = b; } bool allow_signal_assignment() const { return sig_assign_; } @@ -777,6 +779,7 @@ private: decl_list_t decls_; vhdl_scope *parent_; bool init_, sig_assign_; + bool hoisted_init_; }; @@ -795,6 +798,11 @@ public: void added_wait_stmt() { contains_wait_stmt_ = true; } bool contains_wait_stmt() const { return contains_wait_stmt_; } + + // Managing set of blocking assignment targets in this block + void add_blocking_target(vhdl_var_ref* ref); + bool is_blocking_target(vhdl_var_ref* ref) const; + protected: stmt_container stmts_; vhdl_scope scope_; @@ -804,6 +812,10 @@ protected: // If this is the case then we can't use a sensitivity list for // the process bool contains_wait_stmt_; + + // The set of variable we have performed a blocking + // assignment to + set blocking_targets_; }; diff --git a/tgt-vhdl/vhdl_target.h b/tgt-vhdl/vhdl_target.h index 3a4aad72a..4bbafac7e 100644 --- a/tgt-vhdl/vhdl_target.h +++ b/tgt-vhdl/vhdl_target.h @@ -31,7 +31,6 @@ 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 prune_wait_for_0(stmt_container *container); void require_support_function(support_function_t f); #endif /* #ifndef INC_VHDL_TARGET_H */ From 9faaf5f817d1d495ad84a3543e7c24f7d1e7597e Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Tue, 5 Oct 2010 20:00:16 +0100 Subject: [PATCH 2/6] Add VHDL report statement Not output yet, but will be used to replace std.textio implementation of $display. Conflicts: tgt-vhdl/vhdl_syntax.cc --- tgt-vhdl/vhdl_syntax.cc | 36 ++++++++++++++++++++++++++++++++++-- tgt-vhdl/vhdl_syntax.hh | 31 ++++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/tgt-vhdl/vhdl_syntax.cc b/tgt-vhdl/vhdl_syntax.cc index 44f112fd2..ba881f9fb 100644 --- a/tgt-vhdl/vhdl_syntax.cc +++ b/tgt-vhdl/vhdl_syntax.cc @@ -788,10 +788,42 @@ void vhdl_cassign_stmt::emit(std::ostream &of, int level) const of << ";"; } +vhdl_report_stmt::vhdl_report_stmt(vhdl_expr *text, + vhdl_severity_t severity) + : severity_(severity), + text_(text) +{ + +} + +void vhdl_report_stmt::emit(ostream& of, int level) const +{ + of << "report "; + text_->emit(of, level); + + if (severity_ != SEVERITY_NOTE) { + const char *levels[] = { "note", "warning", "error", "failure" }; + of << " severity " << levels[severity_]; + } + + of << ";"; +} + +void vhdl_report_stmt::find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write) +{ + text_->find_vars(read); +} + +vhdl_assert_stmt::vhdl_assert_stmt(const char *reason) + : vhdl_report_stmt(new vhdl_const_string(reason), SEVERITY_FAILURE) +{ + +} + void vhdl_assert_stmt::emit(std::ostream &of, int level) const { - of << "assert false"; // TODO: Allow arbitrary expression - of << " report \"" << reason_ << "\" severity failure;"; + of << "assert false "; // TODO: Allow arbitrary expression + vhdl_report_stmt::emit(of, level); } vhdl_if_stmt::vhdl_if_stmt(vhdl_expr *test) diff --git a/tgt-vhdl/vhdl_syntax.hh b/tgt-vhdl/vhdl_syntax.hh index bfacd2ed4..8e7bcd694 100644 --- a/tgt-vhdl/vhdl_syntax.hh +++ b/tgt-vhdl/vhdl_syntax.hh @@ -453,15 +453,32 @@ public: }; -class vhdl_assert_stmt : public vhdl_seq_stmt { -public: - vhdl_assert_stmt(const char *reason) - : reason_(reason) {} +enum vhdl_severity_t { + SEVERITY_NOTE, + SEVERITY_WARNING, + SEVERITY_ERROR, + SEVERITY_FAILURE +}; - void emit(std::ostream &of, int level) const; - void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write) {} +class vhdl_report_stmt : public vhdl_seq_stmt { +public: + vhdl_report_stmt(vhdl_expr *text, + vhdl_severity_t severity = SEVERITY_NOTE); + virtual ~vhdl_report_stmt() {} + + virtual void emit(ostream& of, int level) const; + void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write); private: - std::string reason_; + vhdl_severity_t severity_; + vhdl_expr *text_; +}; + + +class vhdl_assert_stmt : public vhdl_report_stmt { +public: + vhdl_assert_stmt(const char *reason); + + void emit(ostream &of, int level) const; }; From a7fe5167e834f9c876b75088ec2d39a51eef5e2b Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Tue, 5 Oct 2010 20:02:04 +0100 Subject: [PATCH 3/6] Generate VHDL report statements for $display This changes the implementation of $display/$write to use VHDL report statements rather the the std.textio functions. The code produced is simpler and more like what a real VHDL designed would write. However it no longer exactly matches the Verilog output as most VHDL simulators prepend the text with simulation time, entity name, severity level, etc. There is a corresponding change in ivtest to support this. Conflicts: tgt-vhdl/cast.cc tgt-vhdl/display.cc tgt-vhdl/vhdl_syntax.cc tgt-vhdl/vhdl_target.h --- tgt-vhdl/Makefile.in | 2 +- tgt-vhdl/cast.cc | 21 +++++ tgt-vhdl/display.cc | 200 ---------------------------------------- tgt-vhdl/stmt.cc | 107 ++++++++++++++++++++- tgt-vhdl/vhdl_syntax.cc | 7 +- tgt-vhdl/vhdl_syntax.hh | 5 +- tgt-vhdl/vhdl_target.h | 4 +- 7 files changed, 132 insertions(+), 214 deletions(-) delete mode 100644 tgt-vhdl/display.cc 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 */ From 249fc93b899fc2bfeb9b8f6c47baea0dd13f9c46 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Tue, 24 Aug 2010 22:17:11 +0100 Subject: [PATCH 4/6] Change VHDL $finish to use report not assert Changes: assert false report "SIMULATION FINISHED" severity failure; To just: report "SIMULATION FINISHED" severity failure; --- tgt-vhdl/stmt.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tgt-vhdl/stmt.cc b/tgt-vhdl/stmt.cc index 2c013104f..57c1b055b 100644 --- a/tgt-vhdl/stmt.cc +++ b/tgt-vhdl/stmt.cc @@ -54,7 +54,9 @@ static int draw_stask_finish(vhdl_procedural *proc, stmt_container *container, container->add_stmt(new vhdl_pcall_stmt("work.Verilog_Support.Finish")); } else { - container->add_stmt(new vhdl_assert_stmt("SIMULATION FINISHED")); + container->add_stmt( + new vhdl_report_stmt(new vhdl_const_string("SIMULATION FINISHED"), + SEVERITY_FAILURE)); } return 0; From 19b592a33606ea8a8a12d3d5a3db690375a7aac6 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Tue, 5 Oct 2010 20:02:49 +0100 Subject: [PATCH 5/6] List parameters/values in VHDL entity comment For example: -- Generated from Verilog module child (vhdl_tests/generics.v:30) -- MY_VALUE = 3 entity child is To make it clear which values were used for this entity. Conflicts: tgt-vhdl/scope.cc --- tgt-vhdl/scope.cc | 20 ++++++++++++++++++++ tgt-vhdl/vhdl_element.cc | 17 ++++++++++++++--- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/tgt-vhdl/scope.cc b/tgt-vhdl/scope.cc index 1cd47f91e..f1ec5c17a 100644 --- a/tgt-vhdl/scope.cc +++ b/tgt-vhdl/scope.cc @@ -926,6 +926,26 @@ static void create_skeleton_entity_for(ivl_scope_t scope, int depth) << " (" << ivl_scope_def_file(scope) << ":" << ivl_scope_def_lineno(scope) << ")"; + unsigned nparams = ivl_scope_params(scope); + for (unsigned i = 0; i < nparams; i++) { + ivl_parameter_t param = ivl_scope_param(scope, i); + ss << "\n " << ivl_parameter_basename(param) << " = "; + + ivl_expr_t value = ivl_parameter_expr(param); + switch (ivl_expr_type(value)) { + case IVL_EX_STRING: + ss << "\"" << ivl_expr_string(value) << "\""; + break; + + case IVL_EX_NUMBER: + ss << ivl_expr_value(value); + break; + + default: + assert(false); + } + } + arch->set_comment(ss.str()); ent->set_comment(ss.str()); diff --git a/tgt-vhdl/vhdl_element.cc b/tgt-vhdl/vhdl_element.cc index 21478c952..8d5bdcb44 100644 --- a/tgt-vhdl/vhdl_element.cc +++ b/tgt-vhdl/vhdl_element.cc @@ -81,10 +81,21 @@ void vhdl_element::emit_comment(std::ostream &of, int level, { if (comment_.size() > 0) { if (end_of_line) - of << " "; - of << "-- " << comment_; - if (!end_of_line) + of << " -- " << comment_; + else { + // Comment may contain embedded newlines + of << "-- "; + for (string::const_iterator it = comment_.begin(); + it != comment_.end(); ++it) { + if (*it == '\n') { + newline(of, level); + of << "-- "; + } + else + of << *it; + } newline(of, level); + } } } From 0144b5a2bb68fc00e83e467226726e2f662c3064 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sat, 11 Sep 2010 12:17:06 +0100 Subject: [PATCH 6/6] Basic parameter support in VHDL target This is a fix for pr2555831. A separate entity/architecture pair is generated for each module that is instantiated with a unique parameter combination. --- tgt-vhdl/scope.cc | 2 +- tgt-vhdl/state.cc | 75 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/tgt-vhdl/scope.cc b/tgt-vhdl/scope.cc index f1ec5c17a..43ddd328b 100644 --- a/tgt-vhdl/scope.cc +++ b/tgt-vhdl/scope.cc @@ -938,7 +938,7 @@ static void create_skeleton_entity_for(ivl_scope_t scope, int depth) break; case IVL_EX_NUMBER: - ss << ivl_expr_value(value); + ss << ivl_expr_uvalue(value); break; default: diff --git a/tgt-vhdl/state.cc b/tgt-vhdl/state.cc index 8de2f33d0..01262ab62 100644 --- a/tgt-vhdl/state.cc +++ b/tgt-vhdl/state.cc @@ -25,8 +25,9 @@ #include #include #include -#include +#include #include +#include using namespace std; @@ -67,7 +68,7 @@ struct signal_defn_t { static entity_list_t g_entities; // Store the mapping of ivl scope names to entity names -typedef map scope_name_map_t; +typedef map scope_name_map_t; static scope_name_map_t g_scope_names; typedef std::map signal_defn_map_t; @@ -77,10 +78,9 @@ static vhdl_entity *g_active_entity = NULL; // Set of scopes that are treated as the default examples of // that type. Any other scopes of the same type are ignored. -typedef set default_scopes_t; +typedef vector default_scopes_t; static default_scopes_t g_default_scopes; - // True if signal `sig' has already been encountered by the code // generator. This means we have already assigned it to a VHDL code // object and possibly renamed it. @@ -179,18 +179,31 @@ vhdl_entity* find_entity(ivl_scope_t scope) assert(ivl_scope_type(scope) == IVL_SCT_MODULE); - scope_name_map_t::iterator it = g_scope_names.find(ivl_scope_tname(scope)); - if (it != g_scope_names.end()) - return find_entity((*it).second); - else + if (is_default_scope_instance(scope)) { + scope_name_map_t::iterator it = g_scope_names.find(scope); + if (it != g_scope_names.end()) + return find_entity((*it).second); + else + return NULL; + } + else { + const char *tname = ivl_scope_tname(scope); + + for (scope_name_map_t::iterator it = g_scope_names.begin(); + it != g_scope_names.end(); ++it) { + if (strcmp(tname, ivl_scope_tname((*it).first)) == 0) + return find_entity((*it).second); + } + return NULL; + } } // Add an entity/architecture pair to the list of entities to emit. void remember_entity(vhdl_entity* ent, ivl_scope_t scope) { g_entities.push_back(ent); - g_scope_names[ivl_scope_tname(scope)] = ent->get_name(); + g_scope_names[scope] = ent->get_name(); } // Print all VHDL entities, in order, to the specified output stream. @@ -228,12 +241,52 @@ void set_active_entity(vhdl_entity *ent) { g_active_entity = ent; } + /* * True if two scopes have the same type name. */ static bool same_scope_type_name(ivl_scope_t a, ivl_scope_t b) { - return strcmp(ivl_scope_tname(a), ivl_scope_tname(b)) == 0; + if (strcmp(ivl_scope_tname(a), ivl_scope_tname(b)) != 0) + return false; + + unsigned nparams_a = ivl_scope_params(a); + unsigned nparams_b = ivl_scope_params(b); + + if (nparams_a != nparams_b) + return false; + + for (unsigned i = 0; i < nparams_a; i++) { + ivl_parameter_t param_a = ivl_scope_param(a, i); + ivl_parameter_t param_b = ivl_scope_param(b, i); + + if (strcmp(ivl_parameter_basename(param_a), + ivl_parameter_basename(param_b)) != 0) + return false; + + ivl_expr_t value_a = ivl_parameter_expr(param_a); + ivl_expr_t value_b = ivl_parameter_expr(param_b); + + if (ivl_expr_type(value_a) != ivl_expr_type(value_b)) + return false; + + switch (ivl_expr_type(value_a)) { + case IVL_EX_STRING: + if (strcmp(ivl_expr_string(value_a), ivl_expr_string(value_b)) != 0) + return false; + break; + + case IVL_EX_NUMBER: + if (ivl_expr_uvalue(value_a) != ivl_expr_uvalue(value_b)) + return false; + break; + + default: + assert(false); + } + } + + return true; } /* @@ -246,7 +299,7 @@ bool seen_this_scope_type(ivl_scope_t s) if (find_if(g_default_scopes.begin(), g_default_scopes.end(), bind1st(ptr_fun(same_scope_type_name), s)) == g_default_scopes.end()) { - g_default_scopes.insert(s); + g_default_scopes.push_back(s); return false; } else