Blend NetPartSelect(PV) objects into NetConcat

If a signal s driven by multiple non-overlapping NetPartSelect(PV)
objects, then combine them into a single NetConcat object. This
eliminates the need for resolvers in the target.
This commit is contained in:
Stephen Williams 2012-12-19 19:01:22 -08:00
parent a90e264ed7
commit 367d7bf94b
7 changed files with 170 additions and 9 deletions

139
cprop.cc
View File

@ -19,13 +19,14 @@
# include "config.h" # include "config.h"
# include <algorithm>
# include <vector>
# include <cstdlib> # include <cstdlib>
# include "netlist.h" # include "netlist.h"
# include "netmisc.h" # include "netmisc.h"
# include "functor.h" # include "functor.h"
# include "compiler.h" # include "compiler.h"
# include "ivl_assert.h" # include "ivl_assert.h"
# include <vector>
/* /*
@ -46,6 +47,7 @@ struct cprop_functor : public functor_t {
virtual void lpm_ff(Design*des, NetFF*obj); virtual void lpm_ff(Design*des, NetFF*obj);
virtual void lpm_logic(Design*des, NetLogic*obj); virtual void lpm_logic(Design*des, NetLogic*obj);
virtual void lpm_mux(Design*des, NetMux*obj); virtual void lpm_mux(Design*des, NetMux*obj);
virtual void lpm_part_select(Design*des, NetPartSelect*obj);
}; };
void cprop_functor::signal(Design*, NetNet*) void cprop_functor::signal(Design*, NetNet*)
@ -150,6 +152,141 @@ void cprop_functor::lpm_mux(Design*des, NetMux*obj)
count += 1; count += 1;
} }
static bool compare_base(NetPartSelect*a, NetPartSelect*b)
{
return a->base() < b->base();
}
/*
* This optimization searches for Nexa that are driven only by
* NetPartSelect(PV) outputs. These might turn from Verilog input that
* looks like this:
* wire [7:0] foo
* assign foo[7:4] = a;
* assign foo[3:0] = b;
* The idea is to convert the part selects of the above to a single
* concatenation that looks like this:
* assign foo = {a, b};
*/
void cprop_functor::lpm_part_select(Design*des, NetPartSelect*obj)
{
if (obj->dir() != NetPartSelect::PV)
return;
NetScope*scope = obj->scope();
Nexus*nex = obj->pin(1).nexus();
vector<NetPartSelect*> obj_set;
for (Link*cur = nex->first_nlink() ; cur ; cur = cur->next_nlink()) {
// If this is an input (or passive) then ignore it.
if (cur->get_dir() != Link::OUTPUT)
continue;
// Check to see if this is the output of a
// NetPartSelect::PV. If not, then give up on the blend.
NetPins*tmp_obj = cur->get_obj();
unsigned tmp_pin = cur->get_pin();
NetPartSelect*cur_obj = dynamic_cast<NetPartSelect*> (tmp_obj);
if (cur_obj == 0)
return;
if (cur_obj->dir() != NetPartSelect::PV)
return;
if (tmp_pin != 1)
return;
obj_set.push_back(cur_obj);
}
if (obj_set.size() < 2)
return;
if (debug_optimizer)
cerr << obj->get_fileline() << ": cprop::lpm_part_select: "
<< "Found " << obj_set.size() << " NetPartSelect(PV) objects."
<< endl;
// Sort by increasing base offset.
sort(obj_set.begin(), obj_set.end(), compare_base);
// Check and make sure there are no overlaps. If there are,
// then give up on this optimization.
for (size_t idx = 1 ; idx < obj_set.size() ; idx += 1) {
unsigned top = obj_set[idx-1]->base() + obj_set[idx-1]->width();
if (top > obj_set[idx]->base()) {
if (debug_optimizer)
cerr << obj->get_fileline() << ": cprop::lpm_part_select: "
<< "Range [" << obj_set[idx-1]->base()
<< " " << top << ") overlaps PV starting at "
<< obj_set[idx]->base() << ". Give up." << endl;
return;
}
}
// Check if the tail runs off the end of the target. If so it
// should be possible to replace it with a bit select to
// shorten the object for the target, but for now just give up.
unsigned sig_width = nex->vector_width();
if (obj_set.back()->base() + obj_set.back()->width() > sig_width) {
if (debug_optimizer)
cerr << obj->get_fileline() << ": cprop::lpm_part_select: "
<< "Range [" << obj_set.back()->base()
<< ":" << (obj_set.back()->base() + obj_set.back()->width() - 1)
<< "] runs off the end of target." << endl;
return;
}
// Figure out how many components we are going to need.
unsigned part_count = 0;
unsigned off = 0;
for (size_t idx = 0 ; idx < obj_set.size() ; idx += 1) {
if (obj_set[idx]->base() > off) {
off = obj_set[idx]->base();
part_count += 1;
}
off += obj_set[idx]->width();
part_count += 1;
}
if (off < sig_width)
part_count += 1;
NetConcat*concat = new NetConcat(scope, scope->local_symbol(),
sig_width, part_count);
des->add_node(concat);
connect(concat->pin(0), obj->pin(1));
off = 0;
size_t concat_pin = 1;
for (size_t idx = 0 ; idx < obj_set.size() ; idx += 1) {
NetPartSelect*cobj = obj_set[idx];
if (cobj->base() > off) {
NetNet*zzz = make_const_z(des, scope, cobj->base()-off);
connect(concat->pin(concat_pin), zzz->pin(0));
concat_pin += 1;
off = cobj->base();
}
connect(concat->pin(concat_pin), cobj->pin(0));
concat_pin += 1;
off += cobj->width();
}
if (off < sig_width) {
NetNet*zzz = make_const_z(des, scope, sig_width-off);
connect(concat->pin(concat_pin), zzz->pin(0));
concat_pin += 1;
}
ivl_assert(*obj, concat_pin == concat->pin_count());
for (size_t idx = 0 ; idx < obj_set.size() ; idx += 1) {
delete obj_set[idx];
}
count += 1;
}
/* /*
* This functor looks to see if the constant is connected to nothing * This functor looks to see if the constant is connected to nothing
* but signals. If that is the case, delete the dangling constant and * but signals. If that is the case, delete the dangling constant and

View File

@ -24,6 +24,8 @@
# include "functor.h" # include "functor.h"
# include "netlist.h" # include "netlist.h"
using namespace std;
functor_t::~functor_t() functor_t::~functor_t()
{ {
} }
@ -84,6 +86,10 @@ void functor_t::lpm_mux(Design*, NetMux*)
{ {
} }
void functor_t::lpm_part_select(Design*, NetPartSelect*)
{
}
void functor_t::lpm_pow(Design*, NetPow*) void functor_t::lpm_pow(Design*, NetPow*)
{ {
} }
@ -225,6 +231,11 @@ void NetMux::functor_node(Design*des, functor_t*fun)
fun->lpm_mux(des, this); fun->lpm_mux(des, this);
} }
void NetPartSelect::functor_node(Design*des, functor_t*fun)
{
fun->lpm_part_select(des, this);
}
void NetPow::functor_node(Design*des, functor_t*fun) void NetPow::functor_node(Design*des, functor_t*fun)
{ {
fun->lpm_pow(des, this); fun->lpm_pow(des, this);

View File

@ -1,7 +1,7 @@
#ifndef __functor_H #ifndef __functor_H
#define __functor_H #define __functor_H
/* /*
* Copyright (c) 1999-2008 Stephen Williams (steve@icarus.com) * Copyright (c) 1999-2008,2012 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -81,6 +81,8 @@ struct functor_t {
/* This method is called for each MUX. */ /* This method is called for each MUX. */
virtual void lpm_mux(class Design*des, class NetMux*); virtual void lpm_mux(class Design*des, class NetMux*);
virtual void lpm_part_select(class Design*des, class NetPartSelect*);
/* This method is called for each power. */ /* This method is called for each power. */
virtual void lpm_pow(class Design*des, class NetPow*); virtual void lpm_pow(class Design*des, class NetPow*);

View File

@ -1031,11 +1031,6 @@ unsigned NetPartSelect::base() const
return off_; return off_;
} }
NetPartSelect::dir_t NetPartSelect::dir() const
{
return dir_;
}
NetProc::NetProc() NetProc::NetProc()
: next_(0) : next_(0)
{ {

View File

@ -2002,12 +2002,13 @@ class NetPartSelect : public NetNode {
unsigned base() const; unsigned base() const;
unsigned width() const; unsigned width() const;
dir_t dir() const; inline dir_t dir() const { return dir_; }
/* Is the select signal signed? */ /* Is the select signal signed? */
bool signed_flag() const { return signed_flag_; } inline bool signed_flag() const { return signed_flag_; }
virtual void dump_node(ostream&, unsigned ind) const; virtual void dump_node(ostream&, unsigned ind) const;
bool emit_node(struct target_t*tgt) const; bool emit_node(struct target_t*tgt) const;
virtual void functor_node(Design*des, functor_t*fun);
private: private:
unsigned off_; unsigned off_;

View File

@ -660,6 +660,20 @@ NetNet* make_const_x(Design*des, NetScope*scope, unsigned long wid)
return sig; return sig;
} }
NetNet* make_const_z(Design*des, NetScope*scope, unsigned long wid)
{
verinum xxx (verinum::Vz, wid);
NetConst*res = new NetConst(scope, scope->local_symbol(), xxx);
des->add_node(res);
netvector_t*sig_vec = new netvector_t(IVL_VT_LOGIC, wid-1, 0);
NetNet*sig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, sig_vec);
sig->local_flag(true);
connect(sig->pin(0), res->pin(0));
return sig;
}
NetExpr* condition_reduce(NetExpr*expr) NetExpr* condition_reduce(NetExpr*expr)
{ {
if (expr->expr_type() == IVL_VT_REAL) { if (expr->expr_type() == IVL_VT_REAL) {

View File

@ -202,6 +202,7 @@ extern NetEConst*make_const_val(unsigned long val);
* Make A const net * Make A const net
*/ */
extern NetNet* make_const_x(Design*des, NetScope*scope, unsigned long wid); extern NetNet* make_const_x(Design*des, NetScope*scope, unsigned long wid);
extern NetNet* make_const_z(Design*des, NetScope*scope, unsigned long wid);
/* /*
* In some cases the lval is accessible as a pointer to the head of * In some cases the lval is accessible as a pointer to the head of