diff --git a/.flake8 b/.flake8 index e4757ebb..e3b27d50 100644 --- a/.flake8 +++ b/.flake8 @@ -16,7 +16,7 @@ ; ========================================================================== [flake8] -max-line-length = 100 +max-line-length = 120 max-complexity = 18 exclude = diff --git a/.gitignore b/.gitignore index 8346437e..ad605536 100644 --- a/.gitignore +++ b/.gitignore @@ -172,4 +172,4 @@ cython_debug/ *.lvsdb unit_tests_* lvs_run_* - +*_extracted.cir diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/README.md b/ihp-sg13g2/libs.tech/klayout/tech/lvs/README.md index 447fba22..f760d8ed 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/lvs/README.md +++ b/ihp-sg13g2/libs.tech/klayout/tech/lvs/README.md @@ -106,49 +106,69 @@ The `run_lvs.py` script takes your gds and netlist files to run LVS rule decks w ```bash run_lvs.py (--help| -h) - run_lvs.py (--layout=) (--netlist=) [--thr=] + run_lvs.py (--layout=) (--netlist=) [--run_dir=] [--topcell=] [--run_mode=] - [--lvs_sub=] [--no_net_names] [--spice_comments] [--schematic_simplify] - [--net_only] [--top_lvl_pins] [--combine] [--purge] [--purge_nets] [--verbose] + [--lvs_sub=] [--no_net_names] [--spice_comments] [--net_only] + [--no_simplify] [--no_series_res] [--no_parallel_res] [--combine_devices] + [--top_lvl_pins] [--purge] [--purge_nets] [--verbose] ``` **Options:** -- `--help -h ` Print this help message. +- `--help -h ` Displays this help message. -- `--layout=` The input GDS file path. +- `--layout=` Specifies the file path of the input GDS file. -- `--netlist=` The input netlist file path. +- `--netlist=` Specifies the file path of the input netlist file. -- `--thr=` The number of threads used in run. +- `--run_dir=` Run directory to save all the generated results [default: pwd] -- `--run_dir=` Run directory to save all the results [default: pwd] +- `--topcell=` Specifies the name of the top cell to be used. -- `--topcell=` Topcell name to use. +- `--run_mode=` Selects the allowed KLayout mode. (flat, deep). [default: flat] -- `--run_mode=` Select Allowed klayout mode. (flat, deep). [default: flat] +- `--lvs_sub=` Sets the substrate name used in your design. -- `--lvs_sub=` Substrate name used in your design. +- `--no_net_names` Omits net names in the extracted netlist. -- `--no_net_names` Discard net names in extracted netlist. +- `--spice_comments` Includes netlist comments in the extracted netlist. -- `--spice_comments` Enable netlist comments in extracted netlist. +- `--net_only` Generates netlist objects only in the extracted netlist. -- `--net_only` Enable netlist object creation for extracted netlist. +- `--no_simplify` Disables simplification for both layout and schematic netlists. -- `--top_lvl_pins` Enable top level pins only for extracted netlist. +- `--no_series_res` Prevents the simplification of series resistors for both layout and schematic netlists. -- `--no_simplify` Disable layout simplification for extracted netlist. +- `--no_parallel_res` Avoids simplifying parallel resistors within the extracted netlist. -- `--combine` Enable netlist combination for extracted netlist. +- `--combine_devices` Enables device combination for both layout and schematic netlists. -- `--purge` Enable netlist purge all for extracted netlist. +- `--top_lvl_pins` Creates pins for top-level circuits in both layout and schematic netlists. -- `--purge_nets` Enable netlist purge nets for extracted netlist. +- `--purge` Removes unused nets from both layout and schematic netlists. -- `--schematic_simplify` Enable schematic simplification for input netlist. +- `--purge_nets` Purges floating nets from both layout and schematic netlists. -- `--verbose` Detailed rule execution log for debugging. +- `--verbose` Enables detailed rule execution logs for debugging purposes. + + +--- +**NOTE** + +* By utilizing the `no_simplify` option, you can prevent layout simplification for both layout and schematic netlists. Simplification is enabled by default, incorporating steps such as `make_top_level_pins`, `purge`, `combine_devices`, and `purge_nets`. +
+ +* When you use the `no_simplify` option to disable simplification, you can then use the `make_top_level_pins`, `purge`, `combine_devices`, and `purge_nets` options individually to fine-tune the behavior according to your needs. +
+ +* Series resistors with identical parameters (except length) will combine into one resistor with the total length. +
+ +* Parallel resistors will merge into a single resistor with the parameter `m` representing the number of parallel resistors, provided they share identical parameters. +
+ +* The options `no_series_res` and `no_parallel_res` are specifically designed to disable layout simplification for resistors exclusively. When specified, they take priority over `combine_devices` option. +--- **Example:** @@ -218,15 +238,41 @@ KLAYOUT_PATH=$PDKPATH/libs.tech/klayout:$PDKPATH/libs.tech/klayout/tech/ klayout Then, you will get the LVS menus for SG13G2, you could set your desired options as shown below: -> **_NOTE:_** You need to select the path of the netlist will be used in the LVS run. If no path is specified, the tool will automatically search for the netlist file in the same directory as the layout file, considering files with the extensions .cdl, .spice, or .cir and has same name of the layout file. +

+ +

+

+ Fig. 5. Setting up LVS Options-GUI - 1 +

- +

- Fig. 5. Visualization of LVS results on Klayout-GUI + Fig. 6. Setting up LVS Options-GUI - 2

-Finally, you could run the LVS using `Run Klayout LVS` option. +--- +**NOTE** + +* To utilize the LVS options, an active cell must be present. The currently active cell is automatically chosen as the default for running LVS. You could change it using `Top Cell` option. +
+ +* To conduct the LVS comparison, you must specify the path to the schematic netlist via `Netlist Path` option. If no path is provided, the tool will search for the netlist file automatically. It will look for files with extensions such as .cdl, .spice, or .cir in the same directory as the layout file, matching the name of the layout file. + +--- + +For additional details on GUI options, please refer to the [CLI Options section](#cli). + +Finally, after setting your option, you could execute the LVS using `Run Klayout LVS` from the dropdown menu. + +

+ +

+

+ Fig. 7. Running LVS using Klayout menus +

Upon executing the LVS, the result database will appear on your layout interface, allowing you to verify the outcome of the run similarly as shown above in Fig. 4. + +Additionally, you can find the extracted netlist generated from your design at (`_extracted.cir`) in the same directory as the layout file. \ No newline at end of file diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/images/lvs_menus.png b/ihp-sg13g2/libs.tech/klayout/tech/lvs/images/lvs_menus.png deleted file mode 100644 index 6759d3c7..00000000 Binary files a/ihp-sg13g2/libs.tech/klayout/tech/lvs/images/lvs_menus.png and /dev/null differ diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/images/lvs_menus_1.png b/ihp-sg13g2/libs.tech/klayout/tech/lvs/images/lvs_menus_1.png new file mode 100644 index 00000000..8c278e51 Binary files /dev/null and b/ihp-sg13g2/libs.tech/klayout/tech/lvs/images/lvs_menus_1.png differ diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/images/lvs_menus_2.png b/ihp-sg13g2/libs.tech/klayout/tech/lvs/images/lvs_menus_2.png new file mode 100644 index 00000000..bedcee15 Binary files /dev/null and b/ihp-sg13g2/libs.tech/klayout/tech/lvs/images/lvs_menus_2.png differ diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/images/lvs_menus_3.png b/ihp-sg13g2/libs.tech/klayout/tech/lvs/images/lvs_menus_3.png new file mode 100644 index 00000000..014d538d Binary files /dev/null and b/ihp-sg13g2/libs.tech/klayout/tech/lvs/images/lvs_menus_3.png differ diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/bjt_derivations.lvs b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/bjt_derivations.lvs index a54da0e2..d63ef4c3 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/bjt_derivations.lvs +++ b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/bjt_derivations.lvs @@ -30,18 +30,17 @@ logger.info('Starting BJT DERIVATIONS') logger.info('Starting NPN-BJT DERIVATIONS') bjt_exclude = gatpoly_drw.join(pwell_block).join(nsd_drw) - .join(heatres_drw).join(salblock_drw).join(polyres_drw) - .join(mim_drw).join(extblock_drw).join(res_drw) - .join(recog_diode).join(recog_esd).join(ind_drw) - .join(ind_pin).join(substrate_drw) + .join(salblock_drw).join(polyres_drw).join(extblock_drw) + .join(res_drw).join(recog_diode).join(recog_esd) + .join(ind_drw).join(ind_pin).join(substrate_drw) npn_exclude = nwell_drw.join(psd_drw).join(nbulay_drw).join(bjt_exclude) # ---------- General NPN ---------- npn_mk = trans_drw.and(pwell).and(ptap_holes) npn_c_exc = emwind_drw.join(emwihv_drw).join(activ_mask) - .join(nsd_block).join(heattrans_drw).join(npn_exclude) -npn_b_exc = emwind_drw.join(emwihv_drw).join(heattrans_drw).join(npn_exclude) + .join(nsd_block).join(npn_exclude) +npn_b_exc = emwind_drw.join(emwihv_drw).join(npn_exclude) npn_sub = npn_mk.not(npn_exclude) npn_dev = activ_drw.join(activ_mask).and(npn_mk) @@ -51,7 +50,7 @@ npn13G2_e_exc = activ_drw.join(emwihv_drw).join(npn_exclude) npn13G2_b_exc = npn_b_exc.join(activ_mask) # npn13G2 nodes -npn13G2_e_ = emwind_drw.and(activ_mask).and(heattrans_drw).and(nsd_block).and(npn_mk).not(npn13G2_e_exc) +npn13G2_e_ = emwind_drw.and(activ_mask).and(nsd_block).and(npn_mk).not(npn13G2_e_exc) # npn13G2 is a fixed device (0.07um X 0.9um) npn13G2_e_pin = npn13G2_e_.with_bbox_min(0.07.um).with_bbox_max(0.9.um).with_area(0.063.um) npn13G2_b_pin = nsd_block.and(npn_mk).not(npn13G2_b_exc) @@ -71,7 +70,7 @@ npn13G2l_e_exc = activ_mask.join(nsd_block).join(emwihv_drw).join(npn_exclude) npn13G2l_b_exc = npn_b_exc.join(activ_drw).join(nsd_block) # npn13G2L nodes -npn13G2l_e_ = emwind_drw.and(activ_drw).and(heattrans_drw).and(npn_mk).not(npn13G2l_e_exc) +npn13G2l_e_ = emwind_drw.and(activ_drw).and(npn_mk).not(npn13G2l_e_exc) # npn13G2L has fixed width (0.07um), Length could vary from 1:2.5 um npn13G2l_e_pin = npn13G2l_e_.with_bbox_min(0.07.um).with_bbox_max(1.um, 2.5.um).with_area(0.07.um, 0.175.um) npn13G2l_b_pin = activ_mask.and(npn_mk).not(npn13G2l_b_exc) @@ -90,7 +89,7 @@ npn13G2l_te = npn13G2l_e npn13G2v_e_exc = activ_mask.join(nsd_block).join(emwind_drw).join(npn_exclude) # npn13G2V nodes -npn13G2v_e_ = emwihv_drw.and(activ_drw).and(heattrans_drw).and(npn_mk).not(npn13G2v_e_exc) +npn13G2v_e_ = emwihv_drw.and(activ_drw).and(npn_mk).not(npn13G2v_e_exc) # npn13G2L has fixed width (0.12um), Length could vary from 1:5 um npn13G2v_e_pin = npn13G2v_e_.with_bbox_min(0.12.um).with_bbox_max(1.um, 5.um).with_area(0.12.um, 0.6.um) npn13G2v_b_pin = npn13G2l_b_pin @@ -110,7 +109,7 @@ npn13G2v_te = npn13G2v_e logger.info('Starting PNP-BJT DERIVATIONS') -pnp_exclude = heattrans_drw.join(trans_drw).join(emwind_drw) +pnp_exclude = trans_drw.join(emwind_drw) .join(emwihv_drw).join(nsd_block).join(bjt_exclude) pnp_mk = ptap_holes.not(pnp_exclude) diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/cap_derivations.lvs b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/cap_derivations.lvs index c30371fd..cbbbd749 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/cap_derivations.lvs +++ b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/cap_derivations.lvs @@ -23,18 +23,10 @@ logger.info('Starting CAP DERIVATIONS') -cap_exc = nsd_drw.join(heattrans_drw).join(trans_drw) - .join(emwind_drw).join(emwihv_drw).join(heatres_drw) - .join(salblock_drw).join(polyres_drw).join(extblock_drw) - .join(res_drw).join(activ_mask).join(recog_diode) - .join(recog_esd).join(ind_drw).join(ind_pin) - .join(substrate_drw) - -rfmimcap_exc = gatpoly_drw.join(nsd_block).join(psd) - .join(nbulay_drw).join(thickgateox_drw).join(cap_exc) +rfmimcap_exc = ind_drw.join(ind_pin) # === MIMCAP === -mimcap_exclude = rfmimcap_exc.join(pwell_block).join(ptap_holes) +mimcap_exclude = pwell_block.join(rfmimcap_exc) mim_top = mim_drw.overlapping(topmetal1_drw).and(metal5_drw) mim_btm = metal5_drw.and(mim_drw).sized(0.6.um) @@ -55,8 +47,13 @@ rfmim_sub = ptap.extents.interacting(rfmim_area) rfmeas_mk = metal5_drw.overlapping(rfmim_btm).and(rfmim_area) # === svaricap === -varicap_exc = pwell.join(pwell_block).join(nwell_holes) - .join(mim_drw).join(cap_exc) +cap_exc = nsd_drw.join(trans_drw).join(emwind_drw) + .join(emwihv_drw).join(salblock_drw).join(polyres_drw) + .join(extblock_drw).join(res_drw).join(activ_mask) + .join(recog_diode).join(recog_esd).join(ind_drw) + .join(ind_pin).join(substrate_drw) + +varicap_exc = pwell.join(pwell_block).join(nwell_holes).join(cap_exc) varicap_core = ngate_hv_base.and(nwell_iso).not(varicap_exc) varicap_diff_port = nactiv.interacting(varicap_core).not(varicap_core) diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/custom_combiner.lvs b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/custom_combiner.lvs index b5564072..8ef59f44 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/custom_combiner.lvs +++ b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/custom_combiner.lvs @@ -34,7 +34,7 @@ module DeviceCombinerMethods b_net = b.net_for_terminal(name) # same polarity - same_po = a_net.cluster_id == b_net.cluster_id if a_net && b_net + same_po = a_net.expanded_name == b_net.expanded_name if a_net && b_net a_net && b_net && same_po end @@ -45,11 +45,12 @@ module DeviceCombinerMethods b_net1 = b.net_for_terminal(net1) b_net2 = b.net_for_terminal(net2) - if a_net1 && b_net1 && a_net2 && b_net2 - same_po = a_net1.cluster_id == b_net1.cluster_id && a_net2.cluster_id == b_net2.cluster_id - diff_po = a_net1.cluster_id == b_net2.cluster_id && a_net2.cluster_id == b_net1.cluster_id - end - a_net1 && b_net1 && a_net2 && b_net2 && (same_po || diff_po) + return false unless a_net1 && b_net1 && a_net2 && b_net2 + + same_po = a_net1.expanded_name == b_net1.expanded_name && a_net2.expanded_name == b_net2.expanded_name + diff_po = a_net1.expanded_name == b_net2.expanded_name && a_net2.expanded_name == b_net1.expanded_name + + same_po || diff_po end # A helper function to check whether two device connected in series @@ -59,24 +60,30 @@ module DeviceCombinerMethods b_net1 = b.net_for_terminal(net1) b_net2 = b.net_for_terminal(net2) - if a_net1 && b_net1 && a_net2 && b_net2 - series_con1 = a_net1.cluster_id == b_net1.cluster_id || a_net2.cluster_id == b_net2.cluster_id - series_con2 = a_net1.cluster_id == b_net2.cluster_id || a_net2.cluster_id == b_net1.cluster_id - series_con = series_con1 || series_con2 - end + return false unless a_net1 && b_net1 && a_net2 && b_net2 + + series_con_a1 = a_net1.expanded_name == b_net1.expanded_name || a_net1.expanded_name == b_net2.expanded_name + series_con_a2 = a_net2.expanded_name == b_net1.expanded_name || a_net2.expanded_name == b_net2.expanded_name + series_con = series_con_a1 || series_con_a2 # Reroute terminal connections based on cluster IDs - if a_net1.cluster_id == b_net1.cluster_id - a.connect_terminal(0, b_net2) - elsif a_net2.cluster_id == b_net2.cluster_id - a.connect_terminal(1, b_net1) - elsif a_net1.cluster_id == b_net2.cluster_id - a.connect_terminal(0, b_net1) - elsif a_net2.cluster_id == b_net1.cluster_id - a.connect_terminal(1, b_net2) + if series_con_a1 + if a_net1.expanded_name == b_net1.expanded_name + a.connect_terminal(0, b_net2) + else + a.connect_terminal(0, b_net1) + end end - a_net1 && b_net1 && a_net2 && b_net2 && series_con + if series_con_a2 + if a_net2.expanded_name == b_net1.expanded_name + a.connect_terminal(1, b_net2) + else + a.connect_terminal(1, b_net1) + end + end + + series_con end # A helper function to check whether two parameters have approximately the same value @@ -168,12 +175,17 @@ class RESDeviceCombiner < RBA::GenericDeviceCombiner res_nets = [0, 1] # Using id instead of names # Check if same parameters - return false unless %w[w l ps b].all? { |param| same_parameter(a, b, param) } - # Check if terminals series or parallel + # Check if terminals series or parallel (With same params) if supp_parallel(a, b, 0, 1) + return false unless PARALLEL_RES + return false unless %w[w l ps b].all? { |param| same_parameter(a, b, param) } + a.set_parameter('m', a.parameter('m') + b.parameter('m')) elsif supp_series(a, b, 0, 1) + return false unless SERIES_RES + return false unless %w[w ps b m].all? { |param| same_parameter(a, b, param) } + a.set_parameter('l', a.parameter('l') + b.parameter('l')) else return false diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/custom_devices.lvs b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/custom_devices.lvs index 830bcb12..17b1a7f2 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/custom_devices.lvs +++ b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/custom_devices.lvs @@ -58,9 +58,11 @@ class DeviceCustomRes < RBA::DeviceClassResistor add_parameters('w', 'l', 'ps', 'b') add_parameter(RBA::DeviceParameterDefinition.new('m', 'multiplier', 1, true)) - self.combiner = RESDeviceCombiner.new - self.supports_serial_combination=true - self.supports_parallel_combination=true + if SERIES_RES || PARALLEL_RES + self.combiner = RESDeviceCombiner.new + else + self.combiner =nil + end add_terminals(name, num, 0) end diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/custom_extractor.lvs b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/custom_extractor.lvs index 6c7aee2a..98735701 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/custom_extractor.lvs +++ b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/custom_extractor.lvs @@ -161,7 +161,7 @@ class GeneralNTerminalExtractor < RBA::GenericDeviceExtractor # Length length_edges = dev.edges.interacting(width_edges).not(width_edges) - length = get_uniq_length(length_edges) + length, _ = get_min_max_length(length_edges) # Bends corners = meas_mk.interacting(dev).corners.not_interacting(ports).count diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/diode_derivations.lvs b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/diode_derivations.lvs index e19a0137..6006f701 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/diode_derivations.lvs +++ b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/diode_derivations.lvs @@ -23,12 +23,11 @@ logger.info('Starting DIODE DERIVATIONS') -diode_exclude = gatpoly_drw.join(nsd_drw).join(heattrans_drw) +diode_exclude = gatpoly_drw.join(nsd_drw) .join(trans_drw).join(emwind_drw).join(emwihv_drw) - .join(heatres_drw).join(polyres_drw).join(mim_drw) - .join(extblock_drw).join(res_drw).join(activ_mask) - .join(recog_esd).join(ind_drw).join(ind_pin) - .join(substrate_drw) + .join(polyres_drw).join(extblock_drw).join(res_drw) + .join(activ_mask).join(recog_esd).join(ind_drw) + .join(ind_pin).join(substrate_drw) antenna_d_exc = pwell_block.join(nbulay_drw).join(salblock_drw) .join(nsd_block).join(diode_exclude) diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/esd_derivations.lvs b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/esd_derivations.lvs index 5ed60fe3..a2b07c59 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/esd_derivations.lvs +++ b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/esd_derivations.lvs @@ -24,13 +24,11 @@ logger.info('Starting ESD DERIVATIONS') # General -esd_exclude = nsd_block.join(nsd_drw).join(heattrans_drw) - .join(trans_drw).join(emwind_drw).join(emwihv_drw) - .join(heatres_drw).join(polyres_drw).join(mim_drw) +esd_exclude = nsd_block.join(nsd_drw).join(trans_drw) + .join(emwind_drw).join(emwihv_drw).join(polyres_drw) .join(extblock_drw).join(res_drw).join(recog_diode) .join(substrate_drw).join(ind_drw).join(ind_pin) - esd_exc_d = gatpoly_drw.join(thickgateox_drw).join(salblock_drw) .join(esd_exclude) diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/general_derivations.lvs b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/general_derivations.lvs index ebda573c..62e64cf8 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/general_derivations.lvs +++ b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/general_derivations.lvs @@ -32,7 +32,9 @@ nwell_iso = nwell_drw.and(nbulay_drw) nwell_n_iso = nwell_drw.not(nbulay_drw) # pwell -pwell = pwell_drw.join(CHIP.not(nwell_drw.join(pwell_block))) +pwell_pre = pwell_drw.join(CHIP.not(nwell_drw.join(pwell_block).join(digisub_drw))) +digisub_pre = digisub_drw.sized(-1.nm) +pwell = pwell_pre.join(digisub_pre) # psd, nsd active & res psd = psd_drw diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/ind_derivations.lvs b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/ind_derivations.lvs index 020936ac..2375a66b 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/ind_derivations.lvs +++ b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/ind_derivations.lvs @@ -24,11 +24,11 @@ logger.info('Starting Inductor DERIVATIONS') ind_exc = gatpoly_drw.join(pwell).join(nsd_drw) - .join(nbulay_drw).join(thickgateox_drw).join(heattrans_drw) - .join(emwind_drw).join(emwihv_drw).join(heatres_drw) - .join(salblock_drw).join(polyres_drw).join(mim_drw) - .join(extblock_drw).join(res_drw).join(activ_mask) - .join(recog_diode).join(recog_esd).join(substrate_drw) + .join(nbulay_drw).join(thickgateox_drw).join(emwind_drw) + .join(emwihv_drw).join(salblock_drw).join(polyres_drw) + .join(mim_drw).join(extblock_drw).join(res_drw) + .join(activ_mask).join(recog_diode).join(recog_esd) + .join(substrate_drw) # General ind_edges = ind_drw.edges diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/mos_derivations.lvs b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/mos_derivations.lvs index 6baf05ff..82fb42ee 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/mos_derivations.lvs +++ b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/mos_derivations.lvs @@ -24,11 +24,11 @@ logger.info('Starting MOSFET DERIVATIONS') mos_exclude = pwell_block.join(nsd_drw).join(trans_drw) - .join(emwind_drw).join(emwihv_drw).join(heatres_drw) - .join(salblock_drw).join(polyres_drw).join(mim_drw) - .join(extblock_drw).join(res_drw).join(activ_mask) - .join(recog_diode).join(recog_esd).join(ind_drw) - .join(ind_pin).join(ind_drw).join(substrate_drw) + .join(emwind_drw).join(emwihv_drw).join(salblock_drw) + .join(polyres_drw).join(extblock_drw).join(res_drw) + .join(activ_mask).join(recog_diode).join(recog_esd) + .join(ind_drw).join(ind_pin).join(ind_drw) + .join(substrate_drw) # ============== # ---- NMOS ---- @@ -38,11 +38,14 @@ logger.info('Starting NMOS DERIVATIONS') nmos_exclude = nwell_drw.join(psd).join(mos_exclude) +# for regular FETs +nmos_exc = nmos_exclude.join(ptap_holes) + # nmos - LV -ngate_lv = ngate_lv_base.and(heattrans_drw).not(nmos_exclude) +ngate_lv = ngate_lv_base.not(nmos_exc) # nmos - HV -ngate_hv = ngate_hv_base.and(heattrans_drw).not(nmos_exclude) +ngate_hv = ngate_hv_base.not(nmos_exc) # ============== # ---- PMOS ---- @@ -52,8 +55,11 @@ logger.info('Starting PMOS DERIVATIONS') pmos_exclude = pwell.join(nsd_all).join(nwell_holes).join(mos_exclude) +# for regular FETs +pmos_exc = pmos_exclude.join(ntap_holes) + # pmos - LV -pgate_lv = pgate_lv_base.and(heattrans_drw).not(pmos_exclude) +pgate_lv = pgate_lv_base.not(pmos_exc) # pmos - HV -pgate_hv = pgate_hv_base.and(heattrans_drw).not(pmos_exclude) +pgate_hv = pgate_hv_base.not(pmos_exc) diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/res_derivations.lvs b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/res_derivations.lvs index 0d699975..f206065a 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/res_derivations.lvs +++ b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/res_derivations.lvs @@ -24,19 +24,17 @@ logger.info('Starting RESISTOR DERIVATIONS') polyres_exclude = activ_drw.join(pwell_block).join(nsd_block) - .join(nbulay_drw).join(thickgateox_drw).join(heattrans_drw) - .join(trans_drw).join(emwind_drw).join(emwihv_drw) - .join(mim_drw).join(activ_mask).join(recog_diode) - .join(recog_esd).join(ind_drw).join(ind_pin) - .join(substrate_drw) + .join(nbulay_drw).join(thickgateox_drw).join(trans_drw) + .join(emwind_drw).join(emwihv_drw).join(activ_mask) + .join(recog_diode).join(recog_esd).join(ind_drw) + .join(ind_pin).join(substrate_drw) # ============== # ---- POLY ---- # ============== ## polyres -polyres_mk = polyres_drw.and(heatres_drw).and(extblock_drw) - .interacting(gatpoly_drw).not(polyres_exclude) +polyres_mk = polyres_drw.and(extblock_drw).interacting(gatpoly_drw).not(polyres_exclude) ## rhigh rhigh_res = polyres_mk.and(psd_drw).and(nsd_drw).and(salblock_drw) diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/res_extraction.lvs b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/res_extraction.lvs index 104ca555..2d9bb322 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/res_extraction.lvs +++ b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/res_extraction.lvs @@ -34,7 +34,7 @@ extract_devices(GeneralNTerminalExtractor.new('rsil', 2), { 'ports' => rsil_ports, 'meas_mk' => polyres_drw, 'dev_mk' => polyres_drw.interacting(rsil_res), - 'sub_mk' => pwell + 'sub_mk' => pwell.join(nwell_drw) }) # rppd logger.info('Extracting rppd resistor') @@ -43,7 +43,7 @@ extract_devices(GeneralNTerminalExtractor.new('rppd', 2), { 'ports' => rppd_ports, 'meas_mk' => polyres_drw, 'dev_mk' => polyres_drw.interacting(rppd_res), - 'sub_mk' => pwell + 'sub_mk' => pwell.join(nwell_drw) }) # rhigh @@ -53,7 +53,7 @@ extract_devices(GeneralNTerminalExtractor.new('rhigh', 2), { 'ports' => rhigh_ports, 'meas_mk' => polyres_drw, 'dev_mk' => polyres_drw.interacting(rhigh_res), - 'sub_mk' => pwell + 'sub_mk' => pwell.join(nwell_drw) }) # =============== diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/rfmos_derivations.lvs b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/rfmos_derivations.lvs index 28e93b28..c9a56081 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/rfmos_derivations.lvs +++ b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/rfmos_derivations.lvs @@ -27,7 +27,7 @@ logger.info('Starting RF-MOSFET DERIVATIONS') logger.info('Starting RF-NMOS DERIVATIONS') -rfnmos_exclude = nmos_exclude.join(heattrans_drw) +rfnmos_exclude = nmos_exclude # rfnmos - LV rfngate_lv = ngate_lv_base.and(ptap_holes).not(rfnmos_exclude) @@ -41,7 +41,7 @@ rfngate_hv = ngate_hv_base.and(ptap_holes).not(rfnmos_exclude) logger.info('Starting RF-PMOS DERIVATIONS') -rfpmos_exclude = pmos_exclude.join(heattrans_drw) +rfpmos_exclude = pmos_exclude # rfpmos - LV rfpgate_lv = pgate_lv_base.and(ntap_holes).not(rfpmos_exclude) diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/tap_derivations.lvs b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/tap_derivations.lvs index 93f23a6f..a888b25d 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/tap_derivations.lvs +++ b/ihp-sg13g2/libs.tech/klayout/tech/lvs/rule_decks/tap_derivations.lvs @@ -23,10 +23,9 @@ logger.info('Starting Taps DERIVATIONS') -taps_exclude = gatpoly_drw.join(nsd_drw).join(heattrans_drw) - .join(trans_drw).join(emwind_drw).join(emwihv_drw) - .join(salblock_drw).join(heatres_drw).join(polyres_drw) - .join(mim_drw).join(extblock_drw).join(res_drw) +taps_exclude = gatpoly_drw.join(nsd_drw).join(trans_drw) + .join(emwind_drw).join(emwihv_drw).join(salblock_drw) + .join(polyres_drw).join(extblock_drw).join(res_drw) .join(activ_mask).join(recog_diode).join(recog_esd) .join(ind_drw).join(ind_pin) diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/run_lvs.py b/ihp-sg13g2/libs.tech/klayout/tech/lvs/run_lvs.py index 09ca3f66..45ffa091 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/lvs/run_lvs.py +++ b/ihp-sg13g2/libs.tech/klayout/tech/lvs/run_lvs.py @@ -19,31 +19,31 @@ Usage: run_lvs.py (--help| -h) - run_lvs.py (--layout=) (--netlist=) [--thr=] + run_lvs.py (--layout=) (--netlist=) [--run_dir=] [--topcell=] [--run_mode=] [--lvs_sub=] [--no_net_names] [--spice_comments] [--net_only] - [--top_lvl_pins] [--no_simplify] [--combine] [--purge] [--purge_nets] - [--schematic_simplify] [--verbose] + [--no_simplify] [--no_series_res] [--no_parallel_res] [--combine_devices] + [--top_lvl_pins] [--purge] [--purge_nets] [--verbose] Options: - --help -h Print this help message. - --layout= The input GDS file path. - --netlist= The input netlist file path. - --thr= The number of threads used in run. - --run_dir= Run directory to save all the results [default: pwd] - --topcell= Topcell name to use. - --run_mode= Select Allowed klayout mode. (flat, deep). [default: flat] - --lvs_sub= Substrate name used in your design. - --no_net_names Discard net names in extracted netlist. - --spice_comments Enable netlist comments in extracted netlist. - --net_only Enable netlist object creation for extracted netlist. - --top_lvl_pins Enable top level pins only for extracted netlist. - --no_simplify Disable layout simplification for extracted netlist. - --combine Enable netlist combination for extracted netlist. - --purge Enable netlist purge all for extracted netlist. - --purge_nets Enable netlist purge nets for extracted netlist. - --schematic_simplify Enable schematic simplification for input netlist. - --verbose Detailed rule execution log for debugging. + --help -h Displays this help message. + --layout= Specifies the file path of the input GDS file. + --netlist= Specifies the file path of the input netlist file. + --run_dir= Run directory to save all generated results [default: pwd] + --topcell= Specifies the name of the top cell to be used. + --run_mode= Selects allowed KLayout mode. (flat, deep). [default: flat] + --lvs_sub= Sets the substrate name used in your design. + --no_net_names Omits net names in the extracted netlist. + --spice_comments Includes netlist comments in the extracted netlist. + --net_only Generates netlist objects only in the extracted netlist. + --no_simplify Disables simplification for both layout and schematic. + --no_series_res Prevents simplification of series resistors for both layout and schematic. + --no_parallel_res Prevents simplification of parallel resistors for both layout and schematic. + --combine_devices Enables device combination for both layout and schematic netlists. + --top_lvl_pins Creates pins for top-level circuits in both layout and schematic netlists. + --purge Removes unused nets from both layout and schematic netlists. + --purge_nets Purges floating nets from both layout and schematic netlists. + --verbose Enables detailed rule execution logs for debugging purposes. """ from docopt import docopt @@ -191,74 +191,29 @@ def generate_klayout_switches(arguments, layout_path, netlist_path): """ switches = dict() - # No. of threads - thrCount = 2 if arguments["--thr"] is None else int(arguments["--thr"]) - switches["thr"] = str(int(thrCount)) - if arguments["--run_mode"] in ["flat", "deep"]: switches["run_mode"] = arguments["--run_mode"] else: logging.error("Allowed klayout modes are (flat , deep) only") exit() - if arguments["--lvs_sub"]: - switches["lvs_sub"] = arguments["--lvs_sub"] - else: - switches["lvs_sub"] = "sub!" - - if arguments["--no_net_names"]: - switches["spice_net_names"] = "false" - else: - switches["spice_net_names"] = "true" - - if arguments["--spice_comments"]: - switches["spice_comments"] = "true" - else: - switches["spice_comments"] = "false" - - if arguments["--net_only"]: - switches["net_only"] = "true" - else: - switches["net_only"] = "false" - - if arguments["--top_lvl_pins"]: - switches["top_lvl_pins"] = "true" - else: - switches["top_lvl_pins"] = "false" - - if arguments["--no_simplify"]: - switches["netlist_simplify"] = "false" - else: - switches["netlist_simplify"] = "true" - - if arguments["--combine"]: - switches["combine"] = "true" - else: - switches["combine"] = "false" - - if arguments["--purge"]: - switches["purge"] = "true" - else: - switches["purge"] = "false" - - if arguments["--purge_nets"]: - switches["purge_nets"] = "true" - else: - switches["purge_nets"] = "false" - - if arguments["--schematic_simplify"]: - switches["schematic_simplify"] = "true" - else: - switches["schematic_simplify"] = "false" - - if arguments["--verbose"]: - switches["verbose"] = "true" - else: - switches["verbose"] = "false" - - switches["topcell"] = get_run_top_cell_name(arguments, layout_path) - switches["input"] = os.path.abspath(layout_path) - switches["schematic"] = os.path.abspath(netlist_path) + switches = { + "lvs_sub": arguments.get("--lvs_sub") if arguments.get("--lvs_sub") else "sub!", + "no_net_names": "true" if arguments.get("--no_net_names") else "false", + "spice_comments": "true" if arguments.get("--spice_comments") else "false", + "net_only": "true" if arguments.get("--net_only") else "false", + "top_lvl_pins": "true" if arguments.get("--top_lvl_pins") else "false", + "no_simplify": "true" if arguments.get("--no_simplify") else "false", + "no_series_res": "true" if arguments.get("--no_series_res") else "false", + "no_parallel_res": "true" if arguments.get("--no_parallel_res") else "false", + "combine_devices": "true" if arguments.get("--combine_devices") else "false", + "purge": "true" if arguments.get("--purge") else "false", + "purge_nets": "true" if arguments.get("--purge_nets") else "false", + "verbose": "true" if arguments.get("--verbose") else "false", + "topcell": get_run_top_cell_name(arguments, layout_path), + "input": os.path.abspath(layout_path), + "schematic": os.path.abspath(netlist_path) + } return switches diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/sg13g2.lvs b/ihp-sg13g2/libs.tech/klayout/tech/lvs/sg13g2.lvs index c1e16def..83e3a6dc 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/lvs/sg13g2.lvs +++ b/ihp-sg13g2/libs.tech/klayout/tech/lvs/sg13g2.lvs @@ -77,7 +77,7 @@ logger.info("Substrate name used: #{$lvs_sub}") #=== NET NAMES OPTION === # true: use net names instead of numbers # false: use numbers for nets -SPICE_WITH_NET_NAMES = bool_check?($spice_net_names) +SPICE_WITH_NET_NAMES = !bool_check?($no_net_names) logger.info("Extracted netlist with net names: #{SPICE_WITH_NET_NAMES}") @@ -88,11 +88,6 @@ SPICE_WITH_COMMENTS = bool_check?($spice_comments) logger.info("Extracted netlist with comments in details: #{SPICE_WITH_COMMENTS}") -# SCH_SIMPLE -SCH_SIMPLE = bool_check?($schematic_simplify) - -logger.info("Selected SCH_SIMPLE option: #{SCH_SIMPLE}") - # NET_ONLY NET_ONLY = bool_check?($net_only) @@ -104,9 +99,9 @@ TOP_LVL_PINS = bool_check?($top_lvl_pins) logger.info("Selected TOP_LVL_PINS option: #{TOP_LVL_PINS}") # COMBINE -COMBINE = bool_check?($combine) +COMBINE_DEVICES = bool_check?($combine_devices) -logger.info("Selected COMBINE option: #{COMBINE}") +logger.info("Selected COMBINE DEVICES option: #{COMBINE_DEVICES}") # PURGE PURGE = bool_check?($purge) @@ -119,9 +114,19 @@ PURGE_NETS = bool_check?($purge_nets) logger.info("Selected PURGE_NETS option: #{PURGE_NETS}") # SIMPLIFY -NETLIST_SIMPLIFY = bool_check?($netlist_simplify) +SIMPLIFY = !bool_check?($no_simplify) + +logger.info("Selected SIMPLIFY option: #{SIMPLIFY}") + +# SIMPLIFY +SERIES_RES = !bool_check?($no_series_res) -logger.info("Selected NETLIST_SIMPLIFY option: #{NETLIST_SIMPLIFY}") +logger.info("Selected SERIES_RES option: #{SERIES_RES}") + +# SIMPLIFY +PARALLEL_RES = !bool_check?($no_parallel_res) + +logger.info("Selected PARALLEL_RES option: #{PARALLEL_RES}") #=== PRINT DETAILS === logger.info("Verbose mode: #{$verbose}") @@ -133,11 +138,6 @@ end # === TILING MODE === case $run_mode -when 'tiling' - tiles(500.um) - tile_borders(10.um) - logger.info('Tiling mode is enabled.') - when 'deep' #=== HIER MODE === deep @@ -150,7 +150,6 @@ end # === Tech Switches === - #================================================ # --------------- CUSTOM CLASSES ---------------- #================================================ @@ -161,24 +160,27 @@ end reader = RBA::NetlistSpiceReader.new(CustomReader.new) #=== GET NETLIST === -if $schematic - schematic($schematic, reader) - logger.info("Netlist file: #{$schematic}") -else - exts = ["spice", "cdl", "cir"] - candidates = exts.map{|ext| "#{source.cell_name}.#{ext}"} - netlists = candidates.select{|f| File.exist?(f)} - if netlists.empty? - error("Netlist not found, tried: #{candidates}") +unless NET_ONLY + if $schematic + schematic($schematic, reader) + logger.info("Netlist file: #{$schematic}") else - schematic(netlists[0], reader) - logger.info("Netlist file: #{netlists[0]}") + exts = %w[spice cdl cir] + candidates = exts.map { |ext| "#{source.cell_name}.#{ext}" } + netlists = candidates.select { |f| File.exist?(f) } + if netlists.empty? + error("Netlist not found, tried: #{candidates}") + else + schematic(netlists[0], reader) + logger.info("Netlist file: #{netlists[0]}") + end end end # Instantiate a writer using the new delegate custom_spice_writer = RBA::NetlistSpiceWriter.new(CustomWriter.new) custom_spice_writer.use_net_names = SPICE_WITH_NET_NAMES +custom_spice_writer.with_comments = SPICE_WITH_COMMENTS if $target_netlist logger.info("LVS extracted netlist at: #{$target_netlist}") @@ -336,36 +338,46 @@ logger.info('Starting SG13G2 LVS DEVICES EXTRACTION') # %include rule_decks/tap_extraction.lvs -#================================================ -#------------- COMPARISON OPTIONS --------------- -#================================================ +#================================================== +# ------------ COMPARISON PREPARATIONS ------------ +#================================================== -logger.info('Starting SG13G2 LVS comparison section') +logger.info('Starting SG13G2 LVS Options Preparations') -#=== FLATTEN CELLS === -align +# === Extract Netlist Only === +netlist if NET_ONLY +return if NET_ONLY -#=== NETLIST EXTRACTION === -netlist.simplify if NETLIST_SIMPLIFY +# === Aligns the extracted netlist vs. the schematic === +align #=== NETLIST OPTIONS === -netlist if NET_ONLY +# SIMPLIFY +netlist.simplify if SIMPLIFY +schematic.simplify if SIMPLIFY +# TOP_LVL_PINS netlist.make_top_level_pins if TOP_LVL_PINS +schematic.make_top_level_pins if TOP_LVL_PINS -netlist.combine_devices if COMBINE +# COMBINE_DEVICES +netlist.combine_devices if COMBINE_DEVICES +schematic.combine_devices if COMBINE_DEVICES +# PURGE netlist.purge if PURGE +schematic.purge if PURGE +# PURGE_NETS netlist.purge_nets if PURGE_NETS - -#=== SCHEMATIC OPTIONS === -schematic.simplify if SCH_SIMPLE +schematic.purge_nets if PURGE_NETS #=== IGNORE EXTREME VALUES === max_res(1e9) min_caps(1e-18) +# === COMPARISON === +logger.info('Starting SG13G2 LVS Comparison') compare #================================================ diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/testing/run_regression.py b/ihp-sg13g2/libs.tech/klayout/tech/lvs/testing/run_regression.py index cab547cb..45dfcf85 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/lvs/testing/run_regression.py +++ b/ihp-sg13g2/libs.tech/klayout/tech/lvs/testing/run_regression.py @@ -51,7 +51,7 @@ def check_klayout_version(): """ - check_klayout_version checks klayout version and makes sure it would work with the DRC. + Check klayout version and makes sure it would work with the LVS. """ # ======= Checking Klayout version ======= klayout_v_ = os.popen("klayout -b -v").read() @@ -69,20 +69,20 @@ def check_klayout_version(): exit(1) elif len(klayout_v_list) >= 2 or len(klayout_v_list) <= 3: if klayout_v_list[1] < 28 or ( - klayout_v_list[1] == 28 and klayout_v_list[2] <= 3 + klayout_v_list[1] == 28 and klayout_v_list[2] <= 13 ): - logging.error("Prerequisites at a minimum: KLayout 0.28.4") + logging.error("Prerequisites at a minimum: KLayout 0.28.14") logging.error( - "Using this klayout version is not supported in this development." + "Using this klayout version has not been assessed. Limits are unknown" ) - exit(1) + # exit(1) logging.info(f"Your Klayout version is: {klayout_v_}") def parse_existing_devices(rule_deck_path, output_path, target_device_group=None): """ - This function collects the rule names from the existing drc rule decks. + This function collects the rule names from the existing lvs rule decks. Parameters ---------- @@ -229,7 +229,7 @@ def run_test_case( device_name, ): """ - This function run a single test case using the correct DRC file. + This function run a single test case using the correct LVS file. Parameters ---------- @@ -286,7 +286,7 @@ def run_test_case( if len(pattern_results) < 1: logging.error("%s generated an exception: %s" % (pattern_clean, e)) traceback.print_exc() - raise Exception("Failed DRC run.") + raise Exception("Failed LVS run.") # dumping log into output to make CI have the log if os.path.isfile(pattern_log): diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/testing/testcases/unit/res_devices/layout/rhigh.gds b/ihp-sg13g2/libs.tech/klayout/tech/lvs/testing/testcases/unit/res_devices/layout/rhigh.gds index 216d4758..8e6cb6f8 100644 Binary files a/ihp-sg13g2/libs.tech/klayout/tech/lvs/testing/testcases/unit/res_devices/layout/rhigh.gds and b/ihp-sg13g2/libs.tech/klayout/tech/lvs/testing/testcases/unit/res_devices/layout/rhigh.gds differ diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/testing/testcases/unit/res_devices/layout/rsil.gds b/ihp-sg13g2/libs.tech/klayout/tech/lvs/testing/testcases/unit/res_devices/layout/rsil.gds index 60cc6e04..3792ac65 100644 Binary files a/ihp-sg13g2/libs.tech/klayout/tech/lvs/testing/testcases/unit/res_devices/layout/rsil.gds and b/ihp-sg13g2/libs.tech/klayout/tech/lvs/testing/testcases/unit/res_devices/layout/rsil.gds differ diff --git a/ihp-sg13g2/libs.tech/klayout/tech/lvs/testing/testcases/unit/res_devices/netlist/rhigh.cdl b/ihp-sg13g2/libs.tech/klayout/tech/lvs/testing/testcases/unit/res_devices/netlist/rhigh.cdl index 236bb3d3..8c26874d 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/lvs/testing/testcases/unit/res_devices/netlist/rhigh.cdl +++ b/ihp-sg13g2/libs.tech/klayout/tech/lvs/testing/testcases/unit/res_devices/netlist/rhigh.cdl @@ -21,4 +21,14 @@ Rh2 net3 net4 rhigh m=1 l=1u w=0.5u Rh3 net5 net6 rhigh m=1 l=1.2u w=0.7u Rh4 net7 net8 rhigh m=1 l=1u w=0.5u ps=0.2u b=1 Rh5 net9 net10 rhigh m=1 l=1.2u w=0.5u ps=0.2u b=2 +** Testing combiner +* res-A +R1 p_split p1 rhigh w=0.5e-6 l=30e-6 m=1 b=0 +R2 p2 p_split rhigh w=0.5e-6 l=10e-6 m=1 b=0 +* res-B +R3 p3 p4 rhigh w=0.5e-6 l=40e-6 m=1 b=0 +* res-C with FET +R1 p5 p_intern rhigh w=0.5e-6 l=30e-6 m=1 b=0 +R2 p_intern p6 rhigh w=0.5e-6 l=10e-6 m=1 b=0 +M1 p_intern G S B sg13_lv_nmos w=0.15e-6 l=0.2e-6 m=1 ng=1 .ENDS diff --git a/ihp-sg13g2/libs.tech/klayout/tech/macros/lvs_options.yml b/ihp-sg13g2/libs.tech/klayout/tech/macros/lvs_options.yml index 351276b1..1e9c0e2d 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/macros/lvs_options.yml +++ b/ihp-sg13g2/libs.tech/klayout/tech/macros/lvs_options.yml @@ -1,15 +1,16 @@ --- -sub_name: sub! -run_mode: flat netlist: '' -spice_net: true -top_cell_name: '' -spice_comment: false -netlist_simplify: true +top_cell: '' +run_mode: flat +sub_name: sub! +no_net_names: false +spice_comments: false net_only: false +no_simplify: false +no_series_res: false +no_parallel_res: false +combine_devices: false top_lvl_pins: false -combine: false purge: false purge_nets: false -schematic_simplify: true verbose: false diff --git a/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_lvs.lylvs b/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_lvs.lylvs index 0a4a850e..4e2c5d9b 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_lvs.lylvs +++ b/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_lvs.lylvs @@ -26,9 +26,10 @@ false false + 0 true - + Run Klayout LVS sg13g2_menu>end("SG13G2 PDK").end dsl lvs-dsl-xml @@ -36,69 +37,98 @@ require 'yaml' -## reading the parent directory of the current file +# Load LVS options from a YAML file +# +# yaml_file_path - The path to the YAML file +# +# Returns a hash containing the loaded options +def load_options(yaml_file_path) + options = {} + if File.exist?(yaml_file_path) + begin + options = YAML.safe_load(File.read(yaml_file_path)) + rescue => e + puts "Error loading options from YAML file: #{e.message}" + end + else + puts "YAML file not found at #{yaml_file_path}. Using default options." + options = set_default_options(yaml_file_path) + end + options +end + +# Set default LVS options and save them to a YAML file +# +# yaml_file_path - The path to the YAML file +# +# Returns a hash containing the default options +def set_default_options(yaml_file_path) + default_options = { + 'netlist' => '', + 'top_cell' => '', + 'run_mode' => 'flat', + 'sub_name' => 'sub!', + 'no_net_names' => false, + 'spice_comments' => false, + 'net_only' => false, + 'no_simplify' => false, + 'no_series_res' => false, + 'no_parallel_res' => false, + 'combine_devices' => false, + 'top_lvl_pins' => false, + 'purge' => false, + 'purge_nets' => false, + 'verbose' => false, + } + default_options +end + dir_path = File.dirname(File.expand_path(__FILE__)) -run_dir = File.expand_path("..", dir_path) ## reading the loaded gds file path layout_path = Pathname.new(RBA::CellView.active.filename) -## read saved options from yaml file -options = YAML.load(File.read(dir_path + "/lvs_options.yml")) +options = {} +yaml_file_path = "#{dir_path}/lvs_options.yml" +# Load options from YAML file +options = load_options(yaml_file_path) ## reading netlist option to get netlist_path -if options["netlist"] == "" - net_name = layout_path.split()[1].to_s().split(".")[0] - - net_dir = "#{layout_path.split()[0]}/#{net_name}.cdl" - unless File.exist?net_dir - net_dir = "#{layout_path.split()[0]}/#{net_name}.spice" - end - unless File.exist?net_dir - net_dir = "#{layout_path.split()[0]}/#{net_name}.cir" - end -else - net_dir = options["netlist"] +if options['netlist'] == '' + net_name = layout_path.split[1].to_s.split('.')[0] + + net_dir = "#{layout_path.split[0]}/#{net_name}.cir" + net_dir = "#{layout_path.split[0]}/#{net_name}.spice" unless File.exist? net_dir + net_dir = "#{layout_path.split[0]}/#{net_name}.cdl" unless File.exist? net_dir +else + net_dir = options['netlist'] end -unless File.exist?net_dir - STDERR.puts "The netlist file used #{net_dir} doesn't exist, please recheck" - exit +if !options["net_only"] + unless File.exist?(net_dir) + raise StandardError, "The netlist path #{net_dir} doesn't exist, please recheck" + end end -## read the log of lvs run to get output lydb file paths if run_dir option is disables -gds_name = layout_path.split()[1].to_s.split(".")[0] +# input layout +$input = (layout_path.realpath).to_s ## passing options to lvs run file $schematic = net_dir - -$lvs_sub = options["sub_name"] - -$run_mode = options["run_mode"] - -unless options["top_cell_name"] == "" - $topcell = options["top_cell_name"] -end - -$spice_net_names = options["spice_net"] - -$spice_comments = options["spice_comment"] - +$topcell = options['top_cell'] +$run_mode = options['run_mode'] +$lvs_sub = options['sub_name'] +$no_net_names = options['no_net_names'] +$spice_comments = options['spice_comments'] $net_only = options["net_only"] - -$top_lvl_pins = options["top_lvl_pins"] - -$netlist_simplify = options["netlist_simplify"] - -$combine = options["combine"] - -$purge = options["purge"] - -$purge_nets = options["purge_nets"] - -$schematic_simplify = options["schematic_simplify"] - -$verbose = options["verbose"] +$no_simplify = options['no_simplify'] +$no_series_res = options['no_series_res'] +$no_parallel_res = options['no_parallel_res'] +$combine_devices = options['combine_devices'] +$top_lvl_pins = options['top_lvl_pins'] +$purge = options['purge'] +$purge_nets = options['purge_nets'] +$verbose = options['verbose'] ## include lvs run file #%include ../lvs/sg13g2.lvs diff --git a/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_options.lym b/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_options.lym index 6f98c5b1..add19873 100644 --- a/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_options.lym +++ b/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_options.lym @@ -1,32 +1,26 @@ - Load PDK options - - + SG13G2 LVS Options + 0.1 + lvs @@ -35,304 +29,267 @@ 0 true - Load PDK options + SG13G2 LVS Options sg13g2_menu>end("SG13G2 PDK").end ruby - module MyMacro -require 'yaml' - + + module SG13G2LVSOptions + require 'yaml' include RBA - # ===================================================================== - # -------------------------- Loading Options -------------------------- - # ===================================================================== - - mw = RBA::MainWindow::instance - - # create a toolbar sg13g2_menu - mw.menu.insert_menu("sg13g2_menu.end", "lvs_menu", "Klayout LVS Options") - mw.menu.insert_separator("sg13g2_menu.lvs_menu+", "name2") + # Main function to manage LVS options + def self.main + # Construct the absolute path to the YAML file + yaml_file_path = "#{__dir__}/lvs_options.yml" - # ===================================================================== - # ------------------------ Adding LVS Options ------------------------- - # ===================================================================== + # Load options from YAML file + options = load_options(yaml_file_path) - # Adding substrate name - run_action = RBA::Action::new - run_action.title = "Substrate name" - run_action.on_triggered do - sub_name = RBA::InputDialog::ask_string("Substrate name", "Please enter substrate name", "sub!") - options = YAML.load(File.read(__dir__ + "/lvs_options.yml")) - if sub_name - options["sub_name"] = sub_name - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml) } - end - - end - mw.menu.insert_item("sg13g2_menu.lvs_menu.end", "SUB_name", run_action) - - # Setting run mode - run_mode_action = RBA::Action::new - run_mode_action.title = "Run mode" - run_mode_action.on_triggered do + # Create a dialog box for selecting options + dialog = create_options_dialog(options, yaml_file_path) - options = YAML.load(File.read(__dir__ + "/lvs_options.yml")) - run_mode_select = RBA::InputDialog::ask_item("Run mode", "Select run mode:", ["deep", "flat", "tiling"], 1) - if run_mode_select - options["run_mode"] = run_mode_select - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml) } - end - - end - mw.menu.insert_item("sg13g2_menu.lvs_menu.end", "run_mode", run_mode_action) + # Show the dialog box and handle user input + if dialog.exec == 1 #RBA::QDialog::Accepted + # Get values from the dialog box + update_options_from_dialog(options, dialog) - # Adding netlist path - netlist_action = RBA::Action::new - netlist_action.title = "Netlist Path" - netlist_action.on_triggered do - file_filter = "CDL Files (*.cdl);;SPICE Files (*.spice);;Circuit Files (*.cir)" - netlist_path = RBA::FileDialog::get_open_file_name("Select File", ".", file_filter) - netlist_path = netlist_path.to_s - options = YAML.load(File.read(__dir__ + "/lvs_options.yml")) - if netlist_path - options["netlist"] = netlist_path - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml) } - end - - end - mw.menu.insert_item("sg13g2_menu.lvs_menu.end", "Netlist_path", netlist_action) - - #4 Adding Top cell name - top_cell_action = RBA::Action::new - top_cell_action.title = "Top_cell name" - top_cell_action.on_triggered do - top_cell_name = RBA::InputDialog::ask_string("Top cell name", "Please enter Top_cell name", "TOP") - options = YAML.load(File.read(__dir__ + "/lvs_options.yml")) - if top_cell_name - options["top_cell_name"] = top_cell_name - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml) } - end - - end - mw.menu.insert_item("sg13g2_menu.lvs_menu.end", "TOP_cell_name", top_cell_action) - - # Setting spice net names - spice_net_action = RBA::Action::new - spice_net_action.title = "SPICE net name" - spice_net_action.checkable=(true) - spice_net_action.checked=(true) - spice_net_action.on_triggered do - - options = YAML.load(File.read(__dir__ + "/lvs_options.yml")) - if spice_net_action.is_checked? - options["spice_net"] = true - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml) } - else - options["spice_net"] = false - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml) } + # Save options to YAML file + if save_options(yaml_file_path, options) + puts "Options saved successfully." + else + puts "Failed to save options." end - + end end - mw.menu.insert_item("sg13g2_menu.lvs_menu.end", "SPICE_net_name", spice_net_action) - - # Setting spice comments - spice_comment_action = RBA::Action::new - spice_comment_action.title = "SPICE comments" - spice_comment_action.checkable=(true) - spice_comment_action.checked=(false) - spice_comment_action.on_triggered do - - options = YAML.load(File.read(__dir__ + "/lvs_options.yml")) - if spice_comment_action.is_checked? - options["spice_comment"] = true - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml) } - else - options["spice_comment"] = false - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml) } + + # Load LVS options from a YAML file + # + # yaml_file_path - The path to the YAML file + # + # Returns a hash containing the loaded options + def self.load_options(yaml_file_path) + options = {} + if File.exist?(yaml_file_path) + begin + options = YAML.safe_load(File.read(yaml_file_path)) + rescue => e + puts "Error loading options from YAML file: #{e.message}" end - + else + puts "YAML file not found at #{yaml_file_path}. Using default options." + options = set_default_options(yaml_file_path) + end + options end - mw.menu.insert_item("sg13g2_menu.lvs_menu.end", "SPICE_comments", spice_comment_action) - - # Setting netlist only - net_only_action = RBA::Action::new - net_only_action.title = "Netlist only" - net_only_action.checkable=(true) - net_only_action.checked=(false) - net_only_action.on_triggered do - - options = YAML.load(File.read(__dir__ + "/lvs_options.yml")) - if net_only_action.is_checked? - options["net_only"] = true - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml)} - else - options["net_only"] = false - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml)} - end - + + # Set default LVS options and save them to a YAML file + # + # yaml_file_path - The path to the YAML file + # + # Returns a hash containing the default options + def self.set_default_options(yaml_file_path) + default_options = { + 'netlist' => '', + 'top_cell' => '', + 'run_mode' => 'flat', + 'sub_name' => 'sub!', + 'no_net_names' => false, + 'spice_comments' => false, + 'net_only' => false, + 'no_simplify' => false, + 'no_series_res' => false, + 'no_parallel_res' => false, + 'combine_devices' => false, + 'top_lvl_pins' => false, + 'purge' => false, + 'purge_nets' => false, + 'verbose' => false, + } + save_options(yaml_file_path, default_options) + default_options end - mw.menu.insert_item("sg13g2_menu.lvs_menu.end", "net_only", net_only_action) - # Setting top level pins - top_lvl_pins_action = RBA::Action::new - top_lvl_pins_action.title = "Top level pins" - top_lvl_pins_action.checkable=(true) - top_lvl_pins_action.checked=(false) - top_lvl_pins_action.on_triggered do - - options = YAML.load(File.read(__dir__ + "/lvs_options.yml")) - if top_lvl_pins_action.is_checked? - options["top_lvl_pins"] = true - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml)} - else - options["top_lvl_pins"] = false - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml)} - end - + # Get all names for top cells + # + # Returns a list contains all top cell names + def self.get_top_cell_names + top_cells = [] + layout = RBA::Layout.new + layout.read(Pathname.new(RBA::CellView.active.filename)) + + layout.top_cells.each do |cell| + top_cells.push(cell.name) + end + top_cells end - mw.menu.insert_item("sg13g2_menu.lvs_menu.end", "top_lvl_pins", top_lvl_pins_action) - # Setting Netlist simplification - netlist_simplify_action = RBA::Action::new - netlist_simplify_action.title = "Netlist simplify" - netlist_simplify_action.checkable=(true) - netlist_simplify_action.checked=(true) - netlist_simplify_action.on_triggered do + # Create a dialog box for selecting LVS options + # + # options - A hash containing the current options + # yaml_file_path - The path to the YAML file + # + # Returns a QDialog instance representing the options dialog + def self.create_options_dialog(options, yaml_file_path) + dialog = RBA::QDialog.new + dialog.windowTitle = "LVS Options" - options = YAML.load(File.read(__dir__ + "/lvs_options.yml")) - if netlist_simplify_action.is_checked? - options["netlist_simplify"] = true - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml) } - else - options["netlist_simplify"] = false - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml) } - end + layout = RBA::QFormLayout.new(dialog) + + layout.addRow('Netlist Path:', create_file_input(options['netlist'], dialog)) + layout.addRow('Top Cell', create_combo_box(options['top_cell'], get_top_cell_names)) + layout.addRow('Run Mode:', create_combo_box(options['run_mode'], ['deep', 'flat'])) + layout.addRow('Substrate Name:', create_line_edit(options['sub_name'])) + layout.addRow('', create_check_box('No Net Names', options['no_net_names'])) + layout.addRow('', create_check_box('Spice Comments', options['spice_comments'])) + layout.addRow('', create_check_box('Netlist Only', options['net_only'])) + layout.addRow('', create_check_box('No Simplify', options['no_simplify'])) + layout.addRow('', create_check_box('No Series Resistance', options['no_series_res'])) + layout.addRow('', create_check_box('No Parallel Resistance', options['no_parallel_res'])) + layout.addRow('', create_check_box('Combine Devices', options['combine_devices'])) + layout.addRow('', create_check_box('Top Level Pins', options['top_lvl_pins'])) + layout.addRow('', create_check_box('Purge', options['purge'])) + layout.addRow('', create_check_box('Purge Nets', options['purge_nets'])) + layout.addRow('', create_check_box('Verbose', options['verbose'])) + layout.addRow('', create_buttons(dialog, yaml_file_path)) + + dialog end - mw.menu.insert_item("sg13g2_menu.lvs_menu.end", "netlist_simplify", netlist_simplify_action) - # Setting device combination - combine_action = RBA::Action::new - combine_action.title = "Devices combine" - combine_action.checkable=(true) - combine_action.checked=(false) - combine_action.on_triggered do - - options = YAML.load(File.read(__dir__ + "/lvs_options.yml")) - if combine_action.is_checked? - options["combine"] = true - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml) } - else - options["combine"] = false - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml) } - end - + # Create a line edit widget + # + # text - The initial text for the line edit + # + # Returns a QLineEdit instance + def self.create_line_edit(text) + line_edit = RBA::QLineEdit.new + line_edit.text = text + line_edit end - mw.menu.insert_item("sg13g2_menu.lvs_menu.end", "combine", combine_action) - - # Setting purge - purge_action = RBA::Action::new - purge_action.title = "Purge" - purge_action.checkable=(true) - purge_action.checked=(false) - purge_action.on_triggered do + + # Create a combo box widget + # + # current_index - The index of the currently selected item + # items - An array containing the items for the combo box + # + # Returns a QComboBox instance + def self.create_combo_box(current_index, items) + combo_box = RBA::QComboBox.new + items.each { |item| combo_box.addItem(item) } + combo_box.currentIndex = items.index(current_index) + combo_box + end + + # Create a file input widget + # + # text - The initial text for the file input + # parent - The parent widget for the file input + # + # Returns a QWidget instance containing the file input + def self.create_file_input(text, parent) + file_input = RBA::QLineEdit.new + file_input.text = text - options = YAML.load(File.read(__dir__ + "/lvs_options.yml")) - - if purge_action.is_checked? - options["purge"] = true - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml) } - else - options["purge"] = false - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml) } - end + browse_button = RBA::QPushButton.new('Browse') + browse_button.clicked.connect(Proc.new { + file_filter = 'CDL Files (*.cdl);;SPICE Files (*.spice);;Circuit Files (*.cir)' + netlist_path = RBA::FileDialog.get_open_file_name('Select File', '.', file_filter) + file_input.text = netlist_path.to_s + }) + + container = RBA::QWidget.new + layout = RBA::QHBoxLayout.new(container) + layout.addWidget(file_input) + layout.addWidget(browse_button) + container end - mw.menu.insert_item("sg13g2_menu.lvs_menu.end", "purge", purge_action) - # Setting purge nets - purge_nets_action = RBA::Action::new - purge_nets_action.title = "Purge nets" - purge_nets_action.checkable=(true) - purge_nets_action.checked=(false) - purge_nets_action.on_triggered do - - options = YAML.load(File.read(__dir__ + "/lvs_options.yml")) - if purge_nets_action.is_checked? - options["purge_nets"] = true - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml) } - else - options["purge_nets"] = false - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml) } - end - + # Create a check box widget + # + # text - The text for the check box + # checked - A boolean indicating whether the check box is checked + # + # Returns a QCheckBox instance + def self.create_check_box(text, checked) + check_box = RBA::QCheckBox.new(text) + check_box.checked = checked + check_box end - mw.menu.insert_item("sg13g2_menu.lvs_menu.end", "purge_nets", purge_nets_action) + + # Create buttons for the options dialog + # + # dialog - The options dialog + # yaml_file_path - The path to the YAML file + # + # Returns a QWidget instance containing the buttons + def self.create_buttons(dialog, yaml_file_path) + container = RBA::QWidget.new + layout = RBA::QHBoxLayout.new(container) - # Setting schematic simplification - schematic_simplify_action = RBA::Action::new - schematic_simplify_action.title = "Schematic simplify" - schematic_simplify_action.checkable=(true) - schematic_simplify_action.checked=(false) - schematic_simplify_action.on_triggered do - - options = YAML.load(File.read(__dir__ + "/lvs_options.yml")) - if schematic_simplify_action.is_checked? - options["schematic_simplify"] = true - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml) } - else - options["schematic_simplify"] = false - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml) } + ok_button = RBA::QPushButton.new('OK') + ok_button.clicked.connect(Proc.new { dialog.accept }) + + reset_button = RBA::QPushButton.new('Reset') + reset_button.clicked.connect(Proc.new { + if set_default_options(yaml_file_path) + dialog.reject + else + puts "Failed to reset options." end - - end - mw.menu.insert_item("sg13g2_menu.lvs_menu.end", "schematic_simplify", schematic_simplify_action) + }) - # Setting verbose mode - verbose_action = RBA::Action::new - verbose_action.title = "Verbose mode" - verbose_action.checkable=(true) - verbose_action.checked=(false) - verbose_action.on_triggered do + cancel_button = RBA::QPushButton.new('Cancel') + cancel_button.clicked.connect(Proc.new { dialog.reject }) - options = YAML.load(File.read(__dir__ + "/lvs_options.yml")) - if verbose_action.is_checked? - options["verbose"] = true - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml) } - else - options["verbose"] = false - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(options.to_yaml) } - end - + layout.addWidget(ok_button) + layout.addWidget(reset_button) + layout.addWidget(cancel_button) + + container end - mw.menu.insert_item("sg13g2_menu.lvs_menu.end", "verbose", verbose_action) - - # ================================= - # -- Return to defaults on closing - # ================================= - - mw.on_view_closed do - ## write default options - lvs_options = {"sub_name"=>"sub!", - "run_mode"=>"flat", - "netlist"=>"" , - "spice_net"=>true, - "top_cell_name"=>"", - "spice_comment"=>false, - "netlist_simplify"=>true, - "net_only"=>false, - "top_lvl_pins"=>false, - "combine"=>false, - "purge"=>false, - "purge_nets"=>false, - "schematic_simplify"=>true, - "verbose"=>false, - } - File.open(__dir__ + "/lvs_options.yml", "w") { |file| file.write(lvs_options.to_yaml) } + + # Update options hash from the dialog + # + # options - The options hash to be updated + # dialog - The dialog containing the updated options + def self.update_options_from_dialog(options, dialog) + options['netlist'] = dialog.layout.itemAt(1).widget.layout.itemAt(0).widget.text + options['top_cell'] = dialog.layout.itemAt(3).widget.currentText + options['run_mode'] = dialog.layout.itemAt(5).widget.currentText + options['sub_name'] = dialog.layout.itemAt(7).widget.text + options['no_net_names'] = dialog.layout.itemAt(8).widget.checked + options['spice_comments'] = dialog.layout.itemAt(9).widget.checked + options['net_only'] = dialog.layout.itemAt(10).widget.checked + options['no_simplify'] = dialog.layout.itemAt(11).widget.checked + options['no_series_res'] = dialog.layout.itemAt(12).widget.checked + options['no_parallel_res'] = dialog.layout.itemAt(13).widget.checked + options['combine_devices'] = dialog.layout.itemAt(14).widget.checked + options['top_lvl_pins'] = dialog.layout.itemAt(15).widget.checked + options['purge'] = dialog.layout.itemAt(16).widget.checked + options['purge_nets'] = dialog.layout.itemAt(17).widget.checked + options['verbose'] = dialog.layout.itemAt(18).widget.checked end + # Save options to a YAML file + # + # yaml_file_path - The path to the YAML file + # options - The options hash to be saved + # + # Returns true if successful, false otherwise + def self.save_options(yaml_file_path, options) + begin + File.open(yaml_file_path, 'w') { |file| file.write(options.to_yaml) } + true + rescue => e + puts "Error saving options to YAML file: #{e.message}" + false + end + end end +SG13G2LVSOptions.main +