librelane 3.1.0.dev1__tar.gz → 3.1.0.dev3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/PKG-INFO +3 -3
  2. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/__main__.py +1 -1
  3. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/common/metrics/__main__.py +16 -143
  4. librelane-3.1.0.dev3/librelane/common/metrics/compare.py +156 -0
  5. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/config/config.py +14 -4
  6. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/config/flow.py +6 -0
  7. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/config/pdk_compat.py +2 -2
  8. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/config/preprocessor.py +20 -13
  9. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/config/variable.py +89 -0
  10. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/examples/spm/src/impl.sdc +2 -2
  11. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/examples/spm/src/signoff.sdc +2 -2
  12. librelane-3.1.0.dev3/librelane/pdk_hashes.yaml +3 -0
  13. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/base.sdc +2 -2
  14. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/magic/def/antenna_check.tcl +3 -0
  15. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/magic/def/mag.tcl +4 -1
  16. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/magic/def/mag_gds.tcl +1 -0
  17. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/magic/drc.tcl +1 -0
  18. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/magic/extract_spice.tcl +3 -0
  19. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/magic/gds/drc_batch.tcl +3 -0
  20. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/magic/gds/erase_box.tcl +4 -0
  21. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/magic/gds/extras_mag.tcl +2 -0
  22. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/magic/gds/mag_with_pointers.tcl +2 -0
  23. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/magic/get_bbox.tcl +4 -0
  24. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/magic/lef/extras_maglef.tcl +2 -0
  25. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/magic/lef/maglef.tcl +2 -0
  26. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/magic/lef.tcl +3 -0
  27. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/magic/spice_rcx.tcl +4 -0
  28. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/power_utils.py +19 -11
  29. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/common/io.tcl +7 -3
  30. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/common/pad_cfg.tcl +25 -11
  31. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/drt.tcl +16 -1
  32. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/gpl.tcl +6 -0
  33. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/repair_design.tcl +5 -2
  34. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/pyosys/construct_abc_script.py +5 -5
  35. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/pyosys/json_header.py +5 -0
  36. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/pyosys/synthesize.py +36 -19
  37. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/pyosys/ys_common.py +32 -5
  38. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/steps/common_variables.py +1 -1
  39. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/steps/klayout.py +13 -9
  40. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/steps/openroad.py +52 -8
  41. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/steps/pyosys.py +12 -13
  42. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/steps/tclstep.py +6 -4
  43. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/steps/verilator.py +21 -6
  44. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/steps/yosys.py +0 -12
  45. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/pyproject.toml +3 -3
  46. librelane-3.1.0.dev1/librelane/pdk_hashes.yaml +0 -3
  47. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/Readme.md +0 -0
  48. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/__init__.py +0 -0
  49. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/__version__.py +0 -0
  50. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/common/__init__.py +0 -0
  51. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/common/cli.py +0 -0
  52. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/common/drc.py +0 -0
  53. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/common/generic_dict.py +0 -0
  54. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/common/metrics/__init__.py +0 -0
  55. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/common/metrics/library.py +0 -0
  56. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/common/metrics/metric.py +0 -0
  57. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/common/metrics/util.py +0 -0
  58. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/common/misc.py +0 -0
  59. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/common/ring_buffer.py +0 -0
  60. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/common/tcl.py +0 -0
  61. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/common/toolbox.py +0 -0
  62. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/common/tpe.py +0 -0
  63. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/common/types.py +0 -0
  64. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/config/__init__.py +0 -0
  65. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/config/__main__.py +0 -0
  66. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/config/removals.py +0 -0
  67. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/container.py +0 -0
  68. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/env_info.py +0 -0
  69. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/examples/hold_eco_demo/config.yaml +0 -0
  70. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/examples/hold_eco_demo/demo.v +0 -0
  71. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/examples/spm/config.yaml +0 -0
  72. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/examples/spm/pin_order.cfg +0 -0
  73. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/examples/spm/src/spm.v +0 -0
  74. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/examples/spm/verify/spm_tb.v +0 -0
  75. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/examples/spm-user_project_wrapper/SPM_example.v +0 -0
  76. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/examples/spm-user_project_wrapper/base_sdc_file.sdc +0 -0
  77. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/examples/spm-user_project_wrapper/config-tut.json +0 -0
  78. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/examples/spm-user_project_wrapper/config.json +0 -0
  79. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/examples/spm-user_project_wrapper/defines.v +0 -0
  80. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/examples/spm-user_project_wrapper/template.def +0 -0
  81. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/examples/spm-user_project_wrapper/user_project_wrapper.v +0 -0
  82. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/flows/__init__.py +0 -0
  83. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/flows/builtins.py +0 -0
  84. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/flows/chip.py +0 -0
  85. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/flows/classic.py +0 -0
  86. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/flows/cli.py +0 -0
  87. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/flows/flow.py +0 -0
  88. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/flows/misc.py +0 -0
  89. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/flows/optimizing.py +0 -0
  90. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/flows/sequential.py +0 -0
  91. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/flows/synth_explore.py +0 -0
  92. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/help/__main__.py +0 -0
  93. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/logging/__init__.py +0 -0
  94. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/logging/logger.py +0 -0
  95. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/plugins.py +0 -0
  96. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/py.typed +0 -0
  97. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/klayout/Readme.md +0 -0
  98. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/klayout/insert_cell.py +0 -0
  99. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/klayout/open_design.py +0 -0
  100. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/klayout/render.py +0 -0
  101. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/klayout/stream_out.py +0 -0
  102. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/klayout/xml_drc_report_to_json.py +0 -0
  103. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/klayout/xor.drc +0 -0
  104. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/magic/Readme.md +0 -0
  105. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/magic/common/read.tcl +0 -0
  106. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/magic/open.tcl +0 -0
  107. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/magic/wrapper.tcl +0 -0
  108. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/netgen/setup.tcl +0 -0
  109. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/apply_def_template.py +0 -0
  110. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/cell_frequency.py +0 -0
  111. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/check_antenna_properties.py +0 -0
  112. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/contextualize.py +0 -0
  113. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/defutil.py +0 -0
  114. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/diodes.py +0 -0
  115. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/disconnected_pins.py +0 -0
  116. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/eco_buffer.py +0 -0
  117. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/eco_diode.py +0 -0
  118. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/filter_unannotated.py +0 -0
  119. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/io_place.py +0 -0
  120. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/ioplace_parser/__init__.py +0 -0
  121. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/ioplace_parser/parse.py +0 -0
  122. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/label_macro_pins.py +0 -0
  123. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/lefutil.py +0 -0
  124. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/placers.py +0 -0
  125. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/random_place.py +0 -0
  126. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/reader.py +0 -0
  127. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/remove_buffers.py +0 -0
  128. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/snap_to_grid.py +0 -0
  129. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/odbpy/wire_lengths.py +0 -0
  130. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/antenna_check.tcl +0 -0
  131. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/antenna_repair.tcl +0 -0
  132. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/buffer_list.tcl +0 -0
  133. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/common/dpl.tcl +0 -0
  134. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/common/dpl_cell_pad.tcl +0 -0
  135. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/common/grt.tcl +0 -0
  136. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/common/pdn_cfg.tcl +0 -0
  137. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/common/resizer.tcl +0 -0
  138. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/common/set_global_connections.tcl +0 -0
  139. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/common/set_layer_adjustments.tcl +0 -0
  140. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/common/set_power_nets.tcl +0 -0
  141. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/common/set_rc.tcl +0 -0
  142. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/common/set_routing_layers.tcl +0 -0
  143. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/cts.tcl +0 -0
  144. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/cut_rows.tcl +0 -0
  145. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/dpl.tcl +0 -0
  146. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/dump_rc.tcl +0 -0
  147. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/fill.tcl +0 -0
  148. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/floorplan.tcl +0 -0
  149. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/grt.tcl +0 -0
  150. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/gui.tcl +0 -0
  151. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/insert_buffer.tcl +0 -0
  152. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/ioplacer.tcl +0 -0
  153. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/irdrop.tcl +0 -0
  154. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/pad.tcl +0 -0
  155. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/pdn.tcl +0 -0
  156. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/rcx.tcl +0 -0
  157. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/repair_design_postgrt.tcl +0 -0
  158. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/rsz_timing_postcts.tcl +0 -0
  159. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/rsz_timing_postgrt.tcl +0 -0
  160. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/sta/check_macro_instances.tcl +0 -0
  161. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/sta/corner.tcl +0 -0
  162. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/tapcell.tcl +0 -0
  163. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/ungpl.tcl +0 -0
  164. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/write_cdl.tcl +0 -0
  165. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/openroad/write_views.tcl +0 -0
  166. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/scripts/tclsh/hello.tcl +0 -0
  167. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/state/__init__.py +0 -0
  168. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/state/__main__.py +0 -0
  169. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/state/design_format.py +0 -0
  170. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/state/state.py +0 -0
  171. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/steps/__init__.py +0 -0
  172. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/steps/__main__.py +0 -0
  173. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/steps/checker.py +0 -0
  174. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/steps/magic.py +0 -0
  175. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/steps/misc.py +0 -0
  176. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/steps/netgen.py +0 -0
  177. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/steps/odb.py +0 -0
  178. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/steps/openroad_alerts.py +0 -0
  179. {librelane-3.1.0.dev1 → librelane-3.1.0.dev3}/librelane/steps/step.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: librelane
3
- Version: 3.1.0.dev1
3
+ Version: 3.1.0.dev3
4
4
  Summary: An infrastructure for implementing chip design flows
5
5
  License-Expression: Apache-2.0
6
6
  Maintainer: Mohamed Gaber
@@ -13,7 +13,7 @@ Classifier: Programming Language :: Python :: 3.12
13
13
  Classifier: Programming Language :: Python :: 3.13
14
14
  Classifier: Programming Language :: Python :: 3.14
15
15
  Requires-Dist: ciel (>=2.3.1,<3)
16
- Requires-Dist: click (>=8,<8.3)
16
+ Requires-Dist: click (>=8,<8.4)
17
17
  Requires-Dist: cloup (>=3.0.5,<4)
18
18
  Requires-Dist: deprecated (>=1.2.10,<2)
19
19
  Requires-Dist: httpx (>=0.22.0,<0.29)
@@ -23,7 +23,7 @@ Requires-Dist: lxml (>=4.9.0)
23
23
  Requires-Dist: psutil (>=5.9.0)
24
24
  Requires-Dist: pyyaml (>=5,<7)
25
25
  Requires-Dist: rapidfuzz (>=3.9.0,<4)
26
- Requires-Dist: rich (>=12,<15)
26
+ Requires-Dist: rich (>=12,<16)
27
27
  Requires-Dist: semver (>=3.0.2,<4)
28
28
  Requires-Dist: yamlcore (>=0.0.2,<0.1.0)
29
29
  Project-URL: Bug Tracker, https://github.com/librelane/librelane/issues
@@ -94,7 +94,7 @@ def run(
94
94
  )
95
95
  ctx.exit(1)
96
96
  elif isinstance(meta.flow, list):
97
- TargetFlow = SequentialFlow.make(meta.flow)
97
+ TargetFlow = SequentialFlow.Make(meta.flow)
98
98
  if meta.substituting_steps is not None:
99
99
  if meta.flow is None:
100
100
  err("config_file has substituting_steps set with no flow.")
@@ -1,3 +1,7 @@
1
+ # Copyright 2026 LibreLane Contributors
2
+ #
3
+ # Adapted from OpenLane 2
4
+ #
1
5
  # Copyright 2023 Efabless Corporation
2
6
  #
3
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,42 +24,16 @@ import tarfile
20
24
  import tempfile
21
25
  from io import BytesIO
22
26
  from decimal import Decimal
23
- from typing import Optional, Set, Tuple
27
+ from typing import Optional, Tuple
24
28
 
25
29
  import cloup
26
30
  import httpx
27
31
 
28
32
  from .util import MetricDiff, TableVerbosity
33
+ from .compare import compare_metric_directories, default_filter_set
29
34
  from ..misc import Filter, get_httpx_session, mkdirp
30
35
  from ..cli import formatter_settings, IntEnumChoice
31
36
 
32
- default_filter_set = [
33
- "design__*__area",
34
- "design__max_*",
35
- "design__lvs_error__count",
36
- "antenna__violating*",
37
- "clock__*",
38
- "ir__*",
39
- "power__*",
40
- "timing__*_vio__*",
41
- "timing*wns*",
42
- "timing*tns*",
43
- "*error*",
44
- "!*__iter:*",
45
- ]
46
-
47
- # passing_filter_set = [
48
- # "design__*__area",
49
- # "route__wirelength__max",
50
- # "design__instance__utilization",
51
- # "antenna__violating*",
52
- # "timing__*__ws",
53
- # "clock__skew__*",
54
- # "ir__*",
55
- # "power__*",
56
- # "!*__iter:*",
57
- # ]
58
-
59
37
 
60
38
  @cloup.group(
61
39
  no_args_is_help=True,
@@ -141,130 +119,25 @@ def compare(
141
119
  cli.add_command(compare)
142
120
 
143
121
 
144
- def _compare_metric_folders(
145
- filter_wildcards: Tuple[str, ...],
146
- table_verbosity: TableVerbosity,
147
- path_a: str,
148
- path_b: str,
149
- significant_figures: int,
150
- ) -> Tuple[str, str]: # (summary, table)
151
- a: Set[Tuple[str, str, str]] = set()
152
- b: Set[Tuple[str, str, str]] = set()
153
-
154
- def add_designs(in_dir: str, to_set: Set[Tuple[str, str, str]]):
155
- for file in os.listdir(in_dir):
156
- basename = os.path.basename(file)
157
- if not basename.endswith(".metrics.json"):
158
- continue
159
- basename = basename[: -len(".metrics.json")]
160
-
161
- # We have to rsplit, since ihp-sg13g2 contains a "-"
162
- parts = basename.rsplit("-", maxsplit=2)
163
- if len(parts) != 3:
164
- raise ValueError(
165
- f"Invalid filename {basename}: not in the format {{pdk}}-{{scl}}-{{design_name}}"
166
- )
167
- pdk, scl, design = parts
168
- to_set.add((pdk, scl, design))
169
-
170
- add_designs(path_a, a)
171
- add_designs(path_b, b)
172
-
173
- not_in_a = b - a
174
- not_in_b = a - b
175
- common = a.intersection(b)
176
- difference_report = ""
177
- for tup in not_in_a:
178
- pdk, scl, design = tup
179
- difference_report += f"* Results for a new test, `{'/'.join(tup)}`, detected.\n"
180
- for tup in not_in_b:
181
- pdk, scl, design = tup
182
- difference_report += (
183
- f"* ‼️ Results for `{'/'.join(tup)}` appear to be missing!\n"
184
- )
185
-
186
- final_filters = []
187
- for wildcard in filter_wildcards:
188
- if wildcard == "DEFAULT":
189
- final_filters += default_filter_set
190
- else:
191
- final_filters.append(wildcard)
192
-
193
- filter = Filter(final_filters)
194
- critical_change_report = ""
195
- tables = ""
196
- total_critical = 0
197
- for pdk, scl, design in sorted(common):
198
- metrics_a = json.load(
199
- open(
200
- os.path.join(path_a, f"{pdk}-{scl}-{design}.metrics.json"),
201
- encoding="utf8",
202
- ),
203
- parse_float=Decimal,
204
- )
205
-
206
- metrics_b = json.load(
207
- open(
208
- os.path.join(path_b, f"{pdk}-{scl}-{design}.metrics.json"),
209
- encoding="utf8",
210
- ),
211
- parse_float=Decimal,
212
- )
213
-
214
- diff = MetricDiff.from_metrics(
215
- metrics_a,
216
- metrics_b,
217
- significant_figures,
218
- filter=filter,
219
- )
220
-
221
- stats = diff.stats()
222
-
223
- total_critical += stats.critical
224
- if stats.critical > 0:
225
- critical_change_report += f" * `{pdk}/{scl}/{design}` \n"
226
- if table_verbosity != "NONE":
227
- rendered = diff.render_md(("corner", ""), table_verbosity)
228
- if rendered.strip() != "":
229
- tables += f"<details><summary><code>{pdk}/{scl}/{design}</code></summary>\n{rendered}\n</details>\n\n"
230
-
231
- if total_critical == 0:
232
- critical_change_report = (
233
- "* No changes to critical metrics were detected in analyzed designs.\n"
234
- + critical_change_report
235
- )
236
- else:
237
- critical_change_report = (
238
- "* **Changes to critical metrics were detected in the following designs:**\n"
239
- + critical_change_report
240
- )
241
-
242
- report = ""
243
- report += difference_report
244
- report += critical_change_report
245
-
246
- return report, tables.strip()
247
-
248
-
249
122
  @cloup.command(no_args_is_help=True)
250
123
  @common_opts
251
- @cloup.argument("metric_folders", nargs=2)
124
+ @cloup.argument("metric_directories", nargs=2)
252
125
  def compare_multiple(
253
126
  filter_wildcards: Tuple[str, ...],
254
127
  table_verbosity: TableVerbosity,
255
- metric_folders: Tuple[str, str],
128
+ metric_directories: Tuple[str, str],
256
129
  table_out: Optional[str],
257
130
  significant_figures: int,
258
131
  ):
259
132
  """
260
- Creates a small summary/report of the differences between two folders with
133
+ Creates a small summary/report of the differences between two directories with
261
134
  metrics files.
262
135
 
263
136
  The metrics files must be named in the format ``{pdk}-{scl}-{design}.metrics.json``.
264
137
  All other files are ignored.
265
138
  """
266
- path_a, path_b = metric_folders
267
- summary, tables = _compare_metric_folders(
139
+ path_a, path_b = metric_directories
140
+ summary, tables = compare_metric_directories(
268
141
  filter_wildcards, table_verbosity, path_a, path_b, significant_figures
269
142
  )
270
143
  print(summary)
@@ -309,7 +182,7 @@ cli.add_command(compare_multiple)
309
182
  help="A GitHub token to use to query the API and fetch the metrics. Not strictly required, but helps avoid rate-limiting.",
310
183
  )
311
184
  @common_opts
312
- @cloup.argument("metric_folder", nargs=1)
185
+ @cloup.argument("metric_directory", nargs=1)
313
186
  def compare_remote(
314
187
  filter_wildcards: Tuple[str, ...],
315
188
  table_verbosity: TableVerbosity,
@@ -317,13 +190,13 @@ def compare_remote(
317
190
  metric_repo: str,
318
191
  commit: Optional[str],
319
192
  token: str,
320
- metric_folder: str,
193
+ metric_directory: str,
321
194
  table_out: Optional[str],
322
195
  significant_figures: int,
323
196
  branch: str,
324
197
  ):
325
198
  """
326
- Creates a small summary/report of the differences between a folder and
199
+ Creates a small summary/report of the differences between a directory and
327
200
  a set of metrics stored in --metric-repo. Requires Internet access and
328
201
  access to GitHub.
329
202
 
@@ -379,11 +252,11 @@ def compare_remote(
379
252
  with open(final_path, "wb") as f:
380
253
  f.write(io.read())
381
254
 
382
- summary, tables = _compare_metric_folders(
255
+ summary, tables = compare_metric_directories(
383
256
  filter_wildcards,
384
257
  table_verbosity,
385
258
  d,
386
- metric_folder,
259
+ metric_directory,
387
260
  significant_figures,
388
261
  )
389
262
  print(summary)
@@ -0,0 +1,156 @@
1
+ # Copyright 2026 LibreLane Contributors
2
+ #
3
+ # Adapted from OpenLane 2
4
+ #
5
+ # Copyright 2023 Efabless Corporation
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ import os
19
+ import json
20
+ from decimal import Decimal
21
+ from typing import Set, Tuple
22
+
23
+ from .util import MetricDiff, TableVerbosity
24
+ from ..misc import Filter
25
+
26
+ default_filter_set = [
27
+ "design__*__area",
28
+ "design__max_*",
29
+ "design__lvs_error__count",
30
+ "antenna__violating*",
31
+ "clock__*",
32
+ "ir__*",
33
+ "power__*",
34
+ "timing__*_vio__*",
35
+ "timing*wns*",
36
+ "timing*tns*",
37
+ "*error*",
38
+ "!*__iter:*",
39
+ ]
40
+
41
+ # passing_filter_set = [
42
+ # "design__*__area",
43
+ # "route__wirelength__max",
44
+ # "design__instance__utilization",
45
+ # "antenna__violating*",
46
+ # "timing__*__ws",
47
+ # "clock__skew__*",
48
+ # "ir__*",
49
+ # "power__*",
50
+ # "!*__iter:*",
51
+ # ]
52
+
53
+
54
+ def compare_metric_directories(
55
+ filter_wildcards: Tuple[str, ...],
56
+ table_verbosity: TableVerbosity,
57
+ path_a: str,
58
+ path_b: str,
59
+ significant_figures: int,
60
+ ) -> Tuple[str, str]: # (summary, table)
61
+ a: Set[Tuple[str, str, str]] = set()
62
+ b: Set[Tuple[str, str, str]] = set()
63
+
64
+ def add_designs(in_dir: str, to_set: Set[Tuple[str, str, str]]):
65
+ for file in os.listdir(in_dir):
66
+ basename = os.path.basename(file)
67
+ if not basename.endswith(".metrics.json"):
68
+ continue
69
+ basename = basename[: -len(".metrics.json")]
70
+
71
+ # We have to rsplit, since ihp-sg13g2 contains a "-"
72
+ parts = basename.rsplit("-", maxsplit=2)
73
+ if len(parts) != 3:
74
+ raise ValueError(
75
+ f"Invalid filename {basename}: not in the format {{pdk}}-{{scl}}-{{design_name}}"
76
+ )
77
+ pdk, scl, design = parts
78
+ to_set.add((pdk, scl, design))
79
+
80
+ add_designs(path_a, a)
81
+ add_designs(path_b, b)
82
+
83
+ not_in_a = b - a
84
+ not_in_b = a - b
85
+ common = a.intersection(b)
86
+ difference_report = ""
87
+ for tup in not_in_a:
88
+ pdk, scl, design = tup
89
+ difference_report += f"* Results for a new test, `{'/'.join(tup)}`, detected.\n"
90
+ for tup in not_in_b:
91
+ pdk, scl, design = tup
92
+ difference_report += (
93
+ f"* ‼️ Results for `{'/'.join(tup)}` appear to be missing!\n"
94
+ )
95
+
96
+ final_filters = []
97
+ for wildcard in filter_wildcards:
98
+ if wildcard == "DEFAULT":
99
+ final_filters += default_filter_set
100
+ else:
101
+ final_filters.append(wildcard)
102
+
103
+ filter = Filter(final_filters)
104
+ critical_change_report = ""
105
+ tables = ""
106
+ total_critical = 0
107
+ for pdk, scl, design in sorted(common):
108
+ metrics_a = json.load(
109
+ open(
110
+ os.path.join(path_a, f"{pdk}-{scl}-{design}.metrics.json"),
111
+ encoding="utf8",
112
+ ),
113
+ parse_float=Decimal,
114
+ )
115
+
116
+ metrics_b = json.load(
117
+ open(
118
+ os.path.join(path_b, f"{pdk}-{scl}-{design}.metrics.json"),
119
+ encoding="utf8",
120
+ ),
121
+ parse_float=Decimal,
122
+ )
123
+
124
+ diff = MetricDiff.from_metrics(
125
+ metrics_a,
126
+ metrics_b,
127
+ significant_figures,
128
+ filter=filter,
129
+ )
130
+
131
+ stats = diff.stats()
132
+
133
+ total_critical += stats.critical
134
+ if stats.critical > 0:
135
+ critical_change_report += f" * `{pdk}/{scl}/{design}` \n"
136
+ if table_verbosity != "NONE":
137
+ rendered = diff.render_md(("corner", ""), table_verbosity)
138
+ if rendered.strip() != "":
139
+ tables += f"<details><summary><code>{pdk}/{scl}/{design}</code></summary>\n{rendered}\n</details>\n\n"
140
+
141
+ if total_critical == 0:
142
+ critical_change_report = (
143
+ "* No changes to critical metrics were detected in analyzed designs.\n"
144
+ + critical_change_report
145
+ )
146
+ else:
147
+ critical_change_report = (
148
+ "* **Changes to critical metrics were detected in the following designs:**\n"
149
+ + critical_change_report
150
+ )
151
+
152
+ report = ""
153
+ report += difference_report
154
+ report += critical_change_report
155
+
156
+ return report, tables.strip()
@@ -495,10 +495,10 @@ class Config(GenericImmutableDict[str, Any]):
495
495
  :param pdk: A process design kit to use. Required unless specified via the
496
496
  "PDK" key in a configuration object.
497
497
 
498
- :param pdk_root: Required if Volare is not installed.
498
+ :param pdk_root: Required if Ciel is not installed.
499
499
 
500
- If Volare is installed, this value can be used to optionally override
501
- Volare's default.
500
+ If Ciel is installed, this value can be used to optionally override
501
+ Ciel's default.
502
502
 
503
503
  :param scl: A standard cell library to use. If not specified, the PDK's
504
504
  default standard cell library will be used instead.
@@ -678,6 +678,7 @@ class Config(GenericImmutableDict[str, Any]):
678
678
  raw,
679
679
  only_extract_process_info=True,
680
680
  design_dir=design_dir,
681
+ version=meta.version,
681
682
  )
682
683
  )
683
684
 
@@ -713,8 +714,9 @@ class Config(GenericImmutableDict[str, Any]):
713
714
  pdk=pdk,
714
715
  pdkpath=pdkpath,
715
716
  scl=mutable[SpecialKeys.scl],
716
- pad=mutable.get(SpecialKeys.pad, None),
717
+ pad=mutable.get(SpecialKeys.pad, pad),
717
718
  design_dir=design_dir,
719
+ version=meta.version,
718
720
  )
719
721
  )
720
722
 
@@ -775,6 +777,9 @@ class Config(GenericImmutableDict[str, Any]):
775
777
  tcl_config,
776
778
  only_extract_process_info=True,
777
779
  design_dir=design_dir,
780
+ # Tcl is version 1 by default I believe; on par with JSON
781
+ # also this path is deprecated anyways
782
+ version=1,
778
783
  )
779
784
 
780
785
  pdk = process_info.get(SpecialKeys.pdk) or pdk
@@ -833,6 +838,11 @@ class Config(GenericImmutableDict[str, Any]):
833
838
  if scl is not None:
834
839
  pdk_config[SpecialKeys.scl] = scl
835
840
 
841
+ # HACK: Prevent loading default SCL cfg vars for old openlane PDK
842
+ # configs
843
+ # For more info: https://github.com/librelane/librelane/issues/932
844
+ pdk_config["STD_CELL_LIBRARY_OPT"] = scl
845
+
836
846
  if pad is not None:
837
847
  pdk_config[SpecialKeys.pad] = pad
838
848
 
@@ -39,6 +39,12 @@ pdk_variables = [
39
39
  "Specifies the default standard cell library to be used under the specified PDK. Must be a valid C identifier, i.e., matches the regular expression `[_a-zA-Z][_a-zA-Z0-9]+`.",
40
40
  pdk=True,
41
41
  ),
42
+ Variable(
43
+ "PAD_CELL_LIBRARY",
44
+ Optional[str],
45
+ "Specifies the default pad cell library to be used under the specified PDK. Must be a valid C identifier, i.e., matches the regular expression `[_a-zA-Z][_a-zA-Z0-9]+`.",
46
+ pdk=True,
47
+ ),
42
48
  Variable(
43
49
  "VDD_PIN",
44
50
  str,
@@ -325,9 +325,9 @@ def migrate_old_config(config: Mapping[str, Any]) -> Dict[str, Any]:
325
325
  if "CLOCK_TRANSITION_CONSTRAINT" not in config:
326
326
  new["CLOCK_TRANSITION_CONSTRAINT"] = 0.15
327
327
  if "TIME_DERATING_CONSTRAINT" not in config:
328
- new["TIME_DERATING_CONSTRAINT"] = 5
328
+ new["TIME_DERATING_CONSTRAINT"] = 5.0
329
329
  if "IO_DELAY_CONSTRAINT" not in config:
330
- new["IO_DELAY_CONSTRAINT"] = 20
330
+ new["IO_DELAY_CONSTRAINT"] = 20.0
331
331
 
332
332
  # Unspecififed is fine
333
333
  # if "FP_IO_MIN_DISTANCE" not in config:
@@ -211,10 +211,7 @@ class Expr(object):
211
211
  ref_rx = re.compile(r"^\$([A-Za-z_][A-Za-z0-9_\.\[\]]*)")
212
212
 
213
213
 
214
- def process_string(
215
- value: str,
216
- symbols: Mapping[str, Any],
217
- ) -> Valid:
214
+ def process_string(value: str, symbols: Mapping[str, Any], version: int = 1) -> Valid:
218
215
  global ref_rx
219
216
  EXPR_PREFIX = "expr::"
220
217
  REF_PREFIX = "ref::"
@@ -230,6 +227,11 @@ def process_string(
230
227
  elif value.startswith(PDK_DIR_PREFIX):
231
228
  mutable = value.replace(PDK_DIR_PREFIX, f"refg::${Keys.pdkpath}/")
232
229
 
230
+ # gate this behind meta version 3
231
+ if version >= 3:
232
+ # before we do anything else, apply inline variable substitution
233
+ mutable = mutable.format(**symbols)
234
+
233
235
  if mutable.startswith(EXPR_PREFIX):
234
236
  try:
235
237
  return Expr.evaluate(value[len(EXPR_PREFIX) :], symbols)
@@ -296,6 +298,7 @@ def process_list_recursive(
296
298
  input: Sequence[Any],
297
299
  ref: List[Any],
298
300
  symbols: Dict[str, Any],
301
+ version: int = 1,
299
302
  *,
300
303
  key_path: str = "",
301
304
  ):
@@ -308,6 +311,7 @@ def process_list_recursive(
308
311
  value,
309
312
  processed,
310
313
  symbols,
314
+ version,
311
315
  key_path=current_key_path,
312
316
  )
313
317
  elif isinstance(value, Sequence) and not is_string(value):
@@ -316,10 +320,11 @@ def process_list_recursive(
316
320
  value,
317
321
  processed,
318
322
  symbols,
323
+ version,
319
324
  key_path=current_key_path,
320
325
  )
321
326
  elif is_string(value):
322
- processed = process_string(value, symbols)
327
+ processed = process_string(value, symbols, version)
323
328
  else:
324
329
  processed = value
325
330
 
@@ -332,6 +337,7 @@ def process_dict_recursive(
332
337
  input: Mapping[str, Any],
333
338
  ref: Dict[str, Any],
334
339
  symbols: Dict[str, Any],
340
+ version: int = 1,
335
341
  *,
336
342
  key_path: str = "",
337
343
  ):
@@ -348,6 +354,7 @@ def process_dict_recursive(
348
354
  value,
349
355
  ref,
350
356
  symbols,
357
+ version,
351
358
  key_path=key_path,
352
359
  )
353
360
  elif key.startswith(SCL_PREFIX):
@@ -359,6 +366,7 @@ def process_dict_recursive(
359
366
  value,
360
367
  ref,
361
368
  symbols,
369
+ version,
362
370
  key_path=key_path,
363
371
  )
364
372
  else:
@@ -367,6 +375,7 @@ def process_dict_recursive(
367
375
  value,
368
376
  processed,
369
377
  symbols,
378
+ version,
370
379
  key_path=current_key_path,
371
380
  )
372
381
 
@@ -376,10 +385,11 @@ def process_dict_recursive(
376
385
  value,
377
386
  processed,
378
387
  symbols,
388
+ version,
379
389
  key_path=current_key_path,
380
390
  )
381
391
  elif is_string(value):
382
- processed = process_string(value, symbols)
392
+ processed = process_string(value, symbols, version)
383
393
  else:
384
394
  processed = value
385
395
 
@@ -389,12 +399,11 @@ def process_dict_recursive(
389
399
 
390
400
 
391
401
  def process_config_dict(
392
- config_in: Mapping[str, Any],
393
- exposed_variables: Dict[str, Any],
402
+ config_in: Mapping[str, Any], exposed_variables: Dict[str, Any], version: int = 1
394
403
  ) -> Dict[str, Any]:
395
404
  state = dict(exposed_variables)
396
405
  symbols = dict(exposed_variables)
397
- process_dict_recursive(config_in, state, symbols)
406
+ process_dict_recursive(config_in, state, symbols, version)
398
407
  return state
399
408
 
400
409
 
@@ -414,6 +423,7 @@ def preprocess_dict(
414
423
  pdkpath: Optional[str] = None,
415
424
  scl: Optional[str] = None,
416
425
  pad: Optional[str] = None,
426
+ version: int = 1,
417
427
  ) -> Dict[str, Any]:
418
428
  if None in (pdk, pdkpath, scl):
419
429
  if only_extract_process_info:
@@ -433,10 +443,7 @@ def preprocess_dict(
433
443
  Keys.design_dir: design_dir,
434
444
  }
435
445
 
436
- preprocessed = process_config_dict(
437
- config_dict,
438
- base_vars,
439
- )
446
+ preprocessed = process_config_dict(config_dict, base_vars, version)
440
447
  if only_extract_process_info:
441
448
  preprocessed = extract_process_vars(preprocessed)
442
449