diff --git a/src/interface/python/py_ircx/CMakeLists.txt b/src/interface/python/py_ircx/CMakeLists.txt index d2b51924d..afa21cb78 100644 --- a/src/interface/python/py_ircx/CMakeLists.txt +++ b/src/interface/python/py_ircx/CMakeLists.txt @@ -5,7 +5,6 @@ add_library(py_ircx ${PY_IRCX_SRC}) target_link_libraries(py_ircx PUBLIC ircx - OpenMP::OpenMP_CXX pybind11::pybind11 ) target_include_directories(py_ircx diff --git a/src/interface/python/py_ircx/py_ircx.cpp b/src/interface/python/py_ircx/py_ircx.cpp index 4364438aa..728accf09 100644 --- a/src/interface/python/py_ircx/py_ircx.cpp +++ b/src/interface/python/py_ircx/py_ircx.cpp @@ -19,7 +19,6 @@ #include #include -#include "ParasiticXEngine.hpp" #include "RCX.hpp" namespace python_interface { @@ -67,19 +66,6 @@ bool read_rcx_corner(const std::string& corner_name, return rcx().readCorner(corner_name, itf_file.c_str(), captab_file.c_str()); } -bool read_rcx_itf(const std::vector& itf_files) -{ - if (itf_files.empty()) { - throw std::invalid_argument("itf_files is empty."); - } - - for (const auto& itf_file : itf_files) { - require_file_exists(itf_file, "itf_file"); - } - - return rcx().readItf(itf_files); -} - bool read_rcx_mapping(const std::string& mapping_file) { require_file_exists(mapping_file, "mapping_file"); @@ -114,15 +100,12 @@ bool extract_rcx_parasitics() bool run_rcx(const std::string& config) { require_file_exists(config, "config"); - - auto* parasitic_x_engine = ircx::ParasiticXEngine::get_or_create_parasitic_x_engine(); - return parasitic_x_engine != nullptr && parasitic_x_engine->run_rcx(config); + return rcx().runFromConfig(config); } bool report_rcx(const std::string& output_dir) { - auto* parasitic_x_engine = ircx::ParasiticXEngine::get_or_create_parasitic_x_engine(); - return parasitic_x_engine != nullptr && parasitic_x_engine->report_rcx(output_dir); + return rcx().reportSpef(output_dir); } } // namespace python_interface diff --git a/src/interface/python/py_ircx/py_ircx.h b/src/interface/python/py_ircx/py_ircx.h index 3742cefcf..b0c2ae2d6 100644 --- a/src/interface/python/py_ircx/py_ircx.h +++ b/src/interface/python/py_ircx/py_ircx.h @@ -17,7 +17,6 @@ #pragma once #include -#include namespace python_interface { @@ -25,7 +24,6 @@ bool init_rcx(unsigned thread_number); bool read_rcx_corner(const std::string& corner_name, const std::string& itf_file, const std::string& captab_file); -bool read_rcx_itf(const std::vector& itf_files); bool read_rcx_mapping(const std::string& mapping_file); bool adapt_rcx_db(); diff --git a/src/interface/python/py_ircx/py_register_ircx.h b/src/interface/python/py_ircx/py_register_ircx.h index a34be75e1..60c8af60f 100644 --- a/src/interface/python/py_ircx/py_register_ircx.h +++ b/src/interface/python/py_ircx/py_register_ircx.h @@ -28,7 +28,6 @@ void register_ircx(py::module& m) m.def("init_rcx", init_rcx, py::arg("thread_number") = 64); m.def("read_rcx_corner", read_rcx_corner, py::arg("corner_name"), py::arg("itf_file"), py::arg("captab_file")); - m.def("read_rcx_itf", read_rcx_itf, py::arg("itf_files")); m.def("read_rcx_mapping", read_rcx_mapping, py::arg("mapping_file")); m.def("adapt_rcx_db", adapt_rcx_db); diff --git a/src/operation/iRCX/api/CMakeLists.txt b/src/operation/iRCX/api/CMakeLists.txt index 7b7716827..f4e11551f 100644 --- a/src/operation/iRCX/api/CMakeLists.txt +++ b/src/operation/iRCX/api/CMakeLists.txt @@ -12,10 +12,8 @@ target_link_libraries(ircx_engine PRIVATE def_service idb - ircx_config lef_service log - OpenMP::OpenMP_CXX ) target_include_directories(ircx_engine diff --git a/src/operation/iRCX/api/ParasiticXEngine.cpp b/src/operation/iRCX/api/ParasiticXEngine.cpp index 9154244da..08223087f 100644 --- a/src/operation/iRCX/api/ParasiticXEngine.cpp +++ b/src/operation/iRCX/api/ParasiticXEngine.cpp @@ -14,21 +14,13 @@ // // See the Mulan PSL v2 for more details. // *************************************************************************************** -#include #include -#include - -#include #include "ParasiticXEngine.hpp" #include "RCX.hpp" -#include "RCXConfig.hh" -#include "log/Log.hh" namespace ircx { -namespace fs = std::filesystem; - ParasiticXEngine* ParasiticXEngine::_instance = nullptr; ParasiticXEngine* ParasiticXEngine::get_or_create_parasitic_x_engine() @@ -64,65 +56,12 @@ void ParasiticXEngine::set_rcx(RCX* rcx) bool ParasiticXEngine::run_rcx(const std::string& config) { - if (_rcx == nullptr) { - return false; - } - - RCXConfig rcx_config; - if (!rcx_config.loadFromFile(config)) { - return false; - } - - const auto& corners = rcx_config.get_corners(); - _rcx->set_num_threads(rcx_config.get_thread_num()); - omp_set_num_threads(_rcx->num_threads()); - - unsigned result = 1; - for (const auto& corner : corners) { - result &= _rcx->readCorner(corner.name, corner.itf_file.c_str(), corner.captab_file.c_str()); - } - result &= _rcx->readMapping(rcx_config.get_mapping_file().c_str()); - if (!result) { - return false; - } - - result &= _rcx->adaptDB(); - result &= _rcx->buildTopology(); - result &= _rcx->buildEnvironment(); - result &= _rcx->buildProcessVariation(); - result &= _rcx->extractParasitics(); - if (!result) { - return false; - } - - const std::string output_dir = rcx_config.get_output_dir().empty() ? "." : rcx_config.get_output_dir(); - std::error_code ec; - fs::create_directories(output_dir, ec); - if (ec) { - LOG_ERROR << "Failed to create RCX output directory " << output_dir << ": " << ec.message(); - return false; - } - - result &= _rcx->reportSpef(output_dir); - - return result; + return _rcx != nullptr && _rcx->runFromConfig(config); } bool ParasiticXEngine::report_rcx(const std::string& output_dir) { - if (_rcx == nullptr) { - return false; - } - - const std::string resolved_output_dir = output_dir.empty() ? "." : output_dir; - std::error_code ec; - fs::create_directories(resolved_output_dir, ec); - if (ec) { - LOG_ERROR << "Failed to create RCX output directory " << resolved_output_dir << ": " << ec.message(); - return false; - } - - return _rcx->reportSpef(resolved_output_dir); + return _rcx != nullptr && _rcx->reportSpef(output_dir.empty() ? "." : output_dir); } } // namespace ircx diff --git a/src/operation/iRCX/api/ParasiticXIDBAdapter.cpp b/src/operation/iRCX/api/ParasiticXIDBAdapter.cpp index 2b9a7aafc..21ff0ef35 100644 --- a/src/operation/iRCX/api/ParasiticXIDBAdapter.cpp +++ b/src/operation/iRCX/api/ParasiticXIDBAdapter.cpp @@ -90,6 +90,10 @@ bool ParasiticXIDBAdapter::adapt(LayoutData& layout_data, return false; } + layout_data.clear(); + spef_context.clear(); + layer_table.clearDesignLayers(); + IdbLayers* idb_layers = idb_layout->get_layers(); adaptLayerTable(idb_layers); adaptRoutingLayer(idb_layers); diff --git a/src/operation/iRCX/source/module/database/LayerTable.hpp b/src/operation/iRCX/source/module/database/LayerTable.hpp index 9c2f10e24..6f0fe73dd 100644 --- a/src/operation/iRCX/source/module/database/LayerTable.hpp +++ b/src/operation/iRCX/source/module/database/LayerTable.hpp @@ -35,6 +35,26 @@ namespace ircx { class LayerTable { public: // Registration + void clear() { + clearDesignLayers(); + clearProcessLayers(); + clearMappings(); + } + + void clearDesignLayers() { + design_id_to_name_.clear(); + design_name_to_id_.clear(); + } + + void clearProcessLayers() { + process_id_to_name_.clear(); + process_name_to_id_.clear(); + } + + void clearMappings() { + design_to_process_name_.clear(); + process_to_design_name_.clear(); + } void registerDesignLayer(Size id, Str name) { design_id_to_name_[id] = name; diff --git a/src/operation/iRCX/source/module/database/LayoutData.hpp b/src/operation/iRCX/source/module/database/LayoutData.hpp index 12a65bbf6..45e15e025 100644 --- a/src/operation/iRCX/source/module/database/LayoutData.hpp +++ b/src/operation/iRCX/source/module/database/LayoutData.hpp @@ -98,6 +98,15 @@ struct Net { }; struct LayoutData { + void clear() { + design_name.clear(); + die_shape = {}; + micron_to_dbu = 1; + routing_layers.clear(); + net_vec.clear(); + special_net = {}; + } + // Design metadata Str design_name; GtlRectI die_shape; diff --git a/src/operation/iRCX/source/module/database/SpefContext.hpp b/src/operation/iRCX/source/module/database/SpefContext.hpp index 7272830c0..9518e18c3 100644 --- a/src/operation/iRCX/source/module/database/SpefContext.hpp +++ b/src/operation/iRCX/source/module/database/SpefContext.hpp @@ -30,6 +30,14 @@ namespace ircx { // by the layout adapter. // struct SpefContext { + void clear() { + net_names.clear(); + port_names.clear(); + port_io.clear(); + instance_names.clear(); + instance_to_cell.clear(); + } + // Ordered lists for SPEF *NAME_MAP output. // Index 0 = first entry; SPEF IDs start at *1. std::vector net_names; diff --git a/src/operation/iRCX/source/module/environment/Environment.cpp b/src/operation/iRCX/source/module/environment/Environment.cpp index 446058697..42aa96666 100644 --- a/src/operation/iRCX/source/module/environment/Environment.cpp +++ b/src/operation/iRCX/source/module/environment/Environment.cpp @@ -15,7 +15,6 @@ // See the Mulan PSL v2 for more details. // *************************************************************************************** #include -#include #include #include @@ -41,6 +40,8 @@ void Environment::buildTracks() Dbu bucket_dlt = static_cast(bucket_size_um_ * layout_data_->micron_to_dbu); + layer_to_track_.clear(); + // init for (const auto& [lid, layer] : routing_layers) { bool is_horz = layer.is_prefer_horz(); @@ -117,6 +118,9 @@ void Environment::buildPixels() Dbu die_x1 = geom::MaxX(rect); Dbu die_y1 = geom::MaxY(rect); + layer_to_pixel_prefer_dir_.clear(); + layer_to_pixel_nonprefer_dir_.clear(); + // init for (const auto& [lid, layer] : routing_layers) { const RoutingLayer::TrackInfo& ti = layer.track_info(); @@ -128,6 +132,11 @@ void Environment::buildPixels() Dbu ny = ti.ny; Dbu dx = ti.dx; Dbu dy = ti.dy; + if (layer.is_prefer_horz()) { + dx = layer.layer_width(); + } else { + dy = layer.layer_width(); + } while(x0 > die_x0) { x0 -= dx; @@ -150,31 +159,33 @@ void Environment::buildPixels() pixel.set_dy(dy); pixel.initPixel(); - layer_to_pixel_[lid] = std::move(pixel); + layer_to_pixel_prefer_dir_[lid] = pixel; + layer_to_pixel_nonprefer_dir_[lid] = std::move(pixel); } - // build: regular edges - for (const TopoEdge& edge : topo_pool_->edge_pool()) { - if (edge.is_via()) continue; + auto add_pixel_edge = [&](const TopoEdge& edge) { + if (edge.is_via()) { + return; + } Size lid = edge.layer_id(); bool layer_is_horz = routing_layers.at(lid).is_prefer_horz(); if (edge.is_horz() == layer_is_horz) { - layer_to_pixel_.at(lid).addEdge(edge); + layer_to_pixel_prefer_dir_.at(lid).addEdge(edge); + } else { + layer_to_pixel_nonprefer_dir_.at(lid).addEdge(edge); } + }; + + // build: regular edges + for (const TopoEdge& edge : topo_pool_->edge_pool()) { + add_pixel_edge(edge); } // build: special-net edges (power/ground context) for (const TopoEdge& edge : topo_pool_->special_edge_pool()) { - if (edge.is_via()) continue; - - Size lid = edge.layer_id(); - bool layer_is_horz = routing_layers.at(lid).is_prefer_horz(); - - if (edge.is_horz() == layer_is_horz) { - layer_to_pixel_.at(lid).addEdge(edge); - } + add_pixel_edge(edge); } } @@ -182,6 +193,8 @@ void Environment::buildSearchTrackNumMap() { const std::map& routing_layers = layout_data_->routing_layers; + layer_to_search_track_num_.clear(); + for (const auto& [lid, layer] : routing_layers) { // Dbu window_size = static_cast(window_size_um_ * layout_data_->micron_to_dbu); // layer_to_search_track_num_[lid] = window_size / layer_to_track_[lid].track_dlt(); @@ -196,18 +209,13 @@ void Environment::buildNetEnvPools() buildSearchTrackNumMap(); Size net_num = layout_data_->regular_net_count(); + net_env_pools_.clear(); net_env_pools_.resize(net_num); const std::map& routing_layers = layout_data_->routing_layers; const Size min_lid = routing_layers.empty() ? 0 : routing_layers.begin()->first; const Size max_lid = routing_layers.empty() ? 0 : routing_layers.rbegin()->first; - Track::OverlapWidenFunc widen_func = - [](const ircx::OverlapWidenContext& ctx) -> Dbu { - // return ctx.edge->half_width(); - return 0; - }; - auto widen_me = [](const LineSegmentI& seg, Dbu ext) { LineSegmentI out = seg; out.a0 -= ext; @@ -272,13 +280,14 @@ void Environment::buildNetEnvPools() continue; } - // 与 full_seg 平行的层直接跳过;只保留正交层 - if (it_layer->second.is_prefer_horz() == full_seg.is_horz) { - continue; - } + // Cross-over only queries the conductor set orthogonal to full_seg. + const auto& pixel_map = + (it_layer->second.is_prefer_horz() != full_seg.is_horz) + ? layer_to_pixel_prefer_dir_ + : layer_to_pixel_nonprefer_dir_; - auto it_pixel = layer_to_pixel_.find(cand_lid); - if (it_pixel == layer_to_pixel_.end()) { + auto it_pixel = pixel_map.find(cand_lid); + if (it_pixel == pixel_map.end()) { continue; } @@ -287,7 +296,7 @@ void Environment::buildNetEnvPools() continue; } - // delta 从小到大,因此输入顺序天然是“近到远” + // Smaller layer deltas have higher priority in PixelOverlapMerge. PixelOverlapMerge::LayerPixelOverlaps in; in.layer = cand_lid; in.segs = std::move(segs); @@ -309,26 +318,22 @@ void Environment::buildNetEnvPools() } const Size lid = edge.layer_id(); - const LineSegmentI s = widen_me(edge.line_segment(), 0); + const LineSegmentI query_seg = widen_me(edge.line_segment(), 0); - // 同层环境 std::vector track_ov_up = - layer_to_track_[lid].get_overlap(s, layer_to_search_track_num_[lid], nullptr); + layer_to_track_[lid].get_overlap(query_seg, layer_to_search_track_num_[lid], nullptr); std::vector track_ov_dn = - layer_to_track_[lid].get_overlap(s, -layer_to_search_track_num_[lid], nullptr); + layer_to_track_[lid].get_overlap(query_seg, -layer_to_search_track_num_[lid], nullptr); std::vector out; - track_merger.compute(s.a0, s.a1, track_ov_dn, track_ov_up, out); + track_merger.compute(query_seg.a0, query_seg.a1, track_ov_dn, track_ov_up, out); - // 对整条 s 一次性收集上下最多 3 层内的 cross-over 候选 - const auto dn_inputs = collect_cross_side(s, lid, /*search_up=*/false); - const auto up_inputs = collect_cross_side(s, lid, /*search_up=*/true); + const auto dn_inputs = collect_cross_side(query_seg, lid, /*search_up=*/false); + const auto up_inputs = collect_cross_side(query_seg, lid, /*search_up=*/true); - // 对整条 s 一次性 merge cross-over std::vector cross_full; - pixel_merger.compute(s.a0, s.a1, dn_inputs, up_inputs, cross_full); + pixel_merger.compute(query_seg.a0, query_seg.a1, dn_inputs, up_inputs, cross_full); - // 每个 interval 只做裁剪,不再重复 merge for (EnvInterval& interval : out) { interval.cross_segs = clip_cross_segs(cross_full, interval.a0, interval.a1); } diff --git a/src/operation/iRCX/source/module/environment/Environment.hpp b/src/operation/iRCX/source/module/environment/Environment.hpp index 0e2d7fb2f..0abeccd23 100644 --- a/src/operation/iRCX/source/module/environment/Environment.hpp +++ b/src/operation/iRCX/source/module/environment/Environment.hpp @@ -69,7 +69,8 @@ class Environment final { Size cross_layer_{3}; - std::map layer_to_pixel_; + std::map layer_to_pixel_prefer_dir_; + std::map layer_to_pixel_nonprefer_dir_; std::map layer_to_track_; // preferred routing direction only std::map layer_to_search_track_num_; diff --git a/src/operation/iRCX/source/module/environment/Pixel.hpp b/src/operation/iRCX/source/module/environment/Pixel.hpp index 5925402b1..bcce2db9e 100644 --- a/src/operation/iRCX/source/module/environment/Pixel.hpp +++ b/src/operation/iRCX/source/module/environment/Pixel.hpp @@ -152,7 +152,12 @@ class Pixel std::swap(a0, a1); } } - + + static Dbu midpoint(Dbu coord0, Dbu coord1) + { + return coord0 + (coord1 - coord0) / 2; + } + template = a1) return ret; const Dbu a0_idx = coord_to_idx(a0); - const Dbu a1_idx = coord_to_idx(a1); + const Dbu a1_idx = coord_to_idx(a1) + 1; if (a0_idx > a1_idx) return ret; auto clamp_sequence_bounds = [&](Dbu coord_lo, Dbu coord_hi) { @@ -187,13 +192,15 @@ class Pixel bool current_type = cell_or_empty(a0_idx); Dbu run_start = a0_idx; - auto push_sequence = [&](Dbu start_idx, Dbu end_idx, bool has_conductor) { - if (!has_conductor) { + auto push_sequence = [&](Dbu start_idx, Dbu end_idx_exclusive) { + if (end_idx_exclusive <= start_idx) { return; } - const Dbu lo = idx_to_coord(start_idx); - const Dbu hi = idx_to_coord(end_idx); + // Each occupied idx is a sample on the pixel lattice. The effective + // overlap span is bounded by the midpoints of neighboring samples. + const Dbu lo = midpoint(idx_to_coord(start_idx), idx_to_coord(start_idx + 1)); + const Dbu hi = midpoint(idx_to_coord(end_idx_exclusive - 1), idx_to_coord(end_idx_exclusive)); PixelOverlap seq = clamp_sequence_bounds(lo, hi); if (!seq.empty()) { @@ -205,19 +212,7 @@ class Pixel const bool cell = cell_or_empty(idx); if (cell != current_type) { if (current_type) { - Dbu valid_start = run_start; - while (valid_start <= idx && !idx_valid(valid_start)) { - ++valid_start; - } - - Dbu valid_end = idx; - while (valid_end >= valid_start && !idx_valid(valid_end)) { - --valid_end; - } - - if (valid_start <= valid_end) { - push_sequence(valid_start, valid_end, true); - } + push_sequence(run_start, idx); } run_start = idx; @@ -226,26 +221,13 @@ class Pixel } if (current_type) { - Dbu valid_start = run_start; - while (valid_start <= a1_idx && !idx_valid(valid_start)) { - ++valid_start; - } - - Dbu valid_end = a1_idx; - while (valid_end >= valid_start && !idx_valid(valid_end)) { - --valid_end; - } - - if (valid_start <= valid_end) { - push_sequence(valid_start, valid_end, true); - } + push_sequence(run_start, a1_idx + 1); } return ret; } - -private: + private: std::vector> pixel_; // true: conductor, false: empty Dbu x0_{0}, y0_{0}; diff --git a/src/operation/iRCX/source/module/extract/CMakeLists.txt b/src/operation/iRCX/source/module/extract/CMakeLists.txt index 6d4da90c3..97ed87bb6 100644 --- a/src/operation/iRCX/source/module/extract/CMakeLists.txt +++ b/src/operation/iRCX/source/module/extract/CMakeLists.txt @@ -9,8 +9,10 @@ add_library(ircx ${IRCX_EXTRACT_SRC}) target_link_libraries(ircx PUBLIC ircx_cap_table + ircx_config ircx_headers ircx_mapping + OpenMP::OpenMP_CXX PRIVATE idm ircx_engine diff --git a/src/operation/iRCX/source/module/extract/RCTable.hpp b/src/operation/iRCX/source/module/extract/RCTable.hpp index 2ef9dd690..e5de3f032 100644 --- a/src/operation/iRCX/source/module/extract/RCTable.hpp +++ b/src/operation/iRCX/source/module/extract/RCTable.hpp @@ -40,12 +40,22 @@ class RCTable { RCTable() = default; ~RCTable() = default; + void clear() { + corner_num_ = 0; + net_num_ = 0; + corner_net_res_pools_.clear(); + corner_net_gcap_pools_.clear(); + net_ccap_entries_.clear(); + merged_ccap_.clear(); + } + /// Pre-allocate all storage. Must be called before any parallel calc. void init(Size corner_num, Size net_num, const TopoPool& topo) { + clear(); corner_num_ = corner_num; - net_num_ = net_num; + net_num_ = net_num; - Size total = corner_num * net_num; + Size total = corner_num_ * net_num_; corner_net_res_pools_.resize(total); corner_net_gcap_pools_.resize(total); @@ -59,7 +69,6 @@ class RCTable { } net_ccap_entries_.resize(net_num); - merged_ccap_.clear(); } // Resistance: writable span per (corner, net) diff --git a/src/operation/iRCX/source/module/extract/RCX.cpp b/src/operation/iRCX/source/module/extract/RCX.cpp index 622c1c5c9..46a694705 100644 --- a/src/operation/iRCX/source/module/extract/RCX.cpp +++ b/src/operation/iRCX/source/module/extract/RCX.cpp @@ -16,9 +16,7 @@ // *************************************************************************************** #include #include -#include -#include -#include +#include #include #include @@ -30,25 +28,67 @@ #include "ItfBuilder.hpp" #include "ParasiticXIDBAdapter.hpp" #include "CapacitanceCalc.hpp" +#include "RCXConfig.hh" #include "ResistanceCalc.hpp" #include "SpefDumper.hpp" #include "idm.h" #include "log/Log.hh" namespace ircx { -std::vector> -RCX::loadItfFiles(const std::vector& itf_files) +std::unique_ptr<::itf::ProcessCorner> RCX::loadProcessCorner(const Str& corner_name, + const Str& itf_file) { - LOG_FATAL_IF(itf_files.empty()) << "itf file list is empty."; + LOG_FATAL_IF(corner_name.empty()) << "corner name is empty."; + LOG_FATAL_IF(itf_file.empty()) << "itf file is empty for corner " << corner_name; ::itf::ItfBuilder itf_builder; - for (const auto& itf_file : itf_files) { - LOG_INFO << "read itf " << itf_file << " start"; - itf_builder.buildItf(itf_file); - LOG_INFO << "read itf " << itf_file << " end"; + LOG_INFO << "read itf " << itf_file << " for corner " << corner_name << " start"; + itf_builder.buildItf(itf_file); + LOG_INFO << "read itf " << itf_file << " for corner " << corner_name << " end"; + + auto pcs = itf_builder.get_itf_service()->take_process_corners(); + std::unique_ptr<::itf::ProcessCorner> loaded_corner; + Size valid_corner_num = 0; + for (auto& pc : pcs) { + if (!pc || pc->get_technology().empty()) { + continue; + } + ++valid_corner_num; + if (!loaded_corner) { + loaded_corner = std::move(pc); + } } - return itf_builder.get_itf_service()->take_process_corners(); + LOG_FATAL_IF(valid_corner_num != 1) + << "read_corner expects exactly one process corner in ITF file " + << itf_file << ", but got " << valid_corner_num; + LOG_FATAL_IF(!loaded_corner); + + const Str original_corner_name = loaded_corner->get_technology(); + if (original_corner_name != corner_name) { + LOG_INFO << "rename process corner " + << original_corner_name << " -> " << corner_name; + loaded_corner->set_technology(corner_name); + } + + return loaded_corner; +} + +parser::CapTable RCX::loadCapTable(const Str& corner_name, const Str& captab_file) +{ + LOG_FATAL_IF(corner_name.empty()) << "corner name is empty."; + LOG_FATAL_IF(captab_file.empty()) << "captab file is empty for corner " << corner_name; + + LOG_INFO << "read captab " << captab_file + << " for corner " << corner_name << " start"; + + parser::CapTable cap_table; + LOG_FATAL_IF(!cap_table.loadFromFile(captab_file)) + << "failed to load captab: " << captab_file; + + LOG_INFO << "read captab " << captab_file + << " for corner " << corner_name << " end"; + return cap_table; } void RCX::registerProcessLayers(const ::itf::ProcessCorner& pc) @@ -68,100 +108,105 @@ void RCX::registerProcessLayers(const ::itf::ProcessCorner& pc) process_layers_registered_ = true; } -void RCX::storeProcessCorner(std::unique_ptr<::itf::ProcessCorner> pc) +void RCX::validateProcessLayers(const ::itf::ProcessCorner& pc) const { - if (!pc || pc->get_technology().empty()) { + if (corners_.empty()) { return; } - registerProcessLayers(*pc); - const Str corner_name = pc->get_technology(); - process_corners_[corner_name] = std::move(pc); -} - -std::vector RCX::corner_cap_tables() const -{ - std::vector result; - result.reserve(process_corners_.size()); - - for (const auto& [corner_name, _] : process_corners_) { - auto iter = corner_cap_tables_.find(corner_name); - result.push_back(iter == corner_cap_tables_.end() ? nullptr : &iter->second); + const ::itf::ProcessCorner& ref = *corners_.front().process_corner; + const auto& ref_layers = ref.get_layers()->get_layers(); + const auto& cur_layers = pc.get_layers()->get_layers(); + + LOG_FATAL_IF(ref_layers.size() != cur_layers.size()) + << "process layer count mismatch between corner " + << pc.get_technology() << " and " << ref.get_technology() + << ": " << cur_layers.size() << " vs " << ref_layers.size(); + + for (Size idx = 0; idx < ref_layers.size(); ++idx) { + const auto* ref_layer = ref_layers[idx]; + const auto* cur_layer = cur_layers[idx]; + + LOG_FATAL_IF(ref_layer == nullptr || cur_layer == nullptr) + << "null process layer in corner layer list."; + LOG_FATAL_IF(ref_layer->get_type() != cur_layer->get_type()) + << "process layer type mismatch at index " << idx + << " between corner " << pc.get_technology() + << " and " << ref.get_technology(); + LOG_FATAL_IF(ref_layer->get_id() != cur_layer->get_id()) + << "process layer id mismatch at index " << idx + << " between corner " << pc.get_technology() + << " and " << ref.get_technology() + << ": " << cur_layer->get_id() << " vs " << ref_layer->get_id(); + LOG_FATAL_IF(ref_layer->get_order() != cur_layer->get_order()) + << "process layer order mismatch at index " << idx + << " between corner " << pc.get_technology() + << " and " << ref.get_technology(); + LOG_FATAL_IF(ref_layer->get_name() != cur_layer->get_name()) + << "process layer name mismatch at index " << idx + << " between corner " << pc.get_technology() + << " and " << ref.get_technology() + << ": " << cur_layer->get_name() << " vs " << ref_layer->get_name(); } - - return result; } -unsigned RCX::readCorner(const Str& corner_name, - const char* itf_file, - const char* captab_file) +bool RCX::hasCorner(const Str& corner_name) const { - if (!readItf(corner_name, itf_file)) { - return 0; - } - return readCaptab(corner_name, captab_file); + return std::any_of(corners_.begin(), corners_.end(), + [&](const CornerData& corner) { + return corner.name == corner_name; + }); } -unsigned RCX::readCaptab(const Str& corner_name, const char* captab_file) +void RCX::resetConfigData() { - LOG_FATAL_IF(corner_name.empty()) << "corner name is empty."; - LOG_FATAL_IF(!process_corners_.contains(corner_name)) - << "process corner not loaded: " << corner_name; - - LOG_INFO << "read captab " << captab_file - << " for corner " << corner_name << " start"; - - parser::CapTable cap_table; - LOG_FATAL_IF(!cap_table.loadFromFile(captab_file)) - << "failed to load captab: " << captab_file; - corner_cap_tables_[corner_name] = std::move(cap_table); - - LOG_INFO << "read captab " << captab_file - << " for corner " << corner_name << " end"; - return 1; + layout_.clear(); + spef_context_.clear(); + layer_table_.clear(); + corners_.clear(); + mapping_builder_.clear(); + topo_pool_.clear(); + rc_table_.clear(); + process_layers_registered_ = false; } -unsigned RCX::readItf(const Str& corner_name, const char* itf_file) +std::vector RCX::corner_cap_tables() const { - LOG_FATAL_IF(corner_name.empty()) << "corner name is empty."; - - auto pcs = loadItfFiles({itf_file}); - - std::unique_ptr<::itf::ProcessCorner> loaded_corner; - Size valid_corner_num = 0; - for (auto& pc : pcs) { - if (!pc || pc->get_technology().empty()) { - continue; - } - ++valid_corner_num; - if (!loaded_corner) { - loaded_corner = std::move(pc); - } - } - - LOG_FATAL_IF(valid_corner_num != 1) - << "read_corner expects exactly one process corner in ITF file " - << itf_file << ", but got " << valid_corner_num; - LOG_FATAL_IF(!loaded_corner); + std::vector result; + result.reserve(corners_.size()); - const Str original_corner_name = loaded_corner->get_technology(); - if (original_corner_name != corner_name) { - LOG_INFO << "rename process corner " - << original_corner_name << " -> " << corner_name; - loaded_corner->set_technology(corner_name); + for (const auto& corner : corners_) { + result.push_back(&corner.cap_table); } - storeProcessCorner(std::move(loaded_corner)); - return 1; + return result; } -unsigned RCX::readItf(const std::vector& itf_files) +unsigned RCX::readCorner(const Str& corner_name, + const char* itf_file, + const char* captab_file) { - auto pcs = loadItfFiles(itf_files); - for (auto& pc : pcs) { - storeProcessCorner(std::move(pc)); + LOG_FATAL_IF(corner_name.empty()) << "corner name is empty."; + LOG_FATAL_IF(itf_file == nullptr || itf_file[0] == '\0') + << "itf file is empty for corner " << corner_name; + LOG_FATAL_IF(captab_file == nullptr || captab_file[0] == '\0') + << "captab file is empty for corner " << corner_name; + LOG_FATAL_IF(hasCorner(corner_name)) << "duplicate corner: " << corner_name; + + CornerData corner; + corner.name = corner_name; + corner.itf_file = itf_file; + corner.captab_file = captab_file; + corner.process_corner = loadProcessCorner(corner.name, corner.itf_file); + corner.cap_table = loadCapTable(corner.name, corner.captab_file); + + if (corners_.empty()) { + registerProcessLayers(*corner.process_corner); + } else { + validateProcessLayers(*corner.process_corner); } + corners_.push_back(std::move(corner)); return 1; } @@ -170,6 +215,7 @@ unsigned RCX::readMapping(const char* mapping_file) LOG_INFO << "read mapping " << mapping_file << " start"; + layer_table_.clearMappings(); mapping_builder_.read(mapping_file); for (const auto& [dn, pn] : mapping_builder_.design_to_process_layer_names()) @@ -223,6 +269,7 @@ unsigned RCX::buildTopology() { LOG_INFO << "build topology start"; + topo_pool_.clear(); TopologyBuilder tb(topo_pool_); tb.build_all(layout_); tb.build_special(layout_); @@ -304,9 +351,65 @@ unsigned RCX::extractParasitics() return 1; } +unsigned RCX::run() +{ + LOG_INFO << "RCX run begin..."; + + omp_set_num_threads(num_threads_); + + if (!adaptDB() || + !buildTopology() || + !buildEnvironment() || + !buildProcessVariation() || + !extractParasitics()) { + LOG_INFO << "RCX run end."; + return 0; + } + + LOG_INFO << "RCX run end."; + return 1; +} + +unsigned RCX::runFromConfig(const Str& config) +{ + RCXConfig rcx_config; + if (!rcx_config.loadFromFile(config)) { + return 0; + } + + resetConfigData(); + set_num_threads(rcx_config.get_thread_num()); + + for (const auto& corner : rcx_config.get_corners()) { + if (!readCorner(corner.name, corner.itf_file.c_str(), corner.captab_file.c_str())) { + return 0; + } + } + if (!readMapping(rcx_config.get_mapping_file().c_str())) { + return 0; + } + + if (!run()) { + return 0; + } + + const Str output_dir = + rcx_config.get_output_dir().empty() ? "." : rcx_config.get_output_dir(); + return reportSpef(output_dir); +} + unsigned RCX::reportSpef(const Str& output_dir) { LOG_INFO << "report spef start"; + const Str resolved_output_dir = output_dir.empty() ? "." : output_dir; + std::error_code ec; + std::filesystem::create_directories(resolved_output_dir, ec); + if (ec) { + LOG_ERROR << "Failed to create RCX output directory " + << resolved_output_dir << ": " << ec.message(); + return 0; + } + const auto process_corners = corners(); SpefDumper dumper; @@ -319,7 +422,7 @@ unsigned RCX::reportSpef(const Str& output_dir) dumper.set_corners(process_corners); for (Size corner_idx = 0; corner_idx < process_corners.size(); ++corner_idx) { - dumper.dump(output_dir, corner_idx); + dumper.dump(resolved_output_dir, corner_idx); } LOG_INFO << "report spef end"; diff --git a/src/operation/iRCX/source/module/extract/RCX.hpp b/src/operation/iRCX/source/module/extract/RCX.hpp index 4dc257835..483c9948b 100644 --- a/src/operation/iRCX/source/module/extract/RCX.hpp +++ b/src/operation/iRCX/source/module/extract/RCX.hpp @@ -16,7 +16,6 @@ // *************************************************************************************** #pragma once -#include #include #include #include @@ -47,7 +46,7 @@ class MappingBuilder; namespace ircx { -inline constexpr int kDefaultThreadCount = 64; +inline constexpr unsigned kDefaultThreadCount = 64U; class RCX final { public: @@ -65,7 +64,6 @@ class RCX final { // I/O [[nodiscard]] unsigned readCorner(const Str&, const char*, const char*); - [[nodiscard]] unsigned readItf(const std::vector&); [[nodiscard]] unsigned readMapping(const char*); // DB @@ -85,43 +83,53 @@ class RCX final { // Extraction [[nodiscard]] unsigned extractParasitics(); + [[nodiscard]] unsigned run(); + [[nodiscard]] unsigned runFromConfig(const Str& config); // Report [[nodiscard]] unsigned reportSpef(const Str& output_dir); // setters & getters - void set_num_threads(unsigned value) { num_threads_ = value; } + void set_num_threads(unsigned value) { num_threads_ = value == 0 ? 1U : value; } [[nodiscard]] unsigned num_threads() const { return num_threads_; } std::vector<::itf::ProcessCorner*> corners() { std::vector<::itf::ProcessCorner*> out; - out.reserve(process_corners_.size()); - for (auto& [_, ptr] : process_corners_) out.push_back(ptr.get()); + out.reserve(corners_.size()); + for (auto& corner : corners_) out.push_back(corner.process_corner.get()); return out; } std::vector corners() const { std::vector result; - result.reserve(process_corners_.size()); - for (const auto& [_, ptr] : process_corners_) result.push_back(ptr.get()); + result.reserve(corners_.size()); + for (const auto& corner : corners_) result.push_back(corner.process_corner.get()); return result; } private: + struct CornerData { + Str name; + Str itf_file; + Str captab_file; + std::unique_ptr<::itf::ProcessCorner> process_corner; + parser::CapTable cap_table; + }; + RCX(); ~RCX(); - [[nodiscard]] unsigned readItf(const Str&, const char*); - [[nodiscard]] unsigned readCaptab(const Str&, const char*); - - std::vector> loadItfFiles( - const std::vector& itf_files); + std::unique_ptr<::itf::ProcessCorner> loadProcessCorner(const Str& corner_name, + const Str& itf_file); + parser::CapTable loadCapTable(const Str& corner_name, const Str& captab_file); void registerProcessLayers(const ::itf::ProcessCorner& pc); - void storeProcessCorner(std::unique_ptr<::itf::ProcessCorner> pc); + void validateProcessLayers(const ::itf::ProcessCorner& pc) const; + [[nodiscard]] bool hasCorner(const Str& corner_name) const; + void resetConfigData(); std::vector corner_cap_tables() const; private: // running settings - int num_threads_ = kDefaultThreadCount; + unsigned num_threads_ = kDefaultThreadCount; // from db LayoutData layout_; @@ -131,12 +139,9 @@ class RCX final { // from 1.db adapter 2.itf 3.mapping LayerTable layer_table_; - // process corners (from itf file) - std::map> process_corners_; + // per-corner process/cap data loaded by readCorner() + std::vector corners_; - // per-corner cap table (from captab file) - std::map corner_cap_tables_; // mapping table (from mapping file) parser::MappingBuilder mapping_builder_; diff --git a/src/operation/iRCX/source/module/extract/capacitance/CapacitanceCalc.cpp b/src/operation/iRCX/source/module/extract/capacitance/CapacitanceCalc.cpp index b6cf303de..cce71450d 100644 --- a/src/operation/iRCX/source/module/extract/capacitance/CapacitanceCalc.cpp +++ b/src/operation/iRCX/source/module/extract/capacitance/CapacitanceCalc.cpp @@ -25,14 +25,251 @@ #include "log/Log.hh" namespace ircx { +namespace { + +enum class AdjacentKind { + kNone, + kSpecialNet, + kSameNet, + kOtherNet +}; + +struct SideContext { + Micron spacing{0}; + const TopoEdge* adjacent{nullptr}; + AdjacentKind kind{AdjacentKind::kNone}; + + bool occupied() const { return adjacent != nullptr; } + bool sameNet() const { return kind == AdjacentKind::kSameNet; } + bool specialNet() const { return kind == AdjacentKind::kSpecialNet; } +}; + +AdjacentKind classifyAdjacent(const TopoEdge* adjacent, Size net_idx) +{ + if (adjacent == nullptr) { + return AdjacentKind::kNone; + } + if (adjacent->net_id() == kSpecialNetId) { + return AdjacentKind::kSpecialNet; + } + if (adjacent->net_id() == net_idx) { + return AdjacentKind::kSameNet; + } + return AdjacentKind::kOtherNet; +} + +SideContext makeSideContext(Micron spacing, const TopoEdge* adjacent, Size net_idx) +{ + return SideContext{spacing, adjacent, classifyAdjacent(adjacent, net_idx)}; +} + +void resolveCrossLayers( + const LayerTable& layer_table, + const CrossOverlapSub* cross_seg, + Str& below_layer, + Str& above_layer) +{ + below_layer = "SUBSTRATE"; + above_layer.clear(); + + if (cross_seg == nullptr) { + return; + } + + if (cross_seg->blw_layer != 0) { + const Size process_layer_id = layer_table.design_to_process_id(cross_seg->blw_layer); + below_layer = layer_table.process_name(process_layer_id); + } + if (cross_seg->abv_layer != 0) { + const Size process_layer_id = layer_table.design_to_process_id(cross_seg->abv_layer); + above_layer = layer_table.process_name(process_layer_id); + } +} + +class EdgeCapAccumulator { + public: + EdgeCapAccumulator( + const parser::CapTable& cap_table, + const TopoPool& topo_pool, + RCTable& rc_table, + std::span edge_ground_caps, + const Str& layer_name, + Size corner_idx, + Size net_idx, + Size edge_idx, + Size edge_global_id) + : cap_table_(cap_table), + topo_pool_(topo_pool), + rc_table_(rc_table), + edge_ground_caps_(edge_ground_caps), + layer_name_(layer_name), + corner_idx_(corner_idx), + net_idx_(net_idx), + edge_idx_(edge_idx), + edge_global_id_(edge_global_id) + { + } + + void accumulateSpan( + Micron span_length, + const Str& below_layer, + const Str& above_layer, + const SideContext& low_side, + const SideContext& high_side) + { + if (span_length <= 0.0) { + return; + } + + if (low_side.occupied() && high_side.occupied()) { + // Two occupied sides: + // gcap = L * (cg_lo + cg_hi) + // ccap(side) = L * cc_side / 2 + // same-net side keeps only half of its ground term + const parser::CapacitanceResult low_side_cap = + queryNearCap(below_layer, above_layer, low_side.spacing); + const parser::CapacitanceResult high_side_cap = + queryNearCap(below_layer, above_layer, high_side.spacing); + + accumulateGround(low_side, span_length * low_side_cap.ground_cap); + accumulateGround(high_side, span_length * high_side_cap.ground_cap); + foldCoupling(low_side, span_length * low_side_cap.coupling_cap / 2.0); + foldCoupling(high_side, span_length * high_side_cap.coupling_cap / 2.0); + return; + } + + if (low_side.occupied() || high_side.occupied()) { + // One occupied side: + // gcap = 2 * L * cg + // ccap = L * cc + // same-net side keeps only half of the ground term + const SideContext& occupied_side = low_side.occupied() ? low_side : high_side; + const parser::CapacitanceResult occupied_side_cap = + queryNearCap(below_layer, above_layer, occupied_side.spacing); + + accumulateGround(occupied_side, 2.0 * span_length * occupied_side_cap.ground_cap); + foldCoupling(occupied_side, span_length * occupied_side_cap.coupling_cap); + return; + } + + // No occupied sides: + // take the farthest table row and use + // gcap = 2 * L * (cg + cc) + const parser::CapacitanceResult far_cap = + queryFarthestCap(below_layer, above_layer); + edge_ground_caps_[edge_idx_] += + 2.0 * span_length * (far_cap.ground_cap + far_cap.coupling_cap); + } + + private: + parser::CapacitanceResult queryNearCap( + const Str& below_layer, + const Str& above_layer, + Micron spacing) const + { + const Micron lookup_dist = std::max(spacing, 0.0); + if (above_layer.empty()) { + return cap_table_.queryTwoLayerCap(layer_name_, below_layer, lookup_dist); + } + return cap_table_.queryThreeLayerCap( + layer_name_, below_layer, above_layer, lookup_dist); + } + + parser::CapacitanceResult queryFarthestCap( + const Str& below_layer, + const Str& above_layer) const + { + if (above_layer.empty()) { + return cap_table_.queryTwoLayerFarthestCap(layer_name_, below_layer); + } + return cap_table_.queryThreeLayerFarthestCap( + layer_name_, below_layer, above_layer); + } + + void accumulateGround(const SideContext& side, double ground_cap_ff) + { + if (ground_cap_ff <= 0.0 || !side.occupied()) { + return; + } + + // Same-net adjacency only contributes ground cap, with half weight. + edge_ground_caps_[edge_idx_] += side.sameNet() ? ground_cap_ff / 2.0 + : ground_cap_ff; + } + + void foldCoupling(const SideContext& side, double coupling_cap_ff) + { + if (coupling_cap_ff <= 0.0 || !side.occupied()) { + return; + } + + // Special-net coupling is modeled as ground capacitance. + // Same-net adjacency does not contribute coupling. + if (side.specialNet()) { + edge_ground_caps_[edge_idx_] += coupling_cap_ff; + return; + } + if (side.sameNet()) { + return; + } + + const Size adjacent_edge_global_id = topo_pool_.edge_index(*side.adjacent); + rc_table_.append_net_ccap_entry( + net_idx_, + edge_global_id_, + adjacent_edge_global_id, + corner_idx_, + static_cast(coupling_cap_ff)); + } + + const parser::CapTable& cap_table_; + const TopoPool& topo_pool_; + RCTable& rc_table_; + std::span edge_ground_caps_; + const Str& layer_name_; + Size corner_idx_; + Size net_idx_; + Size edge_idx_; + Size edge_global_id_; +}; + +void accumulateSegmentCap( + const LayerTable& layer_table, + Micron dbu_to_micron, + Dbu segment_lo_dbu, + Dbu segment_hi_dbu, + const CrossOverlapSub* cross_overlap, + const SideContext& low_side, + const SideContext& high_side, + EdgeCapAccumulator& accumulator) +{ + if (segment_hi_dbu <= segment_lo_dbu) { + return; + } + + Str below_layer; + Str above_layer; + resolveCrossLayers(layer_table, cross_overlap, below_layer, above_layer); + accumulator.accumulateSpan( + (segment_hi_dbu - segment_lo_dbu) * dbu_to_micron, + below_layer, + above_layer, + low_side, + high_side); +} + +} // namespace + void CapacitanceCalc::calc() { + validateInputs(); + ProcessVariation& pv = ProcessVariation::getOrCreateInst(); Environment& env = Environment::getOrCreateInst(); - const Size corner_count = corner_num_; - const Size net_count = net_num_; + const Size corner_count = corners_.size(); + const Size net_count = layout_data_->regular_net_count(); - LOG_FATAL_IF(cap_tables_.size() != corner_num_) + LOG_FATAL_IF(cap_tables_.size() != corner_count) << "cap table count does not match corner count."; for (Size corner_idx = 0; corner_idx < corner_count; ++corner_idx) { @@ -42,172 +279,12 @@ void CapacitanceCalc::calc() #pragma omp parallel for schedule(dynamic) for (Size net_idx = 0; net_idx < net_count; ++net_idx) { - const auto net_edges = topo_pool_->net_edges(net_idx); - const Size edge_count = net_edges.size(); - auto edge_ground_caps = rc_table_->corner_net_gcap_pool(corner_idx, net_idx); - - const EtchPool& corner_net_etch_pool = pv.corner_net_etch_pool(corner_idx, net_idx); - const EnvPool& net_env_pool = env.net_env_pool(net_idx); - - for (Size edge_idx = 0; edge_idx < edge_count; ++edge_idx) { - const TopoEdge& edge = net_edges[edge_idx]; - if (edge.is_via()) continue; - - const Size process_layer_id = layer_table_->design_to_process_id(edge.layer_id()); - const Str& layer_name = layer_table_->process_name(process_layer_id); - - // Global index of current edge in TopoPool::edge_pool() - const Size edge_global_id = topo_pool_->edge_index(edge); - - const auto env_intervals = net_env_pool.edge_env_interval_pool(edge_idx); - const auto etch_intervals = corner_net_etch_pool.edge_etch_interval_pool(edge_idx); - const Size interval_count = std::min(env_intervals.size(), etch_intervals.size()); - - for (Size interval_idx = 0; interval_idx < interval_count; ++interval_idx) { - const EnvInterval& env_interval = env_intervals[interval_idx]; - const EtchInterval& etch_interval = etch_intervals[interval_idx]; - - const Dbu interval_lo_dbu = env_interval.a0; - const Dbu interval_hi_dbu = env_interval.a1; - if (interval_hi_dbu <= interval_lo_dbu) continue; - - auto queryNearCap = [&](const Str& belowLayer, - const Str& aboveLayer, - Micron spacing) { - const Micron lookup_dist = std::max(spacing, 0.0); - if (aboveLayer.empty()) { - return cap_table->queryTwoLayerCap(layer_name, belowLayer, lookup_dist); - } - return cap_table->queryThreeLayerCap( - layer_name, belowLayer, aboveLayer, lookup_dist); - }; - - auto queryFarthestCap = [&](const Str& belowLayer, - const Str& aboveLayer) { - if (aboveLayer.empty()) { - return cap_table->queryTwoLayerFarthestCap(layer_name, belowLayer); - } - return cap_table->queryThreeLayerFarthestCap( - layer_name, belowLayer, aboveLayer); - }; - - struct SideContext { - Micron spacing{0}; - const TopoEdge* adjacent{nullptr}; - bool occupied{false}; - bool regular{false}; - bool same_net{false}; - }; - - auto buildSideContext = [&](Micron spacing, const TopoEdge* adjacent) { - SideContext side; - side.spacing = spacing; - side.adjacent = adjacent; - side.occupied = adjacent != nullptr; - side.regular = side.occupied && adjacent->net_id() != kSpecialNetId; - side.same_net = side.regular && adjacent->net_id() == net_idx; - return side; - }; - - auto foldCoupling = [&](const SideContext& side, double coupling_cap_ff) { - if (coupling_cap_ff <= 0.0 || !side.occupied) { - return; - } - - // Same-net and special-net coupling are modeled as ground capacitance. - if (!side.regular || side.same_net) { - edge_ground_caps[edge_idx] += coupling_cap_ff; - return; - } - - const Size adjacent_edge_global_id = topo_pool_->edge_index(*side.adjacent); - rc_table_->append_net_ccap_entry( - net_idx, - edge_global_id, - adjacent_edge_global_id, - corner_idx, - static_cast(coupling_cap_ff)); - }; - - const SideContext low_side = buildSideContext( - etch_interval.lo_spacing, env_interval.lo_adjacent); - const SideContext high_side = buildSideContext( - etch_interval.hi_spacing, env_interval.hi_adjacent); - - auto accumulateSpan = [&](Micron span_length, - const Str& belowLayer, - const Str& aboveLayer) { - if (span_length <= 0) { - return; - } - - if (low_side.occupied && high_side.occupied) { - // Two occupied sides: - // gcap = L * (cg_lo + cg_hi) - // ccap(side) = L * cc_side / 2 - const parser::CapacitanceResult low_side_cap = - queryNearCap(belowLayer, aboveLayer, low_side.spacing); - const parser::CapacitanceResult high_side_cap = - queryNearCap(belowLayer, aboveLayer, high_side.spacing); - - edge_ground_caps[edge_idx] += - span_length * (low_side_cap.ground_cap + high_side_cap.ground_cap); - foldCoupling(low_side, span_length * low_side_cap.coupling_cap / 2.0); - foldCoupling(high_side, span_length * high_side_cap.coupling_cap / 2.0); - } else if (low_side.occupied || high_side.occupied) { - // One occupied side: - // gcap = 2 * L * cg - // ccap = L * cc - const SideContext& occupied_side = low_side.occupied ? low_side : high_side; - const parser::CapacitanceResult occupied_side_cap = - queryNearCap(belowLayer, aboveLayer, occupied_side.spacing); - - edge_ground_caps[edge_idx] += 2.0 * span_length * occupied_side_cap.ground_cap; - foldCoupling(occupied_side, span_length * occupied_side_cap.coupling_cap); - } else { - // No occupied sides: - // take the farthest table row and use - // gcap = 2 * L * (cg + cc) - const parser::CapacitanceResult far_cap = - queryFarthestCap(belowLayer, aboveLayer); - edge_ground_caps[edge_idx] += - 2.0 * span_length * (far_cap.ground_cap + far_cap.coupling_cap); - } - }; - - Dbu cursor_dbu = interval_lo_dbu; - for (const CrossOverlapSub& cross_overlap : env_interval.cross_segs) { - if (cursor_dbu < cross_overlap.a0) { - Str below_layer; - Str above_layer; - resolveCrossLayers(nullptr, below_layer, above_layer); - accumulateSpan( - (cross_overlap.a0 - cursor_dbu) * dbu_to_micron_, - below_layer, - above_layer); - cursor_dbu = cross_overlap.a0; - } - - const Dbu overlap_hi_dbu = std::min(interval_hi_dbu, cross_overlap.a1); - if (cursor_dbu < overlap_hi_dbu) { - Str below_layer; - Str above_layer; - resolveCrossLayers(&cross_overlap, below_layer, above_layer); - accumulateSpan((overlap_hi_dbu - cursor_dbu) * dbu_to_micron_, below_layer, above_layer); - cursor_dbu = overlap_hi_dbu; - } - } - - if (cursor_dbu < interval_hi_dbu) { - Str below_layer; - Str above_layer; - resolveCrossLayers(nullptr, below_layer, above_layer); - accumulateSpan((interval_hi_dbu - cursor_dbu) * dbu_to_micron_, below_layer, above_layer); - } - } - } - - // results already written into pre-allocated span + calcNet( + corner_idx, + net_idx, + *cap_table, + pv.corner_net_etch_pool(corner_idx, net_idx), + env.net_env_pool(net_idx)); } } @@ -215,25 +292,135 @@ void CapacitanceCalc::calc() rc_table_->merge_net_ccap_entries(); } -void CapacitanceCalc::resolveCrossLayers( - const CrossOverlapSub* crossSeg, - Str& belowLayer, - Str& aboveLayer) const +void CapacitanceCalc::validateInputs() const { - belowLayer = "SUBSTRATE"; - aboveLayer.clear(); + LOG_FATAL_IF(layout_data_ == nullptr) << "layout data not set."; + LOG_FATAL_IF(layer_table_ == nullptr) << "layer table not set."; + LOG_FATAL_IF(topo_pool_ == nullptr) << "topology pool not set."; + LOG_FATAL_IF(rc_table_ == nullptr) << "RC table not set."; + LOG_FATAL_IF(corners_.empty()) << "process corners not set."; + LOG_FATAL_IF(cap_tables_.empty()) << "cap tables not set."; +} - if (crossSeg == nullptr) { - return; +void CapacitanceCalc::calcNet( + Size corner_idx, + Size net_idx, + const parser::CapTable& cap_table, + const EtchPool& corner_net_etch_pool, + const EnvPool& net_env_pool) +{ + const auto net_edges = topo_pool_->net_edges(net_idx); + auto edge_ground_caps = rc_table_->corner_net_gcap_pool(corner_idx, net_idx); + + for (Size edge_idx = 0; edge_idx < net_edges.size(); ++edge_idx) { + calcEdge( + corner_idx, + net_idx, + edge_idx, + net_edges[edge_idx], + cap_table, + edge_ground_caps, + net_env_pool, + corner_net_etch_pool); } +} - if (crossSeg->blw_layer != 0) { - Size procId = layer_table_->design_to_process_id(crossSeg->blw_layer); - belowLayer = layer_table_->process_name(procId); +void CapacitanceCalc::calcEdge( + Size corner_idx, + Size net_idx, + Size edge_idx, + const TopoEdge& edge, + const parser::CapTable& cap_table, + std::span edge_ground_caps, + const EnvPool& net_env_pool, + const EtchPool& corner_net_etch_pool) +{ + if (edge.is_via()) { + return; } - if (crossSeg->abv_layer != 0) { - Size procId = layer_table_->design_to_process_id(crossSeg->abv_layer); - aboveLayer = layer_table_->process_name(procId); + + const Size process_layer_id = layer_table_->design_to_process_id(edge.layer_id()); + const Str& layer_name = layer_table_->process_name(process_layer_id); + + // Global index of current edge in TopoPool::edge_pool() + const Size edge_global_id = topo_pool_->edge_index(edge); + + const auto env_intervals = net_env_pool.edge_env_interval_pool(edge_idx); + const auto etch_intervals = corner_net_etch_pool.edge_etch_interval_pool(edge_idx); + LOG_ERROR_IF(env_intervals.size() != etch_intervals.size()) + << "environment/etch interval count mismatch for net " + << net_idx << ", edge " << edge_idx << "."; + const Size interval_count = std::min(env_intervals.size(), etch_intervals.size()); + + // Keep all per-edge lookup and accumulation state in one helper so the + // outer flow stays focused on interval traversal. + EdgeCapAccumulator accumulator( + cap_table, + *topo_pool_, + *rc_table_, + edge_ground_caps, + layer_name, + corner_idx, + net_idx, + edge_idx, + edge_global_id); + + for (Size interval_idx = 0; interval_idx < interval_count; ++interval_idx) { + const EnvInterval& env_interval = env_intervals[interval_idx]; + const EtchInterval& etch_interval = etch_intervals[interval_idx]; + + const Dbu interval_lo_dbu = env_interval.a0; + const Dbu interval_hi_dbu = env_interval.a1; + if (interval_hi_dbu <= interval_lo_dbu) { + continue; + } + + const SideContext low_side = makeSideContext( + etch_interval.lo_spacing, env_interval.lo_adjacent, net_idx); + const SideContext high_side = makeSideContext( + etch_interval.hi_spacing, env_interval.hi_adjacent, net_idx); + + Dbu cursor_dbu = interval_lo_dbu; + for (const CrossOverlapSub& cross_overlap : env_interval.cross_segs) { + if (cursor_dbu < cross_overlap.a0) { + accumulateSegmentCap( + *layer_table_, + dbu_to_micron_, + cursor_dbu, + cross_overlap.a0, + nullptr, + low_side, + high_side, + accumulator); + cursor_dbu = cross_overlap.a0; + } + + const Dbu overlap_hi_dbu = std::min(interval_hi_dbu, cross_overlap.a1); + if (cursor_dbu < overlap_hi_dbu) { + accumulateSegmentCap( + *layer_table_, + dbu_to_micron_, + cursor_dbu, + overlap_hi_dbu, + &cross_overlap, + low_side, + high_side, + accumulator); + cursor_dbu = overlap_hi_dbu; + } + } + + if (cursor_dbu < interval_hi_dbu) { + accumulateSegmentCap( + *layer_table_, + dbu_to_micron_, + cursor_dbu, + interval_hi_dbu, + nullptr, + low_side, + high_side, + accumulator); + } } } diff --git a/src/operation/iRCX/source/module/extract/capacitance/CapacitanceCalc.hpp b/src/operation/iRCX/source/module/extract/capacitance/CapacitanceCalc.hpp index d2208f221..834db2a16 100644 --- a/src/operation/iRCX/source/module/extract/capacitance/CapacitanceCalc.hpp +++ b/src/operation/iRCX/source/module/extract/capacitance/CapacitanceCalc.hpp @@ -16,6 +16,7 @@ // *************************************************************************************** #pragma once +#include #include #include "LayoutData.hpp" @@ -28,7 +29,9 @@ class CapTable; } class LayoutData; +class EtchPool; class LayerTable; +class TopoEdge; class TopoPool; } @@ -54,7 +57,6 @@ class CapacitanceCalc { void set_layout_data(const LayoutData* v) { layout_data_ = v; - net_num_ = v->regular_net_count(); dbu_to_micron_ = Micron(1.0) / v->micron_to_dbu; } void set_layer_table(const LayerTable* v) { layer_table_ = v; } @@ -62,10 +64,7 @@ class CapacitanceCalc { void set_cap_tables(const std::vector& v) { cap_tables_ = v; } - void set_corners(const std::vector& v) { - corners_ = v; - corner_num_ = v.size(); - } + void set_corners(const std::vector& v) { corners_ = v; } void set_rc_table(RCTable* v) { rc_table_ = v; } void calc(); @@ -74,14 +73,23 @@ class CapacitanceCalc { CapacitanceCalc() = default; ~CapacitanceCalc() = default; - // Determine below/above process layer names from cross-layer segments. - void resolveCrossLayers( - const CrossOverlapSub* crossSeg, - Str& belowLayer, - Str& aboveLayer) const; + void validateInputs() const; + void calcNet( + Size corner_idx, + Size net_idx, + const parser::CapTable& cap_table, + const EtchPool& corner_net_etch_pool, + const EnvPool& net_env_pool); + void calcEdge( + Size corner_idx, + Size net_idx, + Size edge_idx, + const TopoEdge& edge, + const parser::CapTable& cap_table, + std::span edge_ground_caps, + const EnvPool& net_env_pool, + const EtchPool& corner_net_etch_pool); - Size net_num_{0}; - Size corner_num_{0}; Micron dbu_to_micron_{1}; const LayoutData* layout_data_{nullptr}; diff --git a/src/operation/iRCX/source/module/process/ProcessVariation.cpp b/src/operation/iRCX/source/module/process/ProcessVariation.cpp index 2fcc1cebc..d4f7c6c91 100644 --- a/src/operation/iRCX/source/module/process/ProcessVariation.cpp +++ b/src/operation/iRCX/source/module/process/ProcessVariation.cpp @@ -74,6 +74,7 @@ void ProcessVariation::initEtchIntervals() net_num_ = layout_data_->regular_net_count(); corner_num_ = corners_.size(); + corner_net_etch_pools_.clear(); corner_net_etch_pools_.resize(net_num_ * corner_num_); const std::vector& net_env_pools = env.net_env_pools(); diff --git a/src/operation/iRCX/source/module/shell_cmd/CMakeLists.txt b/src/operation/iRCX/source/module/shell_cmd/CMakeLists.txt index c0a7f5d19..ca37ad768 100644 --- a/src/operation/iRCX/source/module/shell_cmd/CMakeLists.txt +++ b/src/operation/iRCX/source/module/shell_cmd/CMakeLists.txt @@ -14,7 +14,6 @@ target_link_libraries(rcx_shell_cmd PRIVATE ircx log - OpenMP::OpenMP_CXX ) target_include_directories(rcx_shell_cmd diff --git a/src/operation/iRCX/source/module/shell_cmd/CmdRCXRun.cc b/src/operation/iRCX/source/module/shell_cmd/CmdRCXRun.cc index f8d1842af..5d93e05f1 100644 --- a/src/operation/iRCX/source/module/shell_cmd/CmdRCXRun.cc +++ b/src/operation/iRCX/source/module/shell_cmd/CmdRCXRun.cc @@ -22,7 +22,6 @@ * @date 2025-12-09 */ #include -#include #include "RCX.hpp" #include "rcxShellCmd.hh" @@ -45,16 +44,7 @@ unsigned CmdRCXRun::exec() { RCX& rcx = RCX::getOrCreateInst(); - LOG_INFO << "RCX run begin..."; - omp_set_num_threads(rcx.num_threads()); - - ret &= rcx.adaptDB(); - ret &= rcx.buildTopology(); - ret &= rcx.buildEnvironment(); - ret &= rcx.buildProcessVariation(); - // ret &= rcx.checkShortOpen(); - ret &= rcx.extractParasitics(); - LOG_INFO << "RCX run end."; + ret &= rcx.run(); return ret; } diff --git a/src/operation/iRCX/source/module/topology/TopoPool.cpp b/src/operation/iRCX/source/module/topology/TopoPool.cpp index 6bd1c1a89..9ca39db6e 100644 --- a/src/operation/iRCX/source/module/topology/TopoPool.cpp +++ b/src/operation/iRCX/source/module/topology/TopoPool.cpp @@ -46,6 +46,15 @@ void TopoPool::reserve(Size net_count, Size total_nodes, Size total_edges) { edge_pool_.reserve(total_edges); } +void TopoPool::clear() +{ + node_pool_.clear(); + net_node_ranges_.clear(); + edge_pool_.clear(); + net_edge_ranges_.clear(); + special_edge_pool_.clear(); +} + std::span TopoPool::net_nodes(Size net_id) const { LOG_FATAL_IF(net_id >= net_node_ranges_.size()) << "net_id out of range."; auto [offset, count] = net_node_ranges_[net_id]; diff --git a/src/operation/iRCX/source/module/topology/TopoPool.hpp b/src/operation/iRCX/source/module/topology/TopoPool.hpp index 9515683d0..e2c2542d0 100644 --- a/src/operation/iRCX/source/module/topology/TopoPool.hpp +++ b/src/operation/iRCX/source/module/topology/TopoPool.hpp @@ -191,6 +191,7 @@ class TopoPool { // Pre-allocate all pools to avoid incremental reallocation in addNet(). // Call once before the addNet() loop with the totals across all nets. void reserve(Size net_count, Size total_nodes, Size total_edges); + void clear(); // Build interface (called by TopologyBuilder) // Assigns per-net local edge/node ids and appends nodes and edges into the flat pools. diff --git a/src/operation/iRCX/source/parser/mapping/MappingBuilder.cpp b/src/operation/iRCX/source/parser/mapping/MappingBuilder.cpp index 21abca060..614a71982 100644 --- a/src/operation/iRCX/source/parser/mapping/MappingBuilder.cpp +++ b/src/operation/iRCX/source/parser/mapping/MappingBuilder.cpp @@ -21,8 +21,16 @@ namespace ircx { namespace parser { +void MappingBuilder::clear() +{ + design_to_process_layer_names_.clear(); + process_to_design_layer_names_.clear(); +} + void MappingBuilder::read(const std::string& mappingPath) { + clear(); + std::ifstream mappingFile(mappingPath); std::string line; while (std::getline(mappingFile, line)) { diff --git a/src/operation/iRCX/source/parser/mapping/MappingBuilder.hpp b/src/operation/iRCX/source/parser/mapping/MappingBuilder.hpp index 0e99d8a5a..74b7f1a37 100644 --- a/src/operation/iRCX/source/parser/mapping/MappingBuilder.hpp +++ b/src/operation/iRCX/source/parser/mapping/MappingBuilder.hpp @@ -34,6 +34,7 @@ class MappingBuilder return process_to_design_layer_names_; } + void clear(); void read(const std::string& mappingPath); private: