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/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/scope.cc b/tgt-vhdl/scope.cc index 1cd47f91e..43ddd328b 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_uvalue(value); + break; + + default: + assert(false); + } + } + arch->set_comment(ss.str()); ent->set_comment(ss.str()); 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 diff --git a/tgt-vhdl/stmt.cc b/tgt-vhdl/stmt.cc index 7d217eb09..57c1b055b 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 @@ -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 @@ -51,12 +54,114 @@ 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; } +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 +172,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 { @@ -131,27 +236,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 +347,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 +426,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 +450,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 +465,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 +522,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 +535,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 +562,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 +587,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 +614,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 +645,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 +665,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 +885,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 +1021,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 +1034,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 +1549,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_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); + } } } diff --git a/tgt-vhdl/vhdl_syntax.cc b/tgt-vhdl/vhdl_syntax.cc index 363a04736..b634fa918 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) @@ -124,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); @@ -193,6 +203,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 +419,7 @@ void vhdl_wait_stmt::emit(std::ostream &of, int level) const } of << ";"; + emit_comment(of, level, true); } vhdl_decl::~vhdl_decl() @@ -598,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 @@ -630,7 +647,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); } @@ -766,10 +783,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 ea7b7de3f..a27c45bac 100644 --- a/tgt-vhdl/vhdl_syntax.hh +++ b/tgt-vhdl/vhdl_syntax.hh @@ -52,7 +52,8 @@ 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 vhdl_expr *to_string(); + virtual void find_vars(vhdl_var_set_t& read) {} protected: static void open_parens(ostream& of); @@ -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() {} @@ -453,15 +454,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; }; @@ -770,6 +788,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 +797,7 @@ private: decl_list_t decls_; vhdl_scope *parent_; bool init_, sig_assign_; + bool hoisted_init_; }; @@ -795,6 +816,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 +830,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..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,10 +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 prune_wait_for_0(stmt_container *container); void require_support_function(support_function_t f); #endif /* #ifndef INC_VHDL_TARGET_H */