From 0be577cc44745da229c11b721543a81759b793ba Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 26 Jan 2014 19:22:39 -0800 Subject: [PATCH 1/9] Allow some behavioral assignments to unresolved wires. If the l-value is an unresolved wire, then elaboration can allow the assignment as long as it is to bits that are not otherwise driven. Handle this in some simple cases. --- elab_lval.cc | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++-- elab_net.cc | 2 +- netlist.cc | 2 +- netlist.h | 13 +++++--- 4 files changed, 94 insertions(+), 8 deletions(-) diff --git a/elab_lval.cc b/elab_lval.cc index 70802ac9f..e4de5d80b 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -289,7 +289,9 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, /* Get the signal referenced by the identifier, and make sure it is a register. Wires are not allowed in this context, unless this is the l-value of a force. */ - if ((reg->type() != NetNet::REG) && !is_force) { + if ((reg->type() != NetNet::REG) + && (reg->type() != NetNet::UNRESOLVED_WIRE) + && !is_force) { cerr << get_fileline() << ": error: " << path_ << " is not a valid l-value in " << scope_path(use_scope) << "." << endl; @@ -314,7 +316,7 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, // where the name is a member/method of a struct/class. ivl_assert(*this, method_name.nil()); - bool need_const_idx = is_cassign || is_force; + bool need_const_idx = is_cassign || is_force || (reg->type()==NetNet::UNRESOLVED_WIRE); if (reg->unpacked_dimensions() > 0) return elaborate_lval_net_word_(des, scope, reg, need_const_idx); @@ -358,6 +360,14 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, ivl_assert(*this, use_sel == index_component_t::SEL_NONE); + if (reg->type()==NetNet::UNRESOLVED_WIRE) { + cerr << get_fileline() << ": error: " + << path_ << " Unable assign to unresolved wires." + << endl; + des->errors += 1; + return 0; + } + /* No select expressions. */ NetAssign_*lv = new NetAssign_(reg); @@ -507,6 +517,13 @@ NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des, canon_index = new NetEConst(verinum(verinum::Vx)); canon_index->set_line(*this); + if (reg->type()==NetNet::UNRESOLVED_WIRE) { + cerr << get_fileline() << ": error: " + << "Unable to assign words of unresolved wire array." << endl; + des->errors += 1; + return 0; + } + NetAssign_*lv = new NetAssign_(reg); lv->set_word(canon_index); @@ -599,6 +616,19 @@ bool PEIdent::elaborate_lval_net_bit_(Design*des, } } + if (debug_elaborate && (reg->type()==NetNet::UNRESOLVED_WIRE)) { + cerr << get_fileline() << ": PEIdent::elaborate_lval_net_bit_: " + << "Try to assign bits of unresolved wire." + << endl; + } + + // Notice that we might be assigning to an unresolved wire. This + // can happen if we are actually assigning to a variable that + // has a partial continuous assignment to it. If that is the + // case, then the bit select must be constant. + ivl_assert(*this, need_const_idx || (reg->type()!=NetNet::UNRESOLVED_WIRE)); + + if (prefix_indices.size()+2 <= reg->packed_dims().size()) { // Special case: this is a slice of a multi-dimensional // packed array. For example: @@ -613,8 +643,19 @@ bool PEIdent::elaborate_lval_net_bit_(Design*des, bool rcl = reg->sb_to_slice(prefix_indices, lsb, loff, lwid); ivl_assert(*this, rcl); + if (reg->type()==NetNet::UNRESOLVED_WIRE) { + bool rct = reg->test_and_set_part_driver(loff+lwid-1, loff); + if (rct) { + cerr << get_fileline() << ": error: " + << "These bits are already driven." << endl; + des->errors += 1; + } + } + lv->set_part(new NetEConst(verinum(loff)), lwid); + } else { + ivl_assert(*this, reg->type()!=NetNet::UNRESOLVED_WIRE); unsigned long lwid; mux = normalize_variable_slice_base(prefix_indices, mux, reg, lwid); @@ -622,6 +663,7 @@ bool PEIdent::elaborate_lval_net_bit_(Design*des, } } else if (reg->data_type() == IVL_VT_STRING) { + ivl_assert(*this, reg->type()!=NetNet::UNRESOLVED_WIRE); // Special case: This is a select of a string // variable. The target of the assignment is a character // select of a string. Force the r-value to be an 8bit @@ -638,6 +680,8 @@ bool PEIdent::elaborate_lval_net_bit_(Design*des, lv->set_part(new NetEConst(verinum(lsb)), 8); } else if (mux) { + ivl_assert(*this, reg->type()!=NetNet::UNRESOLVED_WIRE); + // Non-constant bit mux. Correct the mux for the range // of the vector, then set the l-value part select // expression. @@ -655,6 +699,10 @@ bool PEIdent::elaborate_lval_net_bit_(Design*des, // Constant bit mux that happens to select the only bit // of the l-value. Don't bother with any select at all. + // NOTE: Don't know what to do about unresolved wires + // here, but they are probably wrong. + ivl_assert(*this, reg->type()!=NetNet::UNRESOLVED_WIRE); + } else { // Constant bit select that does something useful. long loff = reg->sb_to_idx(prefix_indices,lsb); @@ -667,6 +715,15 @@ bool PEIdent::elaborate_lval_net_bit_(Design*des, return 0; } + if (reg->type()==NetNet::UNRESOLVED_WIRE) { + bool rct = reg->test_and_set_part_driver(loff, loff); + if (rct) { + cerr << get_fileline() << ": error: " + << "Bit " << loff << " is already driven." << endl; + des->errors += 1; + } + } + lv->set_part(new NetEConst(verinum(loff)), 1); } @@ -681,6 +738,14 @@ bool PEIdent::elaborate_lval_darray_bit_(Design*des, NetScope*scope, NetAssign_* // For now, only support single-dimension dynamic arrays. ivl_assert(*this, name_tail.index.size() == 1); + if (lv->sig()->type()==NetNet::UNRESOLVED_WIRE) { + cerr << get_fileline() << ": error: " + << path_ << " Unable to darray word select unresolved wires." + << endl; + des->errors += 1; + return false; + } + const index_component_t&index_tail = name_tail.index.back(); ivl_assert(*this, index_tail.msb != 0); ivl_assert(*this, index_tail.lsb == 0); @@ -723,6 +788,14 @@ bool PEIdent::elaborate_lval_net_part_(Design*des, return true; } + if (reg->type()==NetNet::UNRESOLVED_WIRE) { + cerr << get_fileline() << ": error: " + << path_ << " Unable to part select unresolved wires." + << endl; + des->errors += 1; + return false; + } + const vector&packed = reg->packed_dims(); long loff, moff; @@ -1103,6 +1176,14 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope, packed_base = 0; } + if (reg->type()==NetNet::UNRESOLVED_WIRE) { + cerr << get_fileline() << ": error: " + << path_ << " Unable to member-select unresolved wires." + << endl; + des->errors += 1; + return false; + } + if (packed_base == 0) { lv->set_part(new NetEConst(verinum(off)), use_width); return true; diff --git a/elab_net.cc b/elab_net.cc index c81f0646f..1cad8de9c 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -710,7 +710,7 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, unsigned subnet_wid = midx-lidx+1; /* Check if the l-value bits are double-driven. */ - if (sig->type() == NetNet::UNRESOLVED_WIRE && sig->test_part_lref(midx,lidx)) { + if (sig->type() == NetNet::UNRESOLVED_WIRE && sig->test_and_set_part_driver(midx,lidx)) { cerr << get_fileline() << ": error: Unresolved net/uwire " << sig->name() << " cannot have multiple drivers." << endl; des->errors += 1; diff --git a/netlist.cc b/netlist.cc index 6e0a3db5a..e66c9c7c9 100644 --- a/netlist.cc +++ b/netlist.cc @@ -868,7 +868,7 @@ unsigned NetNet::peek_eref() const * Test each of the bits in the range, and set them. If any bits are * already set then return true. */ -bool NetNet::test_part_lref(unsigned pmsb, unsigned plsb) +bool NetNet::test_and_set_part_driver(unsigned pmsb, unsigned plsb) { if (lref_mask_.empty()) lref_mask_.resize(vector_width()); diff --git a/netlist.h b/netlist.h index 513409ec5..577036428 100644 --- a/netlist.h +++ b/netlist.h @@ -726,17 +726,22 @@ class NetNet : public NetObj, public PortType { bool local_flag() const { return local_flag_; } void local_flag(bool f) { local_flag_ = f; } - /* NetESignal objects may reference this object. Keep a - reference count so that I keep track of them. */ + // NetESignal objects may reference this object. Keep a + // reference count so that I keep track of them. void incr_eref(); void decr_eref(); unsigned peek_eref() const; - /* Assignment statements count their lrefs here. */ + // Assignment statements count their lrefs here. And by + // asignment statements, we mean BEHAVIORAL assignments. void incr_lref(); void decr_lref(); unsigned peek_lref() const { return lref_count_; } - bool test_part_lref(unsigned msb, unsigned lsb); + + // Treating this node as a uwire, this function tests whether + // any bits in the canonical part are already driven. This is + // only useful for UNRESOLVED_WIRE objects. + bool test_and_set_part_driver(unsigned msb, unsigned lsb); unsigned get_refs() const; From 7f59c51ca273d1f404d9b617720b256481c8148e Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 2 Feb 2014 16:43:48 -0800 Subject: [PATCH 2/9] Handle proceedural writes to UWIRE objects. The elaborator allows this only if it determines that the bits that are procedural written are not also continuously written. --- tgt-vvp/stmt_assign.c | 12 ++++++++++-- vvp/vvp_net_sig.h | 10 ++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/tgt-vvp/stmt_assign.c b/tgt-vvp/stmt_assign.c index 4201778aa..2274e2ada 100644 --- a/tgt-vvp/stmt_assign.c +++ b/tgt-vvp/stmt_assign.c @@ -453,8 +453,16 @@ static void set_vec_to_lval_slice(ivl_lval_t lval, unsigned bit, unsigned wid) assert(ivl_lval_width(lval) == wid); /* If the word index is a constant, then we can write - directly to the word and save the index calculation. */ - if (word_ix == 0) { + directly to the word and save the index + calculation. Also, note the special case that we are + writing to a UWIRE. In that case, use the %force/x0 + instruction to get the desired effect. */ + if (word_ix == 0 && ivl_signal_type(sig)==IVL_SIT_UWIRE) { + fprintf(vvp_out, " %%ix/load 0, %lu, 0;\n", part_off); + fprintf(vvp_out, " %%force/x0 v%p_%lu, %u, %u;\n", + sig, use_word, bit, wid); + + } else if (word_ix == 0) { fprintf(vvp_out, " %%ix/load 0, %lu, 0;\n", part_off); fprintf(vvp_out, " %%set/x0 v%p_%lu, %u, %u;\n", sig, use_word, bit, wid); diff --git a/vvp/vvp_net_sig.h b/vvp/vvp_net_sig.h index 4c59f47ad..8a1042239 100644 --- a/vvp/vvp_net_sig.h +++ b/vvp/vvp_net_sig.h @@ -394,6 +394,16 @@ class vvp_fun_signal_object_aa : public vvp_fun_signal_object, public automatic_ * The vvp_wire is different from vvp_variable objects in that it * exists only as a filter. The vvp_wire class tree is for * implementing verilog wires/nets (as opposed to regs/variables). + * + * vvp_vpi_callback + * | + * | + * vvp_net_fil_t vvp_signal_value + * | | + * \ / + * \ / + * \ / + * vvp_wire_base */ class vvp_wire_base : public vvp_net_fil_t, public vvp_signal_value { From 4f1c43b690fbb41245079996da6d0ca0a4f1842f Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 2 Feb 2014 17:05:42 -0800 Subject: [PATCH 3/9] Account for force l-values that are uwires. --- elab_lval.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/elab_lval.cc b/elab_lval.cc index e4de5d80b..d30c51bae 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -360,9 +360,9 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, ivl_assert(*this, use_sel == index_component_t::SEL_NONE); - if (reg->type()==NetNet::UNRESOLVED_WIRE) { + if (reg->type()==NetNet::UNRESOLVED_WIRE && !is_force) { cerr << get_fileline() << ": error: " - << path_ << " Unable assign to unresolved wires." + << path_ << " Unable to assign to unresolved wires." << endl; des->errors += 1; return 0; From 52a9fdde8a4bafec74e5fd4eb1c57ddd72a0a4d2 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Mon, 3 Feb 2014 19:22:59 -0800 Subject: [PATCH 4/9] Handle packed structs as module outputs. --- elab_lval.cc | 13 ++++++++----- parse.y | 7 +++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/elab_lval.cc b/elab_lval.cc index d30c51bae..f5ab1320e 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -789,11 +789,14 @@ bool PEIdent::elaborate_lval_net_part_(Design*des, } if (reg->type()==NetNet::UNRESOLVED_WIRE) { - cerr << get_fileline() << ": error: " - << path_ << " Unable to part select unresolved wires." - << endl; - des->errors += 1; - return false; + bool rct = reg->test_and_set_part_driver(msb, lsb); + if (rct) { + cerr << get_fileline() << ": error: " + << path_ << "Part select is double-driving unresolved wire." + << endl; + des->errors += 1; + return false; + } } const vector&packed = reg->packed_dims(); diff --git a/parse.y b/parse.y index aeb6c6007..8e34db037 100644 --- a/parse.y +++ b/parse.y @@ -3853,8 +3853,15 @@ port_declaration use_type = NetNet::IMPLICIT; else use_type = NetNet::IMPLICIT_REG; + + // The SystemVerilog types that can show up as + // output ports are implicitly (on the inside) + // variables because "reg" is not valid syntax + // here. } else if (dynamic_cast ($4)) { use_type = NetNet::IMPLICIT_REG; + } else if (dynamic_cast ($4)) { + use_type = NetNet::IMPLICIT_REG; } } ptmp = pform_module_port_reference(name, @2.text, @2.first_line); From a012406ca48c9b496093a844e702048d1cb65b7c Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Mon, 3 Feb 2014 20:04:24 -0800 Subject: [PATCH 5/9] Fix broken search where member names accidentally match variables. --- symbol_search.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/symbol_search.cc b/symbol_search.cc index c0d2f9494..d27078eb7 100644 --- a/symbol_search.cc +++ b/symbol_search.cc @@ -58,9 +58,9 @@ struct symbol_search_results { NetEvent*eve; }; -bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, - pform_name_t path, struct symbol_search_results*res, - NetScope*start_scope = 0) +static bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, + pform_name_t path, struct symbol_search_results*res, + NetScope*start_scope = 0) { assert(scope); bool prefix_scope = false; @@ -100,6 +100,9 @@ bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, "`" << path_tail.name << "' in path `" << path << "'" << endl; des->errors += 1; } + } else { + // Prefix is present, but is NOT a scope. Fail! + return false; } } From 959ac3229e73f97a1d0fffd0d00670156ccba5b6 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sat, 8 Feb 2014 10:16:11 -0800 Subject: [PATCH 6/9] Start a sizer backend. --- Makefile.in | 2 +- configure.in | 2 +- tgt-sizer/Makefile.in | 103 ++++++++++++++++++++++++++++++++ tgt-sizer/scan_logs.cc | 33 +++++++++++ tgt-sizer/scan_lpms.cc | 43 ++++++++++++++ tgt-sizer/sizer-s.conf | 6 ++ tgt-sizer/sizer.cc | 131 +++++++++++++++++++++++++++++++++++++++++ tgt-sizer/sizer.conf | 6 ++ tgt-sizer/sizer_priv.h | 51 ++++++++++++++++ verilog.spec | 3 + 10 files changed, 378 insertions(+), 2 deletions(-) create mode 100644 tgt-sizer/Makefile.in create mode 100644 tgt-sizer/scan_logs.cc create mode 100644 tgt-sizer/scan_lpms.cc create mode 100644 tgt-sizer/sizer-s.conf create mode 100644 tgt-sizer/sizer.cc create mode 100644 tgt-sizer/sizer.conf create mode 100644 tgt-sizer/sizer_priv.h diff --git a/Makefile.in b/Makefile.in index 04747cf0c..c31e2d735 100644 --- a/Makefile.in +++ b/Makefile.in @@ -38,7 +38,7 @@ srcdir = @srcdir@ datarootdir = @datarootdir@ SUBDIRS = ivlpp vhdlpp vvp vpi libveriuser cadpli tgt-null tgt-stub tgt-vvp \ - tgt-vhdl tgt-vlog95 tgt-pcb tgt-blif driver + tgt-vhdl tgt-vlog95 tgt-pcb tgt-blif tgt-sizer driver # Only run distclean for these directories. NOTUSED = tgt-fpga tgt-pal tgt-verilog diff --git a/configure.in b/configure.in index e7aa13457..c5a31bf33 100644 --- a/configure.in +++ b/configure.in @@ -325,4 +325,4 @@ AC_MSG_ERROR(cannot configure white space in libdir: $libdir) fi AC_MSG_RESULT(ok) -AC_OUTPUT(Makefile ivlpp/Makefile vhdlpp/Makefile vvp/Makefile vpi/Makefile driver/Makefile driver-vpi/Makefile cadpli/Makefile libveriuser/Makefile tgt-null/Makefile tgt-stub/Makefile tgt-vvp/Makefile tgt-vhdl/Makefile tgt-fpga/Makefile tgt-verilog/Makefile tgt-pal/Makefile tgt-vlog95/Makefile tgt-pcb/Makefile tgt-blif/Makefile) +AC_OUTPUT(Makefile ivlpp/Makefile vhdlpp/Makefile vvp/Makefile vpi/Makefile driver/Makefile driver-vpi/Makefile cadpli/Makefile libveriuser/Makefile tgt-null/Makefile tgt-stub/Makefile tgt-vvp/Makefile tgt-vhdl/Makefile tgt-fpga/Makefile tgt-verilog/Makefile tgt-pal/Makefile tgt-vlog95/Makefile tgt-pcb/Makefile tgt-blif/Makefile tgt-sizer/Makefile) diff --git a/tgt-sizer/Makefile.in b/tgt-sizer/Makefile.in new file mode 100644 index 000000000..ad771a714 --- /dev/null +++ b/tgt-sizer/Makefile.in @@ -0,0 +1,103 @@ +# +# This source code is free software; you can redistribute it +# and/or modify it in source code form under the terms of the GNU +# Library General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this program; if not, write to the Free +# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# +SHELL = /bin/sh + +suffix = @install_suffix@ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +srcdir = @srcdir@ + +VPATH = $(srcdir) + +bindir = @bindir@ +libdir = @libdir@ + +CXX = @CXX@ +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +ifeq (@srcdir@,.) +INCLUDE_PATH = -I. -I.. +else +INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. +endif + +CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@ +CXXFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CXX@ @CXXFLAGS@ +LDFLAGS = @LDFLAGS@ + +O = sizer.o scan_lpms.o scan_logs.o + +all: dep sizer.tgt + +check: all + +clean: + rm -rf *.o dep sizer.tgt + +distclean: clean + rm -f Makefile config.log + +cppcheck: $(O:.o=.cc) + cppcheck --enable=all -f $(INCLUDE_PATH) $^ + +Makefile: $(srcdir)/Makefile.in ../config.status + cd ..; ./config.status --file=tgt-sizer/$@ + +dep: + mkdir dep + +%.o: %.cc + $(CXX) $(CPPFLAGS) $(CXXFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o + mv $*.d dep + +ifeq (@WIN32@,yes) + TGTLDFLAGS=-L.. -livl + TGTDEPLIBS=../libivl.a +else + TGTLDFLAGS= + TGTDEPLIBS= +endif + +sizer.tgt: $O $(TGTDEPLIBS) + $(CXX) @shared@ $(LDFLAGS) -o $@ $O $(TGTLDFLAGS) + +install: all installdirs $(libdir)/ivl$(suffix)/sizer.tgt $(INSTALL_DOC) $(libdir)/ivl$(suffix)/sizer.conf $(libdir)/ivl$(suffix)/sizer-s.conf + +$(libdir)/ivl$(suffix)/sizer.tgt: ./sizer.tgt + $(INSTALL_PROGRAM) ./sizer.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/sizer.tgt" + +$(libdir)/ivl$(suffix)/sizer.conf: $(srcdir)/sizer.conf + $(INSTALL_DATA) $(srcdir)/sizer.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/sizer.conf" + +$(libdir)/ivl$(suffix)/sizer-s.conf: $(srcdir)/sizer-s.conf + $(INSTALL_DATA) $(srcdir)/sizer-s.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/sizer-s.conf" + + +installdirs: $(srcdir)/../mkinstalldirs + $(srcdir)/../mkinstalldirs "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)/ivl$(suffix)" + +uninstall: + rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/sizer.tgt" + rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/sizer.conf" + rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/sizer-s.conf" + + +-include $(patsubst %.o, dep/%.d, $O) diff --git a/tgt-sizer/scan_logs.cc b/tgt-sizer/scan_logs.cc new file mode 100644 index 000000000..a58dd0950 --- /dev/null +++ b/tgt-sizer/scan_logs.cc @@ -0,0 +1,33 @@ +/* + * Copyright (c) 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 + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +# include "sizer_priv.h" + +void scan_logs(ivl_scope_t scope, struct sizer_statistics&stats) +{ + for (unsigned idx = 0 ; idx < ivl_scope_logs(scope) ; idx += 1) { + ivl_net_logic_t log = ivl_scope_log(scope, idx); + switch (ivl_logic_type(log)) { + default: + stats.log_unknown += 1; + break; + } + } +} diff --git a/tgt-sizer/scan_lpms.cc b/tgt-sizer/scan_lpms.cc new file mode 100644 index 000000000..cfd3f45fc --- /dev/null +++ b/tgt-sizer/scan_lpms.cc @@ -0,0 +1,43 @@ +/* + * Copyright (c) 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 + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +# include "sizer_priv.h" + +static void scan_lpms_ff(ivl_scope_t scope, ivl_lpm_t lpm, struct sizer_statistics&stats) +{ + ivl_nexus_t out = ivl_lpm_q(lpm); + unsigned wid = get_nexus_width(out); + + stats.flop_count += wid; +} + +void scan_lpms(ivl_scope_t scope, struct sizer_statistics&stats) +{ + for (unsigned idx = 0 ; idx < ivl_scope_lpms(scope) ; idx += 1) { + ivl_lpm_t lpm = ivl_scope_lpm(scope,idx); + switch (ivl_lpm_type(lpm)) { + case IVL_LPM_FF: + scan_lpms_ff(scope, lpm, stats); + break; + default: + stats.lpm_unknown += 1; + break; + } + } +} diff --git a/tgt-sizer/sizer-s.conf b/tgt-sizer/sizer-s.conf new file mode 100644 index 000000000..8a54159b7 --- /dev/null +++ b/tgt-sizer/sizer-s.conf @@ -0,0 +1,6 @@ +functor:synth2 +functor:synth +functor:syn-rules +functor:cprop +functor:nodangle +flag:DLL=sizer.tgt diff --git a/tgt-sizer/sizer.cc b/tgt-sizer/sizer.cc new file mode 100644 index 000000000..d4cd85ec8 --- /dev/null +++ b/tgt-sizer/sizer.cc @@ -0,0 +1,131 @@ +/* + * Copyright (c) 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 + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +# include "version_base.h" +# include "version_tag.h" +# include "config.h" +# include "sizer_priv.h" +# include +# include +# include + +/* + * This is a null target module. It does nothing. + */ + + +static const char*version_string = +"Icarus Verilog SIZER Statistics Generator " VERSION " (" VERSION_TAG ")\n\n" +"Copyright (c) 2014 Stephen Williams (steve@icarus.com)\n\n" +" This program is free software; you can redistribute it and/or modify\n" +" it under the terms of the GNU General Public License as published by\n" +" the Free Software Foundation; either version 2 of the License, or\n" +" (at your option) any later version.\n" +"\n" +" This program is distributed in the hope that it will be useful,\n" +" but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +" GNU General Public License for more details.\n" +"\n" +" You should have received a copy of the GNU General Public License along\n" +" with this program; if not, write to the Free Software Foundation, Inc.,\n" +" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" +; + +int sizer_errors = 0; + +FILE*sizer_out = 0; + +static int process_scan_fun(ivl_process_t net, void*raw); +static void emit_sizer_root(ivl_design_t des, ivl_scope_t model); + +int target_design(ivl_design_t des) +{ + const char*sizer_path = ivl_design_flag(des, "-o"); + + sizer_out = fopen(sizer_path, "wt"); + assert(sizer_out); + + // Detect processes and dispatch them. + ivl_design_process(des, &process_scan_fun, 0); + + // Locate the root scope for the design. + ivl_scope_t*roots; + unsigned nroots; + ivl_design_roots(des, &roots, &nroots); + + for (unsigned idx = 0 ; idx < nroots ; idx += 1) { + if (ivl_scope_type(roots[idx]) != IVL_SCT_MODULE) { + fprintf(stderr, "SIZER: The root scope %s must be a module.\n", ivl_scope_basename(roots[idx])); + sizer_errors += 1; + continue; + } + + emit_sizer_root(des, roots[idx]); + } + + return sizer_errors; +} + + +const char* target_query(const char*key) +{ + if (strcmp(key,"version") == 0) + return version_string; + + return 0; +} + +static int process_scan_fun(ivl_process_t net, void* /*raw*/) +{ + fprintf(stderr, "%s:%u: SIZER: Processes not synthesized for statistics.\n", + ivl_process_file(net), ivl_process_lineno(net)); + sizer_errors += 1; + return 0; +} + +static void emit_sizer_root(ivl_design_t des, ivl_scope_t model) +{ + fprintf(sizer_out, "**** Root module: %s\n", ivl_scope_name(model)); + fprintf(sizer_out, " Logic gates: %u (ivl_net_logic_t nodes))\n", ivl_scope_logs(model)); + fprintf(sizer_out, " LPM nodes : %u (ivl_lpm_t nodes)\n", ivl_scope_lpms(model)); + + struct sizer_statistics stats; + scan_logs(model, stats); + scan_lpms(model, stats); + + fprintf(sizer_out, " Flip-Flops : %u\n", stats.flop_count); + fprintf(sizer_out, " LPM Unknown : %u\n", stats.lpm_unknown); + fprintf(sizer_out, " Logic Unknown: %u\n", stats.log_unknown); +} + +unsigned get_nexus_width(ivl_nexus_t nex) +{ + ivl_signal_t sig = 0; + + for (unsigned idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { + ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex,idx); + sig = ivl_nexus_ptr_sig(ptr); + if (sig) return ivl_signal_width(sig); + } + + fprintf(stderr, "SIZER: Unable to find width of nexus?!\n"); + sizer_errors += 1; + return 0; +} diff --git a/tgt-sizer/sizer.conf b/tgt-sizer/sizer.conf new file mode 100644 index 000000000..8a54159b7 --- /dev/null +++ b/tgt-sizer/sizer.conf @@ -0,0 +1,6 @@ +functor:synth2 +functor:synth +functor:syn-rules +functor:cprop +functor:nodangle +flag:DLL=sizer.tgt diff --git a/tgt-sizer/sizer_priv.h b/tgt-sizer/sizer_priv.h new file mode 100644 index 000000000..39fcdcb72 --- /dev/null +++ b/tgt-sizer/sizer_priv.h @@ -0,0 +1,51 @@ +#ifndef __sizer_priv_H +#define __sizer_priv_H +/* + * Copyright (c) 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 + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +# include "config.h" +# include "ivl_target.h" + +# include + +struct sizer_statistics { + unsigned flop_count; + + unsigned lpm_unknown; + unsigned log_unknown; + + inline sizer_statistics() + { + flop_count = 0; + + lpm_unknown = 0; + log_unknown = 0; + } +}; + +extern int sizer_errors; +extern FILE*sizer_out; + +extern void scan_logs(ivl_scope_t scope, struct sizer_statistics&stats); +extern void scan_lpms(ivl_scope_t scope, struct sizer_statistics&stats); + + +extern unsigned get_nexus_width(ivl_nexus_t nex); + +#endif diff --git a/verilog.spec b/verilog.spec index 80fa8763f..21ef8d295 100644 --- a/verilog.spec +++ b/verilog.spec @@ -74,6 +74,9 @@ rm -rf $RPM_BUILD_ROOT %attr(-,root,root) %{_libdir}/ivl%{suff}/null.tgt %attr(-,root,root) %{_libdir}/ivl%{suff}/null.conf %attr(-,root,root) %{_libdir}/ivl%{suff}/null-s.conf +%attr(-,root,root) %{_libdir}/ivl%{suff}/sizer.tgt +%attr(-,root,root) %{_libdir}/ivl%{suff}/sizer.conf +%attr(-,root,root) %{_libdir}/ivl%{suff}/sizer-s.conf %attr(-,root,root) %{_libdir}/ivl%{suff}/stub.tgt %attr(-,root,root) %{_libdir}/ivl%{suff}/stub.conf %attr(-,root,root) %{_libdir}/ivl%{suff}/stub-s.conf From f5041e6c0953b9d3756d1740f5ece7dcdbef5c92 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sat, 8 Feb 2014 18:53:42 -0800 Subject: [PATCH 7/9] Collect some actual sizer statistics. --- tgt-sizer/scan_logs.cc | 13 +++++++++++++ tgt-sizer/scan_lpms.cc | 18 +++++++++++++++++- tgt-sizer/sizer.cc | 1 + tgt-sizer/sizer_priv.h | 4 ++++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/tgt-sizer/scan_logs.cc b/tgt-sizer/scan_logs.cc index a58dd0950..e70a2ffb8 100644 --- a/tgt-sizer/scan_logs.cc +++ b/tgt-sizer/scan_logs.cc @@ -20,11 +20,24 @@ # include "sizer_priv.h" +void scan_logs_gates(ivl_scope_t scope, ivl_net_logic_t log, struct sizer_statistics&stats) +{ + unsigned wid = ivl_logic_width(log); + + stats.gate_count += wid; +} + void scan_logs(ivl_scope_t scope, struct sizer_statistics&stats) { for (unsigned idx = 0 ; idx < ivl_scope_logs(scope) ; idx += 1) { ivl_net_logic_t log = ivl_scope_log(scope, idx); switch (ivl_logic_type(log)) { + case IVL_LO_AND: + case IVL_LO_OR: + case IVL_LO_XOR: + case IVL_LO_XNOR: + scan_logs_gates(scope, log, stats); + break; default: stats.log_unknown += 1; break; diff --git a/tgt-sizer/scan_lpms.cc b/tgt-sizer/scan_lpms.cc index cfd3f45fc..ddd382504 100644 --- a/tgt-sizer/scan_lpms.cc +++ b/tgt-sizer/scan_lpms.cc @@ -19,7 +19,7 @@ # include "sizer_priv.h" -static void scan_lpms_ff(ivl_scope_t scope, ivl_lpm_t lpm, struct sizer_statistics&stats) +static void scan_lpms_ff(ivl_scope_t, ivl_lpm_t lpm, struct sizer_statistics&stats) { ivl_nexus_t out = ivl_lpm_q(lpm); unsigned wid = get_nexus_width(out); @@ -32,9 +32,25 @@ void scan_lpms(ivl_scope_t scope, struct sizer_statistics&stats) for (unsigned idx = 0 ; idx < ivl_scope_lpms(scope) ; idx += 1) { ivl_lpm_t lpm = ivl_scope_lpm(scope,idx); switch (ivl_lpm_type(lpm)) { + // Part select nodes don't actually take up + // hardware. These represent things like bundle + // manipulations, which are done in routing. + case IVL_LPM_PART_VP: + case IVL_LPM_PART_PV: + case IVL_LPM_CONCAT: + case IVL_LPM_CONCATZ: + case IVL_LPM_REPEAT: + break; + + case IVL_LPM_ADD: + stats.adder_count += 1; + break; + + // D-Type flip-flops. case IVL_LPM_FF: scan_lpms_ff(scope, lpm, stats); break; + default: stats.lpm_unknown += 1; break; diff --git a/tgt-sizer/sizer.cc b/tgt-sizer/sizer.cc index d4cd85ec8..2009ea558 100644 --- a/tgt-sizer/sizer.cc +++ b/tgt-sizer/sizer.cc @@ -111,6 +111,7 @@ static void emit_sizer_root(ivl_design_t des, ivl_scope_t model) scan_lpms(model, stats); fprintf(sizer_out, " Flip-Flops : %u\n", stats.flop_count); + fprintf(sizer_out, " Logic Gates : %u\n", stats.gate_count); fprintf(sizer_out, " LPM Unknown : %u\n", stats.lpm_unknown); fprintf(sizer_out, " Logic Unknown: %u\n", stats.log_unknown); } diff --git a/tgt-sizer/sizer_priv.h b/tgt-sizer/sizer_priv.h index 39fcdcb72..f036d91b9 100644 --- a/tgt-sizer/sizer_priv.h +++ b/tgt-sizer/sizer_priv.h @@ -26,6 +26,8 @@ struct sizer_statistics { unsigned flop_count; + unsigned gate_count; + unsigned adder_count; unsigned lpm_unknown; unsigned log_unknown; @@ -33,6 +35,8 @@ struct sizer_statistics { inline sizer_statistics() { flop_count = 0; + gate_count = 0; + adder_count = 0; lpm_unknown = 0; log_unknown = 0; From 9f2b7d65532f3d69878cb37dfe6a503646eae9cd Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 9 Feb 2014 08:59:51 -0800 Subject: [PATCH 8/9] Rearrange counters to me more realistic Instead of counting gate types, convert to gate count estimates and some other interesting statistics. Also handling the descent into child scopes. --- tgt-sizer/scan_logs.cc | 8 ++- tgt-sizer/scan_lpms.cc | 23 ++++++++- tgt-sizer/sizer.cc | 107 +++++++++++++++++++++++++++++++++-------- tgt-sizer/sizer_priv.h | 17 ++++--- 4 files changed, 123 insertions(+), 32 deletions(-) diff --git a/tgt-sizer/scan_logs.cc b/tgt-sizer/scan_logs.cc index e70a2ffb8..82a4df0d9 100644 --- a/tgt-sizer/scan_logs.cc +++ b/tgt-sizer/scan_logs.cc @@ -20,7 +20,7 @@ # include "sizer_priv.h" -void scan_logs_gates(ivl_scope_t scope, ivl_net_logic_t log, struct sizer_statistics&stats) +void scan_logs_gates(ivl_scope_t, ivl_net_logic_t log, struct sizer_statistics&stats) { unsigned wid = ivl_logic_width(log); @@ -35,11 +35,15 @@ void scan_logs(ivl_scope_t scope, struct sizer_statistics&stats) case IVL_LO_AND: case IVL_LO_OR: case IVL_LO_XOR: + case IVL_LO_NAND: + case IVL_LO_NOR: case IVL_LO_XNOR: + case IVL_LO_BUF: + case IVL_LO_NOT: scan_logs_gates(scope, log, stats); break; default: - stats.log_unknown += 1; + stats.log_bytype[ivl_logic_type(log)] += 1; break; } } diff --git a/tgt-sizer/scan_lpms.cc b/tgt-sizer/scan_lpms.cc index ddd382504..4d23a8f40 100644 --- a/tgt-sizer/scan_lpms.cc +++ b/tgt-sizer/scan_lpms.cc @@ -19,6 +19,12 @@ # include "sizer_priv.h" +using namespace std; + +/* + * Count each bit of flip-flops. It is clear and obvious how these + * come out, so no need to make alternate counts as well. + */ static void scan_lpms_ff(ivl_scope_t, ivl_lpm_t lpm, struct sizer_statistics&stats) { ivl_nexus_t out = ivl_lpm_q(lpm); @@ -27,6 +33,19 @@ static void scan_lpms_ff(ivl_scope_t, ivl_lpm_t lpm, struct sizer_statistics&sta stats.flop_count += wid; } +/* + * Count adders as 2m gates. + * Also keep a count of adders by width, just out of curiosity. + */ +static void scans_lpms_add(ivl_scope_t, ivl_lpm_t lpm, struct sizer_statistics&stats) +{ + unsigned wid = ivl_lpm_width(lpm); + + stats.adder_count[wid] += 1; + + stats.gate_count += 2*wid; +} + void scan_lpms(ivl_scope_t scope, struct sizer_statistics&stats) { for (unsigned idx = 0 ; idx < ivl_scope_lpms(scope) ; idx += 1) { @@ -43,7 +62,7 @@ void scan_lpms(ivl_scope_t scope, struct sizer_statistics&stats) break; case IVL_LPM_ADD: - stats.adder_count += 1; + scans_lpms_add(scope, lpm, stats); break; // D-Type flip-flops. @@ -52,7 +71,7 @@ void scan_lpms(ivl_scope_t scope, struct sizer_statistics&stats) break; default: - stats.lpm_unknown += 1; + stats.lpm_bytype[ivl_lpm_type(lpm)] += 1; break; } } diff --git a/tgt-sizer/sizer.cc b/tgt-sizer/sizer.cc index 2009ea558..5eb17f343 100644 --- a/tgt-sizer/sizer.cc +++ b/tgt-sizer/sizer.cc @@ -25,6 +25,8 @@ # include # include +using namespace std; + /* * This is a null target module. It does nothing. */ @@ -53,8 +55,24 @@ int sizer_errors = 0; FILE*sizer_out = 0; static int process_scan_fun(ivl_process_t net, void*raw); -static void emit_sizer_root(ivl_design_t des, ivl_scope_t model); +static void emit_sizer_scope(ivl_design_t des, ivl_scope_t model, struct sizer_statistics&stats); +static void show_stats(struct sizer_statistics&stats); +/* + * This is called by the ivl core to get version information from the + * loadable code generator. + */ +const char* target_query(const char*key) +{ + if (strcmp(key,"version") == 0) + return version_string; + + return 0; +} + +/* + * This is the main entry point from the IVL core. + */ int target_design(ivl_design_t des) { const char*sizer_path = ivl_design_flag(des, "-o"); @@ -65,11 +83,14 @@ int target_design(ivl_design_t des) // Detect processes and dispatch them. ivl_design_process(des, &process_scan_fun, 0); - // Locate the root scope for the design. + // Locate the root scopes for the design. ivl_scope_t*roots; unsigned nroots; ivl_design_roots(des, &roots, &nroots); + // Process all the root scopes. It is possible that there are + // multiple root scopes, we will give isolated numbers for + // each and keep then separate. for (unsigned idx = 0 ; idx < nroots ; idx += 1) { if (ivl_scope_type(roots[idx]) != IVL_SCT_MODULE) { fprintf(stderr, "SIZER: The root scope %s must be a module.\n", ivl_scope_basename(roots[idx])); @@ -77,21 +98,22 @@ int target_design(ivl_design_t des) continue; } - emit_sizer_root(des, roots[idx]); + struct sizer_statistics stats; + emit_sizer_scope(des, roots[idx], stats); + + fprintf(sizer_out, "**** TOTALS\n"); + show_stats(stats); } return sizer_errors; } -const char* target_query(const char*key) -{ - if (strcmp(key,"version") == 0) - return version_string; - - return 0; -} - +/* + * Processes are not collected into scopes, but we should not have any + * left anyhow. Give error messages for all the processes that we find + * to be remaining. + */ static int process_scan_fun(ivl_process_t net, void* /*raw*/) { fprintf(stderr, "%s:%u: SIZER: Processes not synthesized for statistics.\n", @@ -100,20 +122,43 @@ static int process_scan_fun(ivl_process_t net, void* /*raw*/) return 0; } -static void emit_sizer_root(ivl_design_t des, ivl_scope_t model) +static void emit_sizer_scope(ivl_design_t des, ivl_scope_t scope, struct sizer_statistics&stats) { - fprintf(sizer_out, "**** Root module: %s\n", ivl_scope_name(model)); - fprintf(sizer_out, " Logic gates: %u (ivl_net_logic_t nodes))\n", ivl_scope_logs(model)); - fprintf(sizer_out, " LPM nodes : %u (ivl_lpm_t nodes)\n", ivl_scope_lpms(model)); + fprintf(sizer_out, "**** module/scope: %s\n", ivl_scope_name(scope)); - struct sizer_statistics stats; - scan_logs(model, stats); - scan_lpms(model, stats); + scan_logs(scope, stats); + scan_lpms(scope, stats); + show_stats(stats); + + for (size_t idx = 0 ; idx < ivl_scope_childs(scope) ; idx += 1) { + ivl_scope_t child = ivl_scope_child(scope,idx); + struct sizer_statistics child_stats; + emit_sizer_scope(des, child, child_stats); + stats += child_stats; + } +} + +static void show_stats(struct sizer_statistics&stats) +{ fprintf(sizer_out, " Flip-Flops : %u\n", stats.flop_count); fprintf(sizer_out, " Logic Gates : %u\n", stats.gate_count); - fprintf(sizer_out, " LPM Unknown : %u\n", stats.lpm_unknown); - fprintf(sizer_out, " Logic Unknown: %u\n", stats.log_unknown); + + for (map::const_iterator cur = stats.adder_count.begin() + ; cur != stats.adder_count.end() ; ++ cur) { + fprintf(sizer_out, " ADDER[%u]: %u\n", cur->first, cur->second); + } + + // These are diagnostic outputs for when more detail is needed. + for (map::const_iterator cur = stats.lpm_bytype.begin() + ; cur != stats.lpm_bytype.end() ; ++ cur) { + fprintf(sizer_out, " LPM[%d]: %u unaccounted\n", cur->first, cur->second); + } + + for (map::const_iterator cur = stats.log_bytype.begin() + ; cur != stats.log_bytype.end() ; ++ cur) { + fprintf(sizer_out, " LOG[%d]: %u unaccounted\n", cur->first, cur->second); + } } unsigned get_nexus_width(ivl_nexus_t nex) @@ -130,3 +175,25 @@ unsigned get_nexus_width(ivl_nexus_t nex) sizer_errors += 1; return 0; } + +struct sizer_statistics& sizer_statistics::operator += (const sizer_statistics&that) +{ + flop_count += that.flop_count; + gate_count += that.gate_count; + + for (map::const_iterator cur = that.adder_count.begin() + ; cur != that.adder_count.end() ; ++ cur) + adder_count[cur->first] += cur->second; + + + for (map::const_iterator cur = that.lpm_bytype.begin() + ; cur != that.lpm_bytype.end() ; ++ cur) + lpm_bytype[cur->first] += cur->second; + + + for (map::const_iterator cur = that.log_bytype.begin() + ; cur != that.log_bytype.end() ; ++ cur) + log_bytype[cur->first] += cur->second; + + return *this; +} diff --git a/tgt-sizer/sizer_priv.h b/tgt-sizer/sizer_priv.h index f036d91b9..3732e3174 100644 --- a/tgt-sizer/sizer_priv.h +++ b/tgt-sizer/sizer_priv.h @@ -22,25 +22,26 @@ # include "config.h" # include "ivl_target.h" +# include # include struct sizer_statistics { + // These are the accumulated global statistics unsigned flop_count; unsigned gate_count; - unsigned adder_count; - - unsigned lpm_unknown; - unsigned log_unknown; + // Count adders of various dimension + std::map adder_count; + // Different kinds of nodes that we have not accounted for + std::map lpm_bytype; + std::map log_bytype; inline sizer_statistics() { flop_count = 0; gate_count = 0; - adder_count = 0; - - lpm_unknown = 0; - log_unknown = 0; } + + struct sizer_statistics& operator += (const struct sizer_statistics&that); }; extern int sizer_errors; From be1130ddbf3d1b5ed6ede7208ef37731ea4fce00 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 9 Feb 2014 17:30:30 -0800 Subject: [PATCH 9/9] More robust case statement synthesis Handle the cases that the case statement carries complet sub- statements. This is just a generalization of what is already there. --- synth2.cc | 118 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 73 insertions(+), 45 deletions(-) diff --git a/synth2.cc b/synth2.cc index 0062b1632..e456a0eb9 100644 --- a/synth2.cc +++ b/synth2.cc @@ -189,18 +189,18 @@ bool NetCase::synth_async(Design*des, NetScope*scope, unsigned sel_width = esig->vector_width(); assert(sel_width > 0); - unsigned mux_width = 0; - for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) - mux_width += nex_out.pin(idx).nexus()->vector_width(); + ivl_assert(*this, nex_map.size() == nex_out.pin_count()); - unsigned map_width = 0; - for (unsigned idx = 0 ; idx < nex_map.size() ; idx += 1) - map_width += nex_map[idx].wid; + vector mux_width (nex_out.pin_count()); + for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { + mux_width[idx] = nex_map[idx].wid; + if (debug_synth2) { + cerr << get_fileline() << ": NetCase::synth_async: " + << "idx=" << idx + << ", mux_width[idx]=" << mux_width[idx] << endl; + } + } - /* Calculate the mux width from the map, the mex_map values - are from the top level and are more reliable. */ - if (map_width > mux_width) - mux_width = map_width; /* Collect all the statements into a map of index to statement. The guard expression it evaluated to be the @@ -230,6 +230,7 @@ bool NetCase::synth_async(Design*des, NetScope*scope, max_guard_value = sel_idx; } + // The mux_size is the number of inputs that are selected. unsigned mux_size = max_guard_value + 1; // If the sel_width can select more than just the explicit @@ -240,48 +241,59 @@ bool NetCase::synth_async(Design*des, NetScope*scope, } - NetMux*mux = new NetMux(scope, scope->local_symbol(), - mux_width, mux_size, sel_width); - des->add_node(mux); - - /* The select signal is already synthesized. Simply hook it up. */ - connect(mux->pin_Sel(), esig->pin(0)); - - /* For now, assume that the output is only 1 signal. */ - ivl_assert(*this, nex_out.pin_count() == 1); - connect(mux->pin_Result(), nex_out.pin(0)); - - /* Make sure the output is already connected to a net. */ - if (mux->pin_Result().nexus()->pick_any_net() == 0) { - ivl_variable_type_t mux_data_type = IVL_VT_LOGIC; - netvector_t*tmp_vec = new netvector_t(mux_data_type, mux_width-1, 0); - NetNet*tmp = new NetNet(scope, scope->local_symbol(), - NetNet::TRI, tmp_vec); - tmp->local_flag(true); - ivl_assert(*this, tmp->vector_width() != 0); - connect(mux->pin_Result(), tmp->pin(0)); - } - /* If there is a default clause, synthesize is once and we'll link it in wherever it is needed. */ - NetNet*default_sig = 0; + NetBus default_bus (scope, nex_map.size()); + vectordefault_sig (nex_map.size()); + if (statement_default) { - NetBus tmp (scope, nex_map.size()); - statement_default->synth_async(des, scope, nex_map, tmp); + statement_default->synth_async(des, scope, nex_map, default_bus); // Get the signal from the synthesized statement. This // will be hooked to all the default cases. - ivl_assert(*this, tmp.pin_count()==1); - default_sig = tmp.pin(0).nexus()->pick_any_net(); - ivl_assert(*this, default_sig); + ivl_assert(*this, default_bus.pin_count()==1); + default_sig[0] = default_bus.pin(0).nexus()->pick_any_net(); + ivl_assert(*this, default_sig[0]); + } + + vector mux (mux_width.size()); + for (size_t mdx = 0 ; mdx < mux_width.size() ; mdx += 1) { + mux[mdx] = new NetMux(scope, scope->local_symbol(), + mux_width[mdx], mux_size, sel_width); + des->add_node(mux[mdx]); + + // The select signal is already synthesized, and is + // common for every mux of this case statement. Simply + // hook it up. + connect(mux[mdx]->pin_Sel(), esig->pin(0)); + + // The outputs are in the nex_out, and connected to the + // mux Result pins. + connect(mux[mdx]->pin_Result(), nex_out.pin(mdx)); + + // Make sure the output is now connected to a net. If + // not, then create a fake one to carry the net-ness of + // the pin. + if (mux[mdx]->pin_Result().nexus()->pick_any_net() == 0) { + ivl_variable_type_t mux_data_type = IVL_VT_LOGIC; + netvector_t*tmp_vec = new netvector_t(mux_data_type, mux_width[mdx]-1, 0); + NetNet*tmp = new NetNet(scope, scope->local_symbol(), + NetNet::TRI, tmp_vec); + tmp->local_flag(true); + ivl_assert(*this, tmp->vector_width() != 0); + connect(mux[mdx]->pin_Result(), tmp->pin(0)); + } } for (unsigned idx = 0 ; idx < mux_size ; idx += 1) { NetProc*stmt = statement_map[idx]; - if (stmt==0 && default_sig!=0) { - connect(mux->pin_Data(idx), default_sig->pin(0)); + if (stmt==0 && statement_default) { + ivl_assert(*this, default_sig.size() == mux.size()); + for (size_t mdx = 0 ; mdx < mux.size() ; mdx += 1) + connect(mux[mdx]->pin_Data(idx), default_sig[mdx]->pin(0)); + continue; } if (stmt == 0) { @@ -294,9 +306,24 @@ bool NetCase::synth_async(Design*des, NetScope*scope, NetBus tmp (scope, nex_map.size()); stmt->synth_async(des, scope, nex_map, tmp); - ivl_assert(*this, tmp.pin_count()==1); - connect(mux->pin_Data(idx), tmp.pin(0)); - ivl_assert(*this, mux->pin_Data(idx).nexus()->pick_any_net()); + ivl_assert(*this, tmp.pin_count() == mux.size()); + for (size_t mdx = 0 ; mdx < mux.size() ; mdx += 1) { + connect(mux[mdx]->pin_Data(idx), tmp.pin(mdx)); + + if (mux[mdx]->pin_Data(idx).nexus()->pick_any_net()==0) { + cerr << get_fileline() << ": warning: case " << idx + << " has no input for mux " << mdx << "." << endl; + + ivl_variable_type_t mux_data_type = IVL_VT_LOGIC; + netvector_t*tmp_vec = new netvector_t(mux_data_type, mux_width[mdx]-1, 0); + NetNet*tmpn = new NetNet(scope, scope->local_symbol(), + NetNet::TRI, tmp_vec); + tmpn->local_flag(true); + ivl_assert(*this, tmpn->vector_width() != 0); + connect(mux[mdx]->pin_Data(idx), tmpn->pin(0)); + } + ivl_assert(*this, mux[mdx]->pin_Data(idx).nexus()->pick_any_net()); + } } return true; @@ -842,15 +869,16 @@ bool NetProcTop::synth_sync(Design*des) for (unsigned idx = 0 ; idx < nex_set.size() ; idx += 1) { + ivl_assert(*this, nex_set[idx].nex); if (debug_synth2) { cerr << get_fileline() << ": debug: " << "Top level making a " - << nex_set[idx].nex->vector_width() << "-wide " + << nex_set[idx].wid << "-wide " << "NetFF device." << endl; } NetFF*ff2 = new NetFF(scope(), scope()->local_symbol(), - nex_set[idx].nex->vector_width()); + nex_set[idx].wid); des->add_node(ff2); ff2->set_line(*this);