diff --git a/elaborate.cc b/elaborate.cc index ece48b0a3..17d37c098 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -3110,6 +3110,8 @@ NetProc* PCase::elaborate(Design*des, NetScope*scope) const } } + res->prune(); + return res; } diff --git a/net_proc.cc b/net_proc.cc index 93c746b6d..2aedec7c2 100644 --- a/net_proc.cc +++ b/net_proc.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2014 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 @@ -19,8 +19,10 @@ # include "config.h" +# include "compiler.h" # include "netlist.h" -# include +# include "netmisc.h" +# include "ivl_assert.h" NetBlock::NetBlock(Type t, NetScope*ss) : type_(t), subscope_(ss), last_(0) @@ -73,7 +75,7 @@ const NetProc* NetBlock::proc_next(const NetProc*cur) const NetCase::NetCase(NetCase::TYPE c, NetExpr*ex, unsigned cnt) : type_(c), expr_(ex), items_(cnt) { - assert(expr_); + ivl_assert(*this, expr_); } NetCase::~NetCase() @@ -92,11 +94,73 @@ NetCase::TYPE NetCase::type() const void NetCase::set_case(unsigned idx, NetExpr*e, NetProc*p) { - assert(idx < items_.size()); + ivl_assert(*this, idx < items_.size()); items_[idx].guard = e; items_[idx].statement = p; } +void NetCase::prune() +{ + // Test whether the case expression has been padded out + NetESelect*padded_expr = dynamic_cast(expr_); + if ((padded_expr == 0) || (padded_expr->select() != 0)) + return; + + // If so, run through the case item expressions to find + // the minimum number of bits needed to unambiguously + // select the correct case item. + const NetExpr*unpadded_expr = padded_expr->sub_expr(); + unsigned padded_width = padded_expr->expr_width(); + unsigned prune_width = unpadded_expr->expr_width(); + for (unsigned idx = 0; idx < items_.size(); idx += 1) { + // If there is no guard expression, this is the default + // case, so skip it. + if (items_[idx].guard == 0) + continue; + + // If the guard expression is not constant, assume + // all bits are needed, so no pruning can be done. + NetEConst*gc = dynamic_cast(items_[idx].guard); + if (gc == 0) + return; + + unsigned sig_bits = gc->value().significant_bits(); + if (sig_bits > prune_width) + prune_width = sig_bits; + + // If all the padding bits are needed, no pruning + // can be done. + if (prune_width >= padded_width) + return; + } + ivl_assert(*this, prune_width < padded_width); + + if (debug_elaborate) { + cerr << get_fileline() << ": debug: pruning case expressions to " + << prune_width << " bits." << endl; + } + + // Prune the case expression + expr_ = pad_to_width(unpadded_expr->dup_expr(), prune_width, *expr_); + delete padded_expr; + + // Prune the case item expressions + for (unsigned idx = 0; idx < items_.size(); idx += 1) { + if (items_[idx].guard == 0) + continue; + + NetEConst*gc = dynamic_cast(items_[idx].guard); + ivl_assert(*this, gc); + + verinum value(gc->value(), prune_width); + NetEConst*tmp = new NetEConst(value); + tmp->set_line(*gc); + delete gc; + + items_[idx].guard = tmp; + } +} + NetDisable::NetDisable(NetScope*tgt) : target_(tgt) { @@ -175,7 +239,7 @@ NetPDelay::~NetPDelay() uint64_t NetPDelay::delay() const { - assert(expr_ == 0); + ivl_assert(*this, expr_ == 0); return delay_; } diff --git a/netlist.h b/netlist.h index d50213f44..e3f4393f9 100644 --- a/netlist.h +++ b/netlist.h @@ -2939,6 +2939,8 @@ class NetCase : public NetProc { void set_case(unsigned idx, NetExpr*ex, NetProc*st); + void prune(); + TYPE type() const; const NetExpr*expr() const { return expr_; } inline unsigned nitems() const { return items_.size(); } diff --git a/verinum.cc b/verinum.cc index 250df8293..d5ad29dab 100644 --- a/verinum.cc +++ b/verinum.cc @@ -587,6 +587,21 @@ bool verinum::is_negative() const return (bits_[nbits_-1] == V1) && has_sign(); } +unsigned verinum::significant_bits() const +{ + unsigned sbits = nbits_; + + if (has_sign_) { + V sign_bit = bits_[sbits-1]; + while ((sbits > 1) && (bits_[sbits-2] == sign_bit)) + sbits -= 1; + } else { + while ((sbits > 1) && (bits_[sbits-1] == verinum::V0)) + sbits -= 1; + } + return sbits; +} + void verinum::cast_to_int2() { for (unsigned idx = 0 ; idx < nbits_ ; idx += 1) { diff --git a/verinum.h b/verinum.h index e7f311920..dfae90958 100644 --- a/verinum.h +++ b/verinum.h @@ -59,7 +59,7 @@ class verinum { ~verinum(); verinum& operator= (const verinum&); - // Number of significant bits in this number. + // Number of stored bits in this number. unsigned len() const { return nbits_; } // A number "has a length" if the length was specified fixed @@ -86,6 +86,9 @@ class verinum { // Comparison for use in sorting algorithms. bool is_before(const verinum&that) const; + // Number of significant bits in this number. + unsigned significant_bits() const; + // Convert 4-state to 2-state void cast_to_int2();