3 Commits

Author SHA1 Message Date
  Hao 483a6fd45f docs: update iEDA Docker image links in README-CN.md to point to 5 months ago
  HelloXiao a01f574a23 !70 feat: add module-level path analysis to JSON power report 5 months ago
  HelloXiao 0249ba1785 !69 feat(iPW): add module-level power analysis to JSON power report 5 months ago
4 changed files with 123 additions and 59 deletions
Split View
  1. +5
    -7
      README-CN.md
  2. +70
    -50
      src/operation/iPA/api/Power.cc
  3. +0
    -1
      src/operation/iPA/api/Power.hh
  4. +48
    -1
      src/operation/iSTA/source/module/sta/StaReport.cc

+ 5
- 7
README-CN.md View File

@@ -92,7 +92,7 @@ Open-source is not a goal but a way

若您需要对 iEDA 进行修改,通过源码构建,请按照顺序阅读,查看 [iEDA user guide](https://ieda.oscc.cc/en/tools/ieda-platform/guide.html).。

您也可以直接使用最新的 [iEDA docker 镜像](https://hub.docker.com/r/iedaopensource/base),即可跳过 "*1. 源码构建 iEDA*"。
您也可以直接使用最新的 [iEDA docker 镜像](docker.cnb.cool/ecoslab/rtl2gds/ieda),即可跳过 "*1. 源码构建 iEDA*"。

PS: 关于如何安装 Docker,可参考[Docker安装及初始化](https://www.cnblogs.com/harrypotterisdead/p/17223606.html)。

@@ -102,24 +102,22 @@ PS: 关于如何安装 Docker,可参考[Docker安装及初始化](https://www.

#### 方法1 使用iEDA镜像(推荐)

从 Dockerhub 上下载最新的 iedaopensource/base 镜像,镜像中包含了最新的 master 分支代码和依赖(构建工具和依赖库)。也可使用 `-v` 命令挂载自行下载的 iEDA 代码仓库,仅使用镜像提供的编译工具和依赖库进行构建。
通过 docker pull 拉取最新的 docker.cnb.cool/ecoslab/rtl2gds/ieda:latest 镜像,镜像中包含了最新的 master 分支代码和依赖(构建工具和依赖库)。也可使用 `-v` 命令挂载自行下载的 iEDA 代码仓库,仅使用镜像提供的编译工具和依赖库进行构建。

参考如下命令,进入容器后的当前目录即为 iEDA master 分支代码。

```bash
# iedaopensource/base:(latest, ubuntu, debian)
docker run -it --rm iedaopensource/base:latest bash
# docker.cnb.cool/ecoslab/rtl2gds/ieda:latest
docker run -it --rm docker.cnb.cool/ecoslab/rtl2gds/ieda:latest bash
# 进入容器后执行 build.sh 进行构建
bash build.sh
# 若能够正常输出 "Hello iEDA!" 则编译成功
./bin/iEDA -script scripts/hello.tcl
```

根据个人使用习惯,有 ubuntu(基于Ubuntu20.04)和 debian(基于Debian11)两种不同镜像tag可选。

#### 方法2 手动安装依赖并编译

在 Ubuntu 20.04 下执行如下命令:
在 Ubuntu 22.04 下执行如下命令:

```bash
# 下载iEDA仓库


+ 70
- 50
src/operation/iPA/api/Power.cc View File

@@ -556,11 +556,21 @@ unsigned Power::reportSummaryPowerJSON(const char* rpt_file_name,
report_power(this);
auto& report_summary_data = report_power.get_report_summary_data();
nlohmann::json json_report = nlohmann::json::object();
auto &summary_json = json_report["summary"] = nlohmann::json::array();

// lambda for print power data float to string.
auto data_str = [](double data) { return Str::printf("%.3e", data); };
auto data_str_f = [](double data) { return Str::printf("%.3f", data); };

// extract module name from vertex name
auto extract_module_name = [](const std::string& name) -> std::string {
size_t pos = name.find('/');
if (pos != std::string::npos) {
return name.substr(0, pos);
}
return "";
};

std::map<PwrGroupData::PwrGroupType, std::string> group_type_to_string = {
{PwrGroupData::PwrGroupType::kIOPad, "io_pad"},
{PwrGroupData::PwrGroupType::kMemory, "memory"},
@@ -592,6 +602,48 @@ unsigned Power::reportSummaryPowerJSON(const char* rpt_file_name,
});
}

auto instance_power_data_vec = getInstancePowerData();
std::sort(instance_power_data_vec.begin(), instance_power_data_vec.end(),
[](const IRInstancePower& a, const IRInstancePower& b) {
return a._total_power > b._total_power;
});

// Helper struct for module power statistics
struct ModuleStats {
std::string module_name;
double internal_power = 0.0;
double switch_power = 0.0;
double leakage_power = 0.0;
double total_power = 0.0;
double nominal_voltage = 0.0;
};

std::unordered_map<std::string, ModuleStats> module_stats;

// Try to get module power data
bool failed_extract_module_name = false;
for (auto instance_power_data : instance_power_data_vec) {
auto name = extract_module_name(instance_power_data._instance_name);
if (name.empty() && !failed_extract_module_name) {
LOG_WARNING
<< "Failed to extract module name from instance: "
<< instance_power_data._instance_name
<< ". Hierarchical naming (e.g., 'module/instance') is required "
<< " but Yosys may flatten hierarchy. "
<< "The power summary of individual modules will be stopped.";

failed_extract_module_name = true;
break;
}

module_stats[name].module_name = name;
module_stats[name].internal_power += instance_power_data._internal_power;
module_stats[name].switch_power += instance_power_data._switch_power;
module_stats[name].leakage_power += instance_power_data._leakage_power;
module_stats[name].nominal_voltage += instance_power_data._nominal_voltage;
module_stats[name].total_power += instance_power_data._total_power;
};

// Get switch power
double summary_switch_power = report_summary_data.get_net_switching_power();
std::string summary_switch_power_percentage = data_str_f(CalcPercentage(
@@ -621,6 +673,24 @@ unsigned Power::reportSummaryPowerJSON(const char* rpt_file_name,
// Get total power
json_report["total_power"] = data_str(total_power);

if (!failed_extract_module_name) {
for (const auto& s : module_stats) {
const auto& stats = s.second;

auto percentage = CalcPercentage(stats.total_power / total_power);

summary_json.push_back({
{"module_name", stats.module_name},
{"internal_power", data_str(stats.internal_power)},
{"switch_power", data_str(stats.switch_power)},
{"leakage_power", data_str(stats.leakage_power)},
{"total_power", data_str(stats.total_power)},
{"nominal_voltage", data_str(stats.nominal_voltage)},
{"percentage", data_str_f(percentage)},
});
}
}

std::ofstream out_file(rpt_file_name);
if (out_file.is_open()) {
out_file << json_report.dump(4); // 4 spaces indent
@@ -730,45 +800,6 @@ unsigned Power::reportInstancePowerCSV(const char* rpt_file_name) {
return 1;
}

/**
* @brief report json file
*
* @param rpt_file_name
* @return unsigned
*/
unsigned Power::reportInstancePowerJSON(const char* rpt_file_name) {
nlohmann::json json_report = nlohmann::json::array();
auto data_str = [](double data) { return Str::printf("%.3e", data); };

auto instance_power_data_vec = getInstancePowerData();
std::sort(instance_power_data_vec.begin(), instance_power_data_vec.end(),
[](const IRInstancePower& a, const IRInstancePower& b) {
return a._total_power > b._total_power;
});

for (auto instance_power_data : instance_power_data_vec) {
json_report.push_back({
{"instance_name", instance_power_data._instance_name},
{"nominal_voltage", instance_power_data._nominal_voltage},
{"internal_power", data_str(instance_power_data._internal_power)},
{"switch_power", data_str(instance_power_data._switch_power)},
{"leakage_power", data_str(instance_power_data._leakage_power)},
{"total_power", data_str(instance_power_data._total_power)},
});
};

std::ofstream out_file(rpt_file_name);
if (out_file.is_open()) {
out_file << json_report.dump(4); // 4 spaces indent
LOG_INFO << "JSON report written to: " << rpt_file_name;
out_file.close();
} else {
LOG_ERROR << "Failed to open JSON report file: " << rpt_file_name;
}

return 1;
}

/**
* @brief get instance power data.
*
@@ -1033,17 +1064,6 @@ unsigned Power::reportPower(bool is_copy) {
reportSummaryPowerJSON(output_path.c_str(), PwrAnalysisMode::kAveraged);
}

if (isJsonReportEnabled()) {
std::string file_name =
Str::printf("%s_%s.pwr.json", ista->get_design_name().c_str(), "instance");
if (is_copy) {
CopyFile(backup_work_space, output_dir, file_name);
}

std::string output_path = output_dir + "/" + file_name;
reportInstancePowerJSON(output_path.c_str());
}

LOG_INFO << "power report end, output dir: " << output_dir;
double memory_delta = stats.memoryDelta();
LOG_INFO << "power report memory usage " << memory_delta << "MB";


+ 0
- 1
src/operation/iPA/api/Power.hh View File

@@ -133,7 +133,6 @@ class Power {
unsigned reportInstancePower(const char* rpt_file_name,
PwrAnalysisMode pwr_analysis_mode);
unsigned reportInstancePowerCSV(const char* rpt_file_name);
unsigned reportInstancePowerJSON(const char* rpt_file_name);

unsigned reportPower(bool is_copy = true);



+ 48
- 1
src/operation/iSTA/source/module/sta/StaReport.cc View File

@@ -30,6 +30,7 @@
#include <stack>
#include <string>
#include <vector>
#include <unordered_map>

#include "Sta.hh"
#include "StaDump.hh"
@@ -780,6 +781,25 @@ unsigned StaReportPathDetailJson::operator()(StaSeqPathData* seq_path_data) {
StaVertex* last_vertex = nullptr;

auto& detail_json = path_json["detail"];
auto& summary_json = path_json["summary"] = nlohmann::json::array();

// Helper function to extract module name from hierarchical vertex name
bool failed_extract_module_name = false;
auto extract_module_name = [](const std::string& name) -> std::string {
auto pos = name.find('/');
if (pos != std::string::npos) {
return name.substr(0, pos);
}
return "";
};

// Module statistics tracking
struct stats {
unsigned count = 0;
double total_delay = 0.0;
};

std::unordered_map<std::string, stats> module_stats_map;

while (!path_stack.empty()) {
auto* path_delay_data = path_stack.top();
@@ -800,17 +820,44 @@ unsigned StaReportPathDetailJson::operator()(StaSeqPathData* seq_path_data) {
auto incr_time = arrive_time - last_arrive_time;
last_arrive_time = arrive_time;

auto name = own_vertex->getNameWithCellName();

detail_json.push_back(
{{"name", own_vertex->getNameWithCellName()},
{{"name", name},
{"incr_delay", fix_point_str(incr_time)},
{"path_delay",
std::string(fix_point_str(arrive_time + clock_path_arrive_time)) +
trans_type_str}});

// Check if hierarchical naming convention is followed for module
// extraction
auto module_name = extract_module_name(name);
if (module_name.empty() && !failed_extract_module_name) {
LOG_WARNING
<< "Cannot extract module name from vertex: " << name
<< ". Hierarchical naming (e.g., 'module/instance') is required "
<< ", but Yosys may flatten hierarchy.";
failed_extract_module_name = true;
}

// If once failed to extract module name, do not count it again.
if (!failed_extract_module_name) {
module_stats_map[module_name].count++;
module_stats_map[module_name].total_delay += incr_time;
}

last_vertex = own_vertex;
path_stack.pop();
}

if (!failed_extract_module_name) {
for (const auto& [module, stat] : module_stats_map) {
summary_json.push_back({{"module", module},
{"count", stat.count},
{"total_delay", stat.total_delay}});
}
}

path_json["end_point"] = last_vertex->getNameWithCellName();
};



Loading…
Cancel
Save
Baidu
map