From 3f7c392e1a54ac6b8efe141fad133fc229306f96 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Wed, 16 Oct 2024 20:41:26 -0700 Subject: [PATCH] activity computation --- Makefile | 21 ++--- passes/cmds/activity.cc | 144 ++++++++++++++++++++++++++++++++++ passes/sat/sim.cc | 170 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 318 insertions(+), 17 deletions(-) create mode 100644 passes/cmds/activity.cc diff --git a/Makefile b/Makefile index add13055d..fa5da0b78 100644 --- a/Makefile +++ b/Makefile @@ -155,7 +155,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.46+34 +YOSYS_VER := 0.46+32 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo @@ -729,6 +729,7 @@ OBJS += passes/cmds/setattr.o OBJS += passes/cmds/splitcells.o OBJS += passes/cmds/splitfanout.o OBJS += passes/cmds/splitnets.o +OBJS += passes/cmds/activity.o OBJS += passes/cmds/tee.o OBJS += passes/sat/sim.o @@ -1012,20 +1013,8 @@ endif # also others, but so long as it doesn't fail this is enough to know we tried docs/source/cmd/abc.rst: $(TARGETS) $(EXTRA_TARGETS) - $(Q) mkdir -p docs/source/cmd - $(Q) mkdir -p temp/docs/source/cmd - $(Q) cd temp && ./../$(PROGRAM_PREFIX)yosys -p 'help -write-rst-command-reference-manual' - $(Q) rsync -rc temp/docs/source/cmd docs/source - $(Q) rm -rf temp -docs/source/cell/word_add.rst: $(TARGETS) $(EXTRA_TARGETS) - $(Q) mkdir -p docs/source/cell - $(Q) mkdir -p temp/docs/source/cell - $(Q) cd temp && ./../$(PROGRAM_PREFIX)yosys -p 'help -write-rst-cells-manual' - $(Q) rsync -rc temp/docs/source/cell docs/source - $(Q) rm -rf temp - -docs/source/generated/cells.json: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) - $(Q) ./$(PROGRAM_PREFIX)yosys -p 'help -dump-cells-json $@' + mkdir -p docs/source/cmd + ./$(PROGRAM_PREFIX)yosys -p 'help -write-rst-command-reference-manual' PHONY: docs/gen_examples docs/gen_images docs/guidelines docs/usage docs/reqs docs/gen_examples: $(TARGETS) @@ -1068,7 +1057,7 @@ docs/reqs: $(Q) $(MAKE) -C docs reqs .PHONY: docs/prep -docs/prep: docs/source/cmd/abc.rst docs/source/generated/cells.json docs/gen_examples docs/gen_images docs/guidelines docs/usage +docs/prep: docs/source/cmd/abc.rst docs/gen_examples docs/gen_images docs/guidelines docs/usage DOC_TARGET ?= html docs: docs/prep diff --git a/passes/cmds/activity.cc b/passes/cmds/activity.cc new file mode 100644 index 000000000..527ab3270 --- /dev/null +++ b/passes/cmds/activity.cc @@ -0,0 +1,144 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * 2024 Alain Dargelas + * + * 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 "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct ActivityProp { + Module *module; + SigMap sigmap; + + void tokenize(std::string_view str, std::string_view separator, std::vector &result, bool skipEmpty) + { + std::string::size_type pos{0}; + const auto sepSize = separator.size(); + const auto stringSize = str.size(); + std::string tmp; + std::string::size_type n = str.find(separator, pos); + while (n != std::string::npos) { + tmp = str.substr(pos, n - pos); + if (!(tmp.empty() && skipEmpty)) + result.push_back(tmp); + pos = n + sepSize; + n = str.find(separator, pos); + } + if (pos < stringSize) { // put last part + tmp = str.substr(pos, stringSize - pos); + if (!(tmp.empty() && skipEmpty)) + result.push_back(tmp); + } + } + + std::vector tokenize(std::string_view str, std::string_view separator, bool skipEmpty) + { + std::vector result; + tokenize(str, separator, result, skipEmpty); + return result; + } + + ActivityProp(Module *module) : module(module), sigmap(module) + { + std::map ActivityMap; + std::map DutyMap; + // Build {signal bit - activity} map from the wire activities calculated in the sim pass + for (Wire *wire : module->wires()) { + SigSpec sig(sigmap(wire)); + std::string act = wire->get_string_attribute("$ACKT"); + std::string duty = wire->get_string_attribute("$DUTY"); + std::vector activities = tokenize(act, " ", true); + std::vector duties = tokenize(duty, " ", true); + for (int i = 0; i < GetSize(sig); i++) { + SigBit bit(sig[i]); + ActivityMap.emplace(bit, activities[i]); + DutyMap.emplace(bit, duties[i]); + } + } + // Attach port activity to cell using sigmap + for (auto cell : module->cells()) { + std::string cell_ports_activity; + std::string cell_ports_duty; + for (auto conn : cell->connections()) { + for (int i = 0; i < GetSize(conn.second); i++) { + SigBit bit(sigmap(conn.second[i])); + std::string port_name = std::string(conn.first.c_str()) + "[" + std::to_string(i) + "]"; + { + std::map::iterator itr = ActivityMap.find(bit); + if (itr != ActivityMap.end()) { + cell_ports_activity += port_name + "=" + (*itr).second + " "; + } else { + RTLIL::SigSpec sigspec(bit); + if (!sigspec.is_fully_const()) { + log_warning("No activity found for : %s/%s/%s", module->name.c_str(), cell->name.c_str(), port_name.c_str()); + } + // constants have no activity + cell_ports_activity += port_name + "=" + "0.0 "; + } + } + { + std::map::iterator itr = DutyMap.find(bit); + if (itr != DutyMap.end()) { + cell_ports_duty += port_name + "=" + (*itr).second + " "; + } else { + RTLIL::SigSpec sigspec(bit); + if (!sigspec.is_fully_const()) { + log_warning("No dutycycle found for : %s/%s/%s", module->name.c_str(), cell->name.c_str(), port_name.c_str()); + } + // constant 1 has duty cycle 1, constant 0 has duty cycle 0 + cell_ports_duty += port_name + "=" + (sigspec.as_bool() ? "1.0" : "0.0") + " "; + } + } + } + } + cell->set_string_attribute("$ACKT:", cell_ports_activity); + cell->set_string_attribute("$DUTY:", cell_ports_duty); + } + } +}; + +struct ActivityPropPass : public Pass { + ActivityPropPass() : Pass("activity_prop", "Attaches wire activity to cell ports") {} + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" activity_prop\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing Activity propagation pass\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + // No options currently. When adding in the future make sure to update docstring with [options] + break; + } + extra_args(args, argidx, design); + + for (auto module : design->modules()) { + ActivityProp worker(module); + } + } +} ActivityPropPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index c3fa213f6..813bf00cd 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -1460,7 +1460,7 @@ struct SimWorker : SimShared { log_assert(top == nullptr); fst = new FstData(sim_filename); - + timescale = fst->getTimescaleString(); if (scope.empty()) log_error("Scope must be defined for co-simulation.\n"); @@ -2397,6 +2397,170 @@ struct VCDWriter : public OutputWriter std::ofstream vcdfile; }; +struct AnnotateActivity : public OutputWriter { + AnnotateActivity(SimWorker *worker) : OutputWriter(worker) {} + + struct SignalActivityData { + std::vector lastValues; + std::vector toggleCounts; + std::vector highTimes; + }; + + typedef std::map SignalActivityDataMap; + + void write(std::map &use_signal) override + { + // Init map + SignalActivityDataMap dataMap; + // For each event (new time when a value changed) + for (auto &d : worker->output_data) { + // For each signal/values in that time slice + for (auto &data : d.second) { + int sig = data.first; + if (!use_signal.at(sig)) + continue; + // Create an entry in the map with all zeros for all bits of the signal + SignalActivityDataMap::iterator itr = dataMap.find(sig); + if (itr == dataMap.end()) { + Const value = data.second; + std::vector vals(GetSize(value), 0); + SignalActivityData data; + data.highTimes = vals; + data.lastValues = vals; + data.toggleCounts = vals; + dataMap.emplace(sig, data); + } + } + } + // Max simulation time + int max_time = 0; + // clock pin id (highest toggling signal) + int clk = 0; + int highest_toggle = 0; + // Used to compute time intervals + int prev_time = 0; + // For each event (new time when a value changed) + for (auto &d : worker->output_data) { + int time = d.first; + // For each signal/values in that time slice + for (auto &data : d.second) { + int sig = data.first; + if (!use_signal.at(sig)) + continue; + Const value = data.second; + SignalActivityDataMap::iterator itr = dataMap.find(sig); + std::vector &lastVals = (*itr).second.lastValues; + std::vector &toggleCounts = (*itr).second.toggleCounts; + std::vector &highTimes = (*itr).second.highTimes; + for (int i = GetSize(value) - 1; i >= 0; i--) { + int val = '-'; + switch (value[i]) { + case State::S0: + val = '0'; + break; + case State::S1: + val = '1'; + break; + case State::Sx: + val = 'x'; + break; + default: + val = 'z'; + } + if (val != lastVals[i]) { + toggleCounts[i]++; + if (toggleCounts[i] > highest_toggle) { + highest_toggle = toggleCounts[i]; + clk = sig; + } + lastVals[i] = val; + } + if (lastVals[i] == '1') { + highTimes[i] += time - prev_time; + } + } + } + prev_time = time; + max_time = time; + } + + // Retrieve VCD timescale + std::string timescale = worker->timescale; + double real_timescale = 1e-12; // ps + if (timescale == "ns") + real_timescale = 1e-9; + if (timescale == "fs") + real_timescale = 1e-15; + + bool debug = false; + + // Compute clock period, find the highest toggling signal and compute its average period + SignalActivityDataMap::iterator itr = dataMap.find(clk); + std::vector &clktoggleCounts = (*itr).second.toggleCounts; + double clk_period = real_timescale * (double)max_time / (clktoggleCounts[0] / 2); + if (debug) { + std::cout << "Clock toggle count: " << clktoggleCounts[0] << "\n"; + std::cout << "Max time: " << max_time << "\n"; + std::cout << "Clock period: " << clk_period << "\n"; + } + worker->top->write_output_header( + [this, debug](IdString name) { + if (debug) + std::cout << stringf("module %s\n", log_id(name)); + }, + [this, debug]() { + if (debug) + std::cout << "endmodule\n"; + }, + [this, use_signal, dataMap, max_time, real_timescale, clk_period, debug] + (const char *name, int size, Wire *w, int id, bool is_reg) { + if (!use_signal.at(id)) + return; + std::string full_name = form_vcd_name(name, size, w); + SignalActivityDataMap::const_iterator itr = dataMap.find(id); + const std::vector &toggleCounts = (*itr).second.toggleCounts; + const std::vector &highTimes = (*itr).second.highTimes; + if (debug) { + std::cout << full_name << ":\n"; + std::cout << " TC: "; + for (uint32_t i = 0; i < (uint32_t)size; i++) { + std::cout << toggleCounts[i] << " "; + } + std::cout << "\n"; + std::cout << " HT: "; + for (uint32_t i = 0; i < (uint32_t)size; i++) { + std::cout << highTimes[i] << " "; + } + std::cout << "\n"; + std::cout << " ACK: "; + } + std::string activity_str; + for (uint32_t i = 0; i < (uint32_t)size; i++) { + // Compute Activity + double activity = toggleCounts[i] / ((double)max_time * real_timescale / clk_period); + activity_str += std::to_string(activity) + " "; + } + if (debug) { + std::cout << activity_str; + std::cout << "\n"; + std::cout << " DUTY: "; + } + std::string duty_str; + for (uint32_t i = 0; i < (uint32_t)size; i++) { + // Compute Duty cycle + double duty = (double)highTimes[i] / (double)max_time; + duty_str += std::to_string(duty) + " "; + } + if (debug) { + std::cout << duty_str; + std::cout << "\n"; + } + w->set_string_attribute("$ACKT", activity_str); + w->set_string_attribute("$DUTY", duty_str); + }); + } +}; + struct FSTWriter : public OutputWriter { FSTWriter(SimWorker *worker, std::string filename) : OutputWriter(worker) { @@ -2871,6 +3035,10 @@ struct SimPass : public Pass { worker.multiclock = true; continue; } + if (args[argidx] == "-activity") { + worker.outputfiles.emplace_back(std::unique_ptr(new AnnotateActivity(&worker))); + continue; + } break; } extra_args(args, argidx, design);