From 0d6b42885bcbb93737304ecc72b655a9229197bf Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sun, 15 Aug 2010 19:05:27 +0100 Subject: [PATCH 1/9] Reduce superflous parens in generated VHDL Purely cosmetic, replaces output like: if (x + foo(x + (2 * y))) then ... With: if x + foo(x + (2 * y)) then ... --- tgt-vhdl/vhdl_syntax.cc | 29 ++++++++++++++++++++++------- tgt-vhdl/vhdl_syntax.hh | 5 +++++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/tgt-vhdl/vhdl_syntax.cc b/tgt-vhdl/vhdl_syntax.cc index 753da7b37..5c2393d97 100644 --- a/tgt-vhdl/vhdl_syntax.cc +++ b/tgt-vhdl/vhdl_syntax.cc @@ -767,6 +767,22 @@ void vhdl_if_stmt::emit(std::ostream &of, int level) const of << "end if;"; } +int vhdl_expr::paren_levels(0); + +void vhdl_expr::open_parens(std::ostream& of) +{ + if (paren_levels++ > 0) + of << "("; +} + +void vhdl_expr::close_parens(std::ostream& of) +{ + assert(paren_levels > 0); + + if (--paren_levels > 0) + of << ")"; +} + vhdl_unaryop_expr::~vhdl_unaryop_expr() { @@ -774,7 +790,8 @@ vhdl_unaryop_expr::~vhdl_unaryop_expr() void vhdl_unaryop_expr::emit(std::ostream &of, int level) const { - of << "("; + open_parens(of); + switch (op_) { case VHDL_UNARYOP_NOT: of << "not "; @@ -784,7 +801,8 @@ void vhdl_unaryop_expr::emit(std::ostream &of, int level) const break; } operand_->emit(of, level); - of << ")"; + + close_parens(of); } vhdl_binop_expr::vhdl_binop_expr(vhdl_expr *left, vhdl_binop_t op, @@ -807,10 +825,7 @@ void vhdl_binop_expr::add_expr(vhdl_expr *e) void vhdl_binop_expr::emit(std::ostream &of, int level) const { - // Expressions are fully parenthesised to remove any - // ambiguity in the output - - of << "("; + open_parens(of); assert(operands_.size() > 0); std::list::const_iterator it = operands_.begin(); @@ -828,7 +843,7 @@ void vhdl_binop_expr::emit(std::ostream &of, int level) const (*it)->emit(of, level); } - of << ")"; + close_parens(of); } vhdl_bit_spec_expr::~vhdl_bit_spec_expr() diff --git a/tgt-vhdl/vhdl_syntax.hh b/tgt-vhdl/vhdl_syntax.hh index de3e11313..3c0c81aed 100644 --- a/tgt-vhdl/vhdl_syntax.hh +++ b/tgt-vhdl/vhdl_syntax.hh @@ -47,7 +47,12 @@ public: virtual vhdl_expr *to_integer(); virtual vhdl_expr *to_std_logic(); virtual vhdl_expr *to_vector(vhdl_type_name_t name, int w); + protected: + static void open_parens(ostream& of); + static void close_parens(ostream& of); + static int paren_levels; + const vhdl_type *type_; bool isconst_; }; From 406d3936affeea32597950a441c5b26d930c4887 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sun, 15 Aug 2010 21:52:58 +0100 Subject: [PATCH 2/9] Add find_vars method to VHDL syntax objects Finds set of read and written variables. For use in post-processing the syntax tree for cleanup. --- tgt-vhdl/vhdl_syntax.cc | 113 +++++++++++++++++++++++++++++++++++++++- tgt-vhdl/vhdl_syntax.hh | 30 ++++++++++- 2 files changed, 141 insertions(+), 2 deletions(-) diff --git a/tgt-vhdl/vhdl_syntax.cc b/tgt-vhdl/vhdl_syntax.cc index 5c2393d97..b1d87b832 100644 --- a/tgt-vhdl/vhdl_syntax.cc +++ b/tgt-vhdl/vhdl_syntax.cc @@ -255,6 +255,17 @@ void stmt_container::move_stmts_from(stmt_container *other) other->stmts_.clear(); } +void stmt_container::find_vars(vhdl_var_set_t& read, + vhdl_var_set_t& write) +{ + // Iterate over each sub-statement and find all its + // read/written variables + + for (stmt_list_t::const_iterator it = stmts_.begin(); + it != stmts_.end(); ++it) + (*it)->find_vars(read, write); +} + void stmt_container::emit(std::ostream &of, int level, bool newline) const { emit_children(of, stmts_, level, "", newline); @@ -347,6 +358,13 @@ vhdl_wait_stmt::~vhdl_wait_stmt() } +void vhdl_wait_stmt::find_vars(vhdl_var_set_t& read, + vhdl_var_set_t& write) +{ + if (expr_) + expr_->find_vars(read); +} + void vhdl_wait_stmt::emit(std::ostream &of, int level) const { of << "wait"; @@ -497,6 +515,13 @@ vhdl_expr_list::~vhdl_expr_list() } +void vhdl_expr_list::find_vars(vhdl_var_set_t& read) +{ + for (list::const_iterator it = exprs_.begin(); + it != exprs_.end(); ++it) + (*it)->find_vars(read); +} + void vhdl_expr_list::emit(std::ostream &of, int level) const { of << "("; @@ -520,6 +545,12 @@ void vhdl_pcall_stmt::emit(std::ostream &of, int level) const of << ";"; } +void vhdl_pcall_stmt::find_vars(vhdl_var_set_t& read, + vhdl_var_set_t& write) +{ + exprs_.find_vars(read); +} + vhdl_var_ref::~vhdl_var_ref() { @@ -545,7 +576,12 @@ void vhdl_var_ref::set_slice(vhdl_expr *s, int w) type_ = vhdl_type::std_logic(); } } - + +void vhdl_var_ref::find_vars(vhdl_var_set_t& read) +{ + read.insert(this); +} + void vhdl_var_ref::emit(std::ostream &of, int level) const { of << name_; @@ -575,6 +611,11 @@ void vhdl_null_stmt::emit(std::ostream &of, int level) const emit_comment(of, level, true); } +void vhdl_fcall::find_vars(vhdl_var_set_t& read) +{ + exprs_.find_vars(read); +} + void vhdl_fcall::emit(std::ostream &of, int level) const { of << name_; @@ -586,6 +627,13 @@ 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_); + rhs_->find_vars(read); +} + void vhdl_nbassign_stmt::emit(std::ostream &of, int level) const { lhs_->emit(of, level); @@ -767,6 +815,21 @@ void vhdl_if_stmt::emit(std::ostream &of, int level) const of << "end if;"; } +void vhdl_if_stmt::find_vars(vhdl_var_set_t& read, + vhdl_var_set_t& write) +{ + test_->find_vars(read); + + then_part_.find_vars(read, write); + else_part_.find_vars(read, write); + + for (list::const_iterator it = elsif_parts_.begin(); + it != elsif_parts_.end(); ++it) { + (*it).test->find_vars(read); + (*it).container->find_vars(read, write); + } +} + int vhdl_expr::paren_levels(0); void vhdl_expr::open_parens(std::ostream& of) @@ -788,6 +851,11 @@ vhdl_unaryop_expr::~vhdl_unaryop_expr() } +void vhdl_unaryop_expr::find_vars(vhdl_var_set_t& read) +{ + operand_->find_vars(read); +} + void vhdl_unaryop_expr::emit(std::ostream &of, int level) const { open_parens(of); @@ -823,6 +891,13 @@ void vhdl_binop_expr::add_expr(vhdl_expr *e) operands_.push_back(e); } +void vhdl_binop_expr::find_vars(vhdl_var_set_t& read) +{ + for (list::const_iterator it = operands_.begin(); + it != operands_.end(); ++it) + (*it)->find_vars(read); +} + void vhdl_binop_expr::emit(std::ostream &of, int level) const { open_parens(of); @@ -896,6 +971,18 @@ vhdl_case_stmt::~vhdl_case_stmt() } +void vhdl_case_stmt::find_vars(vhdl_var_set_t& read, + vhdl_var_set_t& write) +{ + test_->find_vars(read); + + for (case_branch_list_t::const_iterator it = branches_.begin(); + it != branches_.end(); ++it) { + (*it)->when_->find_vars(read); + (*it)->stmts_.find_vars(read, write); + } +} + void vhdl_case_stmt::emit(std::ostream &of, int level) const { of << "case "; @@ -921,6 +1008,14 @@ vhdl_while_stmt::~vhdl_while_stmt() } +void vhdl_while_stmt::find_vars(vhdl_var_set_t& read, + vhdl_var_set_t& write) +{ + test_->find_vars(read); + + vhdl_loop_stmt::find_vars(read, write); +} + void vhdl_while_stmt::emit(std::ostream &of, int level) const { of << "while "; @@ -929,6 +1024,12 @@ void vhdl_while_stmt::emit(std::ostream &of, int level) const vhdl_loop_stmt::emit(of, level); } +void vhdl_loop_stmt::find_vars(vhdl_var_set_t& read, + vhdl_var_set_t& write) +{ + stmts_.find_vars(read, write); +} + void vhdl_loop_stmt::emit(std::ostream &of, int level) const { of << "loop"; @@ -941,6 +1042,16 @@ vhdl_for_stmt::~vhdl_for_stmt() } + +void vhdl_for_stmt::find_vars(vhdl_var_set_t& read, + vhdl_var_set_t& write) +{ + from_->find_vars(read); + to_->find_vars(write); + + vhdl_loop_stmt::find_vars(read, write); +} + void vhdl_for_stmt::emit(std::ostream &of, int level) const { of << "for " << lname_ << " in "; diff --git a/tgt-vhdl/vhdl_syntax.hh b/tgt-vhdl/vhdl_syntax.hh index 3c0c81aed..586a8630c 100644 --- a/tgt-vhdl/vhdl_syntax.hh +++ b/tgt-vhdl/vhdl_syntax.hh @@ -22,6 +22,7 @@ #define INC_VHDL_SYNTAX_HH #include +#include #include #include "vhdl_element.hh" #include "vhdl_type.hh" @@ -31,6 +32,9 @@ using namespace std; class vhdl_scope; class vhdl_entity; class vhdl_arch; +class vhdl_var_ref; + +typedef set vhdl_var_set_t; class vhdl_expr : public vhdl_element { public: @@ -47,6 +51,7 @@ public: virtual vhdl_expr *to_integer(); virtual vhdl_expr *to_std_logic(); virtual vhdl_expr *to_vector(vhdl_type_name_t name, int w); + virtual void find_vars(vhdl_var_set_t& read) const {} protected: static void open_parens(ostream& of); @@ -72,13 +77,13 @@ public: const std::string &get_name() const { return name_; } void set_name(const std::string &name) { name_ = name; } void set_slice(vhdl_expr *s, int w=0); + void find_vars(vhdl_var_set_t& read); private: std::string name_; vhdl_expr *slice_; unsigned slice_width_; }; - enum vhdl_binop_t { VHDL_BINOP_AND = 0, VHDL_BINOP_OR, @@ -120,6 +125,7 @@ public: void add_expr(vhdl_expr *e); void emit(std::ostream &of, int level) const; + void find_vars(vhdl_var_set_t& read); private: std::list operands_; vhdl_binop_t op_; @@ -139,6 +145,7 @@ public: ~vhdl_unaryop_expr(); void emit(std::ostream &of, int level) const; + void find_vars(vhdl_var_set_t& read); private: vhdl_unaryop_t op_; vhdl_expr *operand_; @@ -250,6 +257,7 @@ public: void emit(std::ostream &of, int level) const; bool empty() const { return exprs_.empty(); } void add_expr(vhdl_expr *e); + void find_vars(vhdl_var_set_t& read); private: std::list exprs_; }; @@ -266,6 +274,7 @@ public: void add_expr(vhdl_expr *e) { exprs_.add_expr(e); } void emit(std::ostream &of, int level) const; + void find_vars(vhdl_var_set_t& read); private: std::string name_; vhdl_expr_list exprs_; @@ -336,6 +345,12 @@ private: class vhdl_seq_stmt : public vhdl_element { public: virtual ~vhdl_seq_stmt() {} + + // Find all the variables that are read or written in the + // expressions within this statement + // This is used to clean up the VHDL output + virtual void find_vars(vhdl_var_set_t& read, + vhdl_var_set_t& write) = 0; }; @@ -351,6 +366,7 @@ public: void move_stmts_from(stmt_container *other); void emit(std::ostream &of, int level, bool newline=true) const; bool empty() const { return stmts_.empty(); } + void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write); typedef std::list stmt_list_t; stmt_list_t &get_stmts() { return stmts_; } @@ -369,6 +385,7 @@ public: virtual ~vhdl_abstract_assign_stmt(); void set_after(vhdl_expr *after) { after_ = after; } + void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write); protected: vhdl_var_ref *lhs_; vhdl_expr *rhs_, *after_; @@ -419,6 +436,7 @@ public: void emit(std::ostream &of, int level) const; void add_sensitivity(const std::string &s) { sensitivity_.push_back(s); } vhdl_wait_type_t get_type() const { return type_; } + void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write); private: vhdl_wait_type_t type_; vhdl_expr *expr_; @@ -429,6 +447,7 @@ private: class vhdl_null_stmt : public vhdl_seq_stmt { public: void emit(std::ostream &of, int level) const; + void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write) {} }; @@ -438,6 +457,7 @@ public: : reason_(reason) {} void emit(std::ostream &of, int level) const; + void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write) {} private: std::string reason_; }; @@ -452,6 +472,7 @@ public: stmt_container *get_else_container() { return &else_part_; } stmt_container *add_elsif(vhdl_expr *test); void emit(std::ostream &of, int level) const; + void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write); private: struct elsif { vhdl_expr *test; @@ -469,6 +490,7 @@ private: * expression part and a statement container. */ class vhdl_case_branch : public vhdl_element { + friend class vhdl_case_stmt; public: vhdl_case_branch(vhdl_expr *when) : when_(when) {} ~vhdl_case_branch(); @@ -489,6 +511,7 @@ public: void add_branch(vhdl_case_branch *b) { branches_.push_back(b); } void emit(std::ostream &of, int level) const; + void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write); private: vhdl_expr *test_; case_branch_list_t branches_; @@ -501,6 +524,8 @@ public: stmt_container *get_container() { return &stmts_; } void emit(std::ostream &of, int level) const; + virtual void find_vars(vhdl_var_set_t& read, + vhdl_var_set_t& write); private: stmt_container stmts_; }; @@ -512,6 +537,7 @@ public: ~vhdl_while_stmt(); void emit(std::ostream &of, int level) const; + void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write); private: vhdl_expr *test_; }; @@ -524,6 +550,7 @@ public: ~vhdl_for_stmt(); void emit(std::ostream &of, int level) const; + void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write); private: const char *lname_; vhdl_expr *from_, *to_; @@ -540,6 +567,7 @@ public: void emit(std::ostream &of, int level) const; void add_expr(vhdl_expr *e) { exprs_.add_expr(e); } + void find_vars(vhdl_var_set_t& read, vhdl_var_set_t& write); private: std::string name_; vhdl_expr_list exprs_; From f9da800cf5af6c104370fa85b31f472ca67ffe24 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Tue, 17 Aug 2010 22:42:49 +0100 Subject: [PATCH 3/9] 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; --- tgt-vhdl/process.cc | 15 ++-- tgt-vhdl/stmt.cc | 162 ++++++++++++++++++++++++++-------------- tgt-vhdl/vhdl_syntax.cc | 28 ++++++- tgt-vhdl/vhdl_syntax.hh | 14 +++- tgt-vhdl/vhdl_target.h | 1 - 5 files changed, 148 insertions(+), 72 deletions(-) diff --git a/tgt-vhdl/process.cc b/tgt-vhdl/process.cc index 9440b25cf..93e2fba8f 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 774ce9d06..7ac6e6fc7 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,21 +321,33 @@ 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, // a variable, etc?) 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,7 +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) + if (after != NULL) a->set_after(after); } else { @@ -408,8 +458,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 +483,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 +510,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 +541,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,7 +561,7 @@ 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 +781,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 idomatic way before we // fall back on the generic translation @@ -875,9 +916,12 @@ static int draw_if(vhdl_procedural *proc, stmt_container *container, vhdl_expr *test = translate_expr(ivl_stmt_cond_expr(stmt)); 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 +930,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,8 +1445,12 @@ 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 b1d87b832..a65bdf276 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 586a8630c..8bab208d8 100644 --- a/tgt-vhdl/vhdl_syntax.hh +++ b/tgt-vhdl/vhdl_syntax.hh @@ -51,7 +51,7 @@ public: virtual vhdl_expr *to_integer(); virtual vhdl_expr *to_std_logic(); 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); @@ -768,6 +768,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_; } @@ -775,6 +777,7 @@ private: decl_list_t decls_; vhdl_scope *parent_; bool init_, sig_assign_; + bool hoisted_init_; }; @@ -793,6 +796,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_; @@ -802,6 +810,10 @@ protected: // If this is the case then we can't use a sensitvity 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 8d99d97a1..083274252 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 0cec4495caff499cf72da2f6bc6025d0a7600d4d Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Wed, 18 Aug 2010 23:50:13 +0100 Subject: [PATCH 4/9] Add VHDL report statement Not output yet, but will be used to replace std.textio implementation of $display. --- 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 a65bdf276..6a016b51a 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 8bab208d8..2df4b5373 100644 --- a/tgt-vhdl/vhdl_syntax.hh +++ b/tgt-vhdl/vhdl_syntax.hh @@ -451,15 +451,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 48ae8c1ce58a1643473cf097c46803f5b4298c51 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Tue, 24 Aug 2010 22:13:08 +0100 Subject: [PATCH 5/9] 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. --- 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 | 3 +- 7 files changed, 132 insertions(+), 213 deletions(-) delete mode 100644 tgt-vhdl/display.cc diff --git a/tgt-vhdl/Makefile.in b/tgt-vhdl/Makefile.in index 533013cc9..ef1e54de0 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 22f6b89f4..1612e6514 100644 --- a/tgt-vhdl/cast.cc +++ b/tgt-vhdl/cast.cc @@ -56,6 +56,8 @@ vhdl_expr *vhdl_expr::cast(const vhdl_type *to) return to_vector(to->get_name(), to->get_width()); case VHDL_TYPE_STD_LOGIC: return to_std_logic(); + case VHDL_TYPE_STRING: + return to_string(); default: assert(false); } @@ -112,6 +114,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 75eca6110..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 7ac6e6fc7..bc1abdeed 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 6a016b51a..e944abe1f 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 2df4b5373..5d8ab2f83 100644 --- a/tgt-vhdl/vhdl_syntax.hh +++ b/tgt-vhdl/vhdl_syntax.hh @@ -51,6 +51,7 @@ public: virtual vhdl_expr *to_integer(); virtual vhdl_expr *to_std_logic(); 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: @@ -175,7 +176,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; @@ -268,7 +269,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 083274252..60510f83a 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 @@ -29,8 +30,6 @@ 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 0c883a00bf7292851b2d6320e157891c2152a769 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Tue, 24 Aug 2010 22:17:11 +0100 Subject: [PATCH 6/9] 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 bc1abdeed..1fcc44fae 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 e41f2f36a38e1daabcb90c9c1ee16d20c6ae30ff Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sat, 28 Aug 2010 17:13:23 +0100 Subject: [PATCH 7/9] 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. --- 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 35b5d3b76..423fe505d 100644 --- a/tgt-vhdl/scope.cc +++ b/tgt-vhdl/scope.cc @@ -920,6 +920,26 @@ static void create_skeleton_entity_for(ivl_scope_t scope, int depth) ss << "Generated from Verilog module " << ivl_scope_tname(scope) << " (" << 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 6c1b5cbd8..06b97c784 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 d9bf96d8fa09860509cdcbdef68a784567ff4837 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sat, 11 Sep 2010 12:17:06 +0100 Subject: [PATCH 8/9] 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 | 6 ++-- tgt-vhdl/state.cc | 75 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 67 insertions(+), 14 deletions(-) diff --git a/tgt-vhdl/scope.cc b/tgt-vhdl/scope.cc index 423fe505d..bb932e374 100644 --- a/tgt-vhdl/scope.cc +++ b/tgt-vhdl/scope.cc @@ -933,7 +933,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: @@ -954,7 +954,7 @@ static void create_skeleton_entity_for(ivl_scope_t scope, int depth) extern "C" int draw_skeleton_scope(ivl_scope_t scope, void *_unused) { static int depth = 0; - + if (seen_this_scope_type(scope)) return 0; // Already generated a skeleton for this scope type @@ -1071,7 +1071,7 @@ extern "C" int draw_constant_drivers(ivl_scope_t scope, void *_parent) vhdl_var_ref *ref = nexus_to_var_ref(arch_scope, nex); ent->get_arch()->add_stmt - (new vhdl_cassign_stmt(ref, priv->const_driver)); + (new vhdl_cassign_stmt(ref, priv->const_driver)); priv->const_driver = NULL; } diff --git a/tgt-vhdl/state.cc b/tgt-vhdl/state.cc index 0ced93f27..e03c201e1 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 From 43f904f793590f76158453af2df6e437927e834f Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Tue, 24 Aug 2010 22:51:19 +0100 Subject: [PATCH 9/9] Add uwire support to VHDL backend Implemented as std_ulogic which behaves almost identically. --- tgt-vhdl/cast.cc | 28 ++++++++++++++++++++++++---- tgt-vhdl/scope.cc | 11 ++++++++--- tgt-vhdl/vhdl_syntax.hh | 2 ++ tgt-vhdl/vhdl_type.cc | 19 +++++++++++++++---- tgt-vhdl/vhdl_type.hh | 5 ++++- 5 files changed, 53 insertions(+), 12 deletions(-) diff --git a/tgt-vhdl/cast.cc b/tgt-vhdl/cast.cc index 1612e6514..e1568929f 100644 --- a/tgt-vhdl/cast.cc +++ b/tgt-vhdl/cast.cc @@ -28,10 +28,12 @@ vhdl_expr *vhdl_expr::cast(const vhdl_type *to) { - //std::cout << "Cast: from=" << type_->get_string() - // << " (" << type_->get_width() << ") " - // << " to=" << to->get_string() << " (" - // << to->get_width() << ")" << std::endl; +#if 0 + std::cout << "Cast: from=" << type_->get_string() + << " (" << type_->get_width() << ") " + << " to=" << to->get_string() << " (" + << to->get_width() << ")" << std::endl; +#endif // If this expression hasn't been given a type then // we can't generate any type conversion code @@ -58,6 +60,8 @@ vhdl_expr *vhdl_expr::cast(const vhdl_type *to) return to_std_logic(); case VHDL_TYPE_STRING: return to_string(); + case VHDL_TYPE_STD_ULOGIC: + return to_std_ulogic(); default: assert(false); } @@ -206,6 +210,17 @@ vhdl_expr *vhdl_expr::to_std_logic() return NULL; } +vhdl_expr *vhdl_expr::to_std_ulogic() +{ + if (type_->get_name() == VHDL_TYPE_STD_LOGIC) { + vhdl_fcall *f = new vhdl_fcall("std_logic", vhdl_type::std_logic()); + f->add_expr(this); + return f; + } + else + assert(false); +} + /* * Change the width of a signed/unsigned type. */ @@ -325,6 +340,11 @@ vhdl_expr *vhdl_const_bit::to_boolean() return new vhdl_const_bool(bit_ == '1'); } +vhdl_expr *vhdl_const_bit::to_std_ulogic() +{ + return this; +} + vhdl_expr *vhdl_const_bit::to_vector(vhdl_type_name_t name, int w) { // Zero-extend this bit to the correct width diff --git a/tgt-vhdl/scope.cc b/tgt-vhdl/scope.cc index bb932e374..c7caa9716 100644 --- a/tgt-vhdl/scope.cc +++ b/tgt-vhdl/scope.cc @@ -277,6 +277,7 @@ void draw_nexus(ivl_nexus_t nexus) switch (signal_type_of_nexus(nexus, width)) { case IVL_SIT_TRI: + case IVL_SIT_UWIRE: def = 'Z'; break; case IVL_SIT_TRI0: @@ -556,9 +557,13 @@ static void declare_one_signal(vhdl_entity *ent, ivl_signal_t sig, sig_type = new vhdl_type(*array_type); } - else - sig_type = - vhdl_type::type_for(ivl_signal_width(sig), ivl_signal_signed(sig) != 0); + else { + sig_type = vhdl_type::type_for(ivl_signal_width(sig), + ivl_signal_signed(sig) != 0, + 0, ivl_signal_type(sig) == IVL_SIT_UWIRE); + + + } ivl_signal_port_t mode = ivl_signal_port(sig); switch (mode) { diff --git a/tgt-vhdl/vhdl_syntax.hh b/tgt-vhdl/vhdl_syntax.hh index 5d8ab2f83..80076f1c1 100644 --- a/tgt-vhdl/vhdl_syntax.hh +++ b/tgt-vhdl/vhdl_syntax.hh @@ -50,6 +50,7 @@ public: virtual vhdl_expr *to_boolean(); virtual vhdl_expr *to_integer(); 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) {} @@ -211,6 +212,7 @@ public: vhdl_expr *to_boolean(); vhdl_expr *to_integer(); vhdl_expr *to_vector(vhdl_type_name_t name, int w); + vhdl_expr *to_std_ulogic(); private: char bit_; }; diff --git a/tgt-vhdl/vhdl_type.cc b/tgt-vhdl/vhdl_type.cc index 9442826c3..c448f6a5e 100644 --- a/tgt-vhdl/vhdl_type.cc +++ b/tgt-vhdl/vhdl_type.cc @@ -24,12 +24,16 @@ #include #include - vhdl_type *vhdl_type::std_logic() { return new vhdl_type(VHDL_TYPE_STD_LOGIC); } +vhdl_type *vhdl_type::std_ulogic() +{ + return new vhdl_type(VHDL_TYPE_STD_ULOGIC); +} + vhdl_type *vhdl_type::string() { return new vhdl_type(VHDL_TYPE_STRING); @@ -79,6 +83,8 @@ std::string vhdl_type::get_string() const switch (name_) { case VHDL_TYPE_STD_LOGIC: return std::string("std_logic"); + case VHDL_TYPE_STD_ULOGIC: + return std::string("std_ulogic"); case VHDL_TYPE_STD_LOGIC_VECTOR: return std::string("std_logic_vector"); case VHDL_TYPE_STRING: @@ -167,10 +173,15 @@ vhdl_type *vhdl_type::std_logic_vector(int msb, int lsb) return new vhdl_type(VHDL_TYPE_STD_LOGIC_VECTOR, msb, lsb); } -vhdl_type *vhdl_type::type_for(int width, bool issigned, int lsb) +vhdl_type *vhdl_type::type_for(int width, bool issigned, + int lsb, bool unresolved) { - if (width == 1) - return vhdl_type::std_logic(); + if (width == 1) { + if (unresolved) + return vhdl_type::std_ulogic(); + else + return vhdl_type::std_logic(); + } else if (issigned) return vhdl_type::nsigned(width, lsb); else diff --git a/tgt-vhdl/vhdl_type.hh b/tgt-vhdl/vhdl_type.hh index 3d551a3f2..9786ab9ad 100644 --- a/tgt-vhdl/vhdl_type.hh +++ b/tgt-vhdl/vhdl_type.hh @@ -25,6 +25,7 @@ enum vhdl_type_name_t { VHDL_TYPE_STD_LOGIC, + VHDL_TYPE_STD_ULOGIC, VHDL_TYPE_STD_LOGIC_VECTOR, VHDL_TYPE_STRING, VHDL_TYPE_LINE, @@ -71,6 +72,7 @@ public: // Common types static vhdl_type *std_logic(); + static vhdl_type *std_ulogic(); static vhdl_type *string(); static vhdl_type *line(); static vhdl_type *std_logic_vector(int msb, int lsb); @@ -80,7 +82,8 @@ public: static vhdl_type *boolean(); static vhdl_type *time(); - static vhdl_type *type_for(int width, bool issigned, int lsb=0); + static vhdl_type *type_for(int width, bool issigned, + int lsb=0, bool unresolved=false); static vhdl_type *array_of(vhdl_type *b, std::string &n, int m, int l); protected: vhdl_type_name_t name_;