mirror of
https://github.com/YosysHQ/yosys
synced 2025-06-23 06:13:41 +00:00
activity computation
This commit is contained in:
parent
9de07e57b7
commit
3f7c392e1a
3 changed files with 318 additions and 17 deletions
21
Makefile
21
Makefile
|
@ -155,7 +155,7 @@ ifeq ($(OS), Haiku)
|
||||||
CXXFLAGS += -D_DEFAULT_SOURCE
|
CXXFLAGS += -D_DEFAULT_SOURCE
|
||||||
endif
|
endif
|
||||||
|
|
||||||
YOSYS_VER := 0.46+34
|
YOSYS_VER := 0.46+32
|
||||||
|
|
||||||
# Note: We arrange for .gitcommit to contain the (short) commit hash in
|
# Note: We arrange for .gitcommit to contain the (short) commit hash in
|
||||||
# tarballs generated with git-archive(1) using .gitattributes. The git repo
|
# 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/splitcells.o
|
||||||
OBJS += passes/cmds/splitfanout.o
|
OBJS += passes/cmds/splitfanout.o
|
||||||
OBJS += passes/cmds/splitnets.o
|
OBJS += passes/cmds/splitnets.o
|
||||||
|
OBJS += passes/cmds/activity.o
|
||||||
OBJS += passes/cmds/tee.o
|
OBJS += passes/cmds/tee.o
|
||||||
OBJS += passes/sat/sim.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
|
# 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)
|
docs/source/cmd/abc.rst: $(TARGETS) $(EXTRA_TARGETS)
|
||||||
$(Q) mkdir -p docs/source/cmd
|
mkdir -p docs/source/cmd
|
||||||
$(Q) mkdir -p temp/docs/source/cmd
|
./$(PROGRAM_PREFIX)yosys -p 'help -write-rst-command-reference-manual'
|
||||||
$(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 $@'
|
|
||||||
|
|
||||||
PHONY: docs/gen_examples docs/gen_images docs/guidelines docs/usage docs/reqs
|
PHONY: docs/gen_examples docs/gen_images docs/guidelines docs/usage docs/reqs
|
||||||
docs/gen_examples: $(TARGETS)
|
docs/gen_examples: $(TARGETS)
|
||||||
|
@ -1068,7 +1057,7 @@ docs/reqs:
|
||||||
$(Q) $(MAKE) -C docs reqs
|
$(Q) $(MAKE) -C docs reqs
|
||||||
|
|
||||||
.PHONY: docs/prep
|
.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
|
DOC_TARGET ?= html
|
||||||
docs: docs/prep
|
docs: docs/prep
|
||||||
|
|
144
passes/cmds/activity.cc
Normal file
144
passes/cmds/activity.cc
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
* yosys -- Yosys Open SYnthesis Suite
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||||
|
* 2024 Alain Dargelas <alain@silimate.com>
|
||||||
|
*
|
||||||
|
* 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<std::string> &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<std::string> tokenize(std::string_view str, std::string_view separator, bool skipEmpty)
|
||||||
|
{
|
||||||
|
std::vector<std::string> result;
|
||||||
|
tokenize(str, separator, result, skipEmpty);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActivityProp(Module *module) : module(module), sigmap(module)
|
||||||
|
{
|
||||||
|
std::map<SigBit, std::string> ActivityMap;
|
||||||
|
std::map<SigBit, std::string> 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<std::string> activities = tokenize(act, " ", true);
|
||||||
|
std::vector<std::string> 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<SigBit, std::string>::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<SigBit, std::string>::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<std::string> 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
|
|
@ -1460,7 +1460,7 @@ struct SimWorker : SimShared
|
||||||
{
|
{
|
||||||
log_assert(top == nullptr);
|
log_assert(top == nullptr);
|
||||||
fst = new FstData(sim_filename);
|
fst = new FstData(sim_filename);
|
||||||
|
timescale = fst->getTimescaleString();
|
||||||
if (scope.empty())
|
if (scope.empty())
|
||||||
log_error("Scope must be defined for co-simulation.\n");
|
log_error("Scope must be defined for co-simulation.\n");
|
||||||
|
|
||||||
|
@ -2397,6 +2397,170 @@ struct VCDWriter : public OutputWriter
|
||||||
std::ofstream vcdfile;
|
std::ofstream vcdfile;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AnnotateActivity : public OutputWriter {
|
||||||
|
AnnotateActivity(SimWorker *worker) : OutputWriter(worker) {}
|
||||||
|
|
||||||
|
struct SignalActivityData {
|
||||||
|
std::vector<uint32_t> lastValues;
|
||||||
|
std::vector<uint32_t> toggleCounts;
|
||||||
|
std::vector<uint32_t> highTimes;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::map<int, SignalActivityData> SignalActivityDataMap;
|
||||||
|
|
||||||
|
void write(std::map<int, bool> &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<uint32_t> 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<uint32_t> &lastVals = (*itr).second.lastValues;
|
||||||
|
std::vector<uint32_t> &toggleCounts = (*itr).second.toggleCounts;
|
||||||
|
std::vector<uint32_t> &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<uint32_t> &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<uint32_t> &toggleCounts = (*itr).second.toggleCounts;
|
||||||
|
const std::vector<uint32_t> &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
|
struct FSTWriter : public OutputWriter
|
||||||
{
|
{
|
||||||
FSTWriter(SimWorker *worker, std::string filename) : OutputWriter(worker) {
|
FSTWriter(SimWorker *worker, std::string filename) : OutputWriter(worker) {
|
||||||
|
@ -2871,6 +3035,10 @@ struct SimPass : public Pass {
|
||||||
worker.multiclock = true;
|
worker.multiclock = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-activity") {
|
||||||
|
worker.outputfiles.emplace_back(std::unique_ptr<AnnotateActivity>(new AnnotateActivity(&worker)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue