烽火戏诸侯的个人空间 https://blog.eetop.cn/1769267 [收藏] [复制] [分享] [RSS]

空间首页 动态 记录 日志 相册 主题 分享 留言板 个人资料

日志

BOX

已有 448 次阅读| 2025-4-1 22:26 |系统分类:芯片设计

procedure(autoFillLVSBox()
    let((lvsForm ruleCheckbox inputField)
        ; 获取当前LVS验证窗口
        lvsForm = hiGetCurrentForm()
        unless(lvsForm && lvsForm->name == "lvsForm" 
            error("请先打开LVS验证窗口")
        )

        ; 定位include规则复选框(可能需要根据实际控件名称调整)
        ruleCheckbox = hiFindWidget(lvsForm "includeRuleStatements")
        when(ruleCheckbox
            hiFormButtonSelect(ruleCheckbox t)  ; 勾选复选框
        )

        ; 定位输入框并填写内容(可能需要调整控件标识)
        inputField = hiFindWidget(lvsForm "ruleIncludeField")
        when(inputField
            hiSetTextEntryValue(inputField "LVS BOX 1")
        )

        printf("LVS选项设置完成\n")
    )
)



点赞

全部作者的其他最新日志

发表评论 评论 (11 个评论)

回复 烽火戏诸侯 2025-4-2 12:05
在 Virtuoso 617 中,SKILL API 的函数名称和使用方式可能与新版本有所不同。以下是调整后的脚本及分步说明:

```skill
procedure(autoFillLVSBox()
    let((lvsForm ruleCheckbox inputField)
        ; 获取当前窗口 - 可能需要根据窗口标题或类型获取
        lvsForm = car(axlWindowGetAllOpen('LVS)) ; 假设窗口标题包含"LVS"
        unless(lvsForm
            error("请先打开LVS验证窗口")
        )

        ; 定位include规则复选框 - 需要实际控件名称
        ruleCheckbox = axlFormGetField(lvsForm "Include Rules")
        when(ruleCheckbox
            axlFormSetField(ruleCheckbox t)  ; 勾选复选框
        )

        ; 定位输入框 - 需要实际控件名称
        inputField = axlFormGetField(lvsForm "Rule File")
        when(inputField
            axlFormSetFieldValue(inputField "LVS BOX 1")
        )

        printf("LVS选项设置完成\n")
    )
)
```

**分步说明:**

1. **获取LVS窗口对象:**
   - 使用 `axlWindowGetAllOpen` 获取所有打开的LVS窗口
   - 通过窗口标题过滤(可能需要调整匹配字符串)
   - 如果获取失败提示错误

2. **操作复选框:**
   - 使用 `axlFormGetField` 通过控件标签获取复选框对象
   - 使用 `axlFormSetField` 设置选中状态(t 表示选中)

3. **填写输入框:**
   - 使用 `axlFormGetField` 通过控件标签获取输入框对象
   - 使用 `axlFormSetFieldValue` 设置输入框的值

4. **调试提示:**
   ```skill
   ; 打印所有字段名称用于调试
   axlFormDumpFields(lvsForm)
   ```

**注意事项:**

1. 必须通过CIW窗口菜单启动LVS验证,确保窗口对象存在
2. 实际控件名称需要通过以下方式获取:
   - 在LVS窗口打开时执行 `axlFormDumpFields(lvsForm)`
   - 在CIW窗口查看所有字段名称
3. 可能需要根据实际控件类型调整设置方法:
   - 对于按钮使用 `axlFormButtonPress`
   - 对于单选按钮使用 `axlFormSetFieldRadio`

**扩展建议:**
```skill
; 通用表单操作函数示例
procedure(setFormField(form field value)
    let((widget)
        widget = axlFormGetField(form field)
        when(widget
            case(widget->type
                ("CheckBox" axlFormSetField(widget value))
                ("TextEdit" axlFormSetFieldValue(widget value))
                (t printf("Unsupported widget type: %s\n" widget->type))
            )
        )
    )
)

; 使用示例
setFormField(lvsForm "Include Rules" t)
setFormField(lvsForm "Rule File" "LVS BOX 1")
```

建议通过以下步骤验证:
1. 手动打开LVS窗口
2. 在CIW窗口逐步执行代码片段
3. 使用 `axlFormDumpFields` 查看实际字段结构
4. 根据实际输出调整控件名称和操作方式
回复 烽火戏诸侯 2025-4-2 12:56
以下是对调整后的 SKILL 脚本中 **每个关键函数** 的详细解释,适用于 Virtuoso 617 版本:

---

### **1. 获取 LVS 窗口对象**
```skill
lvsForm = car(axlWindowGetAllOpen('LVS))
```
- **函数作用**:
  - `axlWindowGetAllOpen()`:获取当前所有打开的窗口对象列表。
  - `'LVS`:过滤参数,匹配窗口标题中包含 "LVS" 的窗口(需根据实际窗口标题调整)。
  - `car()`:取列表中的第一个元素,假设用户只打开了一个 LVS 窗口。
- **返回值**:
  - 返回 LVS 验证窗口的表单对象 (`lvsForm`)。
- **注意事项**:
  - 如果打开多个 LVS 窗口,可能需要遍历列表选择正确的窗口。
  - 窗口标题匹配规则可能需要调整(如使用正则表达式)。

---

### **2. 勾选复选框**
```skill
ruleCheckbox = axlFormGetField(lvsForm "Include Rules")
axlFormSetField(ruleCheckbox t)
```
- **函数作用**:
  - `axlFormGetField(form fieldLabel)`:通过控件的显示名称(如 "Include Rules")获取表单内指定控件的对象。
  - `axlFormSetField(widget value)`:设置控件的值,`t` 表示勾选复选框。
- **关键点**:
  - 控件名称需与 LVS 窗口中显示的标签完全一致(区分大小写)。
  - 如果控件是复选框,`t` 表示选中,`nil` 表示取消选中。

---

### **3. 填写输入框**
```skill
inputField = axlFormGetField(lvsForm "Rule File")
axlFormSetFieldValue(inputField "LVS BOX 1")
```
- **函数作用**:
  - `axlFormGetField()`:同上,获取输入框控件对象。
  - `axlFormSetFieldValue(widget value)`:设置输入框的文本内容(如填写路径或规则名称)。
- **注意事项**:
  - 输入框类型可能是 `TextEdit` 或 `TextField`,需确保使用正确的设置函数。

---

### **4. 调试函数**
```skill
axlFormDumpFields(lvsForm)
```
- **函数作用**:
  - 打印表单 (`lvsForm`) 中所有控件的详细信息,包括:
    - 控件名称 (`fieldName`)
    - 显示标签 (`label`)
    - 控件类型 (`type`,如 `CheckBox`、`TextEdit`)
    - 当前值 (`value`)
- **使用场景**:
  - 当不确定控件名称时,在 CIW 窗口执行此函数,查看所有可用字段信息。
  - 示例输出:
    ```
    Field Name: ruleInclude  Label: "Include Rules"  Type: CheckBox  Value: nil
    Field Name: ruleFile    Label: "Rule File"      Type: TextEdit  Value: ""
    ```

---

### **5. 通用表单操作函数(扩展建议)**
```skill
procedure(setFormField(form field value)
  let((widget)
    widget = axlFormGetField(form field)
    when(widget
      case(widget->type
        ("CheckBox" axlFormSetField(widget value))
        ("TextEdit" axlFormSetFieldValue(widget value))
        (t printf("Unsupported type: %s\n" widget->type))
      )
    )
  )
)
```
- **函数作用**:
  - 封装对不同类型控件的统一操作。
  - 根据控件类型 (`widget->type`) 自动选择设置方法:
    - 复选框 (`CheckBox`) → `axlFormSetField`
    - 输入框 (`TextEdit`) → `axlFormSetFieldValue`
- **使用示例**:
  ```skill
  setFormField(lvsForm "Include Rules" t)      ; 勾选复选框
  setFormField(lvsForm "Rule File" "LVS BOX 1") ; 填写输入框
  ```

---

### **常见问题排查**
1. **控件无法找到**:
   - 使用 `axlFormDumpFields` 确认控件名称。
   - 检查窗口标题过滤条件是否准确。
2. **操作未生效**:
   - 确保控件类型匹配(如复选框用 `axlFormSetField`,输入框用 `axlFormSetFieldValue`)。
   - 部分控件可能需要先执行 `axlFormDisplay` 刷新表单状态。
3. **窗口对象获取失败**:
   - 确保 LVS 窗口是通过 CIW 菜单打开的,而非脚本创建的临时窗口。

通过以上函数组合,可以实现对 Virtuoso LVS 表单的自动化操作。实际使用时需根据 `axlFormDumpFields` 的输出调整控件名称和操作逻辑。
回复 烽火戏诸侯 2025-4-2 13:03
在 Cadence Virtuoso 的 SKILL 语言中,`mgc()` 函数的具体用途和参数通常需要结合上下文或文档确认,因为它可能是自定义函数或特定工艺设计工具包(PDK)的一部分。以下是查找其用法和参数的步骤:

---

### 1. **确认函数名称和来源**
   - **检查拼写**:确保函数名正确(如 `mgc()`、`mcg()` 或其他变体)。
   - **确认来源**:是否是 PDK 提供的函数、公司内部库,还是自定义脚本中的函数?

---

### 2. **使用内置帮助工具**
   - 在 Virtuoso 的 **CIW(Command Interpreter Window)** 输入:
     ```tcl
     help mgc
     ```
     或使用 SKILL 函数查看帮助(如果文档已集成)。

---

### 3. **通过 SKILL 函数自省**
   - **查看参数数量和名称**:
     ```tcl
     args(mgc)        ; 返回参数数量
     mgc~>parameters  ; 尝试获取参数名(需函数未加密)
     ```
   - **检查函数类型**:
     ```tcl
     mgc~>type        ; 返回函数类型(如“subr”表示内置函数)
     ```

---

### 4. **查找文档或示例**
   - **PDK 文档**:检查 PDK 的用户指南或函数手册。
   - **代码示例**:在现有脚本或库中搜索 `mgc(`,观察其调用方式。
   - **联系支持**:向 PDK 供应商或内部团队咨询。

---

### 5. **常见参数猜测(仅供参考)**
   - 如果是版图相关函数,参数可能类似:
     ```tcl
     mgc(layer width height)  ; 例如:层名、宽度、高度
     ```
   - 如果是仿真或模型函数,参数可能是模型名、变量、配置选项。

---

### 6. **调试与测试**
   - **打印返回值**:尝试调用函数并观察输出或生成的图形。
     ```tcl
     mgc(arg1 arg2 arg3)  ; 替换为实际参数测试
     ```
   - **逐步调试**:使用 `trace` 或 `printf` 跟踪函数行为。

---

### 总结
如果无法通过上述方法找到信息,建议:
1. 在调用 `mgc()` 的脚本中查找上下文。
2. 向 PDK 或 IT 部门提交支持请求。
3. 如果是加密函数,可能需要直接联系开发者获取文档。

希望这些方法能帮助您定位到 `mgc()` 的具体用法!
回复 烽火戏诸侯 2025-4-11 16:44
在 Cadence Virtuoso 中查看自定义 Skill 函数的定义代码,可以通过以下步骤实现:

---

### **方法 1:使用 `getd` 函数**
1. 打开 **CIW(Command Interpreter Window)**(默认 Virtuoso 启动时显示的交互窗口)。
2. 输入以下命令,将 `yourFunctionName` 替换为实际的函数名:
   ```skill
   getd('yourFunctionName)
   ```
   - 示例:
     ```skill
     getd('myCustomFunction)
     ```
3. **输出结果**:
   - 如果函数是明文定义的(未加密/编译),将直接显示函数的定义代码。
   - 如果返回 `nil`,可能是函数不存在、未加载到当前环境,或是编译后的函数(无法查看源码)。

---

### **方法 2:使用 `printf` + `%f` 格式化**
1. 在 CIW 中输入:
   ```skill
   printf("%f\n" getd('yourFunctionName))
   ```
   - 这会以更友好的格式输出函数定义,尤其适合多行代码。

---

### **方法 3:检查加载的库文件**
如果函数定义在某个库文件中(如 `.il` 文件):
1. 确认函数所在的库已加载(可通过 `libList` 或 `load` 命令管理库)。
2. 使用文本编辑器直接搜索库文件中的 `defun yourFunctionName` 或 `procedure(yourFunctionName` 定义。

---

### **常见问题排查**
1. **函数未加载**:
   - 确保包含函数的库已通过 `load` 命令加载。
   - 示例:`load("path/to/library.il")`

2. **函数已编译**:
   - 如果函数是编译后的二进制(如 `.ils` 文件),无法查看源码。需联系代码提供者获取明文版本。

3. **函数名错误**:
   - 使用 `isCallable('yourFunctionName)` 验证函数是否存在。

---

### **总结**
- **明文函数**:直接使用 `getd` 或 `printf` 查看。
- **加密/编译函数**:无法直接查看,需联系提供者。
- **未加载函数**:确保相关库文件已正确加载。

通过上述方法,您可以快速定位自定义 Skill 函数的代码定义。
回复 烽火戏诸侯 2025-4-11 16:46
在 Cadence Virtuoso 中,如果已知某个 Skill 函数存在但无法查看其定义(例如函数被加密或未加载明文代码),可以通过以下方法追踪其**调用路径**或**依赖关系**:

---

### **方法 1:使用 `trace` 动态追踪调用**
通过 `trace` 函数监控目标函数的调用和返回,记录调用时的参数和上下文信息。

```skill
; 启用跟踪
trace('yourFunctionName)

; 运行相关操作(触发函数调用)
; 此时 CIW 窗口会实时打印调用信息,包括参数和调用位置

; 关闭跟踪
untrace('yourFunctionName)
```

**输出示例**:  
```
Entering yourFunctionName: arg1=value1, arg2=value2  
Exiting yourFunctionName: return_value  
```
通过多次触发操作,可以观察哪些流程调用了该函数。

---

### **方法 2:通过断点和调用栈分析**
在函数调用前设置断点,捕获调用栈(Call Stack)信息:  
```skill
; 在目标函数入口设置断点
brkAt('yourFunctionName)

; 运行相关操作(触发函数调用)
; 当断点触发时,在 CIW 中输入以下命令查看调用栈:
stackTrace()

; 取消断点
unbrkAt('yourFunctionName)
```

**输出示例**:  
```
Call Stack:
  yourFunctionName(arg1 arg2)  -- 当前断点位置
  callerFunction1()            -- 调用者1
  callerFunction2()            -- 调用者2
  ...
```

---

### **方法 3:使用 `xref` 查找静态调用关系**
通过交叉引用工具 `xref` 查找代码中对目标函数的静态调用关系(需确保相关库已加载):  
```skill
; 查找所有调用该函数的位置
xref('yourFunctionName 'callers)
```

**输出示例**:  
```
Callers of yourFunctionName:
  /path/to/file1.il: line 100 (callerFunction1)
  /path/to/file2.il: line 50  (callerFunction2)
```

---

### **方法 4:触发错误捕获调用栈**
如果函数可能抛出错误,通过触发错误获取调用栈:  
```skill
; 启用调试模式(显示详细错误)
setSkillVar('debugMode t)

; 手动调用函数(或触发相关操作)
yourFunctionName(arg1 arg2)

; 观察 CIW 中的错误信息,通常包含完整调用栈
```

---

### **方法 5:全局搜索明文代码(仅限未加密库)**
如果函数来自明文库文件(如 `.il` 文件),可通过文本工具全局搜索调用点:  
```bash
# 在 Linux 终端中使用 grep 搜索
grep -rn "yourFunctionName" /path/to/cadence/libs/
```

---

### **适用场景总结**
| **场景**                     | **推荐方法**                          |
|-------------------------------|---------------------------------------|
| 动态运行时追踪               | `trace` 或 `brkAt` + `stackTrace`     |
| 静态代码分析(明文库)       | `xref` 或 `grep` 搜索                |
| 加密函数/无法查看源码         | `trace`(仅能追踪调用,无法查看逻辑)|
| 未加载函数                   | 检查 `libList()` 并加载相关库         |

---

### **注意事项**
1. **加密函数(`.ils` 文件)**  
   - 如果函数是编译后的二进制文件,`xref` 和 `grep` 无法分析其调用关系,需依赖动态追踪(`trace` 或断点)。
   - 需联系库提供者获取明文代码或文档。

2. **函数未加载**  
   - 使用 `isCallable('yourFunctionName)` 确认函数是否存在于当前环境。
   - 使用 `libDefinedBy('yourFunctionName)` 检查函数所属库是否已加载。

3. **动态调用**  
   - 如果函数通过 `apply` 或 `eval` 动态调用,需结合运行时调试工具(如 `trace`)捕获路径。

---

通过上述方法,即使无法查看函数定义,也能有效追踪其调用路径和依赖关系,辅助定位问题或理解代码逻辑。
回复 烽火戏诸侯 2025-5-29 08:50
procedure(convertRectToPath()
    let((cv selSet rects dbu rectBBox ll ur width height
         pathWidth pathPoints newPath)

    ; 获取当前编辑的cellview
    cv = geGetEditCellView()
    when(!cv error("没有打开的版图窗口"))

    ; 获取选中对象
    selSet = geGetSelSet()
    when(!selSet error("没有选中任何对象"))

    ; 筛选出矩形对象
    rects = setof(obj selSet obj~>objType == "rect")
    when(!rects error("没有选中矩形对象"))

    dbu = cv~>dbuPerUU  ; 获取数据库单位

    ; 处理每个选中的矩形
    foreach(rect rects
        ; 获取矩形边界
        rectBBox = rect~>bBox
        ll = rectBBox~>ll
        ur = rectBBox~>ur
        
        ; 计算矩形尺寸
        width = xCoord(ur) - xCoord(ll)
        height = yCoord(ur) - yCoord(ll)
        
        ; 确定path方向(取较长边为方向)
        if(width >= height then
            ; 水平path
            pathWidth = height
            ; 计算中心Y坐标
            centerY = yCoord(ll) + height/2.0
            ; 创建两个点:左中和右中
            pathPoints = list(
                list(xCoord(ll) centerY),
                list(xCoord(ur) centerY)
            )
        else
            ; 垂直path
            pathWidth = width
            ; 计算中心X坐标
            centerX = xCoord(ll) + width/2.0
            ; 创建两个点:下中和上中
            pathPoints = list(
                list(centerX yCoord(ll)),
                list(centerX yCoord(ur))
            )
        )
        
        ; 创建新的path对象
        newPath = dbCreatePath(
            cv~>editLayer,  ; 使用原始矩形所在的层
            pathPoints,     ; 路径点
            pathWidth,      ; 路径宽度
            "truncate"      ; 端点样式
        )
        
        ; 复制其他属性(可选)
        newPath~>lpp = rect~>lpp
        newPath~>display = rect~>display
        
        ; 删除原始矩形(可选,取消注释以启用)
        ; dbDeleteObject(rect)
        
        ; 显示转换信息
        printf("转换矩形: 宽度 %.3fum, 高度 %.3fum -> Path宽度 %.3fum\n",
               width/dbu, height/dbu, pathWidth/dbu)
    )
   
    t  ; 返回成功
)
回复 烽火戏诸侯 2025-6-10 14:39
要实现上层DRC验证时隐藏底层未通过区域,可通过以下方法操作(以常用EDA工具为例):

### 方法1:使用层操作(推荐)
```tcl
# Calibre示例:创建忽略层
COPY LAYER_IGNORE FROM <底层错误层>  # 复制错误标记层
NOT LAYER_IGNORE LAYER_IGNORE       # 反转层(可选)
DRC CHECK MAP <上层规则> EXCLUDE LAYER_IGNORE  # 排除该区域
```

### 方法2:坐标屏蔽(精确区域)
```tcl
# 在规则文件中添加屏蔽区域(示例坐标)
DRC IGNORE BOX (x1 y1 x2 y2)  # 指定屏蔽矩形坐标
```

### 方法3:图形界面操作(Calibre RVE)
1. 打开RVE结果浏览器
2. 框选要隐藏的底层错误区域
3. 右键选择 **"Waive Selected"** → **"Waive as Area"**
4. 保存为`waive file.wv`并重新加载报告

### 方法4:层级过滤(Innovus/Milkyway)
```tcl
# 设置层级过滤
set_drc_ignore -region { {x1 y1} {x2 y2} } -layer METAL1
verify_drc -report_level top_only
```

### 方法5:脚本后处理(Python示例)
```python
import re
with open("drc_report.rpt") as f:
    for line in f:
        if "ERROR" in line:
            coords = re.findall(r'\((\d+),(\d+)\)', line)
            if not in_excluded_zone(coords):  # 自定义区域判断函数
                print(line)
```

### 关键注意事项:
1. **物理验证风险**:隐藏错误仅用于阶段性调试,最终必须修复所有DRC
2. **层级继承**:确保顶层验证命令添加 `-ignore_child_errors` 参数
3. **工具差异**:
   - Synopsys IC Validator: 使用 `set_error_region -exclude`
   - Cadence Pegasus: 在规则中用 `EXCLUDE_REGION`
4. **临时文件**:建议保留原始完整报告,生成过滤后的新报告文件

> ⚠️ **重要警告**:此操作仅适用于临时调试,tapeout前必须确保所有层级DRC完全clean!隐藏DRC错误可能导致芯片失效。

建议流程:
```mermaid
graph LR
A[原始DRC报告] --> B{识别底层错误区域}
B --> C[创建屏蔽层/坐标]
C --> D[重新运行上层验证]
D --> E[生成过滤报告]
E --> F[修复底层DRC]
F --> G[最终全芯片验证]
```
回复 烽火戏诸侯 2025-6-13 15:56
procedure(deleteOddVias()
    let((selectedVias sortedVias viaCount)
        ; 获取所有选中的孔(via)
        selectedVias = geGetSelSet()
        ; 过滤出孔对象(类型为"via"或"pathSeg"等,具体根据实际类型调整)
        selectedVias = setof(via selectedVias via~>objType == "via") ; 根据实际对象类型调整

        when(selectedVias
            ; 按X坐标排序(适用于水平排列的孔)
            sortedVias = sort(selectedVias 'lessp 'x)
            ; 若需要垂直排列孔排序,改用:
            ; sortedVias = sort(selectedVias 'lessp 'y)

            viaCount = length(sortedVias)
            printf("共选中 %d 个孔,将删除奇数位置孔...\n" viaCount)

            ; 遍历并删除奇数索引(索引从0开始,0/2/4...对应第1/3/5...个)
            for(idx 0 viaCount-1
                when(evenp(idx) ; 索引为偶数时对应奇数位置
                    dbDeleteObject(sortedVias[idx])
                )
            )
            printf("删除完成!剩余孔数:%d\n" viaCount - (viaCount+1)/2)
        )
    )
)

; 调用函数
deleteOddVias()
回复 烽火戏诸侯 2025-6-16 14:54
以下是实现将版图中的管子按照ABBA顺序排列的SKILL脚本。该脚本允许用户分两次选择管子(第一次选择标记为A,第二次选择标记为B),然后按ABBA模式(A, B, B, A, A, B, B, A...)重新排列:

```skill
procedure(ABBA_Arrange()
    let((A_insts B_insts sorted_all inst_width gap minX minY total_count indexA indexB newX origin)
        ; 提示用户选择A组管子
        hiDisplayPrompt("Select A group instances then press OK")
        A_insts = geGetSelSet()
        when(!A_insts
            hiDisplayAlert("No A group selected!")
            return()
        )

        ; 提示用户选择B组管子
        hiDisplayPrompt("Select B group instances then press OK")
        B_insts = geGetSelSet()
        when(!B_insts
            hiDisplayAlert("No B group selected!")
            return()
        )

        ; 按X坐标排序两组管子
        A_insts = sort(A_insts, lambda((a b) car(a~>origin) < car(b~>origin)))
        B_insts = sort(B_insts, lambda((a b) car(a~>origin) < car(b~>origin)))

        ; 确定最小数量以保持平衡
        total_count = min(length(A_insts), length(B_insts)) * 2
        indexA = 0
        indexB = 0

        ; 创建ABBA顺序列表
        sorted_all = nil
        for(i 0 total_count-1
            case(i % 4
                (0 ; A位置
                    sorted_all = cons(A_insts[indexA], sorted_all)
                    indexA = indexA + 1
                )
                (3 ; A位置
                    sorted_all = cons(A_insts[indexA], sorted_all)
                    indexA = indexA + 1
                )
                (t ; B位置(1和2)
                    sorted_all = cons(B_insts[indexB], sorted_all)
                    indexB = indexB + 1
                )
            )
        )
        sorted_all = reverse(sorted_all) ; 反转得到正确顺序

        ; 获取最小X/Y坐标作为起始点
        minX = min(append1(mapcar('car, mapcar('getq, A_insts, 'origin)),
                         mapcar('car, mapcar('getq, B_insts, 'origin))))
        minY = min(append1(mapcar('cadr, mapcar('getq, A_insts, 'origin)),
                         mapcar('cadr, mapcar('getq, B_insts, 'origin))))

        ; 计算实例宽度(取第一个A组实例的宽度)
        inst_width = (caar(cadr(A_insts[0]~>bBox)) - caar(A_insts[0]~>bBox))
        gap = inst_width * 0.2 ; 间隙为宽度的20%

        ; 移动实例到新位置
        for(i 0 total_count-1
            newX = minX + i * (inst_width + gap)
            origin = sorted_all~>origin
            dbMoveFig(sorted_all, list(newX - car(origin), minY - cadr(origin)))
        )

        hiDisplayPrompt("ABBA arrangement completed!")
    )
)

; 加载后执行以下命令运行函数
ABBA_Arrange()
```

### 使用说明:
1. **选择A组管子**:运行脚本后,按提示选择第一组管子(标记为A),点击OK
2. **选择B组管子**:按提示选择第二组管子(标记为B),点击OK
3. **自动排列**:脚本将执行以下操作:
   - 按X坐标排序两组管子
   - 按ABBA模式(A, B, B, A...)排列
   - 保持原始Y坐标对齐
   - 自动计算间距(宽度+20%间隙)

### 排列示例:
假设有2个A管和2个B管,排列顺序为:
```
A1 -> B1 -> B2 -> A2
```

### 特性:
- 自动处理不等量组(取最小数量保持平衡)
- 保持管子原始宽度和Y坐标
- 智能间距计算(宽度+20%间隙)
- 基于左下角坐标对齐
- 交互式选择提示

### 注意事项:
- 确保所有管子高度相同以获得最佳效果
- 操作前建议保存版图
- 适用于Virtuoso IC6.1.8及以上版本
- 管子连接关系可能需要重新布线

要使用此脚本,请将代码保存为`.il`文件并通过Virtuoso的CIW窗口加载执行。
回复 烽火戏诸侯 2025-6-17 08:51
procedure(destructiveRemoveNth(lst index)
  ;; 检查索引有效性
  when(index < 0 || index >= length(lst)
      error("Index out of bounds: %d" index)
  
  ;; 处理删除首元素的情况
  if(index == 0 then
      let((removed car(lst))
          setq(lst cdr(lst))  ; 直接修改列表指针
          removed             ; 返回被删除元素
      )
  
  ;; 处理其他位置元素
  else
      let((prevCell nth(index-1 lst)  ; 获取前一个单元格
          removed  cadr(prevCell))   ; 保存待删除元素
      ;; 破坏性修改链表结构
      rplacd(prevCell cddr(prevCell))
      removed  ; 返回被删除元素
)
回复 烽火戏诸侯 2025-6-18 14:22
如果你需要编辑的子模块不在当前层级,而是 **位于更底层(比如下两层)** 的嵌套结构中,可以通过以下代码 **逐层穿透实例** 并自动进入编辑模式。以下是具体实现方法:

---

### 代码目标:
假设当前处于顶层设计 `top_layout`,目标子模块 `"123"` 的层级路径为:  
**`top_layout -> blockA -> blockB -> 123`**  
需要自动穿透 `blockA` 和 `blockB` 两层实例,直接进入 `"123"` 的编辑模式。

---

### 完整代码:
```skill
A = geGetEditCellView()              ; 获取当前层CellView
C = geGetEditCellViewWindow()        ; 获取当前窗口
targetSubName = "123"               ; 目标子模块名称

;------------ 递归查找嵌套实例 ------------
procedure(findSubcellInstance(cv subName level)
    foreach(inst cv~>instances
        ; 若当前实例的Cell名称为目标名称,且到达指定层数,返回实例
        if(inst~>cellName == subName && level <= 0 then
            return inst
        )
        ; 递归向下查找子层实例
        subInst = findSubcellInstance(inst~>master subName level-1)
        if(subInst != nil then
            return subInst
        )
    )
    nil ; 未找到返回nil
)

;------------ 根据层数穿透 ------------
subInstance = findSubcellInstance(A targetSubName 2)  ; 往下2层
if(subInstance == nil then
    error("未找到往下2层名为 %s 的子模块!" targetSubName)
)

;------------ 选中并进入编辑 ------------
; 获取父层级路径(为了逐层穿透)
hierarchyPath = dbGetAncestorPath(subInstance) ; 获取实例的祖先路径

; 逐层进入并选中
foreach(ancestorInst hierarchyPath
    leHiEditInPlace(ancestorInst)    ; 进入父实例的Edit-In-Place模式
)

; 最终进入目标子模块
geSelectObject(geGetEditCellViewWindow() subInstance)
leHiEditInPlace(subInstance)
printf("已进入第 %d 层子模块 %s!\n" 2 targetSubName)
```

---

### 关键步骤说明:
1. **递归查找嵌套实例**:  
   通过 `findSubcellInstance` 函数遍历当前设计的实例,递归向下查找目标层级的子模块。参数 `level=2` 表示穿透两层。

2. **获取实例的层级路径**:  
   `dbGetAncestorPath` 获取目标实例的所有祖先实例(从当前层到目标层的路径),例如 `[inst_blockA, inst_blockB]`。

3. **逐层进入父实例**:  
   通过循环调用 `leHiEditInPlace` 逐层进入父实例的编辑模式,确保路径正确。

4. **编辑目标子模块**:  
   最终选中并进入目标实例的编辑模式。

---

### 适用场景:
- 已知子模块位于 **固定层数(如下两层)** 的嵌套结构中。
- 子模块的父层级实例名称可能未知,但需要通过结构穿透到达。

---

### 参数调整:
- **穿透层数**:修改 `level` 参数(例如 `level=3` 表示往下三层)。
- **动态输入**:可以将 `targetSubName` 和 `level` 改为交互式输入:
  ```skill
  targetSubName = enterString("请输入目标子模块名:")
  level = atoi(enterString("请输入穿透层数:"))
  ```

---

### 错误处理:
- 如果找不到目标子模块或穿透层数不足,代码会抛出明确错误提示。
- 使用 `dbGetAncestorPath` 确保层级路径的准确性,避免误操作。

---

通过这种方式,可直接通过代码操作穿透多层嵌套结构,无需手动逐层进入实例。这在处理深层次布局或电路图时非常高效!

facelist

您需要登录后才可以评论 登录 | 注册

  • 关注TA
  • 加好友
  • 联系TA
  • 0

    周排名
  • 0

    月排名
  • 0

    总排名
  • 3

    关注
  • 1

    粉丝
  • 4

    好友
  • 0

    获赞
  • 12

    评论
  • 11

    访问数
关闭

站长推荐 上一条 /2 下一条

小黑屋| 手机版| 关于我们| 联系我们| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2025-6-22 11:36 , Processed in 0.023546 second(s), 15 queries , Gzip On, MemCached On.

eetop公众号 创芯大讲堂 创芯人才网