diff --git a/src/interface/tcl/CMakeLists.txt b/src/interface/tcl/CMakeLists.txt index 78925efba..e02289d3d 100644 --- a/src/interface/tcl/CMakeLists.txt +++ b/src/interface/tcl/CMakeLists.txt @@ -6,6 +6,7 @@ add_subdirectory(tcl_icts) add_subdirectory(tcl_idrc) add_subdirectory(tcl_instance) add_subdirectory(tcl_irt) +add_subdirectory(tcl_ircx) add_subdirectory(tcl_ifp) add_subdirectory(tcl_ipdn) add_subdirectory(tcl_ipnp) @@ -43,7 +44,8 @@ target_link_libraries(ieda_tcl tcl_flow tcl_icts tcl_idrc - tcl_irt + tcl_irt + tcl_ircx tcl_ifp tcl_ipdn tcl_inst diff --git a/src/interface/tcl/tcl_ircx/CMakeLists.txt b/src/interface/tcl/tcl_ircx/CMakeLists.txt new file mode 100644 index 000000000..efe720f3b --- /dev/null +++ b/src/interface/tcl/tcl_ircx/CMakeLists.txt @@ -0,0 +1,14 @@ +add_library(tcl_ircx INTERFACE) + +target_link_libraries(tcl_ircx + INTERFACE + tool_manager + tcl + rcx_shell_cmd + str +) + +target_include_directories(tcl_ircx + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/src/interface/tcl/tcl_ircx/tcl_register_ircx.h b/src/interface/tcl/tcl_ircx/tcl_register_ircx.h new file mode 100644 index 000000000..52b612160 --- /dev/null +++ b/src/interface/tcl/tcl_ircx/tcl_register_ircx.h @@ -0,0 +1,50 @@ +// *************************************************************************************** +// 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 +/** + * @File Name: tcl_register_rcx.h + * @Brief : + * @Author : Yipei Xu (yipeix@163.com) + * @Version : 1.0 + * @Creat Date : 2025-12-08 + * + */ +#include "ScriptEngine.hh" +#include "UserShell.hh" + +#define TCL_USERSHELL + +#ifdef TCL_USERSHELL +#include "rcxShellCmd.hh" +#endif + +using namespace ieda; + +namespace tcl { +int registerCmdRCX() +{ + registerTclCmd(ircx::CmdRCXRun, "run_rcx"); + registerTclCmd(ircx::CmdRCXInit, "init_rcx"); + registerTclCmd(ircx::CmdRCXReport, "report_rcx"); + + registerTclCmd(ircx::CmdReadCorner, "read_corner"); + registerTclCmd(ircx::CmdReadMapping, "read_mapping"); + + return EXIT_SUCCESS; +} + +} // namespace tcl diff --git a/src/interface/tcl/tcl_register.h b/src/interface/tcl/tcl_register.h index 6f5adebea..5743c6e20 100644 --- a/src/interface/tcl/tcl_register.h +++ b/src/interface/tcl/tcl_register.h @@ -42,6 +42,7 @@ #include "tcl_register_idrc.h" #include "tcl_register_inst.h" #include "tcl_register_irt.h" +#include "tcl_register_ircx.h" #include "tcl_register_no.h" #include "tcl_register_pdn.h" #include "tcl_register_pl.h" @@ -98,6 +99,9 @@ int registerCommands() /// DRC registerCmdDRC(); + /// RCX + registerCmdRCX(); + /// STA registerCmdSTA(); diff --git a/src/operation/CMakeLists.txt b/src/operation/CMakeLists.txt index c0d9d069e..aec1192aa 100644 --- a/src/operation/CMakeLists.txt +++ b/src/operation/CMakeLists.txt @@ -8,6 +8,7 @@ add_subdirectory(iPDN) add_subdirectory(iPNP) add_subdirectory(iPL) add_subdirectory(iRT) +add_subdirectory(iRCX) add_subdirectory(iSTA) add_subdirectory(iPA) add_subdirectory(iTO) diff --git a/src/operation/iRCX/.clang-format b/src/operation/iRCX/.clang-format new file mode 100644 index 000000000..faa4523ef --- /dev/null +++ b/src/operation/iRCX/.clang-format @@ -0,0 +1,3 @@ +Language: Cpp +BasedOnStyle: Google +PointerAlignment: Left \ No newline at end of file diff --git a/src/operation/iRCX/CMakeLists.txt b/src/operation/iRCX/CMakeLists.txt new file mode 100644 index 000000000..c870b4f25 --- /dev/null +++ b/src/operation/iRCX/CMakeLists.txt @@ -0,0 +1,49 @@ +if(TEST_SHELL) + add_definitions(-DTEST_SHELL) +endif() + +# ADD_COMPILE_OPTIONS("-fsanitize=address") LINK_LIBRARIES("-fsanitize=address") + +add_library(ircx_headers INTERFACE) + +target_compile_features(ircx_headers INTERFACE cxx_std_20) + +target_include_directories(ircx_headers + INTERFACE + ${HOME_UTILITY} + ${HOME_UTILITY}/log + ${HOME_UTILITY}/stdBase + ${HOME_UTILITY}/stdBase/include + ${HOME_OPERATION}/iRCX + ${HOME_OPERATION}/iRCX/api + ${HOME_OPERATION}/iRCX/source/module + ${HOME_OPERATION}/iRCX/source/module/database + ${HOME_OPERATION}/iRCX/source/module/environment + ${HOME_OPERATION}/iRCX/source/module/extract + ${HOME_OPERATION}/iRCX/source/module/extract/capacitance + ${HOME_OPERATION}/iRCX/source/module/extract/resistance + ${HOME_OPERATION}/iRCX/source/module/process + ${HOME_OPERATION}/iRCX/source/module/report + ${HOME_OPERATION}/iRCX/source/module/shell_cmd + ${HOME_OPERATION}/iRCX/source/module/topology + ${HOME_OPERATION}/iRCX/source/parser + ${HOME_OPERATION}/iRCX/source/parser/cap_table + ${HOME_OPERATION}/iRCX/source/parser/itf + ${HOME_OPERATION}/iRCX/source/parser/itf_builder + ${HOME_OPERATION}/iRCX/source/parser/itf_data + ${HOME_OPERATION}/iRCX/source/parser/mapping +) + +target_include_directories(ircx_headers + SYSTEM INTERFACE + ${HOME_THIRDPARTY} +) + +add_subdirectory(api) +add_subdirectory(source/module/topology) +add_subdirectory(source/module/environment) +add_subdirectory(source/module/process) +add_subdirectory(source/module/extract) +add_subdirectory(source/module/report) +add_subdirectory(source/module/shell_cmd) +add_subdirectory(source/parser) diff --git a/src/operation/iRCX/api/CMakeLists.txt b/src/operation/iRCX/api/CMakeLists.txt new file mode 100644 index 000000000..f4e11551f --- /dev/null +++ b/src/operation/iRCX/api/CMakeLists.txt @@ -0,0 +1,22 @@ +set(IRCX_ENGINE_SRC + ParasiticXEngine.cpp + ParasiticXIDBAdapter.cpp +) + +add_library(ircx_engine ${IRCX_ENGINE_SRC}) + +target_link_libraries(ircx_engine + PUBLIC + IdbBuilder + ircx_headers + PRIVATE + def_service + idb + lef_service + log +) + +target_include_directories(ircx_engine + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/src/operation/iRCX/api/ParasiticXEngine.cpp b/src/operation/iRCX/api/ParasiticXEngine.cpp new file mode 100644 index 000000000..4255e6a79 --- /dev/null +++ b/src/operation/iRCX/api/ParasiticXEngine.cpp @@ -0,0 +1,44 @@ +// *************************************************************************************** +// 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 + +#include "ParasiticXEngine.hpp" +namespace ircx { + +ParasiticXEngine* ParasiticXEngine::_instance = nullptr; + +ParasiticXEngine* ParasiticXEngine::get_or_create_parasitic_x_engine() +{ + static std::mutex mutex; + if (_instance == nullptr) { + std::lock_guard lock(mutex); + if (_instance == nullptr) { + _instance = new ParasiticXEngine(); + } + } + return _instance; +} + +void ParasiticXEngine::destroyParasiticXEngine() +{ + delete _instance; +} + +ParasiticXEngine::ParasiticXEngine() : _rcx(nullptr), _db_adapter(nullptr) {} + + +} // namespace ircx diff --git a/src/operation/iRCX/api/ParasiticXEngine.hpp b/src/operation/iRCX/api/ParasiticXEngine.hpp new file mode 100644 index 000000000..abcb79d7f --- /dev/null +++ b/src/operation/iRCX/api/ParasiticXEngine.hpp @@ -0,0 +1,60 @@ +// *************************************************************************************** +// 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 + +namespace ircx { + +class RCX; +class ParasiticXDBAdapter; + +// Database Adapter +class ParasiticXEngine { + public: + // Get or create singleton instance + static ParasiticXEngine* get_or_create_parasitic_x_engine(); + + // Destroy singleton instance + static void destroyParasiticXEngine(); + + // getter + [[nodiscard]] RCX* get_rcx() const { return _rcx; } + [[nodiscard]] ParasiticXDBAdapter* get_db_adapter() const { return _db_adapter; } + + // setter + void set_rcx(RCX* rcx) { _rcx = rcx; } + void set_db_adapter(ParasiticXDBAdapter* db_adapter) { _db_adapter = db_adapter; } + + // Disallow copy and move + ParasiticXEngine(const ParasiticXEngine&) = delete; + void operator=(const ParasiticXEngine&) = delete; + ParasiticXEngine(ParasiticXEngine&&) = delete; + void operator=(ParasiticXEngine&&) = delete; + + private: + // Private constructor for singleton + ParasiticXEngine(); + ~ParasiticXEngine() = default; + + // Singleton instance + static ParasiticXEngine* _instance; + + // members + RCX* _rcx; + ParasiticXDBAdapter* _db_adapter; +}; + +} // namespace ircx diff --git a/src/operation/iRCX/api/ParasiticXIDBAdapter.cpp b/src/operation/iRCX/api/ParasiticXIDBAdapter.cpp new file mode 100644 index 000000000..2b9a7aafc --- /dev/null +++ b/src/operation/iRCX/api/ParasiticXIDBAdapter.cpp @@ -0,0 +1,438 @@ +// *************************************************************************************** +// 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 +#include +#include +#include +#include +#include +#include +#include + +#include "ParasiticXIDBAdapter.hpp" +#include "LayerTable.hpp" +#include "LayoutData.hpp" +#include "SpefContext.hpp" +#include "log/Log.hh" +namespace ircx { + +using namespace idb; + +namespace { + +// Escape SPEF-sensitive hierarchy/index tokens once at the adapter boundary so +// all downstream SPEF-facing names stay consistent without scattering fixes +// across the pipeline. +Str escapeSpefIdentifier(Str name) +{ + if (name.find('.') == Str::npos) { + return name; + } + + Str escaped_name; + escaped_name.reserve(name.size()); + for (Size idx = 0; idx < name.size(); ++idx) { + const char current_char = name[idx]; + const bool needs_escape = + current_char == '.' || current_char == '[' || current_char == ']'; + if (needs_escape && (idx == 0 || name[idx - 1] != '\\')) { + escaped_name.push_back('\\'); + } + escaped_name.push_back(current_char); + } + + return escaped_name; +} + +} // namespace + +bool ParasiticXIDBAdapter::adapt(LayoutData& layout_data, + LayerTable& layer_table, + SpefContext& spef_context) +{ + layout_data_ = &layout_data; + layer_table_ = &layer_table; + spef_context_ = &spef_context; + + // idb + auto* def_service = idb_->get_def_service(); + auto* lef_service = idb_->get_lef_service(); + if (!def_service || !lef_service) return false; + + IdbDesign* idb_design = def_service->get_design(); + if (!idb_design) { + LOG_ERROR << "idb::IdbDesign is null"; + return false; + } + + IdbLayout* idb_layout = lef_service->get_layout(); + + IdbNetList* idb_net_list = idb_design->get_net_list(); + IdbSpecialNetList* idb_special_net_list = idb_design->get_special_net_list(); + IdbUnits* idb_units = idb_design->get_units(); + if (!idb_net_list || !idb_layout) { + LOG_ERROR_IF(!idb_net_list) << "idb::IdbNetList is null"; + LOG_ERROR_IF(!idb_layout) << "idb::IdbLayout is null"; + return false; + } + + IdbLayers* idb_layers = idb_layout->get_layers(); + adaptLayerTable(idb_layers); + adaptRoutingLayer(idb_layers); + adaptSpefContext(idb_design); + + // set LayoutData : design_name die_shape micron_to_dbu + IdbDie* idb_die = idb_layout->get_die(); + layout_data.design_name = idb_design->get_design_name(); + if (idb_die) layout_data.die_shape = IdbRectToGtlRect(idb_die->get_bounding_box()); + if (idb_units) layout_data.micron_to_dbu = idb_units->get_micron_dbu(); + adaptNet(idb_net_list); + + adaptSpecialNet(idb_special_net_list); + + return true; +} + +void ParasiticXIDBAdapter::adaptLayerTable(IdbLayers* idb_layers) +{ + layer_table_->registerDesignLayer(0, "SUBSTRATE"); + + Size next_layer_id = 1; + for (IdbLayer* idb_routing_layer : idb_layers->get_routing_layers()) { + layer_table_->registerDesignLayer(next_layer_id, idb_routing_layer->get_name()); + ++next_layer_id; + } + for (IdbLayer* idb_cut_layer : idb_layers->get_cut_layers()) { + layer_table_->registerDesignLayer(next_layer_id, idb_cut_layer->get_name()); + ++next_layer_id; + } +} + +void ParasiticXIDBAdapter::adaptRoutingLayer(IdbLayers* idb_layers) +{ + // set RoutingLayer + std::map& routing_layers = layout_data_->routing_layers; + + std::vector& routing_layer_defs = idb_layers->get_routing_layers(); + for (IdbLayer* idb_layer : routing_layer_defs) { + auto* idb_routing_layer = dynamic_cast(idb_layer); + if (!idb_routing_layer) continue; + + RoutingLayer routing_layer; + + const Size design_layer_id = layer_table_->design_id(idb_routing_layer->get_name()); + routing_layer.set_layer_id(design_layer_id); + routing_layer.set_layer_name(idb_routing_layer->get_name()); + routing_layer.set_layer_width(idb_routing_layer->get_width()); + if (idb_routing_layer->is_horizontal()) routing_layer.set_prefer_horz(true); + else if (idb_routing_layer->is_vertical()) routing_layer.set_prefer_horz(false); + + // track info + RoutingLayer::TrackInfo track_info; + for (IdbTrackGrid* track_grid : idb_routing_layer->get_track_grid_list()) { + IdbTrack* track = track_grid->get_track(); + if (track->get_direction() == IdbTrackDirection::kDirectionX) { + track_info.x0 = track->get_start(); + track_info.dx = track->get_pitch(); + track_info.nx = track_grid->get_track_num(); + } else if (track->get_direction() == IdbTrackDirection::kDirectionY) { + track_info.y0 = track->get_start(); + track_info.dy = track->get_pitch(); + track_info.ny = track_grid->get_track_num(); + } + } + + routing_layer.set_track_info(track_info); + routing_layers.emplace(design_layer_id, std::move(routing_layer)); + } +} + +void ParasiticXIDBAdapter::adaptSpefContext(IdbDesign* idb_design) +{ + const std::vector& idb_nets = idb_design->get_net_list()->get_net_list(); + spef_context_->net_names.reserve(idb_nets.size()); + for (IdbNet* idb_net : idb_nets) { + if (idb_net->is_pdn()) continue; + spef_context_->net_names.push_back(escapeSpefIdentifier(idb_net->get_net_name())); + } + + const std::vector& io_pins = idb_design->get_io_pin_list()->get_pin_list(); + spef_context_->port_names.reserve(io_pins.size()); + spef_context_->port_io.reserve(io_pins.size()); + for (IdbPin* io_pin : io_pins) { + if (io_pin->is_special_net_pin() || !io_pin->get_net()) continue; + spef_context_->port_names.push_back(escapeSpefIdentifier(io_pin->get_pin_name())); + + if (io_pin->is_primary_input()) { + spef_context_->port_io.emplace_back('I'); + } else if (io_pin->is_primary_output()) { + spef_context_->port_io.emplace_back('O'); + } else { + spef_context_->port_io.emplace_back('B'); + } + } + + const std::vector& instances = + idb_design->get_instance_list()->get_instance_list(); + spef_context_->instance_names.reserve(instances.size()); + for (IdbInstance* instance : instances) { + const Str instance_name = escapeSpefIdentifier(instance->get_name()); + spef_context_->instance_names.push_back(instance_name); + spef_context_->instance_to_cell[instance_name] = + escapeSpefIdentifier(instance->get_cell_master()->get_name()); + } +} + + +void ParasiticXIDBAdapter::adaptNet(IdbNetList* idb_netlist) +{ + std::vector& regular_nets = layout_data_->net_vec; + std::vector& idb_nets = idb_netlist->get_net_list(); + const Size net_count = idb_nets.size(); + regular_nets.resize(net_count); + for (Size net_idx = 0; net_idx < net_count; ++net_idx) { + IdbNet* idb_net = idb_nets[net_idx]; + + if (!idb_net) continue; + + Net& net = regular_nets[net_idx]; + net.id = net_idx; + net.name = escapeSpefIdentifier(idb_net->get_net_name()); + + auto* idb_wire_list = idb_net->get_wire_list(); + if (!idb_wire_list || idb_wire_list->get_num() == 0) { + continue; + } + + // adapt driving + if (IdbPin* driver_pin = idb_net->get_driving_pin()) { + Pin driver = adaptPin(driver_pin, true); + net.pins.push_back(std::move(driver)); + } + + // adapt load + for (IdbPin* load_pin : idb_net->get_load_pins()) { + if (!load_pin) continue; + + Pin load = adaptPin(load_pin, false); + net.pins.push_back(std::move(load)); + } + + // adapt segments + for (auto* idb_wire : idb_wire_list->get_wire_list()) { + if (!idb_wire) continue; + + for (auto* idb_segment : idb_wire->get_segment_list()) { + if (!idb_segment) continue; + + // convert segments + if (idb_segment->is_wire()) { // wire + net.segments.push_back(adaptSegments(idb_segment)); + } + if (idb_segment->is_rect()) { // patch + net.patches.push_back(adaptPatch(idb_segment)); + } + for (auto* idb_via : idb_segment->get_via_list()) { // via + net.vias.push_back(adaptVia(idb_via)); + } + } + } + } +} + +Pin ParasiticXIDBAdapter::adaptPin(IdbPin* idb_pin, bool is_driving=false) +{ + Pin pin; + + if (idb_pin->is_io_pin()) { + pin.name = escapeSpefIdentifier(idb_pin->get_pin_name()); + } else { + pin.name = escapeSpefIdentifier(idb_pin->get_instance()->get_name()) + + ':' + + escapeSpefIdentifier(idb_pin->get_pin_name()); + } + + pin.is_driver = is_driving; + + if (IdbTerm* idb_term = idb_pin->get_term()) { + using Dir = IdbConnectDirection; + const Dir pin_direction = idb_term->get_direction(); + switch (pin_direction) { + case Dir::kInput: + pin.is_input = true; + break; + case Dir::kOutput: + case Dir::kOutputTriState: + pin.is_output = true; + break; + case Dir::kInOut: + case Dir::kFeedThru: + pin.is_input = true; + pin.is_output = true; + break; + default: + break; + } + } + + for (IdbLayerShape* layer_shape : idb_pin->get_port_box_list()) { + if (!layer_shape || !layer_shape->get_layer()) continue; + IdbLayer* idb_layer = layer_shape->get_layer(); + const Size design_layer_id = layer_table_->design_id(idb_layer->get_name()); + for (IdbRect* idb_rect : layer_shape->get_rect_list()) { + if (!idb_rect) continue; + pin.layer_id_rects.emplace_back(design_layer_id, IdbRectToGtlRect(idb_rect)); + } + } + + return pin; +} + +Segment ParasiticXIDBAdapter::adaptSegments(IdbRegularWireSegment* idb_seg) +{ + LOG_FATAL_IF(!idb_seg || !idb_seg->is_wire()); + + auto* p1 = idb_seg->get_point_start(); + auto* p2 = idb_seg->get_point_end(); + LOG_FATAL_IF(!p1 || !p2); + + IdbLayer* idb_layer = idb_seg->get_layer(); + LOG_FATAL_IF(!idb_layer); + + IdbRect segment_rect = idb_seg->get_segment_rect(); + + Segment segment; + segment.p0 = GtlPointI(p1->get_x(), p1->get_y()); + segment.p1 = GtlPointI(p2->get_x(), p2->get_y()); + segment.layer_id = layer_table_->design_id(idb_layer->get_name()); + segment.rect = IdbRectToGtlRect(&segment_rect); + return segment; +} + +Patch ParasiticXIDBAdapter::adaptPatch(IdbRegularWireSegment* idb_seg) +{ + LOG_FATAL_IF(!idb_seg || !idb_seg->is_rect()); + + auto* delta_rect = idb_seg->get_delta_rect(); + auto* anchor_point = idb_seg->get_point(0); + auto* idb_layer = idb_seg->get_layer(); + + LOG_FATAL_IF(!delta_rect || !anchor_point || !idb_layer); + + const int lower_x = anchor_point->get_x() + delta_rect->get_low_x(); + const int lower_y = anchor_point->get_y() + delta_rect->get_low_y(); + const int upper_x = anchor_point->get_x() + delta_rect->get_high_x(); + const int upper_y = anchor_point->get_y() + delta_rect->get_high_y(); + + Patch patch; + patch.layer_id = layer_table_->design_id(idb_layer->get_name()); + patch.rect = GtlRectI(lower_x, lower_y, upper_x, upper_y); + return patch; +} + +Via ParasiticXIDBAdapter::adaptVia(IdbVia* idb_via) +{ + LOG_FATAL_IF(!idb_via); + + auto* center_point = idb_via->get_coordinate(); + LOG_FATAL_IF(!center_point); + + Via via; + via.name = escapeSpefIdentifier(idb_via->get_name()); + via.point = GtlPointI(center_point->get_x(), center_point->get_y()); + + auto read_layer_rect = + [&](IdbLayerShape layer_shape, + const char* multi_rect_error) + -> std::optional> { + IdbLayer* idb_layer = layer_shape.get_layer(); + if (!idb_layer) return std::nullopt; + + std::vector& layer_rects = layer_shape.get_rect_list(); + LOG_FATAL_IF(layer_rects.size() != 1) << multi_rect_error; + + const Size design_layer_id = layer_table_->design_id(idb_layer->get_name()); + return std::make_pair(design_layer_id, IdbRectToGtlRect(layer_rects[0])); + }; + + if (auto top_layer_rect = + read_layer_rect(idb_via->get_top_layer_shape(), "not support multirect for via top")) { + via.layer_rect_top = *top_layer_rect; + } + + if (auto bottom_layer_rect = + read_layer_rect(idb_via->get_bottom_layer_shape(), "not support multirect for via bottom")) { + via.layer_rect_btm = *bottom_layer_rect; + } + + if (auto cut_layer_rect = + read_layer_rect(idb_via->get_cut_layer_shape(), "not support multirect for via cut")) { + via.layer_rect_cut = *cut_layer_rect; + } + + return via; +} + +void ParasiticXIDBAdapter::adaptSpecialNet(IdbSpecialNetList* idb_special_net_list) +{ + if (!idb_special_net_list) return; + + Net& layout_special_net = layout_data_->special_net; + + for (auto* special_net : idb_special_net_list->get_net_list()) { + if (!special_net) continue; + + auto* special_wire_list = special_net->get_wire_list(); + if (!special_wire_list) continue; + + for (auto* special_wire : special_wire_list->get_wire_list()) { + if (!special_wire) continue; + + for (auto* special_segment : special_wire->get_segment_list()) { + if (!special_segment) continue; + if (special_segment->is_via()) continue; + + auto* special_layer = special_segment->get_layer(); + auto* special_rect = special_segment->get_bounding_box(); + auto* start_point = special_segment->get_point_start(); + auto* end_point = special_segment->get_point_second(); + if (!special_layer || !special_rect || !start_point || !end_point) continue; + + Segment segment; + segment.layer_id = layer_table_->design_id(special_layer->get_name()); + segment.rect = IdbRectToGtlRect(special_rect); + segment.p0 = IdbPointToGtlPoint(start_point); + segment.p1 = IdbPointToGtlPoint(end_point); + layout_special_net.segments.push_back(std::move(segment)); + } + } + } +} + +GtlRectI ParasiticXIDBAdapter::IdbRectToGtlRect(IdbRect* idb_rect) const +{ + return GtlRectI(idb_rect->get_low_x(), idb_rect->get_low_y(), + idb_rect->get_high_x(), idb_rect->get_high_y()); +} + +GtlPointI ParasiticXIDBAdapter::IdbPointToGtlPoint( + IdbCoordinate* idb_point) const +{ + return GtlPointI(idb_point->get_x(), idb_point->get_y()); +} + +} // namespace ircx diff --git a/src/operation/iRCX/api/ParasiticXIDBAdapter.hpp b/src/operation/iRCX/api/ParasiticXIDBAdapter.hpp new file mode 100644 index 000000000..bccc4c3b0 --- /dev/null +++ b/src/operation/iRCX/api/ParasiticXIDBAdapter.hpp @@ -0,0 +1,68 @@ +// *************************************************************************************** +// 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 + +#include "Types.hpp" +#include "builder.h" +namespace ircx { + +class Net; +class Segment; +class Patch; +class Via; +class Pin; + +class LayoutData; +class LayerTable; +class SpefContext; + +class ParasiticXIDBAdapter { + public: + explicit ParasiticXIDBAdapter(::idb::IdbBuilder* idb) : idb_(idb) { assert(idb_); } + ParasiticXIDBAdapter() = delete; + ~ParasiticXIDBAdapter() = default; + + bool adapt(LayoutData&, LayerTable&, SpefContext&); + + void adaptLayerTable(::idb::IdbLayers* idb_layers); + void adaptRoutingLayer(::idb::IdbLayers* idb_layers); + + void adaptSpefContext(::idb::IdbDesign* idb_design); + + void adaptNet(::idb::IdbNetList* idb_netlist); + Pin adaptPin(::idb::IdbPin* idb_pin, bool is_driving); + Segment adaptSegments(::idb::IdbRegularWireSegment* idb_seg); + Patch adaptPatch(::idb::IdbRegularWireSegment* idb_seg); + Via adaptVia(::idb::IdbVia* idb_via); + + void adaptSpecialNet(::idb::IdbSpecialNetList* idb_special_netlist); + + private: + //idb + ::idb::IdbBuilder* idb_{nullptr}; + // from outside + LayoutData* layout_data_{nullptr}; + LayerTable* layer_table_{nullptr}; + SpefContext* spef_context_{nullptr}; + + GtlRectI IdbRectToGtlRect(::idb::IdbRect*) const; + GtlPointI IdbPointToGtlPoint(::idb::IdbCoordinate*) const; +}; + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/database/Geoms.hpp b/src/operation/iRCX/source/module/database/Geoms.hpp new file mode 100644 index 000000000..d7a190e2c --- /dev/null +++ b/src/operation/iRCX/source/module/database/Geoms.hpp @@ -0,0 +1,591 @@ +// *************************************************************************************** +// 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 +#include +#include +#include +#include +#include + +#include "Types.hpp" +namespace ircx { +namespace geom { + +// ============================================================ +// Internal helpers +// ============================================================ + +template +struct dependent_false : std::false_type {}; + +template +struct dependent_false2 : std::false_type {}; + +template +using remove_cvref_t = std::remove_cv_t>; + +// ------------------------------------------------------------ +// Type detectors for common Boost / GTL geometry types +// ------------------------------------------------------------ + +template +struct is_gtl_point : std::false_type {}; +template +struct is_gtl_point> : std::true_type {}; + +template +struct is_bg_point : std::false_type {}; +template +struct is_bg_point> : std::true_type {}; + +template +struct is_gtl_rect : std::false_type {}; +template +struct is_gtl_rect> : std::true_type {}; + +template +struct is_gtl_polyset90 : std::false_type {}; +template +struct is_gtl_polyset90> : std::true_type {}; + +template +struct is_bg_box : std::false_type {}; +template +struct is_bg_box> : std::true_type {}; + +// ============================================================ +// point_traits: unify X/Y for GTL point and BG point +// ============================================================ + +template +struct point_traits { + static_assert(dependent_false

::value, + "point_traits

is not specialized for this point type"); +}; + +// gtl::point_data +template +struct point_traits> { + using coord_t = T; + static coord_t x(const gtl::point_data& p) { return p.x(); } + static coord_t y(const gtl::point_data& p) { return p.y(); } +}; + +// bg::model::point +template +struct point_traits> { + static_assert(Dim >= 2, "Boost.Geometry point dimension must be >= 2"); + using coord_t = T; + static coord_t x(const bg::model::point& p) { return bg::get<0>(p); } + static coord_t y(const bg::model::point& p) { return bg::get<1>(p); } +}; + +template +using PointCoordT = typename point_traits>::coord_t; + +// Unified point accessors + +template +inline auto X(const P& p) -> PointCoordT

{ + return point_traits>::x(p); +} + +template +inline auto Y(const P& p) -> PointCoordT

{ + return point_traits>::y(p); +} + +// ============================================================ +// point_make_traits: unify point construction +// ============================================================ + +template +struct point_make_traits { + static_assert(dependent_false

::value, + "point_make_traits

is not specialized for this point type"); +}; + +// gtl::point_data +template +struct point_make_traits> { + using coord_t = T; + static gtl::point_data make(coord_t x, coord_t y) { + return gtl::point_data{x, y}; + } +}; + +// bg::model::point +template +struct point_make_traits> { + static_assert(Dim >= 2, "Boost.Geometry point dimension must be >= 2"); + using coord_t = T; + + static bg::model::point make(coord_t x, coord_t y) { + bg::model::point p{}; + bg::set<0>(p, x); + bg::set<1>(p, y); + return p; + } +}; + +template +inline auto MakePoint(PointCoordT

x, PointCoordT

y) -> remove_cvref_t

{ + using PP = remove_cvref_t

; + return point_make_traits::make(x, y); +} + +// ============================================================ +// rect_traits: unify Min/Max for GTL rect and BG box +// ============================================================ + +template +struct rect_traits { + static_assert(dependent_false::value, + "rect_traits is not specialized for this rect/box type"); +}; + +// gtl::rectangle_data +template +struct rect_traits> { + using coord_t = T; + static coord_t min_x(const gtl::rectangle_data& r) { return r.get(gtl::WEST); } + static coord_t min_y(const gtl::rectangle_data& r) { return r.get(gtl::SOUTH); } + static coord_t max_x(const gtl::rectangle_data& r) { return r.get(gtl::EAST); } + static coord_t max_y(const gtl::rectangle_data& r) { return r.get(gtl::NORTH); } +}; + +// bg::model::box +template +struct rect_traits> { + using coord_t = typename bg::coordinate_type::type; + static coord_t min_x(const bg::model::box& b) { return bg::get(b); } + static coord_t min_y(const bg::model::box& b) { return bg::get(b); } + static coord_t max_x(const bg::model::box& b) { return bg::get(b); } + static coord_t max_y(const bg::model::box& b) { return bg::get(b); } +}; + +template +using RectCoordT = typename rect_traits>::coord_t; + +// Unified rect accessors + +template +inline auto MinX(const R& r) -> RectCoordT { + return rect_traits>::min_x(r); +} + +template +inline auto MinY(const R& r) -> RectCoordT { + return rect_traits>::min_y(r); +} + +template +inline auto MaxX(const R& r) -> RectCoordT { + return rect_traits>::max_x(r); +} + +template +inline auto MaxY(const R& r) -> RectCoordT { + return rect_traits>::max_y(r); +} + +// ============================================================ +// rect_make_traits: unify rect/box construction +// ============================================================ + +template +struct rect_make_traits { + static_assert(dependent_false::value, + "rect_make_traits is not specialized for this rect/box type"); +}; + +// gtl::rectangle_data : {lx, ly, hx, hy} +template +struct rect_make_traits> { + using coord_t = T; + static gtl::rectangle_data make(coord_t lx, coord_t ly, coord_t hx, coord_t hy) { + return {lx, ly, hx, hy}; + } +}; + +// bg::model::box : {Point(lx,ly), Point(hx,hy)} +template +struct rect_make_traits> { + using coord_t = typename bg::coordinate_type::type; + static bg::model::box make(coord_t lx, coord_t ly, coord_t hx, coord_t hy) { + return {PointT(lx, ly), PointT(hx, hy)}; + } +}; + +template +inline auto MakeRect(RectCoordT lx, RectCoordT ly, RectCoordT hx, RectCoordT hy) + -> remove_cvref_t { + using RR = remove_cvref_t; + return rect_make_traits::make(lx, ly, hx, hy); +} + +// ============================================================ +// Point utilities (generic) +// ============================================================ + +template +inline auto Manhattan(const P& a, const P& b) + -> decltype(std::abs(X(a) - X(b)) + std::abs(Y(a) - Y(b))) { + const auto dx = std::abs(X(a) - X(b)); + const auto dy = std::abs(Y(a) - Y(b)); + return dx + dy; +} + +template +inline bool IsHorDominant(const P& a, const P& b) { + return std::abs(X(a) - X(b)) >= std::abs(Y(a) - Y(b)); +} + +template +inline bool IsVerDominant(const P& a, const P& b) { + return !IsHorDominant(a, b); +} + +template +inline bool IsLeftBottom(const P& a, const P& b) { + return X(a) <= X(b) && Y(a) <= Y(b); +} + +template +inline bool IsRightTop(const P& a, const P& b) { + return X(a) >= X(b) && Y(a) >= Y(b); +} + +// ============================================================ +// Rect utilities (generic) +// ============================================================ + +template +inline auto DeltaX(const R& r) -> RectCoordT { + return MaxX(r) - MinX(r); +} + +template +inline auto DeltaY(const R& r) -> RectCoordT { + return MaxY(r) - MinY(r); +} + +// Use Min + (Max - Min) / 2 to reduce overflow risk for integral coordinates. +template +inline auto CenterX(const R& r) -> RectCoordT { + using T = RectCoordT; + return MinX(r) + (MaxX(r) - MinX(r)) / T{2}; +} + +template +inline auto CenterY(const R& r) -> RectCoordT { + using T = RectCoordT; + return MinY(r) + (MaxY(r) - MinY(r)) / T{2}; +} + +// Center always returns gtl::point_data. +// Float rect keeps float precision; integral rect keeps integral midpoint semantics. +template +inline auto Center(const R& r) -> gtl::point_data> { + using T = RectCoordT; + return gtl::point_data{CenterX(r), CenterY(r)}; +} + +template +inline bool IsHorDominant(const R& r) { + return DeltaX(r) >= DeltaY(r); +} + +template +inline bool IsVerDominant(const R& r) { + return DeltaX(r) < DeltaY(r); +} + +template +inline bool RectContainsPoint(const R& r, const P& p) { + return X(p) >= MinX(r) && X(p) <= MaxX(r) && + Y(p) >= MinY(r) && Y(p) <= MaxY(r); +} + +// ============================================================ +// Area / Intersects +// ============================================================ + +template +inline double Area(const Shape& s) { + using S = remove_cvref_t; + + if constexpr (is_bg_box::value) { + return static_cast(bg::area(s)); + } else if constexpr (is_gtl_rect::value || is_gtl_polyset90::value) { + return static_cast(gtl::area(s)); + } else { + static_assert(dependent_false::value, + "Area(Shape): unsupported Shape type " + "(expected bg::model::box<...>, gtl::rectangle_data<...>, " + "or gtl::polygon_90_set_data<...>)"); + return 0.0; + } +} + +// Note: +// - This function delegates to the underlying library semantics. +// - If you need strict positive-area overlap, use HasAreaIntersection(). +template +inline bool Intersects(const A& a, const B& b) { + using AA = remove_cvref_t; + using BB = remove_cvref_t; + + if constexpr (is_bg_box::value && is_bg_box::value) { + return bg::intersects(a, b); + } else if constexpr (is_gtl_rect::value && is_gtl_rect::value) { + return gtl::intersects(a, b); + } else { + static_assert(dependent_false2::value, + "Intersects(A,B): unsupported type combination " + "(supported: bg::box vs bg::box, gtl::rect vs gtl::rect)"); + return false; + } +} + +// Strict positive-area overlap for any two rect-like objects. +template +inline bool HasAreaIntersection(const A& a, const B& b) { + using TA = RectCoordT; + using TB = RectCoordT; + using CommonT = std::common_type_t; + + const CommonT lx = std::max(static_cast(MinX(a)), static_cast(MinX(b))); + const CommonT ly = std::max(static_cast(MinY(a)), static_cast(MinY(b))); + const CommonT hx = std::min(static_cast(MaxX(a)), static_cast(MaxX(b))); + const CommonT hy = std::min(static_cast(MaxY(a)), static_cast(MaxY(b))); + + return (lx < hx) && (ly < hy); +} + +// ============================================================ +// Generic intersection / clipping for rect-like objects +// ============================================================ + +// Same-type intersection: returns std::nullopt for zero-area intersection. +template +inline auto Intersection(const R& a, const R& b) -> std::optional> { + using RR = remove_cvref_t; + using T = RectCoordT; + + const T lx = std::max(MinX(a), MinX(b)); + const T ly = std::max(MinY(a), MinY(b)); + const T hx = std::min(MaxX(a), MaxX(b)); + const T hy = std::min(MaxY(a), MaxY(b)); + + if (lx < hx && ly < hy) { + return MakeRect(lx, ly, hx, hy); + } + return std::nullopt; +} + +// Cross-type intersection with explicit output rect type. +// Returns std::nullopt for zero-area intersection. +template +inline auto IntersectionAs(const A& a, const B& b) -> std::optional> { + using RR = remove_cvref_t; + using T = RectCoordT; + + const auto lx_raw = std::max(MinX(a), MinX(b)); + const auto ly_raw = std::max(MinY(a), MinY(b)); + const auto hx_raw = std::min(MaxX(a), MaxX(b)); + const auto hy_raw = std::min(MaxY(a), MaxY(b)); + + if (lx_raw < hx_raw && ly_raw < hy_raw) { + return MakeRect( + static_cast(lx_raw), + static_cast(ly_raw), + static_cast(hx_raw), + static_cast(hy_raw)); + } + return std::nullopt; +} + +// Clip r by win, preserving r's output type. +template +inline auto Clip(const R& r, const W& win) -> std::optional> { + return IntersectionAs>(r, win); +} + +// Clip r by win, with explicit output type. +template +inline auto ClipAs(const R& r, const W& win) -> std::optional> { + return IntersectionAs>(r, win); +} + +// ============================================================ +// Convert +// ============================================================ + +// ToBox: any rect-like -> bg::box< bg::point > +template +inline auto ToBox(const R& r) + -> bg::model::box, 2, bg::cs::cartesian>> { + using T = RectCoordT; + using P = bg::model::point; + using B = bg::model::box

; + return B(P(MinX(r), MinY(r)), P(MaxX(r), MaxY(r))); +} + +// ToRect: any rect-like -> gtl::rectangle_data +template +inline auto ToRect(const R& r) -> gtl::rectangle_data> { + using T = RectCoordT; + return gtl::rectangle_data(MinX(r), MinY(r), MaxX(r), MaxY(r)); +} + +// ------------------------------------------------------------ +// RectCastDivide: integral rect -> floating-point rect with scaling +// ------------------------------------------------------------ + +template +inline OutRect RectCastDivide(const InRect& r, Div divisor) { + using InCoord = RectCoordT; + using OutCoord = RectCoordT; + + static_assert(std::is_integral_v, + "RectCastDivide: InRect must use integral coordinates"); + static_assert(std::is_floating_point_v, + "RectCastDivide: OutRect must use floating-point coordinates"); + + assert(divisor != 0 && "RectCastDivide: divisor must not be zero"); + + const OutCoord d = static_cast(divisor); + + return MakeRect( + static_cast(MinX(r)) / d, + static_cast(MinY(r)) / d, + static_cast(MaxX(r)) / d, + static_cast(MaxY(r)) / d); +} + +// ============================================================ +// Point transforms +// ============================================================ + +template +inline auto TranslatePoint(const P& p, PointCoordT

dx, PointCoordT

dy) -> remove_cvref_t

{ + using PP = remove_cvref_t

; + return MakePoint(X(p) + dx, Y(p) + dy); +} + +// Returns a GTL rectangle centered at p with half-size d in both axes. +template +inline auto RectAround(const P& p, PointCoordT

d) -> gtl::rectangle_data> { + using T = PointCoordT

; + return gtl::rectangle_data{X(p) - d, Y(p) - d, X(p) + d, Y(p) + d}; +} + +// Backward-compatible alias for old API name. +template +inline auto BoxAround(const P& p, PointCoordT

d) -> gtl::rectangle_data> { + return RectAround(p, d); +} + +// ============================================================ +// Rect transforms +// ============================================================ + +template +inline auto TranslateRect(const R& r, RectCoordT dx, RectCoordT dy) -> remove_cvref_t { + using RR = remove_cvref_t; + return MakeRect(MinX(r) + dx, MinY(r) + dy, MaxX(r) + dx, MaxY(r) + dy); +} + +template +inline auto InflateX(const R& r, RectCoordT dx) -> remove_cvref_t { + using RR = remove_cvref_t; + return MakeRect(MinX(r) - dx, MinY(r), MaxX(r) + dx, MaxY(r)); +} + +template +inline auto InflateY(const R& r, RectCoordT dy) -> remove_cvref_t { + using RR = remove_cvref_t; + return MakeRect(MinX(r), MinY(r) - dy, MaxX(r), MaxY(r) + dy); +} + +template +inline auto Inflate(const R& r, RectCoordT d) -> remove_cvref_t { + using RR = remove_cvref_t; + return MakeRect(MinX(r) - d, MinY(r) - d, MaxX(r) + d, MaxY(r) + d); +} + +// ============================================================ +// Polyset conversion helpers +// ============================================================ + +inline std::vector PolysetToRects(const GtlPolysetI& ps) { + std::vector rects; + ps.get_rectangles(rects); + return rects; +} + +inline std::vector PolysetToRects(const GtlPolysetF& ps) { + std::vector rects; + ps.get_rectangles(rects); + return rects; +} + +inline GtlPolysetI RectsToPolyset(const std::vector& rects) { + GtlPolysetI ps; + for (const auto& r : rects) { + ps += r; + } + return ps; +} + +inline GtlPolysetF RectsToPolyset(const std::vector& rects) { + GtlPolysetF ps; + for (const auto& r : rects) { + ps += r; + } + return ps; +} + +// Returns the bounding box of all rects, or std::nullopt when rects is empty. +template +inline auto RectsToBbox(const std::vector& rects) -> std::optional { + if (rects.empty()) { + return std::nullopt; + } + + using T = RectCoordT; + + T minx = MinX(rects[0]); + T maxx = MaxX(rects[0]); + T miny = MinY(rects[0]); + T maxy = MaxY(rects[0]); + + for (std::size_t i = 1; i < rects.size(); ++i) { + minx = std::min(minx, MinX(rects[i])); + maxx = std::max(maxx, MaxX(rects[i])); + miny = std::min(miny, MinY(rects[i])); + maxy = std::max(maxy, MaxY(rects[i])); + } + + return MakeRect(minx, miny, maxx, maxy); +} + +} // namespace geom +} // namespace ircx diff --git a/src/operation/iRCX/source/module/database/HashFactory.hpp b/src/operation/iRCX/source/module/database/HashFactory.hpp new file mode 100644 index 000000000..ee21b4c3e --- /dev/null +++ b/src/operation/iRCX/source/module/database/HashFactory.hpp @@ -0,0 +1,178 @@ +// *************************************************************************************** +// 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 +#include +#include + +#include "Types.hpp" +namespace ircx { + +struct HashFactory { + [[nodiscard]] static constexpr std::size_t HashCombine(std::size_t seed, + std::size_t value) noexcept { + constexpr std::size_t kMagic = + (sizeof(std::size_t) == 8) + ? static_cast(0x9e3779b97f4a7c15ull) + : static_cast(0x9e3779b9ul); + return seed ^ (value + kMagic + (seed << 6) + (seed >> 2)); + } + + template > + static inline void HashAppend(std::size_t& seed, + const T& v) noexcept(noexcept(H{}(v))) { + seed = HashCombine(seed, static_cast(H{}(v))); + } + + // ============================================================ + // Generic pair keys + // ============================================================ + + template + struct DirectedPairKey { + T first{}; + T second{}; + + DirectedPairKey() = default; + DirectedPairKey(T a, T b) noexcept + : first(std::move(a)), second(std::move(b)) {} + + [[nodiscard]] bool operator==(const DirectedPairKey& o) const noexcept { + return first == o.first && second == o.second; + } + }; + + template > + struct UndirectedPairKey { + T first{}; + T second{}; + + UndirectedPairKey() = default; + + UndirectedPairKey(T a, T b) noexcept { + if (Less{}(b, a)) { + first = std::move(b); + second = std::move(a); + } else { + first = std::move(a); + second = std::move(b); + } + } + + [[nodiscard]] bool operator==(const UndirectedPairKey& o) const noexcept { + return first == o.first && second == o.second; + } + }; + + template > + struct PairKeyHash { + [[nodiscard]] std::size_t operator()(const PairKey& k) const + noexcept(noexcept(ElemHash{}(k.first)) && noexcept(ElemHash{}(k.second))) { + std::size_t seed = 0; + HashAppend(seed, k.first); + HashAppend(seed, k.second); + return seed; + } + }; + + // ============================================================ + // Project geometry hashes + // ============================================================ + + struct GtlPointHash { + [[nodiscard]] std::size_t operator()(const GtlPointI& p) const noexcept { + std::size_t seed = 0; + HashAppend(seed, gtl::x(p)); + HashAppend(seed, gtl::y(p)); + return seed; + } + }; + + struct GtlRectHash { + [[nodiscard]] std::size_t operator()(const GtlRectI& r) const noexcept { + std::size_t seed = 0; + HashAppend(seed, gtl::xl(r)); + HashAppend(seed, gtl::yl(r)); + HashAppend(seed, gtl::xh(r)); + HashAppend(seed, gtl::yh(r)); + return seed; + } + }; + + struct LayerPointHash { + [[nodiscard]] std::size_t operator()( + const std::pair& p) const noexcept { + std::size_t seed = 0; + HashAppend(seed, p.first); + HashAppend(seed, gtl::x(p.second)); + HashAppend(seed, gtl::y(p.second)); + return seed; + } + }; + + struct LayerRectHash { + [[nodiscard]] std::size_t operator()( + const std::pair& p) const noexcept { + std::size_t seed = 0; + HashAppend(seed, p.first); + HashAppend(seed, gtl::xl(p.second)); + HashAppend(seed, gtl::yl(p.second)); + HashAppend(seed, gtl::xh(p.second)); + HashAppend(seed, gtl::yh(p.second)); + return seed; + } + }; + + // ============================================================ + // Undirected UID pair + corner + // ============================================================ + + // struct UidPairCornerKey { + // Size uid_lo{kMaxSize}; + // Size uid_hi{kMaxSize}; + // Size corner{0}; + + // UidPairCornerKey() = default; + + // UidPairCornerKey(Size a, Size b, Size c) noexcept : corner(c) { + // if (a <= b) { + // uid_lo = a; + // uid_hi = b; + // } else { + // uid_lo = b; + // uid_hi = a; + // } + // } + + // [[nodiscard]] bool operator==(const UidPairCornerKey& o) const noexcept { + // return uid_lo == o.uid_lo && uid_hi == o.uid_hi && corner == o.corner; + // } + // }; + + // struct UidPairCornerKeyHash { + // [[nodiscard]] std::size_t operator()(const UidPairCornerKey& k) const noexcept { + // std::size_t seed = 0; + // HashAppend(seed, k.uid_lo); + // HashAppend(seed, k.uid_hi); + // HashAppend(seed, k.corner); + // return seed; + // } + // }; +}; + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/database/LayerTable.hpp b/src/operation/iRCX/source/module/database/LayerTable.hpp new file mode 100644 index 000000000..9c2f10e24 --- /dev/null +++ b/src/operation/iRCX/source/module/database/LayerTable.hpp @@ -0,0 +1,95 @@ +// *************************************************************************************** +// 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 +#include + +#include "Types.hpp" +namespace ircx { + +// LayerTable +// +// Centralizes all design ↔ process layer name/id mappings. +// +// Populated in three stages matching the RCX pipeline: +// 1. readCorner() → registerProcessLayer() +// 2. adaptDB() → registerDesignLayer() +// 3. readMapping() → registerMapping() +// +// All query methods throw std::out_of_range on a missing key. +class LayerTable { + public: + // Registration + + void registerDesignLayer(Size id, Str name) { + design_id_to_name_[id] = name; + design_name_to_id_[std::move(name)] = id; + } + + void registerProcessLayer(Size id, Str name) { + process_id_to_name_[id] = name; + process_name_to_id_[std::move(name)] = id; + } + + // Registers both directions of the design↔process name mapping. + void registerMapping(const Str& design_name, + const Str& process_name) { + design_to_process_name_[design_name] = process_name; + process_to_design_name_[process_name] = design_name; + } + + // Single-domain queries + + Size design_id(const Str& name) const { + return design_name_to_id_.at(name); + } + const Str& design_name(Size id) const { + return design_id_to_name_.at(id); + } + + Size process_id(const Str& name) const { + return process_name_to_id_.at(name); + } + const Str& process_name(Size id) const { + return process_id_to_name_.at(id); + } + + // Cross-domain queries + + Size design_to_process_id(Size design_id) const { + const Str& pname = design_to_process_name_.at(design_id_to_name_.at(design_id)); + return process_name_to_id_.at(pname); + } + + Size process_to_design_id(Size process_id) const { + const Str& dname = process_to_design_name_.at(process_id_to_name_.at(process_id)); + return design_name_to_id_.at(dname); + } + + private: + std::unordered_map design_id_to_name_; + std::unordered_map design_name_to_id_; + + std::unordered_map process_id_to_name_; + std::unordered_map process_name_to_id_; + + std::unordered_map design_to_process_name_; + std::unordered_map process_to_design_name_; +}; + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/database/LayoutData.hpp b/src/operation/iRCX/source/module/database/LayoutData.hpp new file mode 100644 index 000000000..12a65bbf6 --- /dev/null +++ b/src/operation/iRCX/source/module/database/LayoutData.hpp @@ -0,0 +1,120 @@ +// *************************************************************************************** +// 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 +#include +#include +#include + +#include "RoutingLayer.hpp" +#include "Types.hpp" +namespace ircx { + +// ============================================================ +// Segment +// ============================================================ +struct Segment { + Size layer_id{kMaxSize}; + GtlRectI rect; + + GtlPointI p0, p1; +}; + +// ============================================================ +// Patch +// ============================================================ +struct Patch { + Size layer_id{kMaxSize}; + GtlRectI rect; +}; + +// ============================================================ +// Pin +// ============================================================ +struct Pin { + Str name; // pin(inst:pin)/port name + + bool is_driver{false}; + bool is_input{false}; + bool is_output{false}; + + std::vector> layer_id_rects; + + bool is_port() const { + return name.find(':') == Str::npos; + } + + Str instance_name() const { + size_t pos = name.find(':'); + return name.substr(0, pos); + } + Str instance_pin_name() const { + size_t pos = name.find(':'); + return name.substr(pos+1); + } + Str port_name() const { + return name; + } +}; + +// ============================================================ +// Via +// ============================================================ +struct Via { + Str name; + + GtlPointI point; + // Top/Cut/Bottom layer rect + std::pair layer_rect_top; + std::pair layer_rect_cut; + std::pair layer_rect_btm; +}; + +// ============================================================ +// Net +// ============================================================ +struct Net { + Size id; + Str name; + std::vector segments; + std::vector patches; + std::vector vias; + std::vector pins; +}; + +struct LayoutData { + // Design metadata + Str design_name; + GtlRectI die_shape; + Dbu micron_to_dbu{1}; + + // Technology layers + std::map routing_layers; + + // Net metadata + // starting from 0. + std::vector net_vec; // net id is index of net_vec + + // Special-net geometry (power/ground, no connectivity graph needed) + Net special_net; + + // Helpers + Size regular_net_count() const { return net_vec.size(); } +}; + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/database/RoutingLayer.hpp b/src/operation/iRCX/source/module/database/RoutingLayer.hpp new file mode 100644 index 000000000..d4a295187 --- /dev/null +++ b/src/operation/iRCX/source/module/database/RoutingLayer.hpp @@ -0,0 +1,65 @@ +// *************************************************************************************** +// 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 "Types.hpp" +namespace ircx { + +class RoutingLayer { + public: + RoutingLayer() = default; + ~RoutingLayer() = default; + + RoutingLayer(const RoutingLayer&) = default; + RoutingLayer& operator=(const RoutingLayer&) = default; + RoutingLayer(RoutingLayer&&) = default; + RoutingLayer& operator=(RoutingLayer&&) = default; + + struct TrackInfo { + Dbu x0{}; + Dbu y0{}; + Dbu dx{}; + Dbu dy{}; + Size nx{}; + Size ny{}; + }; + + void set_layer_id(Size v) { layer_id_ = v; } + Size layer_id() const { return layer_id_; } + + void set_layer_name(Str v) { layer_name_ = std::move(v); } + const Str& layer_name() const { return layer_name_; } + + void set_layer_width(Dbu v) { layer_width_ = v; } + Dbu layer_width() const { return layer_width_; } + + void set_prefer_horz(bool v) { prefer_horz_ = v; } + bool is_prefer_horz() const { return prefer_horz_; } + + void set_track_info(const TrackInfo& v) { track_info_ = v; } + const TrackInfo& track_info() const { return track_info_; } + + private: + Size layer_id_{}; + Str layer_name_{}; + Dbu layer_width_{}; + + bool prefer_horz_{false}; + TrackInfo track_info_{}; +}; + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/database/SpefContext.hpp b/src/operation/iRCX/source/module/database/SpefContext.hpp new file mode 100644 index 000000000..7272830c0 --- /dev/null +++ b/src/operation/iRCX/source/module/database/SpefContext.hpp @@ -0,0 +1,44 @@ +// *************************************************************************************** +// 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 +#include +#include + +#include "Types.hpp" +namespace ircx { + +// SpefContext +// +// SPEF output metadata: compressed name tables and instance-to-cell mapping. +// Previously stored inside Database; now a standalone value type populated +// by the layout adapter. +// +struct SpefContext { + // Ordered lists for SPEF *NAME_MAP output. + // Index 0 = first entry; SPEF IDs start at *1. + std::vector net_names; + std::vector port_names; + std::vector port_io; + std::vector instance_names; + + // instance name → cell name (for *CELL entries) + std::map instance_to_cell; +}; + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/database/Types.hpp b/src/operation/iRCX/source/module/database/Types.hpp new file mode 100644 index 000000000..8820711ff --- /dev/null +++ b/src/operation/iRCX/source/module/database/Types.hpp @@ -0,0 +1,116 @@ +// *************************************************************************************** +// 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 +#include +#include +#include +#include +#include +namespace ircx { + +// ------------------------- +// Fundamental scalar types +// ------------------------- +using U8 = std::uint8_t; +using U16 = std::uint16_t; +using U32 = std::uint32_t; +using U64 = std::uint64_t; + +using I8 = std::int8_t; +using I16 = std::int16_t; +using I32 = std::int32_t; +using I64 = std::int64_t; + +using F32 = float; +using F64 = double; + +using Str = std::string; +using Size = std::size_t; + +using Dbu = I32; // database unit +using Micron = F64; // micron unit + +template +inline constexpr int BitNum = std::numeric_limits::digits; + +template +inline constexpr T MaxVal = std::numeric_limits::max(); + +template +inline constexpr T MinVal = std::numeric_limits::min(); + +template +inline constexpr T InfinityVal = std::numeric_limits::infinity(); + +// Common "invalid" sentinels +inline constexpr Dbu kMaxDbu = MaxVal; +inline constexpr Size kMaxSize = MaxVal; +inline constexpr Micron kMaxMicron = MaxVal; + +// Sentinel net_id for special-net (power/ground) edges. +// Distinct from all valid net ids and from kMaxSize. +inline constexpr Size kSpecialNetId = kMaxSize - 1; + +// ------------------------- +// LineSegment geometry +// ------------------------- + +// A wire segment projected onto one axis. +// is_horz : true → horizontal wire (fixed coord is Y, range is X) +// fixed : the constant coordinate (Y for horz, X for vert) +// a0, a1 : start / end along the wire direction +template +struct LineSegment { + bool is_horz{false}; + T fixed{}; + T a0{}; + T a1{}; +}; + +using LineSegmentI = LineSegment; + +// ------------------------- +// Boost namespace aliases +// ------------------------- +namespace bg = boost::geometry; +namespace bgi = boost::geometry::index; +namespace gtl = boost::polygon; + +// ------------------------- +// Geometry type aliases +// ------------------------- +using Point = bg::model::point; +using Box = bg::model::box; +using Polygon = bg::model::polygon; + +using GtlPointI = gtl::point_data; +using GtlRectI = gtl::rectangle_data; +using GtlPolyI = gtl::polygon_90_data; +using GtlPolysetI = gtl::polygon_90_set_data; + +using GtlPointF = gtl::point_data; +using GtlRectF = gtl::rectangle_data; +using GtlPolyF = gtl::polygon_90_data; +using GtlPolysetF = gtl::polygon_90_set_data; + +using Dir1d = gtl::direction_1d_enum; +using Dir2d = gtl::direction_2d_enum; +using Ori2d = gtl::orientation_2d_enum; + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/environment/CMakeLists.txt b/src/operation/iRCX/source/module/environment/CMakeLists.txt new file mode 100644 index 000000000..95dd9aeec --- /dev/null +++ b/src/operation/iRCX/source/module/environment/CMakeLists.txt @@ -0,0 +1,14 @@ +add_library(ircx_environment Environment.cpp) + +target_link_libraries(ircx_environment + PUBLIC + ircx_topology + log + PRIVATE + OpenMP::OpenMP_CXX +) + +target_include_directories(ircx_environment + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/src/operation/iRCX/source/module/environment/EnvPool.hpp b/src/operation/iRCX/source/module/environment/EnvPool.hpp new file mode 100644 index 000000000..0958dffb7 --- /dev/null +++ b/src/operation/iRCX/source/module/environment/EnvPool.hpp @@ -0,0 +1,92 @@ +// *************************************************************************************** +// 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 +#include +#include + +#include "Types.hpp" +namespace ircx { + +inline constexpr F32 kDefaultWindowUm = 2; // μm +inline constexpr F32 kDefaultBucketUm = 5; // μm + +class TopoEdge; + +struct CrossOverlapSub { + Dbu a0{0}; + Dbu a1{0}; + Size abv_layer{0}; // absolute layer; 0 = none + Size blw_layer{0}; // absolute layer; 0 = SUBSTRATE +}; + + +struct DiagCoupSub { + Dbu a0 = 0; + Dbu a1 = 0; + TopoEdge* neighbor = nullptr; + Dbu dist = 0; // center-to-center, fixed direction, dbu + int16_t layer_delta = 0; // target layer = this_edge.layer + layer_delta +}; + +struct EnvInterval { + Dbu a0{0}; + Dbu a1{0}; + + Dbu lo_spacing{kMaxDbu}; // center-to-center; kMaxDbu = no neighbor + Dbu hi_spacing{kMaxDbu}; + + const TopoEdge* lo_adjacent{nullptr}; + const TopoEdge* hi_adjacent{nullptr}; + + std::vector cross_segs; + std::vector diag_segs; +}; + +class EnvPool { // of each net + public: + EnvPool() = default; + ~EnvPool() = default; + + void append_edge_env_interval_pool(std::vector v) { + edge_interval_ranges_.emplace_back(env_interval_pool_.size(), v.size()); + + env_interval_pool_.insert(env_interval_pool_.end(), + std::make_move_iterator(v.begin()), + std::make_move_iterator(v.end())); + } + + std::span edge_env_interval_pool(Size edge_id) const { + if (edge_id >= edge_interval_ranges_.size()) { + return {}; + } + const auto& [offset, length] = edge_interval_ranges_[edge_id]; + return std::span(env_interval_pool_.data() + offset, length); + } + + void clear() { + env_interval_pool_.clear(); + edge_interval_ranges_.clear(); + } + + private: + std::vector env_interval_pool_; // 同层 + std::vector> edge_interval_ranges_; +}; + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/environment/Environment.cpp b/src/operation/iRCX/source/module/environment/Environment.cpp new file mode 100644 index 000000000..446058697 --- /dev/null +++ b/src/operation/iRCX/source/module/environment/Environment.cpp @@ -0,0 +1,341 @@ +// *************************************************************************************** +// 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 +#include +#include +#include + +#include "Environment.hpp" +#include "LayoutData.hpp" +#include "TopoPool.hpp" +#include "IntervalEngine.hpp" +namespace ircx { + +void Environment::buildTracks() +{ + LOG_FATAL_IF(!layout_data_) << "LayoutData not initialized."; + + const std::map& routing_layers = layout_data_->routing_layers; + + const GtlRectI& rect = layout_data_->die_shape; + Dbu die_x0 = geom::MinX(rect); + Dbu die_y0 = geom::MinY(rect); + Dbu die_x1 = geom::MaxX(rect); + Dbu die_y1 = geom::MaxY(rect); + Dbu die_dx = geom::DeltaX(rect); + Dbu die_dy = geom::DeltaY(rect); + + Dbu bucket_dlt = static_cast(bucket_size_um_ * layout_data_->micron_to_dbu); + + // init + for (const auto& [lid, layer] : routing_layers) { + bool is_horz = layer.is_prefer_horz(); + const RoutingLayer::TrackInfo& ti = layer.track_info(); + Track track; + + Dbu track_ori = is_horz ? ti.y0 : ti.x0; + Dbu track_num = is_horz ? ti.ny : ti.nx; + Dbu track_dlt = is_horz ? ti.dy : ti.dx; + + if (is_horz) { + while(track_ori > die_y0) { + track_ori -= track_dlt; + track_num += 1; + } + while(track_ori + track_dlt * track_num <= die_y1) track_num += 1; + } else { + while(track_ori > die_x0) { + track_ori -= track_dlt; + track_num += 1; + } + while(track_ori + track_dlt * track_num <= die_x1) track_num += 1; + } + + track.set_track_ori(track_ori); + track.set_track_num(track_num); + track.set_track_dlt(track_dlt); + + Dbu bucket_len = is_horz ? die_dx : die_dy; + Dbu bucket_num = (bucket_len + bucket_dlt - 1) / bucket_dlt; + track.set_bucket_ori(is_horz ? die_x0 : die_y0); + track.set_bucket_num(bucket_num); + track.set_bucket_dlt(bucket_dlt); + + track.initTrack(); + layer_to_track_[lid] = std::move(track); + } + + // build: regular edges + const std::vector& edge_pool = topo_pool_->edge_pool(); + for (const TopoEdge& edge : 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_track_.at(lid).addEdge(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_track_.at(lid).addEdge(edge); + } + } +} + +void Environment::buildPixels() +{ + LOG_FATAL_IF(!layout_data_) << "LayoutData not initialized."; + + const std::map& routing_layers = layout_data_->routing_layers; + + const GtlRectI& rect = layout_data_->die_shape; + Dbu die_x0 = geom::MinX(rect); + Dbu die_y0 = geom::MinY(rect); + Dbu die_x1 = geom::MaxX(rect); + Dbu die_y1 = geom::MaxY(rect); + + // init + for (const auto& [lid, layer] : routing_layers) { + const RoutingLayer::TrackInfo& ti = layer.track_info(); + Pixel pixel; + + Dbu x0 = ti.x0; + Dbu y0 = ti.y0; + Dbu nx = ti.nx; + Dbu ny = ti.ny; + Dbu dx = ti.dx; + Dbu dy = ti.dy; + + while(x0 > die_x0) { + x0 -= dx; + nx += 1; + } + while(x0 + dx * nx <= die_x1) nx += 1; + + while(y0 > die_y0) { + y0 -= dy; + ny += 1; + } + while(y0 + dy * ny <= die_y1) ny += 1; + + pixel.set_x0(x0); + pixel.set_nx(nx); + pixel.set_dx(dx); + + pixel.set_y0(y0); + pixel.set_ny(ny); + pixel.set_dy(dy); + + pixel.initPixel(); + layer_to_pixel_[lid] = std::move(pixel); + } + + // build: regular edges + for (const TopoEdge& edge : topo_pool_->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); + } + } + + // 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); + } + } +} + +void Environment::buildSearchTrackNumMap() +{ + const std::map& routing_layers = layout_data_->routing_layers; + + 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(); + layer_to_search_track_num_[lid] = 10; + } +} + +void Environment::buildNetEnvPools() +{ + buildTracks(); + buildPixels(); + buildSearchTrackNumMap(); + + Size net_num = layout_data_->regular_net_count(); + 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; + out.a1 += ext; + return out; + }; + + auto clip_cross_segs = [](const std::vector& full, Dbu a0, Dbu a1) { + std::vector clipped; + if (!(a0 < a1)) { + return clipped; + } + + for (const auto& seg : full) { + const Dbu s = std::max(a0, seg.a0); + const Dbu t = std::min(a1, seg.a1); + if (!(s < t)) { + continue; + } + + if (!clipped.empty() && + clipped.back().a1 == s && + clipped.back().blw_layer == seg.blw_layer && + clipped.back().abv_layer == seg.abv_layer) { + clipped.back().a1 = t; + } else { + CrossOverlapSub sub; + sub.a0 = s; + sub.a1 = t; + sub.blw_layer = seg.blw_layer; + sub.abv_layer = seg.abv_layer; + clipped.push_back(sub); + } + } + + return clipped; + }; + + TrackOverlapMerge track_merger; + PixelOverlapMerge pixel_merger; + + auto collect_cross_side = [&](const LineSegmentI& full_seg, Size base_lid, bool search_up) { + std::vector bufs; + + for (Size delta = 1; delta <= cross_layer_; ++delta) { + Size cand_lid = 0; + + if (search_up) { + if (base_lid > max_lid || max_lid - base_lid < delta) { + break; + } + cand_lid = base_lid + delta; + } else { + if (base_lid < min_lid || base_lid - min_lid < delta) { + break; + } + cand_lid = base_lid - delta; + } + + auto it_layer = routing_layers.find(cand_lid); + if (it_layer == routing_layers.end()) { + continue; + } + + // 与 full_seg 平行的层直接跳过;只保留正交层 + if (it_layer->second.is_prefer_horz() == full_seg.is_horz) { + continue; + } + + auto it_pixel = layer_to_pixel_.find(cand_lid); + if (it_pixel == layer_to_pixel_.end()) { + continue; + } + + std::vector segs = it_pixel->second.get_overlap(full_seg); + if (segs.empty()) { + continue; + } + + // delta 从小到大,因此输入顺序天然是“近到远” + PixelOverlapMerge::LayerPixelOverlaps in; + in.layer = cand_lid; + in.segs = std::move(segs); + bufs.push_back(std::move(in)); + } + + return bufs; + }; + + #pragma omp parallel for schedule(dynamic) + for (Size nid = 0; nid < net_num; nid++) { + EnvPool& net_env_pool = net_env_pools_[nid]; + net_env_pool.clear(); + + for (const TopoEdge& edge : topo_pool_->net_edges(nid)) { + if (edge.is_via()) { + net_env_pool.append_edge_env_interval_pool({}); // placeholder to keep index aligned with TopoPool + continue; + } + + const Size lid = edge.layer_id(); + const LineSegmentI s = 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); + std::vector track_ov_dn = + layer_to_track_[lid].get_overlap(s, -layer_to_search_track_num_[lid], nullptr); + + std::vector out; + track_merger.compute(s.a0, s.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); + + // 对整条 s 一次性 merge cross-over + std::vector cross_full; + pixel_merger.compute(s.a0, s.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); + } + + net_env_pool.append_edge_env_interval_pool(std::move(out)); + } + } +} + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/environment/Environment.hpp b/src/operation/iRCX/source/module/environment/Environment.hpp new file mode 100644 index 000000000..0e2d7fb2f --- /dev/null +++ b/src/operation/iRCX/source/module/environment/Environment.hpp @@ -0,0 +1,79 @@ +// *************************************************************************************** +// 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 +#include + +#include "Types.hpp" +#include "EnvPool.hpp" +#include "Pixel.hpp" +#include "Track.hpp" +namespace ircx { + +class TopoPool; +class LayoutData; + +class Environment final { + public: + // Meyer's singleton + static Environment& getOrCreateInst() { + static Environment inst; + return inst; + } + + // Disallow copy/move + Environment(const Environment&) = delete; + Environment& operator=(const Environment&) = delete; + Environment(Environment&&) = delete; + Environment& operator=(Environment&&) = delete; + + void set_layout_data(const LayoutData* v) { layout_data_ = v; } + void set_topo_pool(const TopoPool* v) { topo_pool_ = v; } + + void buildNetEnvPools(); + + // getter + const std::vector& net_env_pools() const { return net_env_pools_; } + const EnvPool& net_env_pool(Size net_id) const { + LOG_FATAL_IF(net_id >= net_env_pools_.size()) << "net_id out of range."; + return net_env_pools_[net_id]; + } + private: + Environment() = default; + ~Environment() = default; + + void buildTracks(); + void buildPixels(); + void buildSearchTrackNumMap(); + + const LayoutData* layout_data_{nullptr}; + const TopoPool* topo_pool_{nullptr}; + + F32 bucket_size_um_{kDefaultBucketUm}; + F32 window_size_um_{kDefaultWindowUm}; + + Size cross_layer_{3}; + + std::map layer_to_pixel_; + std::map layer_to_track_; // preferred routing direction only + std::map layer_to_search_track_num_; + + std::vector net_env_pools_; +}; + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/environment/IntervalEngine.hpp b/src/operation/iRCX/source/module/environment/IntervalEngine.hpp new file mode 100644 index 000000000..255098f09 --- /dev/null +++ b/src/operation/iRCX/source/module/environment/IntervalEngine.hpp @@ -0,0 +1,414 @@ +// *************************************************************************************** +// 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 +#include +#include + +#include "Types.hpp" +#include "TopoPool.hpp" +#include "EnvPool.hpp" +#include "log/Log.hh" +namespace ircx { + +class TrackOverlapMerge +{ + public: + void compute(Dbu query_a0, + Dbu query_a1, + const std::vector& dn_in, + const std::vector& up_in, + std::vector& out) const + { + if (query_a0 > query_a1) { + std::swap(query_a0, query_a1); + } + + out.clear(); + if (!(query_a0 < query_a1)) { + return; + } + + std::vector dn; + std::vector up; + + normalizeSide(query_a0, query_a1, dn_in, dn); + normalizeSide(query_a0, query_a1, up_in, up); + + mergeTwoSides(dn, up, out); + } + + private: + static TrackOverlap makeNullOverlap(Dbu a0, Dbu a1) + { + TrackOverlap ov; + ov.a0 = a0; + ov.a1 = a1; + ov.sp = kMaxDbu; + ov.edge = nullptr; + return ov; + } + + static void emitTrackOverlap(std::vector& out, const TrackOverlap& ov) + { + if (!(ov.a0 < ov.a1)) { + return; + } + + if (!out.empty() && + out.back().a1 == ov.a0 && + out.back().edge == ov.edge && + out.back().sp == ov.sp) { + out.back().a1 = ov.a1; + return; + } + + out.push_back(ov); + } + + void normalizeSide(Dbu query_a0, + Dbu query_a1, + const std::vector& in, + std::vector& out) const + { + out.clear(); + + std::vector tmp; + tmp.reserve(in.size()); + + for (const auto& ov : in) { + const Dbu a0 = std::max(query_a0, ov.a0); + const Dbu a1 = std::min(query_a1, ov.a1); + if (!(a0 < a1)) { + continue; + } + + TrackOverlap clipped = ov; + clipped.a0 = a0; + clipped.a1 = a1; + tmp.push_back(clipped); + } + + std::sort(tmp.begin(), tmp.end(), [](const TrackOverlap& lhs, const TrackOverlap& rhs) { + if (lhs.a0 != rhs.a0) { + return lhs.a0 < rhs.a0; + } + if (lhs.a1 != rhs.a1) { + return lhs.a1 < rhs.a1; + } + if (lhs.edge != rhs.edge) { + return lhs.edge < rhs.edge; + } + return lhs.sp < rhs.sp; + }); + + Dbu cursor = query_a0; + for (const auto& ov : tmp) { + LOG_FATAL_IF(ov.a0 < cursor) + << "TrackOverlapMerge: same-side overlaps are not allowed. " + << "prev_end=" << cursor << ", cur=[" << ov.a0 << ", " << ov.a1 << ")"; + + if (cursor < ov.a0) { + emitTrackOverlap(out, makeNullOverlap(cursor, ov.a0)); + } + + emitTrackOverlap(out, ov); + cursor = ov.a1; + } + + if (cursor < query_a1) { + emitTrackOverlap(out, makeNullOverlap(cursor, query_a1)); + } + + if (out.empty()) { + out.push_back(makeNullOverlap(query_a0, query_a1)); + } + } + + static void emitOutput(std::vector& out, + Dbu a0, + Dbu a1, + const TrackOverlap& dn, + const TrackOverlap& up) + { + if (!(a0 < a1)) { + return; + } + + const Dbu l_sp = dn.sp; + const Dbu h_sp = up.sp; + const TopoEdge* l_edge = dn.edge; + const TopoEdge* h_edge = up.edge; + + if (!out.empty() && + out.back().a1 == a0 && + out.back().lo_adjacent == l_edge && + out.back().hi_adjacent == h_edge && + out.back().lo_spacing == l_sp && + out.back().hi_spacing == h_sp) { + out.back().a1 = a1; + return; + } + + EnvInterval iv; + iv.a0 = a0; + iv.a1 = a1; + iv.lo_adjacent = l_edge; + iv.hi_adjacent = h_edge; + iv.lo_spacing = l_sp; + iv.hi_spacing = h_sp; + out.push_back(iv); + } + + static void mergeTwoSides(const std::vector& dn, + const std::vector& up, + std::vector& out) + { + out.clear(); + if (dn.empty() || up.empty()) { + return; + } + + Size i = 0; + Size j = 0; + + while (i < dn.size() && j < up.size()) { + const Dbu s = std::max(dn[i].a0, up[j].a0); + const Dbu t = std::min(dn[i].a1, up[j].a1); + + if (s < t) { + emitOutput(out, s, t, dn[i], up[j]); + } + + if (dn[i].a1 == t) { + ++i; + } + if (up[j].a1 == t) { + ++j; + } + } + } +}; + +class PixelOverlapMerge +{ + public: + struct LayerPixelOverlaps { + Size layer{0}; // 0 means invalid / absent + std::vector segs; // higher priority comes first in input order + }; + + void compute(Dbu query_a0, + Dbu query_a1, + const std::vector& dn_inputs, + const std::vector& up_inputs, + std::vector& out) const + { + if (query_a0 > query_a1) { + std::swap(query_a0, query_a1); + } + + out.clear(); + if (!(query_a0 < query_a1)) { + return; + } + + std::vector dn_norm; + std::vector up_norm; + dn_norm.reserve(dn_inputs.size()); + up_norm.reserve(up_inputs.size()); + + for (const auto& in : dn_inputs) { + if (in.layer == 0) { + continue; + } + NormalizedLayerPixelOverlaps ni; + ni.layer = in.layer; + normalizeOne(query_a0, query_a1, in.segs, ni.segs); + if (!ni.segs.empty()) { + dn_norm.push_back(std::move(ni)); + } + } + + for (const auto& in : up_inputs) { + if (in.layer == 0) { + continue; + } + NormalizedLayerPixelOverlaps ni; + ni.layer = in.layer; + normalizeOne(query_a0, query_a1, in.segs, ni.segs); + if (!ni.segs.empty()) { + up_norm.push_back(std::move(ni)); + } + } + + std::vector bp; + bp.reserve(2 + countBreakpoints(dn_norm) + countBreakpoints(up_norm)); + bp.push_back(query_a0); + bp.push_back(query_a1); + + addBreakpoints(dn_norm, bp); + addBreakpoints(up_norm, bp); + + std::sort(bp.begin(), bp.end()); + bp.erase(std::unique(bp.begin(), bp.end()), bp.end()); + + if (bp.size() < 2) { + return; + } + + std::vector dn_cursor(dn_norm.size(), 0); + std::vector up_cursor(up_norm.size(), 0); + + for (Size k = 0; k + 1 < bp.size(); ++k) { + const Dbu a0 = bp[k]; + const Dbu a1 = bp[k + 1]; + if (!(a0 < a1)) { + continue; + } + + const Size blw_layer = firstCoveringLayer(dn_norm, dn_cursor, a0, a1); + const Size abv_layer = firstCoveringLayer(up_norm, up_cursor, a0, a1); + + emit(a0, a1, blw_layer, abv_layer, out); + } + } + + private: + struct NormalizedLayerPixelOverlaps { + Size layer{0}; + std::vector segs; + }; + + static Size countBreakpoints(const std::vector& inputs) + { + Size n = 0; + for (const auto& in : inputs) { + n += static_cast(in.segs.size() * 2); + } + return n; + } + + static void addBreakpoints(const std::vector& inputs, + std::vector& bp) + { + for (const auto& in : inputs) { + for (const auto& seg : in.segs) { + bp.push_back(seg.a0); + bp.push_back(seg.a1); + } + } + } + + static void normalizeOne(Dbu query_a0, + Dbu query_a1, + const std::vector& in, + std::vector& out) + { + out.clear(); + out.reserve(in.size()); + + for (const auto& ov : in) { + const Dbu a0 = std::max(query_a0, ov.a0); + const Dbu a1 = std::min(query_a1, ov.a1); + if (a0 < a1) { + out.push_back(PixelOverlap{a0, a1}); + } + } + + std::sort(out.begin(), out.end(), [](const PixelOverlap& lhs, const PixelOverlap& rhs) { + if (lhs.a0 != rhs.a0) { + return lhs.a0 < rhs.a0; + } + return lhs.a1 < rhs.a1; + }); + + std::vector merged; + merged.reserve(out.size()); + + for (const auto& ov : out) { + if (merged.empty() || merged.back().a1 < ov.a0) { + merged.push_back(ov); + } else { + merged.back().a1 = std::max(merged.back().a1, ov.a1); + } + } + + out.swap(merged); + } + + static void advanceCursor(const std::vector& segs, Size& idx, Dbu x) + { + while (idx < segs.size() && segs[idx].a1 <= x) { + ++idx; + } + } + + static bool covers(const std::vector& segs, Size idx, Dbu a0, Dbu a1) + { + return idx < segs.size() && segs[idx].a0 < a1 && segs[idx].a1 > a0; + } + + static Size firstCoveringLayer(const std::vector& inputs, + std::vector& cursors, + Dbu a0, + Dbu a1) + { + for (Size i = 0; i < inputs.size(); ++i) { + advanceCursor(inputs[i].segs, cursors[i], a0); + if (covers(inputs[i].segs, cursors[i], a0, a1)) { + return inputs[i].layer; + } + } + return Size{0}; + } + + static void emit(Dbu a0, + Dbu a1, + Size blw_layer, + Size abv_layer, + std::vector& out) + { + if (!(a0 < a1)) { + return; + } + + // The uncovered span is represented implicitly by gaps between emitted + // cross-over segments and handled by downstream consumers as substrate/none. + if (blw_layer == 0 && abv_layer == 0) { + return; + } + + if (!out.empty() && + out.back().a1 == a0 && + out.back().blw_layer == blw_layer && + out.back().abv_layer == abv_layer) { + out.back().a1 = a1; + return; + } + + CrossOverlapSub sub; + sub.a0 = a0; + sub.a1 = a1; + sub.blw_layer = blw_layer; + sub.abv_layer = abv_layer; + out.push_back(sub); + } +}; + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/environment/Pixel.hpp b/src/operation/iRCX/source/module/environment/Pixel.hpp new file mode 100644 index 000000000..5925402b1 --- /dev/null +++ b/src/operation/iRCX/source/module/environment/Pixel.hpp @@ -0,0 +1,259 @@ +// *************************************************************************************** +// 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 +#include + +#include "TopoPool.hpp" +#include "log/Log.hh" +namespace ircx { + +// Perpendicular Overlap +struct PixelOverlap +{ + Dbu a0{0}; + Dbu a1{0}; + bool empty() const { return a1 <= a0; } +}; + +class Pixel +{ + public: + Pixel() = default; + ~Pixel() = default; + + // getter + Dbu x0() const { return x0_; } + Dbu y0() const { return y0_; } + Dbu nx() const { return nx_; } + Dbu ny() const { return ny_; } + Dbu dx() const { return dx_; } + Dbu dy() const { return dy_; } + + // setter + void set_x0(Dbu x0) { x0_ = x0; } + void set_y0(Dbu y0) { y0_ = y0; } + void set_nx(Dbu nx) { nx_ = nx; } + void set_ny(Dbu ny) { ny_ = ny; } + void set_dx(Dbu dx) { dx_ = dx; } + void set_dy(Dbu dy) { dy_ = dy; } + + // coordinate mapping + Dbu coordToXIdx(Dbu coord) const { return (coord - x0_) / dx_; } + Dbu coordToYIdx(Dbu coord) const { return (coord - y0_) / dy_; } + + Dbu idxToXCoord(Dbu idx) const { return x0_ + idx * dx_; } + Dbu idxToYCoord(Dbu idx) const { return y0_ + idx * dy_; } + + void initPixel() + { + LOG_FATAL_IF(nx_ <= 0 || ny_ <= 0 || dx_ <= 0 || dy_ <= 0) + << "Grid parameters are not initialized!"; + + pixel_.assign(nx_, std::vector(ny_, false)); + } + + void addEdge(const TopoEdge& edge) + { + if (pixel_.empty() || pixel_.front().empty()) { + initPixel(); + } + + const GtlRectI& rect = edge.shape(); + + Dbu x0 = geom::MinX(rect); + Dbu y0 = geom::MinY(rect); + Dbu x1 = geom::MaxX(rect); + Dbu y1 = geom::MaxY(rect); + + if (x0 >= x1 || y0 >= y1) { + return; + } + + Dbu x_idx0 = coordToXIdx(x0); + Dbu y_idx0 = coordToYIdx(y0); + Dbu x_idx1 = coordToXIdx(x1); + Dbu y_idx1 = coordToYIdx(y1); + + if (!xValid(x_idx0) || !yValid(y_idx0) || !xValid(x_idx1) || !yValid(y_idx1)) { + LOG_ERROR << "The x/y index is out of range!"; + return; + } + + for (Dbu i = x_idx0; i <= x_idx1; ++i) { + for (Dbu j = y_idx0; j <= y_idx1; ++j) { + pixel_[i][j] = true; + } + } + } + + std::vector get_overlap(const LineSegmentI& line_seg) const + { + std::vector ret; + if (pixel_.empty() || pixel_.front().empty()) { + return ret; + } + + bool is_horz = line_seg.is_horz; + Dbu fixed = line_seg.fixed; + Dbu a0 = line_seg.a0; + Dbu a1 = line_seg.a1; + + normalizeInterval(a0, a1); + + if (is_horz) { + const Dbu fixed_y_idx = coordToYIdx(fixed); + if (!yValid(fixed_y_idx)) { + return ret; + } + + return collectConductorRuns( + a0, + a1, + [&](Dbu coord) { return coordToXIdx(coord); }, + [&](Dbu idx) { return xValid(idx); }, + [&](Dbu idx) { return pixel_[idx][fixed_y_idx]; }, + [&](Dbu idx) { return idxToXCoord(idx); }); + } else { + const Dbu fixed_x_idx = coordToXIdx(fixed); + if (!xValid(fixed_x_idx)) { + return ret; + } + + return collectConductorRuns( + a0, + a1, + [&](Dbu coord) { return coordToYIdx(coord); }, + [&](Dbu idx) { return yValid(idx); }, + [&](Dbu idx) { return pixel_[fixed_x_idx][idx]; }, + [&](Dbu idx) { return idxToYCoord(idx); }); + } + } + + private: + static void normalizeInterval(Dbu& a0, Dbu& a1) + { + if (a0 > a1) { + std::swap(a0, a1); + } + } + + template + std::vector collectConductorRuns(Dbu a0, + Dbu a1, + CoordToIdx coord_to_idx, + IdxValid idx_valid, + CellGetter cell_getter, + IdxToCoord idx_to_coord) const + { + std::vector ret; + if (a0 >= a1) return ret; + + const Dbu a0_idx = coord_to_idx(a0); + const Dbu a1_idx = coord_to_idx(a1); + if (a0_idx > a1_idx) return ret; + + auto clamp_sequence_bounds = [&](Dbu coord_lo, Dbu coord_hi) { + PixelOverlap seq; + coord_lo = std::max(coord_lo, a0); + coord_hi = std::min(coord_hi, a1); + seq.a0 = coord_lo; + seq.a1 = coord_hi; + return seq; + }; + + auto cell_or_empty = [&](Dbu idx) -> bool { + return idx_valid(idx) ? cell_getter(idx) : false; + }; + + 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) { + return; + } + + const Dbu lo = idx_to_coord(start_idx); + const Dbu hi = idx_to_coord(end_idx); + + PixelOverlap seq = clamp_sequence_bounds(lo, hi); + if (!seq.empty()) { + ret.push_back(seq); + } + }; + + for (Dbu idx = a0_idx + 1; idx <= a1_idx; ++idx) { + 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); + } + } + + run_start = idx; + current_type = cell; + } + } + + 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); + } + } + + return ret; + } + + +private: + std::vector> pixel_; // true: conductor, false: empty + + Dbu x0_{0}, y0_{0}; + Dbu nx_{0}, ny_{0}; + Dbu dx_{0}, dy_{0}; + + bool xValid(Dbu x) const { return 0 <= x && x < nx_; } + bool yValid(Dbu y) const { return 0 <= y && y < ny_; } +}; + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/environment/Track.hpp b/src/operation/iRCX/source/module/environment/Track.hpp new file mode 100644 index 000000000..e2c05f602 --- /dev/null +++ b/src/operation/iRCX/source/module/environment/Track.hpp @@ -0,0 +1,537 @@ +// *************************************************************************************** +// 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 +#include +#include +#include +#include + +#include "Types.hpp" +#include "TopoPool.hpp" +#include "log/Log.hh" +namespace ircx { + +// Parallel Overlap +struct TrackOverlap { + Dbu a0{0}; + Dbu a1{0}; + + Dbu sp{kMaxDbu}; // unsigned spacing = |current_fixed - coord| + // edge == nullptr means this interval is not covered by any matched edge. + const TopoEdge* edge{nullptr}; +}; + +struct OverlapWidenContext { + Dbu track_distance; // |current_track_idx - base_track_idx| + Dbu overlap_len; // current raw overlap length before widening + const TopoEdge* edge; // matched edge +}; + +class Track +{ + public: + using OverlapWidenFunc = std::function; + + private: + struct EnvInterval { + Dbu a0; + Dbu a1; + }; + + struct SearchContext { + Dbu coord{0}; + Dbu base_track_idx{0}; + Dbu query_a0{0}; + Dbu query_a1{0}; + int step{0}; + const OverlapWidenFunc* widen_func{nullptr}; + }; + + struct TopoEdgeFixedLess + { + using is_transparent = void; + + bool operator()(const TopoEdge* lhs, const TopoEdge* rhs) const + { + if (lhs == rhs) { + return false; + } + if (lhs == nullptr || rhs == nullptr) { + return lhs < rhs; + } + if (lhs->fixed() != rhs->fixed()) { + return lhs->fixed() < rhs->fixed(); + } + return lhs < rhs; + } + + bool operator()(const TopoEdge* lhs, Dbu rhs_fixed) const + { + return lhs->fixed() < rhs_fixed; + } + + bool operator()(Dbu lhs_fixed, const TopoEdge* rhs) const + { + return lhs_fixed < rhs->fixed(); + } + }; + + using EdgeSet = std::set; + + public: + Track() = default; + ~Track() = default; + + // getter + Dbu track_ori() const { return track_ori_; } + Dbu track_num() const { return track_num_; } + Dbu track_dlt() const { return track_dlt_; } + Dbu bucket_ori() const { return bucket_ori_; } + Dbu bucket_num() const { return bucket_num_; } + Dbu bucket_dlt() const { return bucket_dlt_; } + + // setter + void set_track_ori(Dbu v) { track_ori_ = v; } + void set_track_num(Dbu v) { track_num_ = v; } + void set_track_dlt(Dbu v) { track_dlt_ = v; } + void set_bucket_ori(Dbu v) { bucket_ori_ = v; } + void set_bucket_num(Dbu v) { bucket_num_ = v; } + void set_bucket_dlt(Dbu v) { bucket_dlt_ = v; } + + // coordinate mapping + Dbu coordToTrack(Dbu coord) const { return (coord - track_ori_) / track_dlt_; } + Dbu coordToBucket(Dbu coord) const { return (coord - bucket_ori_) / bucket_dlt_; } + + void initTrack() + { + LOG_FATAL_IF(track_num_ <= 0 || track_dlt_ <= 0) << "Track parameters are not initialized!"; + LOG_FATAL_IF(bucket_num_ <= 0 || bucket_dlt_ <= 0) << "Bucket parameters are not initialized!"; + + track_buckets_.assign(track_num_, std::vector(bucket_num_)); + } + + void addEdge(const TopoEdge& edge) + { + Dbu a0 = edge.a0(); + Dbu a1 = edge.a1(); + normalizeInterval(a0, a1); + + const Dbu track_idx = coordToTrack(edge.fixed()); + const Dbu bucket_idx0 = coordToBucket(a0); + const Dbu bucket_idx1 = coordToBucket(a1 - 1); + + if (!trackValid(track_idx) || !bucketValid(bucket_idx0) || !bucketValid(bucket_idx1)) { + LOG_ERROR << "The track/bucket index is out of range!"; + return; + } + + for (Dbu b = bucket_idx0; b <= bucket_idx1; ++b) { + track_buckets_[track_idx][b].insert(&edge); + } + } + + // search_track_num > 0: + // search upward for search_track_num tracks, including the track containing coord; + // every returned non-null edge must satisfy edge->fixed() > coord + // + // search_track_num < 0: + // search downward for |search_track_num| tracks, including the track containing coord; + // every returned non-null edge must satisfy edge->fixed() < coord + // + // interval semantics: open interval (a0, a1) + // + // return semantics: + // - edge != nullptr: this interval is covered by the returned edge + // - edge == nullptr: this interval remains uncovered after searching all requested tracks + // + // widen_func semantics: + // - input: + // OverlapWidenContext{ + // track_distance = |current_track_idx - base_track_idx|, + // overlap_len = current raw overlap length, + // edge = matched edge + // } + // - output: + // single-side widening length + // - widening is symmetric: + // widened interval = (ov.a0 - ext, ov.a1 + ext) + // - widened interval is clipped into the original query interval [a0, a1] + // + // search strategy: + // 1) on each track, collect candidate edges once from the initial remaining intervals; + // 2) traverse those candidates in search direction order (nearest first by fixed); + // 3) for each edge, compute overlap, widen it with widen_func, then clip to query interval; + // 4) cut away the widened covered part from the remaining intervals; + // 5) continue until no remaining interval is left or all candidates are consumed; + // 6) recurse to the next track, until all requested tracks are processed or no remaining + // interval is left; + // 7) any remaining uncovered intervals are returned with edge == nullptr. + std::vector get_overlap(const LineSegmentI& line_seg, + Dbu search_track_num, + const OverlapWidenFunc& widen_func = {}) const + { + std::vector result; + if (search_track_num == 0) { + return result; + } + + Dbu a0 = line_seg.a0; + Dbu a1 = line_seg.a1; + normalizeInterval(a0, a1); + if (!intervalValid({a0, a1})) { + return result; + } + + const Dbu coord = line_seg.fixed; + const Dbu query_a0 = a0; + const Dbu query_a1 = a1; + + const Dbu base_track_idx = coordToTrack(coord); + if (!trackValid(base_track_idx)) { + LOG_ERROR << "The base track index is out of range!"; + return result; + } + + std::vector remaining; + remaining.push_back({query_a0, query_a1}); + + const int step = (search_track_num > 0) ? 1 : -1; + const Dbu tracks_to_search = (search_track_num > 0) ? search_track_num : -search_track_num; + + SearchContext ctx; + ctx.coord = coord; + ctx.base_track_idx = base_track_idx; + ctx.query_a0 = query_a0; + ctx.query_a1 = query_a1; + ctx.step = step; + ctx.widen_func = &widen_func; + + remaining = searchAcrossTracks(base_track_idx, tracks_to_search, remaining, result, ctx); + + for (const auto& iv : remaining) { + TrackOverlap ov; + ov.a0 = iv.a0; + ov.a1 = iv.a1; + ov.sp = kMaxDbu; + ov.edge = nullptr; + result.push_back(ov); + } + + return result; + } + + private: + static void normalizeInterval(Dbu& a0, Dbu& a1) + { + if (a0 > a1) { + std::swap(a0, a1); + } + } + + static bool intervalValid(const EnvInterval& iv) { return iv.a0 < iv.a1; } + + // open interval overlap: (a0, a1) overlaps (b0, b1) iff max(a0, b0) < min(a1, b1) + static bool overlap(Dbu a0, Dbu a1, Dbu b0, Dbu b1) + { + normalizeInterval(a0, a1); + normalizeInterval(b0, b1); + return std::max(a0, b0) < std::min(a1, b1); + } + + static Dbu overlapA0(Dbu a0, Dbu a1, Dbu b0, Dbu b1) + { + normalizeInterval(a0, a1); + normalizeInterval(b0, b1); + return std::max(a0, b0); + } + + static Dbu overlapA1(Dbu a0, Dbu a1, Dbu b0, Dbu b1) + { + normalizeInterval(a0, a1); + normalizeInterval(b0, b1); + return std::min(a1, b1); + } + + // subtract open interval (cut_a0, cut_a1) from current remaining open intervals + static std::vector subtractInterval(const std::vector& remaining, + Dbu cut_a0, + Dbu cut_a1) + { + std::vector next; + normalizeInterval(cut_a0, cut_a1); + + for (const auto& iv : remaining) { + if (!overlap(iv.a0, iv.a1, cut_a0, cut_a1)) { + next.push_back(iv); + continue; + } + + // left residual: (iv.a0, cut_a0) + EnvInterval left{iv.a0, std::min(iv.a1, cut_a0)}; + if (intervalValid(left)) { + next.push_back(left); + } + + // right residual: (cut_a1, iv.a1) + EnvInterval right{std::max(iv.a0, cut_a1), iv.a1}; + if (intervalValid(right)) { + next.push_back(right); + } + } + + return next; + } + + static bool edgeIsInSearchDirection(const TopoEdge* edge, const SearchContext& ctx) + { + if (edge == nullptr) { + return false; + } + return (ctx.step > 0) ? (edge->fixed() > ctx.coord) : (edge->fixed() < ctx.coord); + } + + static TrackOverlap applyWidenAndClip(const TrackOverlap& ov, + Dbu track_idx, + const SearchContext& ctx) + { + TrackOverlap widened = ov; + + if (ctx.widen_func != nullptr && *ctx.widen_func && ov.edge != nullptr) { + const OverlapWidenContext widen_ctx{ + .track_distance = std::abs(track_idx - ctx.base_track_idx), + .overlap_len = ov.a1 - ov.a0, + .edge = ov.edge, + }; + + Dbu ext = (*ctx.widen_func)(widen_ctx); + if (ext < Dbu{0}) { + ext = Dbu{0}; + } + + widened.a0 -= ext; + widened.a1 += ext; + } + + // clip into original query interval [query_a0, query_a1] + widened.a0 = std::clamp(widened.a0, ctx.query_a0, ctx.query_a1); + widened.a1 = std::clamp(widened.a1, ctx.query_a0, ctx.query_a1); + + return widened; + } + + EdgeSet collectCandidateEdgesOnTrack(Dbu track_idx, + const std::vector& remaining) const + { + EdgeSet ordered; + if (!trackValid(track_idx)) { + return ordered; + } + + for (const auto& iv : remaining) { + Dbu bucket_idx0 = coordToBucket(iv.a0); + Dbu bucket_idx1 = coordToBucket(iv.a1 - 1); + if (bucket_idx0 > bucket_idx1) { + std::swap(bucket_idx0, bucket_idx1); + } + + if (!bucketValid(bucket_idx0) || !bucketValid(bucket_idx1)) { + continue; + } + + for (Dbu b = bucket_idx0; b <= bucket_idx1; ++b) { + const auto& edge_set = track_buckets_[track_idx][b]; + ordered.insert(edge_set.begin(), edge_set.end()); + } + } + + return ordered; + } + + bool edgeHitsRemaining(const TopoEdge* edge, const std::vector& remaining) const + { + if (edge == nullptr) { + return false; + } + + Dbu edge_a0 = edge->a0(); + Dbu edge_a1 = edge->a1(); + normalizeInterval(edge_a0, edge_a1); + + for (const auto& iv : remaining) { + if (overlap(edge_a0, edge_a1, iv.a0, iv.a1)) { + return true; + } + } + return false; + } + + std::vector computeEdgeOverlaps(Dbu track_idx, + const TopoEdge* edge, + const std::vector& remaining, + const SearchContext& ctx) const + { + std::vector overlaps; + if (edge == nullptr) { + return overlaps; + } + + Dbu edge_a0 = edge->a0(); + Dbu edge_a1 = edge->a1(); + normalizeInterval(edge_a0, edge_a1); + + for (const auto& iv : remaining) { + if (!overlap(edge_a0, edge_a1, iv.a0, iv.a1)) { + continue; + } + + TrackOverlap ov; + ov.a0 = overlapA0(edge_a0, edge_a1, iv.a0, iv.a1); + ov.a1 = overlapA1(edge_a0, edge_a1, iv.a0, iv.a1); + ov.sp = std::abs(edge->fixed() - ctx.coord); + ov.edge = edge; + + if (ov.a0 < ov.a1) { + TrackOverlap widened = applyWidenAndClip(ov, track_idx, ctx); + + // widened interval should not cross the current remaining fragment + widened.a0 = std::max(widened.a0, iv.a0); + widened.a1 = std::min(widened.a1, iv.a1); + + if (widened.a0 < widened.a1) { + overlaps.push_back(widened); + } + } + } + + return overlaps; + } + + // Iteratively consume one track using a single ordered candidate set. + // The key invariant is that `remaining` only shrinks, so an edge that does not + // belong to the initial candidate set can never become relevant later. + void searchWithinTrack(Dbu track_idx, + std::vector remaining, + std::vector& result, + const SearchContext& ctx) const + { + if (!trackValid(track_idx) || remaining.empty()) { + return; + } + + const EdgeSet ordered = collectCandidateEdgesOnTrack(track_idx, remaining); + if (ordered.empty()) { + return; + } + + auto consume_edge = [&](const TopoEdge* edge) { + if (!edgeHitsRemaining(edge, remaining)) { + return; + } + + const auto overlaps = computeEdgeOverlaps(track_idx, edge, remaining, ctx); + if (overlaps.empty()) { + return; + } + + result.insert(result.end(), overlaps.begin(), overlaps.end()); + + auto next_remaining = remaining; + for (const auto& ov : overlaps) { + next_remaining = subtractInterval(next_remaining, ov.a0, ov.a1); + if (next_remaining.empty()) { + break; + } + } + + remaining = std::move(next_remaining); + }; + + if (ctx.step > 0) { + for (auto it = ordered.upper_bound(ctx.coord); + it != ordered.end() && !remaining.empty(); + ++it) { + consume_edge(*it); + } + } else { + auto it = ordered.lower_bound(ctx.coord); + for (auto rit = std::make_reverse_iterator(it); + rit != ordered.rend() && !remaining.empty(); + ++rit) { + consume_edge(*rit); + } + } + } + + // Recursively process tracks in the requested direction. + // For each track: + // 1) consume as much remaining interval as possible on this track; + // 2) commit only the widened+clipped overlap pieces to the final result; + // 3) subtract those committed overlap pieces from remaining; + // 4) recurse to the next track; + // 5) return whatever interval is still uncovered after all requested tracks are processed. + std::vector searchAcrossTracks(Dbu track_idx, + Dbu tracks_left, + std::vector remaining, + std::vector& result, + const SearchContext& ctx) const + { + if (tracks_left <= 0 || remaining.empty()) { + return remaining; + } + + if (!trackValid(track_idx)) { + return remaining; + } + + std::vector local_overlaps; + searchWithinTrack(track_idx, remaining, local_overlaps, ctx); + + for (const auto& ov : local_overlaps) { + // Defensive check: the directional constraint should already be guaranteed by + if (!edgeIsInSearchDirection(ov.edge, ctx)) { + continue; + } + + result.push_back(ov); + remaining = subtractInterval(remaining, ov.a0, ov.a1); + if (remaining.empty()) { + return remaining; + } + } + + return searchAcrossTracks(track_idx + ctx.step, tracks_left - 1, remaining, result, ctx); + } + + private: + // each track -> multiple buckets + std::vector> track_buckets_; + + Dbu track_ori_{0}; + Dbu track_num_{0}; + Dbu track_dlt_{0}; + + Dbu bucket_ori_{0}; + Dbu bucket_num_{0}; + Dbu bucket_dlt_{10000}; + + bool trackValid(Dbu t) const { return 0 <= t && t < track_num_; } + bool bucketValid(Dbu b) const { return 0 <= b && b < bucket_num_; } +}; + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/extract/CMakeLists.txt b/src/operation/iRCX/source/module/extract/CMakeLists.txt new file mode 100644 index 000000000..6d4da90c3 --- /dev/null +++ b/src/operation/iRCX/source/module/extract/CMakeLists.txt @@ -0,0 +1,28 @@ +set(IRCX_EXTRACT_SRC + RCX.cpp + resistance/ResistanceCalc.cpp + capacitance/CapacitanceCalc.cpp +) + +add_library(ircx ${IRCX_EXTRACT_SRC}) + +target_link_libraries(ircx + PUBLIC + ircx_cap_table + ircx_headers + ircx_mapping + PRIVATE + idm + ircx_engine + ircx_environment + ircx_process + ircx_topology + ircx_report + itf_builder + log +) + +target_include_directories(ircx + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/src/operation/iRCX/source/module/extract/RCTable.hpp b/src/operation/iRCX/source/module/extract/RCTable.hpp new file mode 100644 index 000000000..2ef9dd690 --- /dev/null +++ b/src/operation/iRCX/source/module/extract/RCTable.hpp @@ -0,0 +1,125 @@ +// *************************************************************************************** +// 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 +#include +#include + +#include "HashFactory.hpp" +#include "Types.hpp" +#include "TopoPool.hpp" +namespace ircx { + +using CouplingKey = HashFactory::UndirectedPairKey; +using CouplingKeyHash = HashFactory::PairKeyHash; + +struct CcapEntry { + Size edge_a; + Size edge_b; + Size corner_id; + F32 value; +}; + +class RCTable { + public: + RCTable() = default; + ~RCTable() = default; + + /// Pre-allocate all storage. Must be called before any parallel calc. + void init(Size corner_num, Size net_num, const TopoPool& topo) { + corner_num_ = corner_num; + net_num_ = net_num; + + Size total = corner_num * net_num; + corner_net_res_pools_.resize(total); + corner_net_gcap_pools_.resize(total); + + for (Size corner_idx = 0; corner_idx < corner_num; ++corner_idx) { + for (Size net_idx = 0; net_idx < net_num; ++net_idx) { + const Size edge_count = topo.net_edges(net_idx).size(); + const Size pool_idx = corner_idx * net_num + net_idx; + corner_net_res_pools_[pool_idx].assign(edge_count, 0.0); + corner_net_gcap_pools_[pool_idx].assign(edge_count, 0.0); + } + } + + net_ccap_entries_.resize(net_num); + merged_ccap_.clear(); + } + + // Resistance: writable span per (corner, net) + std::span corner_net_res_pool(Size corner_id, Size net_id) { + return corner_net_res_pools_[corner_id * net_num_ + net_id]; + } + std::span corner_net_res_pool(Size corner_id, Size net_id) const { + return corner_net_res_pools_[corner_id * net_num_ + net_id]; + } + + // Ground cap: writable span per (corner, net) + std::span corner_net_gcap_pool(Size corner_id, Size net_id) { + return corner_net_gcap_pools_[corner_id * net_num_ + net_id]; + } + std::span corner_net_gcap_pool(Size corner_id, Size net_id) const { + return corner_net_gcap_pools_[corner_id * net_num_ + net_id]; + } + + // Coupling cap: per-net accumulation (parallel-safe across nets) + void append_net_ccap_entry(Size net_id, Size edge_a, Size edge_b, Size corner_id, F32 value) { + net_ccap_entries_[net_id].push_back({edge_a, edge_b, corner_id, value}); + } + + /// Merge per-net ccap entries into the final map. + /// Call AFTER the parallel loop completes. + void merge_net_ccap_entries() { + for (auto& net_entries : net_ccap_entries_) { + for (auto& [edge_a_id, edge_b_id, corner_idx, cap_value] : net_entries) { + CouplingKey key(edge_a_id, edge_b_id); + auto it = merged_ccap_.find(key); + if (it == merged_ccap_.end()) { + auto& vec = merged_ccap_[key]; + vec.resize(corner_num_, 0.0f); + vec[corner_idx] += cap_value; + } else { + it->second[corner_idx] += cap_value; + } + } + net_entries.clear(); + } + } + + // Accessors + Size corner_num() const { return corner_num_; } + Size net_num() const { return net_num_; } + const auto& merged_ccap() const { return merged_ccap_; } + + private: + Size corner_num_{0}; + Size net_num_{0}; + + // indexed by [corner_id * net_num_ + net_id], each vec size = edge count of net + std::vector> corner_net_res_pools_; + std::vector> corner_net_gcap_pools_; + + // parallel accumulation: indexed by net_id + std::vector> net_ccap_entries_; + + // merged result + std::unordered_map, CouplingKeyHash> merged_ccap_; +}; + +} diff --git a/src/operation/iRCX/source/module/extract/RCX.cpp b/src/operation/iRCX/source/module/extract/RCX.cpp new file mode 100644 index 000000000..622c1c5c9 --- /dev/null +++ b/src/operation/iRCX/source/module/extract/RCX.cpp @@ -0,0 +1,337 @@ +// *************************************************************************************** +// 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 +#include +#include +#include +#include +#include +#include + +#include "RCX.hpp" +#include "TopologyBuilder.hpp" +#include "Environment.hpp" +#include "ProcessVariation.hpp" +#include "ProcessCorner.hpp" +#include "ItfBuilder.hpp" +#include "ParasiticXIDBAdapter.hpp" +#include "CapacitanceCalc.hpp" +#include "ResistanceCalc.hpp" +#include "SpefDumper.hpp" +#include "idm.h" +#include "log/Log.hh" +namespace ircx { + +std::vector> +RCX::loadItfFiles(const std::vector& itf_files) +{ + LOG_FATAL_IF(itf_files.empty()) << "itf file list is empty."; + + ::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"; + } + + return itf_builder.get_itf_service()->take_process_corners(); +} + +void RCX::registerProcessLayers(const ::itf::ProcessCorner& pc) +{ + if (process_layers_registered_) { + return; + } + + auto& cond_layers = pc.get_layers()->get_conductor_layers(); + auto& via_layers = pc.get_layers()->get_via_layers(); + + for (auto* layer : cond_layers) + layer_table_.registerProcessLayer(layer->get_id(), layer->get_name()); + for (auto* layer : via_layers) + layer_table_.registerProcessLayer(layer->get_id(), layer->get_name()); + + process_layers_registered_ = true; +} + +void RCX::storeProcessCorner(std::unique_ptr<::itf::ProcessCorner> pc) +{ + if (!pc || pc->get_technology().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); + } + + return result; +} + +unsigned RCX::readCorner(const Str& corner_name, + const char* itf_file, + const char* captab_file) +{ + if (!readItf(corner_name, itf_file)) { + return 0; + } + return readCaptab(corner_name, captab_file); +} + +unsigned RCX::readCaptab(const Str& corner_name, const char* captab_file) +{ + 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; +} + +unsigned RCX::readItf(const Str& corner_name, const char* itf_file) +{ + 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); + + 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); + } + + storeProcessCorner(std::move(loaded_corner)); + return 1; +} + +unsigned RCX::readItf(const std::vector& itf_files) +{ + auto pcs = loadItfFiles(itf_files); + for (auto& pc : pcs) { + storeProcessCorner(std::move(pc)); + } + + return 1; +} + +unsigned RCX::readMapping(const char* mapping_file) +{ + LOG_INFO << "read mapping " + << mapping_file << " start"; + + mapping_builder_.read(mapping_file); + + for (const auto& [dn, pn] : mapping_builder_.design_to_process_layer_names()) + layer_table_.registerMapping(dn, pn); + + LOG_INFO << "read mapping " + << mapping_file << " end"; + return 1; +} + +unsigned RCX::adaptDB() +{ + if (!dmInst) { + LOG_ERROR << "adapt db failed: dmInst is null."; + return 0; + } + + auto* idb_builder = dmInst->get_idb_builder(); + if (!idb_builder) { + LOG_ERROR << "adapt db failed: idb builder is null."; + return 0; + } + + ParasiticXIDBAdapter adapter(idb_builder); + + if (!adapter.adapt(layout_, layer_table_, spef_context_)) { + LOG_ERROR << "adapt db failed."; + return 0; + } + + return 1; +} + +unsigned RCX::checkShortOpen() +{ + // if (!db_) { + // if (!adaptDB()) { + // LOG_WARNING << "check short/open: database is empty, call adaptDB() first."; + // return 0; + // } + // } + + // LOG_INFO << "check short/open start"; + + + // LOG_INFO << "check short/open end"; + return 1; +} + +unsigned RCX::buildTopology() +{ + LOG_INFO << "build topology start"; + + TopologyBuilder tb(topo_pool_); + tb.build_all(layout_); + tb.build_special(layout_); + + LOG_INFO << "build topology end"; + return 1; +} + +unsigned RCX::buildEnvironment() { + + LOG_INFO << "build environment start"; + + + Environment& env = Environment::getOrCreateInst(); + env.set_layout_data(&layout_); + env.set_topo_pool(&topo_pool_); + + env.buildNetEnvPools(); + + LOG_INFO << "build environment end"; + return 1; +} + +unsigned RCX::buildProcessVariation() { + + LOG_INFO << "build process variation start"; + + const auto process_corners = corners(); + + ProcessVariation& pv = ProcessVariation::getOrCreateInst(); + pv.set_layout_data(&layout_); + pv.set_topo_pool(&topo_pool_); + pv.set_layer_table(&layer_table_); + pv.set_corners(process_corners); + + pv.buildEtchPools(); + + LOG_INFO << "build process variation end"; + return 1; +} + +unsigned RCX::extractParasitics() +{ + LOG_INFO << "extract parasitics start"; + + const auto process_corners = corners(); + const auto cap_tables = corner_cap_tables(); + LOG_FATAL_IF(process_corners.size() != cap_tables.size()) + << "corner/captab size mismatch."; + for (Size corner_idx = 0; corner_idx < process_corners.size(); ++corner_idx) { + LOG_FATAL_IF(cap_tables[corner_idx] == nullptr) + << "captab not loaded for corner " + << process_corners[corner_idx]->get_technology(); + } + + // Pre-allocate RCTable for parallel access + rc_table_.init(process_corners.size(), layout_.regular_net_count(), topo_pool_); + + // Resistance + ResistanceCalc& res_calc = ResistanceCalc::getOrCreateInst(); + res_calc.set_layout_data(&layout_); + res_calc.set_topo_pool(&topo_pool_); + res_calc.set_layer_table(&layer_table_); + res_calc.set_rc_table(&rc_table_); + res_calc.set_corners(process_corners); + res_calc.calc(); + + // Capacitance + CapacitanceCalc& cap_calc = CapacitanceCalc::getOrCreateInst(); + cap_calc.set_layout_data(&layout_); + cap_calc.set_topo_pool(&topo_pool_); + cap_calc.set_layer_table(&layer_table_); + cap_calc.set_rc_table(&rc_table_); + cap_calc.set_cap_tables(cap_tables); + cap_calc.set_corners(process_corners); + cap_calc.calc(); + + LOG_INFO << "extract parasitics end"; + return 1; +} + +unsigned RCX::reportSpef(const Str& output_dir) +{ + LOG_INFO << "report spef start"; + const auto process_corners = corners(); + + SpefDumper dumper; + + dumper.set_spef_context(&spef_context_); + dumper.set_layout_data(&layout_); + dumper.set_layer_table(&layer_table_); + dumper.set_topo_pool(&topo_pool_); + dumper.set_rc_table(&rc_table_); + dumper.set_corners(process_corners); + + for (Size corner_idx = 0; corner_idx < process_corners.size(); ++corner_idx) { + dumper.dump(output_dir, corner_idx); + } + + LOG_INFO << "report spef end"; + return 1; +} + +RCX::RCX() +{ + char config[] = "iRCX"; + char* argv[] = {config, nullptr}; + ieda::Log::init(argv); +} +RCX::~RCX() = default; + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/extract/RCX.hpp b/src/operation/iRCX/source/module/extract/RCX.hpp new file mode 100644 index 000000000..4dc257835 --- /dev/null +++ b/src/operation/iRCX/source/module/extract/RCX.hpp @@ -0,0 +1,152 @@ +// *************************************************************************************** +// 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 +#include +#include +#include + +#include "Geoms.hpp" +#include "LayerTable.hpp" +#include "LayoutData.hpp" +#include "SpefContext.hpp" +#include "Types.hpp" +#include "TopoPool.hpp" +#include "CapTable.hpp" +#include "MappingBuilder.hpp" +#include "RCTable.hpp" +namespace idb { + class IdbDesign; +} + +namespace itf { + class ProcessCorner; +} + +namespace ircx { +namespace parser { +class CapTable; +class MappingBuilder; +} // namespace parser +} + +namespace ircx { + +inline constexpr int kDefaultThreadCount = 64; + +class RCX final { + public: + // Meyer's singleton + static RCX& getOrCreateInst() { + static RCX inst; // C++11 thread-safe + return inst; + } + + // Disallow copy/move + RCX(const RCX&) = delete; + RCX& operator=(const RCX&) = delete; + RCX(RCX&&) = delete; + RCX& operator=(RCX&&) = delete; + + // I/O + [[nodiscard]] unsigned readCorner(const Str&, const char*, const char*); + [[nodiscard]] unsigned readItf(const std::vector&); + [[nodiscard]] unsigned readMapping(const char*); + + // DB + [[nodiscard]] unsigned adaptDB(); + + // Checks + [[nodiscard]] unsigned checkShortOpen(); + + // Topology + [[nodiscard]] unsigned buildTopology(); + + // Environment + [[nodiscard]] unsigned buildEnvironment(); + + // Process + [[nodiscard]] unsigned buildProcessVariation(); + + // Extraction + [[nodiscard]] unsigned extractParasitics(); + + // Report + [[nodiscard]] unsigned reportSpef(const Str& output_dir); + + // setters & getters + void set_num_threads(unsigned value) { num_threads_ = 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()); + 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()); + return result; + } + + private: + RCX(); + ~RCX(); + + [[nodiscard]] unsigned readItf(const Str&, const char*); + [[nodiscard]] unsigned readCaptab(const Str&, const char*); + + std::vector> loadItfFiles( + const std::vector& itf_files); + void registerProcessLayers(const ::itf::ProcessCorner& pc); + void storeProcessCorner(std::unique_ptr<::itf::ProcessCorner> pc); + std::vector corner_cap_tables() const; + + private: + // running settings + int num_threads_ = kDefaultThreadCount; + + // from db + LayoutData layout_; + SpefContext spef_context_; + + // unified layer id/name mapping (design ↔ process) + // from 1.db adapter 2.itf 3.mapping + LayerTable layer_table_; + + // process corners (from itf file) + std::map> process_corners_; + + // per-corner cap table (from captab file) + std::map corner_cap_tables_; + // mapping table (from mapping file) + parser::MappingBuilder mapping_builder_; + + // buildTopology + TopoPool topo_pool_; + + // extractParasitics results + RCTable rc_table_; + + bool process_layers_registered_{false}; +}; + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/extract/capacitance/CapacitanceCalc.cpp b/src/operation/iRCX/source/module/extract/capacitance/CapacitanceCalc.cpp new file mode 100644 index 000000000..b6cf303de --- /dev/null +++ b/src/operation/iRCX/source/module/extract/capacitance/CapacitanceCalc.cpp @@ -0,0 +1,240 @@ +// *************************************************************************************** +// 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 + +#include "CapacitanceCalc.hpp" +#include "LayerTable.hpp" +#include "Environment.hpp" +#include "ProcessVariation.hpp" +#include "ProcessCorner.hpp" +#include "CapTable.hpp" +#include "log/Log.hh" +namespace ircx { + +void CapacitanceCalc::calc() +{ + ProcessVariation& pv = ProcessVariation::getOrCreateInst(); + Environment& env = Environment::getOrCreateInst(); + const Size corner_count = corner_num_; + const Size net_count = net_num_; + + LOG_FATAL_IF(cap_tables_.size() != corner_num_) + << "cap table count does not match corner count."; + + for (Size corner_idx = 0; corner_idx < corner_count; ++corner_idx) { + const parser::CapTable* cap_table = cap_tables_[corner_idx]; + LOG_FATAL_IF(cap_table == nullptr) + << "cap table missing for corner " << corners_[corner_idx]->get_technology(); + + #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 + } + } + + // merge per-net coupling cap entries (sequential) + rc_table_->merge_net_ccap_entries(); +} + +void CapacitanceCalc::resolveCrossLayers( + const CrossOverlapSub* crossSeg, + Str& belowLayer, + Str& aboveLayer) const +{ + belowLayer = "SUBSTRATE"; + aboveLayer.clear(); + + if (crossSeg == nullptr) { + return; + } + + if (crossSeg->blw_layer != 0) { + Size procId = layer_table_->design_to_process_id(crossSeg->blw_layer); + belowLayer = layer_table_->process_name(procId); + } + if (crossSeg->abv_layer != 0) { + Size procId = layer_table_->design_to_process_id(crossSeg->abv_layer); + aboveLayer = layer_table_->process_name(procId); + } +} + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/extract/capacitance/CapacitanceCalc.hpp b/src/operation/iRCX/source/module/extract/capacitance/CapacitanceCalc.hpp new file mode 100644 index 000000000..d2208f221 --- /dev/null +++ b/src/operation/iRCX/source/module/extract/capacitance/CapacitanceCalc.hpp @@ -0,0 +1,96 @@ +// *************************************************************************************** +// 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 + +#include "LayoutData.hpp" +#include "EnvPool.hpp" +#include "RCTable.hpp" +namespace ircx { + +namespace parser { +class CapTable; +} + +class LayoutData; +class LayerTable; +class TopoPool; +} + +namespace itf { +class ProcessCorner; +} + +namespace ircx { + +class CapacitanceCalc { + public: + // Meyer's singleton + static CapacitanceCalc& getOrCreateInst() { + static CapacitanceCalc inst; + return inst; + } + + // Disallow copy/move + CapacitanceCalc(const CapacitanceCalc&) = delete; + CapacitanceCalc& operator=(const CapacitanceCalc&) = delete; + CapacitanceCalc(CapacitanceCalc&&) = delete; + CapacitanceCalc& operator=(CapacitanceCalc&&) = delete; + + 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; } + void set_topo_pool(const TopoPool* v) { topo_pool_ = v; } + 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_rc_table(RCTable* v) { rc_table_ = v; } + + void calc(); + + private: + CapacitanceCalc() = default; + ~CapacitanceCalc() = default; + + // Determine below/above process layer names from cross-layer segments. + void resolveCrossLayers( + const CrossOverlapSub* crossSeg, + Str& belowLayer, + Str& aboveLayer) const; + + Size net_num_{0}; + Size corner_num_{0}; + Micron dbu_to_micron_{1}; + + const LayoutData* layout_data_{nullptr}; + const LayerTable* layer_table_{nullptr}; + const TopoPool* topo_pool_{nullptr}; + std::vector cap_tables_; + std::vector corners_; + + RCTable* rc_table_{nullptr}; +}; + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/extract/resistance/ResistanceCalc.cpp b/src/operation/iRCX/source/module/extract/resistance/ResistanceCalc.cpp new file mode 100644 index 000000000..893e56ccb --- /dev/null +++ b/src/operation/iRCX/source/module/extract/resistance/ResistanceCalc.cpp @@ -0,0 +1,128 @@ +// *************************************************************************************** +// 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 "ResistanceCalc.hpp" +#include "LayerTable.hpp" +#include "LayoutData.hpp" +#include "TopoPool.hpp" +#include "ProcessVariation.hpp" +#include "ProcessCorner.hpp" +#include "log/Log.hh" +namespace ircx { + +void ResistanceCalc::calc() +{ + ProcessVariation& pv = ProcessVariation::getOrCreateInst(); + const Size regular_net_count = layout_data_->regular_net_count(); + const Size corner_count = corners_.size(); + + for (Size corner_idx = 0; corner_idx < corner_count; corner_idx++) { + #pragma omp parallel for schedule(dynamic) + for (Size net_idx = 0; net_idx < regular_net_count; net_idx++) { + const auto net_edges = topo_pool_->net_edges(net_idx); + const Size edge_count = net_edges.size(); + auto edge_resistances = rc_table_->corner_net_res_pool(corner_idx, net_idx); + const EtchPool& corner_net_etch_pool = pv.corner_net_etch_pool(corner_idx, net_idx); + for (Size edge_idx = 0; edge_idx < edge_count; edge_idx++) { + const TopoEdge& edge = net_edges[edge_idx]; + const Size process_layer_id = layer_table_->design_to_process_id(edge.layer_id()); + + if (edge.is_via()) { + auto* via_layer = corners_[corner_idx]->get_layers()->find_via_layer(process_layer_id); + + if (auto rpv = via_layer->get_rpv()) { + edge_resistances[edge_idx] = rpv.value(); + } else { + edge_resistances[edge_idx] = via_layer->query_rpv_vs_area(geom::Area(edge.shape())); + } + continue; // via res + } + + auto* conductor_layer = corners_[corner_idx]->get_layers()->find_conductor_layer(process_layer_id); + + auto [segment_lo, segment_hi] = node_range(edge); + const auto edge_etch_intervals = corner_net_etch_pool.edge_etch_interval_pool(edge_idx); + F64 resistance = 0.0; + for (const EtchInterval& etch_interval : edge_etch_intervals) { + // Overlap of this etch interval with the u->v segment. + const Micron overlap_lo = std::max(etch_interval.a0, segment_lo); + const Micron overlap_hi = std::min(etch_interval.a1, segment_hi); + if (overlap_hi <= overlap_lo) continue; + + const Micron overlap_length = overlap_hi - overlap_lo; + + // Modeled thickness: use value from ThicknessModel; fall back to nominal. + const Micron thickness = etch_interval.thickness; + const Micron width = etch_interval.width; + LOG_ERROR_IF(width <= 0.0 || thickness <= 0.0) << "etch interval width/thickness <= 0."; + + // ρ (Ω·μm): look up from RHO_VS_SI_WIDTH_AND_THICKNESS (row=T, col=W). + float resistivity = 0.0; + { + auto rho_opt = conductor_layer->get_rho_v_siw_t().query_interpolation( + static_cast(thickness), static_cast(width)); + if (rho_opt.has_value()) { + resistivity = rho_opt.value(); + } else { + resistivity = conductor_layer->get_rho(); + } + } + + float sheet_resistance = 0.0; + if (resistivity <= 0.0) { + // RPSQ path (sheet resistance): R = Rₛ × L / W + // Used when the conductor layer only specifies RPSQ (e.g. poly, diffusion). + auto rpsq_opt = conductor_layer->get_rpsq_vs_si_width().query_interpolation( + static_cast(width)); + if (rpsq_opt.has_value()) { + sheet_resistance = rpsq_opt.value(); + } else { + sheet_resistance = conductor_layer->get_rpsq(); + } + } + + if (resistivity > 0.0) { + resistance += resistivity * overlap_length / (width * thickness); + } + if (sheet_resistance > 0.0) { + resistance += sheet_resistance * overlap_length / width; + } + } + edge_resistances[edge_idx] = resistance; + } + // results already written into pre-allocated span + } + } +} + +std::pair ResistanceCalc::node_range(const TopoEdge& e) const +{ + const TopoNode& u_node = topo_pool_->node_at(e.u()); + const TopoNode& v_node = topo_pool_->node_at(e.v()); + + Micron u_pos, v_pos; + if (e.is_horz()) { + u_pos = geom::X(u_node.point()) * dbu_to_micron_; + v_pos = geom::X(v_node.point()) * dbu_to_micron_; + } else { + u_pos = geom::Y(u_node.point()) * dbu_to_micron_; + v_pos = geom::Y(v_node.point()) * dbu_to_micron_; + } + return {u_pos, v_pos}; +} + + +} diff --git a/src/operation/iRCX/source/module/extract/resistance/ResistanceCalc.hpp b/src/operation/iRCX/source/module/extract/resistance/ResistanceCalc.hpp new file mode 100644 index 000000000..0dea4ae92 --- /dev/null +++ b/src/operation/iRCX/source/module/extract/resistance/ResistanceCalc.hpp @@ -0,0 +1,76 @@ +// *************************************************************************************** +// 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 +#include + +#include "LayoutData.hpp" +#include "RCTable.hpp" +namespace ircx { + +class LayerTable; +class TopoEdge; +class TopoPool; +} + +namespace itf { +class ProcessCorner; +} + +namespace ircx { + +class ResistanceCalc { + public: + // Meyer's singleton + static ResistanceCalc& getOrCreateInst() { + static ResistanceCalc inst; + return inst; + } + + // Disallow copy/move + ResistanceCalc(const ResistanceCalc&) = delete; + ResistanceCalc& operator=(const ResistanceCalc&) = delete; + ResistanceCalc(ResistanceCalc&&) = delete; + ResistanceCalc& operator=(ResistanceCalc&&) = delete; + + void set_layout_data(const LayoutData* v) { + layout_data_ = v; + dbu_to_micron_ = Micron(1.0) / v->micron_to_dbu; + } + void set_layer_table(const LayerTable* v) { layer_table_ = v; } + void set_topo_pool(const TopoPool* v) { topo_pool_ = v; } + void set_rc_table(RCTable* v) { rc_table_ = v; } + void set_corners(const std::vector& v) { corners_ = v; } + + void calc(); + std::pair node_range(const TopoEdge& e) const; + + private: + ResistanceCalc() = default; + ~ResistanceCalc() = default; + + Micron dbu_to_micron_{1}; + + const LayoutData* layout_data_{nullptr}; + const LayerTable* layer_table_{nullptr}; + const TopoPool* topo_pool_{nullptr}; + std::vector corners_; + + RCTable* rc_table_{nullptr}; +}; +} diff --git a/src/operation/iRCX/source/module/process/CMakeLists.txt b/src/operation/iRCX/source/module/process/CMakeLists.txt new file mode 100644 index 000000000..77a932ea1 --- /dev/null +++ b/src/operation/iRCX/source/module/process/CMakeLists.txt @@ -0,0 +1,16 @@ +add_library(ircx_process ProcessVariation.cpp) + +target_link_libraries(ircx_process + PUBLIC + ircx_headers + PRIVATE + ircx_environment + itf_data + OpenMP::OpenMP_CXX + usage +) + +target_include_directories(ircx_process + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/src/operation/iRCX/source/module/process/EtchPool.hpp b/src/operation/iRCX/source/module/process/EtchPool.hpp new file mode 100644 index 000000000..cc79c5415 --- /dev/null +++ b/src/operation/iRCX/source/module/process/EtchPool.hpp @@ -0,0 +1,78 @@ +// *************************************************************************************** +// 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 +#include +#include +#include + +#include "Types.hpp" +namespace ircx { + +struct EtchInterval { + Micron a0 = 0; // interval start + Micron a1 = 0; // interval end + // change with etch + Micron center; + Micron width; + + Micron lo_spacing; + Micron hi_spacing; + + Micron thickness; + Micron height; +}; + +class EtchPool { // of each net + public: + EtchPool() = default; + ~EtchPool() = default; + + void append_edge_etch_interval_pool(std::vector v) { + edge_interval_ranges_.emplace_back(etch_interval_pool_.size(), v.size()); + + etch_interval_pool_.insert(etch_interval_pool_.end(), + std::make_move_iterator(v.begin()), + std::make_move_iterator(v.end())); + } + + void clear() { + etch_interval_pool_.clear(); + edge_interval_ranges_.clear(); + } + + std::span edge_etch_interval_pool(Size edge_id) const { + if (edge_id >= edge_interval_ranges_.size()) { + return {}; + } + const auto& [offset, length] = edge_interval_ranges_[edge_id]; + return std::span(etch_interval_pool_.data() + offset, length); + } + std::span edge_etch_interval_pool(Size edge_id) { + if (edge_id >= edge_interval_ranges_.size()) { + return {}; + } + const auto& [offset, length] = edge_interval_ranges_[edge_id]; + return std::span(etch_interval_pool_.data() + offset, length); + } + + private: + std::vector etch_interval_pool_; + std::vector> edge_interval_ranges_; +}; +} diff --git a/src/operation/iRCX/source/module/process/MetalDensity.hpp b/src/operation/iRCX/source/module/process/MetalDensity.hpp new file mode 100644 index 000000000..495eaa05d --- /dev/null +++ b/src/operation/iRCX/source/module/process/MetalDensity.hpp @@ -0,0 +1,70 @@ +// *************************************************************************************** +// 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 "TopoPool.hpp" +namespace ircx +{ + +class MetalDensity +{ + public: + MetalDensity() = default; + ~MetalDensity() = default; + + void set_topo_pool(const TopoPool* v) { topo_pool_ = v; } + + // Build the per-layer polygon set from all edges (regular + special). + // Special-net edges are stored in a dedicated special_edge_pool() in TopoPool. + void build() + { + polyset_map_.clear(); + + // regular net edges + for (const auto& edge : topo_pool_->edge_pool()) { + polyset_map_[edge.layer_id()] += edge.shape(); + } + + // special net edges (power/ground) + for (const auto& edge : topo_pool_->special_edge_pool()) { + polyset_map_[edge.layer_id()] += edge.shape(); + } + } + + double cal_density(Size layer, const GtlRectI& box) const + { + const auto it = polyset_map_.find(layer); + if (it == polyset_map_.end()) return 0.0; + + return static_cast(gtl::area(it->second & box)) / + static_cast(gtl::area(box)); + } + + private: + // copy + MetalDensity(const MetalDensity&) = delete; + MetalDensity& operator=(const MetalDensity&) = delete; + // move + MetalDensity(MetalDensity&&) = delete; + MetalDensity& operator=(MetalDensity&&) = delete; + + private: + const TopoPool* topo_pool_{nullptr}; + std::map polyset_map_; +}; + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/process/ProcessVariation.cpp b/src/operation/iRCX/source/module/process/ProcessVariation.cpp new file mode 100644 index 000000000..2fcc1cebc --- /dev/null +++ b/src/operation/iRCX/source/module/process/ProcessVariation.cpp @@ -0,0 +1,150 @@ +// *************************************************************************************** +// 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 +#include + +#include "ProcessVariation.hpp" +#include "TopoPool.hpp" +#include "Environment.hpp" +#include "ThicknessModel.hpp" +#include "WidthModel.hpp" +namespace ircx { + +void ProcessVariation::buildEtchPools() +{ + initMetalDensity(); + initEtchIntervals(); + + + WidthModel wm; + wm.set_topo_pool(topo_pool_); + wm.set_layer_table(layer_table_); + wm.set_corners(corners()); + for (Size corner_idx = 0; corner_idx < corner_num_; ++corner_idx) { + #pragma omp parallel for schedule(dynamic) + for (Size net_idx = 0; net_idx < net_num_; ++net_idx) { + wm.apply_width_variation( + corner_idx, + net_idx, + corner_net_etch_pools_[corner_net_pool_index(corner_idx, net_idx)]); + } + } + + ThicknessModel tm; + tm.set_layout_data(layout_data_); + tm.set_topo_pool(topo_pool_); + tm.set_layer_table(layer_table_); + tm.set_corners(corners()); + tm.set_metal_density(&metal_density_); + + for (Size corner_idx = 0; corner_idx < corner_num_; ++corner_idx) { + #pragma omp parallel for schedule(dynamic) + for (Size net_idx = 0; net_idx < net_num_; ++net_idx) { + tm.apply_thickness_variation( + corner_idx, + net_idx, + corner_net_etch_pools_[corner_net_pool_index(corner_idx, net_idx)]); + } + } +} + +void ProcessVariation::initMetalDensity() +{ + metal_density_.set_topo_pool(topo_pool_); + metal_density_.build(); +} + +void ProcessVariation::initEtchIntervals() +{ + Environment& env = Environment::getOrCreateInst(); + + net_num_ = layout_data_->regular_net_count(); + corner_num_ = corners_.size(); + corner_net_etch_pools_.resize(net_num_ * corner_num_); + + const std::vector& net_env_pools = env.net_env_pools(); + + Micron dbu_to_micron = 1. / layout_data_->micron_to_dbu; + + for (Size corner_idx = 0; corner_idx < corner_num_; ++corner_idx) { + #pragma omp parallel for schedule(dynamic) + for (Size net_idx = 0; net_idx < net_num_; ++net_idx) { + const auto net_edges = topo_pool_->net_edges(net_idx); + const Size edge_count = net_edges.size(); + + EtchPool& etch_pool = corner_net_etch_pools_[corner_net_pool_index(corner_idx, net_idx)]; + const EnvPool& env_pool = net_env_pools[net_idx]; + + for (Size edge_idx = 0; edge_idx < edge_count; ++edge_idx) { + const TopoEdge& edge = net_edges[edge_idx]; + + if (edge.is_via()) { + etch_pool.append_edge_etch_interval_pool({}); // placeholder to keep index aligned with TopoPool + continue; + } + + const std::span env_intervals = + env_pool.edge_env_interval_pool(edge_idx); + const Size interval_count = env_intervals.size(); + std::vector etch_intervals(interval_count); + + for (Size interval_idx = 0; interval_idx < interval_count; ++interval_idx) { + const EnvInterval& env_interval = env_intervals[interval_idx]; + EtchInterval& etch_interval = etch_intervals[interval_idx]; + + etch_interval.a0 = env_interval.a0 * dbu_to_micron; + etch_interval.a1 = env_interval.a1 * dbu_to_micron; + etch_interval.center = edge.fixed() * dbu_to_micron; + etch_interval.width = edge.width() * dbu_to_micron; + + // convert center-to-center spacing (EnvInterval) to edge-to-edge gap (EtchInterval) + if (env_interval.lo_adjacent) + etch_interval.lo_spacing = + (env_interval.lo_spacing - env_interval.lo_adjacent->half_width() - edge.half_width()) + * dbu_to_micron; + if (env_interval.hi_adjacent) + etch_interval.hi_spacing = + (env_interval.hi_spacing - env_interval.hi_adjacent->half_width() - edge.half_width()) + * dbu_to_micron; + + etch_interval.thickness = 0; + etch_interval.height = 0; + } + + etch_pool.append_edge_etch_interval_pool(std::move(etch_intervals)); + } + } + } +} + +// accessors + +EtchPool& +ProcessVariation::corner_net_etch_pool(Size corner_idx, Size net_id) +{ + Size pool_idx = corner_net_pool_index(corner_idx, net_id); + return corner_net_etch_pools_[pool_idx]; +} + +const EtchPool& +ProcessVariation::corner_net_etch_pool(Size corner_idx, Size net_id) const +{ + Size pool_idx = corner_net_pool_index(corner_idx, net_id); + return corner_net_etch_pools_[pool_idx]; +} + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/process/ProcessVariation.hpp b/src/operation/iRCX/source/module/process/ProcessVariation.hpp new file mode 100644 index 000000000..98b7f8ccb --- /dev/null +++ b/src/operation/iRCX/source/module/process/ProcessVariation.hpp @@ -0,0 +1,96 @@ +// *************************************************************************************** +// 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 + +#include "Types.hpp" +#include "EtchPool.hpp" +#include "MetalDensity.hpp" +namespace ircx { + class LayoutData; + class ProcessCorner; + class LayerTable; + class MetalDensity; + class TopoPool; +} + +namespace itf { + class ProcessCorner; +} + +namespace ircx { + +class ProcessVariation final { // singleton + public: + // Meyer's singleton + static ProcessVariation& getOrCreateInst() { + static ProcessVariation inst; // C++11 thread-safe + return inst; + } + + // Disallow copy/move + ProcessVariation(const ProcessVariation&) = delete; + ProcessVariation& operator=(const ProcessVariation&) = delete; + ProcessVariation(ProcessVariation&&) = delete; + ProcessVariation& operator=(ProcessVariation&&) = delete; + + void set_layout_data(const LayoutData* v) { layout_data_ = v; } + void set_layer_table(const LayerTable* v) { layer_table_ = v; } + void set_topo_pool(const TopoPool* v) { topo_pool_ = v; } + void set_corners(const std::vector<::itf::ProcessCorner*>& v) { corners_ = v; } + + std::vector<::itf::ProcessCorner*>& corners() { return corners_; } + const std::vector<::itf::ProcessCorner*>& corners() const { return corners_; } + Size corner_num() const { return corner_num_; } + + EtchPool& corner_net_etch_pool(Size corner_idx, Size net_id); + const EtchPool& corner_net_etch_pool(Size corner_idx, Size net_id) const; + + // other built data + const MetalDensity* metal_density() const { return &metal_density_; } + + // entry points + void buildEtchPools(); + + private: + ProcessVariation() = default; + ~ProcessVariation() = default; + + void initMetalDensity(); + void initEtchIntervals(); + + Size corner_net_pool_index(Size corner_idx, Size net_id) const { + return corner_idx * net_num_ + net_id; + } + + // set from outside + const LayoutData* layout_data_{nullptr}; + const LayerTable* layer_table_{nullptr}; + const TopoPool* topo_pool_{nullptr}; + std::vector<::itf::ProcessCorner*> corners_{}; + + // built here + MetalDensity metal_density_; + + Size corner_num_{0}; + + Size net_num_{0}; + std::vector corner_net_etch_pools_; +}; + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/process/ThicknessModel.hpp b/src/operation/iRCX/source/module/process/ThicknessModel.hpp new file mode 100644 index 000000000..c893023ff --- /dev/null +++ b/src/operation/iRCX/source/module/process/ThicknessModel.hpp @@ -0,0 +1,245 @@ +// *************************************************************************************** +// 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 +#include +#include +#include + +#include "LayerTable.hpp" +#include "LayoutData.hpp" +#include "TopoPool.hpp" +#include "EtchPool.hpp" +#include "MetalDensity.hpp" +#include "ProcessCorner.hpp" +namespace ircx { + +class ThicknessModel { + public: + ThicknessModel() = default; + ~ThicknessModel() = default; + + void set_layout_data(const LayoutData* v) { layout_data_ = v; } + void set_layer_table(const LayerTable* v) { layer_table_ = v; } + void set_topo_pool(const TopoPool* v) { topo_pool_ = v; } + void set_corners(std::vector<::itf::ProcessCorner*> v) { corners_ = std::move(v); } + void set_metal_density(const MetalDensity* v) { metal_density_ = v; } + + void apply_thickness_variation(Size corner_idx, Size net_idx, EtchPool& etch_pool) const { + const auto& corner = *corners_[corner_idx]; + const auto net_edges = topo_pool_->net_edges(net_idx); + const Size edge_count = net_edges.size(); + + for (Size edge_idx = 0; edge_idx < edge_count; ++edge_idx) { + const TopoEdge& edge = net_edges[edge_idx]; + if (edge.is_via()) continue; // TODO: 针对via的thickness变化 + + const Size process_layer_id = layer_table_->design_to_process_id(edge.layer_id()); + auto* conductor_layer = corner.get_layers()->find_conductor_layer(process_layer_id); + if (!conductor_layer) continue; + + std::span edge_etch_intervals = etch_pool.edge_etch_interval_pool(edge_idx); + if (edge_etch_intervals.empty()) continue; + + const PBTVCache pbtv_cache = build_pbtv_cache_(corner, *conductor_layer); + PBTVScratch scratch; + + if (pbtv_cache.valid()) { + const double effective_density = cal_effective_density_(edge, *conductor_layer); + fill_polynomial_terms_(effective_density, *pbtv_cache.density_orders, scratch.density_terms); + } + + for (EtchInterval& etch_interval : edge_etch_intervals) { + // Height (no variation: read directly from layer) + etch_interval.height = static_cast(conductor_layer->get_height()); + + // Thickness (POLYNOMIAL_BASED_THICKNESS_VARIATION) + model_thickness_(etch_interval, *conductor_layer, pbtv_cache, scratch); + } + } + } + + private: + struct PBTVCache { + const ::itf::itfiPBTV* table{nullptr}; + const std::vector* density_orders{nullptr}; + const std::vector* width_orders{nullptr}; + const std::vector* width_ranges{nullptr}; + + bool valid() const + { + return table != nullptr && density_orders != nullptr && width_orders != nullptr + && width_ranges != nullptr && !density_orders->empty() && !width_orders->empty(); + } + }; + + struct PBTVScratch { + std::vector density_terms; + std::vector width_terms; + }; + + // POLYNOMIAL_BASED_THICKNESS_VARIATION + // Fills d.thickness using the PBTV polynomial model. + void model_thickness_( + EtchInterval& etch, + const ::itf::LayerConductor& cdt, + const PBTVCache& pbtv_cache, + PBTVScratch& scratch) const + { + const float t_nom = cdt.get_thickness(); + if (!pbtv_cache.valid() || scratch.density_terms.empty()) { + etch.thickness = static_cast(t_nom); + return; + } + + // const float rt_deff = cal_rt_deff_( + // scratch.density_terms, static_cast(etch.width), pbtv_cache, scratch.width_terms); + // const float rt_ws = 0.0f; // f(W, S) — not yet modeled + // const float rt_siw = 0.0f; // f(SiW) — not yet modeled + + // etch.thickness = static_cast(t_nom * (1.0f + rt_deff + rt_ws + rt_siw)); + etch.thickness = static_cast(t_nom); + } + + // DENSITY_BOX_WEIGHTING_FACTOR + // Compute density-weighted effective density at the edge center. + double cal_effective_density_( + const TopoEdge& edge, + const ::itf::LayerConductor& cdt) const + { + const Size process_layer_id = layer_table_->design_to_process_id(edge.layer_id()); + + double effective_density = 0.0; + GtlRectI density_box; + + const GtlPointI center_point = edge.center(); + const Dbu center_x_dbu = geom::X(center_point); + const Dbu center_y_dbu = geom::Y(center_point); + + + for (const auto& [box_size, weight] : cdt.get_density_box_weighting_factor()) { + const Dbu half_box_size_dbu = static_cast(box_size * layout_data_->micron_to_dbu); + density_box = GtlRectI(center_x_dbu - half_box_size_dbu, center_y_dbu - half_box_size_dbu, + center_x_dbu + half_box_size_dbu, center_y_dbu + half_box_size_dbu); + + effective_density += metal_density_->cal_density(process_layer_id, density_box) * weight; + } + + return effective_density; + } + + // cal_rt_deff + // Returns relative thickness change as a function of effective density. + float cal_rt_deff_( + std::span density_terms, + double width, + const PBTVCache& pbtv_cache, + std::vector& width_terms) const + { + if (!pbtv_cache.valid()) { + return 0.0f; + } + + fill_polynomial_terms_(width, *pbtv_cache.width_orders, width_terms); + if (width_terms.empty()) { + return 0.0f; + } + + const size_t table_idx = select_width_table_(width, *pbtv_cache.width_ranges); + const auto& coeffs = pbtv_cache.table->get_polynomial_coefficients_list(table_idx); + if (coeffs.size() != density_terms.size() * width_terms.size()) { + return 0.0f; + } + + // Coefficients are stored row-major with density as the outer index. + float rt_deff = 0.0f; + size_t coeff_idx = 0; + for (size_t density_idx = 0; density_idx < density_terms.size(); ++density_idx) { + const float density_term = density_terms[density_idx]; + for (size_t width_idx = 0; width_idx < width_terms.size(); ++width_idx, ++coeff_idx) { + rt_deff += density_term * coeffs[coeff_idx] * width_terms[width_idx]; + } + } + + return rt_deff; + } + + PBTVCache build_pbtv_cache_( + const ::itf::ProcessCorner& pc, + const ::itf::LayerConductor& cdt) const + { + if (!pc.get_use_si_density()) { + return {}; + } + + const auto& table = cdt.get_PBTV(); + if (table.get_polynomial_coefficients_list_size() == 0) { + return {}; // PBTV not available for this layer + } + + const auto& d_orders = table.get_density_polynomial_orders(); + const auto& w_orders = table.get_width_polynomial_orders(); + if (d_orders.empty() || w_orders.empty()) { + return {}; + } + + PBTVCache cache; + cache.table = &table; + cache.density_orders = &d_orders; + cache.width_orders = &w_orders; + cache.width_ranges = &table.get_width_ranges(); + return cache; + } + + void fill_polynomial_terms_( + double value, + const std::vector& orders, + std::vector& terms) const + { + terms.resize(orders.size()); + for (size_t term_idx = 0; term_idx < orders.size(); ++term_idx) { + const int order = orders[term_idx]; + if (order == 0) { + terms[term_idx] = 1.0f; + } else if (order == 1) { + terms[term_idx] = static_cast(value); + } else { + terms[term_idx] = static_cast(std::pow(value, order)); + } + } + } + + size_t select_width_table_( + double width, + const std::vector& width_ranges) const + { + auto it = std::upper_bound(width_ranges.begin(), width_ranges.end(), + static_cast(width)); + return static_cast(std::distance(width_ranges.begin(), it)); + } + + const LayoutData* layout_data_{nullptr}; + const LayerTable* layer_table_{nullptr}; + const TopoPool* topo_pool_{nullptr}; + std::vector<::itf::ProcessCorner*> corners_{}; + + // built here + const MetalDensity* metal_density_{nullptr}; +}; + +} diff --git a/src/operation/iRCX/source/module/process/WidthModel.hpp b/src/operation/iRCX/source/module/process/WidthModel.hpp new file mode 100644 index 000000000..92bd3cbc8 --- /dev/null +++ b/src/operation/iRCX/source/module/process/WidthModel.hpp @@ -0,0 +1,78 @@ +// *************************************************************************************** +// 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 +#include + +#include "LayerTable.hpp" +#include "TopoPool.hpp" +#include "EtchPool.hpp" +#include "ProcessCorner.hpp" +namespace ircx { + +class WidthModel { + public: + WidthModel() = default; + ~WidthModel() = default; + + void set_topo_pool(const TopoPool* v) { topo_pool_ = v; } + void set_layer_table(const LayerTable* v) { layer_table_ = v; } + void set_corners(std::vector<::itf::ProcessCorner*> v) { corners_ = std::move(v); } + + void apply_width_variation(Size corner_idx, Size net_idx, EtchPool& etch_pool) const { + const auto net_edges = topo_pool_->net_edges(net_idx); + const Size edge_count = net_edges.size(); + + 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()); + auto* conductor_layer = corners_[corner_idx]->get_layers()->find_conductor_layer(process_layer_id); + if (!conductor_layer) continue; + + std::span edge_etch_intervals = etch_pool.edge_etch_interval_pool(edge_idx); + for (EtchInterval& etch_interval : edge_etch_intervals) { + for (const auto& etch_table : conductor_layer->get_etch_vws_list()) { // TODO: 还不完善,只考虑了vws这种情况,甚至没考虑是否CAPACITIVE_ONLY + // Low-side etch interval: lo_spacing is edge-to-edge in microns. + Micron low_side_etch = 0; + auto low_side_etch_opt = + etch_table.query_interpolation(etch_interval.width, etch_interval.lo_spacing); + if (low_side_etch_opt) low_side_etch = low_side_etch_opt.value(); + + // High-side etch interval. + Micron high_side_etch = 0; + auto high_side_etch_opt = + etch_table.query_interpolation(etch_interval.width, etch_interval.hi_spacing); + if (high_side_etch_opt) high_side_etch = high_side_etch_opt.value(); + + // Positive etch interval shrinks the conductor: center shifts, width decreases. + etch_interval.center += 0.5 * low_side_etch - 0.5 * high_side_etch; + etch_interval.width -= low_side_etch + high_side_etch; + } + } + } + } + + private: + const TopoPool* topo_pool_{nullptr}; + const LayerTable* layer_table_{nullptr}; + std::vector<::itf::ProcessCorner*> corners_{}; +}; + +} diff --git a/src/operation/iRCX/source/module/report/CMakeLists.txt b/src/operation/iRCX/source/module/report/CMakeLists.txt new file mode 100644 index 000000000..806b21058 --- /dev/null +++ b/src/operation/iRCX/source/module/report/CMakeLists.txt @@ -0,0 +1,16 @@ +add_library(ircx_report SpefDumper.cpp) + +target_link_libraries(ircx_report + PUBLIC + ircx_headers + PRIVATE + ircx_topology + itf_data + log + usage +) + +target_include_directories(ircx_report + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/src/operation/iRCX/source/module/report/SpefDumper.cpp b/src/operation/iRCX/source/module/report/SpefDumper.cpp new file mode 100644 index 000000000..120176c3e --- /dev/null +++ b/src/operation/iRCX/source/module/report/SpefDumper.cpp @@ -0,0 +1,450 @@ +// *************************************************************************************** +// 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 +#include +#include +#include + +#include "SpefDumper.hpp" +#include "Geoms.hpp" +#include "LayoutData.hpp" +#include "SpefContext.hpp" +#include "TopoPool.hpp" +#include "ProcessCorner.hpp" +#include "RCTable.hpp" +#include "log/Log.hh" +#include "usage/usage.hh" +namespace ircx { + +// ───────────────────────────────────────────────────────────────────────────── +// buildNameMaps +// ───────────────────────────────────────────────────────────────────────────── + +void SpefDumper::buildNameMaps() const +{ + name_maps_ = NameMaps{}; + + // 1. Regular net names + for (const Str& name : spef_context_->net_names) { + name_maps_.net_id_to_name[name_maps_.next_id] = name; + name_maps_.net_name_to_id[name] = name_maps_.next_id; + name_maps_.next_id++; + } + // 2. Ports (IO pins) + for (const Str& name : spef_context_->port_names) { + name_maps_.port_id_to_name[name_maps_.next_id] = name; + name_maps_.port_name_to_id[name] = name_maps_.next_id; + name_maps_.next_id++; + } + // 3. Instance names + for (const Str& name : spef_context_->instance_names) { + name_maps_.inst_id_to_name[name_maps_.next_id] = name; + name_maps_.inst_name_to_id[name] = name_maps_.next_id; + name_maps_.next_id++; + } +} + +void SpefDumper::buildPortIo() const +{ + port_io_.clear(); + port_io_.reserve(spef_context_->port_names.size()); + + for (Size port_idx = 0; port_idx < spef_context_->port_names.size(); ++port_idx) { + port_io_.emplace(spef_context_->port_names[port_idx], spef_context_->port_io[port_idx]); + } +} + +void SpefDumper::buildNodeSpefNames() const +{ + const auto& nodes = topo_pool_->node_pool(); + + node_spef_names_.assign(nodes.size(), Str{}); + for (const TopoNode& node : nodes) { + node_spef_names_[topo_pool_->node_index(node)] = nodeName(node); + } +} + +void SpefDumper::buildNetSpefNames() const +{ + const auto& net_name_to_id = name_maps_.net_name_to_id; + const Size net_num = layout_data_->regular_net_count(); + + net_spef_names_.assign(net_num, Str{}); + for (Size net_idx = 0; net_idx < net_num; ++net_idx) { + const Str& net_name = layout_data_->net_vec[net_idx].name; + auto it = net_name_to_id.find(net_name); + if (it != net_name_to_id.end()) { + net_spef_names_[net_idx] = "*" + std::to_string(it->second); + } else { + net_spef_names_[net_idx] = net_name; + } + } +} + +void SpefDumper::buildCouplingRefs(Size corner_idx) const +{ + const auto& edge_pool = topo_pool_->edge_pool(); + const Size net_num = layout_data_->regular_net_count(); + std::vector coupling_counts(net_num, 0); + + auto for_each_coupling = [&](auto&& fn) { + for (const auto& [key, cap_vec] : rc_table_->merged_ccap()) { + if (corner_idx >= cap_vec.size()) continue; + + const double cap_ff = static_cast(cap_vec[corner_idx]); + if (cap_ff <= 0.0) continue; + + const Size edge_a_id = key.first; + const Size edge_b_id = key.second; + if (edge_a_id >= edge_pool.size() || edge_b_id >= edge_pool.size()) continue; + + const TopoEdge& edge_a = edge_pool[edge_a_id]; + const TopoEdge& edge_b = edge_pool[edge_b_id]; + if (edge_a.net_id() >= net_num || edge_b.net_id() >= net_num) continue; + + fn(edge_a.net_id(), edge_a_id, edge_b_id, cap_ff); + if (edge_b.net_id() != edge_a.net_id()) { + fn(edge_b.net_id(), edge_b_id, edge_a_id, cap_ff); + } + } + }; + + for_each_coupling([&](Size net_idx, Size, Size, double) { + ++coupling_counts[net_idx]; + }); + + net_coupling_refs_.assign(net_num, {}); + for (Size net_idx = 0; net_idx < net_num; ++net_idx) { + net_coupling_refs_[net_idx].reserve(coupling_counts[net_idx]); + } + + for_each_coupling([&](Size net_idx, Size self_edge_id, Size other_edge_id, double cap_ff) { + net_coupling_refs_[net_idx].push_back({self_edge_id, other_edge_id, cap_ff}); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// nodeName +// ───────────────────────────────────────────────────────────────────────────── + +Str SpefDumper::nodeName(const TopoNode& node) const +{ + if (node.is_pin_node()) { + const Str& full_name = node.pin_name(); + // Port pin: no ':' separator + if (full_name.find(':') == Str::npos) { + auto it = name_maps_.port_name_to_id.find(full_name); + if (it != name_maps_.port_name_to_id.end()) + return "*" + std::to_string(it->second); + // Fallback: keep full name + return full_name; + } else { + // Instance pin: "inst_name:pin_name" + auto colon = full_name.find(':'); + Str inst_name = full_name.substr(0, colon); + Str pin_name = full_name.substr(colon + 1); + auto it = name_maps_.inst_name_to_id.find(inst_name); + if (it != name_maps_.inst_name_to_id.end()) + return "*" + std::to_string(it->second) + ":" + pin_name; + // Fallback: keep full name + return full_name; + } + } + + // Internal node: *: + Size net_id = node.net_id(); + Str net_name = layout_data_->net_vec[net_id].name; + auto it = name_maps_.net_name_to_id.find(net_name); + if (it != name_maps_.net_name_to_id.end()) + return "*" + std::to_string(it->second) + ":" + std::to_string(node.id() + 1); + return "*" + net_name + ":" + std::to_string(node.id() + 1); +} + +// ───────────────────────────────────────────────────────────────────────────── +// writeHeader +// ───────────────────────────────────────────────────────────────────────────── + +void SpefDumper::writeHeader(std::ofstream& ofs) const +{ + auto t = std::time(nullptr); + auto tm = *std::localtime(&t); + std::ostringstream date_ss; + date_ss << std::put_time(&tm, "%a %b %d %H:%M:%S %Y"); + + ofs << "*SPEF \"IEEE 1481-1998\"\n"; + ofs << "*DESIGN \"" << layout_data_->design_name << "\"\n"; + ofs << "*DATE \"" << date_ss.str() << "\"\n"; + ofs << "*VENDOR \"iEDA\"\n"; + ofs << "*PROGRAM \"iRCX\"\n"; + ofs << "*VERSION \"1.0\"\n"; + ofs << "*DESIGN_FLOW \"PIN_CAP NONE\"\n"; + ofs << "*DIVIDER /\n"; + ofs << "*DELIMITER :\n"; + ofs << "*BUS_DELIMITER []\n"; + ofs << "*T_UNIT 1.0 NS\n"; + ofs << "*C_UNIT 1.0 FF\n"; + ofs << "*R_UNIT 1.0 OHM\n"; + ofs << "*L_UNIT 1.0 HENRY\n"; +} + +// ───────────────────────────────────────────────────────────────────────────── +// writeNameMap +// ───────────────────────────────────────────────────────────────────────────── + +void SpefDumper::writeNameMap(std::ofstream& ofs) const +{ + ofs << "\n*NAME_MAP\n"; + // 1. Ports (IO pins) + for (const auto& [id, name] : name_maps_.port_id_to_name) { + ofs << "*" << id << " " << name << "\n"; + } + // 2. instance name + for (const auto& [id, name] : name_maps_.inst_id_to_name) { + ofs << "*" << id << " " << name << "\n"; + } + // 3. net name + for (const auto& [id, name] : name_maps_.net_id_to_name) { + ofs << "*" << id << " " << name << "\n"; + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// writePorts +// ───────────────────────────────────────────────────────────────────────────── + +void SpefDumper::writePorts(std::ofstream& ofs) const +{ + ofs << "\n*PORTS\n\n"; + for (Size port_idx = 0; port_idx < spef_context_->port_names.size(); ++port_idx) { + const Str& name = spef_context_->port_names[port_idx]; + ofs << "*" << name_maps_.port_name_to_id[name] << " " << spef_context_->port_io[port_idx] << "\n"; + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// writeDNet +// ───────────────────────────────────────────────────────────────────────────── + +void SpefDumper::writeDNet(std::ostream& os, Size corner_idx, Size net_idx) const +{ + const Net& net = layout_data_->net_vec[net_idx]; + const Str& net_spef_name = net_spef_names_[net_idx]; + const auto& node_spef_name = node_spef_names_; + const auto& port_io = port_io_; + const auto& coupling_refs = net_coupling_refs_[net_idx]; + const auto& edge_pool = topo_pool_->edge_pool(); + + std::unordered_map inst_pin_io; + inst_pin_io.reserve(net.pins.size()); + for (const Pin& p : net.pins) { + if (p.is_input && p.is_output) inst_pin_io[p.name] = 'B'; + else if (p.is_input) inst_pin_io[p.name] = 'I'; + else if (p.is_output) inst_pin_io[p.name] = 'O'; + } + + const auto nodes = topo_pool_->net_nodes(net_idx); + const auto edges = topo_pool_->net_edges(net_idx); + const Size node_offset = topo_pool_->net_node_range(net_idx).first; + + const Size node_num = nodes.size(); + if (node_num == 0) return; + + const Micron dbu_to_micron = Micron(1.0) / layout_data_->micron_to_dbu; + + // Aggregate ground cap to nodes + // Ground cap per edge is split equally to its two endpoint nodes. + auto gcap_pool = rc_table_->corner_net_gcap_pool(corner_idx, net_idx); + std::vector node_gnd(node_num, 0.0); + + for (Size edge_idx = 0; edge_idx < edges.size(); ++edge_idx) { + const TopoEdge& edge = edges[edge_idx]; + if (edge.is_via()) continue; + const double ground_cap = gcap_pool[edge_idx]; + if (ground_cap <= 0.0) continue; + // u()/v() are GLOBAL node indices; subtract node_offset for LOCAL index. + node_gnd[edge.u() - node_offset] += ground_cap / 2.0; + node_gnd[edge.v() - node_offset] += ground_cap / 2.0; + } + + // Aggregate coupling cap to closest node pairs + // key: (local_node_idx_self, other_node_name_string) → accumulated cap (fF) + std::map, double> node_cc_map; + + for (const CouplingRef& coupling : coupling_refs) { + const TopoEdge& e_self = edge_pool[coupling.self_edge_id]; + const TopoEdge& e_other = edge_pool[coupling.other_edge_id]; + + // Guard against edges without valid node assignments. + if (e_self.u() == kMaxSize || e_self.v() == kMaxSize) continue; + if (e_other.u() == kMaxSize || e_other.v() == kMaxSize) continue; + + // Find the closest node pair between self and other edges. + const GtlPointI& pa_u = topo_pool_->node_at(e_self.u()).point(); + const GtlPointI& pa_v = topo_pool_->node_at(e_self.v()).point(); + const GtlPointI& pb_u = topo_pool_->node_at(e_other.u()).point(); + const GtlPointI& pb_v = topo_pool_->node_at(e_other.v()).point(); + + struct Cand { Size self_global; Size other_global; Dbu dist; }; + const Cand cands[4] = { + {e_self.u(), e_other.u(), geom::Manhattan(pa_u, pb_u)}, + {e_self.u(), e_other.v(), geom::Manhattan(pa_u, pb_v)}, + {e_self.v(), e_other.u(), geom::Manhattan(pa_v, pb_u)}, + {e_self.v(), e_other.v(), geom::Manhattan(pa_v, pb_v)}, + }; + const auto& best = *std::min_element( + std::begin(cands), std::end(cands), + [](const Cand& x, const Cand& y){ return x.dist < y.dist; }); + + // Convert GLOBAL ids to LOCAL for array indexing. + const Size self_local = best.self_global - node_offset; + + // Get the SPEF name of the other net's node. + const Str& other_node = node_spef_name[best.other_global]; + + node_cc_map[{self_local, other_node}] += coupling.cap_ff; + } + + // Total capacitance + double tcap = 0.0; + for (double ground_cap : node_gnd) tcap += ground_cap; + for (const auto& [_, coupling_cap] : node_cc_map) tcap += coupling_cap; + + // Write *D_NET + os << "\n*D_NET " << net_spef_name << " " << std::fixed + << std::setprecision(6) << tcap << "\n\n"; + + // *CONN section + os << "*CONN\n"; + for (Size node_idx = 0; node_idx < node_num; ++node_idx) { // *P + const TopoNode& node = nodes[node_idx]; + if (!node.is_pin_node()) continue; + + if (node.pin_name().find(':') == Str::npos) { // port pin node + Micron x = geom::X(node.point()) * dbu_to_micron; + Micron y = geom::Y(node.point()) * dbu_to_micron; + + os << "*" << "P" << " " << node_spef_name[topo_pool_->node_index(net_idx, node.id())] + << " " << port_io.at(node.pin_name()) + << " *C " << std::fixed << std::setprecision(3) << x << " " << y << "\n"; + } + } + + for (Size node_idx = 0; node_idx < node_num; ++node_idx) { // *I + const TopoNode& node = nodes[node_idx]; + if (!node.is_pin_node()) continue; + + if (node.pin_name().find(':') != Str::npos) { // instance pin node + Micron x = geom::X(node.point()) * dbu_to_micron; + Micron y = geom::Y(node.point()) * dbu_to_micron; + const auto io_it = inst_pin_io.find(node.pin_name()); + const char pin_io = (io_it != inst_pin_io.end()) ? io_it->second : 'B'; + + os << "*" << "I" << " " << node_spef_name[topo_pool_->node_index(net_idx, node.id())] + << " " << pin_io + << " *C " << std::fixed << std::setprecision(3) << x << " " << y << "\n"; + } + } + + for (Size node_idx = 0; node_idx < node_num; ++node_idx) { // *N + const TopoNode& node = nodes[node_idx]; + if (node.is_pin_node()) continue; + + Micron x = geom::X(node.point()) * dbu_to_micron; + Micron y = geom::Y(node.point()) * dbu_to_micron; + + os << "*" << "N" << " " << node_spef_name[topo_pool_->node_index(net_idx, node.id())] + << " *C " << std::fixed << std::setprecision(3) << x << " " << y << "\n"; + } + + // *CAP section + os << "\n*CAP\n"; + int cap_id = 1; + // Coupling caps first. + for (const auto& [key, coupling_cap] : node_cc_map) { + if (coupling_cap <= 0.0) continue; + os << cap_id++ << " " << node_spef_name[topo_pool_->node_index(net_idx, key.first)] + << " " << key.second + << " " << std::setprecision(6) << coupling_cap << "\n"; + } + // Ground caps. + for (Size node_idx = 0; node_idx < node_num; ++node_idx) { + if (node_gnd[node_idx] <= 0.0) continue; + os << cap_id++ << " " << node_spef_name[topo_pool_->node_index(net_idx, node_idx)] + << " " << std::setprecision(6) << node_gnd[node_idx] << "\n"; + } + + // *RES section + os << "\n*RES\n"; + int res_id = 1; + auto res_pool = rc_table_->corner_net_res_pool(corner_idx, net_idx); + for (Size edge_idx = 0; edge_idx < edges.size(); ++edge_idx) { + const TopoEdge& edge = edges[edge_idx]; + if (edge.u() == kMaxSize || edge.v() == kMaxSize) continue; + + const double resistance = res_pool[edge_idx]; + + os << res_id++ << " " << node_spef_name[edge.u()] + << " " << node_spef_name[edge.v()] + << " " << std::setprecision(6) << resistance << "\n"; + } + + os << "*END\n"; +} + +// ───────────────────────────────────────────────────────────────────────────── +// dump (public entry point) +// ───────────────────────────────────────────────────────────────────────────── + +void SpefDumper::dump(const Str& output_dir, Size corner_idx) const +{ + Str corner_name = corners_[corner_idx]->get_technology(); + Str filename = output_dir + "/" + layout_data_->design_name + + "_" + corner_name + ".spef"; + std::ofstream ofs(filename); + if (!ofs) { + LOG_ERROR << "SpefDumper: cannot open output file: " << filename; + return; + } + + const Size net_count = layout_data_->regular_net_count(); + + buildNameMaps(); + buildPortIo(); + buildNodeSpefNames(); + buildNetSpefNames(); + buildCouplingRefs(corner_idx); + net_str_buffer_.assign(net_count, Str{}); + + writeHeader(ofs); + writeNameMap(ofs); + writePorts(ofs); + + #pragma omp parallel for schedule(dynamic) + for (Size net_idx = 0; net_idx < net_count; ++net_idx) { + std::ostringstream net_os; + writeDNet(net_os, corner_idx, net_idx); + net_str_buffer_[net_idx] = net_os.str(); + } + + for (const Str& net_str : net_str_buffer_) { + ofs << net_str; + } + + LOG_INFO << "SpefDumper: wrote " << filename; +} + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/report/SpefDumper.hpp b/src/operation/iRCX/source/module/report/SpefDumper.hpp new file mode 100644 index 000000000..3ea64d6e7 --- /dev/null +++ b/src/operation/iRCX/source/module/report/SpefDumper.hpp @@ -0,0 +1,110 @@ +// *************************************************************************************** +// 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 +#include +#include +#include +#include + +#include "Types.hpp" +namespace itf { + class ProcessCorner; +} + +namespace ircx { + +class LayoutData; +class TopoPool; +class LayerTable; +class RCTable; +class TopoNode; +class TopoEdge; +class SpefContext; + +class SpefDumper { + public: + SpefDumper() = default; + ~SpefDumper() = default; + + void set_spef_context(const SpefContext* v) { spef_context_ = v; } + void set_layout_data(const LayoutData* v) { layout_data_ = v; } + void set_layer_table(const LayerTable* v) { layer_table_ = v; } + void set_topo_pool(const TopoPool* v) { topo_pool_ = v; } + void set_rc_table(const RCTable* v) { rc_table_ = v; } + void set_corners(const std::vector<::itf::ProcessCorner*>& v) { corners_ = v; } + + // Dump one .spef file for the given corner. + // corner_name: technology string (e.g. "typ", "slow", "fast") used in the + // output filename. + // corner_idx: index into RCTables vectors. + void dump(const Str& output_dir, Size corner_idx) const; + + private: + // Name-map helpers + struct NameMaps { + std::unordered_map net_name_to_id; + std::unordered_map inst_name_to_id; + std::unordered_map port_name_to_id; + std::map net_id_to_name; + std::map inst_id_to_name; + std::map port_id_to_name; + int next_id{1}; + }; + + struct CouplingRef { + Size self_edge_id{kMaxSize}; + Size other_edge_id{kMaxSize}; + double cap_ff{0.0}; + }; + + void buildNameMaps() const; + void buildPortIo() const; + void buildNodeSpefNames() const; + void buildNetSpefNames() const; + void buildCouplingRefs(Size corner_idx) const; + + // Return the SPEF node-name string for a given node. + // - Port pin node : "*" + // - Instance pin node : "*:" + // - Internal node : "*:" + Str nodeName(const TopoNode& node) const; + + // Write helpers + void writeHeader(std::ofstream& ofs) const; + void writeNameMap(std::ofstream& ofs) const; + void writePorts(std::ofstream& ofs) const; + + void writeDNet(std::ostream& os, Size corner_idx, Size net_idx) const; + + const SpefContext* spef_context_{nullptr}; + const LayoutData* layout_data_{nullptr}; + const LayerTable* layer_table_{nullptr}; + const TopoPool* topo_pool_{nullptr}; + const RCTable* rc_table_{nullptr}; + std::vector<::itf::ProcessCorner*> corners_{}; + + mutable NameMaps name_maps_; + mutable std::unordered_map port_io_; + mutable std::vector node_spef_names_; + mutable std::vector net_spef_names_; + mutable std::vector> net_coupling_refs_; + mutable std::vector net_str_buffer_; +}; + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/shell_cmd/CMakeLists.txt b/src/operation/iRCX/source/module/shell_cmd/CMakeLists.txt new file mode 100644 index 000000000..c0a7f5d19 --- /dev/null +++ b/src/operation/iRCX/source/module/shell_cmd/CMakeLists.txt @@ -0,0 +1,23 @@ +set(IRCX_SHELL_CMD_SRC + CmdRCXInit.cc + CmdRCXReport.cc + CmdRCXRun.cc + CmdReadCorner.cc + CmdReadMapping.cc +) + +add_library(rcx_shell_cmd ${IRCX_SHELL_CMD_SRC}) + +target_link_libraries(rcx_shell_cmd + PUBLIC + tcl + PRIVATE + ircx + log + OpenMP::OpenMP_CXX +) + +target_include_directories(rcx_shell_cmd + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/src/operation/iRCX/source/module/shell_cmd/CmdRCXInit.cc b/src/operation/iRCX/source/module/shell_cmd/CmdRCXInit.cc new file mode 100644 index 000000000..85bcb1ecb --- /dev/null +++ b/src/operation/iRCX/source/module/shell_cmd/CmdRCXInit.cc @@ -0,0 +1,68 @@ +// *************************************************************************************** +// 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 CmdRCXInit.cc + * @author Yipei Xu (yipeix@163.com) + * @brief + * @version 0.1 + * @date 2025-12-09 + */ +#include +#include + +#include "RCX.hpp" +#include "rcxShellCmd.hh" +namespace ircx { + +CmdRCXInit::CmdRCXInit(const char* cmd_name) : TclCmd(cmd_name) { + auto thread_option = std::make_unique("-thread", 1, kDefaultThreadCount); + addOption(thread_option.release()); +} + +unsigned CmdRCXInit::check() { + return 1; +} + +unsigned CmdRCXInit::exec() { + if (!check()) { + return 0; + } + + const std::string hello_info = + "\033[49;32m***************************\n" + " _ _____ _____ __ __\n" + " (_)| __ \\ / __ \\\\ \\ / /\n" + " _ | |__) || | \\ V / \n" + " | || _ / | | > < \n" + " | || | \\ \\ | |____ / . \\ \n" + " |_||_| \\_\\ \\_____/_/ \\_\\\n" + "***************************\n" + "WELCOME TO iRCX TCL-shell interface. \e[0m"; + std::cout << hello_info << std::endl; + + RCX& rcx = RCX::getOrCreateInst(); + + TclOption* thread_option = getOptionOrArg("-thread"); + if (thread_option) { + unsigned thread_count = thread_option->getIntVal(); + rcx.set_num_threads(thread_count); + } + + return 1; +} + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/shell_cmd/CmdRCXReport.cc b/src/operation/iRCX/source/module/shell_cmd/CmdRCXReport.cc new file mode 100644 index 000000000..4428d9e96 --- /dev/null +++ b/src/operation/iRCX/source/module/shell_cmd/CmdRCXReport.cc @@ -0,0 +1,55 @@ +// *************************************************************************************** +// 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 CmdReportSpef.cc + * @author Yipei Xu (yipeix@163.com) + * @brief + * @version 0.1 + * @date 2025-12-09 + */ +#include + +#include "RCX.hpp" +#include "rcxShellCmd.hh" +namespace ircx { + +CmdRCXReport::CmdRCXReport(const char* cmd_name) : TclCmd(cmd_name) { + auto file_name_option = std::make_unique("file_name", 1, nullptr); + addOption(file_name_option.release()); + auto geometry_option = std::make_unique("-geometry"); + addOption(geometry_option.release()); +} + +unsigned CmdRCXReport::check() { + TclOption* file_name_option = getOptionOrArg("file_name"); + LOG_FATAL_IF(!file_name_option); + return 1; +} + +unsigned CmdRCXReport::exec() { + if (!check()) { + return 0; + } + + TclOption* file_name_option = getOptionOrArg("file_name"); + const char* output_dir = file_name_option->getStringVal(); + + RCX& rcx = RCX::getOrCreateInst(); + return rcx.reportSpef(output_dir ? output_dir : "."); +} + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/shell_cmd/CmdRCXRun.cc b/src/operation/iRCX/source/module/shell_cmd/CmdRCXRun.cc new file mode 100644 index 000000000..f8d1842af --- /dev/null +++ b/src/operation/iRCX/source/module/shell_cmd/CmdRCXRun.cc @@ -0,0 +1,62 @@ +// *************************************************************************************** +// 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 CmdRCXInit.cc + * @author Yipei Xu (yipeix@163.com) + * @brief + * @version 0.1 + * @date 2025-12-09 + */ +#include +#include + +#include "RCX.hpp" +#include "rcxShellCmd.hh" +namespace ircx { + +CmdRCXRun::CmdRCXRun(const char* cmd_name) : TclCmd(cmd_name) { + +} + +unsigned CmdRCXRun::check() { + return 1; +} + +unsigned CmdRCXRun::exec() { + unsigned ret = 1; + + if (!check()) { + return 0; + } + + 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."; + + return ret; +} + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/shell_cmd/CmdReadCorner.cc b/src/operation/iRCX/source/module/shell_cmd/CmdReadCorner.cc new file mode 100644 index 000000000..d67b87fde --- /dev/null +++ b/src/operation/iRCX/source/module/shell_cmd/CmdReadCorner.cc @@ -0,0 +1,76 @@ +// *************************************************************************************** +// 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 CmdReadCorner.cc + * @author Yipei Xu (yipeix@163.com) + * @brief Tcl command wrapper for binding one ITF with one captab. + * @version 0.1 + * @date 2025-12-08 + */ +#include +#include + +#include "RCX.hpp" +#include "rcxShellCmd.hh" +#include "log/Log.hh" +namespace ircx { + +CmdReadCorner::CmdReadCorner(const char* cmd_name) : TclCmd(cmd_name) +{ + addOption(new TclStringOption("-name", 1, nullptr)); + addOption(new TclStringOption("-itf", 1, nullptr)); + addOption(new TclStringOption("-captab", 1, nullptr)); +} + +unsigned CmdReadCorner::check() +{ + LOG_FATAL_IF(!getOptionOrArg("-name")); + LOG_FATAL_IF(!getOptionOrArg("-itf")); + LOG_FATAL_IF(!getOptionOrArg("-captab")); + return 1; +} + +unsigned CmdReadCorner::exec() +{ + if (!check()) { + return 0; + } + + TclOption* name_option = getOptionOrArg("-name"); + TclOption* itf_option = getOptionOrArg("-itf"); + TclOption* captab_option = getOptionOrArg("-captab"); + + char* corner_name = name_option->getStringVal(); + char* itf_file = itf_option->getStringVal(); + char* captab_file = captab_option->getStringVal(); + + LOG_FATAL_IF(corner_name == nullptr || corner_name[0] == '\0') + << "corner name is empty."; + LOG_FATAL_IF(itf_file == nullptr || itf_file[0] == '\0') + << "itf file is empty."; + LOG_FATAL_IF(captab_file == nullptr || captab_file[0] == '\0') + << "captab file is empty."; + LOG_FATAL_IF(!std::filesystem::exists(itf_file)) + << "itf file not found: " << itf_file; + LOG_FATAL_IF(!std::filesystem::exists(captab_file)) + << "captab file not found: " << captab_file; + + RCX& rcx = RCX::getOrCreateInst(); + return rcx.readCorner(corner_name, itf_file, captab_file); +} + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/shell_cmd/CmdReadMapping.cc b/src/operation/iRCX/source/module/shell_cmd/CmdReadMapping.cc new file mode 100644 index 000000000..3d11d8491 --- /dev/null +++ b/src/operation/iRCX/source/module/shell_cmd/CmdReadMapping.cc @@ -0,0 +1,57 @@ +// *************************************************************************************** +// 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 CmdReadMapping.cc + * @author Yipei Xu (yipeix@163.com) + * @brief + * @version 0.1 + * @date 2025-12-08 + */ +#include +#include + +#include "RCX.hpp" +#include "rcxShellCmd.hh" +#include "log/Log.hh" +namespace ircx { + +CmdReadMapping::CmdReadMapping(const char* cmd_name) : TclCmd(cmd_name) { + auto file_name_option = std::make_unique("file_name", 1, nullptr); + addOption(file_name_option.release()); +} + +unsigned CmdReadMapping::check() { + TclOption* file_name_option = getOptionOrArg("file_name"); + LOG_FATAL_IF(!file_name_option); + return 1; +} + +unsigned CmdReadMapping::exec() { + if (!check()) { + return 0; + } + + TclOption* file_name_option = getOptionOrArg("file_name"); + char* mapping_file = file_name_option->getStringVal(); + LOG_FATAL_IF(!std::filesystem::exists(mapping_file)) << + "mapping file not found: " << mapping_file; + + RCX& rcx = RCX::getOrCreateInst(); + return rcx.readMapping(mapping_file); +} + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/shell_cmd/rcxShellCmd.hh b/src/operation/iRCX/source/module/shell_cmd/rcxShellCmd.hh new file mode 100644 index 000000000..f7e130bf2 --- /dev/null +++ b/src/operation/iRCX/source/module/shell_cmd/rcxShellCmd.hh @@ -0,0 +1,108 @@ +// *************************************************************************************** +// 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 SellCmd.hh + * @author Yipei Xu (yipeix@163.com) + * @brief + * @version 0.1 + * @date 2025-12-08 + */ +#pragma once + +#include "ScriptEngine.hh" +namespace ircx { + +using ieda::ScriptEngine; +using ieda::TclCmd; +using ieda::TclCmds; +using ieda::TclDoubleListOption; +using ieda::TclDoubleOption; +using ieda::TclEncodeResult; +using ieda::TclIntListOption; +using ieda::TclIntOption; +using ieda::TclOption; +using ieda::TclStringListListOption; +using ieda::TclStringListOption; +using ieda::TclStringOption; +using ieda::TclSwitchOption; + +/** + * @brief Initialize RCX. + * + */ +class CmdRCXInit : public TclCmd { + public: + explicit CmdRCXInit(const char* cmd_name); + ~CmdRCXInit() override = default; + + unsigned check(); + unsigned exec(); +}; + +/** + * @brief Run RCX. + * + */ +class CmdRCXRun : public TclCmd { + public: + explicit CmdRCXRun(const char* cmd_name); + ~CmdRCXRun() override = default; + + unsigned check(); + unsigned exec(); +}; + +/** + * @brief Report RCX. + * + */ +class CmdRCXReport : public TclCmd { + public: + explicit CmdRCXReport(const char* cmd_name); + ~CmdRCXReport() override = default; + + unsigned check(); + unsigned exec(); +}; + +/** + * @brief Read one corner binding. + * + */ +class CmdReadCorner : public TclCmd { + public: + explicit CmdReadCorner(const char* cmd_name); + ~CmdReadCorner() override = default; + + unsigned check(); + unsigned exec(); +}; + +/** + * @brief Read mapping file. + * + */ +class CmdReadMapping : public TclCmd { + public: + explicit CmdReadMapping(const char* cmd_name); + ~CmdReadMapping() override = default; + + unsigned check(); + unsigned exec(); +}; + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/topology/CMakeLists.txt b/src/operation/iRCX/source/module/topology/CMakeLists.txt new file mode 100644 index 000000000..632413ced --- /dev/null +++ b/src/operation/iRCX/source/module/topology/CMakeLists.txt @@ -0,0 +1,20 @@ +set(IRCX_TOPOLOGY_SRC + TopoPool.cpp + TopologyBuilder.cpp +) + +add_library(ircx_topology ${IRCX_TOPOLOGY_SRC}) + +target_link_libraries(ircx_topology + PUBLIC + ircx_headers + PRIVATE + log + OpenMP::OpenMP_CXX + usage +) + +target_include_directories(ircx_topology + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/src/operation/iRCX/source/module/topology/TopoPool.cpp b/src/operation/iRCX/source/module/topology/TopoPool.cpp new file mode 100644 index 000000000..6bd1c1a89 --- /dev/null +++ b/src/operation/iRCX/source/module/topology/TopoPool.cpp @@ -0,0 +1,120 @@ +// *************************************************************************************** +// 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 + +#include "TopoPool.hpp" +#include "log/Log.hh" +namespace ircx { + +// TopoEdge + +void TopoEdge::set_shape(const GtlRectI& v) { + shape_ = v; + + line_seg_.is_horz = geom::IsHorDominant(shape_); + + width_ = line_seg_.is_horz ? geom::DeltaY(shape_) : geom::DeltaX(shape_); + half_width_ = width_ / 2; + length_ = line_seg_.is_horz ? geom::DeltaX(shape_) : geom::DeltaY(shape_); + center_ = geom::Center(shape_); + + line_seg_.fixed = line_seg_.is_horz ? geom::CenterY(shape_) : geom::CenterX(shape_); + line_seg_.a0 = line_seg_.is_horz ? geom::MinX(shape_) : geom::MinY(shape_); + line_seg_.a1 = line_seg_.is_horz ? geom::MaxX(shape_) : geom::MaxY(shape_); +} + +// TopoPool + +void TopoPool::reserve(Size net_count, Size total_nodes, Size total_edges) { + net_node_ranges_.reserve(net_count); + net_edge_ranges_.reserve(net_count); + node_pool_.reserve(total_nodes); + edge_pool_.reserve(total_edges); +} + +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]; + return std::span(node_pool_).subspan(offset, count); +} + +std::span TopoPool::net_edges(Size net_id) const { + LOG_FATAL_IF(net_id >= net_edge_ranges_.size()) << "net_id out of range."; + auto [offset, count] = net_edge_ranges_[net_id]; + return std::span(edge_pool_).subspan(offset, count); +} + +std::span TopoPool::net_nodes(Size net_id) { + LOG_FATAL_IF(net_id >= net_node_ranges_.size()) << "net_id out of range."; + auto [offset, count] = net_node_ranges_[net_id]; + return std::span(node_pool_).subspan(offset, count); +} + +std::span TopoPool::net_edges(Size net_id) { + LOG_FATAL_IF(net_id >= net_edge_ranges_.size()) << "net_id out of range."; + auto [offset, count] = net_edge_ranges_[net_id]; + return std::span(edge_pool_).subspan(offset, count); +} + +std::pair TopoPool::net_node_range(Size net_id) const { + LOG_FATAL_IF(net_id >= net_node_ranges_.size()) << "net_id out of range."; + return net_node_ranges_[net_id]; +} + +std::pair TopoPool::net_edge_range(Size net_id) const { + LOG_FATAL_IF(net_id >= net_edge_ranges_.size()) << "net_id out of range."; + return net_edge_ranges_[net_id]; +} + +void TopoPool::addNet(std::vector nodes, + std::vector edges) +{ + const Size node_off = node_pool_.size(); + const Size node_cnt = nodes.size(); + const Size edge_off = edge_pool_.size(); + const Size edge_cnt = edges.size(); + + // Assign LOCAL ids (0..count-1) for the id() accessor. + for (Size node_idx = 0; node_idx < nodes.size(); ++node_idx) nodes[node_idx].set_id(node_idx); + for (Size edge_idx = 0; edge_idx < edges.size(); ++edge_idx) edges[edge_idx].set_id(edge_idx); + + net_node_ranges_.emplace_back(node_off, node_cnt); + net_edge_ranges_.emplace_back(edge_off, edge_cnt); + + node_pool_.reserve(node_off + node_cnt); + edge_pool_.reserve(edge_off + edge_cnt); + + node_pool_.insert(node_pool_.end(), + std::make_move_iterator(nodes.begin()), + std::make_move_iterator(nodes.end())); + edge_pool_.insert(edge_pool_.end(), + std::make_move_iterator(edges.begin()), + std::make_move_iterator(edges.end())); + +} + +void TopoPool::addSpecialEdges(std::vector edges) +{ + for (Size edge_idx = 0; edge_idx < edges.size(); ++edge_idx) edges[edge_idx].set_id(edge_idx); + + special_edge_pool_.reserve(special_edge_pool_.size() + edges.size()); + special_edge_pool_.insert(special_edge_pool_.end(), + std::make_move_iterator(edges.begin()), + std::make_move_iterator(edges.end())); +} + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/topology/TopoPool.hpp b/src/operation/iRCX/source/module/topology/TopoPool.hpp new file mode 100644 index 000000000..9515683d0 --- /dev/null +++ b/src/operation/iRCX/source/module/topology/TopoPool.hpp @@ -0,0 +1,214 @@ +// *************************************************************************************** +// 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 +#include +#include + +#include "Geoms.hpp" +#include "Types.hpp" +namespace ircx { + +class TopoPool; + +// ============================================================ +// TopoNode +// ============================================================ +class TopoNode { + public: + explicit TopoNode(Size id) : net_id_(id) {} + TopoNode() = delete; + ~TopoNode() = default; + + // Per-net local id assigned by TopoPool::addNet(). + // For a global flat-pool index, use TopoPool::node_index(node). + Size id() const { return id_; } + Size net_id() const { return net_id_; } + + Size layer_id() const { return layer_id_; } + void set_layer_id(Size id) { layer_id_ = id; } + + const GtlPointI& point() const { return point_; } + void set_point(const GtlPointI& v) { point_ = v; } + + const GtlRectI& shape() const { return shape_; } + void set_shape(const GtlRectI& v) { shape_ = v; } + + // pin + bool is_pin_node() const { return !pin_name_.empty(); } + const Str& pin_name() const { return pin_name_; } + void set_pin_name(const Str& v) { pin_name_ = v; } + + private: + friend class TopoPool; // only pool can assign local ids + void set_id(Size id) { id_ = id; } + Size id_{kMaxSize}; + + Size net_id_{kMaxSize}; + + Size layer_id_{kMaxSize}; + GtlPointI point_; + GtlRectI shape_; + + Str pin_name_; +}; + +// ============================================================ +// TopoEdge +// ============================================================ +class TopoEdge { + public: + explicit TopoEdge(Size id) : net_id_(id) {} + TopoEdge() = default; + ~TopoEdge() = default; + + // Per-net local id assigned by TopoPool::addNet(). + // For a global flat-pool index, use TopoPool::edge_index(edge). + Size id() const { return id_; } + Size net_id() const { return net_id_; } + + // via + bool is_via() const { return !via_name_.empty(); } + Str via_name() const { return via_name_; } + void set_via_name(const Str& name) { via_name_ = name; } + + // u_ and v_ are GLOBAL node indices (direct index into TopoPool::node_pool_). + // kMaxSize when no graph node is associated (e.g. special-net edges, vias with no top/btm). + Size u() const { return u_; } + void set_u(Size u) { u_ = u; } + + Size v() const { return v_; } + void set_v(Size v) { v_ = v; } + + Size layer_id() const { return layer_id_; } + void set_layer_id(Size id) { layer_id_ = id; } + + const GtlRectI& shape() const { return shape_; } + void set_shape(const GtlRectI& v); + + Dbu half_width() const { return half_width_; } + Dbu width() const { return width_; } + Dbu length() const { return length_; } + GtlPointI center() const { return center_; } + + const LineSegmentI& line_segment() const { return line_seg_; } + bool is_horz() const { return line_seg_.is_horz; } + Dbu fixed() const { return line_seg_.fixed; } + Dbu a0() const { return line_seg_.a0; } + Dbu a1() const { return line_seg_.a1; } + + private: + friend class TopoPool; // only pool can assign local ids + void set_id(Size id) { id_ = id; } + + Size id_{kMaxSize}; + Size net_id_{kMaxSize}; + Str via_name_; + // GLOBAL node indices into TopoPool::node_pool_. + // kMaxSize → no associated node (special-net edges, incomplete vias). + Size u_{kMaxSize}; + Size v_{kMaxSize}; + + Size layer_id_{kMaxSize}; + GtlRectI shape_; + + Dbu width_{0}; + Dbu half_width_{0}; + Dbu length_{0}; + GtlPointI center_{}; + LineSegmentI line_seg_{}; +}; + +// ============================================================ +// TopoPool +// ============================================================ +class TopoPool { + public: + TopoPool() = default; + ~TopoPool() = default; + + // Flat pool access (used by environment, process, capacitance modules) + std::vector& node_pool() { return node_pool_; } + const std::vector& node_pool() const { return node_pool_; } + std::vector& edge_pool() { return edge_pool_; } + const std::vector& edge_pool() const { return edge_pool_; } + + // Global access by index + // Use e.u() / e.v() directly as the argument. + TopoNode& node_at(Size id) { return node_pool_[id]; } + const TopoNode& node_at(Size id) const { return node_pool_[id]; } + TopoEdge& edge_at(Size id) { return edge_pool_[id]; } + const TopoEdge& edge_at(Size id) const { return edge_pool_[id]; } + + // Flat-pool index of an object already stored in the regular pool. + Size node_index(const TopoNode& e) const { return &e - node_pool_.data(); } + Size edge_index(const TopoEdge& e) const { return &e - edge_pool_.data(); } + + // Translate (net id, local per-net id) into a flat regular-pool index. + // This mapping is only defined for node_pool_/edge_pool_, never for special_edge_pool_. + Size node_index(Size netid, Size id) const { + const auto& [offset, _] = net_node_ranges_[netid]; + return offset + id; + } + Size edge_index(Size netid, Size id) const { + const auto& [offset, _] = net_edge_ranges_[netid]; + return offset + id; + } + + // Per-net spans + std::span net_nodes(Size net_id) const; + std::span net_edges(Size net_id) const; + std::span net_nodes(Size net_id); + std::span net_edges(Size net_id); + + std::pair net_node_range(Size net_id) const; + std::pair net_edge_range(Size net_id) const; + // Special-net edges are stored in a dedicated pool outside the regular flat edge_pool_. + // Conventions: + // 1. net_id() == kSpecialNetId + // 2. id() is local only within special_edge_pool_ + // 3. u()/v() stay kMaxSize because special edges do not participate in the RC graph + // 4. these edges are used only as extraction context and are excluded from regular + // per-net traversal and SPEF connectivity output + std::vector& special_edge_pool() { return special_edge_pool_; } + const std::vector& special_edge_pool() const { return special_edge_pool_; } + + // 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); + + // Build interface (called by TopologyBuilder) + // Assigns per-net local edge/node ids and appends nodes and edges into the flat pools. + // Node references stored in edges are expected to already use global pool indices. + void addNet(std::vector nodes, + std::vector edges); + + // Append special-net edges into the dedicated special_edge_pool_. + void addSpecialEdges(std::vector edges); + + private: + std::vector node_pool_; + std::vector> net_node_ranges_; + + std::vector edge_pool_; + std::vector> net_edge_ranges_; + + std::vector special_edge_pool_; +}; + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/topology/TopologyBuilder.cpp b/src/operation/iRCX/source/module/topology/TopologyBuilder.cpp new file mode 100644 index 000000000..bdd83b386 --- /dev/null +++ b/src/operation/iRCX/source/module/topology/TopologyBuilder.cpp @@ -0,0 +1,234 @@ +// *************************************************************************************** +// 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 + +#include "TopologyBuilder.hpp" +#include "HashFactory.hpp" +#include "LayoutData.hpp" +namespace ircx { + +// --------------------------------------------------------------------------- +// build_one_ (stateless – safe to call from multiple threads simultaneously) +// --------------------------------------------------------------------------- +TopologyBuilder::NetTopo TopologyBuilder::build_one_(const Net& net) const { + TopologyBuilder::NetTopo result; + + const Size net_id = net.id; + + auto& nodes = result.nodes; + auto& edges = result.edges; + + // Helpers that write only into this net's local vectors. + auto append_node = [&](TopoNode node) -> Size { + nodes.push_back(std::move(node)); + return nodes.size() - 1; + }; + auto append_edge = [&](TopoEdge edge) -> Size { + edges.push_back(std::move(edge)); + return edges.size() - 1; + }; + + // (layer, point) -> local node index + std::unordered_map, Size, + HashFactory::LayerPointHash> local_node_index_by_key; + + // Track whether each pin has already been matched to a topology node. + std::map pin_consumed; + for (const auto& pin : net.pins) { + pin_consumed[pin.name] = false; + } + + // Match a (layer_id, point) against the remaining pin entries. + // Returns the matched pin name and marks it consumed; returns empty if none matches. + auto match_pin_name = [&](Size layer_id, GtlPointI point) -> Str { + for (const auto& pin : net.pins) { + if (pin_consumed[pin.name]) continue; + + for (const auto& [pin_layer_id, pin_rect] : pin.layer_id_rects) { + if (layer_id != pin_layer_id) continue; + if (geom::RectContainsPoint(pin_rect, point)) { + pin_consumed[pin.name] = true; + return pin.name; + } + } + } + return {}; + }; + + // Single-pass node building: iterate segments then vias, deduplicating via + // local_node_index_by_key. This replaces the original two-pass approach (separate + // points-set collection followed by node construction). + auto append_node_if_absent = [&](Size layer_id, GtlPointI point) { + const auto node_key = std::make_pair(layer_id, point); + if (local_node_index_by_key.count(node_key)) return; + + TopoNode node(net_id); + node.set_layer_id(layer_id); + node.set_point(point); + node.set_shape(geom::BoxAround(point, 1)); + + node.set_pin_name(match_pin_name(layer_id, point)); + + const Size node_idx = append_node(std::move(node)); + local_node_index_by_key[node_key] = node_idx; + }; + + for (const Segment& wire : net.segments) { + const Size layer_id = wire.layer_id; + append_node_if_absent(layer_id, wire.p0); + append_node_if_absent(layer_id, wire.p1); + } + + for (const Via& via : net.vias) { + const GtlPointI via_point = via.point; + append_node_if_absent(via.layer_rect_top.first, via_point); + append_node_if_absent(via.layer_rect_btm.first, via_point); + } + + // edges: wire — u_/v_ are LOCAL node indices here; remapped to global in addNet() + for (const Segment& wire : net.segments) { + const Size layer_id = wire.layer_id; + const GtlPointI start_point = wire.p0; + const GtlPointI end_point = wire.p1; + + const Size start_node_idx = local_node_index_by_key.at({layer_id, start_point}); + const Size end_node_idx = local_node_index_by_key.at({layer_id, end_point}); + + TopoEdge edge(net_id); + edge.set_layer_id(layer_id); + if (geom::IsLeftBottom(start_point, end_point)) { + edge.set_u(start_node_idx); + edge.set_v(end_node_idx); + } else { + edge.set_u(end_node_idx); + edge.set_v(start_node_idx); + } + + edge.set_shape(wire.rect); + append_edge(std::move(edge)); + } + + // edges: via — u_/v_ are LOCAL node indices here; remapped to global in addNet() + for (const Via& via : net.vias) { + const Size top_layer_id = via.layer_rect_top.first; + const Size cut_layer_id = via.layer_rect_cut.first; + const Size bottom_layer_id = via.layer_rect_btm.first; + + const GtlRectI cut_rect = via.layer_rect_cut.second; + const GtlPointI via_point = via.point; + + const Size top_node_idx = local_node_index_by_key.at({top_layer_id, via_point}); + const Size bottom_node_idx = local_node_index_by_key.at({bottom_layer_id, via_point}); + + TopoEdge edge(net_id); + edge.set_layer_id(cut_layer_id); + edge.set_u(top_node_idx); + edge.set_v(bottom_node_idx); + edge.set_shape(cut_rect); + edge.set_via_name(via.name); + + append_edge(std::move(edge)); + } + + return result; +} + +// --------------------------------------------------------------------------- +// build_all – two-phase parallel build +// --------------------------------------------------------------------------- +void TopologyBuilder::build_all(const LayoutData& ld) const { + if (!topo_pool_) return; + + const std::vector& regular_nets = ld.net_vec; + const Size net_count = ld.regular_net_count(); + + // Pre-allocate to avoid any shared-container writes during the parallel phase. + std::vector net_topologies(net_count); + + // Phase 1: Build each net's topology into independent local storage. + // - No shared mutable state → threads operate completely independently. + // - dynamic scheduling handles variable net sizes efficiently. + #pragma omp parallel for schedule(dynamic) + for (Size net_idx = 0; net_idx < net_count; ++net_idx) { + net_topologies[net_idx] = std::move(build_one_(regular_nets[net_idx])); + } + + // Phase 1.5: Pre-reserve pools (eliminates all reallocations in Phase 2) + { + Size total_nodes = 0, total_edges = 0; + for (const auto& net_topology : net_topologies) { + total_nodes += net_topology.nodes.size(); + total_edges += net_topology.edges.size(); + } + topo_pool_->reserve(net_count, total_nodes, total_edges); + } + + // Phase 2: Serially merge all per-net results into the shared contiguous + // pools. This step is O(total_elements) memory moves – fast in practice – + // and preserves the cache-friendly layout expected by downstream code. + + for (auto& net_topology : net_topologies) { + topo_pool_->addNet(std::move(net_topology.nodes), std::move(net_topology.edges)); + } + + // Phase 3: in edge, local node id → global id + #pragma omp parallel for schedule(dynamic) + for (Size net_idx = 0; net_idx < net_count; ++net_idx) { + auto net_edges = topo_pool_->net_edges(net_idx); + for (auto& net_edge : net_edges) { + const Size local_u = net_edge.u(); + const Size local_v = net_edge.v(); + net_edge.set_u(topo_pool_->node_index(net_idx, local_u)); + net_edge.set_v(topo_pool_->node_index(net_idx, local_v)); + } + } +} + +// --------------------------------------------------------------------------- +// build_special +// --------------------------------------------------------------------------- +void TopologyBuilder::build_special(const LayoutData& ld) const { + if (!topo_pool_) return; + + const Net& special_net = ld.special_net; + + const Size segment_count = special_net.segments.size(); + const Size patch_count = special_net.patches.size(); + + std::vector edges(segment_count + patch_count, TopoEdge(kSpecialNetId)); + + auto set_edge_shape = [&](Size edge_idx, Size layer_id, const GtlRectI& rect) { + edges[edge_idx].set_layer_id(layer_id); + edges[edge_idx].set_shape(rect); + }; + + #pragma omp parallel for schedule(dynamic) + for (Size segment_idx = 0; segment_idx < segment_count; ++segment_idx) { + const Segment& segment = special_net.segments[segment_idx]; + set_edge_shape(segment_idx, segment.layer_id, segment.rect); + } + + #pragma omp parallel for schedule(dynamic) + for (Size patch_idx = 0; patch_idx < patch_count; ++patch_idx) { + const Patch& patch = special_net.patches[patch_idx]; + set_edge_shape(patch_idx + segment_count, patch.layer_id, patch.rect); + } + + topo_pool_->addSpecialEdges(std::move(edges)); +} + +} // namespace ircx diff --git a/src/operation/iRCX/source/module/topology/TopologyBuilder.hpp b/src/operation/iRCX/source/module/topology/TopologyBuilder.hpp new file mode 100644 index 000000000..21ee1f7b4 --- /dev/null +++ b/src/operation/iRCX/source/module/topology/TopologyBuilder.hpp @@ -0,0 +1,67 @@ +// *************************************************************************************** +// 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 +#include +#include +#include +#include + +#include "TopoPool.hpp" +namespace ircx { + +class LayoutData; +class Net; +class SpecialNet; + + +class TopologyBuilder { + public: + explicit TopologyBuilder(TopoPool& topologies) : topo_pool_(&topologies) {} + TopologyBuilder() = delete; + ~TopologyBuilder() = default; + + // Result of building a single net's topology. + // Holds all data until it can be merged into the shared TopoPool. + struct NetTopo { + std::vector nodes; + std::vector edges; + }; + + // Build all regular-net topologies. Phase 1 builds each net in parallel into + // independent local storage; Phase 2 serially merges results into the shared + // contiguous pools, preserving cache-friendly layout. + void build_all(const LayoutData& ld) const; + + // Build a topology for the special net (power/ground) and store it in + // topo_pool_'s dedicated special_edge_pool(). Each per-layer rectangle of every + // segment and Patch becomes one TopoEdge with net_id = kSpecialNetId + // (no nodes; graph connectivity is not needed for special nets). + // Must be called after build_all(). + void build_special(const LayoutData& ld) const; + + private: + // Build one net's topology into independent local storage (no shared state). + // Safe to call from multiple threads simultaneously on different nets. + NetTopo build_one_(const Net& net) const; + + private: + TopoPool* topo_pool_{nullptr}; +}; + +} // namespace ircx diff --git a/src/operation/iRCX/source/parser/CMakeLists.txt b/src/operation/iRCX/source/parser/CMakeLists.txt new file mode 100644 index 000000000..5149bd581 --- /dev/null +++ b/src/operation/iRCX/source/parser/CMakeLists.txt @@ -0,0 +1,5 @@ +add_subdirectory(itf) +add_subdirectory(itf_builder) +add_subdirectory(itf_data) +add_subdirectory(mapping) +add_subdirectory(cap_table) \ No newline at end of file diff --git a/src/operation/iRCX/source/parser/cap_table/CMakeLists.txt b/src/operation/iRCX/source/parser/cap_table/CMakeLists.txt new file mode 100644 index 000000000..6f505a88b --- /dev/null +++ b/src/operation/iRCX/source/parser/cap_table/CMakeLists.txt @@ -0,0 +1,11 @@ +add_library(ircx_cap_table CapTable.cpp) + +target_link_libraries(ircx_cap_table + PRIVATE + log +) + +target_include_directories(ircx_cap_table + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/src/operation/iRCX/source/parser/cap_table/CapTable.cpp b/src/operation/iRCX/source/parser/cap_table/CapTable.cpp new file mode 100644 index 000000000..ba35dba5a --- /dev/null +++ b/src/operation/iRCX/source/parser/cap_table/CapTable.cpp @@ -0,0 +1,396 @@ +// *************************************************************************************** +// 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 +#include + +#include "CapTable.hpp" +#include "log/Log.hh" +namespace ircx { +namespace parser { + +namespace { + +std::string formatConfigKey(const std::string& layer_name, + const std::string& overLayer, + const std::string& underLayer) +{ + std::ostringstream oss; + oss << layer_name << " OVER " << overLayer; + if (!underLayer.empty()) { + oss << " UNDER " << underLayer; + } + return oss.str(); +} + +} // namespace + +bool CapTable::loadFromFile(const std::string& filePath) +{ + std::ifstream file(filePath); + if (!file.is_open()) { + LOG_ERROR << "Failed to open cap table file: " << filePath; + return false; + } + + std::stringstream buffer; + buffer << file.rdbuf(); + return loadFromString(buffer.str()); +} + +bool CapTable::loadFromString(const std::string& content) +{ + configs_.clear(); + + std::istringstream iss(content); + std::string line; + std::string headerLine; + std::vector dataLines; + + while (std::getline(iss, line)) { + // Trim leading/trailing whitespace. + line.erase(0, line.find_first_not_of(" \t\r\n")); + line.erase(line.find_last_not_of(" \t\r\n") + 1); + + // Skip empty lines and comments. + if (line.empty() || line[0] == '#') { + continue; + } + + // Detect a new-format header line. + bool isHeader = false; + if (line.size() >= 2) { + if ((line[0] == 'A' || line[0] == 'B') && line[1] == ' ') { + if (line.find("OVER") != std::string::npos) { + isHeader = true; + } + } + } + + if (isHeader) { + if (!headerLine.empty()) { + parseConfig(headerLine, dataLines); + dataLines.clear(); + } + headerLine = line; + } else { + if (!headerLine.empty()) { + dataLines.push_back(line); + } + } + } + + if (!headerLine.empty()) { + parseConfig(headerLine, dataLines); + } + + LOG_INFO << "Loaded " << configs_.size() << " cap table configs"; + return !configs_.empty(); +} + +bool CapTable::parseConfig(const std::string& headerLine, + const std::vector& dataLines) +{ + if (headerLine.empty() || dataLines.empty()) { + return false; + } + + std::istringstream iss(headerLine); + std::string type, layer_name, overKw, overLayer; + + if (!(iss >> type >> layer_name >> overKw >> overLayer)) { + LOG_ERROR << "Invalid cap table header format: " << headerLine; + return false; + } + + if (type != "A" && type != "B") { + LOG_ERROR << "Invalid cap table type (expected A or B): " << type; + return false; + } + + if (overKw != "OVER") { + LOG_ERROR << "Missing OVER keyword in cap table header: " << headerLine; + return false; + } + + std::string underLayer; + std::string underKw; + if (iss >> underKw) { + if (underKw == "UNDER") { + if (!(iss >> underLayer)) { + LOG_ERROR << "Missing layer after UNDER in cap table header: " << headerLine; + return false; + } + } else { + LOG_ERROR << "Unexpected keyword in cap table header (expected UNDER): " << underKw; + return false; + } + } + + if (type == "A" && !underLayer.empty()) { + LOG_ERROR << "Type A should not have UNDER clause: " << headerLine; + return false; + } + if (type == "B" && underLayer.empty()) { + LOG_ERROR << "Type B must have UNDER clause: " << headerLine; + return false; + } + + std::string key = makeKey(layer_name, overLayer, underLayer); + + CapTableConfig config; + config.key = key; + config.type = type; + config.layer_name = layer_name; + config.over_layer = overLayer; + config.under_layer = underLayer; + + for (const auto& dataLine : dataLines) { + std::istringstream dataIss(dataLine); + CapTableEntry entry; + double couplingCap = 0.0; + double groundCap = 0.0; + if (!(dataIss >> entry.distance >> couplingCap >> groundCap)) { + LOG_ERROR << "Invalid cap table data line: " << dataLine; + continue; + } + entry.coupling_cap = couplingCap; + entry.ground_cap = groundCap; + config.data.push_back(entry); + } + + if (config.data.empty()) { + LOG_ERROR << "No valid data for cap table config: " << key; + return false; + } + + configs_[key] = config; + return true; +} + +const CapTableConfig* CapTable::get_config( + const std::string& layer_name, + const std::string& overLayer, + const std::string& underLayer) const +{ + std::string key = makeKey(layer_name, overLayer, underLayer); + auto iter = configs_.find(key); + if (iter != configs_.end()) { + return &(iter->second); + } + return nullptr; +} + +std::vector CapTable::get_all_keys() const +{ + std::vector keys; + keys.reserve(configs_.size()); + for (const auto& [key, config] : configs_) { + keys.push_back(key); + } + return keys; +} + +std::string CapTable::makeKey(const std::string& layer_name, + const std::string& overLayer, + const std::string& underLayer) const +{ + std::ostringstream oss; + if (underLayer.empty()) { + oss << "A " << layer_name << " OVER " << overLayer; + } else { + oss << "B " << layer_name << " OVER " << overLayer << " UNDER " << underLayer; + } + return oss.str(); +} + +CapacitanceResult CapTable::interpolate( + const std::string& layer_name, + const std::string& overLayer, + const std::string& underLayer, + double neighborDistance) const +{ + CapacitanceResult result; + + const CapTableConfig* config = get_config(layer_name, overLayer, underLayer); + if (!config) { + LOG_ERROR << "Cap table config not found: " + << formatConfigKey(layer_name, overLayer, underLayer); + return result; + } + + const auto& data = config->data; + + if (data.empty()) { + LOG_ERROR << "No data points in cap table config: " << config->key; + return result; + } + + if (neighborDistance < 0.0) { + return isolatedResult(*config); + } + + if (data.size() == 1) { + result.ground_cap = data[0].ground_cap; + result.coupling_cap = data[0].coupling_cap; + return result; + } + + auto [idx1, idx2] = findBracketingIndices(data, neighborDistance); + + if (idx1 < 0 || idx2 < 0) { + if (neighborDistance <= data.front().distance) { + result.ground_cap = data.front().ground_cap; + result.coupling_cap = data.front().coupling_cap; + } else { + result.ground_cap = data.back().ground_cap; + result.coupling_cap = data.back().coupling_cap; + } + return result; + } + + const auto& p1 = data[idx1]; + const auto& p2 = data[idx2]; + + result.ground_cap = linearInterpolate( + neighborDistance, p1.distance, p1.ground_cap, p2.distance, p2.ground_cap); + result.coupling_cap = linearInterpolate( + neighborDistance, p1.distance, p1.coupling_cap, p2.distance, p2.coupling_cap); + + return result; +} + +CapacitanceResult CapTable::isolatedResult(const CapTableConfig& config) const +{ + CapacitanceResult result = farthestResult(config); + result.coupling_cap = 0.0; + return result; +} + +CapacitanceResult CapTable::farthestResult(const CapTableConfig& config) const +{ + CapacitanceResult result; + if (config.data.empty()) { + return result; + } + + const auto& last = config.data.back(); + result.ground_cap = last.ground_cap; + result.coupling_cap = last.coupling_cap; + return result; +} + +double CapTable::linearInterpolate( + double x, double x1, double y1, double x2, double y2) const +{ + if (std::abs(x2 - x1) < 1e-9) { + return (y1 + y2) / 2.0; + } + return y1 + (((y2 - y1) * (x - x1)) / (x2 - x1)); +} + +std::pair CapTable::findBracketingIndices( + const std::vector& data, + double distance) const +{ + if (data.size() < 2) { + return {-1, -1}; + } + + for (size_t i = 0; i < data.size() - 1; ++i) { + if (data[i].distance <= distance && distance <= data[i + 1].distance) { + return {static_cast(i), static_cast(i + 1)}; + } + } + + return {-1, -1}; +} + +// ======================== 查询 ======================== + +CapacitanceResult CapTable::queryTwoLayerCap( + const std::string& layer_name, + const std::string& belowLayer, + double neighborDistance) const +{ + return interpolate(layer_name, belowLayer, "", neighborDistance); +} + +CapacitanceResult CapTable::queryThreeLayerCap( + const std::string& layer_name, + const std::string& belowLayer, + const std::string& aboveLayer, + double neighborDistance) const +{ + return interpolate(layer_name, belowLayer, aboveLayer, neighborDistance); +} + +CapacitanceResult CapTable::queryTwoLayerIsolatedCap( + const std::string& layer_name, + const std::string& belowLayer) const +{ + const CapTableConfig* config = get_config(layer_name, belowLayer, ""); + if (!config) { + LOG_ERROR << "Cap table config not found: " + << formatConfigKey(layer_name, belowLayer, ""); + return {}; + } + return isolatedResult(*config); +} + +CapacitanceResult CapTable::queryThreeLayerIsolatedCap( + const std::string& layer_name, + const std::string& belowLayer, + const std::string& aboveLayer) const +{ + const CapTableConfig* config = get_config(layer_name, belowLayer, aboveLayer); + if (!config) { + LOG_ERROR << "Cap table config not found: " + << formatConfigKey(layer_name, belowLayer, aboveLayer); + return {}; + } + return isolatedResult(*config); +} + +CapacitanceResult CapTable::queryTwoLayerFarthestCap( + const std::string& layer_name, + const std::string& belowLayer) const +{ + const CapTableConfig* config = get_config(layer_name, belowLayer, ""); + if (!config) { + LOG_ERROR << "Cap table config not found: " + << formatConfigKey(layer_name, belowLayer, ""); + return {}; + } + return farthestResult(*config); +} + +CapacitanceResult CapTable::queryThreeLayerFarthestCap( + const std::string& layer_name, + const std::string& belowLayer, + const std::string& aboveLayer) const +{ + const CapTableConfig* config = get_config(layer_name, belowLayer, aboveLayer); + if (!config) { + LOG_ERROR << "Cap table config not found: " + << formatConfigKey(layer_name, belowLayer, aboveLayer); + return {}; + } + return farthestResult(*config); +} + +} // namespace parser +} // namespace ircx diff --git a/src/operation/iRCX/source/parser/cap_table/CapTable.hpp b/src/operation/iRCX/source/parser/cap_table/CapTable.hpp new file mode 100644 index 000000000..8deedcb40 --- /dev/null +++ b/src/operation/iRCX/source/parser/cap_table/CapTable.hpp @@ -0,0 +1,208 @@ +// *************************************************************************************** +// 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 +#include +#include +#include +#include +namespace ircx { +namespace parser { + +/** + * @brief One lookup row from a captab configuration. + * + * Each row is encoded as: + * distance coupling_cap ground_cap + */ +struct CapTableEntry { + double distance{0.0}; // same-layer spacing (um) + double ground_cap{0.0}; // ground capacitance (fF/um) + double coupling_cap{0.0}; // coupling capacitance (fF/um) +}; + +/** + * @brief All lookup rows for one captab environment. + * + * Key examples: + * - A-series (no upper layer): "A M2 OVER M1" + * - A-series (to substrate): "A M1 OVER SUBSTRATE" + * - B-series (upper+lower): "B M2 OVER M1 UNDER M3" + */ +struct CapTableConfig { + std::string key; + std::string type; + std::string layer_name; + std::string over_layer; + std::string under_layer; + std::vector data; +}; + +/** + * @brief Capacitance lookup result. + */ +struct CapacitanceResult { + double ground_cap{0.0}; + double coupling_cap{0.0}; +}; + +/** + * @brief Unified captab parser, interpolator, and query API. + * + * Supports only the new captab format: + * ``` + * A M1 OVER SUBSTRATE + * 0.045 0.098217 0.014883 + * 0.090 0.056322 0.016892 + * ... + * + * B M1 OVER SUBSTRATE UNDER M2 + * 0.045 0.025255 0.092115 + * 0.090 0.032732 0.048268 + * ... + * ``` + */ +class CapTable { + public: + CapTable() = default; + ~CapTable() = default; + + // Parsing + + /** + * @brief Load captab content from a file. + */ + bool loadFromFile(const std::string& filePath); + + /** + * @brief Load captab content from a string buffer. + */ + bool loadFromString(const std::string& content); + + /** + * @brief Return all stored captab keys. + */ + std::vector get_all_keys() const; + + /** + * @brief Number of stored captab configurations. + */ + size_t size() const { return configs_.size(); } + + // Queries + + /** + * @brief Query an A-series table (two-layer context). + * + * The lookup distance is the non-negative same-layer spacing in microns. + * For isolated/no-neighbor cases, use queryTwoLayerIsolatedCap(). + * + * Negative distances are treated as a legacy compatibility path and map + * to the isolated result. + */ + CapacitanceResult queryTwoLayerCap( + const std::string& layer_name, + const std::string& belowLayer, + double neighborDistance) const; + + /** + * @brief Query a B-series table (three-layer context). + * + * The lookup distance is the non-negative same-layer spacing in microns. + * For isolated/no-neighbor cases, use queryThreeLayerIsolatedCap(). + * + * Negative distances are treated as a legacy compatibility path and map + * to the isolated result. + */ + CapacitanceResult queryThreeLayerCap( + const std::string& layer_name, + const std::string& belowLayer, + const std::string& aboveLayer, + double neighborDistance) const; + + /** + * @brief Query the isolated/fringe result for an A-series table. + */ + CapacitanceResult queryTwoLayerIsolatedCap( + const std::string& layer_name, + const std::string& belowLayer) const; + + /** + * @brief Query the isolated/fringe result for a B-series table. + */ + CapacitanceResult queryThreeLayerIsolatedCap( + const std::string& layer_name, + const std::string& belowLayer, + const std::string& aboveLayer) const; + + /** + * @brief Query the farthest lookup row and keep both ground/coupling terms. + * + * Used by open-ended extraction formulas that need the asymptotic `cg + cc` + * contribution instead of the isolated ground-only fallback. + */ + CapacitanceResult queryTwoLayerFarthestCap( + const std::string& layer_name, + const std::string& belowLayer) const; + + /** + * @brief Query the farthest lookup row and keep both ground/coupling terms. + */ + CapacitanceResult queryThreeLayerFarthestCap( + const std::string& layer_name, + const std::string& belowLayer, + const std::string& aboveLayer) const; + + private: + // Parsing helpers + + bool parseConfig(const std::string& headerLine, + const std::vector& dataLines); + + std::string makeKey(const std::string& layer_name, + const std::string& overLayer, + const std::string& underLayer) const; + + const CapTableConfig* get_config( + const std::string& layer_name, + const std::string& overLayer, + const std::string& underLayer = "") const; + + // Query helpers + + CapacitanceResult interpolate( + const std::string& layer_name, + const std::string& overLayer, + const std::string& underLayer, + double neighborDistance) const; + + CapacitanceResult farthestResult(const CapTableConfig& config) const; + CapacitanceResult isolatedResult(const CapTableConfig& config) const; + + double linearInterpolate(double x, double x1, double y1, double x2, double y2) const; + + std::pair findBracketingIndices( + const std::vector& data, + double distance) const; + + private: + std::map configs_; +}; + +} // namespace parser +} // namespace ircx diff --git a/src/operation/iRCX/source/parser/itf/CMakeLists.txt b/src/operation/iRCX/source/parser/itf/CMakeLists.txt new file mode 100644 index 000000000..22265d01f --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/CMakeLists.txt @@ -0,0 +1,48 @@ +set(ITF_GENERATED_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated) +file(MAKE_DIRECTORY ${ITF_GENERATED_DIR}) + +find_package(FLEX) +flex_target(itfScanner + ${CMAKE_CURRENT_SOURCE_DIR}/itf_lex.l + ${ITF_GENERATED_DIR}/itf_lex.cpp + DEFINES_FILE ${ITF_GENERATED_DIR}/itf_lex.hpp + # COMPILE_FLAGS "-d" # debug +) +file(COPY itf_lex.l DESTINATION ${CMAKE_BINARY_DIR}) # for debug + +find_package(BISON) +bison_target(itfParser +${CMAKE_CURRENT_SOURCE_DIR}/itf_parser.yy +${ITF_GENERATED_DIR}/itf_parser.cpp +DEFINES_FILE ${ITF_GENERATED_DIR}/itf_parser.hpp +# COMPILE_FLAGS "-t" # debug +) +file(COPY itf_parser.yy DESTINATION ${CMAKE_BINARY_DIR}) # for debug + +##-------------------------- Library --------------------------------## +set(SRC + itfUtil.cpp + itfiConductor.cpp + itfiDielectric.cpp + itfiVia.cpp + itfrCallBacks.cpp + itfrData.cpp + itfrReader.cpp + itfrSettings.cpp +) + +add_library(itf + STATIC + ${SRC} + ${FLEX_itfScanner_OUTPUTS} + ${BISON_itfParser_OUTPUTS} +) + +target_compile_options(itf PRIVATE -Wall -Wextra -pedantic) + +target_include_directories(itf + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE + ${ITF_GENERATED_DIR} +) diff --git a/src/operation/iRCX/source/parser/itf/itf1DLUT.hpp b/src/operation/iRCX/source/parser/itf/itf1DLUT.hpp new file mode 100644 index 000000000..b3b8ee35a --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itf1DLUT.hpp @@ -0,0 +1,142 @@ +// *************************************************************************************** +// 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 +#include +#include +#include +#include +#include +namespace itf +{ + +/** + * @brief One-dimensional lookup table with clamped linear interpolation. + * + * The table stores ordered `(key, value)` pairs. Queries inside the table range + * are linearly interpolated; queries outside the range are clamped to the + * nearest boundary value. + */ +template +class itf1DLUT { + public: + itf1DLUT() = default; + + explicit itf1DLUT(const char* key_name, const char* value_name) + : _points(), + _key_name(key_name ? key_name : ""), + _value_name(value_name ? value_name : "") + { } + + const std::vector>& get_points() const { return _points; } + std::string get_key_name() const { return _key_name; } + std::string get_value_name() const { return _value_name; } + bool empty() const { return _points.empty(); } + size_t size() const { return _points.size(); } + + void add_point(TKey key, TValue value) { + _points.emplace_back(key, value); + sort_points_(); + } + + void set_points(const std::vector>& points) { + _points = points; + sort_points_(); + } + + void set_key_name(const char* value) { _key_name = value ? value : ""; } + void set_value_name(const char* value) { _value_name = value ? value : ""; } + + void set_names(const char* key_name, const char* value_name) { + set_key_name(key_name); + set_value_name(value_name); + } + + std::optional query(size_t index) const { + if (index >= _points.size()) { + return std::nullopt; + } + return _points[index].second; + } + + std::optional query_interpolation(const TKey& key) const { + if (_points.empty()) { + return std::nullopt; + } + if (_points.size() == 1 || key <= _points.front().first) { + return _points.front().second; + } + if (key >= _points.back().first) { + return _points.back().second; + } + + auto it_high = std::lower_bound( + _points.begin(), _points.end(), key, + [](const std::pair& point, const TKey& query_key) { + return point.first < query_key; + }); + + if (it_high == _points.end()) { + return _points.back().second; + } + if (it_high->first == key) { + return it_high->second; + } + + auto it_low = std::prev(it_high); + const auto& [key_low, value_low] = *it_low; + const auto& [key_high, value_high] = *it_high; + if (key_high == key_low) { + return value_high; + } + + const double ratio = static_cast(key - key_low) + / static_cast(key_high - key_low); + return static_cast( + std::lerp(static_cast(value_low), static_cast(value_high), ratio)); + } + + void clear() { + _points.clear(); + _key_name.clear(); + _value_name.clear(); + } + + bool operator==(const itf1DLUT& rhs) const { + if (this == &rhs) { + return true; + } + return _points == rhs._points + && _key_name == rhs._key_name + && _value_name == rhs._value_name; + } + + private: + void sort_points_() { + std::sort(_points.begin(), _points.end(), + [](const std::pair& lhs, const std::pair& rhs) { + return lhs.first < rhs.first; + }); + } + + std::vector> _points; + std::string _key_name; + std::string _value_name; +}; + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf/itf2DLUT.hpp b/src/operation/iRCX/source/parser/itf/itf2DLUT.hpp new file mode 100644 index 000000000..dced5b94d --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itf2DLUT.hpp @@ -0,0 +1,316 @@ +// *************************************************************************************** +// 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 +#include +#include +#include +#include +#include +namespace itf +{ + +// look-up table +// query value by row column +template +class itf2DLUT { + public: + // constructor + itf2DLUT() = default; + explicit itf2DLUT(const char* row_name, const char* col_name, const char* value_name) + : _rows(), + _cols(), + _values(), + _row_name(row_name), + _col_name(col_name), + _value_name(value_name) + { } + itf2DLUT(const itf2DLUT& other) + { + *this = other; + } + + // getter + const std::vector& get_rows() const { return _rows; } + const std::vector& get_cols() const { return _cols; } + const std::vector& get_values() const { return _values; } + std::string get_row_name() const { return _row_name; } + std::string get_col_name() const { return _col_name; } + std::string get_value_name() const { return _value_name; } + + // setter + void add_row_data(T1 e) { _rows.push_back(e); } + void add_col_data(T2 e) { _cols.push_back(e); } + void add_value(T3 e) { _values.push_back(e); } + void set_row_name(const char* s) { _row_name = s; } + void set_col_name(const char* s) { _col_name = s; } + void set_value_name(const char* s) { _value_name = s; } + + // operator + itf2DLUT& operator=(const itf2DLUT& rhs) { + if (this == &rhs) return *this; + + _rows = rhs._rows; + _cols = rhs._cols; + _values = rhs._values; + _row_name = rhs._row_name; + _col_name = rhs._col_name; + _value_name = rhs._value_name; + + return *this; + } + + bool operator==(const itf2DLUT& rhs) const { + if (this == &rhs) return true; + + return _row_name == rhs._row_name + && _col_name == rhs._col_name + && _value_name == rhs._value_name + && _rows == rhs._rows + && _cols == rhs._cols + && _values == rhs._values + ; + } + + // function + + void clear() { + _rows.clear(); + _cols.clear(); + _values.clear(); + _row_name.clear(); + _col_name.clear(); + _value_name.clear(); + } + + // @param r_idx row index + // @param c_idx col index + std::optional query(int r_idx, int c_idx) const { + int v_idx = r_idx * _cols.size() + c_idx; + if ((0 <= r_idx) && (r_idx < (int)_rows.size() ) + && (0 <= c_idx) && (c_idx < (int)_cols.size() ) + && (0 <= v_idx) && (v_idx < (int)_values.size()) ) + { + return _values.at(v_idx); + } else { + return std::nullopt; + } + } + + // bilinear interpolation for points inside of the range of data points, + // keep boundary value for points outside of the range. + // In other words, no extrapolate beyond the table. + // @param r data in _raws + // @param c data in _cols + std::optional query_interpolation(const T1& r, const T2& c) const { + if (_rows.empty() || _cols.empty()) return std::nullopt; + + // 找到行边界索引 + auto r_it = std::lower_bound(_rows.begin(), _rows.end(), r); + size_t r_idx; + + if (r_it == _rows.end()) { + r_idx = _rows.size() - 1; // r 大于等于所有行 + } else if (r_it == _rows.begin()) { + r_idx = 0; // r 小于等于第一行 + } else { + r_idx = std::distance(_rows.begin(), r_it); // r 在两个行之间,需要插值 + } + + // 找到列边界索引(类似) + auto c_it = std::lower_bound(_cols.begin(), _cols.end(), c); + size_t c_idx; + + if (c_it == _cols.end()) { + c_idx = _cols.size() - 1; + } else if (c_it == _cols.begin()) { + c_idx = 0; + } else { + c_idx = std::distance(_cols.begin(), c_it); + } + + // 确定是否需要插值 + bool r_at_boundary = (r_idx == 0 || r_idx == _rows.size() - 1); + bool c_at_boundary = (c_idx == 0 || c_idx == _cols.size() - 1); + + // 如果查询点在边界或之外,直接返回最近的网格点 + if (r_at_boundary || c_at_boundary) { + // 确定最近的行列索引 + size_t nearest_r = r_idx; + size_t nearest_c = c_idx; + + // 如果 r 小于第一行,用第一行 + if (r < _rows[0]) nearest_r = 0; + // 如果 r 大于最后一行,用最后一行 + else if (r > _rows.back()) nearest_r = _rows.size() - 1; + // 否则 r 在范围内,但可能靠近边界 + + // 列类似处理 + if (c < _cols[0]) nearest_c = 0; + else if (c > _cols.back()) nearest_c = _cols.size() - 1; + + return query(nearest_r, nearest_c); + } + + // 完全在内部的情况,进行双线性插值 + size_t r_low = r_idx - 1; + size_t r_high = r_idx; + size_t c_low = c_idx - 1; + size_t c_high = c_idx; + + auto v_rl_cl = query(r_low, c_low); + auto v_rl_ch = query(r_low, c_high); + auto v_rh_cl = query(r_high, c_low); + auto v_rh_ch = query(r_high, c_high); + + if (v_rl_cl && v_rl_ch && v_rh_cl && v_rh_ch) { + // 列插值 + double col_ratio = (c - _cols[c_low]) / (_cols[c_high] - _cols[c_low]); + auto v_rl_cmid = std::lerp(*v_rl_cl, *v_rl_ch, col_ratio); + auto v_rh_cmid = std::lerp(*v_rh_cl, *v_rh_ch, col_ratio); + + // 行插值 + double row_ratio = (r - _rows[r_low]) / (_rows[r_high] - _rows[r_low]); + return std::lerp(v_rl_cmid, v_rh_cmid, row_ratio); + } + + return std::nullopt; + } + + // @param list_name data_list name. match data_list in an order of rows, cols and values. + template + void add_data(const char* list_name, E e) { + if (_row_name.compare(list_name) == 0) { + add_row_data(T1(e)); + } else if (_col_name.compare(list_name) == 0) { + add_col_data(T2(e)); + } else if (_value_name.compare(list_name) == 0) { + add_value(T3(e)); + } else { + std::cout << "fail to find data list named " << list_name << std::endl; + } + } + + template + void set_data_list(const char* list_name, const std::vector& src) { + if (_row_name.compare(list_name) == 0) { + set_list(&_rows, src); + } else if (_col_name.compare(list_name) == 0) { + set_list(&_cols, src); + } else if (_value_name.compare(list_name) == 0) { + set_list(&_values, src); + } else { + std::cout << "fail to find data list named " << list_name << std::endl; + } + } + + // @param r rows_name + // @param c cols_name + // @param v values_name + void set_names(const char* r, const char* c, const char* v) { + set_row_name(r); + set_col_name(c); + set_value_name(v); + } + + protected: + // members + std::vector _rows; + std::vector _cols; + std::vector _values; + std::string _row_name; + std::string _col_name; + std::string _value_name; + + private: + // function + + template + void set_list(void* dst, const std::vector& src) { + if (typeid(E1) == typeid(E2)) { + std::vector* dst_ptr = (std::vector*)dst; + dst_ptr->clear(); + for (auto& e : src) { + dst_ptr->push_back(e); + } + } else { + std::cout << "data type mismatch" << std::endl; + } + } +}; + +// 2D look-up table with title +template +class itfTitleLut : public itf2DLUT { + public: + // constructor + itfTitleLut() : itf2DLUT(), _title() { } + + itfTitleLut(const char* title, const char* row_name, const char* col_name, const char* value_name) + : itf2DLUT(row_name, col_name, value_name), + _title(title) + { } + + itfTitleLut(const itfTitleLut& other) + : itf2DLUT(other), + _title(other._title) + { } + + itfTitleLut(const char* title, const itf2DLUT lut) + : itf2DLUT(lut), + _title(title) + { } + + // getter + std::string get_title() const { return _title; } + + // setter + void set_title(const char* title) { _title = title; } + void set_lut(const itf2DLUT& lut) { + static_cast&>(*this) = lut; + } + + // operator + itfTitleLut& operator=(const itfTitleLut& rhs) { + if (this == &rhs) return *this; + + static_cast&>(*this) = rhs; + _title = rhs._title; + + return *this; + } + + bool operator==(const itfTitleLut& rhs) const { + if (this == &rhs) return true; + + return _title == rhs._title + && static_cast&>(*this) == rhs + ; + } + + // function + void clear() { + itf2DLUT::clear(); + _title.clear(); + } + + protected: + std::string _title; +}; + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf/itfMarco.h b/src/operation/iRCX/source/parser/itf/itfMarco.h new file mode 100644 index 000000000..dee93d687 --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itfMarco.h @@ -0,0 +1,36 @@ +// *************************************************************************************** +// 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 +#include +namespace itf +{ + +#define ITF_FREE(ptr) \ + if (ptr) { \ + free(ptr); \ + ptr = nullptr;\ + } + +#define ITF_STR_CPY(dst, src) \ + ITF_FREE(dst); \ + if (src) { \ + dst = strdup(src); \ + } + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf/itfUtil.cpp b/src/operation/iRCX/source/parser/itf/itfUtil.cpp new file mode 100644 index 000000000..514c1ae49 --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itfUtil.cpp @@ -0,0 +1,27 @@ +// *************************************************************************************** +// 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 "itfUtil.h" +#include "string.h" +namespace itf +{ + +bool itfStrCmp(const char* s1, const char* s2) +{ + return (s1 && s2) ? strcmp(s1, s2) == 0 : s1 == s2; +} + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf/itfUtil.h b/src/operation/iRCX/source/parser/itf/itfUtil.h new file mode 100644 index 000000000..83ce8087e --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itfUtil.h @@ -0,0 +1,24 @@ +// *************************************************************************************** +// 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 + +namespace itf +{ + +bool itfStrCmp(const char*, const char*); + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf/itf_lex.l b/src/operation/iRCX/source/parser/itf/itf_lex.l new file mode 100644 index 000000000..40c8e8916 --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itf_lex.l @@ -0,0 +1,192 @@ +/* *************************************************************************************** + * 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 + +#include "itf_parser.hpp" + +#undef YY_DECL +#define YY_DECL int itf_lex(ITF_STYPE* yylval_param, ITF_LTYPE* yylloc_param) +#define YYSTYPE ITF_STYPE + + +#define YY_USER_ACTION \ + yylloc_param->first_line = yylloc_param->last_line; \ + yylloc_param->first_column = yylloc_param->last_column; \ + if (yylloc_param->last_line == yylineno) \ + yylloc_param->last_column += yyleng; \ + else { \ + yylloc_param->last_line = yylineno; \ + yylloc_param->last_column = yytext + yyleng - strrchr(yytext, '\n'); \ + } + + +%} + +%option prefix = "itf_" +%option noyywrap nounput yylineno + +DIGIT [0-9] +ALPHA [a-zA-Z] +FLOAT [-+]?(({DIGIT}+\.?{DIGIT}*)|(\.{DIGIT}+))([Ee][-+]?{DIGIT}+)? +PUNCTUATION [={}(),] +COMMENT \$.* +KEYWORD {ALPHA}({ALPHA}|{DIGIT}|_)* +PROCESS_NAME {ALPHA}({ALPHA}|{DIGIT}|_|\.|\+|-)+ +SKIP [ \r\n\t] + +%% + +"TECHNOLOGY" { return K_TECHNOLOGY; } +"PROCESS_FOUNDRY" { return K_PROCESS_FOUNDRY; } +"PROCESS_NODE" { return K_PROCESS_NODE; } +"PROCESS_TYPE" { return K_PROCESS_TYPE; } +"PROCESS_VERSION" { return K_PROCESS_VERSION; } +"PROCESS_CORNER" { return K_PROCESS_CORNER; } +"REFERENCE_DIRECTION" { return K_REFERENCE_DIRECTION; } +"GLOBAL_TEMPERATURE" { return K_GLOBAL_TEMPERATURE; } +"BACKGROUND_ER" { return K_BACKGROUND_ER; } +"HALF_NODE_SCALE_FACTOR" { return K_HALF_NODE_SCALE_FACTOR; } +"USE_SI_DENSITY" { return K_USE_SI_DENSITY; } +"YES" { return K_YES; } +"NO" { return K_NO; } +"DROP_FACTOR_LATERAL_SPACING" { return K_DROP_FACTOR_LATERAL_SPACING; } +"DIELECTRIC" { return K_DIELECTRIC; } +"DIELECTRIC_LAYER" { return K_DIELECTRIC_LAYER; } +"ER" { return K_ER; } +"THICKNESS" { return K_THICKNESS; } +"MEASURED_FROM" { return K_MEASURED_FROM; } +"TOP_OF_CHIP" { return K_TOP_OF_CHIP; } +"SW_T" { return K_SW_T; } +"TW_T" { return K_TW_T; } +"ASSOCIATED_CONDUCTOR" { return K_ASSOCIATED_CONDUCTOR; } +"IS_CONFORMAL" { return K_IS_CONFORMAL; } +"DAMAGE_THICKNESS" { return K_DAMAGE_THICKNESS; } +"DAMAGE_ER" { return K_DAMAGE_ER; } +"CONDUCTOR" { return K_CONDUCTOR; } +"IS_PLANAR" { return K_IS_PLANAR; } +"WMIN" { return K_WMIN; } +"SMIN" { return K_SMIN; } +"AIR_GAP_VS_SPACING" { return K_AIR_GAP_VS_SPACING; } +"SPACINGS" { return K_SPACINGS; } +"AIR_GAP_WIDTHS" { return K_AIR_GAP_WIDTHS; } +"AIR_GAP_THICKNESSES" { return K_AIR_GAP_THICKNESSES; } +"AIR_GAP_BOTTOM_HEIGHTS" { return K_AIR_GAP_BOTTOM_HEIGHTS; } +"BOTTOM_DIELECTRIC_THICKNESS" { return K_BOTTOM_DIELECTRIC_THICKNESS; } +"BOTTOM_DIELECTRIC_ER" { return K_BOTTOM_DIELECTRIC_ER; } +"BOTTOM_THICKNESS_VS_SI_WIDTH" { return K_BOTTOM_THICKNESS_VS_SI_WIDTH; } +"RESISTIVE_ONLY" { return K_RESISTIVE_ONLY; } +"CAPACITIVE_ONLY" { return K_CAPACITIVE_ONLY; } +"T0" { return K_T0; } +"CRT1" { return K_CRT1; } +"CRT2" { return K_CRT2; } +"DROP_FACTOR" { return K_DROP_FACTOR; } +"ETCH" { return K_ETCH; } +"CAPACITIVE_ONLY_ETCH" { return K_CAPACITIVE_ONLY_ETCH; } +"RESISTIVE_ONLY_ETCH" { return K_RESISTIVE_ONLY_ETCH; } +"ETCH_VS_WIDTH_AND_SPACING" { return K_ETCH_VS_WIDTH_AND_SPACING; } +"WIDTHS" { return K_WIDTHS; } +"VALUES" { return K_VALUES; } +"ETCH_FROM_TOP" { return K_ETCH_FROM_TOP; } +"FILL_RATIO" { return K_FILL_RATIO; } +"FILL_SPACING" { return K_FILL_SPACING; } +"FILL_WIDTH" { return K_FILL_WIDTH; } +"FILL_TYPE" { return K_FILL_TYPE; } +"GROUNDED" { return K_GROUNDED; } +"FLOATING" { return K_FLOATING; } +"GATE_TO_CONTACT_SMIN" { return K_GATE_TO_CONTACT_SMIN; } +"GATE_TO_DIFFUSION_CAP" { return K_GATE_TO_DIFFUSION_CAP; } +"NUMBER_OF_TABLES" { return K_NUMBER_OF_TABLES; } +"CONTACT_TO_CONTACT_SPACINGS" { return K_CONTACT_TO_CONTACT_SPACINGS; } +"GATE_TO_CONTACT_SPACINGS" { return K_GATE_TO_CONTACT_SPACINGS; } +"CAPS_PER_MICRON" { return K_CAPS_PER_MICRON; } +"THICKNESS_CHANGES" { return K_THICKNESS_CHANGES; } +"LAYER_TYPE" { return K_LAYER_TYPE; } +"DENSITY_POLYNOMIAL_ORDERS" { return K_DENSITY_POLYNOMIAL_ORDERS; } +"WIDTH_POLYNOMIAL_ORDERS" { return K_WIDTH_POLYNOMIAL_ORDERS; } +"WIDTH_RANGES" { return K_WIDTH_RANGES; } +"POLYNOMIAL_COEFFICIENTS" { return K_POLYNOMIAL_COEFFICIENTS; } +"RPSQ" { return K_RPSQ; } +"RHO" { return K_RHO; } +"RPSQ_VS_SI_WIDTH" { return K_RPSQ_VS_SI_WIDTH; } +"RPSQ_VS_WIDTH_AND_SPACING" { return K_RPSQ_VS_WIDTH_AND_SPACING; } +"WIDTH" { return K_WIDTH; } +"SIDE_TANGENT" { return K_SIDE_TANGENT; } +"THICKNESS_VS_DENSITY" { return K_THICKNESS_VS_DENSITY; } +"VIA" { return K_VIA; } +"FROM" { return K_FROM; } +"TO" { return K_TO; } +"CRT_VS_AREA" { return K_CRT_VS_AREA; } +"RPV" { return K_RPV; } +"AREA" { return K_AREA; } +"RPV_VS_AREA" { return K_RPV_VS_AREA; } +"ETCH_VS_WIDTH_AND_LENGTH" { return K_ETCH_VS_WIDTH_AND_LENGTH; } +"VARIATION_PARAMETERS" { return K_VARIATION_PARAMETERS; } +"DENSITY_BOX_WEIGHTING_FACTOR" { return K_DENSITY_BOX_WEIGHTING_FACTOR; } +"ILD_VS_WIDTH_AND_SPACING" { return K_ILD_VS_WIDTH_AND_SPACING; } +"DELTAPD" { return K_DELTAPD; } +"LENGTHS" { return K_LENGTHS; } +"RHO_VS_SI_WIDTH_AND_THICKNESS" { return K_RHO_VS_SI_WIDTH_AND_THICKNESS; } +"RHO_VS_WIDTH_AND_SPACING" { return K_RHO_VS_WIDTH_AND_SPACING; } +"CRT_VS_SI_WIDTH" { return K_CRT_VS_SI_WIDTH; } +"POLYNOMIAL_BASED_THICKNESS_VARIATION" { return K_POLYNOMIAL_BASED_THICKNESS_VARIATION; } +"THICKNESS_VS_WIDTH_AND_SPACING" { return K_THICKNESS_VS_WIDTH_AND_SPACING; } +"TVF_ADJUSTMENT_TABLES" { return K_TVF_ADJUSTMENT_TABLES; } +"BOTTOM_THICKNESS_VS_WIDTH_AND_SPACING" { return K_BOTTOM_THICKNESS_VS_WIDTH_AND_SPACING; } +"BOTTOM_THICKNESS_VS_WIDTH_AND_DELTAPD" { return K_BOTTOM_THICKNESS_VS_WIDTH_AND_DELTAPD; } +"ETCH_VS_CONTACT_AND_GATE_SPACINGS" { return K_ETCH_VS_CONTACT_AND_GATE_SPACINGS; } +"DENSITY_BOUNDS_VS_WIDTH" { return K_DENSITY_BOUNDS_VS_WIDTH; } +"THICKNESS_BOUNDS" { return K_THICKNESS_BOUNDS; } +"DEVICE_TYPE" { return K_DEVICE_TYPE; } +"PARALLEL_TO_REFERENCE" { return K_PARALLEL_TO_REFERENCE; } +"PERPENDICULAR_TO_REFERENCE" { return K_PERPENDICULAR_TO_REFERENCE; } +"PARALLEL_TO_GATE" { return K_PARALLEL_TO_GATE; } +"BW_T" { return K_BW_T; } +"LINKED_TO" { return K_LINKED_TO; } +"EXTENSIONMIN" { return K_EXTENSIONMIN; } +"RAISED_DIFFUSION_THICKNESS" { return K_RAISED_DIFFUSION_THICKNESS; } +"RAISED_DIFFUSION_TO_GATE_SMIN" { return K_RAISED_DIFFUSION_TO_GATE_SMIN; } +"MULTIGATE" { return K_MULTIGATE; } +"FIN_SPACING" { return K_FIN_SPACING; } +"FIN_WIDTH" { return K_FIN_WIDTH; } +"FIN_LENGTH" { return K_FIN_LENGTH; } +"FIN_THICKNESS" { return K_FIN_THICKNESS; } +"GATE_OXIDE_TOP_T" { return K_GATE_OXIDE_TOP_T; } +"GATE_OXIDE_SIDE_T" { return K_GATE_OXIDE_SIDE_T; } +"GATE_OXIDE_ER" { return K_GATE_OXIDE_ER; } +"GATE_POLY_TOP_T" { return K_GATE_POLY_TOP_T; } +"GATE_POLY_SIDE_T" { return K_GATE_POLY_SIDE_T; } +"CHANNEL_ER" { return K_CHANNEL_ER; } +"RAISED_DIFFUSION_GROWTH" { return K_RAISED_DIFFUSION_GROWTH; } +"GATE_DIFFUSION_LAYER_PAIR" { return K_GATE_DIFFUSION_LAYER_PAIR; } +"RPV_VS_WIDTH_AND_LENGTH" { return K_RPV_VS_WIDTH_AND_LENGTH; } +"RAISED_DIFFUSION_ETCH" { return K_RAISED_DIFFUSION_ETCH; } +"RAISED_DIFFUSION_GATE_SIDE_CONFORMAL_ER" { return K_RAISED_DIFFUSION_GATE_SIDE_CONFORMAL_ER; } + +{COMMENT} { } +{PUNCTUATION} { return yytext[0]; } +{KEYWORD} { yylval_param->string = yytext; return KEYWORD; } +{PROCESS_NAME} { yylval_param->string = yytext; return PROCESS_NAME; } +{FLOAT} { yylval_param->dval = std::atof(yytext); return NUMBER; } +{SKIP} { } + +%% + +void itf_error(const char* s) { + printf("error: %s\n yytext =%s", s, yytext); +} diff --git a/src/operation/iRCX/source/parser/itf/itf_parser.yy b/src/operation/iRCX/source/parser/itf/itf_parser.yy new file mode 100644 index 000000000..8ef7cf9f6 --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itf_parser.yy @@ -0,0 +1,1497 @@ +/* *************************************************************************************** + * 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. + * ***************************************************************************************/ +%code requires { + +#include + +#include +#include +#include + +#include "itfrData.hpp" +#include "itfMarco.h" +#include "itfrSettings.hpp" +#include "itfrCallBacks.hpp" + +} + +%code provides { + +#undef YY_DECL +#define YY_DECL int itf_lex(ITF_STYPE* yylval_param, ITF_LTYPE* yylloc_param) +YY_DECL; + +void itf_error(ITF_LTYPE*, const char*); + +} + +%union { + double dval; + char* string; + void* ent; +} + +%define api.pure full +%define api.prefix {itf_} +%locations + +%{ + +namespace itf { + +#define CALLBACK(func, typ, data) \ + if (func) { \ + (*func) (typ, data, itfSettings->user_data); \ + } + +extern itfrData* itfData; +// var +char* v_layer_name = nullptr; +char* v_measured_from = nullptr; +char* v_model_name = nullptr; +char* v_table_name = nullptr; +char* v_etch_effect_type = nullptr; +float v_thickness; +float v_sw_t; +float v_tw_t; +float v_t0; +float v_crt1; +float v_crt2; +float v_rho; +int v_number_of_tables; +std::vector v_int_list; +std::vector v_float_list; +std::vector> v_float_pair_list; +itf2DLUT v_lut; +itf2DLUT> v_etch_wlv_lut; +itfiVPT v_vpt; +unsigned v_is_lut_working = 0; +unsigned v_flag_dielectric = 0; +%} + +%token K_TECHNOLOGY K_PROCESS_FOUNDRY K_GLOBAL_TEMPERATURE K_BACKGROUND_ER K_HALF_NODE_SCALE_FACTOR +%token K_PROCESS_NODE K_PROCESS_TYPE K_PROCESS_VERSION K_PROCESS_CORNER K_REFERENCE_DIRECTION +%token K_USE_SI_DENSITY K_YES K_NO K_DROP_FACTOR_LATERAL_SPACING K_DIELECTRIC +%token K_DIELECTRIC_LAYER K_ER K_THICKNESS K_MEASURED_FROM K_TOP_OF_CHIP K_SW_T K_TW_T +%token K_ASSOCIATED_CONDUCTOR K_IS_CONFORMAL K_DAMAGE_THICKNESS K_DAMAGE_ER +%token K_CONDUCTOR K_IS_PLANAR K_WMIN K_SMIN K_AIR_GAP_VS_SPACING K_SPACINGS +%token K_AIR_GAP_WIDTHS K_AIR_GAP_THICKNESSES K_AIR_GAP_BOTTOM_HEIGHTS +%token K_BOTTOM_DIELECTRIC_THICKNESS K_BOTTOM_DIELECTRIC_ER +%token K_BOTTOM_THICKNESS_VS_SI_WIDTH K_RESISTIVE_ONLY K_CAPACITIVE_ONLY K_T0 +%token K_CRT1 K_CRT2 K_DROP_FACTOR K_ETCH K_CAPACITIVE_ONLY_ETCH K_RESISTIVE_ONLY_ETCH +%token K_ETCH_VS_WIDTH_AND_SPACING K_WIDTHS K_VALUES K_ETCH_FROM_TOP K_FILL_RATIO +%token K_FILL_SPACING K_FILL_WIDTH K_FILL_TYPE K_GROUNDED K_FLOATING K_GATE_TO_CONTACT_SMIN +%token K_GATE_TO_DIFFUSION_CAP K_NUMBER_OF_TABLES K_CONTACT_TO_CONTACT_SPACINGS +%token K_GATE_TO_CONTACT_SPACINGS K_CAPS_PER_MICRON K_THICKNESS_CHANGES K_LAYER_TYPE +%token K_POLYNOMIAL_BASED_THICKNESS_VARIATION K_DENSITY_POLYNOMIAL_ORDERS +%token K_WIDTH_POLYNOMIAL_ORDERS K_WIDTH_RANGES K_POLYNOMIAL_COEFFICIENTS +%token K_RPSQ K_RHO K_RPSQ_VS_SI_WIDTH K_RPSQ_VS_WIDTH_AND_SPACING +%token K_WIDTH K_SIDE_TANGENT K_THICKNESS_VS_DENSITY K_THICKNESS_VS_WIDTH_AND_SPACING +%token K_TVF_ADJUSTMENT_TABLES K_BOTTOM_THICKNESS_VS_WIDTH_AND_SPACING +%token K_BOTTOM_THICKNESS_VS_WIDTH_AND_DELTAPD K_VIA K_FROM K_TO K_CRT_VS_AREA +%token K_RPV K_AREA K_RPV_VS_AREA K_ETCH_VS_WIDTH_AND_LENGTH K_VARIATION_PARAMETERS +%token K_DENSITY_BOX_WEIGHTING_FACTOR K_ILD_VS_WIDTH_AND_SPACING K_DELTAPD K_LENGTHS +%token K_RHO_VS_SI_WIDTH_AND_THICKNESS K_RHO_VS_WIDTH_AND_SPACING K_ETCH_VS_CONTACT_AND_GATE_SPACINGS +%token K_CRT_VS_SI_WIDTH K_DENSITY_BOUNDS_VS_WIDTH K_THICKNESS_BOUNDS +%token K_DEVICE_TYPE K_PARALLEL_TO_REFERENCE K_PERPENDICULAR_TO_REFERENCE K_PARALLEL_TO_GATE +%token K_BW_T K_LINKED_TO K_EXTENSIONMIN K_RAISED_DIFFUSION_THICKNESS K_RAISED_DIFFUSION_TO_GATE_SMIN +%token K_MULTIGATE K_FIN_SPACING K_FIN_WIDTH K_FIN_LENGTH K_FIN_THICKNESS K_GATE_OXIDE_TOP_T +%token K_GATE_OXIDE_SIDE_T K_GATE_OXIDE_ER K_GATE_POLY_TOP_T K_GATE_POLY_SIDE_T K_CHANNEL_ER +%token K_RAISED_DIFFUSION_GROWTH K_GATE_DIFFUSION_LAYER_PAIR K_RPV_VS_WIDTH_AND_LENGTH +%token K_RAISED_DIFFUSION_ETCH K_RAISED_DIFFUSION_GATE_SIDE_CONFORMAL_ER + +%token KEYWORD PROCESS_NAME +%token NUMBER + +%start itf_file + +%% + +itf_file : + file_optionals + layers + variation_params +; + +file_optionals : + file_optionals file_optional +| +; + +file_optional : + tech + { + CALLBACK(itfCallbacks->technology_cb, itfCallBackType::kTechnologyCbType, itfData->process_name); + } +| global_temperature + { + CALLBACK(itfCallbacks->global_temperature_cb, itfCallBackType::kGlobalTemperatureCbType, itfData->global_temperature); + } +| background_er + { + CALLBACK(itfCallbacks->background_er_cb, itfCallBackType::kBackgroundErCbType, itfData->background_er); + } +| half_node_scale_factor + { + CALLBACK(itfCallbacks->half_node_scale_factor_cb, itfCallBackType::kHalfNodeScaleFactorCbType, itfData->half_node_scale_factor); + } +| use_si_density + { + CALLBACK(itfCallbacks->use_si_density_cb, itfCallBackType::kUseSiDensityCbType, itfData->use_si_density); + } +| drop_factor_lateral_spacing + { + CALLBACK(itfCallbacks->drop_factor_lateral_spacing_cb, itfCallBackType::kDropFactorLateralSpacingCbType, itfData->drop_factor_lateral_spacing); + } +| process_foundry + { + CALLBACK(itfCallbacks->process_foundry_cb, itfCallBackType::kProcessFoundryCbType, itfData->process_foundry); + } +| process_node + { + CALLBACK(itfCallbacks->process_node_cb, itfCallBackType::kProcessNodeCbType, itfData->process_node); + } +| process_type + { + CALLBACK(itfCallbacks->process_type_cb, itfCallBackType::kProcessTypeCbType, itfData->process_type); + } +| process_version + { + CALLBACK(itfCallbacks->process_version_cb, itfCallBackType::kProcessVersionCbType, itfData->process_version); + } +| process_corner + { + CALLBACK(itfCallbacks->process_corner_cb, itfCallBackType::kProcessCornerCbType, itfData->process_corner); + } +| reference_direction + { + CALLBACK(itfCallbacks->reference_direction_cb, itfCallBackType::kReferenceDirectionCbType, itfData->reference_direction); + } +; + +tech : + K_TECHNOLOGY '=' PROCESS_NAME + { + ITF_STR_CPY(itfData->process_name, $3); + } +| K_TECHNOLOGY '=' KEYWORD + { + ITF_STR_CPY(itfData->process_name, $3); + } +; + +global_temperature : + K_GLOBAL_TEMPERATURE '=' NUMBER + { + itfData->has_global_temperature = 1; + itfData->global_temperature = $3; + } +; + +background_er : + K_BACKGROUND_ER '=' NUMBER + { + itfData->has_background_er = 1; + itfData->background_er = $3; + } +; + +half_node_scale_factor : + K_HALF_NODE_SCALE_FACTOR '=' NUMBER + { + itfData->has_half_node_scale_factor = 1; + itfData->half_node_scale_factor = $3; + } +; + +use_si_density : + K_USE_SI_DENSITY '=' use_si_density_value + { + itfData->has_use_si_density = 1; + } +; + +use_si_density_value : + K_YES { itfData->use_si_density = 1; } +| K_NO { itfData->use_si_density = 0; } +; + +drop_factor_lateral_spacing : + K_DROP_FACTOR_LATERAL_SPACING '=' NUMBER + { + itfData->has_drop_factor_lateral_spacing = 1; + itfData->drop_factor_lateral_spacing = $3; + } +; + +process_foundry : + K_PROCESS_FOUNDRY '=' KEYWORD +; + +process_node : + K_PROCESS_NODE '=' NUMBER +; + +process_type : + K_PROCESS_TYPE '=' KEYWORD +; + +process_version : + K_PROCESS_VERSION '=' NUMBER +; + +process_corner : + K_PROCESS_CORNER '=' KEYWORD +; + +reference_direction : + K_REFERENCE_DIRECTION '=' KEYWORD +; + +layers: + x_eol + vias +; + +x_eol : + x_eol dielectric + { + CALLBACK(itfCallbacks->dielectric_cb, itfCallBackType::kDielectricCbType, &itfData->dielectric); + itfData->dielectric.clear(); + } +| x_eol conductor + { + CALLBACK(itfCallbacks->conductor_cb, itfCallBackType::kConductorCbType, &itfData->conductor); + itfData->conductor.clear(); + } +| x_eol multigate +| +; + +dielectric : + K_DIELECTRIC + { + v_flag_dielectric = 1; + } + layer_name + { + itfData->dielectric.set_dielectric_name(v_layer_name); + } + '{' + dielectric_properties + '}' + { + v_flag_dielectric = 0; + } +; + +layer_name : + KEYWORD { ITF_STR_CPY(v_layer_name, $1); } +; + +dielectric_properties : + dielectric_properties dielectric_property +| +; + +dielectric_property: + er +| thickness { itfData->dielectric.set_thickness(v_thickness); } +| diel_measured_from +| associated_conductor + { + itfData->dielectric.set_associated_conductor(v_layer_name); + } +| damage +| K_IS_CONFORMAL + { + itfData->dielectric.set_is_conformal(); + } +| bw_t +; + +er : + K_ER '=' NUMBER + { itfData->dielectric.set_er($3); } +; + +thickness : + K_THICKNESS '=' NUMBER + { v_thickness = $3; } +; + +diel_measured_from : + measured_from { itfData->dielectric.set_measured_from(v_measured_from); } +| sw_t { itfData->dielectric.set_sw_t(v_sw_t); } +| tw_t { itfData->dielectric.set_tw_t(v_tw_t); } +; + +measured_from : + K_MEASURED_FROM '=' measured_from_value +; + +measured_from_value : + layer_name { ITF_STR_CPY(v_measured_from, v_layer_name); } +| K_TOP_OF_CHIP { ITF_STR_CPY(v_measured_from, "TOP_OF_CHIP"); } +; + +sw_t : + K_SW_T '=' NUMBER { v_sw_t = $3; } +; + +tw_t : + K_TW_T '=' NUMBER { v_tw_t = $3; } +; + +associated_conductor : + K_ASSOCIATED_CONDUCTOR '=' layer_name +; + +damage : + K_DAMAGE_THICKNESS '=' NUMBER + K_DAMAGE_ER '=' NUMBER + { + itfData->dielectric.set_damage_thickness($3); + itfData->dielectric.set_damage_er($6); + } +; + +bw_t : + K_BW_T '=' NUMBER + { + + } +; + +conductor : + K_CONDUCTOR layer_name + { + itfData->conductor.set_conductor_name(v_layer_name); + } + '{' + conductor_properties + '}' +; + +conductor_properties : + conductor_properties conductor_property +| +; + +conductor_property : + wmin +| smin +| thickness { itfData->conductor.set_thickness(v_thickness); } +| air_gap_vs_spacing +| bottom_dielectric_property +| bottom_thickness_vs_si_width +| t0 { itfData->conductor.set_t0(v_t0); } +| crt_stmt +| density_box_weight_factor +| drop_factor +| etch_stmt +| etch_vs_width_and_spacing + { + itfData->conductor.add_etch_vws(v_etch_effect_type, v_lut); + ITF_FREE(v_etch_effect_type); + v_lut.clear(); + v_is_lut_working = 0; + } +| fill_stmt +| gate_to_contact_smin +| gate_to_diffusion_cap +| ild_vs_width_and_spacing +| K_IS_PLANAR { itfData->conductor.set_is_planar(); } +| layer_type +| measured_from { itfData->conductor.set_measured_from(v_measured_from); } +| polynomial_based_thickness_variation +| rpsq_stmt +| side_tangent +| thickness_vs_density +| thickness_vs_width_and_spacing +| tvf_adjustment_tables +| device_type +| associated_conductor { } +| linked_to +| extensionmin +| raised_diffusion_thickness +| raised_diffusion_to_gate_smin +| raised_diffusion_etch +| raised_diffusion_gate_side_conformal_er +; + +wmin : + K_WMIN '=' NUMBER + { + itfData->conductor.set_wmin($3); + } +; + +smin : + K_SMIN '=' NUMBER + { + itfData->conductor.set_smin($3); + } +; + +air_gap_vs_spacing : + K_AIR_GAP_VS_SPACING '{' + spacings + air_gap_widths + air_gap_thicknesses + air_gap_bottom_heights + '}' +; + +spacings : + K_SPACINGS '{' float_list '}' + { + if (v_is_lut_working) { + v_lut.set_data_list("SPACINGS", v_float_list); + } else { + itfData->conductor.set_air_gap_spacings(v_float_list); + } + v_float_list.clear(); + } +; + +float_list : + float_list NUMBER + { + v_float_list.push_back($2); + } +| +; + +air_gap_widths : + K_AIR_GAP_WIDTHS '{' float_list '}' + { + itfData->conductor.set_air_gap_widths(v_float_list); + v_float_list.clear(); + } +; + +air_gap_thicknesses : + K_AIR_GAP_THICKNESSES '{' float_list '}' + { + itfData->conductor.set_air_gap_thicknesses(v_float_list); + v_float_list.clear(); + } +; + +air_gap_bottom_heights : + K_AIR_GAP_BOTTOM_HEIGHTS '{' float_list '}' + { + itfData->conductor.set_air_gap_bottom_heights(v_float_list); + v_float_list.clear(); + } +; + +bottom_dielectric_property : + K_BOTTOM_DIELECTRIC_THICKNESS '=' NUMBER + K_BOTTOM_DIELECTRIC_ER '=' NUMBER + { + itfData->conductor.set_bottom_dielectric_thickness($3); + itfData->conductor.set_bottom_dielectric_er($6); + } +; + +bottom_thickness_vs_si_width : + K_BOTTOM_THICKNESS_VS_SI_WIDTH bottom_thickness_vs_si_width_type + '{' float_pair_list '}' + { + itfData->conductor.get_bottom_thickness_vs_si_width().set_sr_list(v_float_pair_list); + v_float_pair_list.clear(); + } +; + +bottom_thickness_vs_si_width_type : + K_RESISTIVE_ONLY + { + itfData->conductor.get_bottom_thickness_vs_si_width().set_type("RESISTIVE_ONLY"); + } +| K_CAPACITIVE_ONLY + { + itfData->conductor.get_bottom_thickness_vs_si_width().set_type("CAPACITIVE_ONLY"); + } +; + +float_pair_list : + float_pair_list '(' NUMBER ',' NUMBER ')' + { v_float_pair_list.push_back({$3, $5}); } +| float_pair_list '(' NUMBER NUMBER ')' + { v_float_pair_list.push_back({$3, $4}); } +| +; + +t0 : + K_T0 '=' NUMBER + { + v_t0 = $3; + } +; + +crt_stmt : + crt1 { itfData->conductor.set_crt1(v_crt1); } +| crt2 { itfData->conductor.set_crt2(v_crt2); } +| crt_vs_si_width +; + +crt1 : + K_CRT1 '=' NUMBER + { + v_crt1 = $3; + } +; + +crt2 : + K_CRT2 '=' NUMBER + { + v_crt2 = $3; + } +; + +crt_vs_si_width : + K_CRT_VS_SI_WIDTH + '{' + siw_crt1_crt2_list + '}' +; + +siw_crt1_crt2_list : + siw_crt1_crt2_list '(' NUMBER ',' NUMBER ',' NUMBER ')' + { + itfData->conductor.add_siw_crt1_crt2($3, $5, $7); + } +| siw_crt1_crt2_list '(' NUMBER NUMBER NUMBER ')' + { + itfData->conductor.add_siw_crt1_crt2($3, $4, $5); + } +| +; + +density_box_weight_factor : + K_DENSITY_BOX_WEIGHTING_FACTOR '{' + s_w_list + '}' +; + +s_w_list : + s_w_list '(' NUMBER ',' NUMBER ')' + { + itfData->conductor.add_density_box_weight(int($3), $5); + } +| s_w_list '(' NUMBER NUMBER ')' + { + itfData->conductor.add_density_box_weight(int($3), $4); + } +| +; + +drop_factor : + K_DROP_FACTOR '=' NUMBER + { + itfData->conductor.set_drop_factor($3); + } +; + +etch_stmt : + K_ETCH '=' NUMBER + { itfData->conductor.set_etch($3); } +| K_CAPACITIVE_ONLY_ETCH '=' NUMBER + { itfData->conductor.set_capacitive_only_etch($3); } +| K_RESISTIVE_ONLY_ETCH '=' NUMBER + { itfData->conductor.set_resistive_only_etch($3); } +; + +etch_vs_width_and_spacing : + K_ETCH_VS_WIDTH_AND_SPACING + { + v_lut.set_names("WIDTHS", "SPACINGS", "VALUES"); + v_is_lut_working = 1; + } + etch_effect_type + apply_etch_to + '{' + spacings + widths + values + '}' +; + +etch_effect_type : + K_RESISTIVE_ONLY { ITF_STR_CPY(v_etch_effect_type, "RESISTIVE_ONLY"); } +| K_CAPACITIVE_ONLY { ITF_STR_CPY(v_etch_effect_type, "CAPACITIVE_ONLY"); } +| K_ETCH_FROM_TOP { ITF_STR_CPY(v_etch_effect_type, "ETCH_FROM_TOP"); } +| +; + +apply_etch_to : + K_PARALLEL_TO_REFERENCE +| K_PERPENDICULAR_TO_REFERENCE +| K_PARALLEL_TO_GATE +| +; + +widths : + K_WIDTHS '{' float_list '}' + { + v_lut.set_data_list("WIDTHS", v_float_list); + v_float_list.clear(); + } +; + +values : + K_VALUES '{' float_list '}' + { + v_lut.set_data_list("VALUES", v_float_list); + v_float_list.clear(); + } +; + +fill_stmt : + K_FILL_RATIO '=' NUMBER + { itfData->conductor.set_fill_ratio($3); } +| K_FILL_SPACING '=' NUMBER + { itfData->conductor.set_fill_spacing($3); } +| K_FILL_WIDTH '=' NUMBER + { itfData->conductor.set_fill_width($3); } +| K_FILL_TYPE '=' fill_type +; + +fill_type : + K_GROUNDED + { itfData->conductor.set_fill_type("GROUNDED"); } +| K_FLOATING + { itfData->conductor.set_fill_type("FLOATING"); } +; + +gate_to_contact_smin : + K_GATE_TO_CONTACT_SMIN '=' NUMBER + { + itfData->conductor.set_gate_to_contact_smin($3); + } +; + +gate_to_diffusion_cap : + K_GATE_TO_DIFFUSION_CAP '{' + number_of_tables + model_list + '}' + { + itfData->conductor.get_gate_to_diffusion_cap().set_number_of_tables(v_number_of_tables); + } +; + +number_of_tables : + K_NUMBER_OF_TABLES '=' NUMBER + { + v_number_of_tables = int($3); + } +; + +model_list : + model_list model +| +; + +model : + model_name + { + v_lut.set_names("GATE_TO_CONTACT_SPACINGS", "CONTACT_TO_CONTACT_SPACINGS", "CAPS_PER_MICRON"); + v_is_lut_working = 1; + } + '{' + contact_to_contact_spacings + gate_to_contact_spacings + caps_per_micron + '}' + { + itfData->conductor.get_gate_to_diffusion_cap().add_model(v_model_name, v_lut); + v_lut.clear(); + v_is_lut_working = 0; + } +; + +model_name : + KEYWORD { ITF_STR_CPY(v_model_name, $1); } +; + +contact_to_contact_spacings : + K_CONTACT_TO_CONTACT_SPACINGS '{' float_list '}' + { + v_lut.set_data_list("CONTACT_TO_CONTACT_SPACINGS", v_float_list); + v_float_list.clear(); + } +; + +gate_to_contact_spacings : + K_GATE_TO_CONTACT_SPACINGS '{' float_list '}' + { + v_lut.set_data_list("GATE_TO_CONTACT_SPACINGS", v_float_list); + v_float_list.clear(); + } +; + +caps_per_micron : + K_CAPS_PER_MICRON '{' float_list '}' + { + v_lut.set_data_list("CAPS_PER_MICRON", v_float_list); + v_float_list.clear(); + } +; + +ild_vs_width_and_spacing : + K_ILD_VS_WIDTH_AND_SPACING + { + v_lut.set_names("WIDTHS", "SPACINGS", "THICKNESS_CHANGES"); + v_is_lut_working = 1; + } + '{' + ild_diel_layer_token '=' layer_name + widths + spacings + thickness_changes + '}' + { + itfData->conductor.set_ild_vws_title(v_layer_name); + itfData->conductor.set_ild_vws_lut(v_lut); + v_lut.clear(); + v_is_lut_working = 0; + } +; + +ild_diel_layer_token : + K_DIELECTRIC +| K_DIELECTRIC_LAYER +; + +thickness_changes : + K_THICKNESS_CHANGES '{' float_list '}' + { + v_lut.set_data_list("THICKNESS_CHANGES", v_float_list); + v_float_list.clear(); + } +; + +layer_type : + K_LAYER_TYPE '=' layer_type_value +; + +layer_type_value : + KEYWORD { itfData->conductor.set_layer_type($1); } +; + +polynomial_based_thickness_variation : + K_POLYNOMIAL_BASED_THICKNESS_VARIATION '{' + density_polynomial_orders + width_polynomial_orders + width_ranges + polynomial_valueicients_lists + polynomial_based_thickness_variation_options + '}' +; + +polynomial_based_thickness_variation_options : + polynomial_based_thickness_variation_options + polynomial_based_thickness_variation_option +| +; + +polynomial_based_thickness_variation_option : + density_bounds_vs_width +| thickness_bounds +; + +density_polynomial_orders : + K_DENSITY_POLYNOMIAL_ORDERS equal_op '{' int_comma_list '}' + { + itfData->conductor.get_PBTV().set_density_polynomial_order(v_int_list); + v_int_list.clear(); + } +; + +equal_op : + '=' +| +; + +width_polynomial_orders : + K_WIDTH_POLYNOMIAL_ORDERS equal_op '{' int_comma_list '}' + { + itfData->conductor.get_PBTV().set_width_polynomial_order(v_int_list); + v_int_list.clear(); + } +; + +width_ranges : + K_WIDTH_RANGES equal_op '{' float_comma_list '}' + { + itfData->conductor.get_PBTV().set_width_range(v_float_list); + v_float_list.clear(); + } +; + +int_comma_list : + int_comma_list NUMBER ',' { v_int_list.push_back(int($2)); } +| int_comma_list NUMBER { v_int_list.push_back(int($2)); } +| +; + +float_comma_list : + float_comma_list NUMBER ',' { v_float_list.push_back($2); } +| float_comma_list NUMBER { v_float_list.push_back($2); } +| +; + +polynomial_valueicients_lists : + polynomial_valueicients_lists + K_POLYNOMIAL_COEFFICIENTS equal_op '{' + float_comma_list + '}' + { + itfData->conductor.get_PBTV().add_polynomial_coefficients(v_float_list); + v_float_list.clear(); + } +| +; + +density_bounds_vs_width : + K_DENSITY_BOUNDS_VS_WIDTH '{' + tuple_3_list + '}' +; + +tuple_3_list : + tuple_3_list + '(' NUMBER NUMBER NUMBER ')' + { + + } +| +; + +thickness_bounds : + K_THICKNESS_BOUNDS '{' NUMBER NUMBER '}' + { + + } +; + +rpsq_stmt : + K_RPSQ '=' NUMBER { itfData->conductor.set_rpsq($3); } +| rho { itfData->conductor.set_rho(v_rho); } +| rpsq_vs_si_width +| rpsq_vs_width_and_spacing +| rho_vs_si_width_and_thickness +| rho_vs_width_and_spacing +; + +rho : + K_RHO '=' NUMBER { v_rho = $3; } +; + +rpsq_vs_si_width : + K_RPSQ_VS_SI_WIDTH '{' + float_pair_list + '}' + { + itfData->conductor.set_rpsq_vs_si_width(v_float_pair_list); + v_float_pair_list.clear(); + } +; + +rpsq_vs_width_and_spacing : + K_RPSQ_VS_WIDTH_AND_SPACING + { + v_lut.set_names("WIDTHS", "SPACINGS", "VALUES"); + v_is_lut_working = 1; + } + '{' + spacings + widths + values + '}' + { + itfData->conductor.set_rpsq_vws(v_lut); + v_lut.clear(); + v_is_lut_working = 0; + } +; + +rho_vs_si_width_and_thickness : + K_RHO_VS_SI_WIDTH_AND_THICKNESS + { + v_lut.set_names("THICKNESS", "WIDTH", "VALUES"); + v_is_lut_working = 1; + } + '{' + width_list + thickness_list + values + '}' + { + itfData->conductor.set_rho_v_siw_t(v_lut); + v_lut.clear(); + v_is_lut_working = 0; + } +; + +width_list : + K_WIDTH '{' float_list '}' + { + v_lut.set_data_list("WIDTH", v_float_list); + v_float_list.clear(); + } +; + +thickness_list : + K_THICKNESS '{' float_list '}' + { + v_lut.set_data_list("THICKNESS", v_float_list); + v_float_list.clear(); + } +; + +rho_vs_width_and_spacing : + K_RHO_VS_WIDTH_AND_SPACING + { + v_lut.set_names("WIDTHS", "SPACINGS", "VALUES"); + v_is_lut_working = 1; + } + '{' + spacings + widths + values + '}' + { + itfData->conductor.set_rho_vws(v_lut); + v_lut.clear(); + v_is_lut_working = 0; + } +; + +side_tangent : + K_SIDE_TANGENT '=' NUMBER + { itfData->conductor.set_side_tangent($3); } +; + +thickness_vs_density : + K_THICKNESS_VS_DENSITY thickness_vs_density_type + '{' float_pair_list '}' + { + itfData->conductor.get_thickness_vs_density().set_dr_list(v_float_pair_list); + v_float_pair_list.clear(); + } +; + +thickness_vs_density_type : + K_RESISTIVE_ONLY + { itfData->conductor.get_thickness_vs_density().set_type("RESISTIVE_ONLY"); } +| K_CAPACITIVE_ONLY + { itfData->conductor.get_thickness_vs_density().set_type("CAPACITIVE_ONLY"); } +| +; + +thickness_vs_width_and_spacing : + K_THICKNESS_VS_WIDTH_AND_SPACING + { + v_lut.set_names("WIDTHS", "SPACINGS", "VALUES"); + v_is_lut_working = 1; + } + thickness_vs_width_and_spacing_type + '{' + spacings + widths + values + '}' + { + itfData->conductor.set_thickness_vws_lut(v_lut); + v_lut.clear(); + v_is_lut_working = 0; + } +; + +thickness_vs_width_and_spacing_type : + K_RESISTIVE_ONLY + { itfData->conductor.set_thickness_vws_title("RESISTIVE_ONLY"); } +| K_CAPACITIVE_ONLY + { itfData->conductor.set_thickness_vws_title("CAPACITIVE_ONLY"); } +| +; + +tvf_adjustment_tables : + K_TVF_ADJUSTMENT_TABLES '{' + bottom_thickness_vs_width_and_others + '}' +; + +bottom_thickness_vs_width_and_others : + bottom_thickness_vs_width_and_others bottom_thickness_vs_width_and_other +| +; + +bottom_thickness_vs_width_and_other : + bottom_thickness_vs_width_and_spacing +| bottom_thickness_vs_width_and_deltapd +; + +bottom_thickness_vs_width_and_spacing : + K_BOTTOM_THICKNESS_VS_WIDTH_AND_SPACING + { + v_lut.set_names("WIDTHS", "SPACINGS", "VALUES"); + v_is_lut_working = 1; + } + '{' + spacings + widths + values + '}' + { + itfData->conductor.set_tvf_bt_vws(v_lut); + v_lut.clear(); + v_is_lut_working = 0; + } +; + +bottom_thickness_vs_width_and_deltapd : + K_BOTTOM_THICKNESS_VS_WIDTH_AND_DELTAPD + { + v_lut.set_names("WIDTHS", "DELTAPD", "VALUES"); + v_is_lut_working = 1; + } + '{' + deltapd + widths + values + '}' + { + itfData->conductor.set_tvf_bt_vwd(v_lut); + v_lut.clear(); + v_is_lut_working = 0; + } +; + +deltapd : + K_DELTAPD '{' float_list '}' + { + v_lut.set_data_list("DELTAPD", v_float_list); + v_float_list.clear(); + } +; + +device_type : + K_DEVICE_TYPE '{' + keyword_list + '}' +; + +keyword_list : + keyword_list + KEYWORD + { + + } +| +; + +linked_to : + K_LINKED_TO '=' layer_name + { + + } +; + +extensionmin : + K_EXTENSIONMIN '=' NUMBER +; + +raised_diffusion_thickness : + K_RAISED_DIFFUSION_THICKNESS '=' NUMBER + { + + } +; + +raised_diffusion_to_gate_smin : + K_RAISED_DIFFUSION_TO_GATE_SMIN '=' NUMBER + { + + } +; + +raised_diffusion_etch : + K_RAISED_DIFFUSION_ETCH '=' NUMBER +; + +raised_diffusion_gate_side_conformal_er : + K_RAISED_DIFFUSION_GATE_SIDE_CONFORMAL_ER '=' NUMBER +; + +multigate : + K_MULTIGATE KEYWORD + '{' + multigate_properties + '}' +; + +multigate_properties : + multigate_properties + multigate_property +| +; + +multigate_property : + fin_spacing +| fin_width +| fin_length +| fin_thickness +| gate_oxide_top_t +| gate_oxide_side_t +| gate_oxide_er +| gate_poly_top_t +| gate_poly_side_t +| channel_er +| raised_diffusion_growth +| gate_diffusion_layer_pair +; + +fin_spacing : + K_FIN_SPACING '=' NUMBER +; + +fin_width : + K_FIN_WIDTH '=' NUMBER +; + +fin_length : + K_FIN_LENGTH '=' NUMBER +; + +fin_thickness : + K_FIN_THICKNESS '=' NUMBER +; + +gate_oxide_top_t : + K_GATE_OXIDE_TOP_T '=' NUMBER +; + +gate_oxide_side_t : + K_GATE_OXIDE_SIDE_T '=' NUMBER +; + +gate_oxide_er : + K_GATE_OXIDE_ER '=' NUMBER +; + +gate_poly_top_t : + K_GATE_POLY_TOP_T '=' NUMBER +; + +gate_poly_side_t : + K_GATE_POLY_SIDE_T '=' NUMBER +; + +channel_er : + K_CHANNEL_ER '=' NUMBER +; + +raised_diffusion_growth : + K_RAISED_DIFFUSION_GROWTH '=' NUMBER +; + +gate_diffusion_layer_pair : + K_GATE_DIFFUSION_LAYER_PAIR '{' + gate_diffusion_layer_pair_list + '}' +; + +gate_diffusion_layer_pair_list : + gate_diffusion_layer_pair_list + '(' keyword_list ')' +| +; + +vias : + vias via + { + CALLBACK(itfCallbacks->via_cb, itfCallBackType::kViaCbType, &itfData->via); + itfData->via.clear(); + } +| +; + +via : + K_VIA layer_name + { + itfData->via.set_via_name(v_layer_name); + } + '{' + via_properties + '}' +; + +via_properties : + via_properties via_property +| +; + +via_property : + from +| to +| crt_property +| rho_property +| etch_property +| device_type +| rpv_vs_width_and_length +; + +from : + K_FROM '=' layer_name + { + itfData->via.set_from(v_layer_name); + } +; + +to : + K_TO '=' layer_name + { + itfData->via.set_to(v_layer_name); + } +; + +crt_property : + crt1 + { + itfData->via.set_crt1(v_crt1); + } +| crt2 { itfData->via.set_crt2(v_crt2); } +| crt_vs_area +| t0 { itfData->via.set_t0(v_crt2); } +; + +crt_vs_area : + K_CRT_VS_AREA '{' + area_crt1_crt2_list + '}' +; + +area_crt1_crt2_list : + area_crt1_crt2_list '(' NUMBER ',' NUMBER ',' NUMBER ')' + { + itfData->via.add_area_crt1_ct2($3, $5, $7); + } +| +; + +rho_property : + rho { itfData->via.set_rho(v_rho); } +| rpv +| area +| rpv_vs_area +; + +rpv : + K_RPV '=' NUMBER + { + itfData->via.set_rpv($3); + } +; + +area : + K_AREA '=' NUMBER + { + itfData->via.set_area($3); + } +; + +rpv_vs_area : + K_RPV_VS_AREA '{' + area_rpv_list + '}' +; + +area_rpv_list : + area_rpv_list '(' NUMBER ',' NUMBER ')' + { itfData->via.add_area_rpv($3, $5); } +| +; + +etch_property : + etch_vs_contact_and_gate_spacings +| etch_vs_width_and_spacing + { + itfData->via.set_etch_vws(v_etch_effect_type, v_lut); + ITF_FREE(v_etch_effect_type); + v_lut.clear(); + v_is_lut_working = 0; + } +| etch_vs_width_and_length +| K_CAPACITIVE_ONLY_ETCH '=' NUMBER + { itfData->via.set_capacitive_only_etch($3); } +; + +etch_vs_contact_and_gate_spacings : + K_ETCH_VS_CONTACT_AND_GATE_SPACINGS + { + v_lut.set_names("GATE_TO_CONTACT_SPACINGS", "CONTACT_TO_CONTACT_SPACINGS", "VALUES"); + v_is_lut_working = 1; + } + K_CAPACITIVE_ONLY '{' + etch_vs_contact_and_gate_spacings_property + '}' + { + v_is_lut_working = 0; + } +; + +etch_vs_contact_and_gate_spacings_property : + table + { + itfData->via.get_etch_cg().add_table("", v_lut); + v_lut.clear(); + } +| number_of_tables name_tables + { + itfData->via.get_etch_cg().set_number_of_tables(v_number_of_tables); + v_lut.clear(); + } +; + +table : + contact_to_contact_spacings + gate_to_contact_spacings + values +; + +name_tables : + name_tables + table_name '{' + table + '}' + { + itfData->via.get_etch_cg().add_table(v_table_name, v_lut); + v_lut.clear(); + + v_lut.set_names("GATE_TO_CONTACT_SPACINGS", "CONTACT_TO_CONTACT_SPACINGS", "VALUES"); + } +| +; + +table_name : + KEYWORD { ITF_STR_CPY(v_table_name, $1); } +; + +etch_vs_width_and_length : + K_ETCH_VS_WIDTH_AND_LENGTH + { + v_etch_wlv_lut.set_names("WIDTHS", "LENGTHS", "VALUES"); + } + K_CAPACITIVE_ONLY '{' + K_LENGTHS '{' float_list '}' + { + v_etch_wlv_lut.set_data_list("LENGTHS", v_float_list); + v_float_list.clear(); + } + K_WIDTHS '{' float_list '}' + { + v_etch_wlv_lut.set_data_list("WIDTHS", v_float_list); + v_float_list.clear(); + } + K_VALUES '{' float_pair_list '}' + { + v_etch_wlv_lut.set_data_list>("VALUES", v_float_pair_list); + v_float_pair_list.clear(); + } + '}' + { + itfData->via.set_etch_vwl(v_etch_wlv_lut); + v_etch_wlv_lut.clear(); + } +; + +rpv_vs_width_and_length : + K_RPV_VS_WIDTH_AND_LENGTH '{' + K_LENGTHS '{' float_list '}' + K_WIDTHS '{' float_list '}' + K_VALUES '{' float_list '}' + '}' +; + +variation_params : + K_VARIATION_PARAMETERS '{' + variation_params_list + '}' +| +; + +variation_params_list : + variation_params_list variation_param +| +; + +variation_param : + variation_param_name '=' '{' + variation_param_table + '}' + { + CALLBACK(itfCallbacks->variation_cb, itfCallBackType::kVariationCbType, &itfData->variation_param); + itfData->variation_param.clear(); + } +; + +variation_param_name : + KEYWORD + { + itfData->variation_param.param_name = $1; + } +; + +variation_param_table : + variation_param_table + variation_param_table_term +| +; + +variation_param_table_term : + '(' layer_name ',' variation_param_type ',' NUMBER ')' + { + v_vpt.layer = v_layer_name; + v_vpt.coeff = $6; + itfData->variation_param.add_term(v_vpt); + v_vpt.clear(); + } +; + +variation_param_type : + K_THICKNESS { v_vpt.type = "THICKNESS"; } +| K_WIDTH { v_vpt.type = "WIDTH"; } +| K_RHO { v_vpt.type = "RHO"; } +| K_ER { v_vpt.type = "ER"; } +| K_RPV { v_vpt.type = "RPV"; } +| KEYWORD { v_vpt.type = $1; } +; + +%% + +} // namespace itf + +void itf_error(ITF_LTYPE* yylloc_param, const char* s) { + std::cout << "error: " << s << ", " + << "at line " << yylloc_param->last_line << ", " + << "col " << yylloc_param->last_column << std::endl; +} diff --git a/src/operation/iRCX/source/parser/itf/itfiConductor.cpp b/src/operation/iRCX/source/parser/itf/itfiConductor.cpp new file mode 100644 index 000000000..9927620ba --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itfiConductor.cpp @@ -0,0 +1,1312 @@ +// *************************************************************************************** +// 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 +#include + +#include "itfiConductor.hpp" +#include "itfMarco.h" +#include "itfUtil.h" +namespace itf +{ + +itfiConductor::itfiConductor() +: _conductor_name(nullptr), + _wmin(0), + _smin(0), + _thickness(0), + _air_gap_spacings(), + _air_gap_widths(), + _air_gap_thicknesses(), + _air_gap_bottom_heights(), + _bottom_dielectric_thickness(0), + _bottom_dielectric_er(0), + _bottom_thickness_vs_si_width(), + _t0(25), + _crt1(std::nullopt), + _crt2(std::nullopt), + _crt_vs_si_width(), + _density_box_weighting_factor(), + _drop_factor(0), + _etch(0), + _capacitive_only_etch(0), + _resistive_only_etch(0), + _etch_vws_list(), + _fill_ratio(0), + _fill_width(0), + _fill_spacing(0), + _fill_type(nullptr), + _gate_to_contact_smin(0), + _gate_to_diffusion_cap(), + _ild_vws(), + _layer_type(nullptr), + _measured_from(nullptr), + _PBTV(), + _rpsq(0), + _rho(0), + _rpsq_v_siw(), + _rpsq_vws(), + _rho_v_siw_t(), + _rho_vws(), + _side_tangent(0), + _thickness_vs_density(), + _thickness_vws(), + _tvf_bt_vws(), + _tvf_bt_vwd(), + _has_air_gap_vs_spacing(0), + _has_bottom_dielectric_thickness(0), + _has_bottom_dielectric_er(0), + _has_bottom_thickness_vs_si_width(0), + _has_t0(0), + _has_crt_stmt(0), + _has_density_box_weighting_factor(0), + _has_drop_factor(0), + _has_etch_stmt(0), + _has_evwas(0), + _has_fill_stmt(0), + _has_gate_to_contact_smin(0), + _has_gate_to_diffusion_cap(0), + _has_ild_vs_width_and_spacing(0), + _is_planar(0), + _has_layer_type(0), + _has_measured_from(0), + _has_polynomial_based_thickness_variation(0), + _has_side_tangent(0), + _has_thickness_vs_density(0), + _has_thickness_vs_width_and_spacing(0), + _has_tvf_adjustment_tables(0) +{ } + +itfiConductor::itfiConductor(const itfiConductor& other) +: _conductor_name(nullptr) +, _fill_type(nullptr) +, _layer_type(nullptr) +, _measured_from(nullptr) +{ + *this = other; +} + +itfiConductor& itfiConductor::operator=(const itfiConductor& rhs) +{ + if (this == &rhs) return *this; + + ITF_STR_CPY(_conductor_name, rhs._conductor_name); + _wmin = rhs._wmin; + _smin = rhs._smin; + _thickness = rhs._thickness; + _air_gap_spacings = rhs._air_gap_spacings; + _air_gap_widths = rhs._air_gap_widths; + _air_gap_thicknesses = rhs._air_gap_thicknesses; + _air_gap_bottom_heights = rhs._air_gap_bottom_heights; + _bottom_dielectric_thickness = rhs._bottom_dielectric_thickness; + _bottom_dielectric_er = rhs._bottom_dielectric_er; + _bottom_thickness_vs_si_width = rhs._bottom_thickness_vs_si_width; + _t0 = rhs._t0; + _crt1 = rhs._crt1; + _crt2 = rhs._crt2; + _crt_vs_si_width = rhs._crt_vs_si_width; + _density_box_weighting_factor = rhs._density_box_weighting_factor; + _drop_factor = rhs._drop_factor; + _etch = rhs._etch; + _capacitive_only_etch = rhs._capacitive_only_etch; + _resistive_only_etch = rhs._resistive_only_etch; + _etch_vws_list = rhs._etch_vws_list; + _fill_ratio = rhs._fill_ratio; + _fill_width = rhs._fill_width; + _fill_spacing = rhs._fill_spacing; + ITF_STR_CPY(_fill_type, rhs._fill_type); + _gate_to_contact_smin = rhs._gate_to_contact_smin; + _gate_to_diffusion_cap = rhs._gate_to_diffusion_cap; + _ild_vws = rhs._ild_vws; + ITF_STR_CPY(_layer_type, rhs._layer_type); + ITF_STR_CPY(_measured_from, rhs._measured_from); + _PBTV = rhs._PBTV; + _rpsq = rhs._rpsq; + _rho = rhs._rho; + _rpsq_v_siw = rhs._rpsq_v_siw; + _rpsq_vws = rhs._rpsq_vws; + _rho_v_siw_t = rhs._rho_v_siw_t; + _rho_vws = rhs._rho_vws; + _side_tangent = rhs._side_tangent; + _thickness_vs_density = rhs._thickness_vs_density; + _thickness_vws = rhs._thickness_vws; + _tvf_bt_vws = rhs._tvf_bt_vws; + _tvf_bt_vwd = rhs._tvf_bt_vwd; + _has_air_gap_vs_spacing = rhs._has_air_gap_vs_spacing; + _has_bottom_dielectric_thickness = rhs._has_bottom_dielectric_thickness; + _has_bottom_dielectric_er = rhs._has_bottom_dielectric_er; + _has_bottom_thickness_vs_si_width = rhs._has_bottom_thickness_vs_si_width; + _has_t0 = rhs._has_t0; + _has_crt_stmt = rhs._has_crt_stmt; + _has_density_box_weighting_factor = rhs._has_density_box_weighting_factor; + _has_drop_factor = rhs._has_drop_factor; + _has_etch_stmt = rhs._has_etch_stmt; + _has_evwas = rhs._has_evwas; + _has_fill_stmt = rhs._has_fill_stmt; + _has_gate_to_contact_smin = rhs._has_gate_to_contact_smin; + _has_gate_to_diffusion_cap = rhs._has_gate_to_diffusion_cap; + _has_ild_vs_width_and_spacing = rhs._has_ild_vs_width_and_spacing; + _is_planar = rhs._is_planar; + _has_layer_type = rhs._has_layer_type; + _has_measured_from = rhs._has_measured_from; + _has_polynomial_based_thickness_variation = rhs._has_polynomial_based_thickness_variation; + _has_side_tangent = rhs._has_side_tangent; + _has_thickness_vs_density = rhs._has_thickness_vs_density; + _has_thickness_vs_width_and_spacing = rhs._has_thickness_vs_width_and_spacing; + _has_tvf_adjustment_tables = rhs._has_tvf_adjustment_tables; + + return *this; +} + +bool +itfiConductor::operator==(const itfiConductor& rhs) const +{ + if (this == &rhs) return true; + + return itfStrCmp(_conductor_name, rhs._conductor_name) + && _wmin == rhs._wmin + && _smin == rhs._smin + && _thickness == rhs._thickness + && _air_gap_spacings == rhs._air_gap_spacings + && _air_gap_widths == rhs._air_gap_widths + && _air_gap_thicknesses == rhs._air_gap_thicknesses + && _air_gap_bottom_heights == rhs._air_gap_bottom_heights + && _bottom_dielectric_thickness == rhs._bottom_dielectric_thickness + && _bottom_dielectric_er == rhs._bottom_dielectric_er + && _bottom_thickness_vs_si_width == rhs._bottom_thickness_vs_si_width + && _t0 == rhs._t0 + && _crt1 == rhs._crt1 + && _crt2 == rhs._crt2 + && _crt_vs_si_width == rhs._crt_vs_si_width + && _density_box_weighting_factor == rhs._density_box_weighting_factor + && _drop_factor == rhs._drop_factor + && _etch == rhs._etch + && _capacitive_only_etch == rhs._capacitive_only_etch + && _resistive_only_etch == rhs._resistive_only_etch + && _etch_vws_list == rhs._etch_vws_list + && _fill_ratio == rhs._fill_ratio + && _fill_width == rhs._fill_width + && _fill_spacing == rhs._fill_spacing + && itfStrCmp(_fill_type, rhs._fill_type) + && _gate_to_contact_smin == rhs._gate_to_contact_smin + && _gate_to_diffusion_cap == rhs._gate_to_diffusion_cap + && _ild_vws == rhs._ild_vws + && itfStrCmp(_layer_type, rhs._layer_type) + && itfStrCmp(_measured_from, rhs._measured_from) + && _PBTV == rhs._PBTV + && _rpsq == rhs._rpsq + && _rho == rhs._rho + && _rpsq_v_siw == rhs._rpsq_v_siw + && _rpsq_vws == rhs._rpsq_vws + && _rho_v_siw_t == rhs._rho_v_siw_t + && _rho_vws == rhs._rho_vws + && _side_tangent == rhs._side_tangent + && _thickness_vs_density == rhs._thickness_vs_density + && _thickness_vws == rhs._thickness_vws + && _tvf_bt_vws == rhs._tvf_bt_vws + && _tvf_bt_vwd == rhs._tvf_bt_vwd + + && _has_air_gap_vs_spacing == rhs._has_air_gap_vs_spacing + && _has_bottom_dielectric_thickness == rhs._has_bottom_dielectric_thickness + && _has_bottom_dielectric_er == rhs._has_bottom_dielectric_er + && _has_bottom_thickness_vs_si_width == rhs._has_bottom_thickness_vs_si_width + && _has_t0 == rhs._has_t0 + && _has_crt_stmt == rhs._has_crt_stmt + && _has_density_box_weighting_factor == rhs._has_density_box_weighting_factor + && _has_drop_factor == rhs._has_drop_factor + && _has_etch_stmt == rhs._has_etch_stmt + && _has_evwas == rhs._has_evwas + && _has_fill_stmt == rhs._has_fill_stmt + && _has_gate_to_contact_smin == rhs._has_gate_to_contact_smin + && _has_gate_to_diffusion_cap == rhs._has_gate_to_diffusion_cap + && _has_ild_vs_width_and_spacing == rhs._has_ild_vs_width_and_spacing + && _is_planar == rhs._is_planar + && _has_layer_type == rhs._has_layer_type + && _has_measured_from == rhs._has_measured_from + && _has_polynomial_based_thickness_variation == rhs._has_polynomial_based_thickness_variation + && _has_side_tangent == rhs._has_side_tangent + && _has_thickness_vs_density == rhs._has_thickness_vs_density + && _has_thickness_vs_width_and_spacing == rhs._has_thickness_vs_width_and_spacing + && _has_tvf_adjustment_tables == rhs._has_tvf_adjustment_tables + ; +} + +itfiConductor::~itfiConductor() +{ + clear(); +} + +const char* +itfiConductor::get_conductor_name() const +{ + return _conductor_name; +} + +float +itfiConductor::get_wmin() const +{ + return _wmin; +} + +float +itfiConductor::get_smin() const +{ + return _smin; +} + +float +itfiConductor::get_thickness () const +{ + return _thickness; +} + +std::vector& +itfiConductor::get_air_gap_spacings() +{ + return _air_gap_spacings; +} + +const std::vector& +itfiConductor::get_air_gap_spacings() const +{ + return _air_gap_spacings; +} + +std::vector& +itfiConductor::get_air_gap_widths() +{ + return _air_gap_widths; +} + +const std::vector& +itfiConductor::get_air_gap_widths() const +{ + return _air_gap_widths; +} + +std::vector& +itfiConductor::get_air_gap_thicknesses() +{ + return _air_gap_thicknesses; +} + +const std::vector& +itfiConductor::get_air_gap_thicknesses() const +{ + return _air_gap_thicknesses; +} + +std::vector& +itfiConductor::get_air_gap_bottom_heights() +{ + return _air_gap_bottom_heights; +} + +const std::vector& +itfiConductor::get_air_gap_bottom_heights() const +{ + return _air_gap_bottom_heights; +} + +float +itfiConductor::get_bottom_dielectric_thickness() const +{ + return _bottom_dielectric_thickness; +} + +float +itfiConductor::get_bottom_dielectric_er() const +{ + return _bottom_dielectric_er; +} + +itfiBTSW& +itfiConductor::get_bottom_thickness_vs_si_width() +{ + return _bottom_thickness_vs_si_width; +} + +const itfiBTSW& +itfiConductor::get_bottom_thickness_vs_si_width() const +{ + return _bottom_thickness_vs_si_width; +} + +float +itfiConductor::get_t0() const +{ + return _t0; +} + +std::optional +itfiConductor::get_crt1() const +{ + return _crt1; +} + +std::optional +itfiConductor::get_crt2() const +{ + return _crt2; +} + +const std::vector& +itfiConductor::get_crt_vs_si_width() const +{ + return _crt_vs_si_width; +} + +const std::vector& +itfiConductor::get_density_box_weighting_factor() const +{ + if (_density_box_weighting_factor.size()) { + return _density_box_weighting_factor; + } else { + static const std::vector kDefaultDensityBoxWeightingFactor{{50, 1}}; + return kDefaultDensityBoxWeightingFactor; + } +} + +float +itfiConductor::get_drop_factor() const +{ + return _drop_factor; +} + +float +itfiConductor::get_etch() const +{ + return _etch; +} + +float +itfiConductor::get_capacitive_only_etch() const +{ + return _capacitive_only_etch; +} + +float +itfiConductor::get_resistive_only_etch() const +{ + return _resistive_only_etch; +} + +// ETCH_VS_WIDTH_AND_SPACING +std::vector& +itfiConductor::get_etch_vws_list() +{ + return _etch_vws_list; +} + +const std::vector& +itfiConductor::get_etch_vws_list() const +{ + return _etch_vws_list; +} + +float +itfiConductor::get_fill_ratio() const +{ + return _fill_ratio; +} + +float +itfiConductor::get_fill_width() const +{ + return _fill_width; +} + +float +itfiConductor::get_fill_spacing() const +{ + return _fill_spacing; +} + +const char* +itfiConductor::get_fill_type() const +{ + if (_fill_type) return _fill_type; + return "GROUNDED"; +} + +float +itfiConductor::get_gate_to_contact_smin() const +{ + return _gate_to_contact_smin; +} + +itfiG2DC& +itfiConductor::get_gate_to_diffusion_cap() +{ + return _gate_to_diffusion_cap; +} + +const itfiG2DC& +itfiConductor::get_gate_to_diffusion_cap() const +{ + return _gate_to_diffusion_cap; +} + +itfTitleLut& +itfiConductor::get_ild_vws() +{ + return _ild_vws; +} + +const itfTitleLut& +itfiConductor::get_ild_vws() const +{ + return _ild_vws; +} + +char* +itfiConductor::get_layer_type() const +{ + return _layer_type; +} + +char* +itfiConductor::get_measured_from() const +{ + return _measured_from; +} + +itfiPBTV& +itfiConductor::get_PBTV() +{ + return _PBTV; +} + +const itfiPBTV& +itfiConductor::get_PBTV() const +{ + return _PBTV; +} + +float +itfiConductor::get_rpsq() const +{ + return _rpsq; +} + +float +itfiConductor::get_rho() const +{ + return _rho; +} + +itf1DLUT& +itfiConductor::get_rpsq_vs_si_width() +{ + return _rpsq_v_siw; +} + +const itf1DLUT& +itfiConductor::get_rpsq_vs_si_width() const +{ + return _rpsq_v_siw; +} + +itf2DLUT& +itfiConductor::get_rpsq_vws() +{ + return _rpsq_vws; +} + +const itf2DLUT& +itfiConductor::get_rpsq_vws() const +{ + return _rpsq_vws; +} + +itfiRhoVWS& +itfiConductor::get_rho_v_siw_t() +{ + return _rho_v_siw_t; +} + +const itfiRhoVWS& +itfiConductor::get_rho_v_siw_t() const +{ + return _rho_v_siw_t; +} + +itf2DLUT& +itfiConductor::get_rho_vws() +{ + return _rho_vws; +} + +const itf2DLUT& +itfiConductor::get_rho_vws() const +{ + return _rho_vws; +} + +float +itfiConductor::get_side_tangent() const +{ + return _side_tangent; +} + +itfiThicknessVsDensity& +itfiConductor::get_thickness_vs_density() +{ + return _thickness_vs_density; +} + +const itfiThicknessVsDensity& +itfiConductor::get_thickness_vs_density() const +{ + return _thickness_vs_density; +} + +itfTitleLut& +itfiConductor::get_thickness_vws() +{ + return _thickness_vws; +} + +const itfTitleLut& +itfiConductor::get_thickness_vws() const +{ + return _thickness_vws; +} + +itf2DLUT& +itfiConductor::get_tvf_bt_vws() +{ + return _tvf_bt_vws; +} + +const itf2DLUT& +itfiConductor::get_tvf_bt_vws() const +{ + return _tvf_bt_vws; +} + +itf2DLUT& +itfiConductor::get_tvf_bt_vwd() +{ + return _tvf_bt_vwd; +} + +const itf2DLUT& +itfiConductor::get_tvf_bt_vwd() const +{ + return _tvf_bt_vwd; +} + +void +itfiConductor::set_conductor_name(const char* name) +{ + ITF_STR_CPY(_conductor_name, name); +} + +void +itfiConductor::set_wmin(float v) +{ + _wmin = v; +} + +void +itfiConductor::set_smin(float v) +{ + _smin = v; +} + +void +itfiConductor::set_thickness(float v) +{ + _thickness = v; +} + +void +itfiConductor::set_air_gap_spacings(const std::vector& v) +{ + _air_gap_spacings = v; +} + +void +itfiConductor::set_air_gap_widths(const std::vector& v) +{ + _air_gap_widths = v; +} + +void +itfiConductor::set_air_gap_thicknesses(const std::vector& v) +{ + _air_gap_thicknesses = v; +} + +void +itfiConductor::set_air_gap_bottom_heights(const std::vector& v) +{ + _air_gap_bottom_heights = v; +} + +// @param s The size of the density box. Units: microns +// @param w The weighting factor. +void +itfiConductor::add_density_box_weight(int s, double w) +{ + if (_density_box_weighting_factor.size() >= 6) { + std::cout << "Up to 5 entries are allowed" << std::endl; + return; + } + + if ((0 < s) && (s < 500) + && (-10 < w) && (w < 10) && (w != 0) ) + { + _density_box_weighting_factor.emplace_back(s, w); + } else { + std::cout << "Data invalid, when s = " << s + << ", w = " << w + << std::endl; + } +} + +void +itfiConductor::set_bottom_dielectric_thickness(float v) +{ + _bottom_dielectric_thickness = v; +} + +void +itfiConductor::set_bottom_dielectric_er(float v) +{ + _bottom_dielectric_er = v; +} + +void +itfiConductor::set_t0(float t) +{ + _t0 = t; +} + +void +itfiConductor::set_crt1(float crt1) +{ + _crt1 = crt1; +} + +void +itfiConductor::set_crt2(float crt2) +{ + _crt2 = crt2; +} + +// @param siw Post-etch conductor widths. Units: microns +// @param crt1 Linear temperature coefficients +// @param crt2 Quadratic temperature coefficients +void +itfiConductor::add_siw_crt1_crt2(float siw, float crt1, float crt2) +{ + _crt_vs_si_width.emplace_back(siw, crt1, crt2); +} + +void +itfiConductor::set_drop_factor(float v) +{ + _drop_factor = v; +} + +void +itfiConductor::set_etch(float v) +{ + _etch = v; +} + +void +itfiConductor::set_capacitive_only_etch(float v) +{ + _capacitive_only_etch = v; +} + +void +itfiConductor::set_resistive_only_etch(float v) +{ + _resistive_only_etch = v; +} + +void +itfiConductor::add_etch_vws(const char* title, const itf2DLUT& lut) +{ + _etch_vws_list.emplace_back(title ? title : "", lut); +} + +void +itfiConductor::set_fill_ratio(float ratio) +{ + _fill_ratio = ratio; +} + +void +itfiConductor::set_fill_width(float width) +{ + _fill_width = width; +} + +void +itfiConductor::set_fill_spacing(float spacing) +{ + _fill_spacing = spacing; +} + +void +itfiConductor::set_fill_type(const char* type) +{ + if ((strncmp("GROUNDED", type, 8) == 0) + || (strncmp("FLOATING", type, 8) == 0) ) + { + ITF_STR_CPY(_fill_type, type); + } else { + std::cout << "Invalid type: " << type << std::endl; + } +} + +void +itfiConductor::set_gate_to_contact_smin(float v) +{ + _gate_to_contact_smin = v; +} + +void +itfiConductor::set_layer_type(const char* type) +{ + ITF_STR_CPY(_layer_type, type); +} + +void +itfiConductor::set_side_tangent(float v) +{ + _side_tangent = v; +} + +void +itfiConductor::set_is_planar() +{ + _is_planar = 1; +} + +void +itfiConductor::set_ild_vws_lut(const itf2DLUT& v) +{ + _ild_vws.set_lut(v); +} + +void +itfiConductor::set_ild_vws_title(const char* v) +{ + _ild_vws.set_title(v); +} + +void +itfiConductor::set_thickness_vws_lut(const itf2DLUT& v) +{ + _thickness_vws.set_lut(v); +} + +void +itfiConductor::set_thickness_vws_title(const char* v) +{ + _thickness_vws.set_title(v); +} + +void +itfiConductor::set_rpsq(float v) +{ + _rpsq = v; +} + +void +itfiConductor::set_rho(float v) +{ + _rho = v; +} + +void +itfiConductor::set_rpsq_vs_si_width(const std::vector>& v) +{ + _rpsq_v_siw.set_points(v); +} + +void +itfiConductor::set_rpsq_vws(const itf2DLUT& v) +{ + _rpsq_vws = v; +} + +void +itfiConductor::set_rho_v_siw_t(const itf2DLUT& v) +{ + _rho_v_siw_t = v; +} + +void +itfiConductor::set_rho_vws(const itf2DLUT& v) +{ + _rho_vws = v; +} + +void +itfiConductor::set_tvf_bt_vws(const itf2DLUT& v) +{ + _tvf_bt_vws = v; +} + +void +itfiConductor::set_tvf_bt_vwd(const itf2DLUT& v) +{ + _tvf_bt_vwd = v; +} + +void +itfiConductor::set_measured_from(const char* s) +{ + ITF_STR_CPY(_measured_from, s); + _has_measured_from = 1; +} + +void +itfiConductor::clear() +{ + ITF_FREE(_conductor_name); + _wmin = 0; + _smin = 0; + _thickness = 0; + _air_gap_spacings.clear(); + _air_gap_widths.clear(); + _air_gap_thicknesses.clear(); + _air_gap_bottom_heights.clear(); + _bottom_dielectric_thickness = 0; + _bottom_dielectric_er = 0; + _bottom_thickness_vs_si_width.clear(); + _t0 = 25; + _crt1.reset(); + _crt2.reset(); + _crt_vs_si_width.clear(); + _density_box_weighting_factor.clear(); + _drop_factor = 0; + _etch = 0; + _capacitive_only_etch = 0; + _resistive_only_etch = 0; + _etch_vws_list.clear(); + _fill_ratio = 0; + _fill_width = 0; + _fill_spacing = 0; + ITF_FREE(_fill_type); + _gate_to_contact_smin = 0; + _gate_to_diffusion_cap.clear(); + _ild_vws.clear(); + ITF_FREE(_layer_type); + ITF_FREE(_measured_from); + _PBTV.clear(); + _rpsq = 0; + _rho = 0; + _rpsq_v_siw.clear(); + _rpsq_vws.clear(); + _rho_v_siw_t.clear(); + _rho_vws.clear(); + _side_tangent = 0; + _thickness_vs_density.clear(); + _thickness_vws.clear(); + _tvf_bt_vws.clear(); + _tvf_bt_vwd.clear(); + + _has_air_gap_vs_spacing = 0; + _has_bottom_dielectric_thickness = 0; + _has_bottom_dielectric_er = 0; + _has_bottom_thickness_vs_si_width = 0; + _has_t0 = 0; + _has_crt_stmt = 0; + _has_density_box_weighting_factor = 0; + _has_drop_factor = 0; + _has_etch_stmt = 0; + _has_evwas = 0; + _has_fill_stmt = 0; + _has_gate_to_contact_smin = 0; + _has_gate_to_diffusion_cap = 0; + _has_ild_vs_width_and_spacing = 0; + _is_planar = 0; + _has_layer_type = 0; + _has_measured_from = 0; + _has_polynomial_based_thickness_variation = 0; + _has_side_tangent = 0; + _has_thickness_vs_density = 0; + _has_thickness_vs_width_and_spacing = 0; + _has_tvf_adjustment_tables = 0; +} + +// linear interpolation when width lies in the table, +// otherwise use boundary value. +// In other words, no extrapolation. +// @param width Conductor silicon (post-etch) widths. Units: microns. +// @param crt1 Linear temperature coefficients for the corresponding conductor widths +// @param crt2 Quadratic temperature coefficients +void +itfiConductor::query_crt(double width, double& crt1, double& crt2) +{ + if (_crt_vs_si_width.size() == 0) { + std::cout << "table _crt_vs_si_width is empty" << std::endl; + return; + } + + auto it_high = std::upper_bound(_crt_vs_si_width.begin(), _crt_vs_si_width.end(), + width, [](double v, itfiWidthCrt elem){ + return v < elem.si_width; + }); + + size_t idx_h = 0, idx_l = 0; + if (it_high == _crt_vs_si_width.end()) { + idx_h = _crt_vs_si_width.size() - 1; + idx_l = idx_h; + } else { + idx_h = std::distance(_crt_vs_si_width.begin(), it_high); + idx_l = idx_h - (idx_h == 0 ? 0 : 1); + } + + auto& elem_h = _crt_vs_si_width.at(idx_h); + auto& elem_l = _crt_vs_si_width.at(idx_l); + auto t = (width - elem_l.si_width) / (elem_h.si_width - elem_l.si_width); + crt1 = std::lerp(elem_l.crt_1, elem_h.crt_1, t); + crt2 = std::lerp(elem_l.crt_2, elem_h.crt_2, t); +} + +itfiBTSW::itfiBTSW() +: _type(nullptr), + _sr_lut() +{ + +} + +itfiBTSW::itfiBTSW( + const itfiBTSW& other) +: _type(nullptr), + _sr_lut() +{ + *this = other; +} + +itfiBTSW& +itfiBTSW::operator=(const itfiBTSW& rhs) +{ + if (this == &rhs) return *this; + + ITF_STR_CPY(_type, rhs._type); + _sr_lut = rhs._sr_lut; + + return *this; +} + +bool +itfiBTSW::operator==(const itfiBTSW& rhs) const +{ + if (this == &rhs) return true; + + return itfStrCmp(_type, rhs._type) + && _sr_lut == rhs._sr_lut + ; +} + +itfiBTSW::~itfiBTSW() +{ + clear(); +} + +const char* +itfiBTSW::get_type() const +{ + return _type; +} + +itf1DLUT& +itfiBTSW::get_sr_lut() +{ + return _sr_lut; +} + +const itf1DLUT& +itfiBTSW::get_sr_lut() const +{ + return _sr_lut; +} + +const std::vector>& +itfiBTSW::get_sr_list() const +{ + return _sr_lut.get_points(); +} + +void +itfiBTSW::set_type(const char* type) +{ + if ((strncmp("RESISTIVE_ONLY", type, 14) == 0) + || (strncmp("CAPACITIVE_ONLY", type, 15) == 0) ) + { + ITF_STR_CPY(_type, type); + } else { + std::cout << "Invalid type: " << type << std::endl; + } +} + +void +itfiBTSW::clear() +{ + ITF_FREE(_type); + _sr_lut.clear(); +} + +void +itfiBTSW::set_sr_list(const std::vector>& pair_list) +{ + _sr_lut.set_points(pair_list); +} + +size_t +itfiG2DC::get_number_of_tables() const +{ + return _number_of_tables; +} + +void +itfiG2DC::set_number_of_tables(size_t n) +{ + _number_of_tables = n; +} + +void +itfiG2DC::add_model( + const char* title, + const itf2DLUT& lut +) { + _model_list.emplace_back(title ? title : "", lut); +} + +bool +itfiG2DC::operator==(const itfiG2DC& rhs) const +{ + if (this == &rhs) return true; + + return _number_of_tables == rhs._number_of_tables + && _model_list == rhs._model_list + ; +} + +void +itfiG2DC::clear() +{ + _number_of_tables = 0; + _model_list.clear(); +} + +const std::vector& +itfiPBTV::get_density_polynomial_orders() const +{ + return _density_polynomial_orders; +} + +const std::vector& +itfiPBTV::get_width_polynomial_orders() const +{ + return _width_polynomial_orders; +} + +const std::vector& +itfiPBTV::get_width_ranges() const +{ + return _width_ranges; +} + +// @param id index of _polynomial_coefficients_list +const std::vector& +itfiPBTV::get_polynomial_coefficients_list(size_t id) const +{ + if (id < _polynomial_coefficients_list.size()) { + return _polynomial_coefficients_list.at(id); + } + + static const std::vector kEmptyCoefficients; + return kEmptyCoefficients; +} + +size_t +itfiPBTV::get_polynomial_coefficients_list_size() const +{ + return _polynomial_coefficients_list.size(); +} + +void +itfiPBTV::add_density_polynomial_order(int order) +{ + _density_polynomial_orders.emplace_back(order); +} + +void +itfiPBTV::add_width_polynomial_order(int order) +{ + _width_polynomial_orders.emplace_back(order); +} + +void +itfiPBTV::add_width_range(float threshold) +{ + _width_ranges.emplace_back(threshold); +} + +void +itfiPBTV::set_density_polynomial_order(const std::vector& list) +{ + _density_polynomial_orders = list; +} + +void +itfiPBTV::set_width_polynomial_order(const std::vector& list) +{ + _width_polynomial_orders = list; +} + +void +itfiPBTV::set_width_range(const std::vector& list) +{ + _width_ranges = list; +} + +void +itfiPBTV::add_polynomial_coefficients(const std::vector& list) +{ + _polynomial_coefficients_list.emplace_back(list); +} + +bool +itfiPBTV::operator==(const itfiPBTV& rhs) const +{ + if (this == &rhs) return true; + + return _density_polynomial_orders == rhs. _density_polynomial_orders + && _width_polynomial_orders == rhs. _width_polynomial_orders + && _width_ranges == rhs. _width_ranges + && _polynomial_coefficients_list == rhs. _polynomial_coefficients_list + ; +} + +void +itfiPBTV::clear() +{ + _density_polynomial_orders.clear(); + _width_polynomial_orders.clear(); + _width_ranges.clear(); + _polynomial_coefficients_list.clear(); +} + +itfiThicknessVsDensity::itfiThicknessVsDensity() +: _type(nullptr), + _dr_lut() +{ + +} + +itfiThicknessVsDensity::itfiThicknessVsDensity(const itfiThicknessVsDensity& other) +: _type(nullptr), + _dr_lut() +{ + *this = other; +} + +itfiThicknessVsDensity& +itfiThicknessVsDensity::operator=(const itfiThicknessVsDensity& rhs) +{ + if (this == &rhs) return *this; + + ITF_STR_CPY(_type, rhs._type); + _dr_lut = rhs._dr_lut; + + return *this; +} + +bool +itfiThicknessVsDensity::operator==(const itfiThicknessVsDensity& rhs) const +{ + if (this == &rhs) return true; + + return itfStrCmp(_type, rhs._type) + && _dr_lut == rhs._dr_lut + ; +} + +itfiThicknessVsDensity::~itfiThicknessVsDensity() +{ + clear(); +} + +const char* +itfiThicknessVsDensity::get_type() const +{ + return _type; +} + +itf1DLUT& +itfiThicknessVsDensity::get_dr_lut() +{ + return _dr_lut; +} + +const itf1DLUT& +itfiThicknessVsDensity::get_dr_lut() const +{ + return _dr_lut; +} + +const std::vector>& +itfiThicknessVsDensity::get_dr_list() const +{ + return _dr_lut.get_points(); +} + +void +itfiThicknessVsDensity::set_type(const char* type) +{ + if ((strncmp("RESISTIVE_ONLY", type, 14) == 0) + || (strncmp("CAPACITIVE_ONLY", type, 15) == 0) ) + { + ITF_STR_CPY(_type, type); + } else { + std::cout << "Invalid type: " << type << std::endl; + } +} + +// @param d density +// @param r relative change in thickness +void +itfiThicknessVsDensity::add_dr(float d, float r) +{ + _dr_lut.add_point(d, r); +} + +void +itfiThicknessVsDensity::set_dr_list(const std::vector>& list) +{ + _dr_lut.set_points(list); +} + +void +itfiThicknessVsDensity::clear() +{ + ITF_FREE(_type); + _dr_lut.clear(); +} + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf/itfiConductor.hpp b/src/operation/iRCX/source/parser/itf/itfiConductor.hpp new file mode 100644 index 000000000..025bbe96e --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itfiConductor.hpp @@ -0,0 +1,392 @@ +// *************************************************************************************** +// 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 + +#include "itf1DLUT.hpp" +#include "itf2DLUT.hpp" +namespace itf +{ + +// Bottom Thickness Vs Silicon Width +class itfiBTSW { + public: + // constructor + itfiBTSW(); + itfiBTSW(const itfiBTSW&); + ~itfiBTSW(); + + // getter + const char* get_type() const; + itf1DLUT& get_sr_lut(); + const itf1DLUT& get_sr_lut() const; + const std::vector>& get_sr_list() const; + + // setter + void set_type(const char*); + void set_sr_list(const std::vector>&); + + // operator + itfiBTSW& operator=(const itfiBTSW&); + bool operator==(const itfiBTSW&) const; + + // function + void clear(); + + private: + // members + char* _type; + itf1DLUT _sr_lut; // (silicon width, relative change) +}; + +// Gate To Diffusion Cap +// (gate_to_contact, contact_to_contact) -> caps_per_micron +class itfiG2DC { + public: + // constructor + + // getter + size_t get_number_of_tables() const; + + // setter + void set_number_of_tables(size_t); + void add_model(const char*, const itf2DLUT&); + + // operator + bool operator==(const itfiG2DC&) const; + + // function + void clear(); + + private: + // members + size_t _number_of_tables; + std::vector> _model_list; // (gate_to_contact, contact_to_contact) -> caps_per_micron +}; + +// Polynomial Based Thickness Variation +class itfiPBTV { + public: + // constructor + + // getter + const std::vector& get_density_polynomial_orders() const; + const std::vector& get_width_polynomial_orders() const; + const std::vector& get_width_ranges() const; + const std::vector& get_polynomial_coefficients_list(size_t) const; + size_t get_polynomial_coefficients_list_size() const; + + // setter + void add_density_polynomial_order(int); + void add_width_polynomial_order(int); + void add_width_range(float); + void set_density_polynomial_order(const std::vector&); + void set_width_polynomial_order(const std::vector&); + void set_width_range(const std::vector&); + void add_polynomial_coefficients(const std::vector&); + + // operator + bool operator==(const itfiPBTV&) const; + + // function + void clear(); + + private: + // members + std::vector _density_polynomial_orders; + std::vector _width_polynomial_orders; + std::vector _width_ranges; + std::vector> _polynomial_coefficients_list; +}; + +class itfiThicknessVsDensity { + public: + // constructor + itfiThicknessVsDensity(); + itfiThicknessVsDensity(const itfiThicknessVsDensity&); + ~itfiThicknessVsDensity(); + + // getter + const char* get_type() const; + itf1DLUT& get_dr_lut(); + const itf1DLUT& get_dr_lut() const; + const std::vector>& get_dr_list() const; + + // setter + void set_type(const char*); + void add_dr(float, float); + void set_dr_list(const std::vector>&); + + // operator + itfiThicknessVsDensity& operator=(const itfiThicknessVsDensity&); + bool operator==(const itfiThicknessVsDensity&) const; + + // function + void clear(); + + private: + // members + char* _type; + itf1DLUT _dr_lut; // (density, relative change in thickness) +}; + +// Specifies CRT-based temperature derating for different conductor widths +class itfiWidthCrt { + public: + double si_width = 0; + double crt_1 = 0; + double crt_2 = 0; + + bool operator==(const itfiWidthCrt& rhs) const { + return si_width == rhs.si_width && crt_1 == rhs.crt_1 && crt_2 == rhs.crt_2; + } +}; + +class itfiDensityBox { + public: + int box_size; // unit microns + double weight; // none + + bool operator==(const itfiDensityBox rhs) const { + return box_size == rhs.box_size && weight == rhs.weight; + } +}; + +// (widths, spacings) -> values +class itfiEtchVWS : public itfTitleLut{ + public: + // constructor + itfiEtchVWS() = default; + itfiEtchVWS(const char* title, const itf2DLUT lut) + : itfTitleLut(title, lut) + { } + + // Units: microns + std::vector get_widths() const { return get_rows();} + // Units: microns + std::vector get_spacings() const { return get_cols();} +}; + +// RHO_VS_SI_WIDTH_AND_THICKNESS +// (thickness, silicon width) -> rho value +class itfiRhoVWS : public itf2DLUT { + public: + itfiRhoVWS() = default; + // Units: microns. + std::vector get_thickness() const { return get_rows(); } + // Units: microns. + std::vector get_width() const { return get_cols(); } + // operator + itfiRhoVWS& operator=(const itf2DLUT& rhs) { + itf2DLUT::operator=(rhs); + return *this; + } + bool operator==(const itfiRhoVWS& rhs) const { + return itf2DLUT::operator==(rhs); + } +}; + +class itfiConductor { + public: + // constructor + itfiConductor(); + itfiConductor(const itfiConductor&); + ~itfiConductor(); + + // getter + const char* get_conductor_name() const; + float get_wmin() const; + float get_smin() const; + float get_thickness () const; + std::vector& get_air_gap_spacings(); + const std::vector& get_air_gap_spacings() const; + std::vector& get_air_gap_widths(); + const std::vector& get_air_gap_widths() const; + std::vector& get_air_gap_thicknesses(); + const std::vector& get_air_gap_thicknesses() const; + std::vector& get_air_gap_bottom_heights(); + const std::vector& get_air_gap_bottom_heights() const; + float get_bottom_dielectric_thickness() const; + float get_bottom_dielectric_er() const; + itfiBTSW& get_bottom_thickness_vs_si_width(); + const itfiBTSW& get_bottom_thickness_vs_si_width() const; + float get_t0() const; + std::optional get_crt1() const; + std::optional get_crt2() const; + const std::vector& get_crt_vs_si_width() const; + const std::vector& get_density_box_weighting_factor() const; + float get_drop_factor() const; + float get_etch() const; + float get_capacitive_only_etch() const; + float get_resistive_only_etch() const; + std::vector& get_etch_vws_list(); + const std::vector& get_etch_vws_list() const; + float get_fill_ratio() const; + float get_fill_width() const; + float get_fill_spacing() const; + const char* get_fill_type() const; + float get_gate_to_contact_smin() const; + itfiG2DC& get_gate_to_diffusion_cap(); + const itfiG2DC& get_gate_to_diffusion_cap() const; + itfTitleLut& get_ild_vws(); + const itfTitleLut& get_ild_vws() const; + char* get_layer_type() const; + char* get_measured_from() const; + itfiPBTV& get_PBTV(); + const itfiPBTV& get_PBTV() const; + float get_rpsq() const; + float get_rho() const; + itf1DLUT& get_rpsq_vs_si_width(); + const itf1DLUT& get_rpsq_vs_si_width() const; + itf2DLUT& get_rpsq_vws(); + const itf2DLUT& get_rpsq_vws() const; + itfiRhoVWS& get_rho_v_siw_t(); + const itfiRhoVWS& get_rho_v_siw_t() const; + itf2DLUT& get_rho_vws(); + const itf2DLUT& get_rho_vws() const; + float get_side_tangent() const; + itfiThicknessVsDensity& get_thickness_vs_density(); + const itfiThicknessVsDensity& get_thickness_vs_density() const; + itfTitleLut& get_thickness_vws(); + const itfTitleLut& get_thickness_vws() const; + itf2DLUT& get_tvf_bt_vws(); + const itf2DLUT& get_tvf_bt_vws() const; + itf2DLUT& get_tvf_bt_vwd(); + const itf2DLUT& get_tvf_bt_vwd() const; + + // setter + void set_conductor_name(const char*); + void set_wmin(float); + void set_smin(float); + void set_thickness(float); + void set_air_gap_spacings(const std::vector&); + void set_air_gap_widths(const std::vector&); + void set_air_gap_thicknesses(const std::vector&); + void set_air_gap_bottom_heights(const std::vector&); + void set_bottom_dielectric_thickness(float); + void set_bottom_dielectric_er(float); + void set_t0(float); + void set_crt1(float crt1); + void set_crt2(float crt2); + void add_siw_crt1_crt2(float, float, float); + void add_density_box_weight(int, double); + void set_drop_factor(float); + void set_etch(float); + void set_capacitive_only_etch(float); + void set_resistive_only_etch(float); + void add_etch_vws(const char*, const itf2DLUT&); + void set_fill_ratio(float); + void set_fill_width(float); + void set_fill_spacing(float); + void set_fill_type(const char*); + void set_gate_to_contact_smin(float); + void set_layer_type(const char*); + void set_side_tangent(float); + void set_is_planar(); + void set_ild_vws_lut(const itf2DLUT&); + void set_ild_vws_title(const char*); + void set_thickness_vws_lut(const itf2DLUT&); + void set_thickness_vws_title(const char*); + void set_rpsq(float); + void set_rho(float); + void set_rpsq_vs_si_width(const std::vector>&); + void set_rpsq_vws(const itf2DLUT&); + void set_rho_v_siw_t(const itf2DLUT&); + void set_rho_vws(const itf2DLUT&); + void set_tvf_bt_vws(const itf2DLUT&); + void set_tvf_bt_vwd(const itf2DLUT&); + void set_measured_from(const char*); + + // operator + itfiConductor& operator=(const itfiConductor&); + bool operator==(const itfiConductor&) const; + + // function + void clear(); + void query_crt(double, double&, double&); + + private: + // members + char* _conductor_name; + float _wmin; // The minimum width of the layer. Units: microns + float _smin; // Minimum spacing value. Units: microns + float _thickness; // Units: microns + std::vector _air_gap_spacings; // Units: microns + std::vector _air_gap_widths; // Units: microns + std::vector _air_gap_thicknesses; // Units: microns + std::vector _air_gap_bottom_heights; // Units: microns + float _bottom_dielectric_thickness; // Units: microns + float _bottom_dielectric_er; // Relative permittivity of the dielectric + itfiBTSW _bottom_thickness_vs_si_width; + float _t0; // Nominal temperature for the layer. Units: degrees Celsius + std::optional _crt1; + std::optional _crt2; + std::vector _crt_vs_si_width; + std::vector _density_box_weighting_factor; + float _drop_factor; + float _etch; + float _capacitive_only_etch; + float _resistive_only_etch; + std::vector _etch_vws_list; // (widths, spacings) -> values + float _fill_ratio; + float _fill_width; + float _fill_spacing; + char* _fill_type; + float _gate_to_contact_smin; + itfiG2DC _gate_to_diffusion_cap; + itfTitleLut _ild_vws; // (widths, spacings) -> thickness_changes + char* _layer_type; + char* _measured_from; + itfiPBTV _PBTV; // polynomial based thickness variation + float _rpsq{0.}; + float _rho{0.}; + itf1DLUT _rpsq_v_siw; + itf2DLUT _rpsq_vws; // (widths , spacings) -> RPSQ value + itfiRhoVWS _rho_v_siw_t; // (thickness, silicon width) -> rho value + itf2DLUT _rho_vws; // (widths, spacings) -> values + float _side_tangent; + itfiThicknessVsDensity _thickness_vs_density; + itfTitleLut _thickness_vws; // (widths, spacings) -> values + itf2DLUT _tvf_bt_vws; // bottom_thickness_vs_width_and_spacing. (widths, spacings) -> values + itf2DLUT _tvf_bt_vwd; // bottom_thickness_vs_width_and_deltapd. (widths, deltapd) -> values + + unsigned _has_air_gap_vs_spacing : 1; + unsigned _has_bottom_dielectric_thickness : 1; + unsigned _has_bottom_dielectric_er : 1; + unsigned _has_bottom_thickness_vs_si_width : 1; + unsigned _has_t0 : 1; + unsigned _has_crt_stmt : 1; + unsigned _has_density_box_weighting_factor : 1; + unsigned _has_drop_factor : 1; + unsigned _has_etch_stmt : 1; + unsigned _has_evwas : 1; + unsigned _has_fill_stmt : 1; + unsigned _has_gate_to_contact_smin : 1; + unsigned _has_gate_to_diffusion_cap : 1; + unsigned _has_ild_vs_width_and_spacing : 1; + unsigned _is_planar : 1; + unsigned _has_layer_type : 1; + unsigned _has_measured_from : 1; + unsigned _has_polynomial_based_thickness_variation : 1; + unsigned _has_side_tangent : 1; + unsigned _has_thickness_vs_density : 1; + unsigned _has_thickness_vs_width_and_spacing : 1; + unsigned _has_tvf_adjustment_tables : 1; +}; + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf/itfiDielectric.cpp b/src/operation/iRCX/source/parser/itf/itfiDielectric.cpp new file mode 100644 index 000000000..316f641f8 --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itfiDielectric.cpp @@ -0,0 +1,298 @@ +// *************************************************************************************** +// 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 +#include + +#include "itfiDielectric.hpp" +#include "itfMarco.h" +#include "itfUtil.h" +namespace itf +{ + +itfiDielectric::itfiDielectric() +: _dielectric_name(nullptr), + _er(0), + _thickness(0), + _measured_from(nullptr), + _sw_t(0), + _tw_t(0), + _associated_conductor(nullptr), + _damage_thickness(0), + _damage_er(0), + _has_measured_from(0), + _has_sw_t(0), + _has_tw_t(0), + _has_associated_conductor(0), + _is_conformal(0), + _has_damage_thickness(0), + _has_damage_er(0) +{ + +} + +itfiDielectric::itfiDielectric(const itfiDielectric& other) +: _dielectric_name(nullptr), + _er(0), + _thickness(0), + _measured_from(nullptr), + _sw_t(0), + _tw_t(0), + _associated_conductor(nullptr), + _damage_thickness(0), + _damage_er(0), + _has_measured_from(0), + _has_sw_t(0), + _has_tw_t(0), + _has_associated_conductor(0), + _is_conformal(0), + _has_damage_thickness(0), + _has_damage_er(0) +{ + *this = other; +} + +itfiDielectric& itfiDielectric::operator=(const itfiDielectric& rhs) +{ + if (this == &rhs) return *this; + + ITF_STR_CPY(_dielectric_name, rhs._dielectric_name); + _er = rhs._er; + _thickness = rhs._thickness; + ITF_STR_CPY(_measured_from, rhs._measured_from); + _sw_t = rhs._sw_t; + _tw_t = rhs._tw_t; + ITF_STR_CPY(_associated_conductor, rhs._associated_conductor); + _damage_thickness = rhs._damage_thickness; + _damage_er = rhs._damage_er; + _has_measured_from = rhs._has_measured_from; + _has_sw_t = rhs._has_sw_t; + _has_tw_t = rhs._has_tw_t; + _has_associated_conductor = rhs._has_associated_conductor; + _is_conformal = rhs._is_conformal; + _has_damage_thickness = rhs._has_damage_thickness; + _has_damage_er = rhs._has_damage_er; + + return *this; +} + +bool +itfiDielectric::operator==(const itfiDielectric& rhs) const +{ + if (this == &rhs) return true; + + return itfStrCmp(_dielectric_name, rhs._dielectric_name) + && _er == rhs._er + && _thickness == rhs._thickness + && itfStrCmp(_measured_from, rhs._measured_from) + && _sw_t == rhs._sw_t + && _tw_t == rhs._tw_t + && itfStrCmp(_associated_conductor, rhs._associated_conductor) + && _damage_thickness == rhs._damage_thickness + && _damage_er == rhs._damage_er + + && _has_measured_from == rhs._has_measured_from + && _has_sw_t == rhs._has_sw_t + && _has_tw_t == rhs._has_tw_t + && _has_associated_conductor == rhs._has_associated_conductor + && _is_conformal == rhs._is_conformal + && _has_damage_thickness == rhs._has_damage_thickness + && _has_damage_er == rhs._has_damage_er + ; +} + +itfiDielectric::~itfiDielectric() { + clear(); +} + +const char* +itfiDielectric::get_dielectric_name() const +{ + return _dielectric_name; +} + +float +itfiDielectric::get_er() const +{ + return _er; +} + +float +itfiDielectric::get_thickness() const +{ + return _thickness; +} + +const char* +itfiDielectric::get_measured_from() const { + return _measured_from; +} + +float +itfiDielectric::get_sw_t() const { + return _sw_t; +} + +float +itfiDielectric::get_tw_t() const { + return _tw_t; +} + +const char* +itfiDielectric::get_associated_conductor() const { + return _associated_conductor; +} + +float +itfiDielectric::get_damage_thickness() const { + return _damage_thickness; +} + +float +itfiDielectric::get_damage_er() const { + return _damage_er; +} + +void +itfiDielectric::set_dielectric_name(const char* layer_name) +{ + ITF_STR_CPY(_dielectric_name, layer_name); +} + +void +itfiDielectric::set_er(float er) +{ + _er = er; +} + +void +itfiDielectric::set_thickness(float thickness) +{ + _thickness = thickness; +} + +void +itfiDielectric::set_measured_from(const char* from) +{ + ITF_STR_CPY(_measured_from, from); + _has_measured_from = 1; +} + +void +itfiDielectric::set_sw_t(float sw_t) +{ + _sw_t = sw_t; + _has_sw_t = 1; +} + +void +itfiDielectric::set_tw_t(float tw_t) +{ + _tw_t = tw_t; + _has_tw_t = 1; +} + +void +itfiDielectric::set_associated_conductor(const char* conductor) +{ + ITF_STR_CPY(_associated_conductor, conductor); + _has_associated_conductor = 1; +} + +void +itfiDielectric::set_is_conformal() { + _is_conformal = 1; +} + +void +itfiDielectric::set_damage_thickness(float thickness) +{ + _damage_thickness = thickness; + _has_damage_thickness = 1; +} + +void +itfiDielectric::set_damage_er(float er) +{ + _damage_er = er; + _has_damage_er = 1; +} + +int +itfiDielectric::has_measured_from() const +{ + return _has_measured_from; +} + +int +itfiDielectric::has_sw_t() const +{ + return _has_sw_t; +} + +int +itfiDielectric::has_tw_t() const +{ + return _has_tw_t; +} + +int +itfiDielectric::has_associated_conductor() const +{ + return _has_associated_conductor; +} + +int +itfiDielectric::get_is_conformal() const +{ + return _is_conformal; +} + +int +itfiDielectric::has_damage_thickness() const +{ + return _has_damage_thickness; +} + +int +itfiDielectric::has_damage_er() const +{ + return _has_damage_er; +} + +void +itfiDielectric::clear() +{ + ITF_FREE(_dielectric_name); + _er = 0; + _thickness = 0; + ITF_FREE(_measured_from); + _sw_t = 0; + _tw_t = 0; + ITF_FREE(_associated_conductor); + _damage_thickness = 0; + _damage_er = 0; + + _has_measured_from = 0; + _has_sw_t = 0; + _has_tw_t = 0; + _has_associated_conductor = 0; + _is_conformal = 0; + _has_damage_thickness = 0; + _has_damage_er = 0; +} + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf/itfiDielectric.hpp b/src/operation/iRCX/source/parser/itf/itfiDielectric.hpp new file mode 100644 index 000000000..b4fa348ca --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itfiDielectric.hpp @@ -0,0 +1,87 @@ +// *************************************************************************************** +// 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 + +namespace itf +{ + +class itfiDielectric { + public: + // constructor + itfiDielectric(); + itfiDielectric(const itfiDielectric&); + ~itfiDielectric(); + + // getter + const char* get_dielectric_name() const; + float get_er() const; + float get_thickness() const; + const char* get_measured_from() const; + float get_sw_t() const; + float get_tw_t() const; + const char* get_associated_conductor() const; + float get_damage_thickness() const; + float get_damage_er() const; + int has_measured_from() const; + int has_sw_t() const; + int has_tw_t() const; + int has_associated_conductor() const; + int get_is_conformal() const; + int has_damage_thickness() const; + int has_damage_er() const; + + // setter + void set_dielectric_name(const char*); + void set_er(float); + void set_thickness(float); + void set_measured_from(const char*); + void set_sw_t(float); + void set_tw_t(float); + void set_associated_conductor(const char*); + void set_damage_thickness(float); + void set_damage_er(float); + void set_is_conformal(); + + // operator + itfiDielectric& operator=(const itfiDielectric&); + bool operator==(const itfiDielectric&) const; + + // function + void clear(); + + private: + // members + char* _dielectric_name; + float _er; // Relative permittivity + float _thickness; // Units: microns + char* _measured_from; + float _sw_t; // The sidewall thickness. Units: microns + float _tw_t; // Topwall thickness. Units: microns + char* _associated_conductor; + float _damage_thickness; // Units: microns + float _damage_er; // Equivalent permittivity + + unsigned _has_measured_from : 1; + unsigned _has_sw_t : 1; + unsigned _has_tw_t : 1; + unsigned _has_associated_conductor : 1; + unsigned _is_conformal : 1; + unsigned _has_damage_thickness : 1; + unsigned _has_damage_er : 1; +}; + +} // namespace itf \ No newline at end of file diff --git a/src/operation/iRCX/source/parser/itf/itfiVariationParam.hpp b/src/operation/iRCX/source/parser/itf/itfiVariationParam.hpp new file mode 100644 index 000000000..01897ca90 --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itfiVariationParam.hpp @@ -0,0 +1,110 @@ +// *************************************************************************************** +// 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 +#include +namespace itf +{ + +// variation parameter term +class itfiVPT { + public: + // operator + bool operator==(const itfiVPT&) const; + + // function + void clear(); + + // members + std::string layer; + std::string type; + float coeff; +}; + +class itfiVariationParam{ + public: + // operator + bool operator==(const itfiVariationParam&) const; + + // function + void clear(); + + void add_term(const itfiVPT&); + void add_term(const char*, const char*, float); + + // members + std::string param_name; + std::vector term_list; +}; + +///////////// inline //////////////// + +inline bool +itfiVPT::operator==(const itfiVPT& rhs) const +{ + if (this == &rhs) return true; + + return layer == rhs.layer + && type == rhs.type + && coeff == rhs.coeff + ; +} + +inline void +itfiVPT::clear() +{ + layer.clear(); + type.clear(); + coeff = 0; +} + +inline bool +itfiVariationParam::operator==(const itfiVariationParam& rhs) const +{ + if (this == &rhs) return true; + + return param_name == rhs.param_name + && term_list == rhs.term_list + ; +} + +inline void +itfiVariationParam::clear() +{ + param_name.clear(); + term_list.clear(); +} + +inline void +itfiVariationParam::add_term(const itfiVPT& t) +{ + term_list.push_back(t); +} + +inline void +itfiVariationParam::add_term( + const char* layer, const char* type, float coeff +) { + itfiVPT t; + t.layer = layer ? layer : ""; + t.type = type ? type : ""; + t.coeff = coeff; + add_term(t); +} + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf/itfiVia.cpp b/src/operation/iRCX/source/parser/itf/itfiVia.cpp new file mode 100644 index 000000000..9382c4c65 --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itfiVia.cpp @@ -0,0 +1,325 @@ +// *************************************************************************************** +// 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 "itfiVia.hpp" +#include "itfMarco.h" +#include "itfUtil.h" +namespace itf +{ + +itfiEVCAGS::itfiEVCAGS() +: _number_of_tables(0), + _tables() +{ + +} + +itfiEVCAGS::~itfiEVCAGS() +{ + clear(); +} + +int +itfiEVCAGS::get_number_of_tables() const +{ + return _number_of_tables; +} + +void +itfiEVCAGS::set_number_of_tables(int n) +{ + _number_of_tables = n; +} + +void +itfiEVCAGS::add_table( + const char* title, + const itf2DLUT& lut +) { + _tables.push_back(itfTitleLut(title, lut)); +} + +bool +itfiEVCAGS::operator==(const itfiEVCAGS& rhs) const +{ + if (this == &rhs) return true; + + return _number_of_tables == rhs._number_of_tables + && _tables == _tables + ; +} + +void +itfiEVCAGS::clear() +{ + _tables.clear(); + _number_of_tables = 0; +} + +itfiVia::itfiVia() +: _via_name(nullptr), + _from(nullptr), + _to(nullptr), + _crt1(std::nullopt), + _crt2(std::nullopt), + _crt_vs_area(), + _t0(0), + _rho(std::nullopt), + _rpv(std::nullopt), + _area(std::nullopt), + _rpv_vs_area(), + _etch_cg(), + _etch_vws(), + _etch_vwl(), + _capacitive_only_etch(0) +{ } + +itfiVia::itfiVia(const itfiVia& other) +: _via_name(nullptr) +, _from(nullptr) +, _to(nullptr) +{ + *this = other; +} + +itfiVia& itfiVia::operator=(const itfiVia& rhs) +{ + if (this == &rhs) return *this; + + ITF_STR_CPY(_via_name, rhs._via_name); + ITF_STR_CPY(_from, rhs._from); + ITF_STR_CPY(_to, rhs._to); + _crt1 = rhs._crt1; + _crt2 = rhs._crt2; + _crt_vs_area = rhs._crt_vs_area; + _t0 = rhs._t0; + _rho = rhs._rho; + _rpv = rhs._rpv; + _area = rhs._area; + _rpv_vs_area = rhs._rpv_vs_area; + _etch_cg = rhs._etch_cg; + _etch_vws = rhs._etch_vws; + _etch_vwl = rhs._etch_vwl; + _capacitive_only_etch = rhs._capacitive_only_etch; + + return *this; +} + +bool +itfiVia::operator==(const itfiVia& rhs) const +{ + if (this == &rhs) return true; + + return itfStrCmp( _via_name, rhs._via_name) + && itfStrCmp(_from, rhs._from) + && itfStrCmp(_to, rhs._to) + && _crt1 == rhs._crt1 + && _crt2 == rhs._crt2 + && _crt_vs_area == rhs._crt_vs_area + && _t0 == rhs._t0 + && _rho == rhs._rho + && _rpv == rhs._rpv + && _area == rhs._area + && _rpv_vs_area == rhs._rpv_vs_area + && _etch_cg == rhs._etch_cg + && _etch_vws == rhs._etch_vws + && _etch_vwl == rhs._etch_vwl + && _capacitive_only_etch == rhs._capacitive_only_etch + ; +} + +itfiVia::~itfiVia() { + clear(); +} + +void +itfiVia::clear() { + ITF_FREE(_via_name); + ITF_FREE(_from); + ITF_FREE(_to); + _crt1.reset(); + _crt2.reset(); + _crt_vs_area.clear(); + _t0 = 0; + _rho.reset(); + _rpv.reset(); + _area.reset(); + _rpv_vs_area.clear(); + _etch_cg.clear(); + _etch_vws.clear(); + _etch_vwl.clear(); + _capacitive_only_etch = 0; +} + +itfiEVCAGS& +itfiVia::get_etch_cg() +{ + return _etch_cg; +} + +const itfiEVCAGS& +itfiVia::get_etch_cg() const +{ + return _etch_cg; +} + +const char* +itfiVia::get_via_name() const +{ + return _via_name; +} + +std::optional +itfiVia::get_rho() const +{ + return _rho; +} + +std::optional +itfiVia::get_rpv() const +{ + return _rpv; +} + +const std::vector& +itfiVia::get_rpv_vs_area() const +{ + return _rpv_vs_area; +} + +std::optional +itfiVia::get_area() const +{ + return _area; +} + +std::optional +itfiVia::get_crt1() const +{ + return _crt1; +} + +std::optional +itfiVia::get_crt2() const +{ + return _crt2; +} + +const std::vector& +itfiVia::get_crt_vs_area() const +{ + return _crt_vs_area; +} + +const char* +itfiVia::get_from() const +{ + return _from; +} + +const char* +itfiVia::get_to() const +{ + return _to; +} + +void +itfiVia::set_via_name(const char* name) +{ + ITF_STR_CPY(_via_name, name); +} + +void +itfiVia::set_from(const char* from) +{ + ITF_STR_CPY(_from, from); +} + +void +itfiVia::set_to(const char* to) +{ + ITF_STR_CPY(_to, to); +} + +void +itfiVia::set_crt1(float v) +{ + _crt1 = v; +} + +void +itfiVia::set_crt2(float v) +{ + _crt2 = v; +} + +void +itfiVia::add_area_crt1_ct2(float area, float crt1, float crt2) +{ + _crt_vs_area.emplace_back(area, crt1, crt2); +} + +void +itfiVia::set_t0(float t) +{ + _t0 = t; +} + +void +itfiVia::set_rho(float r) +{ + _rho = r; +} + +void +itfiVia::set_rpv(float v) +{ + _rpv = v; +} + +void +itfiVia::set_area(float v) +{ + _area = v; +} + +void +itfiVia::add_area_rpv(float area, float rpv) +{ + _rpv_vs_area.emplace_back(area, rpv); +} + +void +itfiVia::set_etch_vws(const char* title, const itf2DLUT& lut) +{ + _etch_vws.set_title(title); + _etch_vws.set_lut(lut); +} + +void +itfiVia::set_etch_vwl( + const itf2DLUT>& lut) +{ + _etch_vwl = lut; +} + +void +itfiVia::set_capacitive_only_etch(float v) +{ + _capacitive_only_etch = v; +} + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf/itfiVia.hpp b/src/operation/iRCX/source/parser/itf/itfiVia.hpp new file mode 100644 index 000000000..f00b7b8fa --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itfiVia.hpp @@ -0,0 +1,134 @@ +// *************************************************************************************** +// 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 +#include + +#include "itf2DLUT.hpp" +namespace itf +{ + +// Etch Vs Contact And Gate Spacings +class itfiEVCAGS { + public: + // constructor + itfiEVCAGS(); + ~itfiEVCAGS(); + + // getter + int get_number_of_tables() const; + + // setter + void set_number_of_tables(int); + void add_table(const char*, const itf2DLUT&); + + // operator + bool operator==(const itfiEVCAGS&) const; + + // function + void clear(); + + private: + // members + int _number_of_tables; + std::vector> _tables; // (gate_to_contact, contact_to_contact) -> values +}; + +struct itfiAreaRpv { + double area; + double rpv; + + bool operator==(const itfiAreaRpv& rhs) const { + return (area == rhs.area) && (rpv == rhs.rpv); + } +}; + +struct itfiAreaCrt { + double area; + double crt1; + double crt2; + + bool operator==(const itfiAreaCrt& rhs) const { + return (area == rhs.area) && (crt1 == rhs.crt1) && (crt2 == rhs.crt2); + } +}; + +class itfiVia { + public: + // constructor + itfiVia(); + itfiVia(const itfiVia&); + ~itfiVia(); + + // getter + itfiEVCAGS& get_etch_cg(); + const itfiEVCAGS& get_etch_cg() const; + const char* get_via_name() const; + std::optional get_rho() const; + std::optional get_rpv() const; + const std::vector& get_rpv_vs_area() const; + std::optional get_area() const; + std::optional get_crt1() const; + std::optional get_crt2() const; + const std::vector& get_crt_vs_area() const; + const char* get_from() const; + const char* get_to() const; + + // setter + void set_via_name(const char*); + void set_from(const char*); + void set_to(const char*); + void set_crt1(float); + void set_crt2(float); + void add_area_crt1_ct2(float, float, float); + void set_t0(float); + void set_rho(float); + void set_rpv(float); + void set_area(float); + void add_area_rpv(float, float); + void set_etch_vws(const char*, const itf2DLUT&); + void set_etch_vwl(const itf2DLUT>&); + void set_capacitive_only_etch(float); + + // operator + itfiVia& operator=(const itfiVia&); + bool operator==(const itfiVia&) const; + + // function + void clear(); + + private: + // members + char* _via_name; + char* _from; + char* _to; + std::optional _crt1; + std::optional _crt2; + std::vector _crt_vs_area; + float _t0; + std::optional _rho; // resistivity. Units: ohms-micron + std::optional _rpv; + std::optional _area; // Area of default via. Units: square microns + std::vector _rpv_vs_area; // (RPV, area). Units: (ohms, square microns) + itfiEVCAGS _etch_cg; //etch_vs_contact_and_gate_spacings + itfTitleLut _etch_vws; // etch_vs_width_and_spacing; + itf2DLUT> _etch_vwl; // etch_vs_width_and_length; (widths, lengths) -> values) + float _capacitive_only_etch; +}; + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf/itfrCallBacks.cpp b/src/operation/iRCX/source/parser/itf/itfrCallBacks.cpp new file mode 100644 index 000000000..e66fc958d --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itfrCallBacks.cpp @@ -0,0 +1,54 @@ +// *************************************************************************************** +// 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 "itfrCallBacks.hpp" +namespace itf +{ + +itfrCallBacks* itfCallbacks = nullptr; + +itfrCallBacks::itfrCallBacks() +: technology_cb(nullptr) +, process_foundry_cb(nullptr) +, process_node_cb(0) +, process_type_cb(nullptr) +, process_version_cb(0) +, process_corner_cb(nullptr) +, reference_direction_cb(nullptr) +, global_temperature_cb(nullptr) +, background_er_cb(nullptr) +, half_node_scale_factor_cb(nullptr) +, use_si_density_cb(nullptr) +, drop_factor_lateral_spacing_cb(nullptr) +, conductor_cb(nullptr) +, dielectric_cb(nullptr) +, via_cb(nullptr) +, variation_cb(nullptr) +{ + +} + +void +itfrCallBacks::reset() +{ + if (itfCallbacks) { + delete itfCallbacks; + } + + itfCallbacks = new itfrCallBacks(); +} + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf/itfrCallBacks.hpp b/src/operation/iRCX/source/parser/itf/itfrCallBacks.hpp new file mode 100644 index 000000000..e03738250 --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itfrCallBacks.hpp @@ -0,0 +1,52 @@ +// *************************************************************************************** +// 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 "itfrReader.hpp" +namespace itf +{ + +class itfrCallBacks { + public: + // constructor + itfrCallBacks(); + + // function + static void reset(); + + // members + itfrStringCbFnType technology_cb; + itfrStringCbFnType process_foundry_cb; + itfrIntegerCbFnType process_node_cb; + itfrStringCbFnType process_type_cb; + itfrDoubleCbFnType process_version_cb; + itfrStringCbFnType process_corner_cb; + itfrStringCbFnType reference_direction_cb; + itfrDoubleCbFnType global_temperature_cb; + itfrDoubleCbFnType background_er_cb; + itfrDoubleCbFnType half_node_scale_factor_cb; + itfrIntegerCbFnType use_si_density_cb; + itfrDoubleCbFnType drop_factor_lateral_spacing_cb; + itfrConductorCbFnType conductor_cb; + itfrDielectricCbFnType dielectric_cb; + itfrViaCbFnType via_cb; + itfrVariationParamCbFnType variation_cb; +}; + +extern itfrCallBacks* itfCallbacks; + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf/itfrData.cpp b/src/operation/iRCX/source/parser/itf/itfrData.cpp new file mode 100644 index 000000000..65e38ad8a --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itfrData.cpp @@ -0,0 +1,78 @@ +// *************************************************************************************** +// 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 "itfrData.hpp" +#include "itfMarco.h" +namespace itf +{ + +itfrData* itfData = nullptr; + +itfrData::itfrData() +: itf_file(nullptr), + log_file(nullptr), + + process_name(nullptr), + process_foundry(nullptr), + process_node(0), + process_type(nullptr), + process_version(0), + process_corner(nullptr), + reference_direction(nullptr), + global_temperature(25.f), + background_er(1.f), + half_node_scale_factor(1.f), + drop_factor_lateral_spacing(.5f), + + dielectric(), + conductor(), + via(), + variation_param(), + + use_si_density(0), + has_open_log_file(0), + has_global_temperature(0), + has_background_er(0), + has_half_node_scale_factor(0), + has_use_si_density(0), + has_drop_factor_lateral_spacing(0), + has_variation_params(0) +{ + +} + +itfrData::~itfrData() +{ + log_file = nullptr; // not release here + + ITF_FREE(itf_file); + ITF_FREE(process_name); + ITF_FREE(process_foundry); +} + +void itfrData::reset() { + if (itfData) { + delete itfData; + } + + itfData = new itfrData(); +} + +void itfrData::initRead() { + +} + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf/itfrData.hpp b/src/operation/iRCX/source/parser/itf/itfrData.hpp new file mode 100644 index 000000000..e16eb3ade --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itfrData.hpp @@ -0,0 +1,74 @@ +// *************************************************************************************** +// 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 +#include + +#include "itfiConductor.hpp" +#include "itfiDielectric.hpp" +#include "itfiVariationParam.hpp" +#include "itfiVia.hpp" +namespace itf +{ + +// refers to StarRC User Guide. Version F-2011.06, June 2011 # 2022-12-06 # +// refers to StarRC User Guide. Version U-2022.12, December 2022 # 2024-01-18 # add some feature in 14 nm +class itfrData { + public: + // constructor + itfrData(); + ~itfrData(); + + // function + static void reset(); + void initRead(); + + // members + char* itf_file; + FILE* log_file; + + char* process_name; + char* process_foundry; + int process_node; + char* process_type; + float process_version; + char* process_corner; + char* reference_direction; + float global_temperature; + float background_er; // Relative permittivity + float half_node_scale_factor; + float drop_factor_lateral_spacing; // Units: microns + + itfiDielectric dielectric; + itfiConductor conductor; + itfiVia via; + itfiVariationParam variation_param; + + unsigned use_si_density : 1; + unsigned has_open_log_file : 1; + unsigned has_global_temperature : 1; + unsigned has_background_er : 1; + unsigned has_half_node_scale_factor : 1; + unsigned has_use_si_density : 1; + unsigned has_drop_factor_lateral_spacing : 1; + unsigned has_variation_params : 1; +}; + +extern itfrData* itfData; + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf/itfrReader.cpp b/src/operation/iRCX/source/parser/itf/itfrReader.cpp new file mode 100644 index 000000000..3779de9fb --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itfrReader.cpp @@ -0,0 +1,217 @@ +// *************************************************************************************** +// 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 "itfrReader.hpp" +#include "itfrCallBacks.hpp" +#include "itfrData.hpp" +#include "itfrSettings.hpp" +#include "itf_lex.hpp" +#include "itf_parser.hpp" +namespace itf +{ + +extern int itf_parse (void); + +#define ITF_INIT itf_init(__FUNCTION__) + +static const char* init_call_func = nullptr; + +void itf_init(const char* func) +{ + if (itfSettings == nullptr) { + itfrSettings::reset(); + init_call_func = func; + } + + if (itfCallbacks == nullptr) { + itfrCallBacks::reset(); + init_call_func = func; + } +} + +int +itfrInit() +{ + return itfrInitSession(0); +} + +int +itfrInitSession (int startSession) +{ + if (startSession) { + if (init_call_func) { + fprintf(stderr, "ERROR: Attempt to call configuration function '%s' in ITF parser before lefrInit() call in session-based mode.\n", init_call_func); + return 1; + } + + itfrSettings::reset(); + itfrCallBacks::reset(); + + } else { + if (itfSettings == nullptr) { + itfrSettings::reset(); + } + + if (itfCallbacks == nullptr) { + itfrCallBacks::reset(); + } + } + + return 0; +} + +int +itfrClear() +{ + if (itfData) delete itfData; + itfData = nullptr; + + if (itfCallbacks) delete itfCallbacks; + itfCallbacks = nullptr; + + if (itfSettings) delete itfSettings; + itfSettings = nullptr; + + return 0; +} + +// @param file readable itf file +// @param file_name itf file name +// @param user_data For extension +// @return 0 succeed +int +itfrRead(FILE* file, const char* file_name, itfiUserData user_data) +{ + if (file == nullptr) return 1; + + itfrData::reset(); + + ITF_STR_CPY(itfData->itf_file, file_name); + if (itfSettings) itfSettings->user_data = user_data; + itf_restart(file); + auto status = itf_parse(); + itf_lex_destroy(); + return status; +} + +const char* +itfrFname() +{ + if (itfData) return itfData->itf_file; + else return ""; +} + +itfiUserData +itfrUserData() +{ + if (itfSettings) return itfSettings->user_data; + else return nullptr; +} + +// This function is effective only when enabling itf/CMakeLists.txt +// +// bison_target(itfParser +// ... +// COMPILE_FLAGS "-t" ... # +// ... +// ) +void +itfSetDebug(int flag) +{ + #ifdef ITF_DEBUG + #if ITF_DEBUG + extern int itf_debug; + itf_debug = flag ? 1 : 0; + #else + if (flag) std::cout << "itfSetDebug() does not work" << std::endl; + #endif + #endif + + ++flag; // for warning +} + +void +itfrSetTechnologyCb(itfrStringCbFnType f) +{ + ITF_INIT; + itfCallbacks->technology_cb = f; +} + +void +itfrSetGlobalTemperatureCb(itfrDoubleCbFnType f) +{ + ITF_INIT; + itfCallbacks->global_temperature_cb = f; +} + +void +itfrSetBackgroundErCb(itfrDoubleCbFnType f) +{ + ITF_INIT; + itfCallbacks->background_er_cb = f; +} + +void +itfrSetHalfNodeScaleFactorCb(itfrDoubleCbFnType f) +{ + ITF_INIT; + itfCallbacks->half_node_scale_factor_cb = f; +} + +void +itfrSetUseSiDensityCb(itfrIntegerCbFnType f) +{ + ITF_INIT; + itfCallbacks->use_si_density_cb = f; +} + +void +itfrSetDropFactorLateralSpacingCb(itfrDoubleCbFnType f) +{ + ITF_INIT; + itfCallbacks->drop_factor_lateral_spacing_cb = f; +} + + +void +itfrSetDielectricCb(itfrDielectricCbFnType f) +{ + ITF_INIT; + itfCallbacks->dielectric_cb = f; +} + +void +itfrSetConductorCb(itfrConductorCbFnType f) +{ + ITF_INIT; + itfCallbacks->conductor_cb = f; +} + +void +itfrSetViaCb(itfrViaCbFnType f) +{ + ITF_INIT; + itfCallbacks->via_cb = f; +} + +void +itfrSetVariationParamCb(itfrVariationParamCbFnType f) +{ + ITF_INIT; + itfCallbacks->variation_cb = f; +} + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf/itfrReader.hpp b/src/operation/iRCX/source/parser/itf/itfrReader.hpp new file mode 100644 index 000000000..04aa8b847 --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itfrReader.hpp @@ -0,0 +1,119 @@ +// *************************************************************************************** +// 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 + +#include "itfiConductor.hpp" +#include "itfiDielectric.hpp" +#include "itfiVariationParam.hpp" +#include "itfiVia.hpp" +#include "itfrSettings.hpp" +namespace itf +{ + +// reader initialization. Must be called before itfrRead() +extern int itfrInit(); +extern int itfrInitSession (int startSession = 1); +extern int itfrRead(FILE* file, const char* file_name, itfiUserData user_data); +extern int itfrClear(); + +// getter + +extern const char* itfrFname(); +extern itfiUserData itfrUserData(); + +// setter + +extern void itfSetDebug(int); + +// call back + +enum class itfCallBackType { + kNone = 0, + kTechnologyCbType, + kProcessFoundryCbType, + kProcessNodeCbType, + kProcessTypeCbType, + kProcessVersionCbType, + kProcessCornerCbType, + kReferenceDirectionCbType, + kGlobalTemperatureCbType, + kBackgroundErCbType, + kHalfNodeScaleFactorCbType, + kUseSiDensityCbType, + kDropFactorLateralSpacingCbType, + kDielectricCbType, + kConductorCbType, + kViaCbType, + kVariationCbType, + + itfrEndCbType +}; + +// A declaration of the signature of all callbacks that return nothing +typedef int (*itfrVoidCbFnType) (itfCallBackType, + void* ptr, + itfiUserData); + +// A declaration of the signature of all callbacks that return a string +typedef int (*itfrStringCbFnType) (itfCallBackType, + const char* string, + itfiUserData); + +// A declaration of the signature of all callbacks that return a integer +typedef int (*itfrIntegerCbFnType) (itfCallBackType, + int num, + itfiUserData); + +// A declaration of the signature of all callbacks that return a double +typedef int (*itfrDoubleCbFnType) (itfCallBackType, + double num, + itfiUserData); + +// A declaration of the signature of all callbacks that return a itfiDielectric +typedef int (*itfrDielectricCbFnType) ( itfCallBackType, + itfiDielectric*, + itfiUserData); + +// A declaration of the signature of all callbacks that return a itfiConductor +typedef int (*itfrConductorCbFnType) (itfCallBackType, + itfiConductor*, + itfiUserData); + +// A declaration of the signature of all callbacks that return a itfiVia +typedef int (*itfrViaCbFnType) (itfCallBackType, + itfiVia*, + itfiUserData); + +// A declaration of the signature of all callbacks that return a itfiVariationParam +typedef int (*itfrVariationParamCbFnType) (itfCallBackType, + itfiVariationParam*, + itfiUserData); + +extern void itfrSetTechnologyCb(itfrStringCbFnType); +extern void itfrSetGlobalTemperatureCb(itfrDoubleCbFnType); +extern void itfrSetBackgroundErCb(itfrDoubleCbFnType); +extern void itfrSetHalfNodeScaleFactorCb(itfrDoubleCbFnType); +extern void itfrSetUseSiDensityCb(itfrIntegerCbFnType); +extern void itfrSetDropFactorLateralSpacingCb(itfrDoubleCbFnType); +extern void itfrSetConductorCb(itfrConductorCbFnType); +extern void itfrSetDielectricCb(itfrDielectricCbFnType); +extern void itfrSetViaCb(itfrViaCbFnType); +extern void itfrSetVariationParamCb(itfrVariationParamCbFnType); + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf/itfrSettings.cpp b/src/operation/iRCX/source/parser/itf/itfrSettings.cpp new file mode 100644 index 000000000..97c1ccba2 --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itfrSettings.cpp @@ -0,0 +1,42 @@ +// *************************************************************************************** +// 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 "itfrSettings.hpp" +namespace itf +{ + +itfrSettings* itfSettings = nullptr; + +itfrSettings::itfrSettings() +: user_data(nullptr) +{ } + +itfrSettings::~itfrSettings() +{ + user_data = nullptr; +} + +void +itfrSettings::reset() +{ + if (itfSettings) { + delete itfSettings; + } + + itfSettings = new itfrSettings(); +} + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf/itfrSettings.hpp b/src/operation/iRCX/source/parser/itf/itfrSettings.hpp new file mode 100644 index 000000000..e36260ed4 --- /dev/null +++ b/src/operation/iRCX/source/parser/itf/itfrSettings.hpp @@ -0,0 +1,40 @@ +// *************************************************************************************** +// 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 + +namespace itf +{ + +typedef void* itfiUserData; + +class itfrSettings { + public: + // constructor + itfrSettings(); + ~itfrSettings(); + + // function + static void reset(); + + // members + itfiUserData user_data; +}; + +extern itfrSettings* itfSettings; + + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf_builder/CMakeLists.txt b/src/operation/iRCX/source/parser/itf_builder/CMakeLists.txt new file mode 100644 index 000000000..0bafccff7 --- /dev/null +++ b/src/operation/iRCX/source/parser/itf_builder/CMakeLists.txt @@ -0,0 +1,17 @@ +set(IRCX_ITF_BUILDER_SRC + ItfBuilder.cpp + ItfRead.cpp +) + +add_library(itf_builder ${IRCX_ITF_BUILDER_SRC}) + +target_link_libraries(itf_builder + PUBLIC + itf + itf_data +) + +target_include_directories(itf_builder + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/src/operation/iRCX/source/parser/itf_builder/ItfBuilder.cpp b/src/operation/iRCX/source/parser/itf_builder/ItfBuilder.cpp new file mode 100644 index 000000000..e90de6470 --- /dev/null +++ b/src/operation/iRCX/source/parser/itf_builder/ItfBuilder.cpp @@ -0,0 +1,35 @@ +// *************************************************************************************** +// 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 "ItfBuilder.hpp" +#include "ItfRead.hpp" +namespace itf +{ + +ItfService* +ItfBuilder::get_itf_service() const +{ + return _itf_service.get(); +} + +void +ItfBuilder::buildItf(const std::string& fname) +{ + ItfRead itf_read(_itf_service.get()); + itf_read.createDb(fname); +} + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf_builder/ItfBuilder.hpp b/src/operation/iRCX/source/parser/itf_builder/ItfBuilder.hpp new file mode 100644 index 000000000..afc0ce8da --- /dev/null +++ b/src/operation/iRCX/source/parser/itf_builder/ItfBuilder.hpp @@ -0,0 +1,43 @@ +// *************************************************************************************** +// 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 +#include + +#include "ItfService.hpp" +namespace itf +{ + +class ItfBuilder { + public: + // constructor + ItfBuilder() = default; + ~ItfBuilder() = default; + + // getter + ItfService* get_itf_service() const; + + // function + void buildItf(const std::string&); + + private: + // members + std::unique_ptr _itf_service{std::make_unique()}; +}; + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf_builder/ItfRead.cpp b/src/operation/iRCX/source/parser/itf_builder/ItfRead.cpp new file mode 100644 index 000000000..078e946d2 --- /dev/null +++ b/src/operation/iRCX/source/parser/itf_builder/ItfRead.cpp @@ -0,0 +1,281 @@ +// *************************************************************************************** +// 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 +#include + +#include "ItfRead.hpp" +namespace itf +{ + + +ItfRead::ItfRead(ItfService* itf_service) +: _itf_service(nullptr) +, _fname() +, _process_corner(nullptr) +{ + assert(itf_service); + _itf_service = itf_service; +} + +bool +ItfRead::createDb(const std::string& fname) +{ + itfrSetTechnologyCb(technologyCb); + itfrSetGlobalTemperatureCb(globalTemperatureCb); + itfrSetBackgroundErCb(backgroundErCb); + itfrSetHalfNodeScaleFactorCb(halfNodeScaleFactorCb); + itfrSetUseSiDensityCb(useSiDensityCb); + itfrSetDropFactorLateralSpacingCb(dropFactorLateralSpacingCb); + itfrSetDielectricCb(dielectricCb); + itfrSetConductorCb(conductorCb); + itfrSetViaCb(viaCb); + itfrSetVariationParamCb(variationParamCb); + + itfrInit(); + + FILE* file = fopen(fname.c_str(), "r"); + if (file == nullptr) { + std::cout << "fail to open: " << fname << std::endl; + return false; + } + + _process_corner = std::make_unique(); + + _fname = fname; + auto ret = itfrRead(file, fname.c_str(), this); + fclose(file); + + itfrClear(); + + _process_corner->update_layers_height(); + _itf_service->add_process_corner(std::move(_process_corner)); + + return ret; +} +int +ItfRead::technologyCb(itfCallBackType c, const char* string, itfiUserData user_data) +{ + if (c != itfCallBackType::kTechnologyCbType) { + std::cout << "Callback type error, needs kTechnologyCbType" << std::endl; + return 1; + } + + if (string == nullptr) { + std::cout << "string is nullptr" << std::endl; + return 1; + } + + auto itf_reader = static_cast(user_data); + return itf_reader->parse_technology(string); +} + +int +ItfRead::globalTemperatureCb(itfCallBackType c, double temperature, itfiUserData user_data) +{ + if (c != itfCallBackType::kGlobalTemperatureCbType) { + std::cout << "Callback type error, needs kGlobalTemperatureCbType" << std::endl; + return 1; + } + auto itf_reader = static_cast(user_data); + return itf_reader->parse_globalTemperature(temperature); +} + +int +ItfRead::backgroundErCb(itfCallBackType c, double background_er, itfiUserData user_data) +{ + if (c != itfCallBackType::kBackgroundErCbType) { + std::cout << "Callback type error, needs kBackgroundErCbType" << std::endl; + return 1; + } + auto itf_reader = static_cast(user_data); + return itf_reader->parse_backgroundEr(background_er); +} + +int +ItfRead::halfNodeScaleFactorCb(itfCallBackType c, double factor, itfiUserData user_data) +{ + if (c != itfCallBackType::kHalfNodeScaleFactorCbType) { + std::cout << "Callback type error, needs kHalfNodeScaleFactorCbType" << std::endl; + return 1; + } + auto itf_reader = static_cast(user_data); + return itf_reader->parse_halfNodeScaleFactor(factor); +} + +int +ItfRead::useSiDensityCb(itfCallBackType c, int use, itfiUserData user_data) +{ + if (c != itfCallBackType::kUseSiDensityCbType) { + std::cout << "Callback type error, needs kUseSiDensityCbType" << std::endl; + return 1; + } + auto itf_reader = static_cast(user_data); + return itf_reader->parse_useSiDensity(use); +} + +int +ItfRead::dropFactorLateralSpacingCb(itfCallBackType c, double factor, itfiUserData user_data) +{ + if (c != itfCallBackType::kDropFactorLateralSpacingCbType) { + std::cout << "Callback type error, needs kDropFactorLateralSpacingCbType" << std::endl; + return 1; + } + auto itf_reader = static_cast(user_data); + return itf_reader->parse_dropFactorLateralSpacing(factor); +} + +int +ItfRead::dielectricCb(itfCallBackType c, itfiDielectric* dielectric, itfiUserData user_data) +{ + if (c != itfCallBackType::kDielectricCbType) { + std::cout << "Callback type error, needs kDielectricCbType" << std::endl; + return 1; + } + + if (dielectric == nullptr) { + std::cout << "itf Dielectric is nullptr" << std::endl; + return 1; + } + + auto itf_reader = static_cast(user_data); + return itf_reader->parse_dielectric(*dielectric); +} + +int +ItfRead::conductorCb(itfCallBackType c, itfiConductor* conductor, itfiUserData user_data) +{ + if (c != itfCallBackType::kConductorCbType) { + std::cout << "Callback type error, needs kConductorCbType" << std::endl; + return 1; + } + + if (conductor == nullptr) { + std::cout << "itf Conductor is nullptr" << std::endl; + return 1; + } + + auto itf_reader = static_cast(user_data); + return itf_reader->parse_conductor(*conductor); +} + +int +ItfRead::viaCb(itfCallBackType c, itfiVia* via, itfiUserData user_data) +{ + if (c != itfCallBackType::kViaCbType) { + std::cout << "Callback type error, needs kViaCbType" << std::endl; + return 1; + } + + if (via == nullptr) { + std::cout << "itf Via is nullptr" << std::endl; + return 1; + } + + auto itf_reader = static_cast(user_data); + return itf_reader->parse_via(*via); +} + +int +ItfRead::variationParamCb(itfCallBackType c, itfiVariationParam* vp, itfiUserData user_data) +{ + if (c != itfCallBackType::kVariationCbType) { + std::cout << "Callback type error, needs kVariationCbType" << std::endl; + return 1; + } + + if (vp == nullptr) { + std::cout << "itf Variation Param is nullptr" << std::endl; + return 1; + } + + auto itf_reader = static_cast(user_data); + return itf_reader->parse_variation_param(*vp); +} + +int +ItfRead::parse_technology(const char* v) +{ + _process_corner->set_technology(v); + return 0; +} +int +ItfRead::parse_globalTemperature(double v) +{ + _process_corner->set_global_temperature(v); + return 0; +} +int +ItfRead::parse_backgroundEr(double v) +{ + _process_corner->set_background_er(v); + return 0; +} +int +ItfRead::parse_halfNodeScaleFactor(double v) +{ + _process_corner->set_half_node_scale_factor(v); + return 0; +} +int +ItfRead::parse_useSiDensity(int v) +{ + _process_corner->set_use_si_density(v); + return 0; +} +int +ItfRead::parse_dropFactorLateralSpacing(double v) +{ + _process_corner->set_drop_factor_lateral_spacing(v); + return 0; +} + +int +ItfRead::parse_dielectric(const itfiDielectric& dielectric) +{ + auto diel = new LayerDielectric(dielectric); + auto layers = _process_corner->get_layers(); + layers->add_dielectric_layer(diel); + return 0; +} + +int +ItfRead::parse_conductor(const itfiConductor& conductor) +{ + auto cdt = new LayerConductor(conductor); + auto layers = _process_corner->get_layers(); + layers->add_conductor_layer(cdt); + return 0; +} + +int +ItfRead::parse_via(const itfiVia& via) +{ + auto v = new LayerVia(via); + auto layers = _process_corner->get_layers(); + layers->add_via_layer(v); + return 0; +} + +int +ItfRead::parse_variation_param(const itfiVariationParam& vp) +{ + auto vps = _process_corner->get_variation_params(); + vps->add_variation_param(vp); + return 0; +} + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf_builder/ItfRead.hpp b/src/operation/iRCX/source/parser/itf_builder/ItfRead.hpp new file mode 100644 index 000000000..2e7ec617f --- /dev/null +++ b/src/operation/iRCX/source/parser/itf_builder/ItfRead.hpp @@ -0,0 +1,72 @@ +// *************************************************************************************** +// 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 +#include + +#include "itfrReader.hpp" +#include "ItfService.hpp" +namespace itf +{ + + +// interface between itf and irc +class ItfRead { + public: + ItfRead(ItfService*); + ~ItfRead() = default; + + // getter + ItfService* get_service() { return _itf_service; } + const ItfService* get_service() const { return _itf_service; } + + // function + bool createDb(const std::string&); + + private: + // callback + static int technologyCb(itfCallBackType, const char*, itfiUserData); + static int globalTemperatureCb(itfCallBackType, double, itfiUserData); + static int backgroundErCb(itfCallBackType, double, itfiUserData); + static int halfNodeScaleFactorCb(itfCallBackType, double, itfiUserData); + static int useSiDensityCb(itfCallBackType, int, itfiUserData); + static int dropFactorLateralSpacingCb(itfCallBackType, double, itfiUserData); + static int dielectricCb(itfCallBackType, itfiDielectric*, itfiUserData); + static int conductorCb(itfCallBackType, itfiConductor*, itfiUserData); + static int viaCb(itfCallBackType, itfiVia*, itfiUserData); + static int variationParamCb(itfCallBackType, itfiVariationParam*, itfiUserData); + + // parser + int parse_technology(const char*); + int parse_globalTemperature(double); + int parse_backgroundEr(double); + int parse_halfNodeScaleFactor(double); + int parse_useSiDensity(int); + int parse_dropFactorLateralSpacing(double); + int parse_conductor(const itfiConductor&); + int parse_dielectric(const itfiDielectric&); + int parse_via(const itfiVia&); + int parse_variation_param(const itfiVariationParam&); + + // members + ItfService* _itf_service; + std::string _fname; + std::unique_ptr _process_corner; +}; + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf_builder/ItfService.hpp b/src/operation/iRCX/source/parser/itf_builder/ItfService.hpp new file mode 100644 index 000000000..6d85a90fc --- /dev/null +++ b/src/operation/iRCX/source/parser/itf_builder/ItfService.hpp @@ -0,0 +1,117 @@ +// *************************************************************************************** +// 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 +#include +#include + +#include "ProcessCorner.hpp" +namespace itf +{ + +class ItfService { + public: + // constructor + ItfService() = default; + ~ItfService() = default; + + // getter + std::vector get_process_corners() const; + ProcessCorner* get_last_process_corner(); + const ProcessCorner* get_last_process_corner() const; + + // take ownership + std::vector> take_process_corners(); + std::unique_ptr take_last_process_corner(); + + // setter + void add_process_corner(std::unique_ptr corner); + + // function + ProcessCorner* find_process_corner(const std::string&) const; + + private: + std::vector> _process_corners; +}; + +//////// inline ///////// + +inline void +ItfService::add_process_corner(std::unique_ptr c) +{ + if (c) _process_corners.push_back(std::move(c)); +} + +inline ProcessCorner* +ItfService::find_process_corner(const std::string& process_name) const +{ + for (const auto& c : _process_corners) { + if (c && c->get_technology() == process_name) { + return c.get(); + } + } + + return nullptr; +} + +inline ProcessCorner* +ItfService::get_last_process_corner() +{ + return _process_corners.size() ? _process_corners.back().get() : nullptr; +} + +inline const ProcessCorner* +ItfService::get_last_process_corner() const +{ + return _process_corners.size() ? _process_corners.back().get() : nullptr; +} + +inline std::vector> +ItfService::take_process_corners() +{ + auto ret = std::move(_process_corners); + _process_corners.clear(); + return ret; +} + +inline std::unique_ptr +ItfService::take_last_process_corner() +{ + if (_process_corners.empty()) { + return nullptr; + } + + auto ret = std::move(_process_corners.back()); + _process_corners.pop_back(); + return ret; +} + +inline std::vector +ItfService::get_process_corners() const +{ + std::vector process_corners; + process_corners.reserve(_process_corners.size()); + for (const auto& corner : _process_corners) { + process_corners.push_back(corner.get()); + } + return process_corners; +} + + + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf_data/CMakeLists.txt b/src/operation/iRCX/source/parser/itf_data/CMakeLists.txt new file mode 100644 index 000000000..2a95ea815 --- /dev/null +++ b/src/operation/iRCX/source/parser/itf_data/CMakeLists.txt @@ -0,0 +1,17 @@ +set(IRCX_ITF_DATA_SRC + Layer.cpp + ProcessCorner.cpp + VariationParams.cpp +) + +add_library(itf_data ${IRCX_ITF_DATA_SRC}) + +target_link_libraries(itf_data + PUBLIC + itf +) + +target_include_directories(itf_data + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/src/operation/iRCX/source/parser/itf_data/Layer.cpp b/src/operation/iRCX/source/parser/itf_data/Layer.cpp new file mode 100644 index 000000000..bf8f5330f --- /dev/null +++ b/src/operation/iRCX/source/parser/itf_data/Layer.cpp @@ -0,0 +1,556 @@ +// *************************************************************************************** +// 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 "Layer.hpp" +namespace itf +{ + +Layer::Layer(LayerType t) +: _type(t) +, _id(0) +, _order(std::nullopt) +, _height(0) +{ } + +LayerType +Layer::get_type() const +{ + return _type; +} + +int16_t +Layer::get_id() const +{ + return _id; +} + +std::optional +Layer::get_order() const +{ + return _order; +} + +double +Layer::get_height() const +{ + return _height; +} + +void +Layer::set_id(int16_t id) +{ + _id = id; +} + +void +Layer::set_order(uint8_t order) +{ + _order = order; +} + +void +Layer::set_height(double height) +{ + _height = height; +} + +LayerConductor::LayerConductor() +: Layer(LayerType::kConductor) +, _width(0) +{ + +} + +LayerConductor::LayerConductor(const itfiConductor& cdt) +: Layer(LayerType::kConductor) +, _width(0) +{ + itfiConductor::operator=(cdt); +} + +LayerConductor::~LayerConductor() +{ } + +std::string +LayerConductor::get_name() const +{ + return get_conductor_name(); +} + +// Units: microns +double +LayerConductor::get_layer_thickness() const +{ + return get_thickness(); +} + +int32_t +LayerConductor::get_width() const +{ + return _width; +} + +void +LayerConductor::set_width(int32_t w) +{ + _width = w; +} + +// @param width Conductor silicon (post-etch) widths +// @param crt1 output Linear temperature coefficients +// @param crt2 output Quadratic temperature coefficients  +void +LayerConductor::query_crt_vs_si_width( + double width, + std::optional& crt1, + std::optional& crt2 +) const { + crt1.reset(); + crt2.reset(); + + auto& list = get_crt_vs_si_width(); + if (list.size() < 2) return; // lack of information to interpolate + + auto it = std::upper_bound(list.begin(), list.end(), width, + [](double width, const itfiWidthCrt& e) { + return width < e.si_width; + }); + + if (it == list.end()) { + // upper extrapolation + crt1 = list.back().crt_1; + crt2 = list.back().crt_2; + } else if (width <= list.front().si_width) { + // lower extrapolation + crt1 = list.front().crt_1; + crt2 = list.front().crt_2; + } else { + // interpolation + size_t id_h = std::distance(list.begin(), it); + size_t id_l = id_h - 1; + double t = (width - list.at(id_l).si_width) / (list.at(id_h).si_width - list.at(id_l).si_width); + crt1 = std::lerp(list.at(id_l).crt_1, list.at(id_h).crt_1, t); + crt2 = std::lerp(list.at(id_l).crt_2, list.at(id_h).crt_2, t); + } +} + +LayerDielectric::LayerDielectric() +: Layer(LayerType::kDielectric) +{ } + +LayerDielectric::LayerDielectric(const itfiDielectric& d) +: Layer(LayerType::kDielectric) +{ + itfiDielectric::operator=(d); +} + +LayerDielectric::~LayerDielectric() +{ } + +std::string +LayerDielectric::get_name() const +{ + return get_dielectric_name(); +} + +// Units: microns +double +LayerDielectric::get_layer_thickness() const +{ + return get_thickness(); +} + +LayerVia::LayerVia() +: Layer(LayerType::kVia) +, _top_height(0) +{ } + +LayerVia::LayerVia(const itfiVia& v) +: Layer(LayerType::kVia) +{ + itfiVia::operator=(v); +} + +LayerVia::~LayerVia() +{ + +} + +std::string +LayerVia::get_name() const +{ + return get_via_name(); +} + +// Units: microns +double +LayerVia::get_layer_thickness() const +{ + return get_top_height() - get_bot_height(); +} + +double +LayerVia::get_bot_height() const +{ + return get_height(); +} + +double +LayerVia::get_top_height() const +{ + return _top_height; +} + +void +LayerVia::set_top_height(double top) +{ + _top_height = top; +} + +// StarRC UG Version U-2022.12, December 2022. P-1333 +double +LayerVia::query_rpv_vs_area(double area) const +{ + auto& table = get_rpv_vs_area(); + if (table.size() <= 1) return 0; // lack of information to interpolate + + auto it = std::upper_bound(table.begin(), table.end(), area, + [](double area, const itfiAreaRpv& e) { + return area < e.area; + }); + + if (it == table.end()) { + // upper extrapolation + return table.back().rpv; + } else if (area <= table.front().area) { + // lower extrapolation + return table.front().rpv; + } else { + // interpolation + size_t id_l, id_h; + id_h = std::distance(table.begin(), it); + id_l = id_h - 1; + double rec_rpv_l = 1 / table.at(id_l).rpv; + double rec_rpv_h = 1 / table.at(id_h).rpv; + double rec_rpv_interpolated = + std::lerp(rec_rpv_l, rec_rpv_h, + (area - table.at(id_l).area) / (table.at(id_h).area - table.at(id_l).area ) ); + return 1 / rec_rpv_interpolated; + } +} + +void +LayerVia::query_crt_vs_area( + double area, + std::optional& crt1, + std::optional& crt2 +) const { + crt1.reset(); + crt2.reset(); + + auto& list = get_crt_vs_area(); + if (list.size() < 2) return; // lack of information to interpolate + + auto it = std::upper_bound(list.begin(), list.end(), area, + [](double area, const itfiAreaCrt& e){ + return area < e.area; + }); + + if (it == list.end()) { + // upper extrapolation + crt1 = list.back().crt1; + crt2 = list.back().crt2; + } else if (area <= list.front().area) { + // lower extrapolation + crt1 = list.front().crt1; + crt2 = list.front().crt2; + } else { + // interpolation + size_t id_h = std::distance(list.begin(), it); + size_t id_l = id_h - 1; + double t = (area - list.at(id_l).area) / (list.at(id_h).area - list.at(id_l).area); + crt1 = std::lerp(list.at(id_l).crt1, list.at(id_h).crt1, t); + crt2 = std::lerp(list.at(id_l).crt2, list.at(id_h).crt2, t); + } +} + +Layers::~Layers() +{ + clear(); +} + +// @param order of layer +Layer* +Layers::find_layer(uint8_t order) const +{ + std::optional lo; + for (auto layer : _layers) { + lo = layer->get_order(); + if (lo.has_value() && lo.value() == order) { + return layer; + } + } + + return nullptr; +} + +Layer* +Layers::find_layer(const std::string& name) const +{ + for (auto layer : _layers) { + if (layer->get_name() == name) { + return layer; + } + } + + return nullptr; +} + +// @param order layer order +LayerConductor* +Layers::find_conductor_layer(uint8_t order) const +{ + for (auto layer : _conductor_layers) { + if (layer->get_order() == order) { + return layer; + } + } + + return nullptr; +} + +LayerConductor* +Layers::find_conductor_layer(const char* s) const +{ + if (!s) return nullptr; + for (auto cdt : _conductor_layers) { + if (cdt->get_name() == s) { + return cdt; + } + } + return nullptr; +} + +LayerVia* +Layers::find_via_layer(uint8_t order) const +{ + for (auto layer : _via_layers) { + if (layer->get_order() == order) { + return layer; + } + } + + return nullptr; +} + +void +Layers::clear() +{ + for (auto layer : _layers) { + delete layer; + } + _layers.clear(); + _conductor_layers.clear(); + _dielectric_layers.clear(); + _via_layers.clear(); +} + +std::vector& +Layers::get_layers() +{ + return _layers; +} + +const std::vector& +Layers::get_layers() const +{ + return _layers; +} + +std::vector& +Layers::get_conductor_layers() +{ + return _conductor_layers; +} + +const std::vector& +Layers::get_conductor_layers() const +{ + return _conductor_layers; +} + +std::vector& +Layers::get_dielectric_layers() +{ + return _dielectric_layers; +} + +const std::vector& +Layers::get_dielectric_layers() const +{ + return _dielectric_layers; +} + +std::vector& +Layers::get_via_layers() +{ + return _via_layers; +} + +const std::vector& +Layers::get_via_layers() const +{ + return _via_layers; +} + +Layer* +Layers::get_uppermost_layer_by_order() const +{ + Layer* ret = nullptr; + for (auto l : _layers) { + if (!l->get_order().has_value()) continue; + + if ((ret == nullptr) + || (ret->get_order().value() < l->get_order().value()) ) { + ret = l; + } + } + return ret; +} + +Layer* +Layers::get_lowermost_layer_by_order() const +{ + Layer* ret = nullptr; + for (auto l : _layers) { + if (!l->get_order().has_value()) continue; + + if ((ret == nullptr) + || (ret->get_order().value() > l->get_order().value()) ) { + ret = l; + } + } + return ret; +} + +void +Layers::add_conductor_layer(LayerConductor* layer) +{ + if (layer) { + layer->set_id(_layers.size()); + layer->set_order(static_cast(_layers.size())); + _conductor_layers.push_back(layer); + _layers.push_back(layer); + } +} + +void +Layers::add_dielectric_layer(LayerDielectric* layer) +{ + if (layer) { + layer->set_id(_layers.size()); + _dielectric_layers.push_back(layer); + _layers.push_back(layer); + } +} + +void +Layers::add_via_layer(LayerVia* layer) +{ + if (layer) { + layer->set_id(_layers.size()); + layer->set_order(static_cast(_layers.size())); + _via_layers.push_back(layer); + _layers.push_back(layer); + } +} + +// @brief find the first dielectric layer below the conductor layer +LayerDielectric* +Layers::find_diel_below(LayerConductor* cdt) const +{ + if (!cdt) return nullptr; + + auto it = std::upper_bound(_dielectric_layers.begin(), _dielectric_layers.end(), cdt->get_id(), + [](int16_t id_high, LayerDielectric* d){ + return d->get_id() > id_high; + }); + + return it == _dielectric_layers.end() ? nullptr : + _dielectric_layers.at(std::distance(_dielectric_layers.begin(), it)); +} + +LayerDielectric* +Layers::find_diel_below(double height) const +{ + auto it = std::upper_bound(_dielectric_layers.begin(), _dielectric_layers.end(), height, + [](double h, LayerDielectric* d){ + return d->get_height() < h; + }); + + return it == _dielectric_layers.end() ? nullptr : *it; +} + +// @brief find the first dielectric layer above the height +LayerDielectric* +Layers::find_diel_above(double height) const +{ + auto it = std::upper_bound(_dielectric_layers.rbegin(), _dielectric_layers.rend(), height, + [](double h, LayerDielectric* d){ + return d->get_height() + d->get_layer_thickness() > h; + }); + + return it == _dielectric_layers.rend() ? nullptr : *it; +} + +LayerDielectric* +Layers::find_diel(int16_t id) const +{ + for (auto diel : _dielectric_layers) { + if (diel->get_id() == id) { + return diel; + } + } + + return nullptr; +} + +LayerDielectric* +Layers::find_diel(const char* s) const +{ + if (!s) return nullptr; + for (auto d : _dielectric_layers) { + if (d->get_name() == s) { + return d; + } + } + return nullptr; +} + +bool +Layers::is_lowermost_diel(int16_t id) const +{ + if (_dielectric_layers.size()) { + auto diel = _dielectric_layers.at(_dielectric_layers.size() - 1); + if (diel->get_id() == id) { + return true; + } + } + + return false; +} + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf_data/Layer.hpp b/src/operation/iRCX/source/parser/itf_data/Layer.hpp new file mode 100644 index 000000000..78c5f4518 --- /dev/null +++ b/src/operation/iRCX/source/parser/itf_data/Layer.hpp @@ -0,0 +1,173 @@ +// *************************************************************************************** +// 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 +#include +#include +#include + +#include "itfiConductor.hpp" +#include "itfiDielectric.hpp" +#include "itfiVia.hpp" +namespace itf +{ + + +enum class LayerType { kDielectric, kConductor, kVia }; + +class Layer { + public: + // constructor + explicit Layer(LayerType); + virtual ~Layer() = default; + + // getter + LayerType get_type() const; + int16_t get_id() const; + std::optional get_order() const; + double get_height() const; + virtual std::string get_name() const = 0; + virtual double get_layer_thickness() const = 0; + + // setter + void set_id(int16_t); + void set_order(uint8_t); + void set_height(double); + + // function + + private: + LayerType _type; + int16_t _id; // refer to the order in .itf file + std::optional _order; // refer to the order in .tlef file + double _height; // Units: micron +}; + +class LayerConductor : public Layer, public itfiConductor { + public: + // constructor + LayerConductor(); + explicit LayerConductor(const itfiConductor&); + ~LayerConductor(); + + // getter + virtual std::string get_name() const override; + virtual double get_layer_thickness() const override; + int32_t get_width() const; + + // setter + void set_width(int32_t); + + // function + void query_crt_vs_si_width(double, std::optional&, std::optional&) const; + + private: + // members + int32_t _width; +}; + +class LayerDielectric : public Layer, public itfiDielectric { + public: + // constructor + LayerDielectric(); + explicit LayerDielectric(const itfiDielectric&); + ~LayerDielectric(); + + // getter + virtual std::string get_name() const override; + virtual double get_layer_thickness() const override; + + // setter + + // function + + private: + // members +}; + +class LayerVia : public Layer, public itfiVia { + public: + // constructor + LayerVia(); + explicit LayerVia(const itfiVia&); + ~LayerVia(); + + // getter + virtual std::string get_name() const override; + virtual double get_layer_thickness() const override; + double get_bot_height() const; + double get_top_height() const; + + // setter + void set_top_height(double); + + // function + double query_rpv_vs_area(double) const; + void query_crt_vs_area(double, std::optional&, std::optional&) const; + + private: + // members + double _top_height; +}; + +class Layers { + public: + // constructor + Layers() = default; + ~Layers(); + + // getter + std::vector& get_layers(); + const std::vector& get_layers() const; + std::vector& get_conductor_layers(); + const std::vector& get_conductor_layers() const; + std::vector& get_dielectric_layers(); + const std::vector& get_dielectric_layers() const; + std::vector& get_via_layers(); + const std::vector& get_via_layers() const; + Layer* get_uppermost_layer_by_order() const; + Layer* get_lowermost_layer_by_order() const; + + // setter + void add_conductor_layer(LayerConductor*); + void add_dielectric_layer(LayerDielectric*); + void add_via_layer(LayerVia*); + + // function + void clear(); + Layer* find_layer(uint8_t) const; + Layer* find_layer(const std::string&) const; + LayerConductor* find_conductor_layer(uint8_t) const; + LayerConductor* find_conductor_layer(const char*) const; + LayerVia* find_via_layer(uint8_t) const; + LayerDielectric* find_diel_below(LayerConductor*) const; + LayerDielectric* find_diel_below(double) const; + LayerDielectric* find_diel_above(double) const; + LayerDielectric* find_diel(int16_t) const; + LayerDielectric* find_diel(const char*) const; + bool is_lowermost_diel(int16_t) const; + + private: + // members + std::vector _layers; + std::vector _conductor_layers; + std::vector _dielectric_layers; + std::vector _via_layers; +}; + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf_data/ProcessCorner.cpp b/src/operation/iRCX/source/parser/itf_data/ProcessCorner.cpp new file mode 100644 index 000000000..fb5b2cd2b --- /dev/null +++ b/src/operation/iRCX/source/parser/itf_data/ProcessCorner.cpp @@ -0,0 +1,265 @@ +// *************************************************************************************** +// 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 +#include + +#include "ProcessCorner.hpp" +#include "magic_enum/magic_enum.hpp" +namespace itf +{ + +ProcessCorner::ProcessCorner() +: _technology() +, _global_temperature(25) +, _background_er(1) +, _half_node_scale_factor(1) +, _use_si_density() +, _drop_factor_lateral_spacing(.5) +, _layers(nullptr) +, _variation_params(nullptr) +{ + _layers = new Layers(); + _variation_params = new VariationParams(); +} + +ProcessCorner::~ProcessCorner() +{ + delete _layers; + delete _variation_params; +} + +std::string +ProcessCorner::get_technology() const +{ + return _technology; +} + +double +ProcessCorner::get_global_temperature() const +{ + return _global_temperature; +} + +double +ProcessCorner::get_background_er() const +{ + return _background_er; +} + +double +ProcessCorner::get_half_node_scale_factor() const +{ + return _half_node_scale_factor; +} + +bool +ProcessCorner::get_use_si_density() const +{ + return _use_si_density; +} + +double +ProcessCorner::get_drop_factor_lateral_spacing() const +{ + return _drop_factor_lateral_spacing; +} + +Layers* +ProcessCorner::get_layers() +{ + return _layers; +} + +const Layers* +ProcessCorner::get_layers() const +{ + return _layers; +} + +VariationParams* +ProcessCorner::get_variation_params() +{ + return _variation_params; +} + +const VariationParams* +ProcessCorner::get_variation_params() const +{ + return _variation_params; +} + +void +ProcessCorner::set_technology(const std::string v) +{ + _technology = v; +} +void +ProcessCorner::set_global_temperature(double v) +{ + _global_temperature = v; +} +void +ProcessCorner::set_background_er(double v) +{ + _background_er = v; +} +void +ProcessCorner::set_half_node_scale_factor(double v) +{ + _half_node_scale_factor = v; +} +void +ProcessCorner::set_use_si_density(bool v) +{ + _use_si_density = v; +} +void +ProcessCorner::set_drop_factor_lateral_spacing(double v) +{ + _drop_factor_lateral_spacing = v; +} + +// @param name_order_map itf layer name -> layer order +void +ProcessCorner::set_layers_map(const std::map& name_order_map) +{ + for (const auto& [name, layer_order] : name_order_map) { + auto layer = _layers->find_layer(name); + if (layer) { + layer->set_order(layer_order); + } + } +} + +void +ProcessCorner::update_layers_height() +{ + std::vector stack_layers; + for (auto layer : _layers->get_layers()) { + if (layer->get_type() == LayerType::kVia) continue; + stack_layers.push_back(layer); + } + std::sort(stack_layers.begin(), stack_layers.end(), [](Layer* high, Layer* low){ + return high->get_id() > low->get_id(); + }); + + LayerDielectric* last_diel = nullptr; + for (auto layer : stack_layers) { + switch (layer->get_type()) { + case LayerType::kConductor: + update_conductor_layer_height(dynamic_cast(layer), last_diel); + break; + case LayerType::kDielectric: + update_dielectric_layer_height(last_diel, dynamic_cast(layer)); + last_diel = dynamic_cast(layer); + break; + default: + std::cout << "Unhandled layer type: " << magic_enum::enum_name(layer->get_type()) << std::endl; + break; + } + } + + // via layer + for (auto via : _layers->get_via_layers()) { + auto bot = _layers->find_layer(via->get_from()); + if (bot) { + via->set_height(bot->get_height() + bot->get_layer_thickness()); + } else if (strncasecmp("SUBSTRATE", via->get_from(), 9)) { + std::cout << "fail to find layer: " << via->get_from() << std::endl; + } + + auto top = _layers->find_layer(via->get_to()); + if (top) { + via->set_top_height(top->get_height()); + } else { + std::cout << "fail to find layer: " << via->get_to() << std::endl; + } + } + + // debug + // show_layers(); +} + +void +ProcessCorner::update_dielectric_layer_height(LayerDielectric* last_diel, LayerDielectric* cur_diel) +{ + if (!cur_diel) return; + + if (cur_diel->has_associated_conductor()) { + auto cdt = _layers->find_conductor_layer(cur_diel->get_associated_conductor()); + if (cdt) { + cur_diel->set_height(cdt->get_height()); + } + } else if (cur_diel->has_measured_from()) { + auto layer = _layers->find_layer(cur_diel->get_measured_from()); + if (layer) { + cur_diel->set_height(layer->get_height() + layer->get_layer_thickness()); + } else if (strncasecmp("TOP_OF_CHIP", cur_diel->get_measured_from(), 11) == 0 && last_diel) { + cur_diel->set_height(last_diel->get_height() + last_diel->get_layer_thickness()); + } + } else if (last_diel) { + cur_diel->set_height(last_diel->get_height() + last_diel->get_layer_thickness()); + } else if (_layers->is_lowermost_diel(cur_diel->get_id())) { + return; + } else { + std::cout << "fail to update layer height at: " << cur_diel->get_name() << std::endl; + } +} + +void +ProcessCorner::update_conductor_layer_height(LayerConductor* cdt, LayerDielectric* last_diel) +{ + if (!cdt) return; + + char* measured = cdt->get_measured_from(); + LayerDielectric* diel = nullptr; + if (measured) { + diel = _layers->find_diel(measured); + } else { + diel = last_diel; + } + + if (diel) { + cdt->set_height(diel->get_height() + diel->get_thickness()); + } else { + std:: cout << "fail to find diel, cdt_id = " << std::to_string(cdt->get_id()) << std::endl; + } +} + +void +ProcessCorner::show_layers() const +{ + auto show_er = [](Layer* layer) -> std::string { + if (layer->get_type() != LayerType::kDielectric) return ""; + else return "| " + std::to_string(dynamic_cast(layer)->get_er()); + }; + auto show_layer_type = [](Layer* layer) -> std::string_view { + return magic_enum::enum_name(layer->get_type()); + }; + std::cout << " layer type | layer name | height | thickness | ER" << std::endl; + for (auto layer : _layers->get_layers()) { + std::cout + << std::left << std::setw(15) << show_layer_type(layer) << "| " + << std::left << std::setw(25) << layer->get_name() << "| " + << std::setw(7) << layer->get_height() << "| " + << std::setw(10) << layer->get_layer_thickness() + << show_er(layer) << "" + << std::endl; + } +} + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf_data/ProcessCorner.hpp b/src/operation/iRCX/source/parser/itf_data/ProcessCorner.hpp new file mode 100644 index 000000000..fbdf05459 --- /dev/null +++ b/src/operation/iRCX/source/parser/itf_data/ProcessCorner.hpp @@ -0,0 +1,75 @@ +// *************************************************************************************** +// 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 +#include +#include + +#include "Layer.hpp" +#include "VariationParams.hpp" +namespace itf +{ + +class ProcessCorner { + public: + // constructor + ProcessCorner(); + ~ProcessCorner(); + + // getter + std::string get_technology() const; + double get_global_temperature() const; + double get_background_er() const; + double get_half_node_scale_factor() const; + bool get_use_si_density() const; + double get_drop_factor_lateral_spacing() const; + Layers* get_layers(); + const Layers* get_layers() const; + VariationParams* get_variation_params(); + const VariationParams* get_variation_params() const; + + // setter + void set_technology(std::string); + void set_global_temperature(double); + void set_background_er(double); + void set_half_node_scale_factor(double); + void set_use_si_density(bool); + void set_drop_factor_lateral_spacing(double); + void set_layers_map(const std::map&); + + // function + void update_layers_height(); + void show_layers() const; + + private: + // function + void update_dielectric_layer_height(LayerDielectric*, LayerDielectric*); + void update_conductor_layer_height(LayerConductor*, LayerDielectric*); + + // members + std::string _technology; + double _global_temperature; + double _background_er; + double _half_node_scale_factor; + bool _use_si_density; + double _drop_factor_lateral_spacing; + Layers* _layers; + VariationParams* _variation_params; +}; + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf_data/VariationParams.cpp b/src/operation/iRCX/source/parser/itf_data/VariationParams.cpp new file mode 100644 index 000000000..fbb091370 --- /dev/null +++ b/src/operation/iRCX/source/parser/itf_data/VariationParams.cpp @@ -0,0 +1,28 @@ +// *************************************************************************************** +// 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 "VariationParams.hpp" +namespace itf +{ + +void +VariationParams::add_variation_param(const itfiVariationParam& vp) +{ + auto ps = new itf::itfiVariationParam(vp); + _params.push_back(ps); +} + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/itf_data/VariationParams.hpp b/src/operation/iRCX/source/parser/itf_data/VariationParams.hpp new file mode 100644 index 000000000..90032b495 --- /dev/null +++ b/src/operation/iRCX/source/parser/itf_data/VariationParams.hpp @@ -0,0 +1,40 @@ +// *************************************************************************************** +// 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 + +#include "itfiVariationParam.hpp" +namespace itf +{ +class VariationParams { + public: + // constructor + + // getter + + // setter + void add_variation_param(const itfiVariationParam&); + + // function + + private: + // members + std::vector _params; +}; + +} // namespace itf diff --git a/src/operation/iRCX/source/parser/mapping/CMakeLists.txt b/src/operation/iRCX/source/parser/mapping/CMakeLists.txt new file mode 100644 index 000000000..6a05a0722 --- /dev/null +++ b/src/operation/iRCX/source/parser/mapping/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(ircx_mapping MappingBuilder.cpp) + +target_include_directories(ircx_mapping + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/src/operation/iRCX/source/parser/mapping/MappingBuilder.cpp b/src/operation/iRCX/source/parser/mapping/MappingBuilder.cpp new file mode 100644 index 000000000..21abca060 --- /dev/null +++ b/src/operation/iRCX/source/parser/mapping/MappingBuilder.cpp @@ -0,0 +1,39 @@ +// *************************************************************************************** +// 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 +#include + +#include "MappingBuilder.hpp" +namespace ircx { +namespace parser { + +void MappingBuilder::read(const std::string& mappingPath) +{ + std::ifstream mappingFile(mappingPath); + std::string line; + while (std::getline(mappingFile, line)) { + std::string designLayerName; + std::string processLayerName; + if (std::istringstream(line) >> designLayerName >> processLayerName) { + design_to_process_layer_names_[designLayerName] = processLayerName; + process_to_design_layer_names_[processLayerName] = designLayerName; + } + } +} + +} // namespace parser +} // namespace ircx diff --git a/src/operation/iRCX/source/parser/mapping/MappingBuilder.hpp b/src/operation/iRCX/source/parser/mapping/MappingBuilder.hpp new file mode 100644 index 000000000..0e99d8a5a --- /dev/null +++ b/src/operation/iRCX/source/parser/mapping/MappingBuilder.hpp @@ -0,0 +1,45 @@ +// *************************************************************************************** +// 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 +#include +namespace ircx { +namespace parser { + +class MappingBuilder +{ + public: + MappingBuilder() = default; + ~MappingBuilder() = default; + + const std::unordered_map& design_to_process_layer_names() const { + return design_to_process_layer_names_; + } + const std::unordered_map& process_to_design_layer_names() const { + return process_to_design_layer_names_; + } + + void read(const std::string& mappingPath); + + private: + std::unordered_map design_to_process_layer_names_; + std::unordered_map process_to_design_layer_names_; +}; + +} // namespace parser +} // namespace ircx