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:
parent
a90e264ed7
commit
367d7bf94b
139
cprop.cc
139
cprop.cc
|
|
@ -19,13 +19,14 @@
|
|||
|
||||
# include "config.h"
|
||||
|
||||
# include <algorithm>
|
||||
# include <vector>
|
||||
# include <cstdlib>
|
||||
# include "netlist.h"
|
||||
# include "netmisc.h"
|
||||
# include "functor.h"
|
||||
# include "compiler.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_logic(Design*des, NetLogic*obj);
|
||||
virtual void lpm_mux(Design*des, NetMux*obj);
|
||||
virtual void lpm_part_select(Design*des, NetPartSelect*obj);
|
||||
};
|
||||
|
||||
void cprop_functor::signal(Design*, NetNet*)
|
||||
|
|
@ -150,6 +152,141 @@ void cprop_functor::lpm_mux(Design*des, NetMux*obj)
|
|||
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
|
||||
* but signals. If that is the case, delete the dangling constant and
|
||||
|
|
|
|||
11
functor.cc
11
functor.cc
|
|
@ -24,6 +24,8 @@
|
|||
# include "functor.h"
|
||||
# include "netlist.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
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*)
|
||||
{
|
||||
}
|
||||
|
|
@ -225,6 +231,11 @@ void NetMux::functor_node(Design*des, functor_t*fun)
|
|||
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)
|
||||
{
|
||||
fun->lpm_pow(des, this);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef __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
|
||||
* 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. */
|
||||
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. */
|
||||
virtual void lpm_pow(class Design*des, class NetPow*);
|
||||
|
||||
|
|
|
|||
|
|
@ -1031,11 +1031,6 @@ unsigned NetPartSelect::base() const
|
|||
return off_;
|
||||
}
|
||||
|
||||
NetPartSelect::dir_t NetPartSelect::dir() const
|
||||
{
|
||||
return dir_;
|
||||
}
|
||||
|
||||
NetProc::NetProc()
|
||||
: next_(0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2002,12 +2002,13 @@ class NetPartSelect : public NetNode {
|
|||
|
||||
unsigned base() const;
|
||||
unsigned width() const;
|
||||
dir_t dir() const;
|
||||
inline dir_t dir() const { return dir_; }
|
||||
/* 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;
|
||||
bool emit_node(struct target_t*tgt) const;
|
||||
virtual void functor_node(Design*des, functor_t*fun);
|
||||
|
||||
private:
|
||||
unsigned off_;
|
||||
|
|
|
|||
14
netmisc.cc
14
netmisc.cc
|
|
@ -660,6 +660,20 @@ NetNet* make_const_x(Design*des, NetScope*scope, unsigned long wid)
|
|||
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)
|
||||
{
|
||||
if (expr->expr_type() == IVL_VT_REAL) {
|
||||
|
|
|
|||
|
|
@ -202,6 +202,7 @@ extern NetEConst*make_const_val(unsigned long val);
|
|||
* Make A const net
|
||||
*/
|
||||
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
|
||||
|
|
|
|||
Loading…
Reference in New Issue