45 Commits

Author SHA1 Message Date
  Hao 7ea11921aa !66 build: update base image to Ubuntu 22.04 in Dockerfile 5 months ago
  Emin 485f8c59f8
!65 build: add libcurl4-openssl-dev to apt dependencies 5 months ago
  Qiming Chu cb6b5808a9
build: add libcurl4-openssl-dev to apt dependencies 5 months ago
  Emin e4b19638c7
!64 Prepare for v0.1.0 5 months ago
  ZhishengZeng d35ac85be4
!54 Merge branch 'master' of gitee.com:ieda-ipd/iEDA into nn_master 5 months ago
  ZhishengZeng 9ddd9c7fc9 Merge branch 'master' of gitee.com:ieda-ipd/iEDA into nn_master 5 months ago
  ZhishengZeng 0eb52ec50d
!52 feature:add NotificationUtility for HTTP notifications and update CMake configurations 5 months ago
  ZhishengZeng c35a36b07c feat(iRT): add JSON file generation and notification for various modules 5 months ago
  ZhishengZeng 220d92212e Merge branch 'master' of gitee.com:ieda-ipd/iEDA into notify 5 months ago
  ZhishengZeng dc49843130 update 5 months ago
  苏坚荣 7f9c61be1d
update src/operation/iPNP/main.cpp. 5 months ago
  ZhishengZeng c28584a018 update 5 months ago
  ZhishengZeng 6e287737e8 update 5 months ago
  ZhishengZeng fa41ef75f3
!53 add json 5 months ago
  ZhishengZeng dc27ad00ee Merge branch 'master' of gitee.com:ieda-ipd/iEDA into nn_master 5 months ago
  ZhishengZeng cd41f87922 update 5 months ago
  simintao 37664a7c3f Merge branch 'master' of gitee.com:ieda-ipd/iEDA 5 months ago
  simintao 1591a15148 fix:merge master 5 months ago
  ZhishengZeng 680c5fd88b Merge branch 'master' of gitee.com:ieda-ipd/iEDA into nn_master 5 months ago
  ZhishengZeng 6c2f722c49 add json 5 months ago
  SuJianrong db25663484 add function: connect M2 and M1 Layer 5 months ago
  苏坚荣 1624d82510
update src/operation/iPNP/README.md. 5 months ago
  simintao af377cc61a fix:merge 5 months ago
  simintao 35f6967ae4 feature:support virtual rc tree 5 months ago
  0xharry e62fd74e75 feature:add NotificationUtility for HTTP notifications and update CMake configurations 5 months ago
  ZhishengZeng 8cd9daa0d9 update 5 months ago
  ZhishengZeng 1d3d941dc6 update csv 5 months ago
  陶思敏 071226fa6d Merge branch 'master' of gitee.com:ieda-ipd/iEDA 5 months ago
  陶思敏 b9342c0a45 refactor:change python add rc node and return rc node name 5 months ago
  ZhishengZeng 0f7fa89c71 update enclosure eol 5 months ago
  ZhishengZeng b5e752cf92 Merge branch 'nn_master' of gitee.com:ieda-ipd/iEDA into nn_master 5 months ago
  ZhishengZeng 1f79185f0e update gr 5 months ago
  SuJianrong 178f40bc41 Merge branch 'iPDN-IRAware' of https://gitee.com/ieda-ipd/iEDA into iPDN-IRAware 5 months ago
  SuJianrong 57e3267fc8 iPDN version 1.0: fix init IR Analysis 5 months ago
  jixiao 1774c89abe add eolenclosure 5 months ago
  ZhishengZeng 239621f8a8 update 5 months ago
  ZhishengZeng e83dc4fa19 update 5 months ago
  ZhishengZeng 588074f48b update cut eol 5 months ago
  ZhishengZeng 5c457a6e2f update enclosure edge 6 months ago
  ZhishengZeng 123039f177 update eol 6 months ago
  ZhishengZeng d4c985f7f4 Merge branch 'nn_master' of gitee.com:ieda-ipd/iEDA into nn_master 6 months ago
  ZhishengZeng 86a5ca335a update summary 6 months ago
  ZhishengZeng 465136ef73 review eol 6 months ago
  ZhishengZeng 6cab0b1a13 Merge branch 'nn_master' of gitee.com:ieda-ipd/iEDA into nn_master 6 months ago
  ZhishengZeng 5cfdb09588 update 6 months ago
71 changed files with 3409 additions and 1831 deletions
Split View
  1. +1
    -1
      Dockerfile
  2. +1
    -1
      build.sh
  3. +1
    -0
      src/database/data/design/db_property/IdbCutLayerLef58Property.h
  4. +2
    -0
      src/database/manager/builder/lef_builder/property_parser/cutlayer_parser.cpp
  5. +2
    -2
      src/database/manager/builder/lef_builder/property_parser/routinglayer_parser.cpp
  6. +64
    -36
      src/feature/database/feature_irt.h
  7. +4
    -0
      src/feature/parser/feature_parser_tools.cpp
  8. +4
    -4
      src/interface/python/py_ista/py_ista.cpp
  9. +2
    -2
      src/interface/python/py_ista/py_ista.h
  10. +118
    -167
      src/operation/iDRC/interface/DRCInterface.cpp
  11. +1
    -0
      src/operation/iDRC/interface/DRCInterface.hpp
  12. +4
    -4
      src/operation/iDRC/source/data_manager/advance/Config.hpp
  13. +6
    -0
      src/operation/iDRC/source/data_manager/advance/CutLayer.hpp
  14. +24
    -0
      src/operation/iDRC/source/data_manager/basic/PlanarRect.hpp
  15. +9
    -3
      src/operation/iDRC/source/data_manager/design_rule/CutEOLSpacingRule.hpp
  16. +7
    -0
      src/operation/iDRC/source/data_manager/design_rule/EnclosureEdgeRule.hpp
  17. +9
    -0
      src/operation/iDRC/source/data_manager/design_rule/EnclosureParallelRule.hpp
  18. +247
    -387
      src/operation/iDRC/source/module/rule_validator/rv_design_rule/CutEOLSpacing.cpp
  19. +123
    -386
      src/operation/iDRC/source/module/rule_validator/rv_design_rule/EnclosureEdge.cpp
  20. +242
    -0
      src/operation/iDRC/source/module/rule_validator/rv_design_rule/EnclosureParallel.cpp
  21. +144
    -168
      src/operation/iDRC/source/module/rule_validator/rv_design_rule/EndOfLineSpacing.cpp
  22. +0
    -278
      src/operation/iDRC/source/module/rule_validator/rv_design_rule/JogToJogSpacing.cpp
  23. +28
    -1
      src/operation/iDRC/source/toolkit/utility/Utility.hpp
  24. +8
    -1
      src/operation/iPNP/README.md
  25. +1
    -0
      src/operation/iPNP/example/add_via1.tcl
  26. +29
    -6
      src/operation/iPNP/main.cpp
  27. +6
    -0
      src/operation/iPNP/source/data_manager/iPNPIdbWrapper.cpp
  28. +2
    -0
      src/operation/iPNP/source/data_manager/iPNPIdbWrapper.hh
  29. +131
    -124
      src/operation/iPNP/source/iPNP.cpp
  30. +2
    -0
      src/operation/iPNP/source/iPNP.hh
  31. +106
    -0
      src/operation/iPNP/source/module/synthesis/PowerVia.cpp
  32. +2
    -1
      src/operation/iPNP/source/module/synthesis/PowerVia.hh
  33. +1
    -0
      src/operation/iPNP/source/module/tcl-cmd/CMakeLists.txt
  34. +94
    -0
      src/operation/iPNP/source/module/tcl-cmd/CmdAddVIA1.cc
  35. +31
    -7
      src/operation/iPNP/source/module/tcl-cmd/CmdRunPnp.cc
  36. +12
    -4
      src/operation/iPNP/source/module/tcl-cmd/ShellCmd.hh
  37. +54
    -50
      src/operation/iRT/interface/RTInterface.cpp
  38. +5
    -1
      src/operation/iRT/interface/RTInterface.hpp
  39. +77
    -0
      src/operation/iRT/source/module/detailed_router/DetailedRouter.cpp
  40. +2
    -0
      src/operation/iRT/source/module/detailed_router/DetailedRouter.hpp
  41. +6
    -0
      src/operation/iRT/source/module/drc_engine/DRCEngine.cpp
  42. +75
    -8
      src/operation/iRT/source/module/layer_assigner/LayerAssigner.cpp
  43. +3
    -1
      src/operation/iRT/source/module/layer_assigner/LayerAssigner.hpp
  44. +51
    -38
      src/operation/iRT/source/module/layer_assigner/la_data_manager/LANode.hpp
  45. +81
    -0
      src/operation/iRT/source/module/pin_accessor/PinAccessor.cpp
  46. +2
    -0
      src/operation/iRT/source/module/pin_accessor/PinAccessor.hpp
  47. +87
    -17
      src/operation/iRT/source/module/space_router/SpaceRouter.cpp
  48. +3
    -1
      src/operation/iRT/source/module/space_router/SpaceRouter.hpp
  49. +51
    -38
      src/operation/iRT/source/module/space_router/sr_data_manager/SRNode.hpp
  50. +21
    -30
      src/operation/iRT/source/module/supply_analyzer/SupplyAnalyzer.cpp
  51. +13
    -9
      src/operation/iRT/source/module/supply_analyzer/sa_data_manager/SAComParam.hpp
  52. +73
    -7
      src/operation/iRT/source/module/topology_generator/TopologyGenerator.cpp
  53. +3
    -1
      src/operation/iRT/source/module/topology_generator/TopologyGenerator.hpp
  54. +51
    -38
      src/operation/iRT/source/module/topology_generator/tg_data_manager/TGNode.hpp
  55. +76
    -0
      src/operation/iRT/source/module/track_assigner/TrackAssigner.cpp
  56. +2
    -0
      src/operation/iRT/source/module/track_assigner/TrackAssigner.hpp
  57. +75
    -0
      src/operation/iRT/source/module/violation_reporter/ViolationReporter.cpp
  58. +2
    -0
      src/operation/iRT/source/module/violation_reporter/ViolationReporter.hpp
  59. +1
    -0
      src/operation/iRT/test/CMakeLists.txt
  60. +5
    -0
      src/operation/iRT/test/test_json/CMakeLists.txt
  61. +32
    -0
      src/operation/iRT/test/test_json/test_json.cpp
  62. +140
    -5
      src/operation/iSTA/api/TimingEngine.cc
  63. +23
    -3
      src/operation/iSTA/api/TimingEngine.hh
  64. +3
    -0
      src/operation/iSTA/source/module/delay/ElmoreDelayCalc.hh
  65. +37
    -0
      src/operation/iSTA/test/DelayTest.cc
  66. +1
    -1
      src/operation/iSTA/test/main.cc
  67. +1
    -0
      src/utility/CMakeLists.txt
  68. +28
    -0
      src/utility/notification/CMakeLists.txt
  69. +358
    -0
      src/utility/notification/NotificationUtility.cpp
  70. +226
    -0
      src/utility/notification/NotificationUtility.h
  71. +273
    -0
      src/utility/notification/README.md

+ 1
- 1
Dockerfile View File

@@ -1,5 +1,5 @@
# syntax=docker/dockerfile:1.5-labs
ARG BASE_IMAGE=ubuntu:20.04
ARG BASE_IMAGE=ubuntu:22.04
FROM ${BASE_IMAGE}
LABEL maintainer="harry0789@qq.com"



+ 1
- 1
build.sh View File

@@ -182,7 +182,7 @@ install_dependencies_apt()
g++-10 cmake ninja-build \
tcl-dev libgflags-dev libgoogle-glog-dev libboost-all-dev libgtest-dev flex\
libeigen3-dev libunwind-dev libmetis-dev libgmp-dev bison rustc cargo\
libhwloc-dev libcairo2-dev
libhwloc-dev libcairo2-dev libcurl4-openssl-dev
exit 0
else
echo -e "${red}apt-get not found, pleas make sure you were running on Debian-Based Linux distribution${clear}"


+ 1
- 0
src/database/data/design/db_property/IdbCutLayerLef58Property.h View File

@@ -207,6 +207,7 @@ class Lef58EnclosureEdge
void set_min_width(int32_t width) { _min_width = width; }
void set_max_width(int32_t width) { _max_width = width; }
void set_par_length(int32_t par_length) { _par_length = par_length; }
void set_par_within(int32_t par_within) { _par_within = par_within; }
void set_except_extracut(bool has_except_extracut) { _except_extracut = has_except_extracut; }
void set_except_extracut_cutwithin(int32_t cut_within) { _cut_within = cut_within; }
void set_except_twoedges(bool has_except_twoedges) { _except_two_edges = has_except_twoedges; }


+ 2
- 0
src/database/manager/builder/lef_builder/property_parser/cutlayer_parser.cpp View File

@@ -120,6 +120,7 @@ bool CutLayerParser::parse_lef58_enclosureedge(const std::string& value, IdbLaye
auto& width = boost::get<cutlayer_property::lef58_enclosureedge_width>(item._width_convex);
enclosure_edge->set_min_width(transUnitDB(width._min_width));
enclosure_edge->set_par_length(transUnitDB(width._par_length));
enclosure_edge->set_par_within(transUnitDB(width._par_within));
if (width._except_extracut == "EXCEPTEXTRACUT") {
enclosure_edge->set_except_extracut(true);
if (width._cut_within) {
@@ -179,6 +180,7 @@ bool CutLayerParser::parse_lef58_eolenclosure(const std::string& value, IdbLayer
cutlayer::Lef58EolEnclosure::Extension ext;
ext.set_backward_ext(transUnitDB(overhang._backward_ext.value()));
ext.set_forward_ext(transUnitDB(overhang._forward_ext.value()));
enclosure->set_extension(ext);
}
if (overhang._min_length) {
enclosure->set_min_length(transUnitDB(overhang._min_length.value()));


+ 2
- 2
src/database/manager/builder/lef_builder/property_parser/routinglayer_parser.cpp View File

@@ -237,9 +237,9 @@ bool RoutingLayerParser::parse_lef58_spacing_eol(const std::string& value, IdbLa
// cut.set_cut_to_metal_space(t);
// cut.set_cut_to_metal_space(transUnitDB(cut_._cut_to_metal_space));
cut.set_all_cuts(!cut_._all_cuts.empty());
cut.set_enclose_dist(transUnitDB(cut_._enclose_dist));
cut.set_cut_to_metal_space(transUnitDB(cut_._cut_to_metal_space));
spacing_eol->set_enclose_cut(cut);
spacing_eol->get_enclose_cut()->set_enclose_dist(transUnitDB(cut_._enclose_dist));
spacing_eol->get_enclose_cut()->set_cut_to_metal_space(transUnitDB(cut_._cut_to_metal_space));
}

data->add_spacing_eol(spacing_eol);


+ 64
- 36
src/feature/database/feature_irt.h View File

@@ -29,8 +29,15 @@ namespace ieda_feature {
struct PASummary
{
std::map<int32_t, int32_t> routing_access_point_num_map;
std::map<std::string, int32_t> type_access_point_num_map;
int32_t total_access_point_num = 0;
std::map<int32_t, double> routing_wire_length_map;
double total_wire_length = 0;
std::map<int32_t, int32_t> cut_via_num_map;
int32_t total_via_num = 0;
std::map<int32_t, int32_t> routing_patch_num_map;
int32_t total_patch_num = 0;
std::map<int32_t, int32_t> routing_violation_num_map;
int32_t total_violation_num = 0;
};

struct SASummary
@@ -41,64 +48,64 @@ struct SASummary

struct TGSummary
{
int32_t total_demand = 0;
int32_t total_overflow = 0;
double total_demand = 0;
double total_overflow = 0;
double total_wire_length = 0;
std::vector<ClockTiming> clocks_timing;
PowerInfo power_info;
std::map<std::string, std::map<std::string, double>> clock_timing;
std::map<std::string, double> power_map;
};

struct LASummary
{
std::map<int32_t, int32_t> routing_demand_map;
int32_t total_demand = 0;
std::map<int32_t, int32_t> routing_overflow_map;
int32_t total_overflow = 0;
std::map<int32_t, double> routing_demand_map;
double total_demand = 0;
std::map<int32_t, double> routing_overflow_map;
double total_overflow = 0;
std::map<int32_t, double> routing_wire_length_map;
double total_wire_length = 0;
std::map<int32_t, int32_t> cut_via_num_map;
int32_t total_via_num = 0;
std::vector<ClockTiming> clocks_timing;
PowerInfo power_info;
std::map<std::string, std::map<std::string, double>> clock_timing;
std::map<std::string, double> power_map;
};

struct ERSummary
struct SRSummary
{
std::map<int32_t, int32_t> routing_demand_map;
int32_t total_demand = 0;
std::map<int32_t, int32_t> routing_overflow_map;
int32_t total_overflow = 0;
std::map<int32_t, double> routing_demand_map;
double total_demand = 0;
std::map<int32_t, double> routing_overflow_map;
double total_overflow = 0;
std::map<int32_t, double> routing_wire_length_map;
double total_wire_length = 0;
std::map<int32_t, int32_t> cut_via_num_map;
int32_t total_via_num = 0;
std::vector<ClockTiming> clocks_timing;
PowerInfo power_info;
std::map<std::string, std::map<std::string, double>> clock_timing;
std::map<std::string, double> power_map;
};

struct GRSummary
struct TASummary
{
std::map<int32_t, int32_t> routing_demand_map;
int32_t total_demand = 0;
std::map<int32_t, int32_t> routing_overflow_map;
int32_t total_overflow = 0;
std::map<int32_t, double> routing_wire_length_map;
double total_wire_length = 0;
std::map<int32_t, int32_t> cut_via_num_map;
int32_t total_via_num = 0;
std::vector<ClockTiming> clocks_timing;
PowerInfo power_info;
std::map<int32_t, int32_t> routing_violation_num_map;
int32_t total_violation_num = 0;
};

struct TASummary
struct DRSummary
{
std::map<int32_t, double> routing_wire_length_map;
double total_wire_length = 0;
std::map<int32_t, int32_t> cut_via_num_map;
int32_t total_via_num = 0;
std::map<int32_t, int32_t> routing_patch_num_map;
int32_t total_patch_num = 0;
std::map<int32_t, int32_t> routing_violation_num_map;
int32_t total_violation_num = 0;
std::map<std::string, std::map<std::string, double>> clock_timing;
std::map<std::string, double> power_map;
};

struct DRSummary
struct VRSummary
{
std::map<int32_t, double> routing_wire_length_map;
double total_wire_length = 0;
@@ -106,22 +113,43 @@ struct DRSummary
int32_t total_via_num = 0;
std::map<int32_t, int32_t> routing_patch_num_map;
int32_t total_patch_num = 0;
std::map<int32_t, int32_t> routing_violation_num_map;
int32_t total_violation_num = 0;
std::vector<ClockTiming> clocks_timing;
PowerInfo power_info;
std::map<int32_t, std::map<std::string, int32_t>> within_net_routing_violation_type_num_map;
std::map<std::string, int32_t> within_net_violation_type_num_map;
std::map<int32_t, int32_t> within_net_routing_violation_num_map;
int32_t within_net_total_violation_num = 0;
std::map<int32_t, std::map<std::string, int32_t>> among_net_routing_violation_type_num_map;
std::map<std::string, int32_t> among_net_violation_type_num_map;
std::map<int32_t, int32_t> among_net_routing_violation_num_map;
int32_t among_net_total_violation_num = 0;
std::map<std::string, std::map<std::string, double>> clock_timing;
std::map<std::string, double> power_map;
};

struct ERSummary
{
std::map<int32_t, int32_t> routing_demand_map;
int32_t total_demand = 0;
std::map<int32_t, int32_t> routing_overflow_map;
int32_t total_overflow = 0;
std::map<int32_t, double> routing_wire_length_map;
double total_wire_length = 0;
std::map<int32_t, int32_t> cut_via_num_map;
int32_t total_via_num = 0;
std::map<std::string, std::map<std::string, double>> clock_timing;
std::map<std::string, double> power_map;
};

struct RTSummary
{
PASummary pa_summary;
std::map<int32_t, PASummary> iter_pa_summary_map;
SASummary sa_summary;
TGSummary tg_summary;
LASummary la_summary;
ERSummary er_summary;
std::map<int32_t, GRSummary> iter_gr_summary_map;
std::map<int32_t, SRSummary> iter_sr_summary_map;
TASummary ta_summary;
std::map<int32_t, DRSummary> iter_dr_summary_map;
VRSummary vr_summary;
ERSummary er_summary;
};

/// ###################################################################################///


+ 4
- 0
src/feature/parser/feature_parser_tools.cpp View File

@@ -40,6 +40,8 @@ json FeatureParser::buildSummaryRT()
{
json json_rt;

#if 0

RTSummary& rt_sum = _summary->get_summary_irt();

/// PA
@@ -237,6 +239,8 @@ json FeatureParser::buildSummaryRT()
}
json_rt["DR"] = json_dr_list;

#endif

return json_rt;
}



+ 4
- 4
src/interface/python/py_ista/py_ista.cpp View File

@@ -122,7 +122,7 @@ double getSegmentCapacitance(int layer_id, double segment_length) {
return capacitance;
}

bool makeRCTreeInnerNode(const std::string& net_name, int id, float cap)
std::string makeRCTreeInnerNode(const std::string& net_name, int id, float cap)
{
auto* timing_engine = ista::TimingEngine::getOrCreateTimingEngine();
auto* ista = ista::Sta::getOrCreateSta();
@@ -130,10 +130,10 @@ bool makeRCTreeInnerNode(const std::string& net_name, int id, float cap)
auto* rc_node = timing_engine->makeOrFindRCTreeNode(the_net, id);
rc_node->incrCap(cap);

return true;
return rc_node->get_name();
}

bool makeRCTreeObjNode(const std::string& pin_port_name, float cap) {
std::string makeRCTreeObjNode(const std::string& pin_port_name, float cap) {
auto* timing_engine = ista::TimingEngine::getOrCreateTimingEngine();
auto* ista = ista::Sta::getOrCreateSta();

@@ -143,7 +143,7 @@ bool makeRCTreeObjNode(const std::string& pin_port_name, float cap) {
auto* rc_node = timing_engine->makeOrFindRCTreeNode(the_pin_ports.front());
rc_node->incrCap(cap);

return true;
return rc_node->get_name();
}

bool makeRCTreeEdge(const std::string& net_name, std::string& node1, std::string& node2, float res) {


+ 2
- 2
src/interface/python/py_ista/py_ista.h View File

@@ -46,8 +46,8 @@ std::string getNetName(const std::string& pin_port_name);
double getSegmentResistance(int layer_id, double segment_length);
double getSegmentCapacitance(int layer_id, double segment_length);

bool makeRCTreeInnerNode(const std::string& net_name, int id, float cap);
bool makeRCTreeObjNode(const std::string& pin_port_name, float cap);
std::string makeRCTreeInnerNode(const std::string& net_name, int id, float cap);
std::string makeRCTreeObjNode(const std::string& pin_port_name, float cap);
bool makeRCTreeEdge(const std::string& net_name, std::string& node1, std::string& node2, float res);
bool updateRCTreeInfo(const std::string& net_name);
bool updateTiming();


+ 118
- 167
src/operation/iDRC/interface/DRCInterface.cpp View File

@@ -86,6 +86,7 @@ void DRCInterface::checkDef()
type_violation_map[ids_violation.violation_type].push_back(ids_violation);
}
printSummary(type_violation_map);
outputViolationJson(type_violation_map);
outputSummary(type_violation_map);
}

@@ -282,170 +283,42 @@ void DRCInterface::wrapRoutingDesignRule(RoutingLayer& routing_layer, idb::IdbLa
// EndOfLineSpacingRule
{
std::vector<EndOfLineSpacingRule>& end_of_line_spacing_rule_list = routing_layer.get_end_of_line_spacing_rule_list();
if (0) {
if (!idb_layer->get_lef58_spacing_eol_list().empty()) {
for (std::shared_ptr<idb::routinglayer::Lef58SpacingEol> idb_spacing_eol : idb_layer->get_lef58_spacing_eol_list()) {
EndOfLineSpacingRule end_of_line_spacing_rule;

end_of_line_spacing_rule.eol_spacing = idb_spacing_eol.get()->get_eol_space();
end_of_line_spacing_rule.eol_width = idb_spacing_eol.get()->get_eol_width();
end_of_line_spacing_rule.eol_within = idb_spacing_eol.get()->get_eol_within().value();

end_of_line_spacing_rule.has_ete = idb_spacing_eol.get()->get_end_to_end().has_value();
if (idb_spacing_eol.get()->get_end_to_end().has_value()) {
end_of_line_spacing_rule.ete_spacing = idb_spacing_eol.get()->get_end_to_end().value().get_end_to_end_space();
}
if (!idb_layer->get_lef58_spacing_eol_list().empty()) {
for (std::shared_ptr<idb::routinglayer::Lef58SpacingEol> idb_spacing_eol : idb_layer->get_lef58_spacing_eol_list()) {
EndOfLineSpacingRule end_of_line_spacing_rule;

end_of_line_spacing_rule.has_par = idb_spacing_eol.get()->get_parallel_edge().has_value();
if (idb_spacing_eol.get()->get_parallel_edge().has_value()) {
end_of_line_spacing_rule.has_subtrace_eol_width = idb_spacing_eol.get()->get_parallel_edge().value().is_subtract_eol_width();
end_of_line_spacing_rule.par_spacing = idb_spacing_eol.get()->get_parallel_edge().value().get_par_space();
end_of_line_spacing_rule.par_within = idb_spacing_eol.get()->get_parallel_edge().value().get_par_within();
end_of_line_spacing_rule.has_two_edges = idb_spacing_eol.get()->get_parallel_edge().value().is_two_edges();
end_of_line_spacing_rule.has_min_length = idb_spacing_eol.get()->get_parallel_edge().value().get_min_length().has_value();
if (idb_spacing_eol.get()->get_parallel_edge().value().get_min_length().has_value()) {
end_of_line_spacing_rule.min_length = idb_spacing_eol.get()->get_parallel_edge().value().get_min_length().value();
}
end_of_line_spacing_rule.has_same_metal = idb_spacing_eol.get()->get_parallel_edge().value().is_same_metal();
}
end_of_line_spacing_rule.eol_spacing = idb_spacing_eol.get()->get_eol_space();
end_of_line_spacing_rule.eol_width = idb_spacing_eol.get()->get_eol_width();
end_of_line_spacing_rule.eol_within = idb_spacing_eol.get()->get_eol_within().value();

end_of_line_spacing_rule.has_ete = idb_spacing_eol.get()->get_end_to_end().has_value();
if (idb_spacing_eol.get()->get_end_to_end().has_value()) {
end_of_line_spacing_rule.ete_spacing = idb_spacing_eol.get()->get_end_to_end().value().get_end_to_end_space();
}

end_of_line_spacing_rule.has_enclose_cut = idb_spacing_eol.get()->get_enclose_cut().has_value();
if (idb_spacing_eol.get()->get_enclose_cut().has_value()) {
end_of_line_spacing_rule.has_below
= idb_spacing_eol.get()->get_enclose_cut().value().get_direction() == idb::routinglayer::Lef58SpacingEol::Direction::kBelow;
end_of_line_spacing_rule.has_above
= idb_spacing_eol.get()->get_enclose_cut().value().get_direction() == idb::routinglayer::Lef58SpacingEol::Direction::kAbove;
end_of_line_spacing_rule.enclosed_dist = idb_spacing_eol.get()->get_enclose_cut().value().get_enclose_dist();
end_of_line_spacing_rule.cut_to_metal_spacing = idb_spacing_eol.get()->get_enclose_cut().value().get_cut_to_metal_space();
end_of_line_spacing_rule.has_all_cuts = idb_spacing_eol.get()->get_enclose_cut().value().is_all_cuts();
end_of_line_spacing_rule.has_par = idb_spacing_eol.get()->get_parallel_edge().has_value();
if (idb_spacing_eol.get()->get_parallel_edge().has_value()) {
end_of_line_spacing_rule.has_subtrace_eol_width = idb_spacing_eol.get()->get_parallel_edge().value().is_subtract_eol_width();
end_of_line_spacing_rule.par_spacing = idb_spacing_eol.get()->get_parallel_edge().value().get_par_space();
end_of_line_spacing_rule.par_within = idb_spacing_eol.get()->get_parallel_edge().value().get_par_within();
end_of_line_spacing_rule.has_two_edges = idb_spacing_eol.get()->get_parallel_edge().value().is_two_edges();
end_of_line_spacing_rule.has_min_length = idb_spacing_eol.get()->get_parallel_edge().value().get_min_length().has_value();
if (idb_spacing_eol.get()->get_parallel_edge().value().get_min_length().has_value()) {
end_of_line_spacing_rule.min_length = idb_spacing_eol.get()->get_parallel_edge().value().get_min_length().value();
}
end_of_line_spacing_rule_list.push_back(end_of_line_spacing_rule);
end_of_line_spacing_rule.has_same_metal = idb_spacing_eol.get()->get_parallel_edge().value().is_same_metal();
}

end_of_line_spacing_rule.has_enclose_cut = idb_spacing_eol.get()->get_enclose_cut().has_value();
if (idb_spacing_eol.get()->get_enclose_cut().has_value()) {
end_of_line_spacing_rule.has_below
= idb_spacing_eol.get()->get_enclose_cut().value().get_direction() == idb::routinglayer::Lef58SpacingEol::Direction::kBelow;
end_of_line_spacing_rule.has_above
= idb_spacing_eol.get()->get_enclose_cut().value().get_direction() == idb::routinglayer::Lef58SpacingEol::Direction::kAbove;
end_of_line_spacing_rule.enclosed_dist = idb_spacing_eol.get()->get_enclose_cut().value().get_enclose_dist();
end_of_line_spacing_rule.cut_to_metal_spacing = idb_spacing_eol.get()->get_enclose_cut().value().get_cut_to_metal_space();
end_of_line_spacing_rule.has_all_cuts = idb_spacing_eol.get()->get_enclose_cut().value().is_all_cuts();
}
exist_rule_set.insert(ViolationType::kEndOfLineSpacing);
}
} else {
{
// eol_rule_list.push_back({/*eol spacing*/ 140, /*eol width*/ 140, /*eol within*/ 50, /*ete*/ true, 160,
// /*has par*/ false, /*sub_eol*/ false, /*par space*/ 0, /*par within*/ 0, /* two egde*/ false, /*min length*/ false, 0,
// /*enclose cut*/ false, /*below */ false, /*above */ false, /*enclose dist */ 0, /*cut to metal space*/ 0, /*all cuts*/
// false,
// /*same metal*/ false});
EndOfLineSpacingRule end_of_line_spacing_rule;
end_of_line_spacing_rule.eol_spacing = 140;
end_of_line_spacing_rule.eol_width = 140;
end_of_line_spacing_rule.eol_within = 50;

end_of_line_spacing_rule.has_ete = true;
end_of_line_spacing_rule.ete_spacing = 160;

end_of_line_spacing_rule.has_par = false;
end_of_line_spacing_rule.has_subtrace_eol_width = false;
end_of_line_spacing_rule.par_spacing = 0;
end_of_line_spacing_rule.par_within = 0;
end_of_line_spacing_rule.has_two_edges = false;
end_of_line_spacing_rule.has_min_length = false;
end_of_line_spacing_rule.min_length = 0;
end_of_line_spacing_rule.has_same_metal = false;

end_of_line_spacing_rule.has_enclose_cut = false;
end_of_line_spacing_rule.has_below = false;
end_of_line_spacing_rule.has_above = false;
end_of_line_spacing_rule.enclosed_dist = 0;
end_of_line_spacing_rule.cut_to_metal_spacing = 0;
end_of_line_spacing_rule.has_all_cuts = false;
end_of_line_spacing_rule_list.push_back(end_of_line_spacing_rule);
}
{
// eol_rule_list.push_back({/*eol spacing*/ 160, /*eol width*/ 140, /*eol within*/ 50, /*ete*/ true, 160,
// /*has par*/ true, /*sub_eol*/ true, /*par space*/ 230, /*par within*/ 140, /* two egde*/ false, /*min length*/ true, 100,
// /*enclose cut*/ false, /*below */ false, /*above */ false, /*enclose dist */ 0, /*cut to metal space*/ 0, /*all cuts*/
// false,
// /*same metal*/ false});
EndOfLineSpacingRule end_of_line_spacing_rule;
end_of_line_spacing_rule.eol_spacing = 160;
end_of_line_spacing_rule.eol_width = 140;
end_of_line_spacing_rule.eol_within = 50;

end_of_line_spacing_rule.has_ete = true;
end_of_line_spacing_rule.ete_spacing = 160;

end_of_line_spacing_rule.has_par = true;
end_of_line_spacing_rule.has_subtrace_eol_width = true;
end_of_line_spacing_rule.par_spacing = 230;
end_of_line_spacing_rule.par_within = 140;
end_of_line_spacing_rule.has_two_edges = false;
end_of_line_spacing_rule.has_min_length = true;
end_of_line_spacing_rule.min_length = 100;
end_of_line_spacing_rule.has_same_metal = false;

end_of_line_spacing_rule.has_enclose_cut = false;
end_of_line_spacing_rule.has_below = false;
end_of_line_spacing_rule.has_above = false;
end_of_line_spacing_rule.enclosed_dist = 0;
end_of_line_spacing_rule.cut_to_metal_spacing = 0;
end_of_line_spacing_rule.has_all_cuts = false;
end_of_line_spacing_rule_list.push_back(end_of_line_spacing_rule);
}
{
// eol_rule_list.push_back({/*eol spacing*/ 200, /*eol width*/ 140, /*eol within*/ 50, /*ete*/ true, 160,
// /*has par*/ true, /*sub_eol*/ true, /*par space*/ 230, /*par within*/ 140, /* two egde*/ false, /*min length*/ true, 100,
// /*enclose cut*/ true, /*below */ true, /*above */ false, /*enclose dist */ 100, /*cut to metal space*/ 290, /*all cuts*/
// true,
// /*same metal*/ false});
EndOfLineSpacingRule end_of_line_spacing_rule;
end_of_line_spacing_rule.eol_spacing = 200;
end_of_line_spacing_rule.eol_width = 140;
end_of_line_spacing_rule.eol_within = 50;

end_of_line_spacing_rule.has_ete = true;
end_of_line_spacing_rule.ete_spacing = 160;

end_of_line_spacing_rule.has_par = true;
end_of_line_spacing_rule.has_subtrace_eol_width = true;
end_of_line_spacing_rule.par_spacing = 230;
end_of_line_spacing_rule.par_within = 140;
end_of_line_spacing_rule.has_two_edges = false;
end_of_line_spacing_rule.has_min_length = true;
end_of_line_spacing_rule.min_length = 100;
end_of_line_spacing_rule.has_same_metal = false;

end_of_line_spacing_rule.has_enclose_cut = true;
end_of_line_spacing_rule.has_below = true;
end_of_line_spacing_rule.has_above = false;
end_of_line_spacing_rule.enclosed_dist = 100;
end_of_line_spacing_rule.cut_to_metal_spacing = 290;
end_of_line_spacing_rule.has_all_cuts = true;
end_of_line_spacing_rule_list.push_back(end_of_line_spacing_rule);
}
{
// eol_rule_list.push_back({/*eol spacing*/ 230, /*eol width*/ 110, /*eol within*/ 0, /*ete*/ false, 0,
// /*has par*/ true, /*sub_eol*/ false, /*par space*/ 120, /*par within*/ 240, /* two egde*/ true, /*min length*/ true, 300,
// /*enclose cut*/ false, /*below */ false, /*above */ false, /*enclose dist */ 0, /*cut to metal space*/ 0, /*all cuts*/
// false,
// /*same metal*/ true});
EndOfLineSpacingRule end_of_line_spacing_rule;
end_of_line_spacing_rule.eol_spacing = 230;
end_of_line_spacing_rule.eol_width = 110;
end_of_line_spacing_rule.eol_within = 0;

end_of_line_spacing_rule.has_ete = false;
end_of_line_spacing_rule.ete_spacing = 0;

end_of_line_spacing_rule.has_par = true;
end_of_line_spacing_rule.has_subtrace_eol_width = false;
end_of_line_spacing_rule.par_spacing = 120;
end_of_line_spacing_rule.par_within = 240;
end_of_line_spacing_rule.has_two_edges = true;
end_of_line_spacing_rule.has_min_length = true;
end_of_line_spacing_rule.min_length = 300;
end_of_line_spacing_rule.has_same_metal = true;

end_of_line_spacing_rule.has_enclose_cut = false;
end_of_line_spacing_rule.has_below = false;
end_of_line_spacing_rule.has_above = false;
end_of_line_spacing_rule.enclosed_dist = 0;
end_of_line_spacing_rule.cut_to_metal_spacing = 0;
end_of_line_spacing_rule.has_all_cuts = false;
end_of_line_spacing_rule_list.push_back(end_of_line_spacing_rule);
}
exist_rule_set.insert(ViolationType::kEndOfLineSpacing);
@@ -563,13 +436,15 @@ void DRCInterface::wrapCutDesignRule(CutLayer& cut_layer, idb::IdbLayerCut* idb_
CutEOLSpacingRule& cut_eol_spacing_rule = cut_layer.get_cut_eol_spacing_rule();
if (idb_layer->get_lef58_eol_spacing().get() != nullptr) {
idb::cutlayer::Lef58EolSpacing* idb_eol_spacing = idb_layer->get_lef58_eol_spacing().get();

int32_t curr_eol_spacing = idb_eol_spacing->get_cut_spacing1();
int32_t curr_eol_prl = idb_eol_spacing->get_prl();
int32_t curr_eol_prl_spacing = idb_eol_spacing->get_cut_spacing2();
cut_eol_spacing_rule.curr_eol_spacing = curr_eol_spacing;
cut_eol_spacing_rule.curr_eol_prl = curr_eol_prl;
cut_eol_spacing_rule.curr_eol_prl_spacing = curr_eol_prl_spacing;
cut_eol_spacing_rule.eol_spacing = idb_eol_spacing->get_cut_spacing1();
cut_eol_spacing_rule.eol_prl = idb_eol_spacing->get_prl();
cut_eol_spacing_rule.eol_prl_spacing = idb_eol_spacing->get_cut_spacing2();
cut_eol_spacing_rule.eol_width = idb_eol_spacing->get_eol_width();
cut_eol_spacing_rule.smaller_overhang = idb_eol_spacing->get_smaller_overhang();
cut_eol_spacing_rule.equal_overhang = idb_eol_spacing->get_equal_overhang();
cut_eol_spacing_rule.side_ext = idb_eol_spacing->get_side_ext();
cut_eol_spacing_rule.backward_ext = idb_eol_spacing->get_backward_ext();
cut_eol_spacing_rule.span_length = idb_eol_spacing->get_span_length();
exist_rule_set.insert(ViolationType::kCutEOLSpacing);
}
}
@@ -597,6 +472,50 @@ void DRCInterface::wrapCutDesignRule(CutLayer& cut_layer, idb::IdbLayerCut* idb_
}
}
}
// EnclosureEdgeRule
{
std::vector<EnclosureEdgeRule>& enclosure_edge_rule_list = cut_layer.get_enclosure_edge_rule_list();
if (!idb_layer->get_lef58_enclosure_edge_list().empty()) {
for (std::shared_ptr<idb::cutlayer::Lef58EnclosureEdge>& idb_enclosure_edge : idb_layer->get_lef58_enclosure_edge_list()) {
if (idb_enclosure_edge.get()->get_convex_corners().has_value()) {
continue;
}
EnclosureEdgeRule enclosure_edge_rule;
enclosure_edge_rule.has_above = (idb_enclosure_edge.get()->get_direction() == idb::cutlayer::Lef58EnclosureEdge::Direction::kAbove);
enclosure_edge_rule.has_below = (idb_enclosure_edge.get()->get_direction() == idb::cutlayer::Lef58EnclosureEdge::Direction::kBelow);
enclosure_edge_rule.overhang = idb_enclosure_edge.get()->get_overhang();
enclosure_edge_rule.min_width = idb_enclosure_edge.get()->get_min_width().value();
enclosure_edge_rule.par_length = idb_enclosure_edge.get()->get_par_length().value();
enclosure_edge_rule.par_within = idb_enclosure_edge.get()->get_par_within().value();
enclosure_edge_rule.has_except_two_edges = idb_enclosure_edge.get()->has_except_twoedges();
enclosure_edge_rule_list.push_back(enclosure_edge_rule);
}
exist_rule_set.insert(ViolationType::kEnclosureEdge);
}
}
// EnclosureParallelRule
{
EnclosureParallelRule& enclosure_parallel_rule = cut_layer.get_enclosure_parallel_rule();
if (idb_layer->get_lef58_eol_enclosure().get() != nullptr) {
idb::cutlayer::Lef58EolEnclosure* idb_eol_enclosure = idb_layer->get_lef58_eol_enclosure().get();
enclosure_parallel_rule.eol_width = idb_eol_enclosure->get_eol_width();
enclosure_parallel_rule.has_above = (idb_eol_enclosure->get_direction() == idb::cutlayer::Lef58EolEnclosure::Direction::kAbove);
enclosure_parallel_rule.has_below = (idb_eol_enclosure->get_direction() == idb::cutlayer::Lef58EolEnclosure::Direction::kBelow);
enclosure_parallel_rule.overhang = idb_eol_enclosure->get_overhang();
if (idb_eol_enclosure->get_par_space().has_value()) {
enclosure_parallel_rule.par_spacing = idb_eol_enclosure->get_par_space().value();
}
if (idb_eol_enclosure->get_extension().has_value()) {
enclosure_parallel_rule.backward_ext = idb_eol_enclosure->get_extension().value().get_backward_ext();
enclosure_parallel_rule.forward_ext = idb_eol_enclosure->get_extension().value().get_forward_ext();
}
enclosure_parallel_rule.has_min_length = idb_eol_enclosure->get_min_length().has_value();
if (idb_eol_enclosure->get_min_length().has_value()) {
enclosure_parallel_rule.min_length = idb_eol_enclosure->get_min_length().value();
}
exist_rule_set.insert(ViolationType::kEnclosureParallel);
}
}
// SameLayerCutSpacingRule
{
SameLayerCutSpacingRule& same_layer_cut_spacing_rule = cut_layer.get_same_layer_cut_spacing_rule();
@@ -1034,6 +953,38 @@ void DRCInterface::printSummary(std::map<std::string, std::vector<ids::Violation
DRCUTIL.printTableList({type_violation_map_table});
}

void DRCInterface::outputViolationJson(std::map<std::string, std::vector<ids::Violation>>& type_violation_map)
{
std::vector<RoutingLayer>& routing_layer_list = DRCDM.getDatabase().get_routing_layer_list();
std::vector<CutLayer>& cut_layer_list = DRCDM.getDatabase().get_cut_layer_list();
std::map<int32_t, std::vector<int32_t>>& cut_to_adjacent_routing_map = DRCDM.getDatabase().get_cut_to_adjacent_routing_map();
std::string& temp_directory_path = DRCDM.getConfig().temp_directory_path;

std::vector<idb::IdbNet*>& idb_net_list = dmInst->get_idb_def_service()->get_design()->get_net_list()->get_net_list();

std::vector<nlohmann::json> violation_json_list;
for (auto& [type, violation_list] : type_violation_map) {
for (ids::Violation& violation : violation_list) {
nlohmann::json violation_json;
violation_json["type"] = violation.violation_type;

int32_t layer_idx = violation.layer_idx;
if (!violation.is_routing) {
std::vector<int32_t>& routing_layer_idx_list = cut_to_adjacent_routing_map[layer_idx];
layer_idx = *std::min_element(routing_layer_idx_list.begin(), routing_layer_idx_list.end());
}
violation_json["shape"] = {violation.ll_x, violation.ll_y, violation.ur_x, violation.ur_y, routing_layer_list[layer_idx].get_layer_name()};
for (int32_t net_idx : violation.violation_net_set) {
violation_json["net"].push_back(idb_net_list[net_idx]->get_net_name());
}
violation_json_list.push_back(violation_json);
}
}
std::ofstream* violation_json_file = DRCUTIL.getOutputFileStream(DRCUTIL.getString(temp_directory_path, "violation_map.json"));
(*violation_json_file) << violation_json_list;
DRCUTIL.closeFileStream(violation_json_file);
}

void DRCInterface::outputSummary(std::map<std::string, std::vector<ids::Violation>>& type_violation_map)
{
std::vector<RoutingLayer>& routing_layer_list = DRCDM.getDatabase().get_routing_layer_list();


+ 1
- 0
src/operation/iDRC/interface/DRCInterface.hpp View File

@@ -93,6 +93,7 @@ class DRCInterface
bool isSkipping(idb::IdbNet* idb_net);
std::vector<ids::Shape> buildResultShapeList();
void printSummary(std::map<std::string, std::vector<ids::Violation>>& type_violation_map);
void outputViolationJson(std::map<std::string, std::vector<ids::Violation>>& type_violation_map);
void outputSummary(std::map<std::string, std::vector<ids::Violation>>& type_violation_map);
DRCShape convertToDRCShape(const ids::Shape& ids_shape);
#endif


+ 4
- 4
src/operation/iDRC/source/data_manager/advance/Config.hpp View File

@@ -27,12 +27,12 @@ class Config
~Config() = default;
//////////////////////////////////////////////
// ********** DRC ********** //
std::string temp_directory_path; // required
int32_t thread_number; // optional
std::string golden_directory_path; // optional
std::string temp_directory_path; // required
int32_t thread_number; // optional
std::string golden_directory_path; // optional
/////////////////////////////////////////////
// ********** DRC ********** //
std::string log_file_path; // building
std::string log_file_path; // building
// ********** RuleValidator ********** //
std::string rv_temp_directory_path; // building
// ********** GDSPlotter ********** //


+ 6
- 0
src/operation/iDRC/source/data_manager/advance/CutLayer.hpp View File

@@ -19,6 +19,8 @@
#include "CutEOLSpacingRule.hpp"
#include "DRCHeader.hpp"
#include "DifferentLayerCutSpacingRule.hpp"
#include "EnclosureEdgeRule.hpp"
#include "EnclosureParallelRule.hpp"
#include "PlanarRect.hpp"
#include "SameLayerCutSpacingRule.hpp"

@@ -35,7 +37,9 @@ class CutLayer
std::string& get_layer_name() { return _layer_name; }
CutEOLSpacingRule& get_cut_eol_spacing_rule() { return _cut_eol_spacing_rule; }
DifferentLayerCutSpacingRule& get_different_layer_cut_spacing_rule() { return _different_layer_cut_spacing_rule; }
std::vector<EnclosureEdgeRule>& get_enclosure_edge_rule_list() { return _enclosure_edge_rule_list; }
SameLayerCutSpacingRule& get_same_layer_cut_spacing_rule() { return _same_layer_cut_spacing_rule; }
EnclosureParallelRule& get_enclosure_parallel_rule() { return _enclosure_parallel_rule; }
// setter
void set_layer_idx(const int32_t layer_idx) { _layer_idx = layer_idx; }
void set_layer_order(const int32_t layer_order) { _layer_order = layer_order; }
@@ -47,7 +51,9 @@ class CutLayer
std::string _layer_name;
CutEOLSpacingRule _cut_eol_spacing_rule;
DifferentLayerCutSpacingRule _different_layer_cut_spacing_rule;
std::vector<EnclosureEdgeRule> _enclosure_edge_rule_list;
SameLayerCutSpacingRule _same_layer_cut_spacing_rule;
EnclosureParallelRule _enclosure_parallel_rule;
};

} // namespace idrc

+ 24
- 0
src/operation/iDRC/source/data_manager/basic/PlanarRect.hpp View File

@@ -17,6 +17,7 @@
#pragma once

#include "Direction.hpp"
#include "Orientation.hpp"
#include "PlanarCoord.hpp"
#include "Segment.hpp"

@@ -68,6 +69,7 @@ class PlanarRect
inline int32_t getPerimeter() const;
inline double getArea() const;
inline std::vector<Segment<PlanarCoord>> getEdgeList() const;
inline Segment<PlanarCoord> getOrientEdge(Orientation orient) const;
inline PlanarCoord getMidPoint() const;
inline bool isIncorrect() const;

@@ -149,6 +151,28 @@ inline std::vector<Segment<PlanarCoord>> PlanarRect::getEdgeList() const
return segment_list;
}

inline Segment<PlanarCoord> PlanarRect::getOrientEdge(Orientation orient) const
{
int32_t ll_x = _ll.get_x();
int32_t ll_y = _ll.get_y();
int32_t ur_x = _ur.get_x();
int32_t ur_y = _ur.get_y();

Segment<PlanarCoord> segment;
if (orient == Orientation::kEast) {
segment = Segment<PlanarCoord>(PlanarCoord(ur_x, ll_y), _ur);
} else if (orient == Orientation::kSouth) {
segment = Segment<PlanarCoord>(_ll, PlanarCoord(ur_x, ll_y));
} else if (orient == Orientation::kWest) {
segment = Segment<PlanarCoord>(_ll, PlanarCoord(ll_x, ur_y));
} else if (orient == Orientation::kNorth) {
segment = Segment<PlanarCoord>(PlanarCoord(ll_x, ur_y), _ur);
} else {
DRCLOG.error(Loc::current(), "The orient is error!");
}
return segment;
}

inline PlanarCoord PlanarRect::getMidPoint() const
{
return PlanarCoord((get_ll_x() + get_ur_x()) / 2, (get_ll_y() + get_ur_y()) / 2);


+ 9
- 3
src/operation/iDRC/source/data_manager/design_rule/CutEOLSpacingRule.hpp View File

@@ -25,9 +25,15 @@ class CutEOLSpacingRule
public:
CutEOLSpacingRule() = default;
~CutEOLSpacingRule() = default;
int32_t curr_eol_spacing = -1;
int32_t curr_eol_prl = -1;
int32_t curr_eol_prl_spacing = -1;
int32_t eol_spacing = -1;
int32_t eol_prl = -1;
int32_t eol_prl_spacing = -1;
int32_t eol_width = -1;
int32_t smaller_overhang = -1;
int32_t equal_overhang = -1;
int32_t side_ext = -1;
int32_t backward_ext = -1;
int32_t span_length = -1;
};

} // namespace idrc

+ 7
- 0
src/operation/iDRC/source/data_manager/design_rule/EnclosureEdgeRule.hpp View File

@@ -25,6 +25,13 @@ class EnclosureEdgeRule
public:
EnclosureEdgeRule() = default;
~EnclosureEdgeRule() = default;
bool has_above = false;
bool has_below = false;
int32_t overhang = -1;
int32_t min_width = -1;
int32_t par_length = -1;
int32_t par_within = -1;
bool has_except_two_edges = false;
};

} // namespace idrc

+ 9
- 0
src/operation/iDRC/source/data_manager/design_rule/EnclosureParallelRule.hpp View File

@@ -25,6 +25,15 @@ class EnclosureParallelRule
public:
EnclosureParallelRule() = default;
~EnclosureParallelRule() = default;
int32_t eol_width;
bool has_above;
bool has_below;
int32_t overhang;
/**/ int32_t par_spacing;
/**/ int32_t backward_ext;
/**/ int32_t forward_ext;
bool has_min_length;
/**/ int32_t min_length;
};

} // namespace idrc

+ 247
- 387
src/operation/iDRC/source/module/rule_validator/rv_design_rule/CutEOLSpacing.cpp View File

@@ -19,443 +19,307 @@
namespace idrc {
void RuleValidator::verifyCutEOLSpacing(RVBox& rv_box)
{
/*
对应的规则如下:
PROPERTY LEF58_EOLSPACING "
EOLSPACING 0.08 0.09 CUTCLASS VSINGLECUT TO VDOUBLECUT 0.085 0.09 ENDWIDTH 0.07 PRL -0.04
ENCLOSURE 0.04 0.00
EXTENSION 0.065 0.12 SPANLENGTH 0.055 ; " ;
*/
// 规则定义
int32_t cut_spacing_a = 80 * 2;
int32_t cut_spacing_b = 90 * 2;

int32_t signle_double_cut_spacing_a = 85 * 2; // double cut暂时不考虑
int32_t signle_double_cut_spacing_b = 90 * 2; // double cut暂时不考虑

int32_t eol_width = 70 * 2; // EOL edge的长度 要小于这个值
int32_t prl = -1 * 40 * 2; // spacing的prl
int32_t smaller_overhang = 40 * 2; // 有一个overhang小于这个值
int32_t equal_overhang = 0; // 正交边(相邻边)的overhang等于这个值

int32_t side_ext = 65 * 2; //
int32_t backward_ext = 120 * 2; //
int32_t span_length = 55 * 2; //

#if 1
auto update_cut_overhang = [](std::vector<Segment<PlanarCoord>>& overhang_list, PlanarRect& polygon_rect) {
PlanarCoord ll = polygon_rect.get_ll();
PlanarCoord ur = polygon_rect.get_ur();
PlanarCoord ul = PlanarCoord(ll.get_x(), ur.get_y());
PlanarCoord lr = PlanarCoord(ur.get_x(), ll.get_y());
// 东南西北
Segment<PlanarCoord> east_segment(lr, ur);
Segment<PlanarCoord> south_segment(ll, lr);
Segment<PlanarCoord> west_segment(ll, ul);
Segment<PlanarCoord> north_segment(ul, ur);
if (overhang_list.empty()) {
overhang_list.push_back(east_segment);
overhang_list.push_back(south_segment);
overhang_list.push_back(west_segment);
overhang_list.push_back(north_segment);
} else {
if (east_segment.get_first().get_x() >= overhang_list[0].get_first().get_x()) {
overhang_list[0] = east_segment;
}
if (south_segment.get_first().get_y() <= overhang_list[1].get_first().get_y()) {
overhang_list[1] = south_segment;
}
if (west_segment.get_first().get_x() <= overhang_list[2].get_first().get_x()) {
overhang_list[2] = west_segment;
}
if (north_segment.get_first().get_y() >= overhang_list[3].get_first().get_y()) {
overhang_list[3] = north_segment;
}
}
};

auto is_rect_interact_polygon = [](GTLRectInt query_rect, GTLPolySetInt polygon_set, bool is_consider_egde) {
GTLPolySetInt query_rect_set;
query_rect_set += query_rect;
if (is_consider_egde) {
polygon_set.interact(query_rect_set);
} else {
polygon_set &= query_rect_set;
}

return gtl::area(polygon_set) > 0;
};

auto gen_eol_search_rect = [&](PlanarRect& cut_wire_rect, int egde_dir) {
std::vector<PlanarRect> search_rect_list; // 0
int32_t wire_llx = cut_wire_rect.get_ll_x();
int32_t wire_lly = cut_wire_rect.get_ll_y();
int32_t wire_urx = cut_wire_rect.get_ur_x();
int32_t wire_ury = cut_wire_rect.get_ur_y();
switch (egde_dir) {
case 0:
return std::vector<PlanarRect>{PlanarRect(wire_urx - backward_ext, wire_ury, wire_urx, wire_ury + side_ext),
PlanarRect(wire_urx - backward_ext, wire_lly - side_ext, wire_urx, wire_lly)};
break;
case 1:
return std::vector<PlanarRect>{PlanarRect(wire_urx, wire_lly, wire_urx + side_ext, wire_lly + backward_ext),
PlanarRect(wire_llx - side_ext, wire_lly, wire_llx, wire_lly + backward_ext)};
break;
case 2:
return std::vector<PlanarRect>{PlanarRect(wire_llx, wire_lly - side_ext, wire_llx + backward_ext, wire_lly),
PlanarRect(wire_llx, wire_ury, wire_llx + backward_ext, wire_ury + side_ext)};
break;
case 3:
return std::vector<PlanarRect>{PlanarRect(wire_llx - side_ext, wire_ury - backward_ext, wire_llx, wire_ury),
PlanarRect(wire_urx, wire_ury - backward_ext, wire_urx + side_ext, wire_ury)};
break;
default:
break;
}
return std::vector<PlanarRect>{};
};
#endif

std::vector<int32_t> cut_eol_spacing_layers = {1, 2, 3, 4, 5, 6};
// 基础数据
std::vector<Violation>& violation_list = rv_box.get_violation_list();
std::vector<RoutingLayer>& routing_layer_list = DRCDM.getDatabase().get_routing_layer_list();
std::vector<CutLayer>& cut_layer_list = DRCDM.getDatabase().get_cut_layer_list();
std::map<int32_t, std::vector<int32_t>>& routing_to_adjacent_cut_map = DRCDM.getDatabase().get_routing_to_adjacent_cut_map();
std::map<int32_t, std::vector<int32_t>>& cut_to_adjacent_routing_map = DRCDM.getDatabase().get_cut_to_adjacent_routing_map();

std::map<int32_t, bgi::rtree<std::pair<BGRectInt, int32_t>, bgi::quadratic<16>>> cut_layer_all_query_tree;

std::map<int32_t, bgi::rtree<BGRectInt, bgi::quadratic<16>>> routing_layer_query_tree; // 所有的shape都融合在一起
std::map<int32_t, GTLPolySetInt> routing_layer_gtl_poly_set;

for (DRCShape* rect : rv_box.get_drc_env_shape_list()) {
if (rect->get_is_routing()) {
int32_t layer_idx = rect->get_layer_idx();
int32_t net_idx = rect->get_net_idx();
routing_layer_gtl_poly_set[layer_idx] += DRCUTIL.convertToGTLRectInt(rect->get_rect());
} else {
cut_layer_all_query_tree[rect->get_layer_idx()].insert(std::make_pair(DRCUTIL.convertToBGRectInt(rect->get_rect()), rect->get_net_idx()));
std::map<int32_t, GTLPolySetInt> routing_gtl_poly_set_map;
{
for (DRCShape* drc_shape : rv_box.get_drc_env_shape_list()) {
if (drc_shape->get_is_routing()) {
routing_gtl_poly_set_map[drc_shape->get_layer_idx()] += DRCUTIL.convertToGTLRectInt(drc_shape->get_rect());
}
}
for (DRCShape* drc_shape : rv_box.get_drc_result_shape_list()) {
if (drc_shape->get_is_routing()) {
routing_gtl_poly_set_map[drc_shape->get_layer_idx()] += DRCUTIL.convertToGTLRectInt(drc_shape->get_rect());
}
}
}

for (DRCShape* rect : rv_box.get_drc_result_shape_list()) {
if (rect->get_is_routing()) {
int32_t layer_idx = rect->get_layer_idx();
int32_t net_idx = rect->get_net_idx();
routing_layer_gtl_poly_set[layer_idx] += DRCUTIL.convertToGTLRectInt(rect->get_rect());
} else {
cut_layer_all_query_tree[rect->get_layer_idx()].insert(std::make_pair(DRCUTIL.convertToBGRectInt(rect->get_rect()), rect->get_net_idx()));
std::map<int32_t, bgi::rtree<BGRectInt, bgi::quadratic<16>>> routing_bg_rtree_map;
for (auto& [routing_layer_idx, gtl_poly_set] : routing_gtl_poly_set_map) {
std::vector<GTLRectInt> gtl_rect_list;
gtl::get_max_rectangles(gtl_rect_list, gtl_poly_set);
for (GTLRectInt& gtl_rect : gtl_rect_list) {
routing_bg_rtree_map[routing_layer_idx].insert(DRCUTIL.convertToBGRectInt(gtl_rect));
}
}

// 用max rect作为被查找的
for (auto& [routing_layer_idx, gtl_poly_set] : routing_layer_gtl_poly_set) {
std::vector<GTLRectInt> rect_list;
gtl::get_max_rectangles(rect_list, gtl_poly_set);
for (GTLRectInt& rect : rect_list) {
routing_layer_query_tree[routing_layer_idx].insert(DRCUTIL.convertToBGRectInt(rect));
std::map<int32_t, bgi::rtree<std::pair<BGRectInt, int32_t>, bgi::quadratic<16>>> cut_bg_rtree_map;
{
for (DRCShape* drc_shape : rv_box.get_drc_env_shape_list()) {
if (!drc_shape->get_is_routing()) {
cut_bg_rtree_map[drc_shape->get_layer_idx()].insert(std::make_pair(DRCUTIL.convertToBGRectInt(drc_shape->get_rect()), drc_shape->get_net_idx()));
}
}
for (DRCShape* drc_shape : rv_box.get_drc_result_shape_list()) {
if (!drc_shape->get_is_routing()) {
cut_bg_rtree_map[drc_shape->get_layer_idx()].insert(std::make_pair(DRCUTIL.convertToBGRectInt(drc_shape->get_rect()), drc_shape->get_net_idx()));
}
}
}

for (auto& [routing_layer_idx, gtl_poly_set] : routing_layer_gtl_poly_set) {
int32_t cut_layer_idx = routing_layer_idx; // 对应routing layer底层的cut layer的idx

int32_t violation_routing_layer_idx = -1;
for (auto& [routing_layer_idx, gtl_poly_set] : routing_gtl_poly_set_map) {
int32_t cut_layer_idx = -1;
{
std::vector<int32_t>& routing_layer_idx_list = cut_to_adjacent_routing_map[cut_layer_idx];
violation_routing_layer_idx = *std::min_element(routing_layer_idx_list.begin(), routing_layer_idx_list.end());
std::vector<int32_t>& cut_layer_idx_list = routing_to_adjacent_cut_map[routing_layer_idx];
cut_layer_idx = *std::min_element(cut_layer_idx_list.begin(), cut_layer_idx_list.end());
}
if (DRCUTIL.exist(cut_eol_spacing_layers, cut_layer_idx) == false) {
continue; // 没有对应的cut layer 此时routing_layer_idx 刚好对应其下一层的cut layer idx
if (cut_to_adjacent_routing_map[cut_layer_idx].size() < 2) {
continue;
}

int32_t min_width = routing_layer_list[routing_layer_idx].get_minimum_width_rule().min_width;
CutLayer& cut_layer = cut_layer_list[cut_layer_idx];
int32_t eol_spacing = cut_layer.get_cut_eol_spacing_rule().eol_spacing;
int32_t eol_prl = cut_layer.get_cut_eol_spacing_rule().eol_prl;
int32_t eol_prl_spacing = cut_layer.get_cut_eol_spacing_rule().eol_prl_spacing;
int32_t eol_width = cut_layer.get_cut_eol_spacing_rule().eol_width;
int32_t smaller_overhang = cut_layer.get_cut_eol_spacing_rule().smaller_overhang;
int32_t equal_overhang = cut_layer.get_cut_eol_spacing_rule().equal_overhang;
int32_t side_ext = cut_layer.get_cut_eol_spacing_rule().side_ext;
int32_t backward_ext = cut_layer.get_cut_eol_spacing_rule().backward_ext;
int32_t span_length = cut_layer.get_cut_eol_spacing_rule().span_length;

std::vector<GTLHolePolyInt> gtl_hole_poly_list;
gtl_poly_set.get(gtl_hole_poly_list);

// polyset -> polygon -> maxrect -> cut
for (GTLHolePolyInt& gtl_hole_poly : gtl_hole_poly_list) {
// 获得polygon 的信息
int32_t coord_size = static_cast<int32_t>(gtl_hole_poly.size());
if (coord_size < 4) {
continue;
}
std::vector<PlanarCoord> coord_list;
for (auto iter = gtl_hole_poly.begin(); iter != gtl_hole_poly.end(); iter++) {
coord_list.push_back(DRCUTIL.convertToPlanarCoord(*iter));
}
std::vector<bool> convex_corner_list;
std::vector<int32_t> edge_length_list;
std::vector<Segment<PlanarCoord>> edge_list;
for (int32_t i = 0; i < coord_size; i++) {
PlanarCoord& pre_coord = coord_list[getIdx(i - 1, coord_size)];
PlanarCoord& curr_coord = coord_list[i];
PlanarCoord& post_coord = coord_list[getIdx(i + 1, coord_size)];
convex_corner_list.push_back(DRCUTIL.isConvexCorner(DRCUTIL.getRotation(gtl_hole_poly), pre_coord, curr_coord, post_coord));
edge_length_list.push_back(DRCUTIL.getManhattanDistance(pre_coord, curr_coord));
edge_list.push_back(Segment<PlanarCoord>(pre_coord, curr_coord));
}
std::vector<int32_t> edge_length_list;
std::set<int32_t> eol_edge_idx_set;
for (int32_t i = 0; i < coord_size; i++) {
if (convex_corner_list[getIdx(i - 1, coord_size)] && convex_corner_list[i]) {
eol_edge_idx_set.insert(i);
}
}

// 获得cut
// net_idx -> map(rect,overhang_segment)
struct RectCompare // cut rect 作为key
{
bool operator()(const PlanarRect& a, const PlanarRect& b) const
{
if (a.get_ll_x() != b.get_ll_x()) {
return a.get_ll_x() < b.get_ll_x();
} else if (a.get_ll_y() != b.get_ll_y()) {
return a.get_ll_y() < b.get_ll_y();
} else if (a.get_ur_x() != b.get_ur_x()) {
return a.get_ur_x() < b.get_ur_x();
}
return a.get_ur_y() < b.get_ur_y();
std::vector<PlanarCoord> coord_list;
for (auto iter = gtl_hole_poly.begin(); iter != gtl_hole_poly.end(); iter++) {
coord_list.push_back(DRCUTIL.convertToPlanarCoord(*iter));
}
};
std::map<int32_t, std::map<PlanarRect, std::vector<Segment<PlanarCoord>>, RectCompare>> net_cut_overhang_list; // 记录overhang
std::map<int32_t, std::map<PlanarRect, std::vector<PlanarRect>, RectCompare>> net_cut_metal_rect_list; // 记录cut的metal rect --和span rect open overlap
std::map<int32_t, std::map<PlanarRect, std::vector<PlanarRect>, RectCompare>> net_cut_span_rect_list; // cut 用来算span的rect --和cut open overlap
std::vector<GTLRectInt> polygon_rect_list;
gtl::get_max_rectangles(polygon_rect_list, gtl_hole_poly);
for (GTLRectInt& gtl_polygon_rect : polygon_rect_list) {
PlanarRect polygon_rect = DRCUTIL.convertToPlanarRect(gtl_polygon_rect);
if (polygon_rect.getWidth() < min_width) {
continue;
std::vector<bool> convex_corner_list;
for (int32_t i = 0; i < coord_size; i++) {
PlanarCoord& pre_coord = coord_list[getIdx(i - 1, coord_size)];
PlanarCoord& curr_coord = coord_list[i];
PlanarCoord& post_coord = coord_list[getIdx(i + 1, coord_size)];
convex_corner_list.push_back(DRCUTIL.isConvexCorner(DRCUTIL.getRotation(gtl_hole_poly), pre_coord, curr_coord, post_coord));
edge_list.push_back(Segment<PlanarCoord>(pre_coord, curr_coord));
edge_length_list.push_back(DRCUTIL.getManhattanDistance(pre_coord, curr_coord));
}
// 查出所有的cut
std::vector<std::pair<BGRectInt, int32_t>> bg_cut_result;
cut_layer_all_query_tree[cut_layer_idx].query(bgi::intersects(DRCUTIL.convertToBGRectInt(polygon_rect)), std::back_inserter(bg_cut_result));
for (auto& [bg_cut, net_idx] : bg_cut_result) {
PlanarRect cut_rect = DRCUTIL.convertToPlanarRect(bg_cut);
if (DRCUTIL.isOpenOverlap(cut_rect, polygon_rect)) {
net_cut_span_rect_list[net_idx][cut_rect].push_back(polygon_rect);
}
// 更新cut的overhang segment
if (DRCUTIL.isInside(polygon_rect, cut_rect)) {
update_cut_overhang(net_cut_overhang_list[net_idx][cut_rect], polygon_rect);
for (int32_t i = 0; i < coord_size; i++) {
if (convex_corner_list[getIdx(i - 1, coord_size)] && convex_corner_list[i]) {
eol_edge_idx_set.insert(i);
}
}
}

// 根据overhang进行cut spacing的检查
for (auto& [net_idx, cut_overhang_list] : net_cut_overhang_list) {
for (auto& [cut, overhang_list] : cut_overhang_list) {
// 更新cut的metal rect --和span rect open overlap
for (GTLRectInt& gtl_polygon_rect : polygon_rect_list) {
PlanarRect polygon_rect = DRCUTIL.convertToPlanarRect(gtl_polygon_rect);
for (PlanarRect& span_rect : net_cut_span_rect_list[net_idx][cut]) {
if (DRCUTIL.isOpenOverlap(polygon_rect, span_rect)) {
net_cut_metal_rect_list[net_idx][cut].push_back(polygon_rect);
continue;
}
}
}

// ENCLOSURE条件
bool is_enclosure = false;
std::vector<int32_t> overhang_dis_list(4, 0);
overhang_dis_list[0] = std::abs(overhang_list[0].get_first().get_x() - cut.get_ur_x()); // east
overhang_dis_list[1] = std::abs(overhang_list[1].get_first().get_y() - cut.get_ll_y()); // south
overhang_dis_list[2] = std::abs(overhang_list[2].get_first().get_x() - cut.get_ll_x()); // west
overhang_dis_list[3] = std::abs(overhang_list[3].get_first().get_y() - cut.get_ur_y()); // north
for (int32_t i = 0; i < overhang_dis_list.size(); i++) {
int32_t pre_idx = getIdx(i - 1, overhang_dis_list.size());
int32_t post_idx = getIdx(i + 1, overhang_dis_list.size());
if (overhang_dis_list[i] < smaller_overhang && (overhang_dis_list[pre_idx] == equal_overhang || overhang_dis_list[post_idx] == equal_overhang)) {
is_enclosure = true;
}
std::map<int32_t, std::map<PlanarRect, std::vector<Segment<PlanarCoord>>, CmpPlanarRectByXASC>> net_cut_rect_overhang_map;
std::map<int32_t, std::map<PlanarRect, std::vector<PlanarRect>, CmpPlanarRectByXASC>> net_cut_rect_span_rect_map;
std::map<int32_t, std::map<PlanarRect, std::vector<PlanarRect>, CmpPlanarRectByXASC>> net_cut_rect_routing_rect_map;
{
std::vector<GTLRectInt> gtl_rect_list;
gtl::get_max_rectangles(gtl_rect_list, gtl_hole_poly);
for (GTLRectInt& gtl_rect : gtl_rect_list) {
PlanarRect routing_rect = DRCUTIL.convertToPlanarRect(gtl_rect);
if (routing_rect.getWidth() < routing_layer_list[routing_layer_idx].get_minimum_width_rule().min_width) {
continue;
}

// eol edge 条件
// 再判断条件2,eol edge是否满足
bool is_eol_edge = false;
std::vector<bool> is_eol_edge_list(4, false);
for (auto& eol_idx : eol_edge_idx_set) { // 遍历eol egde
Segment<PlanarCoord>& eol_segment = edge_list[eol_idx];
PlanarRect eol_segment_rect = DRCUTIL.getBoundingBox({eol_segment.get_first(), eol_segment.get_second()});
for (int32_t i = 0; i < overhang_dis_list.size(); i++) { // 找到对应的overhang segment
PlanarRect overhang_segment_rect = DRCUTIL.getBoundingBox({overhang_list[i].get_first(), overhang_list[i].get_second()});
if (DRCUTIL.isInside(eol_segment_rect, overhang_segment_rect) && edge_length_list[eol_idx] < eol_width) {
is_eol_edge_list[i] = true;
is_eol_edge = true;
std::vector<std::pair<BGRectInt, int32_t>> cut_bg_rect_net_pair_list;
cut_bg_rtree_map[cut_layer_idx].query(bgi::intersects(DRCUTIL.convertToBGRectInt(routing_rect)), std::back_inserter(cut_bg_rect_net_pair_list));
for (auto& [bg_env_rect, env_net_idx] : cut_bg_rect_net_pair_list) {
PlanarRect cut_rect = DRCUTIL.convertToPlanarRect(bg_env_rect);
if (DRCUTIL.isInside(routing_rect, cut_rect)) {
std::vector<Segment<PlanarCoord>>& overhang_list = net_cut_rect_overhang_map[env_net_idx][cut_rect];
Segment<PlanarCoord> east_segment = routing_rect.getOrientEdge(Orientation::kEast);
Segment<PlanarCoord> south_segment = routing_rect.getOrientEdge(Orientation::kSouth);
Segment<PlanarCoord> west_segment = routing_rect.getOrientEdge(Orientation::kWest);
Segment<PlanarCoord> north_segment = routing_rect.getOrientEdge(Orientation::kNorth);
if (overhang_list.empty()) {
overhang_list.push_back(east_segment);
overhang_list.push_back(south_segment);
overhang_list.push_back(west_segment);
overhang_list.push_back(north_segment);
} else {
if (east_segment.get_first().get_x() >= overhang_list[0].get_first().get_x()) {
overhang_list[0] = east_segment;
}
if (south_segment.get_first().get_y() <= overhang_list[1].get_first().get_y()) {
overhang_list[1] = south_segment;
}
if (west_segment.get_first().get_x() <= overhang_list[2].get_first().get_x()) {
overhang_list[2] = west_segment;
}
if (north_segment.get_first().get_y() >= overhang_list[3].get_first().get_y()) {
overhang_list[3] = north_segment;
}
}
}
if (DRCUTIL.isOpenOverlap(routing_rect, cut_rect)) {
net_cut_rect_span_rect_map[env_net_idx][cut_rect].push_back(routing_rect);
}
}

// 更新overhang segment
for (auto& polygon_segment : edge_list) {
PlanarRect polygon_segment_rect = DRCUTIL.getBoundingBox({polygon_segment.get_first(), polygon_segment.get_second()});
for (auto& cut_overhang : overhang_list) {
if (DRCUTIL.isInside(polygon_segment_rect, DRCUTIL.getBoundingBox({cut_overhang.get_first(), cut_overhang.get_second()}))) {
cut_overhang = polygon_segment;
}
for (auto& [net_idx, cut_rect_overhang_map] : net_cut_rect_overhang_map) {
for (auto& [cut_rect, overhang_list] : cut_rect_overhang_map) {
for (GTLRectInt& gtl_rect : gtl_rect_list) {
PlanarRect routing_rect = DRCUTIL.convertToPlanarRect(gtl_rect);
for (PlanarRect& span_rect : net_cut_rect_span_rect_map[net_idx][cut_rect]) {
if (DRCUTIL.isOpenOverlap(routing_rect, span_rect)) {
net_cut_rect_routing_rect_map[net_idx][cut_rect].push_back(routing_rect);
continue;
}
}
}
}

// EXTENSION 条件
// span length 记录
int32_t max_x_span = 0;
int32_t max_y_span = 0;
for (PlanarRect& span_rect : net_cut_span_rect_list[net_idx][cut]) {
int32_t x_span = span_rect.getXSpan();
int32_t y_span = span_rect.getYSpan();
max_x_span = std::max(max_x_span, x_span);
max_y_span = std::max(max_y_span, y_span);
}

// 查找周围的routing矩形,原矩形的基础上膨胀side ext
std::vector<BGRectInt> bg_env_rect_list;
}
}
for (auto& [net_idx, cut_rect_overhang_map] : net_cut_rect_overhang_map) {
for (auto& [cut_rect, overhang_list] : cut_rect_overhang_map) {
std::vector<bool> need_spacing_list(4, false);
{
std::vector<PlanarCoord> overhang_coord_list;
for (auto& overhang_segment : overhang_list) {
overhang_coord_list.push_back(overhang_segment.get_first());
overhang_coord_list.push_back(overhang_segment.get_second());
int32_t max_x_span = 0;
int32_t max_y_span = 0;
for (PlanarRect& span_rect : net_cut_rect_span_rect_map[net_idx][cut_rect]) {
max_x_span = std::max(max_x_span, span_rect.getXSpan());
max_y_span = std::max(max_y_span, span_rect.getYSpan());
}
PlanarRect check_rect = DRCUTIL.getEnlargedRect(DRCUTIL.getBoundingBox(overhang_coord_list), side_ext);
routing_layer_query_tree[cut_layer_idx].query(bgi::intersects(DRCUTIL.convertToBGRectInt(check_rect)), std::back_inserter(bg_env_rect_list));
}

GTLPolySetInt env_gtl_poly_set; // 周围env的环境
for (auto& bg_env_rect : bg_env_rect_list) {
PlanarRect env_rect = DRCUTIL.convertToPlanarRect(bg_env_rect);
env_gtl_poly_set += DRCUTIL.convertToGTLRectInt(env_rect);
}
// env里需要减去cut所在的metal
for (PlanarRect& cut_metal_rect : net_cut_metal_rect_list[net_idx][cut]) {
env_gtl_poly_set -= DRCUTIL.convertToGTLRectInt(cut_metal_rect);
}

std::vector<bool> is_use_spacing_list(4, false); // 用于判断cut需要用spacing的边
// 遍历四条overhang并检查区域情况
for (int32_t i = 0; i < overhang_dis_list.size(); i++) {
int32_t pre_idx = getIdx(i - 1, overhang_dis_list.size());
int32_t post_idx = getIdx(i + 1, overhang_dis_list.size());
if (overhang_dis_list[i] >= smaller_overhang) {
continue; // overhang不满足条件
}
Direction direction = DRCUTIL.getDirection(overhang_list[i].get_first(), overhang_list[i].get_second());
bool is_span_length = direction == Direction::kHorizontal ? max_x_span >= span_length : max_y_span >= span_length;
// 是否和pre满足
if (overhang_dis_list[pre_idx] == equal_overhang) {
// 大于span则不需要考虑eol
// 小于span需要考虑eol
// 两条对边启用距离规则
PlanarRect wire_rect = DRCUTIL.getBoundingBox(
{overhang_list[i].get_first(), overhang_list[i].get_second(), overhang_list[pre_idx].get_first(), overhang_list[pre_idx].get_second()});
std::vector<PlanarRect> eol_ext_rect_list = gen_eol_search_rect(wire_rect, i);
bool is_overlap_pre = is_rect_interact_polygon(DRCUTIL.convertToGTLRectInt(eol_ext_rect_list[0]), env_gtl_poly_set, false);
bool is_overlap_post = is_rect_interact_polygon(DRCUTIL.convertToGTLRectInt(eol_ext_rect_list[1]), env_gtl_poly_set, false);
if (is_span_length && is_overlap_pre || (!is_span_length && is_overlap_pre && !is_overlap_post && is_eol_edge_list[i])) {
// 两条对边启用距离规则
is_use_spacing_list[getIdx(i + 2, overhang_dis_list.size())] = true;
is_use_spacing_list[getIdx(pre_idx + 2, overhang_dis_list.size())] = true;
GTLPolySetInt env_gtl_poly_set;
{
std::vector<PlanarCoord> coord_list;
for (Segment<PlanarCoord>& overhang : overhang_list) {
coord_list.push_back(overhang.get_first());
coord_list.push_back(overhang.get_second());
}
PlanarRect check_rect = DRCUTIL.getEnlargedRect(DRCUTIL.getBoundingBox(coord_list), side_ext);
std::vector<BGRectInt> bg_rect_list;
routing_bg_rtree_map[cut_layer_idx].query(bgi::intersects(DRCUTIL.convertToBGRectInt(check_rect)), std::back_inserter(bg_rect_list));
for (auto& bg_rect : bg_rect_list) {
env_gtl_poly_set += DRCUTIL.convertToGTLRectInt(bg_rect);
}
for (PlanarRect& routing_rect : net_cut_rect_routing_rect_map[net_idx][cut_rect]) {
env_gtl_poly_set -= DRCUTIL.convertToGTLRectInt(routing_rect);
}
}
// 是否和post满足
if (overhang_dis_list[post_idx] == equal_overhang) {
// 两条对边启用距离规则
PlanarRect wire_rect = DRCUTIL.getBoundingBox(
{overhang_list[i].get_first(), overhang_list[i].get_second(), overhang_list[post_idx].get_first(), overhang_list[post_idx].get_second()});
std::vector<PlanarRect> eol_ext_rect_list = gen_eol_search_rect(wire_rect, i);
bool is_overlap_pre = is_rect_interact_polygon(DRCUTIL.convertToGTLRectInt(eol_ext_rect_list[0]), env_gtl_poly_set, false);
bool is_overlap_post = is_rect_interact_polygon(DRCUTIL.convertToGTLRectInt(eol_ext_rect_list[1]), env_gtl_poly_set, false);
if (is_span_length && is_overlap_post || (!is_span_length && is_overlap_post && !is_overlap_pre && is_eol_edge_list[i])) {
// 两条对边启用距离规则
is_use_spacing_list[getIdx(i + 2, overhang_dis_list.size())] = true;
is_use_spacing_list[getIdx(post_idx + 2, overhang_dis_list.size())] = true;
std::vector<int32_t> overhang_distance_list(4, 0);
overhang_distance_list[0] = std::abs(overhang_list[0].get_first().get_x() - cut_rect.get_ur_x()); // east
overhang_distance_list[1] = std::abs(overhang_list[1].get_first().get_y() - cut_rect.get_ll_y()); // south
overhang_distance_list[2] = std::abs(overhang_list[2].get_first().get_x() - cut_rect.get_ll_x()); // west
overhang_distance_list[3] = std::abs(overhang_list[3].get_first().get_y() - cut_rect.get_ur_y()); // north
for (int32_t i = 0; i < static_cast<int32_t>(overhang_distance_list.size()); i++) {
if (overhang_distance_list[i] >= smaller_overhang) {
continue;
}
bool is_span_length;
if (DRCUTIL.isHorizontal(overhang_list[i].get_first(), overhang_list[i].get_second())) {
is_span_length = max_x_span >= span_length;
} else {
is_span_length = max_y_span >= span_length;
}
bool is_eol_edge = false;
for (int32_t eol_edge_idx : eol_edge_idx_set) {
if (DRCUTIL.isInside(edge_list[eol_edge_idx], overhang_list[i]) && edge_length_list[eol_edge_idx] < eol_width) {
is_eol_edge = true;
}
}
std::vector<std::pair<bool, int32_t>> is_pre_adj_i_list;
is_pre_adj_i_list.emplace_back(true, getIdx(i - 1, overhang_distance_list.size()));
is_pre_adj_i_list.emplace_back(false, getIdx(i + 1, overhang_distance_list.size()));
for (auto& [is_pre, adj_i] : is_pre_adj_i_list) {
if (overhang_distance_list[adj_i] != equal_overhang) {
continue;
}
bool need_spacing = false;
{
PlanarRect rect = DRCUTIL.getBoundingBox(
{overhang_list[i].get_first(), overhang_list[i].get_second(), overhang_list[adj_i].get_first(), overhang_list[adj_i].get_second()});
GTLRectInt pre_rect;
GTLRectInt post_rect;
if (i == 0) {
pre_rect = GTLRectInt(rect.get_ur_x() - backward_ext, rect.get_ur_y(), rect.get_ur_x(), rect.get_ur_y() + side_ext);
post_rect = GTLRectInt(rect.get_ur_x() - backward_ext, rect.get_ll_y() - side_ext, rect.get_ur_x(), rect.get_ll_y());
} else if (i == 1) {
pre_rect = GTLRectInt(rect.get_ur_x(), rect.get_ll_y(), rect.get_ur_x() + side_ext, rect.get_ll_y() + backward_ext);
post_rect = GTLRectInt(rect.get_ll_x() - side_ext, rect.get_ll_y(), rect.get_ll_x(), rect.get_ll_y() + backward_ext);
} else if (i == 2) {
pre_rect = GTLRectInt(rect.get_ll_x(), rect.get_ll_y() - side_ext, rect.get_ll_x() + backward_ext, rect.get_ll_y());
post_rect = GTLRectInt(rect.get_ll_x(), rect.get_ur_y(), rect.get_ll_x() + backward_ext, rect.get_ur_y() + side_ext);
} else if (i == 3) {
pre_rect = GTLRectInt(rect.get_ll_x() - side_ext, rect.get_ur_y() - backward_ext, rect.get_ll_x(), rect.get_ur_y());
post_rect = GTLRectInt(rect.get_ur_x(), rect.get_ur_y() - backward_ext, rect.get_ur_x() + side_ext, rect.get_ur_y());
}
bool is_overlap_pre = DRCUTIL.isOverlap(env_gtl_poly_set, pre_rect, false);
bool is_overlap_post = DRCUTIL.isOverlap(env_gtl_poly_set, post_rect, false);
if (is_pre) {
need_spacing = ((is_span_length && is_overlap_pre) || (!is_span_length && is_overlap_pre && !is_overlap_post && is_eol_edge));
} else {
need_spacing = ((is_span_length && is_overlap_post) || (!is_span_length && is_overlap_post && !is_overlap_pre && is_eol_edge));
}
}
if (need_spacing) {
need_spacing_list[getIdx(i + 2, overhang_distance_list.size())] = true;
need_spacing_list[getIdx(adj_i + 2, overhang_distance_list.size())] = true;
}
}
}
}
// cut
// 东南西北生成一个spacing区域
// 东南西北的边进行膨胀即可
PlanarCoord cut_ll = cut.get_ll();
PlanarCoord cut_ur = cut.get_ur();
PlanarCoord cut_ul = PlanarCoord(cut_ll.get_x(), cut_ur.get_y());
PlanarCoord cut_lr = PlanarCoord(cut_ur.get_x(), cut_ll.get_y());
PlanarRect cut_east_segment_rect = DRCUTIL.getOffsetRect(DRCUTIL.getBoundingBox({cut_lr, cut_ur}), PlanarCoord(1, 0));
PlanarRect cut_south_segment_rect = DRCUTIL.getOffsetRect(DRCUTIL.getBoundingBox({cut_ll, cut_lr}), PlanarCoord(0, -1));
PlanarRect cut_west_segment_rect = DRCUTIL.getOffsetRect(DRCUTIL.getBoundingBox({cut_ll, cut_ul}), PlanarCoord(-1, 0));
PlanarRect cut_north_segment_rect = DRCUTIL.getOffsetRect(DRCUTIL.getBoundingBox({cut_ul, cut_ur}), PlanarCoord(0, 1));

std::vector<PlanarRect> cut_spacing_rect_list;
std::vector<PlanarRect> cut_spacing_b_rect_list;
if (is_use_spacing_list[0]) {
cut_spacing_rect_list.push_back(DRCUTIL.getEnlargedRect(cut_east_segment_rect, 0, cut_spacing_b, cut_spacing_b, cut_spacing_b));
cut_spacing_b_rect_list.push_back(DRCUTIL.getEnlargedRect(cut_east_segment_rect, 0, -1 * prl, cut_spacing_b, -1 * prl));
}
if (is_use_spacing_list[1]) {
cut_spacing_rect_list.push_back(DRCUTIL.getEnlargedRect(cut_south_segment_rect, cut_spacing_b, cut_spacing_b, cut_spacing_b, 0));
cut_spacing_b_rect_list.push_back(DRCUTIL.getEnlargedRect(cut_south_segment_rect, -1 * prl, cut_spacing_b, -1 * prl, 0));
}
if (is_use_spacing_list[2]) {
cut_spacing_rect_list.push_back(DRCUTIL.getEnlargedRect(cut_west_segment_rect, cut_spacing_b, cut_spacing_b, 0, cut_spacing_b));
cut_spacing_b_rect_list.push_back(DRCUTIL.getEnlargedRect(cut_west_segment_rect, cut_spacing_b, -1 * prl, 0, -1 * prl));
}
if (is_use_spacing_list[3]) {
cut_spacing_rect_list.push_back(DRCUTIL.getEnlargedRect(cut_north_segment_rect, cut_spacing_b, 0, cut_spacing_b, cut_spacing_b));
cut_spacing_b_rect_list.push_back(DRCUTIL.getEnlargedRect(cut_north_segment_rect, -1 * prl, 0, -1 * prl, cut_spacing_b));
}
if (cut_spacing_rect_list.empty()) {
continue; // 没有需要的spacing区域,跳过
}
GTLPolySetInt cut_spacing_a_region;
GTLPolySetInt cut_spacing_b_region;
for (PlanarRect& cut_spacing_rect : cut_spacing_rect_list) {
cut_spacing_a_region += DRCUTIL.convertToGTLRectInt(cut_spacing_rect);
}
for (PlanarRect& cut_spacing_b_rect : cut_spacing_b_rect_list) {
cut_spacing_b_region += DRCUTIL.convertToGTLRectInt(cut_spacing_b_rect);
}
// 由于b的rect 小于 a的rect,所以判断时要先判断b区域
// 查找周围的cut rect
std::vector<std::pair<BGRectInt, int32_t>> bg_env_cut_net_pair_list;
{
PlanarRect check_rect = DRCUTIL.getEnlargedRect(cut, cut_spacing_b);
cut_layer_all_query_tree[cut_layer_idx].query(bgi::intersects(DRCUTIL.convertToBGRectInt(check_rect)),
std::back_inserter(bg_env_cut_net_pair_list));
if (need_spacing_list[0]) {
Segment<PlanarCoord> segment = cut_rect.getOrientEdge(Orientation::kEast);
PlanarRect segment_rect = DRCUTIL.getOffsetRect(DRCUTIL.getRect(segment.get_first(), segment.get_second()), PlanarCoord(1, 0));
cut_spacing_a_region += DRCUTIL.convertToGTLRectInt(DRCUTIL.getEnlargedRect(segment_rect, 0, -1 * eol_prl, eol_prl_spacing, -1 * eol_prl));
cut_spacing_b_region += DRCUTIL.convertToGTLRectInt(DRCUTIL.getEnlargedRect(segment_rect, 0, eol_prl_spacing, eol_prl_spacing, eol_prl_spacing));
}
if (need_spacing_list[1]) {
Segment<PlanarCoord> segment = cut_rect.getOrientEdge(Orientation::kSouth);
PlanarRect segment_rect = DRCUTIL.getOffsetRect(DRCUTIL.getRect(segment.get_first(), segment.get_second()), PlanarCoord(0, -1));
cut_spacing_a_region += DRCUTIL.convertToGTLRectInt(DRCUTIL.getEnlargedRect(segment_rect, -1 * eol_prl, eol_prl_spacing, -1 * eol_prl, 0));
cut_spacing_b_region += DRCUTIL.convertToGTLRectInt(DRCUTIL.getEnlargedRect(segment_rect, eol_prl_spacing, eol_prl_spacing, eol_prl_spacing, 0));
}
if (need_spacing_list[2]) {
Segment<PlanarCoord> segment = cut_rect.getOrientEdge(Orientation::kWest);
PlanarRect segment_rect = DRCUTIL.getOffsetRect(DRCUTIL.getRect(segment.get_first(), segment.get_second()), PlanarCoord(-1, 0));
cut_spacing_a_region += DRCUTIL.convertToGTLRectInt(DRCUTIL.getEnlargedRect(segment_rect, eol_prl_spacing, -1 * eol_prl, 0, -1 * eol_prl));
cut_spacing_b_region += DRCUTIL.convertToGTLRectInt(DRCUTIL.getEnlargedRect(segment_rect, eol_prl_spacing, eol_prl_spacing, 0, eol_prl_spacing));
}
if (need_spacing_list[3]) {
Segment<PlanarCoord> segment = cut_rect.getOrientEdge(Orientation::kNorth);
PlanarRect segment_rect = DRCUTIL.getOffsetRect(DRCUTIL.getRect(segment.get_first(), segment.get_second()), PlanarCoord(0, 1));
cut_spacing_a_region += DRCUTIL.convertToGTLRectInt(DRCUTIL.getEnlargedRect(segment_rect, -1 * eol_prl, 0, -1 * eol_prl, eol_prl_spacing));
cut_spacing_b_region += DRCUTIL.convertToGTLRectInt(DRCUTIL.getEnlargedRect(segment_rect, eol_prl_spacing, 0, eol_prl_spacing, eol_prl_spacing));
}
}

// 和周围的cut逐个判断
for (auto& [bg_env_cut, env_net_idx] : bg_env_cut_net_pair_list) {
if (gtl::area(cut_spacing_b_region) == 0) {
continue;
}
std::vector<std::pair<BGRectInt, int32_t>> bg_cut_rect_net_pair_list;
cut_bg_rtree_map[cut_layer_idx].query(bgi::intersects(DRCUTIL.convertToBGRectInt(DRCUTIL.getEnlargedRect(cut_rect, eol_prl_spacing))),
std::back_inserter(bg_cut_rect_net_pair_list));
for (auto& [bg_env_cut_rect, env_net_idx] : bg_cut_rect_net_pair_list) {
if (net_idx == -1 && env_net_idx == -1) {
continue; // -1忽略
continue;
}

PlanarRect env_cut = DRCUTIL.convertToPlanarRect(bg_env_cut);
PlanarRect ori_cut = cut;
if (env_cut == ori_cut) {
continue; // 自己和自己不算
PlanarRect env_cut_rect = DRCUTIL.convertToPlanarRect(bg_env_cut_rect);
GTLRectInt gtl_env_cut_rect = DRCUTIL.convertToGTLRectInt(bg_env_cut_rect);
if (cut_rect == env_cut_rect) {
continue;
}
int32_t required_size = cut_spacing_a;
int32_t real_spacing = DRCUTIL.getEuclideanDistance(env_cut, ori_cut);
// 是否在spacing b区域
if (is_rect_interact_polygon(DRCUTIL.convertToGTLRectInt(env_cut), cut_spacing_b_region, true)) {
required_size = cut_spacing_b;
} else if (is_rect_interact_polygon(DRCUTIL.convertToGTLRectInt(env_cut), cut_spacing_a_region, true)) {
required_size = cut_spacing_a;
} else {
continue; // 不在spacing区域
int32_t required_size = eol_spacing;
if (DRCUTIL.isOverlap(cut_spacing_a_region, gtl_env_cut_rect, true)) {
required_size = eol_prl_spacing;
} else if (!DRCUTIL.isOverlap(cut_spacing_b_region, gtl_env_cut_rect, true)) {
continue;
}
if (real_spacing >= required_size) {
continue; // 满足条件
if (DRCUTIL.getEuclideanDistance(cut_rect, env_cut_rect) >= required_size) {
continue;
}
int32_t violation_routing_layer_idx = -1;
{
std::vector<int32_t>& routing_layer_idx_list = cut_to_adjacent_routing_map[cut_layer_idx];
violation_routing_layer_idx = *std::min_element(routing_layer_idx_list.begin(), routing_layer_idx_list.end());
}

// 生成违例矩形
PlanarRect violation_rect;
if (DRCUTIL.isClosedOverlap(env_cut, ori_cut)) {
violation_rect = DRCUTIL.getOverlap(env_cut, ori_cut);
if (DRCUTIL.isClosedOverlap(cut_rect, env_cut_rect)) {
violation_rect = DRCUTIL.getOverlap(cut_rect, env_cut_rect);
} else {
violation_rect = DRCUTIL.getSpacingRect(env_cut, cut);
violation_rect = DRCUTIL.getSpacingRect(cut_rect, env_cut_rect);
}

Violation violation;
violation.set_violation_type(ViolationType::kCutEOLSpacing);
violation.set_is_routing(true);
@@ -463,15 +327,11 @@ void RuleValidator::verifyCutEOLSpacing(RVBox& rv_box)
violation.set_layer_idx(violation_routing_layer_idx);
violation.set_rect(violation_rect);
violation.set_required_size(required_size);
violation_list.push_back(violation);
// env_cut
rv_box.get_violation_list().push_back(violation);
}
}
// cut
}
// polygon
}
// polygon set
}
}



+ 123
- 386
src/operation/iDRC/source/module/rule_validator/rv_design_rule/EnclosureEdge.cpp View File

@@ -20,282 +20,170 @@ namespace idrc {

void RuleValidator::verifyEnclosureEdge(RVBox& rv_box)
{
/*
规则:
PROPERTY LEF58_ENCLOSUREEDGE "
VIA1
ENCLOSUREEDGE CUTCLASS VSINGLECUT 0.015000 WIDTH 0.160500 PARALLEL 0.100000 WITHIN 0.130000 ;
ENCLOSUREEDGE CUTCLASS VSINGLECUT ABOVE 0.010000 WIDTH 0.070500 PARALLEL 0.100000 WITHIN 0.100000 ;
ENCLOSUREEDGE CUTCLASS VSINGLECUT ABOVE 0.005000 WIDTH 0.055500 PARALLEL 0.100000 WITHIN 0.065000 ;
ENCLOSUREEDGE CUTCLASS VSINGLECUT ABOVE 0.005000 WIDTH 0.050500 PARALLEL 0.100000 WITHIN 0.060000 EXCEPTTWOEDGES ;
ENCLOSUREEDGE CUTCLASS VSINGLECUT ABOVE 0.01 CONVEXCORNERS 0.120 0.060 PARALLEL 0.051 LENGTH 0.1 ;
std::vector<CutLayer>& cut_layer_list = DRCDM.getDatabase().get_cut_layer_list();
std::map<int32_t, std::vector<int32_t>>& cut_to_adjacent_routing_map = DRCDM.getDatabase().get_cut_to_adjacent_routing_map();

VIA2-6
ENCLOSUREEDGE CUTCLASS VSINGLECUT 0.015000 WIDTH 0.160500 PARALLEL 0.100000 WITHIN 0.130000 ;
ENCLOSUREEDGE CUTCLASS VSINGLECUT 0.010000 WIDTH 0.070500 PARALLEL 0.100000 WITHIN 0.100000 ;
ENCLOSUREEDGE CUTCLASS VSINGLECUT 0.005000 WIDTH 0.055500 PARALLEL 0.100000 WITHIN 0.065000 ;
ENCLOSUREEDGE CUTCLASS VSINGLECUT 0.005000 WIDTH 0.050500 PARALLEL 0.100000 WITHIN 0.060000 EXCEPTTWOEDGES ;
ENCLOSUREEDGE CUTCLASS VSINGLECUT ABOVE 0.01 CONVEXCORNERS 0.120 0.060 PARALLEL 0.051 LENGTH 0.1 ;
前四条可以看做是一种 rule1
最后一条是一种 rule2目前没有违例,所以暂时不能确定
*/
struct enclosure_edge_rule
std::map<int32_t, bgi::rtree<std::pair<BGRectInt, int32_t>, bgi::quadratic<16>>> routing_bg_rtree_map;
{
bool has_above;
bool has_below; // below | above要么只会出现一个,要么都不出现
int overhang;

// 这条和下一条应该是同级互斥的 |
int min_width; // >=
int par_length; // >
bool has_par_within;
int par_within; // <
bool has_except_two_edges; // 两边都要 这下面还有一个可选参数,t28没有就没有加了

// 和上面一条是 |
bool has_convex_corners;
int convex_length; //<=
int adjacent_length; //<=
int convex_par_within; //< lef文档中这个也叫做par_within
int length; //>=
};

std::map<int, std::vector<enclosure_edge_rule>> layer_enclosure_edge_rule_list;
// t28在V1-V6有该规则,每层的规则是一个列表
// V1和V2-V6有些许不同 v2-v6的前几项没有above
for (int i = 1; i <= 6; i++) {
std::vector<enclosure_edge_rule> enclosure_edge_rule_list;
enclosure_edge_rule_list.push_back({/*above below*/ false, false, /*overhang*/ 30, /*case1 */ 321, 200, true, 260, /*two edge*/ false,
/*has_convex_corners*/ false, 0, 0, 0, 0});
enclosure_edge_rule_list.push_back({/*above below*/ i == 1 ? true : false, false, /*overhang*/ 20, /*case1 */ 141, 200, true, 200, /*two edge*/ false,
/*has_convex_corners*/ false, 0, 0, 0, 0});
enclosure_edge_rule_list.push_back({/*above below*/ i == 1 ? true : false, false, /*overhang*/ 10, /*case1 */ 111, 200, true, 130, /*two edge*/ false,
/*has_convex_corners*/ false, 0, 0, 0, 0});
enclosure_edge_rule_list.push_back({/*above below*/ i == 1 ? true : false, false, /*overhang*/ 10, /*case1 */ 101, 200, true, 120, /*two edge*/ true,
/*has_convex_corners*/ false, 0, 0, 0, 0});
enclosure_edge_rule_list.push_back({/*above below*/ true, false, /*overhang*/ 20, /*case1 */ 0, 0, false, 0, /*two edge*/ false,
/*has_convex_corners*/ true, 240, 120, 102, 200});
layer_enclosure_edge_rule_list[i] = enclosure_edge_rule_list;
}

// 工具类函数
#if 1
auto get_overhang = [](const PlanarRect& cut_rect, const Segment<PlanarCoord>& metal_egde) {
Orientation orientation = DRCUTIL.getOrientation(metal_egde.get_first(), metal_egde.get_second());
int32_t overhang = 0;
if (orientation == Orientation::kWest) {
overhang = std::abs(cut_rect.get_ur_y() - metal_egde.get_first().get_y());
} else if (orientation == Orientation::kEast) {
overhang = std::abs(cut_rect.get_ll_y() - metal_egde.get_first().get_y());
} else if (orientation == Orientation::kNorth) {
overhang = std::abs(cut_rect.get_ur_x() - metal_egde.get_first().get_x());
} else if (orientation == Orientation::kSouth) {
overhang = std::abs(cut_rect.get_ll_x() - metal_egde.get_first().get_x());
std::map<int32_t, std::map<int32_t, GTLPolySetInt>> routing_net_gtl_poly_set_map;
for (DRCShape* drc_shape : rv_box.get_drc_env_shape_list()) {
if (drc_shape->get_is_routing()) {
routing_net_gtl_poly_set_map[drc_shape->get_layer_idx()][drc_shape->get_net_idx()] += DRCUTIL.convertToGTLRectInt(drc_shape->get_rect());
}
}
return overhang;
};
auto get_convex_rule_check_rect = [](const PlanarRect& cut_rect, const Segment<PlanarCoord>& metal_egde, int32_t enlarge_size) {
Orientation orientation = DRCUTIL.getOrientation(metal_egde.get_first(), metal_egde.get_second());
PlanarRect check_rect;
if (orientation == Orientation::kWest) { // 往北
check_rect = DRCUTIL.getEnlargedRect(cut_rect.get_ur(), cut_rect.getXSpan(), 0, 0, enlarge_size);
} else if (orientation == Orientation::kEast) { // 往南
check_rect = DRCUTIL.getEnlargedRect(cut_rect.get_ll(), 0, enlarge_size, cut_rect.getXSpan(), 0);
} else if (orientation == Orientation::kNorth) { // 往东
check_rect = DRCUTIL.getEnlargedRect(cut_rect.get_ur(), 0, cut_rect.getYSpan(), enlarge_size, 0);
} else if (orientation == Orientation::kSouth) { // 往西
check_rect = DRCUTIL.getEnlargedRect(cut_rect.get_ll(), enlarge_size, 0, 0, cut_rect.getYSpan());
for (DRCShape* drc_shape : rv_box.get_drc_result_shape_list()) {
if (drc_shape->get_is_routing()) {
routing_net_gtl_poly_set_map[drc_shape->get_layer_idx()][drc_shape->get_net_idx()] += DRCUTIL.convertToGTLRectInt(drc_shape->get_rect());
}
}
return check_rect;
};
#endif
// 得到基础数据
std::vector<RoutingLayer>& routing_layer_list = DRCDM.getDatabase().get_routing_layer_list();
std::map<int32_t, std::vector<int32_t>>& routing_to_adjacent_cut_map = DRCDM.getDatabase().get_routing_to_adjacent_cut_map();
std::map<int32_t, std::vector<int32_t>>& cut_to_adjacent_routing_map = DRCDM.getDatabase().get_cut_to_adjacent_routing_map();
// 使用R树查询检测
std::map<int32_t, bgi::rtree<std::pair<BGRectInt, int32_t>, bgi::quadratic<16>>> cut_bg_rtree_map;

std::map<int32_t, std::map<int32_t, GTLPolySetInt>> routing_net_gtl_poly_set_map;
std::map<int32_t, std::vector<PlanarRect>> cut_layer_via_list;

std::map<int32_t, std::map<int32_t, std::vector<PlanarRect>>> routing_net_rect_map;
for (DRCShape* drc_shape : rv_box.get_drc_env_shape_list()) {
if (!drc_shape->get_is_routing()) {
// cut_layer_via_list[drc_shape->get_layer_idx()].push_back(drc_shape->get_rect()); env_cut貌似不检测,原因未知
cut_bg_rtree_map[drc_shape->get_layer_idx()].insert(std::make_pair(DRCUTIL.convertToBGRectInt(drc_shape->get_rect()), drc_shape->get_net_idx()));
} else {
routing_net_rect_map[drc_shape->get_layer_idx()][drc_shape->get_net_idx()].push_back(drc_shape->get_rect());
for (auto& [routing_layer_idx, net_gtl_poly_set_map] : routing_net_gtl_poly_set_map) {
for (auto& [net_idx, gtl_poly_set] : net_gtl_poly_set_map) {
std::vector<GTLRectInt> gtl_rect_list;
gtl::get_max_rectangles(gtl_rect_list, gtl_poly_set);
for (GTLRectInt& gtl_rect : gtl_rect_list) {
routing_bg_rtree_map[routing_layer_idx].insert(std::make_pair(DRCUTIL.convertToBGRectInt(gtl_rect), net_idx));
}
}
}
}
std::map<int32_t, std::vector<PlanarRect>> cut_rect_map;
for (DRCShape* drc_shape : rv_box.get_drc_result_shape_list()) {
if (!drc_shape->get_is_routing()) {
cut_layer_via_list[drc_shape->get_layer_idx()].push_back(drc_shape->get_rect());
cut_bg_rtree_map[drc_shape->get_layer_idx()].insert(std::make_pair(DRCUTIL.convertToBGRectInt(drc_shape->get_rect()), drc_shape->get_net_idx()));
} else {
routing_net_rect_map[drc_shape->get_layer_idx()][drc_shape->get_net_idx()].push_back(drc_shape->get_rect());
}
}
for (auto& [routing_layer_idx, net_rect_map] : routing_net_rect_map) {
for (auto& [net_idx, rect_list] : net_rect_map) {
GTLPolySetInt gtl_poly_set;
for (PlanarRect& rect : rect_list) {
gtl_poly_set += DRCUTIL.convertToGTLRectInt(rect);
}
routing_net_gtl_poly_set_map[routing_layer_idx][net_idx] = gtl_poly_set;
rect_list.clear();
std::vector<GTLRectInt> gtl_rect_list;
gtl::get_max_rectangles(gtl_rect_list, gtl_poly_set);
for (GTLRectInt& gtl_rect : gtl_rect_list) {
rect_list.push_back(DRCUTIL.convertToPlanarRect(gtl_rect));
}
cut_rect_map[drc_shape->get_layer_idx()].push_back(drc_shape->get_rect());
}
}

std::map<int32_t, bgi::rtree<std::pair<BGRectInt, int32_t>, bgi::quadratic<16>>> routing_bg_rtree_map;
for (auto& [routing_layer_idx, net_rect_map] : routing_net_rect_map) {
for (auto& [net_idx, rect_list] : net_rect_map) {
for (PlanarRect& rect : rect_list) {
routing_bg_rtree_map[routing_layer_idx].insert(std::make_pair(DRCUTIL.convertToBGRectInt(rect), net_idx));
}
}
}

/*
对于rule1,有两种方案:
1.用via去搜索metal rect
2.用metal rect去搜索via
发现用第一种方案效果好
*/
/// rule1
for (auto& [cut_layer_idx, via_list] : cut_layer_via_list) {
if (cut_layer_idx < 1 || cut_layer_idx > 6) {
continue; // 只处理V1-V6 layer CO也有该规则,先不管
}

std::vector<enclosure_edge_rule> enclosure_edge_rule_list = layer_enclosure_edge_rule_list[cut_layer_idx];
for (auto& [cut_layer_idx, cut_rect_list] : cut_rect_map) {
std::vector<int32_t>& routing_layer_idx_list = cut_to_adjacent_routing_map[cut_layer_idx];
if (routing_layer_idx_list.size() < 2) {
continue;
}
int32_t above_routing_layer_idx = *std::max_element(routing_layer_idx_list.begin(), routing_layer_idx_list.end());
int32_t below_routing_layer_idx = *std::min_element(routing_layer_idx_list.begin(), routing_layer_idx_list.end());
// 处理rule1
for (auto& cut_rect : via_list) {
for (PlanarRect& cut_rect : cut_rect_list) {
for (int32_t routing_layer_idx : routing_layer_idx_list) {
// query所有metal rect
std::vector<std::pair<BGRectInt, int32_t>> metal_rect_result;
routing_bg_rtree_map[routing_layer_idx].query(bgi::intersects(DRCUTIL.convertToBGRectInt(cut_rect)), std::back_inserter(metal_rect_result));
// 用所有的metal rect更新当前的overhang
int32_t west_overhang = 0;
int32_t east_overhang = 0;
int32_t north_overhang = 0;
int32_t south_overhang = 0;
for (auto& [metal_bg_rect, metal_net_idx] : metal_rect_result) {
PlanarRect metal_rect = DRCUTIL.convertToPlanarRect(metal_bg_rect);
if (DRCUTIL.isClosedOverlap(metal_rect, cut_rect) == false) {
std::vector<std::pair<BGRectInt, int32_t>> bg_rect_net_pair_list;
{
routing_bg_rtree_map[routing_layer_idx].query(bgi::intersects(DRCUTIL.convertToBGRectInt(cut_rect)), std::back_inserter(bg_rect_net_pair_list));
}
std::map<Orientation, int32_t> orient_overhang_map;
for (auto& [bg_rect, net_idx] : bg_rect_net_pair_list) {
PlanarRect routing_rect = DRCUTIL.convertToPlanarRect(bg_rect);
if (!DRCUTIL.isClosedOverlap(routing_rect, cut_rect)) {
continue;
}
if (metal_rect.get_ll_x() <= cut_rect.get_ll_x())
west_overhang = std::max(west_overhang, std::abs(cut_rect.get_ll_x() - metal_rect.get_ll_x()));
if (metal_rect.get_ur_x() >= cut_rect.get_ur_x())
east_overhang = std::max(east_overhang, std::abs(cut_rect.get_ur_x() - metal_rect.get_ur_x()));
if (metal_rect.get_ur_y() >= cut_rect.get_ur_y())
north_overhang = std::max(north_overhang, std::abs(cut_rect.get_ur_y() - metal_rect.get_ur_y()));
if (metal_rect.get_ll_y() <= cut_rect.get_ll_y())
south_overhang = std::max(south_overhang, std::abs(cut_rect.get_ll_y() - metal_rect.get_ll_y()));
if (routing_rect.get_ll_x() <= cut_rect.get_ll_x()) {
orient_overhang_map[Orientation::kWest]
= std::max(orient_overhang_map[Orientation::kWest], std::abs(cut_rect.get_ll_x() - routing_rect.get_ll_x()));
}
if (routing_rect.get_ur_x() >= cut_rect.get_ur_x()) {
orient_overhang_map[Orientation::kEast]
= std::max(orient_overhang_map[Orientation::kEast], std::abs(cut_rect.get_ur_x() - routing_rect.get_ur_x()));
}
if (routing_rect.get_ur_y() >= cut_rect.get_ur_y()) {
orient_overhang_map[Orientation::kNorth]
= std::max(orient_overhang_map[Orientation::kNorth], std::abs(cut_rect.get_ur_y() - routing_rect.get_ur_y()));
}
if (routing_rect.get_ll_y() <= cut_rect.get_ll_y()) {
orient_overhang_map[Orientation::kSouth]
= std::max(orient_overhang_map[Orientation::kSouth], std::abs(cut_rect.get_ll_y() - routing_rect.get_ll_y()));
}
}
for (auto& [metal_bg_rect, metal_net_idx] : metal_rect_result) {
PlanarRect metal_rect = DRCUTIL.convertToPlanarRect(metal_bg_rect);
if (DRCUTIL.isClosedOverlap(metal_rect, cut_rect) == false) {
for (auto& [bg_rect, net_idx] : bg_rect_net_pair_list) {
PlanarRect routing_rect = DRCUTIL.convertToPlanarRect(bg_rect);
if (!DRCUTIL.isClosedOverlap(routing_rect, cut_rect)) {
continue;
}
for (const Direction& metal_direction : {Direction::kHorizontal, Direction::kVertical}) { // 两边都需要check
int metal_rect_width = metal_direction == Direction::kHorizontal ? metal_rect.getYSpan() : metal_rect.getXSpan();
// 根据width找到第一个适用的规则,这里取决于width的排列顺序 t28 tlef是从大到小的
// 这里先不检测第二个规则的 !enclosure_edge_rule_list[i].has_convex_corners
int rule_index = -1;
for (int i = 0; i < enclosure_edge_rule_list.size(); i++) {
if ((!enclosure_edge_rule_list[i].has_convex_corners) && metal_rect_width >= enclosure_edge_rule_list[i].min_width) {
rule_index = i;
break;
for (const Direction& curr_direction : {Direction::kHorizontal, Direction::kVertical}) {
EnclosureEdgeRule curr_rule;
{
int32_t routing_rect_width;
if (curr_direction == Direction::kHorizontal) {
routing_rect_width = routing_rect.getYSpan();
} else {
routing_rect_width = routing_rect.getXSpan();
}
for (EnclosureEdgeRule& enclosure_edge_rule : cut_layer_list[cut_layer_idx].get_enclosure_edge_rule_list()) {
if (routing_rect_width >= enclosure_edge_rule.min_width) {
curr_rule = enclosure_edge_rule;
break;
}
}
}
if (rule_index == -1) {
if (curr_rule.overhang < 0) {
continue;
}

enclosure_edge_rule& cur_rule = enclosure_edge_rule_list[rule_index];
// 判断当前是下层还是上层,有above则豁免下层,有below则豁免上层,否则就检查
bool is_above = (routing_layer_idx == above_routing_layer_idx);
if ((cur_rule.has_above && !is_above) || (cur_rule.has_below && is_above)) {
if (curr_rule.has_above && (routing_layer_idx != above_routing_layer_idx)) {
continue;
}
if (curr_rule.has_below && (routing_layer_idx != below_routing_layer_idx)) {
continue;
}
std::vector<std::pair<BGRectInt, int32_t>> env_bg_rect_net_pair_list;
std::set<int32_t> left_par_net_idx_set;
std::set<int32_t> right_par_net_idx_set;
{
PlanarRect check_rect; // 用于查找周围的矩形
if (metal_direction == Direction::kHorizontal) {
check_rect = DRCUTIL.getEnlargedRect(metal_rect, 0, cur_rule.par_within);
PlanarRect left_par_rect;
PlanarRect right_par_rect;
if (curr_direction == Direction::kHorizontal) {
left_par_rect = DRCUTIL.getEnlargedRect(routing_rect.get_ll(), 0, curr_rule.par_within, routing_rect.getXSpan(), 0);
right_par_rect = DRCUTIL.getEnlargedRect(routing_rect.get_ur(), routing_rect.getXSpan(), 0, 0, curr_rule.par_within);
} else {
check_rect = DRCUTIL.getEnlargedRect(metal_rect, cur_rule.par_within, 0);
left_par_rect = DRCUTIL.getEnlargedRect(routing_rect.get_ll(), curr_rule.par_within, 0, 0, routing_rect.getYSpan());
right_par_rect = DRCUTIL.getEnlargedRect(routing_rect.get_ur(), 0, routing_rect.getYSpan(), curr_rule.par_within, 0);
}
routing_bg_rtree_map[routing_layer_idx].query(bgi::intersects(DRCUTIL.convertToBGRectInt(check_rect)),
std::back_inserter(env_bg_rect_net_pair_list));
}
// 检测两个par区域
PlanarRect left_par_rect;
PlanarRect right_par_rect;
std::set<int> left_par_net_list;
std::set<int> right_par_net_list;
if (metal_direction == Direction::kHorizontal) {
left_par_rect = DRCUTIL.getEnlargedRect(metal_rect.get_ll(), 0, cur_rule.par_within, metal_rect.getXSpan(), 0); // 往south
right_par_rect = DRCUTIL.getEnlargedRect(metal_rect.get_ur(), metal_rect.getXSpan(), 0, 0, cur_rule.par_within); // 往north
} else {
left_par_rect = DRCUTIL.getEnlargedRect(metal_rect.get_ll(), cur_rule.par_within, 0, 0, metal_rect.getYSpan()); // 往west
right_par_rect = DRCUTIL.getEnlargedRect(metal_rect.get_ur(), 0, metal_rect.getYSpan(), cur_rule.par_within, 0); // 往east
}
for (auto& [env_bg_rect, env_metal_net_idx] : env_bg_rect_net_pair_list) {
PlanarRect env_metal_rect = DRCUTIL.convertToPlanarRect(env_bg_rect);
// 跳过重叠矩形
if (DRCUTIL.isClosedOverlap(metal_rect, env_metal_rect)) {
continue;
}
PlanarRect env_rect = DRCUTIL.convertToPlanarRect(env_bg_rect);
// within是小于,par是大于
if (DRCUTIL.isOpenOverlap(env_rect, left_par_rect) && DRCUTIL.getParallelLength(env_rect, left_par_rect) > cur_rule.par_length) {
left_par_net_list.insert(env_metal_net_idx);
std::vector<std::pair<BGRectInt, int32_t>> env_bg_rect_net_pair_list;
{
PlanarRect check_rect;
if (curr_direction == Direction::kHorizontal) {
check_rect = DRCUTIL.getEnlargedRect(routing_rect, 0, curr_rule.par_within);
} else {
check_rect = DRCUTIL.getEnlargedRect(routing_rect, curr_rule.par_within, 0);
}
routing_bg_rtree_map[routing_layer_idx].query(bgi::intersects(DRCUTIL.convertToBGRectInt(check_rect)),
std::back_inserter(env_bg_rect_net_pair_list));
}
if (DRCUTIL.isOpenOverlap(env_rect, right_par_rect) && DRCUTIL.getParallelLength(env_rect, right_par_rect) > cur_rule.par_length) {
right_par_net_list.insert(env_metal_net_idx);
for (auto& [bg_env_rect, env_net_idx] : env_bg_rect_net_pair_list) {
PlanarRect env_routing_rect = DRCUTIL.convertToPlanarRect(bg_env_rect);
if (DRCUTIL.isClosedOverlap(routing_rect, env_routing_rect)) {
continue;
}
if (DRCUTIL.isOpenOverlap(env_routing_rect, left_par_rect)) {
if (DRCUTIL.getParallelLength(env_routing_rect, left_par_rect) > curr_rule.par_length) {
left_par_net_idx_set.insert(env_net_idx);
}
}
if (DRCUTIL.isOpenOverlap(env_routing_rect, right_par_rect)) {
if (DRCUTIL.getParallelLength(env_routing_rect, right_par_rect) > curr_rule.par_length) {
right_par_net_idx_set.insert(env_net_idx);
}
}
}
}
// two edges
if (cur_rule.has_except_two_edges && (!left_par_net_list.empty() && !right_par_net_list.empty())) {
// 如果满足了两个区域的条件,则不需要继续检查
if (curr_rule.has_except_two_edges && !left_par_net_idx_set.empty() && !right_par_net_idx_set.empty()) {
continue;
}

std::set<std::pair<int32_t, int32_t>> violation_net_set;
// check south west的overhang是否满足条件
if ((metal_direction == Direction::kHorizontal && south_overhang < cur_rule.overhang)
|| (metal_direction == Direction::kVertical && west_overhang < cur_rule.overhang)) {
for (int32_t net_idx : left_par_net_list) {
violation_net_set.insert(std::make_pair(net_idx, metal_net_idx));
std::set<std::set<int32_t>> violation_net_set_set;
{
if ((curr_direction == Direction::kHorizontal && orient_overhang_map[Orientation::kSouth] < curr_rule.overhang)
|| (curr_direction == Direction::kVertical && orient_overhang_map[Orientation::kWest] < curr_rule.overhang)) {
for (int32_t left_par_net_idx : left_par_net_idx_set) {
violation_net_set_set.insert({left_par_net_idx, net_idx});
}
}
}

// check north east的overhang是否满足条件
if (metal_direction == Direction::kHorizontal && north_overhang < cur_rule.overhang
|| metal_direction == Direction::kVertical && east_overhang < cur_rule.overhang) {
for (int32_t net_idx : right_par_net_list) {
violation_net_set.insert(std::make_pair(net_idx, metal_net_idx));
if ((curr_direction == Direction::kHorizontal && orient_overhang_map[Orientation::kNorth] < curr_rule.overhang)
|| (curr_direction == Direction::kVertical && orient_overhang_map[Orientation::kEast] < curr_rule.overhang)) {
for (int32_t right_par_net_idx : right_par_net_idx_set) {
violation_net_set_set.insert({right_par_net_idx, net_idx});
}
}
}
for (const auto& [env_metal_net_idx, metal_net_idx] : violation_net_set) {
for (const std::set<int32_t>& violation_net_set : violation_net_set_set) {
Violation violation;
violation.set_violation_type(ViolationType::kEnclosureEdge);
violation.set_is_routing(true);
violation.set_violation_net_set({env_metal_net_idx, metal_net_idx});
violation.set_violation_net_set(violation_net_set);
violation.set_layer_idx(below_routing_layer_idx);
violation.set_rect(cut_rect);
violation.set_required_size(cur_rule.overhang);
violation.set_required_size(curr_rule.overhang);
rv_box.get_violation_list().push_back(violation);
}
}
@@ -303,157 +191,6 @@ void RuleValidator::verifyEnclosureEdge(RVBox& rv_box)
}
}
}

/// rule2
for (auto& [routing_layer_idx, net_gtl_poly_set_map] : routing_net_gtl_poly_set_map) {
std::vector<int32_t>& cut_layer_idx_list = routing_to_adjacent_cut_map[routing_layer_idx];
int32_t above_cuting_layer_idx = *std::max_element(cut_layer_idx_list.begin(), cut_layer_idx_list.end());
int32_t below_cuting_layer_idx = *std::min_element(cut_layer_idx_list.begin(), cut_layer_idx_list.end());
// TODO 这里需要处理底层和顶层的情况 0和6的情况
std::vector<enclosure_edge_rule> enclosure_edge_rule_list = layer_enclosure_edge_rule_list[below_cuting_layer_idx]; // 目前只支持below的,也就是cut的above
if (routing_layer_idx <= 0 || routing_layer_idx > 6) { // 目前只care M2-M7 // 所以第一层的metal跳过
continue;
}
for (enclosure_edge_rule& cur_rule : enclosure_edge_rule_list) {
if (!cur_rule.has_convex_corners) {
continue;
}
for (auto& [net_idx, gtl_poly_set] : net_gtl_poly_set_map) {
std::vector<GTLHolePolyInt> gtl_hole_poly_list;
gtl_poly_set.get(gtl_hole_poly_list);

for (GTLHolePolyInt& gtl_hole_poly : gtl_hole_poly_list) {
int32_t coord_size = static_cast<int32_t>(gtl_hole_poly.size());
if (coord_size < 4) {
continue;
}

std::vector<PlanarCoord> coord_list;
std::vector<int32_t> edge_length_list;
std::vector<bool> convex_corner_list;
std::vector<Segment<PlanarCoord>> edge_list;

for (auto iter = gtl_hole_poly.begin(); iter != gtl_hole_poly.end(); iter++) {
coord_list.push_back(DRCUTIL.convertToPlanarCoord(*iter));
}
for (int32_t i = 0; i < coord_size; i++) {
PlanarCoord& pre_coord = coord_list[getIdx(i - 1, coord_size)];
PlanarCoord& curr_coord = coord_list[i];
PlanarCoord& post_coord = coord_list[getIdx(i + 1, coord_size)];
edge_length_list.push_back(DRCUTIL.getManhattanDistance(pre_coord, curr_coord));
convex_corner_list.push_back(DRCUTIL.isConvexCorner(DRCUTIL.getRotation(gtl_hole_poly), pre_coord, curr_coord, post_coord));
edge_list.emplace_back(pre_coord, curr_coord);
}

for (int32_t i = 0; i < coord_size; i++) {
// 出现连续的三个凸角时才会满足条件
if (!convex_corner_list[i] || !convex_corner_list[getIdx(i + 1, coord_size)] || !convex_corner_list[getIdx(i + 2, coord_size)]) {
continue;
}
// 此时会有两种情况
/**
* 三凸角
*
* i i(length_edge)
* o---------o o---------o o
* | o | | o
* i+1 | | i+3 i+1 | i+1 | | i+3
* | | (convex_edge) | (adj_edge) | | (length_edge)
* o------------o o------------o o------------o
* i+2 i+2(adj_edge) i+2(convex_edge)
*
*/
for (auto [convex_edge_idx, adj_edge_idx, length_edge_idx] :
{std::tuple(getIdx(i + 1, coord_size), getIdx(i + 2, coord_size), i),
std::tuple(getIdx(i + 2, coord_size), getIdx(i + 1, coord_size), getIdx(i + 3, coord_size))}) {
int32_t convex_edge_length = edge_length_list[convex_edge_idx];
int32_t adj_edge_length = edge_length_list[adj_edge_idx];
int32_t length_edge_length = edge_length_list[length_edge_idx];
if (!(cur_rule.convex_length <= convex_edge_length && cur_rule.adjacent_length <= adj_edge_length && cur_rule.length >= length_edge_length)) {
continue; // 不满足规则
}
// 满足长度规则,那么此时convex_edge将作为cut的overhang
// 用convex边和短边构成矩形
Segment<PlanarCoord>& short_edge = adj_edge_length < convex_edge_length ? edge_list[adj_edge_idx] : edge_list[convex_edge_idx];
PlanarRect metal_rect = DRCUTIL.getBoundingBox(
{short_edge.get_first(), short_edge.get_second(), edge_list[convex_edge_idx].get_first(), edge_list[convex_edge_idx].get_second()});
// 查询下面的cut,没有cut就跳过,理论上来说需要考虑above和below,这里为了方便先只考虑below也就是t28的情况
std::vector<std::pair<BGRectInt, int32_t>> cut_bg_rect_net_pair_list;
cut_bg_rtree_map[below_cuting_layer_idx].query(bgi::intersects(DRCUTIL.convertToBGRectInt(metal_rect)),
std::back_inserter(cut_bg_rect_net_pair_list));
// 没有cut就跳过
if (cut_bg_rect_net_pair_list.empty()) {
continue;
}
// 拿到convex edge和adj edge的方向,这个方向依赖于boost对hole polygon的逆时针遍历,不然可能导致出错
Orientation convex_edge_orientation = DRCUTIL.getOrientation(edge_list[convex_edge_idx].get_first(), edge_list[convex_edge_idx].get_second());
Orientation adj_edge_orientation = DRCUTIL.getOrientation(edge_list[adj_edge_idx].get_first(), edge_list[adj_edge_idx].get_second());

int32_t require_overhang = cur_rule.overhang;
for (auto& [cut_bg_rect, cut_net_idx] : cut_bg_rect_net_pair_list) {
PlanarRect cut_rect = DRCUTIL.convertToPlanarRect(cut_bg_rect);
if (DRCUTIL.isInside(cut_rect, metal_rect) == false) {
continue; // cut_rect不在metal_rect内
}
// 判断convex overhang是否满足,不满足跳过
int32_t convex_overhang = get_overhang(cut_rect, edge_list[convex_edge_idx]);
if (convex_overhang >= require_overhang) {
continue; // overhang满足要求
}
int32_t adj_overhang = get_overhang(cut_rect, short_edge); // 用来从cut往外扩展得到查询区域的
// 因为要求与cut的prl不为0,所以从cut生成区域往外查
PlanarRect convex_edge_check_rect
= get_convex_rule_check_rect(cut_rect, edge_list[convex_edge_idx], cur_rule.convex_par_within + convex_overhang);
PlanarRect adj_edge_check_rect = get_convex_rule_check_rect(cut_rect, edge_list[adj_edge_idx], cur_rule.convex_par_within + adj_overhang);
bool is_convex_edge_fulfilled = false;
bool is_adj_edge_fulfilled = false;
PlanarRect check_rect = DRCUTIL.getBoundingBox(
{adj_edge_check_rect.get_ll(), adj_edge_check_rect.get_ur(), convex_edge_check_rect.get_ll(), convex_edge_check_rect.get_ur()});
std::vector<std::pair<BGRectInt, int32_t>> env_metal_bg_rect_net_pair_list;
routing_bg_rtree_map[routing_layer_idx].query(bgi::intersects(DRCUTIL.convertToBGRectInt(check_rect)),
std::back_inserter(env_metal_bg_rect_net_pair_list));
std::set<int32_t> env_metal_net_set;
for (auto& [env_metal_bg_rect, env_metal_net_idx] : env_metal_bg_rect_net_pair_list) {
PlanarRect env_metal_rect = DRCUTIL.convertToPlanarRect(env_metal_bg_rect);
if (DRCUTIL.isClosedOverlap(env_metal_rect, metal_rect)) {
continue; // 重叠的矩形不参与检查
}
if (DRCUTIL.isOpenOverlap(env_metal_rect, convex_edge_check_rect)) {
is_convex_edge_fulfilled = true; // convex edge的par满足
env_metal_net_set.insert(env_metal_net_idx);
}
if (DRCUTIL.isOpenOverlap(env_metal_rect, adj_edge_check_rect)) {
is_adj_edge_fulfilled = true; // adj edge的par满足
env_metal_net_set.insert(env_metal_net_idx);
}
}
if (!is_convex_edge_fulfilled || !is_adj_edge_fulfilled) {
// 两个有一个不满足都跳出
continue;
}
for (int32_t env_net_idx : env_metal_net_set) {
// 生成违例
Violation violation;
violation.set_violation_type(ViolationType::kEnclosureEdge);
violation.set_is_routing(true);
violation.set_violation_net_set({env_net_idx, cut_net_idx});
violation.set_layer_idx(below_cuting_layer_idx - 1);
violation.set_rect(cut_rect);
violation.set_required_size(100);
rv_box.get_violation_list().push_back(violation);
}
// cut
}
}
// coord
}
// polygon
}
// polygonset
}
// rule
}
}
}

} // namespace idrc

+ 242
- 0
src/operation/iDRC/source/module/rule_validator/rv_design_rule/EnclosureParallel.cpp View File

@@ -20,6 +20,248 @@ namespace idrc {

void RuleValidator::verifyEnclosureParallel(RVBox& rv_box)
{
#if 1 // 数据结构定义
struct PolyInfo
{
int32_t coord_size = -1;
std::vector<Segment<PlanarCoord>> edge_list;
std::vector<int32_t> edge_length_list;
std::set<int32_t> eol_edge_idx_set;
GTLHolePolyInt gtl_hole_poly;
int32_t poly_info_idx = -1;
};
#endif
std::vector<CutLayer>& cut_layer_list = DRCDM.getDatabase().get_cut_layer_list();
std::map<int32_t, std::vector<int32_t>>& cut_to_adjacent_routing_map = DRCDM.getDatabase().get_cut_to_adjacent_routing_map();

std::map<int32_t, std::map<int32_t, std::vector<PolyInfo>>> routing_net_poly_info_map;
{
std::map<int32_t, std::map<int32_t, GTLPolySetInt>> routing_net_gtl_poly_set_map;
for (DRCShape* drc_shape : rv_box.get_drc_env_shape_list()) {
if (drc_shape->get_is_routing()) {
routing_net_gtl_poly_set_map[drc_shape->get_layer_idx()][drc_shape->get_net_idx()] += DRCUTIL.convertToGTLRectInt(drc_shape->get_rect());
}
}
for (DRCShape* drc_shape : rv_box.get_drc_result_shape_list()) {
if (drc_shape->get_is_routing()) {
routing_net_gtl_poly_set_map[drc_shape->get_layer_idx()][drc_shape->get_net_idx()] += DRCUTIL.convertToGTLRectInt(drc_shape->get_rect());
}
}
for (auto& [routing_layer_idx, net_gtl_poly_set_map] : routing_net_gtl_poly_set_map) {
for (auto& [net_idx, gtl_poly_set] : net_gtl_poly_set_map) {
std::vector<GTLHolePolyInt> gtl_hole_poly_list;
gtl_poly_set.get(gtl_hole_poly_list);
for (GTLHolePolyInt& gtl_hole_poly : gtl_hole_poly_list) {
int32_t coord_size = static_cast<int32_t>(gtl_hole_poly.size());
if (coord_size < 4) {
continue;
}
std::vector<PlanarCoord> coord_list;
for (auto iter = gtl_hole_poly.begin(); iter != gtl_hole_poly.end(); iter++) {
coord_list.push_back(DRCUTIL.convertToPlanarCoord(*iter));
}
std::vector<bool> convex_corner_list;
std::vector<Segment<PlanarCoord>> edge_list;
std::vector<int32_t> edge_length_list;
for (int32_t i = 0; i < coord_size; i++) {
PlanarCoord& pre_coord = coord_list[getIdx(i - 1, coord_size)];
PlanarCoord& curr_coord = coord_list[i];
PlanarCoord& post_coord = coord_list[getIdx(i + 1, coord_size)];
convex_corner_list.push_back(DRCUTIL.isConvexCorner(DRCUTIL.getRotation(gtl_hole_poly), pre_coord, curr_coord, post_coord));
edge_list.push_back(Segment<PlanarCoord>(pre_coord, curr_coord));
edge_length_list.push_back(DRCUTIL.getManhattanDistance(pre_coord, curr_coord));
}
std::set<int32_t> eol_edge_idx_set;
for (int32_t i = 0; i < coord_size; i++) {
if (convex_corner_list[getIdx(i - 1, coord_size)] && convex_corner_list[i]) {
eol_edge_idx_set.insert(i);
}
}
routing_net_poly_info_map[routing_layer_idx][net_idx].emplace_back(coord_size, edge_list, edge_length_list, eol_edge_idx_set, gtl_hole_poly);
}
}
}
}
std::map<int32_t, bgi::rtree<std::pair<BGRectInt, std::pair<int32_t, int32_t>>, bgi::quadratic<16>>> routing_bg_rtree_map;
{
for (auto& [routing_layer_idx, net_poly_info_map] : routing_net_poly_info_map) {
for (auto& [net_idx, poly_info_list] : net_poly_info_map) {
for (int32_t i = 0; i < static_cast<int32_t>(poly_info_list.size()); i++) {
std::vector<GTLRectInt> gtl_rect_list;
gtl::get_max_rectangles(gtl_rect_list, poly_info_list[i].gtl_hole_poly);
for (GTLRectInt& gtl_rect : gtl_rect_list) {
routing_bg_rtree_map[routing_layer_idx].insert(std::make_pair(DRCUTIL.convertToBGRectInt(gtl_rect), std::make_pair(net_idx, i)));
}
poly_info_list[i].poly_info_idx = i;
}
}
}
}
std::map<int32_t, std::vector<PlanarRect>> cut_rect_map;
for (DRCShape* drc_shape : rv_box.get_drc_result_shape_list()) {
if (!drc_shape->get_is_routing()) {
cut_rect_map[drc_shape->get_layer_idx()].push_back(drc_shape->get_rect());
}
}
for (auto& [cut_layer_idx, cut_rect_list] : cut_rect_map) {
std::vector<int32_t>& routing_layer_idx_list = cut_to_adjacent_routing_map[cut_layer_idx];
if (routing_layer_idx_list.size() < 2) {
continue;
}
int32_t above_routing_layer_idx = *std::max_element(routing_layer_idx_list.begin(), routing_layer_idx_list.end());
int32_t below_routing_layer_idx = *std::min_element(routing_layer_idx_list.begin(), routing_layer_idx_list.end());
EnclosureParallelRule curr_rule = cut_layer_list[cut_layer_idx].get_enclosure_parallel_rule();
for (PlanarRect& cut_rect : cut_rect_list) {
std::set<Segment<PlanarCoord>, CmpSegmentXASC> processed_segment_set;
for (int32_t routing_layer_idx : routing_layer_idx_list) {
if (curr_rule.has_above && (routing_layer_idx != above_routing_layer_idx)) {
continue;
}
if (curr_rule.has_below && (routing_layer_idx != below_routing_layer_idx)) {
continue;
}
std::vector<std::pair<BGRectInt, std::pair<int32_t, int32_t>>> bg_rect_net_pair_list;
routing_bg_rtree_map[routing_layer_idx].query(bgi::intersects(DRCUTIL.convertToBGRectInt(cut_rect)), std::back_inserter(bg_rect_net_pair_list));
std::vector<Orientation> orientation_list;
{
std::map<Orientation, int32_t> orient_overhang_map;
for (auto& [bg_rect, net_poly_info_idx_pair] : bg_rect_net_pair_list) {
PlanarRect routing_rect = DRCUTIL.convertToPlanarRect(bg_rect);
if (!DRCUTIL.isClosedOverlap(routing_rect, cut_rect)) {
continue;
}
if (routing_rect.get_ll_x() <= cut_rect.get_ll_x()) {
orient_overhang_map[Orientation::kWest]
= std::max(orient_overhang_map[Orientation::kWest], std::abs(cut_rect.get_ll_x() - routing_rect.get_ll_x()));
}
if (routing_rect.get_ur_x() >= cut_rect.get_ur_x()) {
orient_overhang_map[Orientation::kEast]
= std::max(orient_overhang_map[Orientation::kEast], std::abs(cut_rect.get_ur_x() - routing_rect.get_ur_x()));
}
if (routing_rect.get_ur_y() >= cut_rect.get_ur_y()) {
orient_overhang_map[Orientation::kNorth]
= std::max(orient_overhang_map[Orientation::kNorth], std::abs(cut_rect.get_ur_y() - routing_rect.get_ur_y()));
}
if (routing_rect.get_ll_y() <= cut_rect.get_ll_y()) {
orient_overhang_map[Orientation::kSouth]
= std::max(orient_overhang_map[Orientation::kSouth], std::abs(cut_rect.get_ll_y() - routing_rect.get_ll_y()));
}
}
for (auto& [orient, overhang] : orient_overhang_map) {
if (overhang >= curr_rule.overhang) {
continue;
}
orientation_list.push_back(orient);
}
}
for (auto& [bg_rect, net_poly_info_idx_pair] : bg_rect_net_pair_list) {
int32_t net_idx = net_poly_info_idx_pair.first;
PlanarRect routing_rect = DRCUTIL.convertToPlanarRect(bg_rect);
if (!DRCUTIL.isClosedOverlap(routing_rect, cut_rect)) {
continue;
}
PolyInfo& poly_info = routing_net_poly_info_map[routing_layer_idx][net_idx][net_poly_info_idx_pair.second];
for (Orientation& orient : orientation_list) {
int32_t edge_idx = -1;
for (int32_t eol_idx : poly_info.eol_edge_idx_set) {
if (DRCUTIL.isInside(poly_info.edge_list[eol_idx], routing_rect.getOrientEdge(orient))) {
edge_idx = eol_idx;
break;
}
}
if (edge_idx == -1) {
continue;
}
Segment<PlanarCoord> curr_segment = poly_info.edge_list[getIdx(edge_idx, poly_info.coord_size)];
if (DRCUTIL.exist(processed_segment_set, curr_segment)) {
continue;
}
if (DRCUTIL.getManhattanDistance(curr_segment.get_first(), curr_segment.get_second()) >= curr_rule.eol_width) {
continue;
}
int32_t pre_segment_length;
int32_t post_segment_length;
{
Segment<PlanarCoord> pre_segment = poly_info.edge_list[getIdx(edge_idx - 1, poly_info.coord_size)];
Segment<PlanarCoord> post_segment = poly_info.edge_list[getIdx(edge_idx + 1, poly_info.coord_size)];
pre_segment_length = DRCUTIL.getManhattanDistance(pre_segment.get_first(), pre_segment.get_second());
post_segment_length = DRCUTIL.getManhattanDistance(post_segment.get_first(), post_segment.get_second());
}
if (curr_rule.has_min_length && pre_segment_length < curr_rule.min_length && post_segment_length < curr_rule.min_length) {
continue;
}
processed_segment_set.insert(curr_segment);
std::set<std::set<int32_t>> violation_net_set_set;
{
PlanarRect left_par_rect;
PlanarRect right_par_rect;
{
int32_t par_spacing = curr_rule.par_spacing - DRCUTIL.getManhattanDistance(curr_segment.get_first(), curr_segment.get_second());
if (orient == Orientation::kEast) {
left_par_rect = DRCUTIL.getEnlargedRect(curr_segment.get_first(), curr_rule.backward_ext + 1, par_spacing, curr_rule.forward_ext + 1, 0);
right_par_rect = DRCUTIL.getEnlargedRect(curr_segment.get_second(), curr_rule.backward_ext + 1, 0, curr_rule.forward_ext + 1, par_spacing);
} else if (orient == Orientation::kWest) {
left_par_rect = DRCUTIL.getEnlargedRect(curr_segment.get_first(), curr_rule.forward_ext + 1, 0, curr_rule.backward_ext + 1, par_spacing);
right_par_rect = DRCUTIL.getEnlargedRect(curr_segment.get_second(), curr_rule.forward_ext + 1, par_spacing, curr_rule.backward_ext + 1, 0);
} else if (orient == Orientation::kSouth) {
left_par_rect = DRCUTIL.getEnlargedRect(curr_segment.get_first(), par_spacing, curr_rule.forward_ext + 1, 0, curr_rule.backward_ext + 1);
right_par_rect = DRCUTIL.getEnlargedRect(curr_segment.get_second(), 0, curr_rule.forward_ext + 1, par_spacing, curr_rule.backward_ext + 1);
} else if (orient == Orientation::kNorth) {
left_par_rect = DRCUTIL.getEnlargedRect(curr_segment.get_first(), 0, curr_rule.backward_ext + 1, par_spacing, curr_rule.forward_ext + 1);
right_par_rect = DRCUTIL.getEnlargedRect(curr_segment.get_second(), par_spacing, curr_rule.backward_ext + 1, 0, curr_rule.forward_ext + 1);
} else {
DRCLOG.error(Loc::current(), "The orientation is error!");
}
}
std::vector<std::pair<BGRectInt, std::pair<int32_t, int32_t>>> env_bg_rect_net_pair_list;
{
PlanarRect check_rect;
if (pre_segment_length >= curr_rule.min_length && post_segment_length >= curr_rule.min_length) {
check_rect = DRCUTIL.getBoundingBox({left_par_rect.get_ll(), left_par_rect.get_ur(), right_par_rect.get_ll(), right_par_rect.get_ur()});
} else if (pre_segment_length >= curr_rule.min_length) {
check_rect = left_par_rect;
} else if (post_segment_length >= curr_rule.min_length) {
check_rect = right_par_rect;
}
routing_bg_rtree_map[routing_layer_idx].query(bgi::intersects(DRCUTIL.convertToBGRectInt(check_rect)),
std::back_inserter(env_bg_rect_net_pair_list));
}
std::set<int32_t> left_par_net_idx_set;
std::set<int32_t> right_par_net_idx_set;
for (auto& [bg_env_rect, env_net_poly_info_idx_pair] : env_bg_rect_net_pair_list) {
PlanarRect env_routing_rect = DRCUTIL.convertToPlanarRect(bg_env_rect);
if (DRCUTIL.isClosedOverlap(routing_rect, env_routing_rect)) {
continue;
}
if (DRCUTIL.isOpenOverlap(env_routing_rect, left_par_rect)) {
left_par_net_idx_set.insert(env_net_poly_info_idx_pair.first);
}
if (DRCUTIL.isOpenOverlap(env_routing_rect, right_par_rect)) {
right_par_net_idx_set.insert(env_net_poly_info_idx_pair.first);
}
}
for (int32_t left_par_net_idx : left_par_net_idx_set) {
violation_net_set_set.insert({left_par_net_idx, net_idx});
}
for (int32_t right_par_net_idx : right_par_net_idx_set) {
violation_net_set_set.insert({right_par_net_idx, net_idx});
}
}
for (const std::set<int32_t>& violation_net_set : violation_net_set_set) {
Violation violation;
violation.set_violation_type(ViolationType::kEnclosureParallel);
violation.set_is_routing(true);
violation.set_violation_net_set(violation_net_set);
violation.set_layer_idx(below_routing_layer_idx);
violation.set_rect(cut_rect);
violation.set_required_size(curr_rule.overhang);
rv_box.get_violation_list().push_back(violation);
}
}
}
}
}
}
}

} // namespace idrc

+ 144
- 168
src/operation/iDRC/source/module/rule_validator/rv_design_rule/EndOfLineSpacing.cpp View File

@@ -122,7 +122,7 @@ void RuleValidator::verifyEndOfLineSpacing(RVBox& rv_box)
break;
}
}
std::map<Segment<PlanarCoord>, std::vector<std::pair<Segment<PlanarCoord>, Violation>>, CmpSegmentXASC> edge_violation_map;
std::map<Segment<PlanarCoord>, std::vector<std::pair<Violation, Segment<PlanarCoord>>>, CmpSegmentXASC> edge_violation_edge_map;
for (auto& [net_idx, poly_info_list] : net_poly_info_map) {
for (PolyInfo& poly_info : poly_info_list) {
if (DRCUTIL.getRotation(poly_info.gtl_hole_poly) != Rotation::kCounterclockwise) {
@@ -148,6 +148,10 @@ void RuleValidator::verifyEndOfLineSpacing(RVBox& rv_box)
for (int32_t eol_edge_idx : poly_info.eol_edge_idx_set) {
PlanarCoord pre_coord = poly_info.coord_list[getIdx(eol_edge_idx - 1, poly_info.coord_size)];
PlanarCoord curr_coord = poly_info.coord_list[getIdx(eol_edge_idx, poly_info.coord_size)];
if (!DRCUTIL.isRightAngled(pre_coord, curr_coord)) {
DRCLOG.error(Loc::current(), "The edge is error!");
}
Direction direction = DRCUTIL.getDirection(pre_coord, curr_coord);
Orientation orientation = DRCUTIL.getOrientation(pre_coord, curr_coord);
// all spacing rect
std::vector<PlanarRect> eol_spacing_rect_list;
@@ -203,8 +207,8 @@ void RuleValidator::verifyEndOfLineSpacing(RVBox& rv_box)
}
}
}
// env_net_polygon_map
std::map<int32_t, std::set<int32_t>> env_net_polygon_map;
// env_net_poly_info_idx_map
std::map<int32_t, std::set<int32_t>> env_net_poly_info_idx_map;
{
int32_t max_eol_spacing = 0;
int32_t max_eol_width = 0;
@@ -239,220 +243,191 @@ void RuleValidator::verifyEndOfLineSpacing(RVBox& rv_box)
std::vector<std::pair<BGRectInt, std::pair<int32_t, int32_t>>> bg_rect_net_pair_list;
routing_bg_rtree_map[routing_layer_idx].query(bgi::intersects(DRCUTIL.convertToBGRectInt(max_check_rect)),
std::back_inserter(bg_rect_net_pair_list));
for (auto& [bgrect, net_polygon_pair] : bg_rect_net_pair_list) {
env_net_polygon_map[net_polygon_pair.first].insert(net_polygon_pair.second);
for (auto& [bg_env_rect, net_poly_info_idx_pair] : bg_rect_net_pair_list) {
env_net_poly_info_idx_map[net_poly_info_idx_pair.first].insert(net_poly_info_idx_pair.second);
}
}
/////////////////////////////////////////
/////////////////////////////////////////
/////////////////////////////////////////
/////////////////////////////////////////
// 基本条件 满足eol_width // segment所在的max rect
// segment 所在的线rect
PlanarRect eol_segment_rect = DRCUTIL.getBoundingBox({pre_coord, curr_coord});
// 拿到edge的方向
Direction direction = DRCUTIL.getDirection(pre_coord, curr_coord);
if (direction != Direction::kHorizontal && direction != Direction::kVertical) {
DRCLOG.error(Loc::current(), "get wrong direction!");
}

// 普通的left right par
std::vector<bool> left_par(end_of_line_spacing_rule_list.size(), false);
std::vector<bool> right_par(end_of_line_spacing_rule_list.size(), false);

for (int32_t eol_rule_idx = 0; eol_rule_idx < end_of_line_spacing_rule_list.size(); eol_rule_idx++) {
// 不存在par规则的直接跳过 same metal后边再判断
if (end_of_line_spacing_rule_list[eol_rule_idx].has_par == false || end_of_line_spacing_rule_list[eol_rule_idx].has_same_metal) {
continue;
// skip_rule_idx_set
std::set<int32_t> skip_rule_idx_set;
for (int32_t i = 0; i < static_cast<int32_t>(end_of_line_spacing_rule_list.size()); i++) {
if (poly_info.edge_length_list[eol_edge_idx] >= end_of_line_spacing_rule_list[i].eol_width) {
skip_rule_idx_set.insert(i);
}

for (auto& [env_net_idx, env_poly_idx_set] : env_net_polygon_map) {
if (left_par[eol_rule_idx] && right_par[eol_rule_idx]) {
if (end_of_line_spacing_rule_list[i].has_min_length) {
if (poly_info.edge_length_list[getIdx(eol_edge_idx - 1, poly_info.coord_size)] < end_of_line_spacing_rule_list[i].min_length
&& poly_info.edge_length_list[getIdx(eol_edge_idx + 1, poly_info.coord_size)] < end_of_line_spacing_rule_list[i].min_length) {
skip_rule_idx_set.insert(i);
}
}
}
// par status
std::vector<bool> left_par_status_list(end_of_line_spacing_rule_list.size(), false);
std::vector<bool> right_par_status_list(end_of_line_spacing_rule_list.size(), false);
for (auto& [env_net_idx, env_poly_info_idx_set] : env_net_poly_info_idx_map) {
for (int32_t env_poly_info_idx : env_poly_info_idx_set) {
if (env_net_idx == net_idx && env_poly_info_idx == poly_info.poly_info_idx) {
continue;
}
for (const int32_t& env_poly_idx : env_poly_idx_set) {
if (env_net_idx == net_idx && env_poly_idx == poly_info.poly_info_idx) {
continue; // 同一个polygon不参与par,这里存疑
}
PolyInfo& env_poly_info = routing_net_poly_info_map[routing_layer_idx][env_net_idx][env_poly_idx];
// 在left par rect
if (gtl::area(env_poly_info.gtl_hole_poly & DRCUTIL.convertToGTLRectInt(left_par_spacing_rect_list[eol_rule_idx])) > 0) {
left_par[eol_rule_idx] = true;
for (Segment<PlanarCoord>& env_edge : net_poly_info_map[env_net_idx][env_poly_info_idx].edge_list) {
if (DRCUTIL.getDirection(env_edge.get_first(), env_edge.get_second()) == direction) {
continue;
}
// 在right par rect
if (gtl::area(env_poly_info.gtl_hole_poly & DRCUTIL.convertToGTLRectInt(right_par_spacing_rect_list[eol_rule_idx])) > 0) {
right_par[eol_rule_idx] = true;
for (size_t i = 0; i < end_of_line_spacing_rule_list.size(); i++) {
if (!end_of_line_spacing_rule_list[i].has_par || end_of_line_spacing_rule_list[i].has_same_metal) {
continue;
}
if (DRCUTIL.isOpenOverlap(env_edge.get_first(), env_edge.get_second(), left_par_spacing_rect_list[i])) {
left_par_status_list[i] = true;
}
if (DRCUTIL.isOpenOverlap(env_edge.get_first(), env_edge.get_second(), right_par_spacing_rect_list[i])) {
right_par_status_list[i] = true;
}
}
}
}
}

PlanarCoord post_coord = poly_info.coord_list[getIdx(eol_edge_idx + 1, poly_info.coord_size)];
PlanarRect eol_segment_max_rect = DRCUTIL.getBoundingBox({pre_coord, post_coord});

for (int32_t eol_rule_idx = end_of_line_spacing_rule_list.size() - 1; eol_rule_idx >= 0; eol_rule_idx--) {
EndOfLineSpacingRule cur_rule = end_of_line_spacing_rule_list[eol_rule_idx];
// eol width , min length ,没有same metal 的par,这三个可以在最外层判断掉
if (poly_info.edge_length_list[eol_edge_idx] >= cur_rule.eol_width) {
continue; // 不满足eol width
}
// 如果有min length判断min length
if (cur_rule.has_min_length) {
int32_t min_length = cur_rule.min_length;
if (poly_info.edge_length_list[getIdx(eol_edge_idx - 1, poly_info.coord_size)] < min_length
&& poly_info.edge_length_list[getIdx(eol_edge_idx + 1, poly_info.coord_size)] < min_length) {
continue; // 不满足min length
}
// check
PlanarRect curr_edge_rect = DRCUTIL.getRect(pre_coord, curr_coord);
PlanarRect curr_max_rect = DRCUTIL.getRect(pre_coord, poly_info.coord_list[getIdx(eol_edge_idx + 1, poly_info.coord_size)]);
for (auto& [env_net_idx, env_poly_info_idx_set] : env_net_poly_info_idx_map) {
if (net_idx == -1 && env_net_idx == -1) {
continue;
}
// 如果有par判断par
if (cur_rule.has_par && cur_rule.has_same_metal == false) {
if (cur_rule.has_two_edges) {
if (!(left_par[eol_rule_idx] && right_par[eol_rule_idx])) {
continue; // 两边都需要满足
for (int32_t env_poly_info_idx : env_poly_info_idx_set) {
PolyInfo& env_poly_info = net_poly_info_map[env_net_idx][env_poly_info_idx];
std::vector<Segment<PlanarCoord>>& env_edge_list = env_poly_info.edge_list;
int32_t env_edge_size = static_cast<int32_t>(env_edge_list.size());
for (int32_t env_edge_idx = 0; env_edge_idx < env_edge_size; env_edge_idx++) {
Segment<PlanarCoord>& env_edge = env_edge_list[env_edge_idx];
if (DRCUTIL.getDirection(env_edge.get_first(), env_edge.get_second()) != direction) {
continue;
}
} else {
if (!(left_par[eol_rule_idx] || right_par[eol_rule_idx])) {
continue; // 至少一边满足
if (DRCUTIL.getOrientation(env_edge.get_first(), env_edge.get_second()) == orientation) {
continue;
}
}
}
for (auto& [env_net_idx, env_poly_idx_set] : env_net_polygon_map) {
if (env_net_idx == -1 && net_idx == -1) {
continue;
}

for (const int32_t& env_poly_idx : env_poly_idx_set) {
PolyInfo& env_poly_info = routing_net_poly_info_map[routing_layer_idx][env_net_idx][env_poly_idx];
for (int32_t env_segment_idx = 0; env_segment_idx < env_poly_info.edge_list.size(); env_segment_idx++) {
Segment<PlanarCoord>& env_segment = env_poly_info.edge_list[env_segment_idx];
// eol 的另一条边满足direction相同,orirntation不同
Direction env_direction = DRCUTIL.getDirection(env_segment.get_first(), env_segment.get_second());
Orientation env_orientation = DRCUTIL.getOrientation(env_segment.get_first(), env_segment.get_second());
if (!(env_direction == direction && env_orientation != orientation)) {
PlanarRect env_edge_rect = DRCUTIL.getRect(env_edge.get_first(), env_edge.get_second());
for (int32_t eol_rule_idx = static_cast<int32_t>(end_of_line_spacing_rule_list.size()) - 1; eol_rule_idx >= 0; eol_rule_idx--) {
if (DRCUTIL.exist(skip_rule_idx_set, eol_rule_idx)) {
continue;
}
PlanarRect env_segment_rect = DRCUTIL.getBoundingBox({env_segment.get_first(), env_segment.get_second()});
// 下面的是与env segment相关的rule,必须放到segment粒度
// 如果有cut相关的判断cut的规则 cut不满足直接跳出
if (cur_rule.has_enclose_cut) {
int32_t enclosed_dist = cur_rule.enclosed_dist;
int32_t cut_to_metal_spacing = cur_rule.cut_to_metal_spacing;
// 拿到当前矩形下cut
EndOfLineSpacingRule& curr_rule = end_of_line_spacing_rule_list[eol_rule_idx];
// has_enclose_cut
if (curr_rule.has_enclose_cut) {
bool is_cut_require = false;
// ALL CUT要求所有的都满足,但是实测下来并非如此
for (PlanarRect& cut_rect : adjacent_cut_shape_list) {
if (DRCUTIL.isClosedOverlap(eol_segment_max_rect, cut_rect)) {
int32_t cut_to_segment_dis = DRCUTIL.getEuclideanDistance(cut_rect, eol_segment_rect);
int32_t cut_to_env_dis = DRCUTIL.getEuclideanDistance(cut_rect, env_segment_rect);
if (cut_to_segment_dis < enclosed_dist && cut_to_env_dis < cut_to_metal_spacing) {
for (PlanarRect& adjacent_cut_shape : adjacent_cut_shape_list) {
if (DRCUTIL.isClosedOverlap(curr_max_rect, adjacent_cut_shape)) {
if (DRCUTIL.getEuclideanDistance(adjacent_cut_shape, curr_edge_rect) < curr_rule.enclosed_dist
&& DRCUTIL.getEuclideanDistance(adjacent_cut_shape, env_edge_rect) < curr_rule.cut_to_metal_spacing) {
is_cut_require = true;
break;
}
}
}
if (is_cut_require == false) {
if (!is_cut_require) {
continue;
}
}

// same metal的单独判断
if (cur_rule.has_same_metal) {
int32_t par_within = cur_rule.par_within;
Segment<PlanarCoord> pre_env_segment = env_poly_info.edge_list[getIdx(env_segment_idx - 1, env_poly_info.edge_list.size())];
Segment<PlanarCoord> post_env_segment = env_poly_info.edge_list[getIdx(env_segment_idx + 1, env_poly_info.edge_list.size())];
PlanarRect pre_env_segment_rect = DRCUTIL.getBoundingBox({pre_env_segment.get_first(), pre_env_segment.get_second()});
PlanarRect post_env_segment_rect = DRCUTIL.getBoundingBox({post_env_segment.get_first(), post_env_segment.get_second()});
bool pre_left = false;
bool pre_right = false;
if (DRCUTIL.isOpenOverlap(pre_env_segment_rect, left_par_spacing_rect_list[eol_rule_idx])
&& DRCUTIL.getParallelLength(pre_env_segment_rect, left_par_spacing_rect_list[eol_rule_idx]) < par_within) {
pre_left = true;
} else if (DRCUTIL.isOpenOverlap(pre_env_segment_rect, right_par_spacing_rect_list[eol_rule_idx])
&& DRCUTIL.getParallelLength(pre_env_segment_rect, right_par_spacing_rect_list[eol_rule_idx]) < par_within) {
pre_right = true;
}

bool post_left = false;
bool post_right = false;
if (DRCUTIL.isOpenOverlap(post_env_segment_rect, left_par_spacing_rect_list[eol_rule_idx])
&& DRCUTIL.getParallelLength(post_env_segment_rect, left_par_spacing_rect_list[eol_rule_idx]) < par_within) {
post_left = true;
} else if (DRCUTIL.isOpenOverlap(post_env_segment_rect, right_par_spacing_rect_list[eol_rule_idx])
&& DRCUTIL.getParallelLength(post_env_segment_rect, right_par_spacing_rect_list[eol_rule_idx]) < par_within) {
post_right = true;
}
if (cur_rule.has_two_edges) {
if (!(pre_left && post_right) && !(pre_right && post_left)) {
continue; // 两边都需要满足
// has_par
if (curr_rule.has_par) {
if (curr_rule.has_same_metal) {
bool pre_left = false;
bool pre_right = false;
{
Segment<PlanarCoord> edge = env_edge_list[getIdx(env_edge_idx - 1, env_edge_size)];
PlanarRect rect = DRCUTIL.getRect(edge.get_first(), edge.get_second());
if (DRCUTIL.isOpenOverlap(rect, left_par_spacing_rect_list[eol_rule_idx])
&& DRCUTIL.getParallelLength(rect, left_par_spacing_rect_list[eol_rule_idx]) < curr_rule.par_within) {
pre_left = true;
} else if (DRCUTIL.isOpenOverlap(rect, right_par_spacing_rect_list[eol_rule_idx])
&& DRCUTIL.getParallelLength(rect, right_par_spacing_rect_list[eol_rule_idx]) < curr_rule.par_within) {
pre_right = true;
}
}
bool post_left = false;
bool post_right = false;
{
Segment<PlanarCoord> edge = env_edge_list[getIdx(env_edge_idx + 1, env_edge_size)];
PlanarRect rect = DRCUTIL.getBoundingBox({edge.get_first(), edge.get_second()});
if (DRCUTIL.isOpenOverlap(rect, left_par_spacing_rect_list[eol_rule_idx])
&& DRCUTIL.getParallelLength(rect, left_par_spacing_rect_list[eol_rule_idx]) < curr_rule.par_within) {
post_left = true;
} else if (DRCUTIL.isOpenOverlap(rect, right_par_spacing_rect_list[eol_rule_idx])
&& DRCUTIL.getParallelLength(rect, right_par_spacing_rect_list[eol_rule_idx]) < curr_rule.par_within) {
post_right = true;
}
}
if (curr_rule.has_two_edges) {
if (!(pre_left && post_right) && !(pre_right && post_left)) {
continue;
}
} else {
if (!(pre_left || pre_right) && !(post_left || post_right)) {
continue;
}
}
} else {
if (!(pre_left || pre_right) && !(post_left || post_right)) {
continue; // 至少一边满足
if (curr_rule.has_two_edges) {
if (!(left_par_status_list[eol_rule_idx] && right_par_status_list[eol_rule_idx])) {
continue;
}
} else {
if (!(left_par_status_list[eol_rule_idx] || right_par_status_list[eol_rule_idx])) {
continue;
}
}
}
}

// 如果有ete那么判断ete
PlanarRect eol_spacing_rect = eol_spacing_rect_list[eol_rule_idx];
if (cur_rule.has_ete) {
// 判断是否为ete
if (DRCUTIL.exist(env_poly_info.eol_edge_idx_set, env_segment_idx)
&& env_poly_info.edge_length_list[env_segment_idx] < cur_rule.eol_width) {
eol_spacing_rect = ete_spacing_rect_list[eol_rule_idx];
// eol & ete
PlanarRect spacing_rect = eol_spacing_rect_list[eol_rule_idx];
if (curr_rule.has_ete) {
if (DRCUTIL.exist(env_poly_info.eol_edge_idx_set, env_edge_idx) && env_poly_info.edge_length_list[env_edge_idx] < curr_rule.eol_width) {
spacing_rect = ete_spacing_rect_list[eol_rule_idx];
}
}
// 满足当前所有规则那么生成违例矩形并break
PlanarRect violation_rect = DRCUTIL.getSpacingRect(eol_segment_rect, env_segment_rect);
int32_t require_size = direction == Direction::kHorizontal ? eol_spacing_rect.getYSpan() : eol_spacing_rect.getXSpan();
if (!DRCUTIL.isOpenOverlap(eol_spacing_rect, env_segment_rect)) {
continue; // 由于within不一样,所以需要往前
if (!DRCUTIL.isOpenOverlap(spacing_rect, env_edge_rect)) {
continue;
}
Violation violation;
violation.set_violation_type(ViolationType::kEndOfLineSpacing);
violation.set_required_size(require_size);
violation.set_required_size(direction == Direction::kHorizontal ? spacing_rect.getYSpan() : spacing_rect.getXSpan());
violation.set_is_routing(true);
violation.set_violation_net_set({net_idx, env_net_idx});
violation.set_layer_idx(routing_layer_idx);
violation.set_rect(violation_rect);
edge_violation_map[poly_info.edge_list[eol_edge_idx]].push_back(std::make_pair(env_poly_info.edge_list[env_segment_idx], violation));
violation.set_rect(DRCUTIL.getSpacingRect(curr_edge_rect, env_edge_rect));
edge_violation_edge_map[poly_info.edge_list[eol_edge_idx]].push_back(std::make_pair(violation, env_edge_list[env_edge_idx]));
break;
}
}
}
// rule
}
// eol edge
}
// polygon
}
}

// 每个segment保留最短距离的违例
std::set<Violation, CmpViolation> no_need_vio;
for (auto& [segment, segment_vio_list] : edge_violation_map) {
bool use_x_span = segment.get_first().get_x() == segment.get_second().get_x();
int32_t best_idx = 0;

int32_t best_length = use_x_span ? segment_vio_list[0].second.getXSpan() : segment_vio_list[0].second.getYSpan();
for (int32_t i = 0; i < segment_vio_list.size(); i++) {
int32_t now_length = use_x_span ? segment_vio_list[i].second.getXSpan() : segment_vio_list[i].second.getYSpan();
if (now_length < best_length) {
best_length = now_length;
best_idx = i;
std::set<Violation, CmpViolation> invalid_violation_set;
for (auto& [edge, violation_edge_list] : edge_violation_edge_map) {
int32_t min_length = INT32_MAX;
for (auto& [violation, edge] : violation_edge_list) {
if (DRCUTIL.isHorizontal(edge.get_first(), edge.get_second())) {
min_length = std::min(min_length, violation.getYSpan());
} else {
min_length = std::min(min_length, violation.getXSpan());
}
}
for (int32_t i = 0; i < segment_vio_list.size(); i++) {
bool use_x_span = segment.get_first().get_x() == segment.get_second().get_x();
int32_t now_length = use_x_span ? segment_vio_list[i].second.getXSpan() : segment_vio_list[i].second.getYSpan();
if (best_length == now_length) {
continue;
for (auto& [violation, edge] : violation_edge_list) {
if (DRCUTIL.isHorizontal(edge.get_first(), edge.get_second())) {
if (min_length != violation.getYSpan()) {
invalid_violation_set.insert(violation);
}
} else {
if (min_length != violation.getXSpan()) {
invalid_violation_set.insert(violation);
}
}
no_need_vio.insert(segment_vio_list[i].second);
}
}

for (auto& [segment, segment_vio_list] : edge_violation_map) {
for (auto& [env_segment, violation] : segment_vio_list) {
if (DRCUTIL.exist(no_need_vio, violation)) {
for (auto& [segment, violation_edge_list] : edge_violation_edge_map) {
for (auto& [violation, edge] : violation_edge_list) {
if (DRCUTIL.exist(invalid_violation_set, violation)) {
continue;
}
rv_box.get_violation_list().push_back(violation);
@@ -460,4 +435,5 @@ void RuleValidator::verifyEndOfLineSpacing(RVBox& rv_box)
}
}
}

} // namespace idrc

+ 0
- 278
src/operation/iDRC/source/module/rule_validator/rv_design_rule/JogToJogSpacing.cpp View File

@@ -22,282 +22,4 @@ void RuleValidator::verifyJogToJogSpacing(RVBox& rv_box)
{
}

#if 0
// 由于与prl有重叠,暂时关闭

/*
思路:
1. 取max_rectangles,获得宽线;
2. 根据宽度,获取宽线对应的rule;
3.宽线膨胀,找到满足 wthin和prl的另外一条线;
4.获取两条线之间的 spacing rect;
5. 减去原来两条线所属线网 在 spacing rect中 凹陷、突出 部分,得到 check_region;
6. 按宽线的宽度方向,切割check_region,得到多个待检查的区域
7. 检查每个待检查的区域,是否满足jog间距的要求;
*/

void RuleValidator::verifyJogToJogSpacing(RVBox& rv_box)
{
/// 规则构造
#if 1
struct JogToJogSpacingRule
{
// 全局参数
int32_t jogToJogSpacing; // jog之间的间距
int32_t jogWidth; // jog宽度
int32_t shortJogSpacing; // 短jog间距
int32_t rows; // 行数

// 每个宽度规则的结构
struct WidthRule
{
int32_t width; // 宽度
int32_t parallelLength; // 平行长度
int32_t within; // within距离
int32_t longJogSpacing; // 长jog间距

WidthRule(int32_t w, int32_t pLength, int32_t w_in, int32_t longJog) : width(w), parallelLength(pLength), within(w_in), longJogSpacing(longJog) {}
};

std::vector<WidthRule> widthRules;

JogToJogSpacingRule(int32_t jts, int32_t jw, int32_t sjs, int32_t rows) : jogToJogSpacing(jts), jogWidth(jw), shortJogSpacing(sjs), rows(rows) {}
void addWidthRule(int32_t width, int32_t parLength, int32_t within, int32_t longJogSpacing)
{
widthRules.emplace_back(width, parLength, within, longJogSpacing);
}
};

JogToJogSpacingRule rule(600, 440, 120, 4);

// rule.addWidthRule(500, 600, 580, 160);
rule.addWidthRule(500, 600, 380, 200);
rule.addWidthRule(940, 1000, 640, 260);
rule.addWidthRule(1260, 1400, 680, 300);
rule.addWidthRule(3000, 3000, 1000, 600);
#endif
// 工具类函数 Rtree
#if 1
auto addRectToRtree
= [](std::map<int32_t, bgi::rtree<std::pair<BGRectInt, int32_t>, bgi::quadratic<16>>>& _query_tree, GTLRectInt rect, int32_t layer_idx, int32_t net_idx) {
BGRectInt rtree_rect(BGPointInt(xl(rect), yl(rect)), BGPointInt(xh(rect), yh(rect)));
_query_tree[layer_idx].insert(std::make_pair(rtree_rect, net_idx));
};
auto queryNetIdbyRtreeWithIntersects = [](std::map<int32_t, bgi::rtree<std::pair<BGRectInt, int32_t>, bgi::quadratic<16>>>& _query_tree, int32_t layer_idx,
int32_t llx, int32_t lly, int32_t urx, int32_t ury) {
std::set<int32_t> net_ids;
std::vector<std::pair<BGRectInt, int32_t>> result;
BGRectInt rect(BGPointInt(llx, lly), BGPointInt(urx, ury));
_query_tree[layer_idx].query(bgi::intersects(rect), std::back_inserter(result));
for (auto& pair : result) {
net_ids.insert(pair.second);
}
return net_ids;
};
auto queryRectbyRtreeWithIntersects = [](std::map<int32_t, bgi::rtree<std::pair<BGRectInt, int32_t>, bgi::quadratic<16>>>& _query_tree, int32_t layer_idx,
int32_t llx, int32_t lly, int32_t urx, int32_t ury) {
std::set<int32_t> net_ids;
std::vector<std::pair<BGRectInt, int32_t>> result;
BGRectInt rect(BGPointInt(llx, lly), BGPointInt(urx, ury));
_query_tree[layer_idx].query(bgi::intersects(rect), std::back_inserter(result));
return result;
};
auto queryNetIdbyRtreeWithWithin = [](std::map<int32_t, bgi::rtree<std::pair<BGRectInt, int32_t>, bgi::quadratic<16>>>& _query_tree, int32_t layer_idx,
int32_t llx, int32_t lly, int32_t urx, int32_t ury) {
std::set<int32_t> net_ids;
std::vector<std::pair<BGRectInt, int32_t>> result;
BGRectInt rect(BGPointInt(llx, lly), BGPointInt(urx, ury));
_query_tree[layer_idx].query(bgi::within(rect), std::back_inserter(result));
for (auto& pair : result) {
net_ids.insert(pair.second);
}
return net_ids;
};
auto queryRectbyRtreeWithWithin = [](std::map<int32_t, bgi::rtree<std::pair<BGRectInt, int32_t>, bgi::quadratic<16>>>& _query_tree, int32_t layer_idx,
int32_t llx, int32_t lly, int32_t urx, int32_t ury) {
std::set<int32_t> net_ids;
std::vector<std::pair<BGRectInt, int32_t>> result;
BGRectInt rect(BGPointInt(llx, lly), BGPointInt(urx, ury));
_query_tree[layer_idx].query(bgi::within(rect), std::back_inserter(result));
return result;
};

auto queryRectbyRtreeWithOverlaps = [](std::map<int32_t, bgi::rtree<std::pair<BGRectInt, int32_t>, bgi::quadratic<16>>>& _query_tree, int32_t layer_idx,
int32_t llx, int32_t lly, int32_t urx, int32_t ury) {
std::set<int32_t> net_ids;
std::vector<std::pair<BGRectInt, int32_t>> result;
BGRectInt rect(BGPointInt(llx, lly), BGPointInt(urx, ury));
_query_tree[layer_idx].query(bgi::overlaps(rect), std::back_inserter(result));
return result;
};

auto get_width_idx = [](int32_t width, std::vector<JogToJogSpacingRule::WidthRule>& width_rules) {
int32_t width_idx = -1;
for (int32_t i = width_rules.size() - 1; i >= 0; i--) {
if (width >= width_rules[i].width) {
width_idx = i;
break;
}
}
return width_idx;
};
auto getApplicableRule
= [](int32_t width, int32_t parallel_length, int32_t within, int32_t long_jog_spacing, int32_t short_jog_spacing, const JogToJogSpacingRule& rule) {
for (int32_t i = rule.widthRules.size() - 1; i >= 0; i--) {
if (width >= rule.widthRules[i].width && parallel_length > rule.widthRules[i].parallelLength && within < rule.widthRules[i].within) {
return i;
}
}
return -1;
};
auto get_parallel_length = [](const PlanarRect& rect_a, const PlanarRect& rect_b) {
int32_t parallel_length = 0;
int32_t h_spacing = std::max(0, std::max(rect_a.get_ll_x() - rect_b.get_ur_x(), rect_b.get_ll_x() - rect_a.get_ur_x()));
int32_t v_spacing = std::max(0, std::max(rect_a.get_ll_y() - rect_b.get_ur_y(), rect_b.get_ll_y() - rect_a.get_ur_y()));

if (h_spacing > 0 && v_spacing > 0) {
parallel_length = 0;
} else {
if (v_spacing == 0) {
parallel_length = std::min(rect_a.get_ur_y(), rect_b.get_ur_y()) - std::max(rect_a.get_ll_y(), rect_b.get_ll_y());
} else {
parallel_length = std::min(rect_a.get_ur_x(), rect_b.get_ur_x()) - std::max(rect_a.get_ll_x(), rect_b.get_ll_x());
}
}
return parallel_length;
};
#endif

// 得到基础数据
std::vector<RoutingLayer>& routing_layer_list = DRCDM.getDatabase().get_routing_layer_list();
// 使用R树查询检测
std::map<int32_t, bgi::rtree<std::pair<BGRectInt, int32_t>, bgi::quadratic<16>>> routing_layer_all_query_tree;

std::map<int32_t, std::map<int32_t, GTLPolySetInt>> routing_layer_net_gtl_all_poly_set;
std::map<int32_t, std::map<int32_t, std::vector<GTLRectInt>>> routing_layer_net_gtl_all_maxrect_list;

{
for (DRCShape* rect : rv_box.get_drc_result_shape_list()) {
if (rect->get_is_routing() == true) {
int32_t layer_idx = rect->get_layer_idx();
int32_t net_idx = rect->get_net_idx();
routing_layer_net_gtl_all_poly_set[layer_idx][net_idx] += DRCUTIL.convertToGTLRectInt(rect->get_rect());
}
}

for (DRCShape* rect : rv_box.get_drc_env_shape_list()) {
if (rect->get_is_routing() == true) {
int32_t layer_idx = rect->get_layer_idx();
int32_t net_idx = rect->get_net_idx();
routing_layer_net_gtl_all_poly_set[layer_idx][net_idx] += DRCUTIL.convertToGTLRectInt(rect->get_rect());
}
}
for (auto& [routing_layer_idx, net_all_gtl_poly_set] : routing_layer_net_gtl_all_poly_set) {
for (auto& [net_idx, res_all_poly_set] : net_all_gtl_poly_set) {
std::vector<GTLRectInt> rect_list;
gtl::get_max_rectangles(rect_list, res_all_poly_set);
for (GTLRectInt& rect : rect_list) {
routing_layer_net_gtl_all_maxrect_list[routing_layer_idx][net_idx].push_back(rect);
addRectToRtree(routing_layer_all_query_tree, rect, routing_layer_idx, net_idx);
}
}
}
}

#if 1
for (auto& [routing_layer_idx, net_gtl_all_maxrect_list] : routing_layer_net_gtl_all_maxrect_list) {
for (auto& [current_net_idx, rect_list] : net_gtl_all_maxrect_list) {
for (GTLRectInt& current_gtl_rect : rect_list) {
PlanarRect current_planar_rect = DRCUTIL.convertToPlanarRect(current_gtl_rect);
auto current_rect_orientation = gtl::guess_orientation(current_gtl_rect);
auto current_rect_width_orientation = current_rect_orientation.get_perpendicular();
int current_rect_width = gtl::delta(current_gtl_rect, current_rect_width_orientation);

// 宽度不满足条件
if (current_rect_width < rule.widthRules[0].width) {
continue;
}

auto checkFunction = [&](const gtl::orientation_2d& orientation) {
GTLRectInt bloat_current_rect = current_gtl_rect;
gtl::bloat(bloat_current_rect, orientation, rule.widthRules[rule.rows - 1].within - 1);

std::vector<std::pair<BGRectInt, int32_t>> around_rect_result
= queryRectbyRtreeWithIntersects(routing_layer_all_query_tree, routing_layer_idx, gtl::xl(bloat_current_rect), gtl::yl(bloat_current_rect),
gtl::xh(bloat_current_rect), gtl::yh(bloat_current_rect));

for (auto& [around_bg_rect, around_rect_net_idx] : around_rect_result) {
GTLRectInt around_gtl_rect = DRCUTIL.convertToGTLRectInt(around_bg_rect);
PlanarRect around_planar_rect = DRCUTIL.convertToPlanarRect(around_gtl_rect);

if (DRCUTIL.isClosedOverlap(current_planar_rect, around_planar_rect)) {
continue;
}

// 创建两个矩形之间的spacing rect
PlanarRect spacing_planar_rect = DRCUTIL.getSpacingRect(current_planar_rect, around_planar_rect);
GTLRectInt spacing_gtl_rect = DRCUTIL.convertToGTLRectInt(spacing_planar_rect);

int32_t spacing_within = gtl::delta(spacing_gtl_rect, current_rect_width_orientation);
int32_t spacing_prl = gtl::delta(spacing_gtl_rect, current_rect_orientation);

int32_t applicable_rule_idx = getApplicableRule(current_rect_width, spacing_prl, spacing_within, rule.jogToJogSpacing, rule.shortJogSpacing, rule);

if (applicable_rule_idx == -1) {
continue;
}

auto& applicable_rule = rule.widthRules[applicable_rule_idx];

GTLPolySetInt check_region_all;
check_region_all += spacing_gtl_rect;

gtl::shrink(spacing_gtl_rect, current_rect_width_orientation, 1);

/// 查询
std::vector<std::pair<BGRectInt, int32_t>> rects_in_spacing_rect
= queryRectbyRtreeWithIntersects(routing_layer_all_query_tree, routing_layer_idx, gtl::xl(spacing_gtl_rect), gtl::yl(spacing_gtl_rect),
gtl::xh(spacing_gtl_rect), gtl::yh(spacing_gtl_rect));

for (auto& [jog_bg_rect, jog_net_idx] : rects_in_spacing_rect) {
if (jog_net_idx != current_net_idx && jog_net_idx != around_rect_net_idx) {
continue;
}
GTLRectInt jog_gtl_rect = DRCUTIL.convertToGTLRectInt(jog_bg_rect);
PlanarRect jog_planar_rect = DRCUTIL.convertToPlanarRect(jog_gtl_rect);

PlanarRect overlap_rect = DRCUTIL.getOverlap(jog_planar_rect, spacing_planar_rect);
GTLRectInt overlap_gtl_rect = DRCUTIL.convertToGTLRectInt(overlap_rect);
check_region_all -= overlap_gtl_rect;
}
std::vector<GTLRectInt> check_regions_rect;
gtl::get_rectangles(check_regions_rect, check_region_all, current_rect_width_orientation);
for (GTLRectInt& check_region_rect : check_regions_rect) {
PlanarRect check_region_planar_rect = DRCUTIL.convertToPlanarRect(check_region_rect);
int32_t jog_width = gtl::delta(check_region_rect, current_rect_orientation);
int32_t jog_spacing = gtl::delta(check_region_rect, current_rect_width_orientation);
int32_t need_sapcing = jog_width > rule.jogWidth ? applicable_rule.longJogSpacing : rule.shortJogSpacing;

if (jog_spacing < need_sapcing) {
Violation violation;
violation.set_violation_type(ViolationType::kJogToJogSpacing);
violation.set_is_routing(true);
violation.set_violation_net_set({current_net_idx, around_rect_net_idx});
violation.set_layer_idx(routing_layer_idx);
violation.set_rect(spacing_planar_rect);
violation.set_required_size(need_sapcing);
rv_box.get_violation_list().push_back(violation);
}
}
}
};

checkFunction(current_rect_width_orientation);
}
}
#endif
}
}
#endif

} // namespace idrc

+ 28
- 1
src/operation/iDRC/source/toolkit/utility/Utility.hpp View File

@@ -44,7 +44,7 @@ class Utility
}

// 获得两个矩形的欧式距离
static double getEuclideanDistance(PlanarRect& a, PlanarRect& b)
static double getEuclideanDistance(const PlanarRect& a, const PlanarRect& b)
{
int32_t x_spacing = std::max(b.get_ll_x() - a.get_ur_x(), a.get_ll_x() - b.get_ur_x());
int32_t y_spacing = std::max(b.get_ll_y() - a.get_ur_y(), a.get_ll_y() - b.get_ur_y());
@@ -194,6 +194,16 @@ class Utility
return isInside(master, seg.get_first()) && isInside(master, seg.get_second());
}

// 线段在线段内
static bool isInside(const Segment<PlanarCoord>& master, const Segment<PlanarCoord>& seg)
{
if (!isRightAngled(master.get_first(), master.get_second()) || !isRightAngled(seg.get_first(), seg.get_second())) {
DRCLOG.error(Loc::current(), "The segment is error!");
}
PlanarRect rect = getRect(master.get_first(), master.get_second());
return isInside(rect, seg.get_first()) && isInside(rect, seg.get_second());
}

/**
* !在检测DRC中
* 如果a与b中有膨胀矩形,那么则用isOpenOverlap
@@ -203,6 +213,11 @@ class Utility
*/
static bool isOpenOverlap(const PlanarRect& a, const PlanarRect& b) { return isOverlap(a, b, false); }

static bool isOpenOverlap(const PlanarCoord& start_coord, const PlanarCoord& end_coord, const PlanarRect& rect)
{
return isOverlap(getRect(start_coord, end_coord), rect, false);
}

/**
* !在检测DRC中
* 如果a与b中有膨胀矩形,那么则用isOpenOverlap
@@ -309,6 +324,18 @@ class Utility
boost_box.max_corner().set<1>(boost_box.max_corner().y() + coord.get_y());
}

static bool isOverlap(GTLPolySetInt a, GTLRectInt b, bool consider_edge = true)
{
GTLPolySetInt gtl_poly_set;
gtl_poly_set += b;
if (consider_edge) {
a.interact(gtl_poly_set);
} else {
a &= gtl_poly_set;
}
return gtl::area(a) > 0;
}

static bool isOverlap(BGRectInt& a, BGRectInt& b, bool consider_edge = true)
{
int32_t a_ll_x = a.min_corner().x(), a_ll_y = a.min_corner().y();


+ 8
- 1
src/operation/iPNP/README.md View File

@@ -73,12 +73,19 @@ cd bin/
### TCL命令
在交互模式下,可以使用以下TCL命令
运行完整的iPNP流程
```tcl
run_pnp -config /path/to/pnp_config.json
```
在VIA1上添加通孔,即连接M2和M1层。根据VIA2的通孔位置,在相同坐标下添加VIA1的通孔,确保电源网络可以完全连通到M1层。
(注意:这个tcl命令不会运行完整的iPNP流程,只用于通孔没达到M1上的数据集)
```tcl
add_via1 -config /path/to/pnp_config.json
```
## 配置文件详解


+ 1
- 0
src/operation/iPNP/example/add_via1.tcl View File

@@ -0,0 +1 @@
add_via1 -config ../src/operation/iPNP/example/pnp_config.json

+ 29
- 6
src/operation/iPNP/main.cpp View File

@@ -16,10 +16,10 @@
// ***************************************************************************************
/**
* @file main.cpp
* @author Xinhao li
* @author Jianrong Su
* @brief The main function of iPNP. function: Launch the tcl console and interact with the user. Refer to iSTA/main.cc.
* @version 0.1
* @date 2024-07-15
* @version 1.0
* @date 2025-07-01
*/

#include <iostream>
@@ -42,7 +42,8 @@ using namespace ipnp;

int registerCommands() {
registerTclCmd(CmdRunPnp, "run_pnp");
registerTclCmd(CmdAddVIA1, "add_via1");

return EXIT_SUCCESS;
}

@@ -135,10 +136,32 @@ int main(int argc, char** argv) {
ipnp.set_output_def_path(result["output"].as<std::string>());
}

std::string start_info =
"\033[49;32m"
" _ ____ _ ______ ______________ ____ ______\n"
" (_) __ \\/ | / / __ \\ / ___/_ __/ | / __ \\/_ __/\n"
" / / /_/ / |/ / /_/ / \\__ \\ / / / /| | / /_/ / / / \n"
" / / ____/ /| / ____/ ___/ // / / ___ |/ _, _/ / / \n"
"/_/_/ /_/ |_/_/ /____//_/ /_/ |_/_/ |_| /_/ \n"
" \n"
"\e[0m";

std::cout << start_info << std::endl;

// Run iPNP
LOG_INFO << "Running iPNP..." << std::endl;
ipnp.run();
LOG_INFO << "iPNP completed successfully" << std::endl;
std::string finish_info =
"\033[49;32m"
" _ ____ _ ______ ___________ ___________ __ __\n"
" (_) __ \\/ | / / __ \\ / ____/ _/ | / / _/ ___// / / /\n"
" / / /_/ / |/ / /_/ / / /_ / // |/ // / \\__ \\/ /_/ / \n"
" / / ____/ /| / ____/ / __/ _/ // /| // / ___/ / __ / \n"
"/_/_/ /_/ |_/_/ /_/ /___/_/ |_/___//____/_/ /_/ \n"
" \n"
"\e[0m";

std::cout << finish_info << std::endl;
}

} catch (const cxxopts::exceptions::exception& e) {


+ 6
- 0
src/operation/iPNP/source/data_manager/iPNPIdbWrapper.cpp View File

@@ -98,8 +98,14 @@ namespace ipnp {
else {
std::cerr << "Error: Failed to save DEF file to: " << def_file_path << std::endl;
}
}

void iPNPIdbWrapper::connect_M2_M1_Layer()
{
PowerVia* power_via = new PowerVia();
_idb_design = power_via->connectM2M1Layer(_idb_design);

delete power_via;
}

} // namespace ipnp

+ 2
- 0
src/operation/iPNP/source/data_manager/iPNPIdbWrapper.hh View File

@@ -89,6 +89,8 @@ class iPNPIdbWrapper
void saveToIdb(GridManager pnp_network);
void writeIdbToDef(std::string def_file_path);

void connect_M2_M1_Layer();

private:
idb::IdbDesign* _idb_design = nullptr;
idb::IdbBuilder* _idb_builder = nullptr;


+ 131
- 124
src/operation/iPNP/source/iPNP.cpp View File

@@ -35,144 +35,151 @@

namespace ipnp {

iPNP::iPNP() {
_pnp_config = new PNPConfig();
}

iPNP::iPNP(const std::string& config_file) {
_pnp_config = new PNPConfig();
if (!loadConfigFromJson(config_file, _pnp_config)) {
LOG_WARNING << "Initializing iPNP with default configuration" << std::endl;
iPNP::iPNP() {
_pnp_config = new PNPConfig();
}
// If LEF and DEF file paths are specified in the configuration, read the DEF file
if (!_pnp_config->get_lef_files().empty() && !_pnp_config->get_def_path().empty()) {
readLefDef(_pnp_config->get_lef_files(), _pnp_config->get_def_path());
LOG_INFO << "DEF file read: " << _pnp_config->get_def_path() << std::endl;
} else {
LOG_WARNING << "LEF files or DEF file path not specified in configuration" << std::endl;
}
// Set output DEF file path
if (!_pnp_config->get_output_def_path().empty()) {
_output_def_path = _pnp_config->get_output_def_path();

iPNP::iPNP(const std::string& config_file) {
_pnp_config = new PNPConfig();
if (!loadConfigFromJson(config_file, _pnp_config)) {
LOG_WARNING << "Initializing iPNP with default configuration" << std::endl;
}

// If LEF and DEF file paths are specified in the configuration, read the DEF file
if (!_pnp_config->get_lef_files().empty() && !_pnp_config->get_def_path().empty()) {
readLefDef(_pnp_config->get_lef_files(), _pnp_config->get_def_path());
LOG_INFO << "DEF file read: " << _pnp_config->get_def_path() << std::endl;
}
else {
LOG_WARNING << "LEF files or DEF file path not specified in configuration" << std::endl;
}

// Set output DEF file path
if (!_pnp_config->get_output_def_path().empty()) {
_output_def_path = _pnp_config->get_output_def_path();
}
else {
_output_def_path = "./output.def";
}
}
else {
_output_def_path = "./output.def";

iPNP::~iPNP() {
if (_pnp_config) {
delete _pnp_config;
_pnp_config = nullptr;
}
}
}

iPNP::~iPNP() {
if (_pnp_config) {
delete _pnp_config;
_pnp_config = nullptr;
void iPNP::runSynthesis()
{
NetworkSynthesis network_synthesizer(SysnType::kDefault, _input_network);
network_synthesizer.synthesizeNetwork();
_initialized_network = network_synthesizer.get_network();
_current_opt_network = _initialized_network;
}
}

void iPNP::runSynthesis()
{
NetworkSynthesis network_synthesizer(SysnType::kDefault, _input_network);
network_synthesizer.synthesizeNetwork();
_initialized_network = network_synthesizer.get_network();
_current_opt_network = _initialized_network;
}

void iPNP::runOptimize()
{
saveToIdb();

PdnOptimizer pdn_optimizer;
pdn_optimizer.optimizeGlobal(_initialized_network, _idb_wrapper.get_idb_builder());
_current_opt_network = pdn_optimizer.get_out_put_grid();
}

void iPNP::runFastPlacer()
{
FastPlacer fast_placer;
fast_placer.runFastPlacer(_idb_wrapper.get_idb_builder());
}

void iPNP::readLefDef(std::vector<std::string> lef_files, std::string def_path)
{
auto* db_builder = new idb::IdbBuilder();
db_builder->buildLef(lef_files);
db_builder->buildDef(def_path);

auto* idb_design = db_builder->get_def_service()->get_design();

_idb_wrapper.set_idb_design(idb_design);
_idb_wrapper.set_idb_builder(db_builder);
}

void iPNP::init()
{
// Initialize the input network
_input_network = GridManager();
if (_pnp_config) {
_input_network.set_power_layers(_pnp_config->get_power_layers());
_input_network.set_ho_region_num(_pnp_config->get_ho_region_num());
_input_network.set_ver_region_num(_pnp_config->get_ver_region_num());

void iPNP::runOptimize()
{
saveToIdb();

PdnOptimizer pdn_optimizer;
pdn_optimizer.optimizeGlobal(_initialized_network, _idb_wrapper.get_idb_builder());
_current_opt_network = pdn_optimizer.get_out_put_grid();
}
else {
_input_network.set_power_layers({ 9,8,7,6,5,4,3 });
_input_network.set_ho_region_num(2);
_input_network.set_ver_region_num(2);

void iPNP::runFastPlacer()
{
FastPlacer fast_placer;
fast_placer.runFastPlacer(_idb_wrapper.get_idb_builder());
}
_input_network.set_layer_count(_input_network.get_power_layers().size());
_input_network.set_die_width(_idb_wrapper.get_input_die_width());
_input_network.set_die_height(_idb_wrapper.get_input_die_height());
// Initialize with configuration-based templates if available
if (_pnp_config) {
_input_network.init_GridManager_data(_pnp_config);
} else {
_input_network.init_GridManager_data();

void iPNP::readLefDef(std::vector<std::string> lef_files, std::string def_path)
{
auto* db_builder = new idb::IdbBuilder();
db_builder->buildLef(lef_files);
db_builder->buildDef(def_path);

auto* idb_design = db_builder->get_def_service()->get_design();

_idb_wrapper.set_idb_design(idb_design);
_idb_wrapper.set_idb_builder(db_builder);
}

}
void iPNP::init()
{
// Initialize the input network
_input_network = GridManager();
if (_pnp_config) {
_input_network.set_power_layers(_pnp_config->get_power_layers());
_input_network.set_ho_region_num(_pnp_config->get_ho_region_num());
_input_network.set_ver_region_num(_pnp_config->get_ver_region_num());
}
else {
_input_network.set_power_layers({ 9,8,7,6,5,4,3 });
_input_network.set_ho_region_num(2);
_input_network.set_ver_region_num(2);
}
_input_network.set_layer_count(_input_network.get_power_layers().size());
_input_network.set_die_width(_idb_wrapper.get_input_die_width());
_input_network.set_die_height(_idb_wrapper.get_input_die_height());

// Initialize with configuration-based templates if available
if (_pnp_config) {
_input_network.init_GridManager_data(_pnp_config);
}
else {
_input_network.init_GridManager_data();
}

void iPNP::initIRAnalysis() {
}

void iPNP::initIRAnalysis() {
// Initialize IREval
_ir_eval.initIREval(_idb_wrapper.get_idb_builder(), _pnp_config);

}

void iPNP::runAnalysis()
{
saveToIdb();

_cong_eval.set_config(_pnp_config);
_cong_eval.evalEGR(_idb_wrapper.get_idb_builder());
initIRAnalysis();
_ir_eval.runIREval(_idb_wrapper.get_idb_builder());
}

void iPNP::outputDef()
{
readLefDef(_pnp_config->get_lef_files(), _pnp_config->get_def_path());
saveToIdb();
writeIdbToDef(_output_def_path);
}

void iPNP::run()
{
if (_idb_wrapper.get_idb_design()) {

init();
runSynthesis();
runFastPlacer();
runOptimize();
runAnalysis();
outputDef();
LOG_INFO << "Output written to DEF file: " << _output_def_path << std::endl;
}
else {
LOG_ERROR << "Warning: idb design is empty!" << std::endl;

void iPNP::runAnalysis()
{
saveToIdb();

_cong_eval.set_config(_pnp_config);
_cong_eval.evalEGR(_idb_wrapper.get_idb_builder());

initIRAnalysis();
_ir_eval.runIREval(_idb_wrapper.get_idb_builder());
}

void iPNP::outputDef()
{
readLefDef(_pnp_config->get_lef_files(), _pnp_config->get_def_path());
saveToIdb();
writeIdbToDef(_output_def_path);
}

void iPNP::connect_M2_M1()
{
_idb_wrapper.connect_M2_M1_Layer();
}

void iPNP::run()
{
if (_idb_wrapper.get_idb_design()) {

init();
runSynthesis();
runFastPlacer();
runOptimize();
runAnalysis();
outputDef();


LOG_INFO << "Output written to DEF file: " << _output_def_path << std::endl;

}
else {
LOG_ERROR << "Warning: idb design is empty!" << std::endl;
}
}
}

} // namespace ipnp

+ 2
- 0
src/operation/iPNP/source/iPNP.hh View File

@@ -87,6 +87,8 @@ namespace ipnp {
void runAnalysis();
void outputDef();

void connect_M2_M1();

void run(); // According to the config. e.g. which Evaluator, which opt algorithm.

// Set the output DEF file path


+ 106
- 0
src/operation/iPNP/source/module/synthesis/PowerVia.cpp View File

@@ -49,6 +49,15 @@ namespace ipnp {
return idb_design;
}

idb::IdbDesign* PowerVia::connectM2M1Layer(idb::IdbDesign* idb_design)
{
idb_design = connect_M2_M1("VDD", idb_design);
idb_design = connect_M2_M1("VSS", idb_design);

LOG_INFO << "Success : Connected M2 and M1 layers";
return idb_design;
}

idb::IdbDesign* PowerVia::connectNetworkLayers(GridManager& pnp_network, PowerType net_type, idb::IdbDesign* idb_design)
{
std::string net_name = (net_type == PowerType::kVDD) ? "VDD" : "VSS";
@@ -520,12 +529,24 @@ namespace ipnp {
auto idb_layout = idb_design->get_layout();
auto idb_layer_list = idb_layout->get_layers();
auto idb_pdn_list = idb_design->get_special_net_list();
auto idb_via_list = idb_design->get_via_list();

// Get layer information
idb::IdbLayerRouting* layer_M3 = dynamic_cast<idb::IdbLayerRouting*>(
idb_layer_list->find_layer("M3"));
idb::IdbLayerRouting* layer_M2 = dynamic_cast<idb::IdbLayerRouting*>(
idb_layer_list->find_layer("M2"));
idb::IdbLayerRouting* layer_M1 = dynamic_cast<idb::IdbLayerRouting*>(
idb_layer_list->find_layer("M1"));
idb::IdbLayerCut* layer_via1 = dynamic_cast<idb::IdbLayerCut*>(
idb_layer_list->find_layer("VIA1"));
idb::IdbLayerCut* layer_via2 = dynamic_cast<idb::IdbLayerCut*>(
idb_layer_list->find_layer("VIA2"));

if (!layer_M3 || !layer_M2 || !layer_M1 || !layer_via1 || !layer_via2) {
LOG_INFO << "Error: Cannot find required layers";
return nullptr;
}

// Get network
idb::IdbSpecialNet* net = idb_pdn_list->find_net(net_name);
@@ -541,6 +562,91 @@ namespace ipnp {
return nullptr;
}

// 首先检查是否已经存在VIAGEN12_RECT_1 via
idb::IdbVia* m2_m1_via = idb_via_list->find_via("VIAGEN12_RECT_1");
// 如果不存在,则需要创建
if (m2_m1_via == nullptr) {
LOG_INFO << "VIAGEN12_RECT_1 not found, creating it...";
// 查找M3-M2 via作为模板
idb::IdbVia* m3_m2_via = nullptr;
for (auto via : idb_via_list->get_via_list()) {
auto cut_layer_shape = via->get_cut_layer_shape();
auto cut_layer = cut_layer_shape.get_layer();
if (cut_layer->compareLayer(layer_via2) && via->get_name().find("VIAGEN23") != std::string::npos) {
m3_m2_via = via;
LOG_INFO << "Found M3-M2 via : " << via->get_name();
break;
}
}
if (m3_m2_via) {
// clone via
m2_m1_via = m3_m2_via->clone();

// set via master
idb::IdbViaMaster* m2_m1_via_master = m3_m2_via->get_instance()->clone();

// set via master generate
idb::IdbViaMasterGenerate* m2_m1_via_master_generate = m2_m1_via_master->get_master_generate()->clone();
m2_m1_via_master_generate->set_rule_name("VIAGEN12_RECT_1");
m2_m1_via_master_generate->set_layer_bottom(layer_M1);
m2_m1_via_master_generate->set_layer_top(layer_M2);
m2_m1_via_master_generate->set_layer_cut(layer_via1);

// set rule generate
idb::IdbViaRuleGenerate* m2_m1_via_rule_generate = m2_m1_via_master_generate->get_rule_generate();
m2_m1_via_rule_generate->set_name("VIAGEN12_RECT_1");
m2_m1_via_rule_generate->set_layer_bottom(layer_M1);
m2_m1_via_rule_generate->set_layer_top(layer_M2);
m2_m1_via_rule_generate->set_layer_cut(layer_via1);

m2_m1_via_master_generate->set_rule_generate(m2_m1_via_rule_generate);
m2_m1_via_master->set_master_generate(m2_m1_via_master_generate);
m2_m1_via->set_instance(m2_m1_via_master);

// set via name
m2_m1_via->set_name("VIAGEN12_RECT_1");

idb_via_list->add_via(m2_m1_via);

LOG_INFO << "Created new M2-M1 via: VIAGEN12_RECT_1 based on " << m3_m2_via->get_name();
} else {
LOG_INFO << "Error: Cannot find M3-M2 via template";
return idb_design;
}
} else {
LOG_INFO << "Using existing VIAGEN12_RECT_1 via for " << net_name;
}

// 无论是新创建还是已存在,都使用m2_m1_via添加到网络中
for (idb::IdbSpecialWire* wire : wire_list->get_wire_list()) {
auto segment_list = wire->get_segment_list();

for (idb::IdbSpecialWireSegment* segment : segment_list) {
if (segment->is_via() && segment->get_layer()->compareLayer(layer_M3)) {
// 在M2层添加通孔
idb::IdbSpecialWireSegment* new_segment = new idb::IdbSpecialWireSegment();
new_segment->set_layer(layer_M2);
new_segment->set_is_via(true);
new_segment->set_route_width(0);

// 使用M2-M1 via
new_segment->set_via(m2_m1_via);

// 复制坐标点
if (!segment->get_point_list().empty()) {
new_segment->add_point(segment->get_point_list()[0]->get_x(), segment->get_point_list()[0]->get_y());
}

// 添加到wire中
wire->add_segment(new_segment);
}
}
}

return idb_design;
}

+ 2
- 1
src/operation/iPNP/source/module/synthesis/PowerVia.hh View File

@@ -64,7 +64,8 @@ namespace ipnp {
~PowerVia() = default;

idb::IdbDesign* connectAllPowerLayers(GridManager& pnp_network, idb::IdbDesign* idb_design);

idb::IdbDesign* connectM2M1Layer(idb::IdbDesign* idb_design);
private:
idb::IdbDesign* connectNetworkLayers(GridManager& pnp_network, PowerType net_type, idb::IdbDesign* idb_design);


+ 1
- 0
src/operation/iPNP/source/module/tcl-cmd/CMakeLists.txt View File

@@ -1,5 +1,6 @@
add_library(pnp-cmd
CmdRunPnp.cc
CmdAddVIA1.cc
)

target_link_libraries(pnp-cmd


+ 94
- 0
src/operation/iPNP/source/module/tcl-cmd/CmdAddVIA1.cc View File

@@ -0,0 +1,94 @@
// ***************************************************************************************
// Copyright (c) 2023-2025 Peng Cheng Laboratory
// Copyright (c) 2023-2025 Institute of Computing Technology, Chinese Academy of Sciences
// Copyright (c) 2023-2025 Beijing Institute of Open Source Chip
//
// iEDA is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
//
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
//
// See the Mulan PSL v2 for more details.
// ***************************************************************************************
/**
* @file CmdAddVIA1.cc
* @author Jianrong Su
* @brief Command to add VIA1 connections between M2 and M1 layers
* @version 1.0
* @date 2025-06-30
*/

#include "ShellCmd.hh"
#include "log/Log.hh"
#include "iPNP.hh"
#include "iPNPApi.hh"
#include <filesystem>

namespace ipnp {

CmdAddVIA1::CmdAddVIA1(const char* cmd_name) : TclCmd(cmd_name) {
auto* config_option = new TclStringOption("-config", 0, "");
addOption(config_option);
}

unsigned CmdAddVIA1::check() {
TclOption* config_option = getOptionOrArg("-config");

if (config_option) {
auto* config_file = config_option->getStringVal();
if (!std::filesystem::exists(config_file)) {
LOG_ERROR << "Configuration file not found: " << config_file;
return 0;
}
}
return 1; // check success
}

unsigned CmdAddVIA1::exec() {
if (!check()) {
return 0;
}

TclOption* config_option = getOptionOrArg("-config");
if (config_option) {
auto* config_file = config_option->getStringVal();

if (iPNPApi::getInstance()) {
LOG_ERROR << "An existing iPNP instance was found. It will be replaced.";
delete iPNPApi::getInstance();
iPNPApi::setInstance(nullptr);
}

LOG_INFO << "Initializing iPNP with configuration: " << config_file;
auto* new_instance = new ipnp::iPNP(config_file);
iPNPApi::setInstance(new_instance);
}

auto* ipnp = iPNPApi::getInstance();

LOG_INFO << "Adding VIA1 connections between M2 and M1 layers...";

ipnp->connect_M2_M1();

std::string output_def_path;
if (ipnp->get_config() && !ipnp->get_config()->get_output_def_path().empty()) {
output_def_path = ipnp->get_config()->get_output_def_path();
LOG_INFO << "DEF output path read from configuration file: " << output_def_path;
} else {
output_def_path = "./output.def";
LOG_ERROR << "DEF output path not found in configuration file, using default path.";
}

ipnp->writeIdbToDef(output_def_path);

LOG_INFO << "VIA1 connections added successfully.";

return 1;
}

} // namespace ipnp

+ 31
- 7
src/operation/iPNP/source/module/tcl-cmd/CmdRunPnp.cc View File

@@ -16,10 +16,10 @@
// ***************************************************************************************
/**
* @file CmdRunPnp.cc
* @author sujianrong
* @brief Implementation of the PNP algorithm execution command
* @version 0.1
* @date 2024-07-20
* @author Jianrong Su
* @brief
* @version 1.0
* @date 2025-06-30
*/

#include "ShellCmd.hh"
@@ -27,12 +27,13 @@
#include "iPNP.hh"
#include "iPNPApi.hh"
#include <filesystem>
#include <iostream>

namespace ipnp {

CmdRunPnp::CmdRunPnp(const char* cmd_name) : TclCmd(cmd_name) {

auto* config_option = new TclStringOption("-config", 0, "../src/operation/iPNP/example/pnp_config.json");
auto* config_option = new TclStringOption("-config", 0, "");
addOption(config_option);

}
@@ -73,9 +74,32 @@ unsigned CmdRunPnp::exec() {
}

auto* ipnp = iPNPApi::getInstance();
LOG_INFO << "Starting iPNP algorithm...";
std::string start_info =
"\033[49;32m"
" _ ____ _ ______ ______________ ____ ______\n"
" (_) __ \\/ | / / __ \\ / ___/_ __/ | / __ \\/_ __/\n"
" / / /_/ / |/ / /_/ / \\__ \\ / / / /| | / /_/ / / / \n"
" / / ____/ /| / ____/ ___/ // / / ___ |/ _, _/ / / \n"
"/_/_/ /_/ |_/_/ /____//_/ /_/ |_/_/ |_| /_/ \n"
" \n"
"\e[0m";

std::cout << start_info << std::endl;
ipnp->run();
LOG_INFO << "iPNP algorithm finished successfully.";
std::string finish_info =
"\033[49;32m"
" _ ____ _ ______ ___________ ___________ __ __\n"
" (_) __ \\/ | / / __ \\ / ____/ _/ | / / _/ ___// / / /\n"
" / / /_/ / |/ / /_/ / / /_ / // |/ // / \\__ \\/ /_/ / \n"
" / / ____/ /| / ____/ / __/ _/ // /| // / ___/ / __ / \n"
"/_/_/ /_/ |_/_/ /_/ /___/_/ |_/___//____/_/ /_/ \n"
" \n"
"\e[0m";

std::cout << finish_info << std::endl;
return 1;
}


+ 12
- 4
src/operation/iPNP/source/module/tcl-cmd/ShellCmd.hh View File

@@ -16,10 +16,10 @@
// ***************************************************************************************
/**
* @file ShellCmd.hh
* @author sujianrong
* @brief iPNP命令类定义
* @version 0.1
* @date 2024-07-20
* @author Jianrong Su
* @brief
* @version 1.0
* @date 2025-06-30
*/

#pragma once
@@ -55,5 +55,13 @@ public:
unsigned exec() override;
};

class CmdAddVIA1 : public TclCmd {
public:
explicit CmdAddVIA1(const char* cmd_name);
~CmdAddVIA1() override = default;

unsigned check() override;
unsigned exec() override;
};

} // namespace ipnp

+ 54
- 50
src/operation/iRT/interface/RTInterface.cpp View File

@@ -1177,15 +1177,25 @@ void RTInterface::outputNetList()

void RTInterface::outputSummary()
{
#if 0
ieda_feature::RTSummary& top_rt_summary = featureInst->get_summary()->get_summary_irt();

Summary& rt_summary = RTDM.getDatabase().get_summary();

// pa_summary
{
top_rt_summary.pa_summary.routing_access_point_num_map = rt_summary.pa_summary.routing_access_point_num_map;
top_rt_summary.pa_summary.total_access_point_num = rt_summary.pa_summary.total_access_point_num;
for (auto& [iter, pa_summary] : rt_summary.iter_pa_summary_map) {
ieda_feature::PASummary& top_pa_summary = top_rt_summary.iter_pa_summary_map[iter];
top_pa_summary.routing_access_point_num_map = pa_summary.routing_access_point_num_map;
top_pa_summary.total_access_point_num = pa_summary.total_access_point_num;
top_pa_summary.routing_wire_length_map = pa_summary.routing_wire_length_map;
top_pa_summary.total_wire_length = pa_summary.total_wire_length;
top_pa_summary.cut_via_num_map = pa_summary.cut_via_num_map;
top_pa_summary.total_via_num = pa_summary.total_via_num;
top_pa_summary.routing_patch_num_map = pa_summary.routing_patch_num_map;
top_pa_summary.total_patch_num = pa_summary.total_patch_num;
top_pa_summary.routing_violation_num_map = pa_summary.routing_violation_num_map;
top_pa_summary.total_violation_num = pa_summary.total_violation_num;
}
}
// sa_summary
{
@@ -1197,15 +1207,8 @@ void RTInterface::outputSummary()
top_rt_summary.tg_summary.total_demand = rt_summary.tg_summary.total_demand;
top_rt_summary.tg_summary.total_overflow = rt_summary.tg_summary.total_overflow;
top_rt_summary.tg_summary.total_wire_length = rt_summary.tg_summary.total_wire_length;
for (auto& [clock_name, timing_map] : rt_summary.tg_summary.clock_timing) {
ieda_feature::ClockTiming clock_timing;
clock_timing.clock_name = clock_name;
clock_timing.setup_tns = timing_map["TNS"];
clock_timing.setup_wns = timing_map["WNS"];
clock_timing.suggest_freq = timing_map["Freq(MHz)"];
top_rt_summary.tg_summary.clocks_timing.push_back(clock_timing);
}
top_rt_summary.tg_summary.power_info = {rt_summary.tg_summary.power_map["static_power"], rt_summary.tg_summary.power_map["dynamic_power"]};
top_rt_summary.tg_summary.clock_timing = rt_summary.tg_summary.clock_timing;
top_rt_summary.tg_summary.power_map = rt_summary.tg_summary.power_map;
}
// la_summary
{
@@ -1217,15 +1220,8 @@ void RTInterface::outputSummary()
top_rt_summary.la_summary.total_wire_length = rt_summary.la_summary.total_wire_length;
top_rt_summary.la_summary.cut_via_num_map = rt_summary.la_summary.cut_via_num_map;
top_rt_summary.la_summary.total_via_num = rt_summary.la_summary.total_via_num;
for (auto& [clock_name, timing_map] : rt_summary.la_summary.clock_timing) {
ieda_feature::ClockTiming clock_timing;
clock_timing.clock_name = clock_name;
clock_timing.setup_tns = timing_map["TNS"];
clock_timing.setup_wns = timing_map["WNS"];
clock_timing.suggest_freq = timing_map["Freq(MHz)"];
top_rt_summary.la_summary.clocks_timing.push_back(clock_timing);
}
top_rt_summary.la_summary.power_info = {rt_summary.la_summary.power_map["static_power"], rt_summary.la_summary.power_map["dynamic_power"]};
top_rt_summary.la_summary.clock_timing = rt_summary.la_summary.clock_timing;
top_rt_summary.la_summary.power_map = rt_summary.la_summary.power_map;
}
// sr_summary
{
@@ -1239,15 +1235,8 @@ void RTInterface::outputSummary()
top_sr_summary.total_wire_length = sr_summary.total_wire_length;
top_sr_summary.cut_via_num_map = sr_summary.cut_via_num_map;
top_sr_summary.total_via_num = sr_summary.total_via_num;
for (auto& [clock_name, timing_map] : sr_summary.clock_timing) {
ieda_feature::ClockTiming clock_timing;
clock_timing.clock_name = clock_name;
clock_timing.setup_tns = timing_map["TNS"];
clock_timing.setup_wns = timing_map["WNS"];
clock_timing.suggest_freq = timing_map["Freq(MHz)"];
top_sr_summary.clocks_timing.push_back(clock_timing);
}
top_sr_summary.power_info = {sr_summary.power_map["static_power"], sr_summary.power_map["dynamic_power"]};
top_sr_summary.clock_timing = sr_summary.clock_timing;
top_sr_summary.power_map = sr_summary.power_map;
}
}
// ta_summary
@@ -1265,20 +1254,33 @@ void RTInterface::outputSummary()
top_dr_summary.total_wire_length = dr_summary.total_wire_length;
top_dr_summary.cut_via_num_map = dr_summary.cut_via_num_map;
top_dr_summary.total_via_num = dr_summary.total_via_num;
top_dr_summary.routing_patch_num_map = dr_summary.routing_patch_num_map;
top_dr_summary.total_patch_num = dr_summary.total_patch_num;
top_dr_summary.routing_violation_num_map = dr_summary.routing_violation_num_map;
top_dr_summary.total_violation_num = dr_summary.total_violation_num;

for (auto& [clock_name, timing_map] : dr_summary.clock_timing) {
ieda_feature::ClockTiming clock_timing;
clock_timing.clock_name = clock_name;
clock_timing.setup_tns = timing_map["TNS"];
clock_timing.setup_wns = timing_map["WNS"];
clock_timing.suggest_freq = timing_map["Freq(MHz)"];
top_dr_summary.clocks_timing.push_back(clock_timing);
}
top_dr_summary.power_info = {dr_summary.power_map["static_power"], dr_summary.power_map["dynamic_power"]};
top_dr_summary.clock_timing = dr_summary.clock_timing;
top_dr_summary.power_map = dr_summary.power_map;
}
}
// vr_summary
{
top_rt_summary.vr_summary.routing_wire_length_map = rt_summary.vr_summary.routing_wire_length_map;
top_rt_summary.vr_summary.total_wire_length = rt_summary.vr_summary.total_wire_length;
top_rt_summary.vr_summary.cut_via_num_map = rt_summary.vr_summary.cut_via_num_map;
top_rt_summary.vr_summary.total_via_num = rt_summary.vr_summary.total_via_num;
top_rt_summary.vr_summary.routing_patch_num_map = rt_summary.vr_summary.routing_patch_num_map;
top_rt_summary.vr_summary.total_patch_num = rt_summary.vr_summary.total_patch_num;
top_rt_summary.vr_summary.within_net_routing_violation_type_num_map = rt_summary.vr_summary.within_net_routing_violation_type_num_map;
top_rt_summary.vr_summary.within_net_violation_type_num_map = rt_summary.vr_summary.within_net_violation_type_num_map;
top_rt_summary.vr_summary.within_net_routing_violation_num_map = rt_summary.vr_summary.within_net_routing_violation_num_map;
top_rt_summary.vr_summary.within_net_total_violation_num = rt_summary.vr_summary.within_net_total_violation_num;
top_rt_summary.vr_summary.among_net_routing_violation_type_num_map = rt_summary.vr_summary.among_net_routing_violation_type_num_map;
top_rt_summary.vr_summary.among_net_violation_type_num_map = rt_summary.vr_summary.among_net_violation_type_num_map;
top_rt_summary.vr_summary.among_net_routing_violation_num_map = rt_summary.vr_summary.among_net_routing_violation_num_map;
top_rt_summary.vr_summary.among_net_total_violation_num = rt_summary.vr_summary.among_net_total_violation_num;
top_rt_summary.vr_summary.clock_timing = rt_summary.vr_summary.clock_timing;
top_rt_summary.vr_summary.power_map = rt_summary.vr_summary.power_map;
}
// er_summary
{
top_rt_summary.er_summary.routing_demand_map = rt_summary.er_summary.routing_demand_map;
@@ -1289,17 +1291,9 @@ void RTInterface::outputSummary()
top_rt_summary.er_summary.total_wire_length = rt_summary.er_summary.total_wire_length;
top_rt_summary.er_summary.cut_via_num_map = rt_summary.er_summary.cut_via_num_map;
top_rt_summary.er_summary.total_via_num = rt_summary.er_summary.total_via_num;
for (auto& [clock_name, timing_map] : rt_summary.er_summary.clock_timing) {
ieda_feature::ClockTiming clock_timing;
clock_timing.clock_name = clock_name;
clock_timing.setup_tns = timing_map["TNS"];
clock_timing.setup_wns = timing_map["WNS"];
clock_timing.suggest_freq = timing_map["Freq(MHz)"];
top_rt_summary.er_summary.clocks_timing.push_back(clock_timing);
}
top_rt_summary.er_summary.power_info = {rt_summary.er_summary.power_map["static_power"], rt_summary.er_summary.power_map["dynamic_power"]};
top_rt_summary.er_summary.clock_timing = rt_summary.er_summary.clock_timing;
top_rt_summary.er_summary.power_map = rt_summary.er_summary.power_map;
}
#endif
}

#endif
@@ -1889,6 +1883,16 @@ void RTInterface::routeTAPanel(TAPanel& ta_panel)

#endif

#if 1 // ecos

void RTInterface::sendNotification(std::string stage, std::string json_path)
{
std::cout << "stage: " << stage << std::endl;
std::cout << "json_path: " << json_path << std::endl;
}

#endif

#endif

// private


+ 5
- 1
src/operation/iRT/interface/RTInterface.hpp View File

@@ -92,7 +92,7 @@ class RTInterface
void wrapDatabase();
void wrapDBInfo();
void wrapMicronDBU();
void wrapManufactureGrid();
void wrapManufactureGrid();
void wrapDie();
void wrapRow();
void wrapLayerList();
@@ -155,6 +155,10 @@ class RTInterface
void routeTAPanel(TAPanel& ta_panel);
#endif

#if 1 // ecos
void sendNotification(std::string stage, std::string json_path);
#endif

#endif

private:


+ 77
- 0
src/operation/iRT/source/module/detailed_router/DetailedRouter.cpp View File

@@ -148,6 +148,8 @@ void DetailedRouter::routeDRModel(DRModel& dr_model)
printSummary(dr_model);
outputNetCSV(dr_model);
outputViolationCSV(dr_model);
outputNetJson(dr_model);
outputViolationJson(dr_model);
RTLOG.info(Loc::current(), "***** End Iteration ", iter, "/", dr_iter_param_list.size(), "(", RTUTIL.getPercentage(iter, dr_iter_param_list.size()), ")",
iter_monitor.getStatsInfo(), "*****");
if (stopIteration(dr_model)) {
@@ -2080,6 +2082,8 @@ void DetailedRouter::selectBestResult(DRModel& dr_model)
printSummary(dr_model);
outputNetCSV(dr_model);
outputViolationCSV(dr_model);
outputNetJson(dr_model);
outputViolationJson(dr_model);

RTLOG.info(Loc::current(), "Completed", monitor.getStatsInfo());
}
@@ -2978,6 +2982,79 @@ void DetailedRouter::outputViolationCSV(DRModel& dr_model)
}
}

void DetailedRouter::outputNetJson(DRModel& dr_model)
{
Die& die = RTDM.getDatabase().get_die();
std::vector<RoutingLayer>& routing_layer_list = RTDM.getDatabase().get_routing_layer_list();
std::vector<CutLayer>& cut_layer_list = RTDM.getDatabase().get_cut_layer_list();
std::vector<Net>& net_list = RTDM.getDatabase().get_net_list();
std::string& dr_temp_directory_path = RTDM.getConfig().dr_temp_directory_path;
int32_t output_inter_result = RTDM.getConfig().output_inter_result;
if (!output_inter_result) {
return;
}
std::vector<nlohmann::json> net_json_list;
net_json_list.resize(net_list.size());
for (Net& net : net_list) {
net_json_list[net.get_net_idx()]["net_name"] = net.get_net_name();
}
for (auto& [net_idx, segment_set] : RTDM.getNetDetailedResultMap(die)) {
for (Segment<LayerCoord>* segment : segment_set) {
for (NetShape& net_shape : RTDM.getNetShapeList(net_idx, *segment)) {
std::string layer_name;
if (net_shape.get_is_routing()) {
layer_name = routing_layer_list[net_shape.get_layer_idx()].get_layer_name();
} else {
layer_name = cut_layer_list[net_shape.get_layer_idx()].get_layer_name();
}
net_json_list[net_idx]["result"].push_back({net_shape.get_ll_x(), net_shape.get_ll_y(), net_shape.get_ur_x(), net_shape.get_ur_y(), layer_name});
}
}
}
for (auto& [net_idx, patch_set] : RTDM.getNetDetailedPatchMap(die)) {
for (EXTLayerRect* patch : patch_set) {
net_json_list[net_idx]["patch"].push_back({patch->get_real_ll_x(), patch->get_real_ll_y(), patch->get_real_ur_x(), patch->get_real_ur_y(),
routing_layer_list[patch->get_layer_idx()].get_layer_name()});
}
}
std::string net_json_file_path = RTUTIL.getString(dr_temp_directory_path, "net_map_", dr_model.get_iter(), ".json");
std::ofstream* net_json_file = RTUTIL.getOutputFileStream(net_json_file_path);
(*net_json_file) << net_json_list;
RTUTIL.closeFileStream(net_json_file);
RTI.sendNotification(RTUTIL.getString("DR_", dr_model.get_iter(), "_net_map"), net_json_file_path);
}

void DetailedRouter::outputViolationJson(DRModel& dr_model)
{
Die& die = RTDM.getDatabase().get_die();
std::vector<RoutingLayer>& routing_layer_list = RTDM.getDatabase().get_routing_layer_list();
std::vector<Net>& net_list = RTDM.getDatabase().get_net_list();
std::string& dr_temp_directory_path = RTDM.getConfig().dr_temp_directory_path;
int32_t output_inter_result = RTDM.getConfig().output_inter_result;
if (!output_inter_result) {
return;
}
std::vector<nlohmann::json> violation_json_list;
for (Violation* violation : RTDM.getViolationSet(die)) {
EXTLayerRect& violation_shape = violation->get_violation_shape();

nlohmann::json violation_json;
violation_json["type"] = GetViolationTypeName()(violation->get_violation_type());
violation_json["shape"]
= {violation_shape.get_real_rect().get_ll_x(), violation_shape.get_real_rect().get_ll_y(), violation_shape.get_real_rect().get_ur_x(),
violation_shape.get_real_rect().get_ur_y(), routing_layer_list[violation_shape.get_layer_idx()].get_layer_name()};
for (int32_t net_idx : violation->get_violation_net_set()) {
violation_json["net"].push_back(net_list[net_idx].get_net_name());
}
violation_json_list.push_back(violation_json);
}
std::string violation_json_file_path = RTUTIL.getString(dr_temp_directory_path, "violation_map_", dr_model.get_iter(), ".json");
std::ofstream* violation_json_file = RTUTIL.getOutputFileStream(violation_json_file_path);
(*violation_json_file) << violation_json_list;
RTUTIL.closeFileStream(violation_json_file);
RTI.sendNotification(RTUTIL.getString("DR_", dr_model.get_iter(), "_violation_map"), violation_json_file_path);
}

#endif

#if 1 // debug


+ 2
- 0
src/operation/iRT/source/module/detailed_router/DetailedRouter.hpp View File

@@ -174,6 +174,8 @@ class DetailedRouter
void printSummary(DRModel& dr_model);
void outputNetCSV(DRModel& dr_model);
void outputViolationCSV(DRModel& dr_model);
void outputNetJson(DRModel& dr_model);
void outputViolationJson(DRModel& dr_model);
#endif

#if 1 // debug


+ 6
- 0
src/operation/iRT/source/module/drc_engine/DRCEngine.cpp View File

@@ -251,8 +251,12 @@ std::vector<Violation> DRCEngine::getExpandedViolationList(DETask& de_task, Viol
case ViolationType::kEnclosure:
break;
case ViolationType::kEnclosureEdge:
new_real_rect = enlargeRect(new_real_rect, 0);
layer_routing_list = expandLayer(violation, {-1, 0, +1});
break;
case ViolationType::kEnclosureParallel:
new_real_rect = enlargeRect(new_real_rect, 0);
layer_routing_list = expandLayer(violation, {-1, 0, +1});
break;
case ViolationType::kEndOfLineSpacing:
new_real_rect = enlargeRect(new_real_rect, violation.get_required_size());
@@ -341,6 +345,8 @@ std::vector<Violation> DRCEngine::getExpandedViolationList(DETask& de_task, Viol
layer_routing_list = expandLayer(violation, {0});
break;
case ViolationType::kEnclosureParallel:
new_real_rect = enlargeRect(new_real_rect, 0);
layer_routing_list = expandLayer(violation, {0});
break;
case ViolationType::kEndOfLineSpacing:
new_real_rect = enlargeRect(new_real_rect, 0);


+ 75
- 8
src/operation/iRT/source/module/layer_assigner/LayerAssigner.cpp View File

@@ -66,8 +66,10 @@ void LayerAssigner::assign()
updateSummary(la_model);
printSummary(la_model);
outputGuide(la_model);
outputDemandCSV(la_model);
outputNetCSV(la_model);
outputOverflowCSV(la_model);
outputNetJson(la_model);
outputOverflowJson(la_model);
RTLOG.info(Loc::current(), "Completed", monitor.getStatsInfo());
}

@@ -1171,7 +1173,7 @@ void LayerAssigner::outputGuide(LAModel& la_model)
RTUTIL.closeFileStream(guide_file_stream);
}

void LayerAssigner::outputDemandCSV(LAModel& la_model)
void LayerAssigner::outputNetCSV(LAModel& la_model)
{
std::vector<RoutingLayer>& routing_layer_list = RTDM.getDatabase().get_routing_layer_list();
std::string& la_temp_directory_path = RTDM.getConfig().la_temp_directory_path;
@@ -1181,17 +1183,15 @@ void LayerAssigner::outputDemandCSV(LAModel& la_model)
}
std::vector<GridMap<LANode>>& layer_node_map = la_model.get_layer_node_map();
for (RoutingLayer& routing_layer : routing_layer_list) {
std::ofstream* demand_csv_file
= RTUTIL.getOutputFileStream(RTUTIL.getString(la_temp_directory_path, "demand_map_", routing_layer.get_layer_name(), ".csv"));

std::ofstream* net_csv_file = RTUTIL.getOutputFileStream(RTUTIL.getString(la_temp_directory_path, "net_map_", routing_layer.get_layer_name(), ".csv"));
GridMap<LANode>& la_node_map = layer_node_map[routing_layer.get_layer_idx()];
for (int32_t y = la_node_map.get_y_size() - 1; y >= 0; y--) {
for (int32_t x = 0; x < la_node_map.get_x_size(); x++) {
RTUTIL.pushStream(demand_csv_file, la_node_map[x][y].getDemand(), ",");
RTUTIL.pushStream(net_csv_file, la_node_map[x][y].getDemand(), ",");
}
RTUTIL.pushStream(demand_csv_file, "\n");
RTUTIL.pushStream(net_csv_file, "\n");
}
RTUTIL.closeFileStream(demand_csv_file);
RTUTIL.closeFileStream(net_csv_file);
}
}

@@ -1219,6 +1219,73 @@ void LayerAssigner::outputOverflowCSV(LAModel& la_model)
}
}

void LayerAssigner::outputNetJson(LAModel& la_model)
{
Die& die = RTDM.getDatabase().get_die();
ScaleAxis& gcell_axis = RTDM.getDatabase().get_gcell_axis();
std::vector<RoutingLayer>& routing_layer_list = RTDM.getDatabase().get_routing_layer_list();
std::vector<Net>& net_list = RTDM.getDatabase().get_net_list();
std::string& la_temp_directory_path = RTDM.getConfig().la_temp_directory_path;
int32_t output_inter_result = RTDM.getConfig().output_inter_result;
if (!output_inter_result) {
return;
}
std::vector<nlohmann::json> net_json_list;
net_json_list.resize(net_list.size());
for (Net& net : net_list) {
net_json_list[net.get_net_idx()]["net_name"] = net.get_net_name();
}
for (auto& [net_idx, segment_set] : RTDM.getNetGlobalResultMap(die)) {
for (Segment<LayerCoord>* segment : segment_set) {
PlanarRect first_gcell = RTUTIL.getRealRectByGCell(segment->get_first(), gcell_axis);
PlanarRect second_gcell = RTUTIL.getRealRectByGCell(segment->get_second(), gcell_axis);
if (segment->get_first().get_layer_idx() != segment->get_second().get_layer_idx()) {
net_json_list[net_idx]["result"].push_back({first_gcell.get_ll_x(), first_gcell.get_ll_y(), first_gcell.get_ur_x(), first_gcell.get_ur_y(),
routing_layer_list[segment->get_first().get_layer_idx()].get_layer_name()});
net_json_list[net_idx]["result"].push_back({second_gcell.get_ll_x(), second_gcell.get_ll_y(), second_gcell.get_ur_x(), second_gcell.get_ur_y(),
routing_layer_list[segment->get_second().get_layer_idx()].get_layer_name()});
} else {
PlanarRect gcell = RTUTIL.getBoundingBox({first_gcell, second_gcell});
net_json_list[net_idx]["result"].push_back({gcell.get_ll_x(), gcell.get_ll_y(), gcell.get_ur_x(), gcell.get_ur_y(),
routing_layer_list[segment->get_first().get_layer_idx()].get_layer_name()});
}
}
}
std::string net_json_file_path = RTUTIL.getString(la_temp_directory_path, "net_map.json");
std::ofstream* net_json_file = RTUTIL.getOutputFileStream(net_json_file_path);
(*net_json_file) << net_json_list;
RTUTIL.closeFileStream(net_json_file);
RTI.sendNotification(RTUTIL.getString("LA_net_map"), net_json_file_path);
}

void LayerAssigner::outputOverflowJson(LAModel& la_model)
{
ScaleAxis& gcell_axis = RTDM.getDatabase().get_gcell_axis();
std::vector<RoutingLayer>& routing_layer_list = RTDM.getDatabase().get_routing_layer_list();
std::string& la_temp_directory_path = RTDM.getConfig().la_temp_directory_path;
int32_t output_inter_result = RTDM.getConfig().output_inter_result;
if (!output_inter_result) {
return;
}
std::vector<GridMap<LANode>>& layer_node_map = la_model.get_layer_node_map();
std::vector<nlohmann::json> overflow_json_list;
for (int32_t layer_idx = 0; layer_idx < static_cast<int32_t>(layer_node_map.size()); layer_idx++) {
GridMap<LANode>& la_node_map = layer_node_map[layer_idx];
for (int32_t x = 0; x < la_node_map.get_x_size(); x++) {
for (int32_t y = 0; y < la_node_map.get_y_size(); y++) {
PlanarRect gcell = RTUTIL.getRealRectByGCell(PlanarCoord(x, y), gcell_axis);
overflow_json_list.push_back({gcell.get_ll_x(), gcell.get_ll_y(), gcell.get_ur_x(), gcell.get_ur_y(), routing_layer_list[layer_idx].get_layer_name(),
la_node_map[x][y].getOverflow()});
}
}
}
std::string overflow_json_file_path = RTUTIL.getString(la_temp_directory_path, "overflow_map.json");
std::ofstream* overflow_json_file = RTUTIL.getOutputFileStream(overflow_json_file_path);
(*overflow_json_file) << overflow_json_list;
RTUTIL.closeFileStream(overflow_json_file);
RTI.sendNotification(RTUTIL.getString("LA_overflow_map"), overflow_json_file_path);
}

#endif

#if 1 // debug


+ 3
- 1
src/operation/iRT/source/module/layer_assigner/LayerAssigner.hpp View File

@@ -97,8 +97,10 @@ class LayerAssigner
void updateSummary(LAModel& la_model);
void printSummary(LAModel& la_model);
void outputGuide(LAModel& la_model);
void outputDemandCSV(LAModel& la_model);
void outputNetCSV(LAModel& la_model);
void outputOverflowCSV(LAModel& la_model);
void outputNetJson(LAModel& la_model);
void outputOverflowJson(LAModel& la_model);
#endif

#if 1 // debug


+ 51
- 38
src/operation/iRT/source/module/layer_assigner/la_data_manager/LANode.hpp View File

@@ -44,16 +44,16 @@ class LANode : public LayerCoord
double get_internal_via_unit() const { return _internal_via_unit; }
std::map<Orientation, LANode*>& get_neighbor_node_map() { return _neighbor_node_map; }
std::map<Orientation, int32_t>& get_orient_supply_map() { return _orient_supply_map; }
std::map<Orientation, std::set<int32_t>>& get_orient_demand_map() { return _orient_demand_map; }
std::set<int32_t>& get_via_net_set() { return _via_net_set; }
std::map<Orientation, std::set<int32_t>>& get_orient_net_map() { return _orient_net_map; }
std::map<int32_t, std::set<Orientation>>& get_net_orient_map() { return _net_orient_map; }
// setter
void set_boundary_wire_unit(const double boundary_wire_unit) { _boundary_wire_unit = boundary_wire_unit; }
void set_internal_wire_unit(const double internal_wire_unit) { _internal_wire_unit = internal_wire_unit; }
void set_internal_via_unit(const double internal_via_unit) { _internal_via_unit = internal_via_unit; }
void set_neighbor_node_map(const std::map<Orientation, LANode*>& neighbor_node_map) { _neighbor_node_map = neighbor_node_map; }
void set_orient_supply_map(const std::map<Orientation, int32_t>& orient_supply_map) { _orient_supply_map = orient_supply_map; }
void set_orient_demand_map(const std::map<Orientation, std::set<int32_t>>& orient_demand_map) { _orient_demand_map = orient_demand_map; }
void set_via_net_set(const std::set<int32_t>& via_net_set) { _via_net_set = via_net_set; }
void set_orient_net_map(const std::map<Orientation, std::set<int32_t>>& orient_net_map) { _orient_net_map = orient_net_map; }
void set_net_orient_map(const std::map<int32_t, std::set<Orientation>>& net_orient_map) { _net_orient_map = net_orient_map; }
// function
LANode* getNeighborNode(Orientation orientation)
{
@@ -63,7 +63,7 @@ class LANode : public LayerCoord
}
return neighbor_node;
}
double getOverflowCost(int32_t net_idx, Orientation orientation, double overflow_unit)
double getOverflowCost(int32_t curr_net_idx, Orientation orientation, double overflow_unit)
{
if (!validDemandUnit()) {
RTLOG.error(Loc::current(), "The demand unit is error!");
@@ -71,10 +71,10 @@ class LANode : public LayerCoord
double boundary_overflow = 0;
if (orientation == Orientation::kEast || orientation == Orientation::kWest || orientation == Orientation::kSouth || orientation == Orientation::kNorth) {
double boundary_demand = 0;
if (RTUTIL.exist(_orient_demand_map, orientation)) {
std::set<int32_t>& net_set = _orient_demand_map[orientation];
if (RTUTIL.exist(_orient_net_map, orientation)) {
std::set<int32_t>& net_set = _orient_net_map[orientation];
boundary_demand += (static_cast<double>(net_set.size()) * _boundary_wire_unit);
if (RTUTIL.exist(net_set, net_idx)) {
if (RTUTIL.exist(net_set, curr_net_idx)) {
boundary_demand -= _boundary_wire_unit;
}
}
@@ -87,15 +87,22 @@ class LANode : public LayerCoord
double internal_overflow = 0;
{
double internal_demand = 0;
for (auto& [orient, net_set] : _orient_demand_map) {
for (auto& [orient, net_set] : _orient_net_map) {
if (orient == Orientation::kAbove || orient == Orientation::kBelow) {
continue;
}
internal_demand += (static_cast<double>(net_set.size()) * _internal_wire_unit);
if (RTUTIL.exist(net_set, net_idx)) {
if (RTUTIL.exist(net_set, curr_net_idx)) {
internal_demand -= _internal_wire_unit;
}
}
internal_demand += (static_cast<double>(_via_net_set.size()) * _internal_via_unit);
if (RTUTIL.exist(_via_net_set, net_idx)) {
internal_demand -= _internal_via_unit;
for (auto& [net_idx, orient_set] : _net_orient_map) {
if (net_idx == curr_net_idx) {
continue;
}
if (RTUTIL.exist(orient_set, Orientation::kAbove) || RTUTIL.exist(orient_set, Orientation::kBelow)) {
internal_demand += _internal_via_unit;
}
}
double internal_supply = 0;
for (auto& [orient, supply] : _orient_supply_map) {
@@ -142,14 +149,22 @@ class LANode : public LayerCoord
RTLOG.error(Loc::current(), "The demand unit is error!");
}
double boundary_demand = 0;
for (auto& [orient, net_set] : _orient_demand_map) {
boundary_demand += (static_cast<double>(net_set.size()) * _boundary_wire_unit);
for (Orientation orient : {Orientation::kEast, Orientation::kWest, Orientation::kSouth, Orientation::kNorth}) {
if (RTUTIL.exist(_orient_net_map, orient)) {
boundary_demand += (static_cast<double>(_orient_net_map[orient].size()) * _boundary_wire_unit);
}
}
double internal_demand = 0;
for (auto& [orient, net_set] : _orient_demand_map) {
internal_demand += (static_cast<double>(net_set.size()) * _internal_wire_unit);
for (Orientation orient : {Orientation::kEast, Orientation::kWest, Orientation::kSouth, Orientation::kNorth}) {
if (RTUTIL.exist(_orient_net_map, orient)) {
internal_demand += (static_cast<double>(_orient_net_map[orient].size()) * _internal_wire_unit);
}
}
for (auto& [net_idx, orient_set] : _net_orient_map) {
if (RTUTIL.exist(orient_set, Orientation::kAbove) || RTUTIL.exist(orient_set, Orientation::kBelow)) {
internal_demand += _internal_via_unit;
}
}
internal_demand += (static_cast<double>(_via_net_set.size()) * _internal_via_unit);
return (boundary_demand + internal_demand);
}
double getOverflow()
@@ -160,8 +175,8 @@ class LANode : public LayerCoord
double boundary_overflow = 0;
for (Orientation orient : {Orientation::kEast, Orientation::kWest, Orientation::kSouth, Orientation::kNorth}) {
double boundary_demand = 0;
if (RTUTIL.exist(_orient_demand_map, orient)) {
boundary_demand = (static_cast<double>(_orient_demand_map[orient].size()) * _boundary_wire_unit);
if (RTUTIL.exist(_orient_net_map, orient)) {
boundary_demand = (static_cast<double>(_orient_net_map[orient].size()) * _boundary_wire_unit);
}
double boundary_supply = 0;
if (RTUTIL.exist(_orient_supply_map, orient)) {
@@ -172,10 +187,16 @@ class LANode : public LayerCoord
double internal_overflow = 0;
{
double internal_demand = 0;
for (auto& [orient, net_set] : _orient_demand_map) {
internal_demand += (static_cast<double>(net_set.size()) * _internal_wire_unit);
for (Orientation orient : {Orientation::kEast, Orientation::kWest, Orientation::kSouth, Orientation::kNorth}) {
if (RTUTIL.exist(_orient_net_map, orient)) {
internal_demand += (static_cast<double>(_orient_net_map[orient].size()) * _internal_wire_unit);
}
}
for (auto& [net_idx, orient_set] : _net_orient_map) {
if (RTUTIL.exist(orient_set, Orientation::kAbove) || RTUTIL.exist(orient_set, Orientation::kBelow)) {
internal_demand += _internal_via_unit;
}
}
internal_demand += (static_cast<double>(_via_net_set.size()) * _internal_via_unit);
double internal_supply = 0;
for (auto& [orient, supply] : _orient_supply_map) {
internal_supply += (supply * _internal_wire_unit);
@@ -187,20 +208,12 @@ class LANode : public LayerCoord
void updateDemand(int32_t net_idx, std::set<Orientation> orient_set, ChangeType change_type)
{
for (const Orientation& orient : orient_set) {
if (orient == Orientation::kEast || orient == Orientation::kWest || orient == Orientation::kSouth || orient == Orientation::kNorth) {
if (change_type == ChangeType::kAdd) {
_orient_demand_map[orient].insert(net_idx);
} else {
_orient_demand_map[orient].erase(net_idx);
}
} else if (orient == Orientation::kAbove || orient == Orientation::kBelow) {
if (change_type == ChangeType::kAdd) {
_via_net_set.insert(net_idx);
} else {
_via_net_set.erase(net_idx);
}
if (change_type == ChangeType::kAdd) {
_orient_net_map[orient].insert(net_idx);
_net_orient_map[net_idx].insert(orient);
} else {
RTLOG.error(Loc::current(), "The orientation is error!");
_orient_net_map[orient].erase(net_idx);
_net_orient_map[net_idx].erase(orient);
}
}
}
@@ -227,8 +240,8 @@ class LANode : public LayerCoord
double _internal_via_unit = -1;
std::map<Orientation, LANode*> _neighbor_node_map;
std::map<Orientation, int32_t> _orient_supply_map;
std::map<Orientation, std::set<int32_t>> _orient_demand_map;
std::set<int32_t> _via_net_set;
std::map<Orientation, std::set<int32_t>> _orient_net_map;
std::map<int32_t, std::set<Orientation>> _net_orient_map;
#if 1 // astar
// single path
LANodeState _state = LANodeState::kNone;


+ 81
- 0
src/operation/iRT/source/module/pin_accessor/PinAccessor.cpp View File

@@ -591,6 +591,8 @@ void PinAccessor::routePAModel(PAModel& pa_model)
printSummary(pa_model);
outputNetCSV(pa_model);
outputViolationCSV(pa_model);
outputNetJson(pa_model);
outputViolationJson(pa_model);
RTLOG.info(Loc::current(), "***** End Iteration ", iter, "/", pa_iter_param_list.size(), "(", RTUTIL.getPercentage(iter, pa_iter_param_list.size()), ")",
iter_monitor.getStatsInfo(), "*****");
if (stopIteration(pa_model)) {
@@ -2471,6 +2473,8 @@ void PinAccessor::selectBestResult(PAModel& pa_model)
printSummary(pa_model);
outputNetCSV(pa_model);
outputViolationCSV(pa_model);
outputNetJson(pa_model);
outputViolationJson(pa_model);

RTLOG.info(Loc::current(), "Completed", monitor.getStatsInfo());
}
@@ -3437,6 +3441,83 @@ void PinAccessor::outputViolationCSV(PAModel& pa_model)
}
}

void PinAccessor::outputNetJson(PAModel& pa_model)
{
Die& die = RTDM.getDatabase().get_die();
std::vector<RoutingLayer>& routing_layer_list = RTDM.getDatabase().get_routing_layer_list();
std::vector<CutLayer>& cut_layer_list = RTDM.getDatabase().get_cut_layer_list();
std::vector<Net>& net_list = RTDM.getDatabase().get_net_list();
std::string& pa_temp_directory_path = RTDM.getConfig().pa_temp_directory_path;
int32_t output_inter_result = RTDM.getConfig().output_inter_result;
if (!output_inter_result) {
return;
}
std::vector<nlohmann::json> net_json_list;
net_json_list.resize(net_list.size());
for (Net& net : net_list) {
net_json_list[net.get_net_idx()]["net_name"] = net.get_net_name();
}
for (auto& [net_idx, pin_access_result_map] : RTDM.getNetPinAccessResultMap(die)) {
for (auto& [pin_idx, segment_set] : pin_access_result_map) {
for (Segment<LayerCoord>* segment : segment_set) {
for (NetShape& net_shape : RTDM.getNetShapeList(net_idx, *segment)) {
std::string layer_name;
if (net_shape.get_is_routing()) {
layer_name = routing_layer_list[net_shape.get_layer_idx()].get_layer_name();
} else {
layer_name = cut_layer_list[net_shape.get_layer_idx()].get_layer_name();
}
net_json_list[net_idx]["result"].push_back({net_shape.get_ll_x(), net_shape.get_ll_y(), net_shape.get_ur_x(), net_shape.get_ur_y(), layer_name});
}
}
}
}
for (auto& [net_idx, pin_access_patch_map] : RTDM.getNetPinAccessPatchMap(die)) {
for (auto& [pin_idx, patch_set] : pin_access_patch_map) {
for (EXTLayerRect* patch : patch_set) {
net_json_list[net_idx]["patch"].push_back({patch->get_real_ll_x(), patch->get_real_ll_y(), patch->get_real_ur_x(), patch->get_real_ur_y(),
routing_layer_list[patch->get_layer_idx()].get_layer_name()});
}
}
}
std::string net_json_file_path = RTUTIL.getString(pa_temp_directory_path, "net_map_", pa_model.get_iter(), ".json");
std::ofstream* net_json_file = RTUTIL.getOutputFileStream(net_json_file_path);
(*net_json_file) << net_json_list;
RTUTIL.closeFileStream(net_json_file);
RTI.sendNotification(RTUTIL.getString("PA_", pa_model.get_iter(), "_net_map"), net_json_file_path);
}

void PinAccessor::outputViolationJson(PAModel& pa_model)
{
Die& die = RTDM.getDatabase().get_die();
std::vector<RoutingLayer>& routing_layer_list = RTDM.getDatabase().get_routing_layer_list();
std::vector<Net>& net_list = RTDM.getDatabase().get_net_list();
std::string& pa_temp_directory_path = RTDM.getConfig().pa_temp_directory_path;
int32_t output_inter_result = RTDM.getConfig().output_inter_result;
if (!output_inter_result) {
return;
}
std::vector<nlohmann::json> violation_json_list;
for (Violation* violation : RTDM.getViolationSet(die)) {
EXTLayerRect& violation_shape = violation->get_violation_shape();

nlohmann::json violation_json;
violation_json["type"] = GetViolationTypeName()(violation->get_violation_type());
violation_json["shape"]
= {violation_shape.get_real_rect().get_ll_x(), violation_shape.get_real_rect().get_ll_y(), violation_shape.get_real_rect().get_ur_x(),
violation_shape.get_real_rect().get_ur_y(), routing_layer_list[violation_shape.get_layer_idx()].get_layer_name()};
for (int32_t net_idx : violation->get_violation_net_set()) {
violation_json["net"].push_back(net_list[net_idx].get_net_name());
}
violation_json_list.push_back(violation_json);
}
std::string violation_json_file_path = RTUTIL.getString(pa_temp_directory_path, "violation_map_", pa_model.get_iter(), ".json");
std::ofstream* violation_json_file = RTUTIL.getOutputFileStream(violation_json_file_path);
(*violation_json_file) << violation_json_list;
RTUTIL.closeFileStream(violation_json_file);
RTI.sendNotification(RTUTIL.getString("PA_", pa_model.get_iter(), "_violation_map"), violation_json_file_path);
}

#endif

#if 1 // debug


+ 2
- 0
src/operation/iRT/source/module/pin_accessor/PinAccessor.hpp View File

@@ -183,6 +183,8 @@ class PinAccessor
void printSummary(PAModel& pa_model);
void outputNetCSV(PAModel& pa_model);
void outputViolationCSV(PAModel& pa_model);
void outputNetJson(PAModel& pa_model);
void outputViolationJson(PAModel& pa_model);
#endif

#if 1 // debug


+ 87
- 17
src/operation/iRT/source/module/space_router/SpaceRouter.cpp View File

@@ -156,8 +156,8 @@ void SpaceRouter::reviseNodeDemand(SRModel& sr_model)
for (int32_t x = 0; x < gcell_map.get_x_size(); x++) {
for (int32_t y = 0; y < gcell_map.get_y_size(); y++) {
for (int32_t layer_idx = 0; layer_idx < static_cast<int32_t>(layer_node_map.size()); layer_idx++) {
layer_node_map[layer_idx][x][y].get_orient_demand_map().clear();
layer_node_map[layer_idx][x][y].get_via_net_set().clear();
layer_node_map[layer_idx][x][y].get_orient_net_map().clear();
layer_node_map[layer_idx][x][y].get_net_orient_map().clear();
}
}
}
@@ -205,8 +205,10 @@ void SpaceRouter::routeSRModel(SRModel& sr_model)
updateSummary(sr_model);
printSummary(sr_model);
outputGuide(sr_model);
outputDemandCSV(sr_model);
outputNetCSV(sr_model);
outputOverflowCSV(sr_model);
outputNetJson(sr_model);
outputOverflowJson(sr_model);
RTLOG.info(Loc::current(), "***** End Iteration ", iter, "/", sr_iter_param_list.size(), "(", RTUTIL.getPercentage(iter, sr_iter_param_list.size()), ")",
iter_monitor.getStatsInfo(), "*****");
if (stopIteration(sr_model)) {
@@ -577,10 +579,10 @@ void SpaceRouter::buildOverflow(SRModel& sr_model, SRBox& sr_box)
total_overflow += node_overflow;
if (node_overflow > 0) {
std::set<int32_t> overflow_net_set;
for (auto& [orient, net_set] : sr_node_map[x][y].get_orient_demand_map()) {
for (auto& [orient, net_set] : sr_node_map[x][y].get_orient_net_map()) {
overflow_net_set.insert(net_set.begin(), net_set.end());
}
for (int32_t net_idx : sr_node_map[x][y].get_via_net_set()) {
for (auto& [net_idx, orient_set] : sr_node_map[x][y].get_net_orient_map()) {
overflow_net_set.insert(net_idx);
}
overflow_net_set_list.push_back(overflow_net_set);
@@ -738,8 +740,8 @@ void SpaceRouter::buildOrientDemand(SRModel& sr_model, SRBox& sr_box)
for (int32_t x = 0; x < sr_node_map.get_x_size(); x++) {
for (int32_t y = 0; y < sr_node_map.get_y_size(); y++) {
SRNode& sr_node = sr_node_map[x][y];
sr_node.set_orient_demand_map(top_sr_node_map[sr_node.get_x()][sr_node.get_y()].get_orient_demand_map());
sr_node.set_via_net_set(top_sr_node_map[sr_node.get_x()][sr_node.get_y()].get_via_net_set());
sr_node.set_orient_net_map(top_sr_node_map[sr_node.get_x()][sr_node.get_y()].get_orient_net_map());
sr_node.set_net_orient_map(top_sr_node_map[sr_node.get_x()][sr_node.get_y()].get_net_orient_map());
}
}
}
@@ -1175,10 +1177,10 @@ void SpaceRouter::updateOverflow(SRBox& sr_box)
total_overflow += node_overflow;
if (node_overflow > 0) {
std::set<int32_t> overflow_net_set;
for (auto& [orient, net_set] : sr_node_map[x][y].get_orient_demand_map()) {
for (auto& [orient, net_set] : sr_node_map[x][y].get_orient_net_map()) {
overflow_net_set.insert(net_set.begin(), net_set.end());
}
for (int32_t net_idx : sr_node_map[x][y].get_via_net_set()) {
for (auto& [net_idx, orient_set] : sr_node_map[x][y].get_net_orient_map()) {
overflow_net_set.insert(net_idx);
}
overflow_net_set_list.push_back(overflow_net_set);
@@ -1365,8 +1367,10 @@ void SpaceRouter::selectBestResult(SRModel& sr_model)
updateSummary(sr_model);
printSummary(sr_model);
outputGuide(sr_model);
outputDemandCSV(sr_model);
outputNetCSV(sr_model);
outputOverflowCSV(sr_model);
outputNetJson(sr_model);
outputOverflowJson(sr_model);

RTLOG.info(Loc::current(), "Completed", monitor.getStatsInfo());
}
@@ -1748,7 +1752,7 @@ void SpaceRouter::outputGuide(SRModel& sr_model)
RTUTIL.closeFileStream(guide_file_stream);
}

void SpaceRouter::outputDemandCSV(SRModel& sr_model)
void SpaceRouter::outputNetCSV(SRModel& sr_model)
{
std::vector<RoutingLayer>& routing_layer_list = RTDM.getDatabase().get_routing_layer_list();
std::string& sr_temp_directory_path = RTDM.getConfig().sr_temp_directory_path;
@@ -1758,17 +1762,16 @@ void SpaceRouter::outputDemandCSV(SRModel& sr_model)
}
std::vector<GridMap<SRNode>>& layer_node_map = sr_model.get_layer_node_map();
for (RoutingLayer& routing_layer : routing_layer_list) {
std::ofstream* demand_csv_file
= RTUTIL.getOutputFileStream(RTUTIL.getString(sr_temp_directory_path, "demand_map_", routing_layer.get_layer_name(), "_", sr_model.get_iter(), ".csv"));

std::ofstream* net_csv_file
= RTUTIL.getOutputFileStream(RTUTIL.getString(sr_temp_directory_path, "net_map_", routing_layer.get_layer_name(), "_", sr_model.get_iter(), ".csv"));
GridMap<SRNode>& sr_node_map = layer_node_map[routing_layer.get_layer_idx()];
for (int32_t y = sr_node_map.get_y_size() - 1; y >= 0; y--) {
for (int32_t x = 0; x < sr_node_map.get_x_size(); x++) {
RTUTIL.pushStream(demand_csv_file, sr_node_map[x][y].getDemand(), ",");
RTUTIL.pushStream(net_csv_file, sr_node_map[x][y].getDemand(), ",");
}
RTUTIL.pushStream(demand_csv_file, "\n");
RTUTIL.pushStream(net_csv_file, "\n");
}
RTUTIL.closeFileStream(demand_csv_file);
RTUTIL.closeFileStream(net_csv_file);
}
}

@@ -1796,6 +1799,73 @@ void SpaceRouter::outputOverflowCSV(SRModel& sr_model)
}
}

void SpaceRouter::outputNetJson(SRModel& sr_model)
{
Die& die = RTDM.getDatabase().get_die();
ScaleAxis& gcell_axis = RTDM.getDatabase().get_gcell_axis();
std::vector<RoutingLayer>& routing_layer_list = RTDM.getDatabase().get_routing_layer_list();
std::vector<Net>& net_list = RTDM.getDatabase().get_net_list();
std::string& sr_temp_directory_path = RTDM.getConfig().sr_temp_directory_path;
int32_t output_inter_result = RTDM.getConfig().output_inter_result;
if (!output_inter_result) {
return;
}
std::vector<nlohmann::json> net_json_list;
net_json_list.resize(net_list.size());
for (Net& net : net_list) {
net_json_list[net.get_net_idx()]["net_name"] = net.get_net_name();
}
for (auto& [net_idx, segment_set] : RTDM.getNetGlobalResultMap(die)) {
for (Segment<LayerCoord>* segment : segment_set) {
PlanarRect first_gcell = RTUTIL.getRealRectByGCell(segment->get_first(), gcell_axis);
PlanarRect second_gcell = RTUTIL.getRealRectByGCell(segment->get_second(), gcell_axis);
if (segment->get_first().get_layer_idx() != segment->get_second().get_layer_idx()) {
net_json_list[net_idx]["result"].push_back({first_gcell.get_ll_x(), first_gcell.get_ll_y(), first_gcell.get_ur_x(), first_gcell.get_ur_y(),
routing_layer_list[segment->get_first().get_layer_idx()].get_layer_name()});
net_json_list[net_idx]["result"].push_back({second_gcell.get_ll_x(), second_gcell.get_ll_y(), second_gcell.get_ur_x(), second_gcell.get_ur_y(),
routing_layer_list[segment->get_second().get_layer_idx()].get_layer_name()});
} else {
PlanarRect gcell = RTUTIL.getBoundingBox({first_gcell, second_gcell});
net_json_list[net_idx]["result"].push_back({gcell.get_ll_x(), gcell.get_ll_y(), gcell.get_ur_x(), gcell.get_ur_y(),
routing_layer_list[segment->get_first().get_layer_idx()].get_layer_name()});
}
}
}
std::string net_json_file_path = RTUTIL.getString(sr_temp_directory_path, "net_map_", sr_model.get_iter(), ".json");
std::ofstream* net_json_file = RTUTIL.getOutputFileStream(net_json_file_path);
(*net_json_file) << net_json_list;
RTUTIL.closeFileStream(net_json_file);
RTI.sendNotification(RTUTIL.getString("SR_", sr_model.get_iter(), "_net_map"), net_json_file_path);
}

void SpaceRouter::outputOverflowJson(SRModel& sr_model)
{
ScaleAxis& gcell_axis = RTDM.getDatabase().get_gcell_axis();
std::vector<RoutingLayer>& routing_layer_list = RTDM.getDatabase().get_routing_layer_list();
std::string& sr_temp_directory_path = RTDM.getConfig().sr_temp_directory_path;
int32_t output_inter_result = RTDM.getConfig().output_inter_result;
if (!output_inter_result) {
return;
}
std::vector<GridMap<SRNode>>& layer_node_map = sr_model.get_layer_node_map();
std::vector<nlohmann::json> overflow_json_list;
for (int32_t layer_idx = 0; layer_idx < static_cast<int32_t>(layer_node_map.size()); layer_idx++) {
GridMap<SRNode>& sr_node_map = layer_node_map[layer_idx];
for (int32_t x = 0; x < sr_node_map.get_x_size(); x++) {
for (int32_t y = 0; y < sr_node_map.get_y_size(); y++) {
PlanarRect gcell = RTUTIL.getRealRectByGCell(PlanarCoord(x, y), gcell_axis);
overflow_json_list.push_back({gcell.get_ll_x(), gcell.get_ll_y(), gcell.get_ur_x(), gcell.get_ur_y(), routing_layer_list[layer_idx].get_layer_name(),
sr_node_map[x][y].getOverflow()});
}
}
}
std::string overflow_json_file_path = RTUTIL.getString(sr_temp_directory_path, "overflow_map_", sr_model.get_iter(), ".json");
std::ofstream* overflow_json_file = RTUTIL.getOutputFileStream(overflow_json_file_path);
(*overflow_json_file) << overflow_json_list;
RTUTIL.closeFileStream(overflow_json_file);
RTI.sendNotification(RTUTIL.getString("SR_", sr_model.get_iter(), "_net_map"), overflow_json_file_path);
}

#endif

#if 1 // debug


+ 3
- 1
src/operation/iRT/source/module/space_router/SpaceRouter.hpp View File

@@ -118,8 +118,10 @@ class SpaceRouter
void updateSummary(SRModel& sr_model);
void printSummary(SRModel& sr_model);
void outputGuide(SRModel& sr_model);
void outputDemandCSV(SRModel& sr_model);
void outputNetCSV(SRModel& sr_model);
void outputOverflowCSV(SRModel& sr_model);
void outputNetJson(SRModel& sr_model);
void outputOverflowJson(SRModel& sr_model);
#endif

#if 1 // debug


+ 51
- 38
src/operation/iRT/source/module/space_router/sr_data_manager/SRNode.hpp View File

@@ -44,16 +44,16 @@ class SRNode : public LayerCoord
double get_internal_via_unit() const { return _internal_via_unit; }
std::map<Orientation, SRNode*>& get_neighbor_node_map() { return _neighbor_node_map; }
std::map<Orientation, int32_t>& get_orient_supply_map() { return _orient_supply_map; }
std::map<Orientation, std::set<int32_t>>& get_orient_demand_map() { return _orient_demand_map; }
std::set<int32_t>& get_via_net_set() { return _via_net_set; }
std::map<Orientation, std::set<int32_t>>& get_orient_net_map() { return _orient_net_map; }
std::map<int32_t, std::set<Orientation>>& get_net_orient_map() { return _net_orient_map; }
// setter
void set_boundary_wire_unit(const double boundary_wire_unit) { _boundary_wire_unit = boundary_wire_unit; }
void set_internal_wire_unit(const double internal_wire_unit) { _internal_wire_unit = internal_wire_unit; }
void set_internal_via_unit(const double internal_via_unit) { _internal_via_unit = internal_via_unit; }
void set_neighbor_node_map(const std::map<Orientation, SRNode*>& neighbor_node_map) { _neighbor_node_map = neighbor_node_map; }
void set_orient_supply_map(const std::map<Orientation, int32_t>& orient_supply_map) { _orient_supply_map = orient_supply_map; }
void set_orient_demand_map(const std::map<Orientation, std::set<int32_t>>& orient_demand_map) { _orient_demand_map = orient_demand_map; }
void set_via_net_set(const std::set<int32_t>& via_net_set) { _via_net_set = via_net_set; }
void set_orient_net_map(const std::map<Orientation, std::set<int32_t>>& orient_net_map) { _orient_net_map = orient_net_map; }
void set_net_orient_map(const std::map<int32_t, std::set<Orientation>>& net_orient_map) { _net_orient_map = net_orient_map; }
// function
SRNode* getNeighborNode(Orientation orientation)
{
@@ -63,7 +63,7 @@ class SRNode : public LayerCoord
}
return neighbor_node;
}
double getOverflowCost(int32_t net_idx, Orientation orientation, double overflow_unit)
double getOverflowCost(int32_t curr_net_idx, Orientation orientation, double overflow_unit)
{
if (!validDemandUnit()) {
RTLOG.error(Loc::current(), "The demand unit is error!");
@@ -71,10 +71,10 @@ class SRNode : public LayerCoord
double boundary_overflow = 0;
if (orientation == Orientation::kEast || orientation == Orientation::kWest || orientation == Orientation::kSouth || orientation == Orientation::kNorth) {
double boundary_demand = 0;
if (RTUTIL.exist(_orient_demand_map, orientation)) {
std::set<int32_t>& net_set = _orient_demand_map[orientation];
if (RTUTIL.exist(_orient_net_map, orientation)) {
std::set<int32_t>& net_set = _orient_net_map[orientation];
boundary_demand += (static_cast<double>(net_set.size()) * _boundary_wire_unit);
if (RTUTIL.exist(net_set, net_idx)) {
if (RTUTIL.exist(net_set, curr_net_idx)) {
boundary_demand -= _boundary_wire_unit;
}
}
@@ -87,15 +87,22 @@ class SRNode : public LayerCoord
double internal_overflow = 0;
{
double internal_demand = 0;
for (auto& [orient, net_set] : _orient_demand_map) {
for (auto& [orient, net_set] : _orient_net_map) {
if (orient == Orientation::kAbove || orient == Orientation::kBelow) {
continue;
}
internal_demand += (static_cast<double>(net_set.size()) * _internal_wire_unit);
if (RTUTIL.exist(net_set, net_idx)) {
if (RTUTIL.exist(net_set, curr_net_idx)) {
internal_demand -= _internal_wire_unit;
}
}
internal_demand += (static_cast<double>(_via_net_set.size()) * _internal_via_unit);
if (RTUTIL.exist(_via_net_set, net_idx)) {
internal_demand -= _internal_via_unit;
for (auto& [net_idx, orient_set] : _net_orient_map) {
if (net_idx == curr_net_idx) {
continue;
}
if (RTUTIL.exist(orient_set, Orientation::kAbove) || RTUTIL.exist(orient_set, Orientation::kBelow)) {
internal_demand += _internal_via_unit;
}
}
double internal_supply = 0;
for (auto& [orient, supply] : _orient_supply_map) {
@@ -142,14 +149,22 @@ class SRNode : public LayerCoord
RTLOG.error(Loc::current(), "The demand unit is error!");
}
double boundary_demand = 0;
for (auto& [orient, net_set] : _orient_demand_map) {
boundary_demand += (static_cast<double>(net_set.size()) * _boundary_wire_unit);
for (Orientation orient : {Orientation::kEast, Orientation::kWest, Orientation::kSouth, Orientation::kNorth}) {
if (RTUTIL.exist(_orient_net_map, orient)) {
boundary_demand += (static_cast<double>(_orient_net_map[orient].size()) * _boundary_wire_unit);
}
}
double internal_demand = 0;
for (auto& [orient, net_set] : _orient_demand_map) {
internal_demand += (static_cast<double>(net_set.size()) * _internal_wire_unit);
for (Orientation orient : {Orientation::kEast, Orientation::kWest, Orientation::kSouth, Orientation::kNorth}) {
if (RTUTIL.exist(_orient_net_map, orient)) {
internal_demand += (static_cast<double>(_orient_net_map[orient].size()) * _internal_wire_unit);
}
}
for (auto& [net_idx, orient_set] : _net_orient_map) {
if (RTUTIL.exist(orient_set, Orientation::kAbove) || RTUTIL.exist(orient_set, Orientation::kBelow)) {
internal_demand += _internal_via_unit;
}
}
internal_demand += (static_cast<double>(_via_net_set.size()) * _internal_via_unit);
return (boundary_demand + internal_demand);
}
double getOverflow()
@@ -160,8 +175,8 @@ class SRNode : public LayerCoord
double boundary_overflow = 0;
for (Orientation orient : {Orientation::kEast, Orientation::kWest, Orientation::kSouth, Orientation::kNorth}) {
double boundary_demand = 0;
if (RTUTIL.exist(_orient_demand_map, orient)) {
boundary_demand = (static_cast<double>(_orient_demand_map[orient].size()) * _boundary_wire_unit);
if (RTUTIL.exist(_orient_net_map, orient)) {
boundary_demand = (static_cast<double>(_orient_net_map[orient].size()) * _boundary_wire_unit);
}
double boundary_supply = 0;
if (RTUTIL.exist(_orient_supply_map, orient)) {
@@ -172,10 +187,16 @@ class SRNode : public LayerCoord
double internal_overflow = 0;
{
double internal_demand = 0;
for (auto& [orient, net_set] : _orient_demand_map) {
internal_demand += (static_cast<double>(net_set.size()) * _internal_wire_unit);
for (Orientation orient : {Orientation::kEast, Orientation::kWest, Orientation::kSouth, Orientation::kNorth}) {
if (RTUTIL.exist(_orient_net_map, orient)) {
internal_demand += (static_cast<double>(_orient_net_map[orient].size()) * _internal_wire_unit);
}
}
for (auto& [net_idx, orient_set] : _net_orient_map) {
if (RTUTIL.exist(orient_set, Orientation::kAbove) || RTUTIL.exist(orient_set, Orientation::kBelow)) {
internal_demand += _internal_via_unit;
}
}
internal_demand += (static_cast<double>(_via_net_set.size()) * _internal_via_unit);
double internal_supply = 0;
for (auto& [orient, supply] : _orient_supply_map) {
internal_supply += (supply * _internal_wire_unit);
@@ -187,20 +208,12 @@ class SRNode : public LayerCoord
void updateDemand(int32_t net_idx, std::set<Orientation> orient_set, ChangeType change_type)
{
for (const Orientation& orient : orient_set) {
if (orient == Orientation::kEast || orient == Orientation::kWest || orient == Orientation::kSouth || orient == Orientation::kNorth) {
if (change_type == ChangeType::kAdd) {
_orient_demand_map[orient].insert(net_idx);
} else {
_orient_demand_map[orient].erase(net_idx);
}
} else if (orient == Orientation::kAbove || orient == Orientation::kBelow) {
if (change_type == ChangeType::kAdd) {
_via_net_set.insert(net_idx);
} else {
_via_net_set.erase(net_idx);
}
if (change_type == ChangeType::kAdd) {
_orient_net_map[orient].insert(net_idx);
_net_orient_map[net_idx].insert(orient);
} else {
RTLOG.error(Loc::current(), "The orientation is error!");
_orient_net_map[orient].erase(net_idx);
_net_orient_map[net_idx].erase(orient);
}
}
}
@@ -227,8 +240,8 @@ class SRNode : public LayerCoord
double _internal_via_unit = -1;
std::map<Orientation, SRNode*> _neighbor_node_map;
std::map<Orientation, int32_t> _orient_supply_map;
std::map<Orientation, std::set<int32_t>> _orient_demand_map;
std::set<int32_t> _via_net_set;
std::map<Orientation, std::set<int32_t>> _orient_net_map;
std::map<int32_t, std::set<Orientation>> _net_orient_map;
#if 1 // astar
// single path
SRNodeState _state = SRNodeState::kNone;


+ 21
- 30
src/operation/iRT/source/module/supply_analyzer/SupplyAnalyzer.cpp View File

@@ -81,17 +81,19 @@ SAModel SupplyAnalyzer::initSAModel()
void SupplyAnalyzer::setSAComParam(SAModel& sa_model)
{
double supply_reduction = 0.2;
double wire_unit = 1;
double via_unit = 0.5;
double boundary_wire_unit = 1;
double internal_wire_unit = 1;
double internal_via_unit = 0.5;
/**
* supply_reduction, wire_unit, via_unit
* supply_reduction, boundary_wire_unit, internal_wire_unit, internal_via_unit
*/
// clang-format off
SAComParam sa_com_param(supply_reduction, wire_unit, via_unit);
SAComParam sa_com_param(supply_reduction, boundary_wire_unit, internal_wire_unit, internal_via_unit);
// clang-format on
RTLOG.info(Loc::current(), "supply_reduction: ", sa_com_param.get_supply_reduction());
RTLOG.info(Loc::current(), "wire_unit: ", sa_com_param.get_wire_unit());
RTLOG.info(Loc::current(), "via_unit: ", sa_com_param.get_via_unit());
RTLOG.info(Loc::current(), "boundary_wire_unit: ", sa_com_param.get_boundary_wire_unit());
RTLOG.info(Loc::current(), "internal_wire_unit: ", sa_com_param.get_internal_wire_unit());
RTLOG.info(Loc::current(), "internal_via_unit: ", sa_com_param.get_internal_via_unit());
sa_model.set_sa_com_param(sa_com_param);
}

@@ -206,25 +208,13 @@ void SupplyAnalyzer::analyzeSupply(SAModel& sa_model)
second_orient_supply_map[second_orientation]++;
}
}

// decrease supply
// std::vector<LayerRect> wire_list = getCrossingWireList(search_rect);
// for (LayerRect& wire : wire_list) {
// if (isAccess(wire, obs_rect_list)) {
// first_orient_supply_map[first_orientation]++;
// second_orient_supply_map[second_orientation]++;
// }
// }
// for (auto& [orient, supply] : first_orient_supply_map) {
// if (supply == static_cast<int32_t>(wire_list.size())) {
// supply = std::max(0, supply - 7);
// }
// }
// for (auto& [orient, supply] : second_orient_supply_map) {
// if (supply == static_cast<int32_t>(wire_list.size())) {
// supply = std::max(0, supply - 7);
// }
// }
int32_t max_supply = std::max(0, static_cast<int32_t>(std::round(static_cast<double>(wire_list.size()) * (1 - supply_reduction))));
for (auto& [orient, supply] : first_orient_supply_map) {
supply = std::min(supply, max_supply);
}
for (auto& [orient, supply] : second_orient_supply_map) {
supply = std::min(supply, max_supply);
}
}
analyzed_pair_num += grid_pair_list.size();
RTLOG.info(Loc::current(), "Analyzed ", analyzed_pair_num, "/", total_pair_num, "(", RTUTIL.getPercentage(analyzed_pair_num, total_pair_num),
@@ -349,15 +339,16 @@ void SupplyAnalyzer::replenishPinSupply(SAModel& sa_model)
void SupplyAnalyzer::analyzeDemandUnit(SAModel& sa_model)
{
GridMap<GCell>& gcell_map = RTDM.getDatabase().get_gcell_map();
double wire_unit = sa_model.get_sa_com_param().get_wire_unit();
double via_unit = sa_model.get_sa_com_param().get_via_unit();
double boundary_wire_unit = sa_model.get_sa_com_param().get_boundary_wire_unit();
double internal_wire_unit = sa_model.get_sa_com_param().get_internal_wire_unit();
double internal_via_unit = sa_model.get_sa_com_param().get_internal_via_unit();

for (int32_t x = 0; x < gcell_map.get_x_size(); x++) {
for (int32_t y = 0; y < gcell_map.get_y_size(); y++) {
GCell& gcell = gcell_map[x][y];
gcell.set_boundary_wire_unit(wire_unit);
gcell.set_internal_wire_unit(wire_unit);
gcell.set_internal_via_unit(via_unit);
gcell.set_boundary_wire_unit(boundary_wire_unit);
gcell.set_internal_wire_unit(internal_wire_unit);
gcell.set_internal_via_unit(internal_via_unit);
}
}
}


+ 13
- 9
src/operation/iRT/source/module/supply_analyzer/sa_data_manager/SAComParam.hpp View File

@@ -22,26 +22,30 @@ class SAComParam
{
public:
SAComParam() = default;
SAComParam(double supply_reduction, double wire_unit, double via_unit)
SAComParam(double supply_reduction, double boundary_wire_unit, double internal_wire_unit, double internal_via_unit)
{
_supply_reduction = supply_reduction;
_wire_unit = wire_unit;
_via_unit = via_unit;
_boundary_wire_unit = boundary_wire_unit;
_internal_wire_unit = internal_wire_unit;
_internal_via_unit = internal_via_unit;
}
~SAComParam() = default;
// getter
double get_supply_reduction() const { return _supply_reduction; }
double get_wire_unit() const { return _wire_unit; }
double get_via_unit() const { return _via_unit; }
double get_boundary_wire_unit() const { return _boundary_wire_unit; }
double get_internal_wire_unit() const { return _internal_wire_unit; }
double get_internal_via_unit() const { return _internal_via_unit; }
// setter
void set_supply_reduction(const double supply_reduction) { _supply_reduction = supply_reduction; }
void set_wire_unit(const double wire_unit) { _wire_unit = wire_unit; }
void set_via_unit(const double via_unit) { _via_unit = via_unit; }
void set_boundary_wire_unit(const double boundary_wire_unit) { _boundary_wire_unit = boundary_wire_unit; }
void set_internal_wire_unit(const double internal_wire_unit) { _internal_wire_unit = internal_wire_unit; }
void set_internal_via_unit(const double internal_via_unit) { _internal_via_unit = internal_via_unit; }

private:
double _supply_reduction = -1;
double _wire_unit = -1;
double _via_unit = -1;
double _boundary_wire_unit = -1;
double _internal_wire_unit = -1;
double _internal_via_unit = -1;
};

} // namespace irt

+ 73
- 7
src/operation/iRT/source/module/topology_generator/TopologyGenerator.cpp View File

@@ -64,8 +64,10 @@ void TopologyGenerator::generate()
updateSummary(tg_model);
printSummary(tg_model);
outputGuide(tg_model);
outputDemandCSV(tg_model);
outputNetCSV(tg_model);
outputOverflowCSV(tg_model);
outputNetJson(tg_model);
outputOverflowJson(tg_model);
RTLOG.info(Loc::current(), "Completed", monitor.getStatsInfo());
}

@@ -680,22 +682,22 @@ void TopologyGenerator::outputGuide(TGModel& tg_model)
RTUTIL.closeFileStream(guide_file_stream);
}

void TopologyGenerator::outputDemandCSV(TGModel& tg_model)
void TopologyGenerator::outputNetCSV(TGModel& tg_model)
{
std::string& tg_temp_directory_path = RTDM.getConfig().tg_temp_directory_path;
int32_t output_inter_result = RTDM.getConfig().output_inter_result;
if (!output_inter_result) {
return;
}
std::ofstream* demand_csv_file = RTUTIL.getOutputFileStream(RTUTIL.getString(tg_temp_directory_path, "demand_map_planar.csv"));
std::ofstream* net_csv_file = RTUTIL.getOutputFileStream(RTUTIL.getString(tg_temp_directory_path, "net_map.csv"));
GridMap<TGNode>& tg_node_map = tg_model.get_tg_node_map();
for (int32_t y = tg_node_map.get_y_size() - 1; y >= 0; y--) {
for (int32_t x = 0; x < tg_node_map.get_x_size(); x++) {
RTUTIL.pushStream(demand_csv_file, tg_node_map[x][y].getDemand(), ",");
RTUTIL.pushStream(net_csv_file, tg_node_map[x][y].getDemand(), ",");
}
RTUTIL.pushStream(demand_csv_file, "\n");
RTUTIL.pushStream(net_csv_file, "\n");
}
RTUTIL.closeFileStream(demand_csv_file);
RTUTIL.closeFileStream(net_csv_file);
}

void TopologyGenerator::outputOverflowCSV(TGModel& tg_model)
@@ -705,7 +707,7 @@ void TopologyGenerator::outputOverflowCSV(TGModel& tg_model)
if (!output_inter_result) {
return;
}
std::ofstream* overflow_csv_file = RTUTIL.getOutputFileStream(RTUTIL.getString(tg_temp_directory_path, "overflow_map_planar.csv"));
std::ofstream* overflow_csv_file = RTUTIL.getOutputFileStream(RTUTIL.getString(tg_temp_directory_path, "overflow_map.csv"));
GridMap<TGNode>& tg_node_map = tg_model.get_tg_node_map();
for (int32_t y = tg_node_map.get_y_size() - 1; y >= 0; y--) {
for (int32_t x = 0; x < tg_node_map.get_x_size(); x++) {
@@ -716,6 +718,70 @@ void TopologyGenerator::outputOverflowCSV(TGModel& tg_model)
RTUTIL.closeFileStream(overflow_csv_file);
}

void TopologyGenerator::outputNetJson(TGModel& tg_model)
{
Die& die = RTDM.getDatabase().get_die();
ScaleAxis& gcell_axis = RTDM.getDatabase().get_gcell_axis();
std::vector<RoutingLayer>& routing_layer_list = RTDM.getDatabase().get_routing_layer_list();
std::vector<Net>& net_list = RTDM.getDatabase().get_net_list();
std::string& tg_temp_directory_path = RTDM.getConfig().tg_temp_directory_path;
int32_t output_inter_result = RTDM.getConfig().output_inter_result;
if (!output_inter_result) {
return;
}
std::vector<nlohmann::json> net_json_list;
net_json_list.resize(net_list.size());
for (Net& net : net_list) {
net_json_list[net.get_net_idx()]["net_name"] = net.get_net_name();
}
for (auto& [net_idx, segment_set] : RTDM.getNetGlobalResultMap(die)) {
for (Segment<LayerCoord>* segment : segment_set) {
PlanarRect first_gcell = RTUTIL.getRealRectByGCell(segment->get_first(), gcell_axis);
PlanarRect second_gcell = RTUTIL.getRealRectByGCell(segment->get_second(), gcell_axis);
if (segment->get_first().get_layer_idx() != segment->get_second().get_layer_idx()) {
net_json_list[net_idx]["result"].push_back({first_gcell.get_ll_x(), first_gcell.get_ll_y(), first_gcell.get_ur_x(), first_gcell.get_ur_y(),
routing_layer_list[segment->get_first().get_layer_idx()].get_layer_name()});
net_json_list[net_idx]["result"].push_back({second_gcell.get_ll_x(), second_gcell.get_ll_y(), second_gcell.get_ur_x(), second_gcell.get_ur_y(),
routing_layer_list[segment->get_second().get_layer_idx()].get_layer_name()});
} else {
PlanarRect gcell = RTUTIL.getBoundingBox({first_gcell, second_gcell});
net_json_list[net_idx]["result"].push_back({gcell.get_ll_x(), gcell.get_ll_y(), gcell.get_ur_x(), gcell.get_ur_y(),
routing_layer_list[segment->get_first().get_layer_idx()].get_layer_name()});
}
}
}
std::string net_json_file_path = RTUTIL.getString(tg_temp_directory_path, "net_map.json");
std::ofstream* net_json_file = RTUTIL.getOutputFileStream(net_json_file_path);
(*net_json_file) << net_json_list;
RTUTIL.closeFileStream(net_json_file);
RTI.sendNotification(RTUTIL.getString("TG_net_map"), net_json_file_path);
}

void TopologyGenerator::outputOverflowJson(TGModel& tg_model)
{
ScaleAxis& gcell_axis = RTDM.getDatabase().get_gcell_axis();
std::vector<RoutingLayer>& routing_layer_list = RTDM.getDatabase().get_routing_layer_list();
std::string& tg_temp_directory_path = RTDM.getConfig().tg_temp_directory_path;
int32_t output_inter_result = RTDM.getConfig().output_inter_result;
if (!output_inter_result) {
return;
}
GridMap<TGNode>& tg_node_map = tg_model.get_tg_node_map();
std::vector<nlohmann::json> overflow_json_list;
for (int32_t x = 0; x < tg_node_map.get_x_size(); x++) {
for (int32_t y = 0; y < tg_node_map.get_y_size(); y++) {
PlanarRect gcell = RTUTIL.getRealRectByGCell(PlanarCoord(x, y), gcell_axis);
overflow_json_list.push_back(
{gcell.get_ll_x(), gcell.get_ll_y(), gcell.get_ur_x(), gcell.get_ur_y(), routing_layer_list[0].get_layer_name(), tg_node_map[x][y].getOverflow()});
}
}
std::string overflow_json_file_path = RTUTIL.getString(tg_temp_directory_path, "overflow_map.json");
std::ofstream* overflow_json_file = RTUTIL.getOutputFileStream(overflow_json_file_path);
(*overflow_json_file) << overflow_json_list;
RTUTIL.closeFileStream(overflow_json_file);
RTI.sendNotification(RTUTIL.getString("TG_overflow_map"), overflow_json_file_path);
}

#endif

#if 1 // debug


+ 3
- 1
src/operation/iRT/source/module/topology_generator/TopologyGenerator.hpp View File

@@ -74,8 +74,10 @@ class TopologyGenerator
void updateSummary(TGModel& tg_model);
void printSummary(TGModel& tg_model);
void outputGuide(TGModel& tg_model);
void outputDemandCSV(TGModel& tg_model);
void outputNetCSV(TGModel& tg_model);
void outputOverflowCSV(TGModel& tg_model);
void outputNetJson(TGModel& tg_model);
void outputOverflowJson(TGModel& tg_model);
#endif

#if 1 // debug


+ 51
- 38
src/operation/iRT/source/module/topology_generator/tg_data_manager/TGNode.hpp View File

@@ -35,16 +35,16 @@ class TGNode : public PlanarCoord
double get_internal_via_unit() const { return _internal_via_unit; }
std::map<Orientation, TGNode*>& get_neighbor_node_map() { return _neighbor_node_map; }
std::map<Orientation, int32_t>& get_orient_supply_map() { return _orient_supply_map; }
std::map<Orientation, std::set<int32_t>>& get_orient_demand_map() { return _orient_demand_map; }
std::set<int32_t>& get_via_net_set() { return _via_net_set; }
std::map<Orientation, std::set<int32_t>>& get_orient_net_map() { return _orient_net_map; }
std::map<int32_t, std::set<Orientation>>& get_net_orient_map() { return _net_orient_map; }
// setter
void set_boundary_wire_unit(const double boundary_wire_unit) { _boundary_wire_unit = boundary_wire_unit; }
void set_internal_wire_unit(const double internal_wire_unit) { _internal_wire_unit = internal_wire_unit; }
void set_internal_via_unit(const double internal_via_unit) { _internal_via_unit = internal_via_unit; }
void set_neighbor_node_map(const std::map<Orientation, TGNode*>& neighbor_node_map) { _neighbor_node_map = neighbor_node_map; }
void set_orient_supply_map(const std::map<Orientation, int32_t>& orient_supply_map) { _orient_supply_map = orient_supply_map; }
void set_orient_demand_map(const std::map<Orientation, std::set<int32_t>>& orient_demand_map) { _orient_demand_map = orient_demand_map; }
void set_via_net_set(const std::set<int32_t>& via_net_set) { _via_net_set = via_net_set; }
void set_orient_net_map(const std::map<Orientation, std::set<int32_t>>& orient_net_map) { _orient_net_map = orient_net_map; }
void set_net_orient_map(const std::map<int32_t, std::set<Orientation>>& net_orient_map) { _net_orient_map = net_orient_map; }
// function
TGNode* getNeighborNode(Orientation orientation)
{
@@ -54,7 +54,7 @@ class TGNode : public PlanarCoord
}
return neighbor_node;
}
double getOverflowCost(int32_t net_idx, Orientation orientation, double overflow_unit)
double getOverflowCost(int32_t curr_net_idx, Orientation orientation, double overflow_unit)
{
if (!validDemandUnit()) {
RTLOG.error(Loc::current(), "The demand unit is error!");
@@ -62,10 +62,10 @@ class TGNode : public PlanarCoord
double boundary_overflow = 0;
if (orientation == Orientation::kEast || orientation == Orientation::kWest || orientation == Orientation::kSouth || orientation == Orientation::kNorth) {
double boundary_demand = 0;
if (RTUTIL.exist(_orient_demand_map, orientation)) {
std::set<int32_t>& net_set = _orient_demand_map[orientation];
if (RTUTIL.exist(_orient_net_map, orientation)) {
std::set<int32_t>& net_set = _orient_net_map[orientation];
boundary_demand += (static_cast<double>(net_set.size()) * _boundary_wire_unit);
if (RTUTIL.exist(net_set, net_idx)) {
if (RTUTIL.exist(net_set, curr_net_idx)) {
boundary_demand -= _boundary_wire_unit;
}
}
@@ -78,15 +78,22 @@ class TGNode : public PlanarCoord
double internal_overflow = 0;
{
double internal_demand = 0;
for (auto& [orient, net_set] : _orient_demand_map) {
for (auto& [orient, net_set] : _orient_net_map) {
if (orient == Orientation::kAbove || orient == Orientation::kBelow) {
continue;
}
internal_demand += (static_cast<double>(net_set.size()) * _internal_wire_unit);
if (RTUTIL.exist(net_set, net_idx)) {
if (RTUTIL.exist(net_set, curr_net_idx)) {
internal_demand -= _internal_wire_unit;
}
}
internal_demand += (static_cast<double>(_via_net_set.size()) * _internal_via_unit);
if (RTUTIL.exist(_via_net_set, net_idx)) {
internal_demand -= _internal_via_unit;
for (auto& [net_idx, orient_set] : _net_orient_map) {
if (net_idx == curr_net_idx) {
continue;
}
if (RTUTIL.exist(orient_set, Orientation::kAbove) || RTUTIL.exist(orient_set, Orientation::kBelow)) {
internal_demand += _internal_via_unit;
}
}
double internal_supply = 0;
for (auto& [orient, supply] : _orient_supply_map) {
@@ -133,14 +140,22 @@ class TGNode : public PlanarCoord
RTLOG.error(Loc::current(), "The demand unit is error!");
}
double boundary_demand = 0;
for (auto& [orient, net_set] : _orient_demand_map) {
boundary_demand += (static_cast<double>(net_set.size()) * _boundary_wire_unit);
for (Orientation orient : {Orientation::kEast, Orientation::kWest, Orientation::kSouth, Orientation::kNorth}) {
if (RTUTIL.exist(_orient_net_map, orient)) {
boundary_demand += (static_cast<double>(_orient_net_map[orient].size()) * _boundary_wire_unit);
}
}
double internal_demand = 0;
for (auto& [orient, net_set] : _orient_demand_map) {
internal_demand += (static_cast<double>(net_set.size()) * _internal_wire_unit);
for (Orientation orient : {Orientation::kEast, Orientation::kWest, Orientation::kSouth, Orientation::kNorth}) {
if (RTUTIL.exist(_orient_net_map, orient)) {
internal_demand += (static_cast<double>(_orient_net_map[orient].size()) * _internal_wire_unit);
}
}
for (auto& [net_idx, orient_set] : _net_orient_map) {
if (RTUTIL.exist(orient_set, Orientation::kAbove) || RTUTIL.exist(orient_set, Orientation::kBelow)) {
internal_demand += _internal_via_unit;
}
}
internal_demand += (static_cast<double>(_via_net_set.size()) * _internal_via_unit);
return (boundary_demand + internal_demand);
}
double getOverflow()
@@ -151,8 +166,8 @@ class TGNode : public PlanarCoord
double boundary_overflow = 0;
for (Orientation orient : {Orientation::kEast, Orientation::kWest, Orientation::kSouth, Orientation::kNorth}) {
double boundary_demand = 0;
if (RTUTIL.exist(_orient_demand_map, orient)) {
boundary_demand = (static_cast<double>(_orient_demand_map[orient].size()) * _boundary_wire_unit);
if (RTUTIL.exist(_orient_net_map, orient)) {
boundary_demand = (static_cast<double>(_orient_net_map[orient].size()) * _boundary_wire_unit);
}
double boundary_supply = 0;
if (RTUTIL.exist(_orient_supply_map, orient)) {
@@ -163,10 +178,16 @@ class TGNode : public PlanarCoord
double internal_overflow = 0;
{
double internal_demand = 0;
for (auto& [orient, net_set] : _orient_demand_map) {
internal_demand += (static_cast<double>(net_set.size()) * _internal_wire_unit);
for (Orientation orient : {Orientation::kEast, Orientation::kWest, Orientation::kSouth, Orientation::kNorth}) {
if (RTUTIL.exist(_orient_net_map, orient)) {
internal_demand += (static_cast<double>(_orient_net_map[orient].size()) * _internal_wire_unit);
}
}
for (auto& [net_idx, orient_set] : _net_orient_map) {
if (RTUTIL.exist(orient_set, Orientation::kAbove) || RTUTIL.exist(orient_set, Orientation::kBelow)) {
internal_demand += _internal_via_unit;
}
}
internal_demand += (static_cast<double>(_via_net_set.size()) * _internal_via_unit);
double internal_supply = 0;
for (auto& [orient, supply] : _orient_supply_map) {
internal_supply += (supply * _internal_wire_unit);
@@ -178,20 +199,12 @@ class TGNode : public PlanarCoord
void updateDemand(int32_t net_idx, std::set<Orientation> orient_set, ChangeType change_type)
{
for (const Orientation& orient : orient_set) {
if (orient == Orientation::kEast || orient == Orientation::kWest || orient == Orientation::kSouth || orient == Orientation::kNorth) {
if (change_type == ChangeType::kAdd) {
_orient_demand_map[orient].insert(net_idx);
} else {
_orient_demand_map[orient].erase(net_idx);
}
} else if (orient == Orientation::kAbove || orient == Orientation::kBelow) {
if (change_type == ChangeType::kAdd) {
_via_net_set.insert(net_idx);
} else {
_via_net_set.erase(net_idx);
}
if (change_type == ChangeType::kAdd) {
_orient_net_map[orient].insert(net_idx);
_net_orient_map[net_idx].insert(orient);
} else {
RTLOG.error(Loc::current(), "The orientation is error!");
_orient_net_map[orient].erase(net_idx);
_net_orient_map[net_idx].erase(orient);
}
}
}
@@ -202,8 +215,8 @@ class TGNode : public PlanarCoord
double _internal_via_unit = -1;
std::map<Orientation, TGNode*> _neighbor_node_map;
std::map<Orientation, int32_t> _orient_supply_map;
std::map<Orientation, std::set<int32_t>> _orient_demand_map;
std::set<int32_t> _via_net_set;
std::map<Orientation, std::set<int32_t>> _orient_net_map;
std::map<int32_t, std::set<Orientation>> _net_orient_map;
};

} // namespace irt

+ 76
- 0
src/operation/iRT/source/module/track_assigner/TrackAssigner.cpp View File

@@ -67,6 +67,9 @@ void TrackAssigner::assign()
printSummary(ta_model);
outputNetCSV(ta_model);
outputViolationCSV(ta_model);
outputNetJson(ta_model);
outputViolationJson(ta_model);

RTLOG.info(Loc::current(), "Completed", monitor.getStatsInfo());
}

@@ -1515,6 +1518,79 @@ void TrackAssigner::outputViolationCSV(TAModel& ta_model)
}
}

void TrackAssigner::outputNetJson(TAModel& ta_model)
{
Die& die = RTDM.getDatabase().get_die();
std::vector<RoutingLayer>& routing_layer_list = RTDM.getDatabase().get_routing_layer_list();
std::vector<CutLayer>& cut_layer_list = RTDM.getDatabase().get_cut_layer_list();
std::vector<Net>& net_list = RTDM.getDatabase().get_net_list();
std::string& ta_temp_directory_path = RTDM.getConfig().ta_temp_directory_path;
int32_t output_inter_result = RTDM.getConfig().output_inter_result;
if (!output_inter_result) {
return;
}
std::vector<nlohmann::json> net_json_list;
net_json_list.resize(net_list.size());
for (Net& net : net_list) {
net_json_list[net.get_net_idx()]["net_name"] = net.get_net_name();
}
for (auto& [net_idx, segment_set] : RTDM.getNetDetailedResultMap(die)) {
for (Segment<LayerCoord>* segment : segment_set) {
for (NetShape& net_shape : RTDM.getNetShapeList(net_idx, *segment)) {
std::string layer_name;
if (net_shape.get_is_routing()) {
layer_name = routing_layer_list[net_shape.get_layer_idx()].get_layer_name();
} else {
layer_name = cut_layer_list[net_shape.get_layer_idx()].get_layer_name();
}
net_json_list[net_idx]["result"].push_back({net_shape.get_ll_x(), net_shape.get_ll_y(), net_shape.get_ur_x(), net_shape.get_ur_y(), layer_name});
}
}
}
for (auto& [net_idx, patch_set] : RTDM.getNetDetailedPatchMap(die)) {
for (EXTLayerRect* patch : patch_set) {
net_json_list[net_idx]["patch"].push_back({patch->get_real_ll_x(), patch->get_real_ll_y(), patch->get_real_ur_x(), patch->get_real_ur_y(),
routing_layer_list[patch->get_layer_idx()].get_layer_name()});
}
}
std::string net_json_file_path = RTUTIL.getString(RTUTIL.getString(ta_temp_directory_path, "net_map.json"));
std::ofstream* net_json_file = RTUTIL.getOutputFileStream(net_json_file_path);
(*net_json_file) << net_json_list;
RTUTIL.closeFileStream(net_json_file);
RTI.sendNotification(RTUTIL.getString("TA_net_map"), net_json_file_path);
}

void TrackAssigner::outputViolationJson(TAModel& ta_model)
{
Die& die = RTDM.getDatabase().get_die();
std::vector<RoutingLayer>& routing_layer_list = RTDM.getDatabase().get_routing_layer_list();
std::vector<Net>& net_list = RTDM.getDatabase().get_net_list();
std::string& ta_temp_directory_path = RTDM.getConfig().ta_temp_directory_path;
int32_t output_inter_result = RTDM.getConfig().output_inter_result;
if (!output_inter_result) {
return;
}
std::vector<nlohmann::json> violation_json_list;
for (Violation* violation : RTDM.getViolationSet(die)) {
EXTLayerRect& violation_shape = violation->get_violation_shape();

nlohmann::json violation_json;
violation_json["type"] = GetViolationTypeName()(violation->get_violation_type());
violation_json["shape"]
= {violation_shape.get_real_rect().get_ll_x(), violation_shape.get_real_rect().get_ll_y(), violation_shape.get_real_rect().get_ur_x(),
violation_shape.get_real_rect().get_ur_y(), routing_layer_list[violation_shape.get_layer_idx()].get_layer_name()};
for (int32_t net_idx : violation->get_violation_net_set()) {
violation_json["net"].push_back(net_list[net_idx].get_net_name());
}
violation_json_list.push_back(violation_json);
}
std::string violation_json_file_path = RTUTIL.getString(ta_temp_directory_path, "violation_map.json");
std::ofstream* violation_json_file = RTUTIL.getOutputFileStream(violation_json_file_path);
(*violation_json_file) << violation_json_list;
RTUTIL.closeFileStream(violation_json_file);
RTI.sendNotification(RTUTIL.getString("TA_violation_map"), violation_json_file_path);
}

#endif

#if 1 // debug


+ 2
- 0
src/operation/iRT/source/module/track_assigner/TrackAssigner.hpp View File

@@ -119,6 +119,8 @@ class TrackAssigner
void printSummary(TAModel& ta_model);
void outputNetCSV(TAModel& ta_model);
void outputViolationCSV(TAModel& ta_model);
void outputNetJson(TAModel& ta_model);
void outputViolationJson(TAModel& ta_model);
#endif

#if 1 // debug


+ 75
- 0
src/operation/iRT/source/module/violation_reporter/ViolationReporter.cpp View File

@@ -61,6 +61,8 @@ void ViolationReporter::report()
printSummary(vr_model);
outputNetCSV(vr_model);
outputViolationCSV(vr_model);
outputNetJson(vr_model);
outputViolationJson(vr_model);
RTLOG.info(Loc::current(), "Completed", monitor.getStatsInfo());
}

@@ -506,6 +508,79 @@ void ViolationReporter::outputViolationCSV(VRModel& vr_model)
}
}

void ViolationReporter::outputNetJson(VRModel& vr_model)
{
Die& die = RTDM.getDatabase().get_die();
std::vector<RoutingLayer>& routing_layer_list = RTDM.getDatabase().get_routing_layer_list();
std::vector<CutLayer>& cut_layer_list = RTDM.getDatabase().get_cut_layer_list();
std::vector<Net>& net_list = RTDM.getDatabase().get_net_list();
std::string& vr_temp_directory_path = RTDM.getConfig().vr_temp_directory_path;
int32_t output_inter_result = RTDM.getConfig().output_inter_result;
if (!output_inter_result) {
return;
}
std::vector<nlohmann::json> net_json_list;
net_json_list.resize(net_list.size());
for (Net& net : net_list) {
net_json_list[net.get_net_idx()]["net_name"] = net.get_net_name();
}
for (auto& [net_idx, segment_set] : RTDM.getNetDetailedResultMap(die)) {
for (Segment<LayerCoord>* segment : segment_set) {
for (NetShape& net_shape : RTDM.getNetShapeList(net_idx, *segment)) {
std::string layer_name;
if (net_shape.get_is_routing()) {
layer_name = routing_layer_list[net_shape.get_layer_idx()].get_layer_name();
} else {
layer_name = cut_layer_list[net_shape.get_layer_idx()].get_layer_name();
}
net_json_list[net_idx]["result"].push_back({net_shape.get_ll_x(), net_shape.get_ll_y(), net_shape.get_ur_x(), net_shape.get_ur_y(), layer_name});
}
}
}
for (auto& [net_idx, patch_set] : RTDM.getNetDetailedPatchMap(die)) {
for (EXTLayerRect* patch : patch_set) {
net_json_list[net_idx]["patch"].push_back({patch->get_real_ll_x(), patch->get_real_ll_y(), patch->get_real_ur_x(), patch->get_real_ur_y(),
routing_layer_list[patch->get_layer_idx()].get_layer_name()});
}
}
std::string net_json_file_path = RTUTIL.getString(RTUTIL.getString(vr_temp_directory_path, "net_map.json"));
std::ofstream* net_json_file = RTUTIL.getOutputFileStream(net_json_file_path);
(*net_json_file) << net_json_list;
RTUTIL.closeFileStream(net_json_file);
RTI.sendNotification(RTUTIL.getString("VR_net_map"), net_json_file_path);
}

void ViolationReporter::outputViolationJson(VRModel& vr_model)
{
Die& die = RTDM.getDatabase().get_die();
std::vector<RoutingLayer>& routing_layer_list = RTDM.getDatabase().get_routing_layer_list();
std::vector<Net>& net_list = RTDM.getDatabase().get_net_list();
std::string& vr_temp_directory_path = RTDM.getConfig().vr_temp_directory_path;
int32_t output_inter_result = RTDM.getConfig().output_inter_result;
if (!output_inter_result) {
return;
}
std::vector<nlohmann::json> violation_json_list;
for (Violation* violation : RTDM.getViolationSet(die)) {
EXTLayerRect& violation_shape = violation->get_violation_shape();

nlohmann::json violation_json;
violation_json["type"] = GetViolationTypeName()(violation->get_violation_type());
violation_json["shape"]
= {violation_shape.get_real_rect().get_ll_x(), violation_shape.get_real_rect().get_ll_y(), violation_shape.get_real_rect().get_ur_x(),
violation_shape.get_real_rect().get_ur_y(), routing_layer_list[violation_shape.get_layer_idx()].get_layer_name()};
for (int32_t net_idx : violation->get_violation_net_set()) {
violation_json["net"].push_back(net_list[net_idx].get_net_name());
}
violation_json_list.push_back(violation_json);
}
std::string violation_json_file_path = RTUTIL.getString(vr_temp_directory_path, "violation_map.json");
std::ofstream* violation_json_file = RTUTIL.getOutputFileStream(violation_json_file_path);
(*violation_json_file) << violation_json_list;
RTUTIL.closeFileStream(violation_json_file);
RTI.sendNotification(RTUTIL.getString("VR_violation_map"), violation_json_file_path);
}

#endif

#if 1 // debug


+ 2
- 0
src/operation/iRT/source/module/violation_reporter/ViolationReporter.hpp View File

@@ -57,6 +57,8 @@ class ViolationReporter
void printSummary(VRModel& vr_model);
void outputNetCSV(VRModel& vr_model);
void outputViolationCSV(VRModel& vr_model);
void outputNetJson(VRModel& vr_model);
void outputViolationJson(VRModel& vr_model);
#endif

#if 1 // debug


+ 1
- 0
src/operation/iRT/test/CMakeLists.txt View File

@@ -7,6 +7,7 @@ set(CMAKE_BUILD_TYPE "Debug")
# add_subdirectory(${IRT_TEST}/process_guide)
# add_subdirectory(${IRT_TEST}/test_boost)
# add_subdirectory(${IRT_TEST}/test_gtl)
# add_subdirectory(${IRT_TEST}/test_json)
# add_subdirectory(${IRT_TEST}/test_libfort)
# add_subdirectory(${IRT_TEST}/test_opencv)
# add_subdirectory(${IRT_TEST}/test_pa)

+ 5
- 0
src/operation/iRT/test/test_json/CMakeLists.txt View File

@@ -0,0 +1,5 @@
add_executable(test_json
${IRT_TEST}/test_json/test_json.cpp
)



+ 32
- 0
src/operation/iRT/test/test_json/test_json.cpp View File

@@ -0,0 +1,32 @@
#include <cassert>
#include <fstream>
#include <sstream>
#include <vector>

#include "json.hpp"

int32_t main()
{
std::ofstream json_file("test.json");
///////////////////////////
///////////////////////////
///////////////////////////

nlohmann::json top_json;
for (size_t i = 0; i < 10; i++) {
nlohmann::json net_json;
net_json["net_name"] = "CK";
net_json["result"].push_back("aaa");
net_json["result"].push_back("bbb");
net_json["patch"].push_back("aaa");
net_json["patch"].push_back("bbb");
top_json.push_back(net_json);
}

///////////////////////////
///////////////////////////
///////////////////////////
json_file << top_json;
json_file.close();
return 0;
}

+ 140
- 5
src/operation/iSTA/api/TimingEngine.cc View File

@@ -373,6 +373,12 @@ void TimingEngine::initRcTree() {
FOREACH_NET(design_nl, net) { initRcTree(net); }
}

RcTree& TimingEngine::initVirtualRcTree(const char* rc_tree_name) {
RcTree virtual_rc_tree;
_virtual_rc_trees[rc_tree_name] = std::move(virtual_rc_tree);
return _virtual_rc_trees[rc_tree_name];
}

/**
* @brief reset rc tree to nullptr.
*
@@ -447,14 +453,37 @@ RctNode* TimingEngine::makeOrFindRCTreeNode(DesignObject* pin_or_port) {
return node;
}

/**
* @brief make or find the virtual rc tree node. The virtual rc tree is
* used to not complete net or virtual net.
*
* @param node_name
* @return RctNode*
*/
RctNode* TimingEngine::makeOrFindVirtualRCTreeNode(const char* rc_tree_name,
const char* node_name) {
if (!_virtual_rc_trees.contains(rc_tree_name)) {
LOG_FATAL << "rc tree " << rc_tree_name << " not found";
}

auto& virtual_rc_tree = _virtual_rc_trees[rc_tree_name];

auto* node = virtual_rc_tree.node(node_name);
if (!node) {
return virtual_rc_tree.insertNode(node_name);
}

return node;
}

/**
* @brief find the exist rc tree node.
*
* @param net
* @param node_name
* @return RctNode*
*
* @param net
* @param node_name
* @return RctNode*
*/
RctNode* TimingEngine::findRCTreeNode(Net *net, std::string& node_name) {
RctNode* TimingEngine::findRCTreeNode(Net* net, std::string& node_name) {
auto* rc_net = _timing_engine->get_ista()->getRcNet(net);
LOG_FATAL_IF(!rc_net) << net->get_name() << " rc net is not found.";
auto* rc_tree = rc_net->rct();
@@ -495,6 +524,37 @@ void TimingEngine::makeResistor(Net* net, RctNode* from_node, RctNode* to_node,
rc_tree->insertEdge(to_node, from_node, res, false);
}

/**
* @brief make virtual rc tree edge.
*
* @param rc_tree_name
* @param from_node
* @param to_node
* @param res
*/
void TimingEngine::makeVirtualRCTreeResistor(const char* rc_tree_name,
RctNode* from_node,
RctNode* to_node, double res) {
if (!_virtual_rc_trees.contains(rc_tree_name)) {
LOG_FATAL << "rc tree " << rc_tree_name << " not found";
}

auto& virtual_rc_tree = _virtual_rc_trees[rc_tree_name];

auto from_fanouts = from_node->get_fanout();

// judge whether the edge is already exist.
auto found = std::ranges::find_if(
from_fanouts, [&](auto* edge) { return &(edge->get_to()) == to_node; });

if (found != from_fanouts.end()) {
return;
}

virtual_rc_tree.insertEdge(from_node, to_node, res, true);
virtual_rc_tree.insertEdge(to_node, from_node, res, false);
}

/**
* @brief update rc info after make rc tree.
*
@@ -511,6 +571,16 @@ void TimingEngine::updateRCTreeInfo(Net* net) {
}
}

/**
* @brief update virutal rc tree rc timing.
*
* @param rc_tree_name
*/
void TimingEngine::updateVirtualRCTreeInfo(const char* rc_tree_name) {
auto& virtual_rc_tree = _virtual_rc_trees[rc_tree_name];
virtual_rc_tree.updateRcTiming();
}

/**
* @brief update all rc tree elmore delay use gpu speedup.
*
@@ -558,6 +628,71 @@ void TimingEngine::buildRcTreeAndUpdateRcTreeInfo(
updateRCTreeInfo(net);
}

/**
* @brief Get all node slew of virtual rc tree.
*
* @param rc_tree_name
* @param driver_slew
* @return std::map<std::string, double>
*/
std::map<std::string, double> TimingEngine::getVirtualRCTreeAllNodeSlew(
const char* rc_tree_name, double driver_slew) {
if (!_virtual_rc_trees.contains(rc_tree_name)) {
LOG_FATAL << "virtual RC tree " << rc_tree_name << " does not exist!";
}

auto& virtual_rc_tree = _virtual_rc_trees[rc_tree_name];

std::map<std::string, double> all_node_slews;
auto* rc_root = virtual_rc_tree.get_root();

all_node_slews[rc_root->get_name()] = driver_slew;

std::function<void(RctNode*, RctNode*)> get_snk_slew =
[&get_snk_slew, this, driver_slew, &all_node_slews](RctNode* parent_node,
RctNode* src_node) {
auto& fanout_edges = src_node->get_fanout();
for (auto* fanout_edge : fanout_edges) {
auto& snk_node = fanout_edge->get_to();
if (fanout_edge->isBreak() || &snk_node == parent_node) {
continue;
}

auto snk_slew = snk_node.slew(AnalysisMode::kMax, TransType::kRise,
NS_TO_PS(driver_slew));
all_node_slews[snk_node.get_name()] = PS_TO_NS(snk_slew);

get_snk_slew(src_node, &snk_node);
}
};

get_snk_slew(nullptr, rc_root);

return all_node_slews;
}

/**
* @brief get all node delay of virtual rc tree.
*
* @param rc_tree_name
* @return std::map<std::string, double>
*/
std::map<std::string, double> TimingEngine::getVirtualRCTreeAllNodeDelay(
const char* rc_tree_name) {
if (!_virtual_rc_trees.contains(rc_tree_name)) {
LOG_FATAL << "virtual RC tree " << rc_tree_name << " does not exist!";
}
auto& virtual_rc_tree = _virtual_rc_trees[rc_tree_name];

std::map<std::string, double> all_node_delays;

for (auto& [node_name, node] : virtual_rc_tree.get_nodes()) {
all_node_delays[node_name] = node.delay();
}

return all_node_delays;
}

/**
* @brief incremental propagation to update timing data.
*


+ 23
- 3
src/operation/iSTA/api/TimingEngine.hh View File

@@ -101,8 +101,9 @@ class TimingEngine {
return *this;
}

TimingEngine &readDefDesign(std::string def_file, std::vector<std::string>& lef_files);
TimingEngine &setDefDesignBuilder(void* db_builder);
TimingEngine &readDefDesign(std::string def_file,
std::vector<std::string> &lef_files);
TimingEngine &setDefDesignBuilder(void *db_builder);

TimingEngine &readSdc(const char *sdc_file) {
_ista->resetConstraint();
@@ -221,17 +222,33 @@ class TimingEngine {
TimingEngine &buildRCTree(const char *spef_file, DelayCalcMethod kmethod);
void initRcTree(Net *net);
void initRcTree();
RcTree &initVirtualRcTree(const char *rc_tree_name);
RcTree* getVirtualRcTree(const char *rc_tree_name) {
return _virtual_rc_trees.contains(rc_tree_name)
? &(_virtual_rc_trees[rc_tree_name])
: nullptr;
}
void resetRcTree(Net *net);
RctNode *makeOrFindRCTreeNode(Net *net, int id);
RctNode *makeOrFindRCTreeNode(DesignObject *pin_or_port);
RctNode* findRCTreeNode(Net *net, std::string& node_name);
RctNode *makeOrFindVirtualRCTreeNode(const char *rc_tree_name,
const char *node_name);
RctNode *findRCTreeNode(Net *net, std::string &node_name);
void incrCap(RctNode *node, double cap, bool is_incremental = false);
void makeResistor(Net *net, RctNode *from_node, RctNode *to_node, double res);
void makeVirtualRCTreeResistor(const char *rc_tree_name, RctNode *from_node,
RctNode *to_node, double res);
void updateRCTreeInfo(Net *net);
void updateVirtualRCTreeInfo(const char* rc_tree_name);
void updateAllRCTree();
void buildRcTreeAndUpdateRcTreeInfo(
const char *net_name, std::map<std::string, double> &loadname2wl);

std::map<std::string, double> getVirtualRCTreeAllNodeSlew(
const char* rc_tree_name, double driver_slew);
std::map<std::string, double> getVirtualRCTreeAllNodeDelay(
const char* rc_tree_name);

TimingEngine &incrUpdateTiming();

TimingEngine &updateTiming() {
@@ -395,6 +412,9 @@ class TimingEngine {
std::unique_ptr<TimingDBAdapter> _db_adapter;
StaIncremental _incr_func;

std::map<std::string, RcTree>
_virtual_rc_trees; //!< The virtual rc tree is maybe not complete net.

// Singleton timing engine.
static TimingEngine *_timing_engine;



+ 3
- 0
src/operation/iSTA/source/module/delay/ElmoreDelayCalc.hh View File

@@ -402,6 +402,9 @@ class RcTree {
RcTree() = default;
~RcTree() = default;

RcTree(RcTree&&) noexcept = default;
RcTree& operator=(RcTree&&) noexcept = default;

void updateRcTiming();
void insertSegment(const std::string&, const std::string&, double);
RctNode* insertNode(const std::string&, double = 0.0);


+ 37
- 0
src/operation/iSTA/test/DelayTest.cc View File

@@ -25,6 +25,7 @@
#include "netlist/Net.hh"
#include "netlist/Netlist.hh"
#include "sta/Sta.hh"
#include "api/TimingEngine.hh"

using ieda::Log;
using ista::DesignObject;
@@ -36,6 +37,10 @@ using ista::NetPinIterator;
using ista::RcNet;
using ista::Sta;

using namespace ista;
using ieda::Log;
using ieda::Stats;

namespace {

class DelayTest : public testing::Test {
@@ -47,4 +52,36 @@ class DelayTest : public testing::Test {
void TearDown() { Log::end(); }
};

TEST_F(DelayTest, virtual_rc_tree) {
auto* timing_engine = TimingEngine::getOrCreateTimingEngine();

auto& virutal_rc_tree = timing_engine->initVirtualRcTree("virtual_rc_tree");

auto* root_node = timing_engine->makeOrFindVirtualRCTreeNode("virtual_rc_tree", "root");
root_node->setCap(0.1);
virutal_rc_tree.set_root(root_node);

auto* inner_node = timing_engine->makeOrFindVirtualRCTreeNode("virtual_rc_tree", "inner");
inner_node->setCap(0.2);

timing_engine->makeVirtualRCTreeResistor("virtual_rc_tree", root_node, inner_node, 10);

auto* leaf_node = timing_engine->makeOrFindVirtualRCTreeNode("virtual_rc_tree", "leaf");
leaf_node->setCap(0.3);

timing_engine->makeVirtualRCTreeResistor("virtual_rc_tree", inner_node, leaf_node, 10);

timing_engine->updateVirtualRCTreeInfo("virtual_rc_tree");

auto node_delays = timing_engine->getVirtualRCTreeAllNodeDelay("virtual_rc_tree");
for (auto& [node_name, delay] : node_delays) {
std::cout << node_name << ": " << delay << std::endl;
}

auto node_slews = timing_engine->getVirtualRCTreeAllNodeSlew("virtual_rc_tree", 0.002);
for (auto& [node_name, slew] : node_slews) {
std::cout << node_name << ": " << slew << std::endl;
}
}

} // namespace

+ 1
- 1
src/operation/iSTA/test/main.cc View File

@@ -20,6 +20,6 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);

testing::GTEST_FLAG(filter) = "LibDataGPUTest.test*";
testing::GTEST_FLAG(filter) = "DelayTest.virtual_rc_tree*";
return RUN_ALL_TESTS();
}

+ 1
- 0
src/utility/CMakeLists.txt View File

@@ -20,6 +20,7 @@ add_subdirectory(time)
add_subdirectory(stdBase)
add_subdirectory(usage)
add_subdirectory(report)
add_subdirectory(notification)
option(BASE_RUN_TESTS "If ON, the tests will be run." OFF)


+ 28
- 0
src/utility/notification/CMakeLists.txt View File

@@ -0,0 +1,28 @@
cmake_minimum_required(VERSION 3.11)
set(CMAKE_CXX_STANDARD 20)

# Find required packages
find_package(PkgConfig REQUIRED)
pkg_check_modules(CURL REQUIRED libcurl)

aux_source_directory(./ SRC)

if(BUILD_STATIC_LIB)
add_library(notification STATIC ${SRC})
else()
add_library(notification SHARED ${SRC})
endif()

target_link_libraries(notification
PUBLIC
${CURL_LIBRARIES}
)

target_include_directories(notification
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${CURL_INCLUDE_DIRS}
${HOME_THIRDPARTY}/json
)

target_compile_options(notification PRIVATE ${CURL_CFLAGS_OTHER})

+ 358
- 0
src/utility/notification/NotificationUtility.cpp View File

@@ -0,0 +1,358 @@
// ***************************************************************************************
// Copyright (c) 2023-2025 Peng Cheng Laboratory
// Copyright (c) 2023-2025 Institute of Computing Technology, Chinese Academy of Sciences
// Copyright (c) 2023-2025 Beijing Institute of Open Source Chip
//
// iEDA is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
//
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
//
// See the Mulan PSL v2 for more details.
// ***************************************************************************************
#include "NotificationUtility.h"

#include <curl/curl.h>
#include <json/json.hpp>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>

using json = nlohmann::json;

namespace ieda {

// Static member definitions
std::unique_ptr<NotificationUtility> NotificationUtility::_instance = nullptr;
std::mutex NotificationUtility::_instance_mutex;

// Callback function for libcurl to write response data
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* userp) {
size_t total_size = size * nmemb;
userp->append(static_cast<char*>(contents), total_size);
return total_size;
}

NotificationUtility& NotificationUtility::getInstance() {
std::lock_guard<std::mutex> lock(_instance_mutex);
if (!_instance) {
_instance = std::unique_ptr<NotificationUtility>(new NotificationUtility());
}
return *_instance;
}

void NotificationUtility::destroyInstance() {
std::lock_guard<std::mutex> lock(_instance_mutex);
if (_instance) {
_instance->waitForPendingNotifications(5000); // Wait up to 5 seconds
_instance.reset();
}
}

NotificationUtility::~NotificationUtility() {
waitForPendingNotifications(5000);
curl_global_cleanup();
}

bool NotificationUtility::initialize(const NotificationConfig& config) {
std::lock_guard<std::mutex> lock(_config_mutex);
// Initialize libcurl globally
CURLcode curl_init_result = curl_global_init(CURL_GLOBAL_DEFAULT);
if (curl_init_result != CURLE_OK) {
std::cerr << "NotificationUtility: Failed to initialize libcurl: "
<< curl_easy_strerror(curl_init_result) << std::endl;
return false;
}
_config = config;
// Load auth token from environment if not provided
if (_config.auth_token.empty()) {
_config.auth_token = loadAuthTokenFromEnv();
}
_initialized = !_config.endpoint_url.empty();
_enabled = _initialized;
if (!_initialized) {
std::cerr << "NotificationUtility: Endpoint URL is required for initialization" << std::endl;
}
return _initialized;
}

bool NotificationUtility::initialize(const std::string& endpoint_url) {
NotificationConfig config;
// Use provided URL or try to get from environment
if (!endpoint_url.empty()) {
config.endpoint_url = endpoint_url;
} else {
const char* env_url = std::getenv("IEDA_NOTIFICATION_URL");
if (env_url) {
config.endpoint_url = env_url;
}
}
return initialize(config);
}

NotificationUtility::HttpResponse NotificationUtility::sendNotification(const NotificationPayload& payload) {
if (!isEnabled() || !isInitialized()) {
HttpResponse response;
response.error_message = "NotificationUtility not initialized or disabled";
return response;
}
std::string payload_json = payloadToJson(payload);
if (_config.async_mode) {
// Launch async notification
std::lock_guard<std::mutex> lock(_futures_mutex);
_pending_futures.emplace_back(
std::async(std::launch::async, &NotificationUtility::asyncNotificationWorker, this, payload_json)
);
// Clean up completed futures
_pending_futures.erase(
std::remove_if(_pending_futures.begin(), _pending_futures.end(),
[](const std::future<void>& f) {
return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
}),
_pending_futures.end()
);
HttpResponse response;
response.success = true;
response.response_body = "Async notification queued";
return response;
} else {
return performHttpRequest(payload_json);
}
}

NotificationUtility::HttpResponse NotificationUtility::sendIterationNotification(
const std::string& algorithm_name,
int iteration,
int total_iterations,
const std::string& status,
const std::map<std::string, std::string>& metrics) {
NotificationPayload payload;
payload.algorithm_name = algorithm_name;
payload.stage = "iteration";
payload.iteration_number = iteration;
payload.total_iterations = total_iterations;
payload.status = status;
payload.metrics = metrics;
payload.timestamp = getCurrentTimestamp();
return sendNotification(payload);
}

bool NotificationUtility::isInitialized() const {
std::lock_guard<std::mutex> lock(_config_mutex);
return _initialized;
}

void NotificationUtility::updateConfig(const NotificationConfig& config) {
std::lock_guard<std::mutex> lock(_config_mutex);
_config = config;
}

const NotificationUtility::NotificationConfig& NotificationUtility::getConfig() const {
std::lock_guard<std::mutex> lock(_config_mutex);
return _config;
}

void NotificationUtility::setEnabled(bool enabled) {
std::lock_guard<std::mutex> lock(_config_mutex);
_enabled = enabled;
}

bool NotificationUtility::isEnabled() const {
std::lock_guard<std::mutex> lock(_config_mutex);
return _enabled;
}

bool NotificationUtility::waitForPendingNotifications(int timeout_ms) {
std::lock_guard<std::mutex> lock(_futures_mutex);
auto start_time = std::chrono::steady_clock::now();
for (auto& future : _pending_futures) {
if (timeout_ms > 0) {
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start_time).count();
if (elapsed >= timeout_ms) {
return false; // Timeout reached
}
auto remaining_time = std::chrono::milliseconds(timeout_ms - elapsed);
if (future.wait_for(remaining_time) != std::future_status::ready) {
return false; // Timeout on this future
}
} else {
future.wait(); // Wait indefinitely
}
}
_pending_futures.clear();
return true;
}

NotificationUtility::HttpResponse NotificationUtility::performHttpRequest(const std::string& payload_json) {
HttpResponse response;
CURL* curl = curl_easy_init();
if (!curl) {
response.error_message = "Failed to initialize CURL";
return response;
}
std::string response_body;
struct curl_slist* headers = nullptr;
try {
// Set URL
curl_easy_setopt(curl, CURLOPT_URL, _config.endpoint_url.c_str());
// Set POST method
curl_easy_setopt(curl, CURLOPT_POST, 1L);
// Set payload
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload_json.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, payload_json.length());
// Set headers
std::string content_type_header = "Content-Type: " + _config.content_type;
headers = curl_slist_append(headers, content_type_header.c_str());
if (!_config.auth_token.empty()) {
std::string auth_header = "Authorization: Bearer " + _config.auth_token;
headers = curl_slist_append(headers, auth_header.c_str());
}
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
// Set response callback
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_body);
// Set timeout
curl_easy_setopt(curl, CURLOPT_TIMEOUT, _config.timeout_seconds);
// SSL verification
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, _config.enable_ssl_verification ? 1L : 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, _config.enable_ssl_verification ? 2L : 0L);
// Perform request with retries
CURLcode res = CURLE_FAILED_INIT;
for (int retry = 0; retry <= _config.max_retries; ++retry) {
res = curl_easy_perform(curl);
if (res == CURLE_OK) {
break;
}
if (retry < _config.max_retries) {
std::this_thread::sleep_for(std::chrono::milliseconds(1000 * (retry + 1))); // Exponential backoff
}
}
if (res == CURLE_OK) {
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response.response_code);
response.response_body = response_body;
response.success = (response.response_code >= 200 && response.response_code < 300);
if (!response.success) {
response.error_message = "HTTP error: " + std::to_string(response.response_code);
}
} else {
response.error_message = "CURL error: " + std::string(curl_easy_strerror(res));
}
} catch (const std::exception& e) {
response.error_message = "Exception during HTTP request: " + std::string(e.what());
}
// Cleanup
if (headers) {
curl_slist_free_all(headers);
}
curl_easy_cleanup(curl);
return response;
}

std::string NotificationUtility::payloadToJson(const NotificationPayload& payload) {
json j;

j["algorithm_name"] = payload.algorithm_name;
j["stage"] = payload.stage;
j["iteration_number"] = payload.iteration_number;
j["total_iterations"] = payload.total_iterations;
j["status"] = payload.status;
j["timestamp"] = payload.timestamp;

// Add metrics
if (!payload.metrics.empty()) {
j["metrics"] = payload.metrics;
}

// Add metadata
if (!payload.metadata.empty()) {
j["metadata"] = payload.metadata;
}

// Add progress percentage
if (payload.total_iterations > 0) {
double progress = static_cast<double>(payload.iteration_number) / payload.total_iterations * 100.0;
j["progress_percentage"] = progress;
}

return j.dump();
}

std::string NotificationUtility::getCurrentTimestamp() {
auto now = std::chrono::system_clock::now();
auto time_t = std::chrono::system_clock::to_time_t(now);
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch()) % 1000;

std::stringstream ss;
ss << std::put_time(std::gmtime(&time_t), "%Y-%m-%dT%H:%M:%S");
ss << '.' << std::setfill('0') << std::setw(3) << ms.count() << 'Z';

return ss.str();
}

std::string NotificationUtility::loadAuthTokenFromEnv() {
const char* token = std::getenv("ID_SECRET");
return token ? std::string(token) : std::string();
}

void NotificationUtility::asyncNotificationWorker(const std::string& payload_json) {
try {
HttpResponse response = performHttpRequest(payload_json);

// Log result (optional - could be made configurable)
if (!response.success) {
std::cerr << "NotificationUtility: Async notification failed: "
<< response.error_message << std::endl;
}
} catch (const std::exception& e) {
std::cerr << "NotificationUtility: Exception in async worker: "
<< e.what() << std::endl;
}
}

} // namespace ieda

+ 226
- 0
src/utility/notification/NotificationUtility.h View File

@@ -0,0 +1,226 @@
// ***************************************************************************************
// Copyright (c) 2023-2025 Peng Cheng Laboratory
// Copyright (c) 2023-2025 Institute of Computing Technology, Chinese Academy of Sciences
// Copyright (c) 2023-2025 Beijing Institute of Open Source Chip
//
// iEDA is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
//
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
//
// See the Mulan PSL v2 for more details.
// ***************************************************************************************
#pragma once

#include <string>
#include <map>
#include <memory>
#include <mutex>
#include <future>
#include <functional>

namespace ieda {

/**
* @brief Universal notification utility for sending HTTP requests to external systems
*
* This utility provides a thread-safe way to send HTTP POST notifications to configurable
* endpoints when algorithms complete iterations. It supports custom payload data, authentication,
* and both synchronous and asynchronous operation modes.
*/
class NotificationUtility {
public:
/**
* @brief Response structure for HTTP requests
*/
struct HttpResponse {
long response_code;
std::string response_body;
std::string error_message;
bool success;
HttpResponse() : response_code(0), success(false) {}
};

/**
* @brief Configuration structure for notification settings
*/
struct NotificationConfig {
std::string endpoint_url; // Target URL for notifications
std::string auth_token; // Authentication token (from ID_SECRET env var)
std::string content_type; // Content-Type header (default: application/json)
int timeout_seconds; // Request timeout in seconds
int max_retries; // Maximum number of retry attempts
bool enable_ssl_verification; // Enable SSL certificate verification
bool async_mode; // Send notifications asynchronously
NotificationConfig()
: content_type("application/json")
, timeout_seconds(30)
, max_retries(3)
, enable_ssl_verification(true)
, async_mode(true) {}
};

/**
* @brief Notification payload data structure
*/
struct NotificationPayload {
std::string algorithm_name; // Name of the algorithm (e.g., "DetailedRouter")
std::string stage; // Current stage/phase
int iteration_number; // Current iteration number
int total_iterations; // Total number of iterations
std::string status; // Status: "running", "completed", "failed"
std::map<std::string, std::string> metrics; // Custom metrics (e.g., violations, timing)
std::map<std::string, std::string> metadata; // Additional metadata
std::string timestamp; // ISO 8601 timestamp
NotificationPayload() : iteration_number(0), total_iterations(0), status("running") {}
};

public:
/**
* @brief Get singleton instance of NotificationUtility
* @return Reference to the singleton instance
*/
static NotificationUtility& getInstance();

/**
* @brief Destroy singleton instance
*/
static void destroyInstance();

/**
* @brief Initialize the notification utility with configuration
* @param config Notification configuration
* @return true if initialization successful, false otherwise
*/
bool initialize(const NotificationConfig& config);

/**
* @brief Initialize with environment variables and default settings
* @param endpoint_url Target URL for notifications
* @return true if initialization successful, false otherwise
*/
bool initialize(const std::string& endpoint_url = "");

/**
* @brief Send notification with custom payload
* @param payload Notification payload data
* @return HttpResponse containing result (for sync mode) or empty response (for async mode)
*/
HttpResponse sendNotification(const NotificationPayload& payload);

/**
* @brief Send notification for algorithm iteration
* @param algorithm_name Name of the algorithm
* @param iteration Current iteration number
* @param total_iterations Total number of iterations
* @param status Current status
* @param metrics Optional metrics map
* @return HttpResponse containing result
*/
HttpResponse sendIterationNotification(
const std::string& algorithm_name,
int iteration,
int total_iterations,
const std::string& status = "running",
const std::map<std::string, std::string>& metrics = {}
);

/**
* @brief Check if notification utility is properly initialized
* @return true if initialized, false otherwise
*/
bool isInitialized() const;

/**
* @brief Update configuration
* @param config New configuration
*/
void updateConfig(const NotificationConfig& config);

/**
* @brief Get current configuration
* @return Current configuration
*/
const NotificationConfig& getConfig() const;

/**
* @brief Enable or disable notifications
* @param enabled true to enable, false to disable
*/
void setEnabled(bool enabled);

/**
* @brief Check if notifications are enabled
* @return true if enabled, false otherwise
*/
bool isEnabled() const;

/**
* @brief Wait for all pending async notifications to complete
* @param timeout_ms Maximum time to wait in milliseconds (0 = wait indefinitely)
* @return true if all notifications completed, false if timeout
*/
bool waitForPendingNotifications(int timeout_ms = 0);

private:
NotificationUtility() = default;
~NotificationUtility();

// Disable copy constructor and assignment operator
NotificationUtility(const NotificationUtility&) = delete;
NotificationUtility& operator=(const NotificationUtility&) = delete;

/**
* @brief Perform the actual HTTP POST request
* @param payload_json JSON payload string
* @return HttpResponse containing result
*/
HttpResponse performHttpRequest(const std::string& payload_json);

/**
* @brief Convert payload to JSON string
* @param payload Notification payload
* @return JSON string representation
*/
std::string payloadToJson(const NotificationPayload& payload);

/**
* @brief Get current timestamp in ISO 8601 format
* @return Timestamp string
*/
std::string getCurrentTimestamp();

/**
* @brief Load authentication token from environment variable
* @return Authentication token or empty string if not found
*/
std::string loadAuthTokenFromEnv();

/**
* @brief Async notification worker function
* @param payload_json JSON payload to send
*/
void asyncNotificationWorker(const std::string& payload_json);

private:
static std::unique_ptr<NotificationUtility> _instance;
static std::mutex _instance_mutex;

mutable std::mutex _config_mutex;
NotificationConfig _config;
bool _initialized;
bool _enabled;
// For async operations
std::vector<std::future<void>> _pending_futures;
mutable std::mutex _futures_mutex;
};

} // namespace ieda

+ 273
- 0
src/utility/notification/README.md View File

@@ -0,0 +1,273 @@
# NotificationUtility - Universal HTTP Notification System

The NotificationUtility provides a thread-safe way to send HTTP POST notifications to external systems when algorithms complete iterations. It's designed to notify external monitoring systems, dashboards, or webhooks about the progress of long-running algorithms like the DetailedRouter.

## Features

- **Thread-safe**: Can be safely used in multi-threaded environments
- **Asynchronous**: Supports both synchronous and asynchronous notification modes
- **Configurable**: Flexible configuration via environment variables or programmatic setup
- **Retry logic**: Built-in retry mechanism with exponential backoff
- **Authentication**: Support for Bearer token authentication
- **JSON payload**: Structured JSON payload with algorithm metrics
- **Error handling**: Comprehensive error handling and logging

## Configuration

### Environment Variables

- `IEDA_NOTIFICATION_URL`: Target endpoint URL for notifications
- `ID_SECRET`: Authentication token (used as Bearer token)

### Programmatic Configuration

```cpp
#include "NotificationUtility.h"

// Get singleton instance
auto& notifier = ieda::NotificationUtility::getInstance();

// Configure with custom settings
ieda::NotificationUtility::NotificationConfig config;
config.endpoint_url = "https://your-webhook-endpoint.com/notifications";
config.auth_token = "your-bearer-token";
config.timeout_seconds = 30;
config.max_retries = 3;
config.async_mode = true;
config.enable_ssl_verification = true;

notifier.initialize(config);
```

## Usage Examples

### Basic Usage

```cpp
#include "NotificationUtility.h"

// Initialize with environment variables
auto& notifier = ieda::NotificationUtility::getInstance();
notifier.initialize(); // Uses IEDA_NOTIFICATION_URL and ID_SECRET env vars

// Send iteration notification
auto response = notifier.sendIterationNotification(
"DetailedRouter", // algorithm name
5, // current iteration
10, // total iterations
"running", // status
{ // metrics map
{"violations", "42"},
{"wire_length", "1234.56"},
{"elapsed_time", "2.5s"}
}
);

if (response.success) {
std::cout << "Notification sent successfully" << std::endl;
} else {
std::cerr << "Failed to send notification: " << response.error_message << std::endl;
}
```

### Custom Payload

```cpp
#include "NotificationUtility.h"

auto& notifier = ieda::NotificationUtility::getInstance();

// Create custom payload
ieda::NotificationUtility::NotificationPayload payload;
payload.algorithm_name = "MyAlgorithm";
payload.stage = "optimization";
payload.iteration_number = 3;
payload.total_iterations = 20;
payload.status = "running";
payload.metrics["cost"] = "123.45";
payload.metrics["temperature"] = "0.85";
payload.metadata["version"] = "1.0.0";
payload.metadata["config"] = "high_performance";

auto response = notifier.sendNotification(payload);
```

### Integration in Algorithm Code

```cpp
void MyAlgorithm::runIterations() {
auto& notifier = ieda::NotificationUtility::getInstance();
// Initialize once
if (!notifier.isInitialized()) {
notifier.initialize();
}
for (int iter = 1; iter <= total_iterations; ++iter) {
// Run algorithm iteration
performIteration(iter);
// Collect metrics
std::map<std::string, std::string> metrics;
metrics["violations"] = std::to_string(getViolationCount());
metrics["cost"] = std::to_string(getCurrentCost());
metrics["elapsed_time"] = getElapsedTime();
// Send notification
if (notifier.isEnabled()) {
std::string status = (iter == total_iterations) ? "completed" : "running";
notifier.sendIterationNotification(
"MyAlgorithm", iter, total_iterations, status, metrics
);
}
}
// Wait for pending async notifications before exit
notifier.waitForPendingNotifications(5000); // 5 second timeout
}
```

## JSON Payload Format

The notification utility sends JSON payloads with the following structure:

```json
{
"algorithm_name": "DetailedRouter",
"stage": "iteration",
"iteration_number": 5,
"total_iterations": 10,
"status": "running",
"progress_percentage": 50.0,
"timestamp": "2025-06-28T10:30:45.123Z",
"metrics": {
"route_violations": "42",
"total_wire_length": "1234.56",
"total_via_num": "789",
"elapsed_time": "2.5s"
},
"metadata": {
"version": "1.0.0",
"config": "default"
}
}
```

## HTTP Headers

The utility automatically sets the following headers:

- `Content-Type`: `application/json` (configurable)
- `Authorization`: `Bearer <token>` (if auth_token is provided)

## Error Handling

The utility provides comprehensive error handling:

- Network timeouts
- HTTP error responses (4xx, 5xx)
- SSL certificate verification failures
- JSON serialization errors
- CURL library errors

All errors are logged and returned in the `HttpResponse` structure.

## Thread Safety

The NotificationUtility is designed to be thread-safe:

- Singleton pattern with mutex protection
- Thread-safe configuration updates
- Async notifications use separate threads
- Proper cleanup of completed futures

## Performance Considerations

- **Async Mode**: Use async mode (`config.async_mode = true`) for better performance in time-critical algorithms
- **Timeout**: Set appropriate timeout values to avoid blocking
- **Retries**: Configure retry count based on network reliability
- **SSL**: Disable SSL verification only in development environments

## Dependencies

- **libcurl**: HTTP client library
- **nlohmann/json**: JSON serialization (included in third_party)
- **C++20**: Modern C++ features

## Building

The notification utility is automatically built when building the iEDA project. Ensure libcurl is installed:

```bash
# Ubuntu/Debian
sudo apt-get install libcurl4-openssl-dev

# CentOS/RHEL
sudo yum install libcurl-devel

# macOS
brew install curl
```

## Webhook Endpoint Requirements

Your webhook endpoint should:

1. Accept HTTP POST requests
2. Handle `application/json` content type
3. Return HTTP 2xx status codes for success
4. Optionally validate the Bearer token
5. Be able to handle the JSON payload structure shown above

Example webhook endpoint (Python Flask):

```python
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/notifications', methods=['POST'])
def handle_notification():
# Validate authorization header
auth_header = request.headers.get('Authorization')
if not auth_header or not auth_header.startswith('Bearer '):
return jsonify({'error': 'Unauthorized'}), 401
# Process notification
data = request.get_json()
print(f"Received notification from {data['algorithm_name']}")
print(f"Iteration {data['iteration_number']}/{data['total_iterations']}")
print(f"Status: {data['status']}")
print(f"Metrics: {data['metrics']}")
return jsonify({'status': 'received'}), 200

if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
```

## Troubleshooting

### Common Issues

1. **"Failed to initialize libcurl"**: Ensure libcurl is properly installed
2. **"Endpoint URL is required"**: Set `IEDA_NOTIFICATION_URL` environment variable
3. **SSL verification errors**: Set `config.enable_ssl_verification = false` for self-signed certificates
4. **Timeout errors**: Increase `config.timeout_seconds` value
5. **Authentication failures**: Verify `ID_SECRET` environment variable is set correctly

### Debug Mode

Enable debug logging by setting the log level appropriately in your application.

### Testing

Test your webhook endpoint using curl:

```bash
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-token" \
-d '{"algorithm_name":"test","iteration_number":1,"total_iterations":1,"status":"completed"}' \
https://your-webhook-endpoint.com/notifications
```

Loading…
Cancel
Save
Baidu
map