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 "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
|
||||||
|
|
|
||||||
11
functor.cc
11
functor.cc
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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*);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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_;
|
||||||
|
|
|
||||||
14
netmisc.cc
14
netmisc.cc
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue