/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include "kernel/yosys.h" #include "libs/sha1/sha1.h" #include "backends/ilang/ilang_backend.h" #if !defined(_WIN32) || defined(__MINGW32__) # include #endif #if defined(__linux__) || defined(__FreeBSD__) # include #endif #include #include #include #include #include #include YOSYS_NAMESPACE_BEGIN std::vector log_files; std::vector log_streams; std::map> log_hdump; std::vector log_warn_regexes, log_nowarn_regexes, log_werror_regexes; dict log_expect_log, log_expect_warning, log_expect_error; std::set log_warnings, log_experimentals, log_experimentals_ignored; int log_warnings_count = 0; int log_warnings_count_noexpect = 0; bool log_expect_no_warnings = false; bool log_hdump_all = false; FILE *log_errfile = NULL; SHA1 *log_hasher = NULL; bool log_time = false; bool log_error_stderr = false; bool log_cmd_error_throw = false; bool log_quiet_warnings = false; int log_verbose_level; string log_last_error; void (*log_error_atexit)() = NULL; int log_make_debug = 0; int log_force_debug = 0; int log_debug_suppressed = 0; vector header_count; vector log_id_cache; vector string_buf; int string_buf_index = -1; static struct timeval initial_tv = { 0, 0 }; static bool next_print_log = false; static int log_newline_count = 0; static bool check_expected_logs = true; static bool display_error_log_msg = true; static void log_id_cache_clear() { for (auto p : log_id_cache) free(p); log_id_cache.clear(); } #if defined(_WIN32) && !defined(__MINGW32__) // this will get time information and return it in timeval, simulating gettimeofday() int gettimeofday(struct timeval *tv, struct timezone *tz) { LARGE_INTEGER counter; LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); QueryPerformanceCounter(&counter); counter.QuadPart *= 1000000; counter.QuadPart /= freq.QuadPart; tv->tv_sec = long(counter.QuadPart / 1000000); tv->tv_usec = counter.QuadPart % 1000000; return 0; } #endif void logv(const char *format, va_list ap) { while (format[0] == '\n' && format[1] != 0) { log("\n"); format++; } if (log_make_debug && !ys_debug(1)) return; std::string str = vstringf(format, ap); if (str.empty()) return; size_t nnl_pos = str.find_last_not_of('\n'); if (nnl_pos == std::string::npos) log_newline_count += GetSize(str); else log_newline_count = GetSize(str) - nnl_pos - 1; if (log_hasher) log_hasher->update(str); if (log_time) { std::string time_str; if (next_print_log || initial_tv.tv_sec == 0) { next_print_log = false; struct timeval tv; gettimeofday(&tv, NULL); if (initial_tv.tv_sec == 0) initial_tv = tv; if (tv.tv_usec < initial_tv.tv_usec) { tv.tv_sec--; tv.tv_usec += 1000000; } tv.tv_sec -= initial_tv.tv_sec; tv.tv_usec -= initial_tv.tv_usec; time_str += stringf("[%05d.%06d] ", int(tv.tv_sec), int(tv.tv_usec)); } if (format[0] && format[strlen(format)-1] == '\n') next_print_log = true; for (auto f : log_files) fputs(time_str.c_str(), f); for (auto f : log_streams) *f << time_str; } for (auto f : log_files) fputs(str.c_str(), f); for (auto f : log_streams) *f << str; static std::string linebuffer; static bool log_warn_regex_recusion_guard = false; if (!log_warn_regex_recusion_guard) { log_warn_regex_recusion_guard = true; if (log_warn_regexes.empty() && log_expect_log.empty()) { linebuffer.clear(); } else { linebuffer += str; if (!linebuffer.empty() && linebuffer.back() == '\n') { for (auto &re : log_warn_regexes) if (YS_REGEX_NS::regex_search(linebuffer, re)) log_warning("Found log message matching -W regex:\n%s", str.c_str()); for (auto &item : log_expect_log) if (YS_REGEX_NS::regex_search(linebuffer, item.second.pattern)) item.second.current_count++; linebuffer.clear(); } } log_warn_regex_recusion_guard = false; } } void logv_header(RTLIL::Design *design, const char *format, va_list ap) { bool pop_errfile = false; log_spacer(); if (header_count.size() > 0) header_count.back()++; if (int(header_count.size()) <= log_verbose_level && log_errfile != NULL) { log_files.push_back(log_errfile); pop_errfile = true; } std::string header_id; for (int c : header_count) header_id += stringf("%s%d", header_id.empty() ? "" : ".", c); log("%s. ", header_id.c_str()); logv(format, ap); log_flush(); if (log_hdump_all) log_hdump[header_id].insert("yosys_dump_" + header_id + ".il"); if (log_hdump.count(header_id) && design != nullptr) for (auto &filename : log_hdump.at(header_id)) { log("Dumping current design to '%s'.\n", filename.c_str()); if (yosys_xtrace) IdString::xtrace_db_dump(); Pass::call(design, {"dump", "-o", filename}); if (yosys_xtrace) log("#X# -- end of dump --\n"); } if (pop_errfile) log_files.pop_back(); } static void logv_warning_with_prefix(const char *prefix, const char *format, va_list ap) { std::string message = vstringf(format, ap); bool suppressed = false; for (auto &re : log_nowarn_regexes) if (YS_REGEX_NS::regex_search(message, re)) suppressed = true; if (suppressed) { log("Suppressed %s%s", prefix, message.c_str()); } else { int bak_log_make_debug = log_make_debug; log_make_debug = 0; for (auto &re : log_werror_regexes) if (YS_REGEX_NS::regex_search(message, re)) log_error("%s", message.c_str()); bool warning_match = false; for (auto &item : log_expect_warning) if (YS_REGEX_NS::regex_search(message, item.second.pattern)) { item.second.current_count++; warning_match = true; } if (log_warnings.count(message)) { log("%s%s", prefix, message.c_str()); log_flush(); } else { if (log_errfile != NULL && !log_quiet_warnings) log_files.push_back(log_errfile); log("%s%s", prefix, message.c_str()); log_flush(); if (log_errfile != NULL && !log_quiet_warnings) log_files.pop_back(); log_warnings.insert(message); } if (!warning_match) log_warnings_count_noexpect++; log_warnings_count++; log_make_debug = bak_log_make_debug; } } void logv_warning(const char *format, va_list ap) { logv_warning_with_prefix("Warning: ", format, ap); } voi
// This is free and unencumbered software released into the public domain.
//
// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any
// means.

#include "kernel/yosys.h"
#include "kernel/sigtools.h"

#include <string>
#include <map>
#include <set>

USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN

// this function is called for each module in the design
static void find_stub_nets(RTLIL::Design *design, RTLIL::Module *module, bool report_bits)
{
	// use a SigMap to convert nets to a unique representation
	SigMap sigmap(module);

	// count how many times a single-bit signal is used
	std::map<RTLIL::SigBit, int> bit_usage_count;

	// count output lines for this module (needed only for summary output at the end)
	int line_count = 0;

	log("Looking for stub wires in module %s:\n", RTLIL::id2cstr(module->name));

	// For all ports on all cells
	for (auto &cell_iter : module->cells_)
	for (auto &conn : cell_iter.second->connections())
	{
		// Get the signals on the port
		// (use sigmap to get a uniqe signal name)
		RTLIL::SigSpec sig = sigmap(conn.second);

		// add each bit to bit_usage_count, unless it is a constant
		for (auto &bit : sig)
			if (bit.wire != NULL)
				bit_usage_count[bit]++;
	}

	// for each wire in the module
	for (auto &wire_iter : module->wires_)
	{
		RTLIL::Wire *wire = wire_iter.second;

		// .. but only selected wires
		if (!design->selected(module, wire))
			continue;

		// add +1 usage if this wire actually is a port
		int usage_offset = wire->port_id > 0 ? 1 : 0;

		// we will record which bits of the (possibly multi-bit) wire are stub signals
		std::set<int> stub_bits;

		// get a signal description for this wire and split it into separate bits
		RTLIL::SigSpec sig = sigmap(wire);

		// for each bit (unless it is a constant):
		// check if it is used at least two times and add to stub_bits otherwise
		for (int i = 0; i < GetSize(sig); i++)
			if (sig[i].wire != NULL && (bit_usage_count[sig[i]] + usage_offset) < 2)
				stub_bits.insert(i);

		// continue if no stub bits found
		if (stub_bits.size() == 0)
			continue;

		// report stub bits and/or stub wires, don't report single bits
		// if called with report_bits set to false.
		if (GetSize(stub_bits) == GetSize(sig)) {
			log("  found stub wire: %s\n", RTLIL::id2cstr(wire->name));
		} else {
			if (!report_bits)
				continue;
			log("  found wire with stub bits: %s [", RTLIL::id2cstr(wire->name));
			for (int bit : stub_bits)
				log("%s%d", bit == *stub_bits.begin() ? "" : ", ", bit);
			log("]\n");
		}

		// we have outputted a line, increment summary counter
		line_count++;
	}

	// report summary
	if (report_bits)
		log("  found %d stub wires or wires with stub bits.\n", line_count);
	else
		log("  found %d stub wires.\n", line_count);
}

// each pass contains a singleton object that is derived from Pass
struct StubnetsPass : public Pass {
	StubnetsPass() : Pass("stubnets") { }
	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
	{
		// variables to mirror information from passed options
		bool report_bits = 0;

		log_header(design, "Executing STUBNETS pass (find stub nets).\n");

		// parse options
		size_t argidx;
		for (argidx = 1; argidx < args.size(); argidx++) {
			std::string arg = args[argidx];
			if (arg == "-report_bits") {
				report_bits = true;
				continue;
			}
			break;
		}

		// handle extra options (e.g. selection)
		extra_args(args, argidx, design);

		// call find_stub_nets() for each module that is either
		// selected as a whole or contains selected objects.
		for (auto &it : design->modules_)
			if (design->selected_module(it.first))
				find_stub_nets(design, it.second, report_bits);
	}
} StubnetsPass;

PRIVATE_NAMESPACE_END