Fix various problems with VHDL `buffer' port generation

This patch corrects several bugs with the generation of
VHDL `buffer' ports. The code generator should now
generate a buffer only if the port needs to be read inside
the architecture, otherwise it will stay `out'.

This also correct a bug where an output port is connected
directly to the output of an instantiated component. Generating
`buffer's would work here, but a more idiomatic VHDL approach is
to declare an intermediate signal which both outputs are connected
to. This is implemented in the patch (fixes the regression of
readout.v in the testsuite).
This commit is contained in:
Nick Gasson 2009-01-24 14:16:19 +00:00 committed by Stephen Williams
parent b8e350188e
commit 69e91e4065
5 changed files with 71 additions and 25 deletions

View File

@ -91,6 +91,10 @@ static vhdl_var_ref *translate_signal(ivl_expr_t e)
vhdl_decl *decl = scope->get_decl(renamed);
assert(decl);
// Make sure we can read from this declaration
// E.g. if this is an `out' port then we need to make it a buffer
decl->ensure_readable();
// Can't generate a constant initialiser for this signal
// later as it has already been read
if (scope->initializing())

View File

@ -1,7 +1,7 @@
/*
* VHDL code generation for logic devices.
*
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
* 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
@ -26,7 +26,18 @@
#include <sstream>
#include <iostream>
// Return a variable reference for a nexus that is guaranteed to
// be readable.
static vhdl_var_ref *readable_ref(vhdl_scope* scope, ivl_nexus_t nex)
{
vhdl_var_ref* ref = nexus_to_var_ref(scope, nex);
vhdl_decl* decl = scope->get_decl(ref->get_name());
decl->ensure_readable();
return ref;
}
/*
* Convert the inputs of a logic gate to a binary expression.
*/
@ -41,7 +52,8 @@ static vhdl_expr *inputs_to_expr(vhdl_scope *scope, vhdl_binop_t op,
int npins = ivl_logic_pins(log);
for (int i = 1; i < npins; i++) {
ivl_nexus_t input = ivl_logic_pin(log, i);
gate->add_expr(nexus_to_var_ref(scope, input));
gate->add_expr(readable_ref(scope, input));
}
return gate;
@ -56,7 +68,7 @@ static vhdl_expr *input_to_expr(vhdl_scope *scope, vhdl_unaryop_t op,
ivl_nexus_t input = ivl_logic_pin(log, 1);
assert(input);
vhdl_expr *operand = nexus_to_var_ref(scope, input);
vhdl_expr *operand = readable_ref(scope, input);
return new vhdl_unaryop_expr(op, operand, vhdl_type::std_logic());
}
@ -66,11 +78,9 @@ static void bufif_logic(vhdl_arch *arch, ivl_net_logic_t log, bool if0)
vhdl_var_ref *lhs = nexus_to_var_ref(arch->get_scope(), output);
assert(lhs);
vhdl_expr *val = nexus_to_var_ref(arch->get_scope(), ivl_logic_pin(log, 1));
assert(val);
vhdl_expr *val = readable_ref(arch->get_scope(), ivl_logic_pin(log, 1));
vhdl_expr *sel = nexus_to_var_ref(arch->get_scope(), ivl_logic_pin(log, 2));
assert(val);
vhdl_expr *sel = readable_ref(arch->get_scope(), ivl_logic_pin(log, 2));
vhdl_expr *cmp;
if (ivl_logic_width(log) == 1) {

View File

@ -1,7 +1,7 @@
/*
* VHDL code generation for scopes.
*
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
* 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
@ -550,24 +550,25 @@ static void map_signal(ivl_signal_t to, vhdl_entity *parent,
// the child entity, then VHDL will not let us read the value
// of the signal (i.e. it must pass straight through).
// However, Verilog allows the signal to be read in the parent.
// The VHDL equivalent of this is to make *both* output ports
// a `buffer'.
vhdl_decl *decl =
parent->get_arch()->get_scope()->get_decl(ref->get_name());
vhdl_port_decl *pdecl;
if ((pdecl = dynamic_cast<vhdl_port_decl*>(decl))
&& pdecl->get_mode() == VHDL_PORT_OUT) {
// The solution used here is to create an intermediate signal
// and connect it to both ports.
vhdl_decl* from_decl =
parent->get_arch()->get_scope()->get_decl(ref->get_name());
if (!from_decl->is_readable()
&& !arch_scope->have_declared(name + "_Readable")) {
vhdl_decl* tmp_decl =
new vhdl_signal_decl(name + "_Readable", ref->get_type());
// First change the mode in the parent entity
//pdecl->set_mode(VHDL_PORT_BUFFER);
// Add a comment to explain what this is for
tmp_decl->set_comment("Needed to connect outputs");
// Now change the mode in the child entity
/* vhdl_port_decl *to_pdecl =
dynamic_cast<vhdl_port_decl*>(find_scope_for_signal(to)->get_decl(name));
assert(to_pdecl);
to_pdecl->set_mode(VHDL_PORT_BUFFER);*/
arch_scope->add_decl(tmp_decl);
parent->get_arch()->add_stmt
(new vhdl_cassign_stmt(from_decl->make_ref(), tmp_decl->make_ref()));
ref = tmp_decl->make_ref();
}
inst->map_port(name.c_str(), ref);
}

View File

@ -355,6 +355,12 @@ vhdl_decl::~vhdl_decl()
}
// Make a reference object to this declaration
vhdl_var_ref* vhdl_decl::make_ref() const
{
return new vhdl_var_ref(name_, type_);
}
const vhdl_type *vhdl_decl::get_type() const
{
assert(type_);
@ -392,6 +398,19 @@ void vhdl_port_decl::emit(std::ostream &of, int level) const
type_->emit(of, level);
}
// If this is an `out' port make it a `buffer' so we can read from it
void vhdl_port_decl::ensure_readable()
{
if (mode_ == VHDL_PORT_OUT)
mode_ = VHDL_PORT_BUFFER;
}
// A port is readable if it is not `out'.
bool vhdl_port_decl::is_readable() const
{
return mode_ != VHDL_PORT_OUT;
}
void vhdl_var_decl::emit(std::ostream &of, int level) const
{
of << "variable " << name_ << " : ";

View File

@ -554,6 +554,9 @@ public:
void set_initial(vhdl_expr *initial);
bool has_initial() const { return has_initial_; }
// Return a new reference to this declaration
vhdl_var_ref* make_ref() const;
// The different sorts of assignment statement
// ASSIGN_CONST is used to generate a variable to shadow a
// constant that cannot be assigned to (e.g. a function parameter)
@ -565,6 +568,13 @@ public:
// to assign to it so calling assignment_type just raises
// an assertion failure
virtual assign_type_t assignment_type() const { assert(false); }
// True if this declaration can be read from
virtual bool is_readable() const { return true; }
// Modify this declaration so it can be read from
// This does nothing for most declaration types
virtual void ensure_readable() {}
protected:
std::string name_;
const vhdl_type *type_;
@ -618,7 +628,7 @@ public:
*/
class vhdl_signal_decl : public vhdl_decl {
public:
vhdl_signal_decl(const char *name, vhdl_type *type)
vhdl_signal_decl(const string& name, const vhdl_type* type)
: vhdl_decl(name, type) {}
virtual void emit(std::ostream &of, int level) const;
assign_type_t assignment_type() const { return ASSIGN_NONBLOCK; }
@ -658,6 +668,8 @@ public:
vhdl_port_mode_t get_mode() const { return mode_; }
void set_mode(vhdl_port_mode_t m) { mode_ = m; }
assign_type_t assignment_type() const { return ASSIGN_NONBLOCK; }
void ensure_readable();
bool is_readable() const;
private:
vhdl_port_mode_t mode_;
};