在 Cadence Virtuoso 的 SKILL 编程以及日常操作中,大家常常会对 Justification(对齐方式)、Rotation(旋转与镜像)以及坐标变换计算函数 dbConcatTransform 感到迷惑。这些图形变换属性在 Schematic 和 Layout 中无处不在:当放置 Label 或 Pin 时,它是怎样进行原点计算的?当我们把一个 Instance 下移、旋转后,它的实际坐标又是如何通过矩阵推导出来的?

本文将为你揭开这些坐标变换的面纱,并且提供了两个可以在网页上直接交互的演示 Demo,帮助你一目了然地理解这背后的工作原理。

1. Justification 与 Rotation 是怎么互相作用的?

在 Virtuoso 中,当你设置一个文本(Text/Label)的属性时,有两个最关键的参数:

  • Justification:指文本的“锚点(Anchor)”或者“原点”在其边界框(Bounding Box)上的什么位置。常见的值包括 lowerLeft, centerCenter, upperRight 等共 9 种。
  • Rotation / Orient:指基于这个原点,图形如何进行旋转和镜像操作(例如 R0, R90, MX, MYR90 等)。

几何法则: Virtuoso 总是先确立图形内部的 Justification(即:原点对齐关系),然后以该原点为轴心执行 Rotation 变换。

🎮 交互式演示 1:Justification 与 Rotation 效果图

尝试在下方切换 JustificationRotation,观察蓝色的字母 "F" 和虚线边框如何相对于红色的十字原点进行变动。




2. 理解嵌套变换的利器:dbConcatTransform

当我们处理拥有层次结构(Hierarchy)的电路层或版图时,经常会遇到坐标系联结的问题。假设:

  • P1:顶层中的一个 Instance(比如 I0),它的坐标是 (x1, y1),方向是 Orient1
  • P2:在这个 Instance 内部的 Cell 中,有一个点(或者子 Instance),其相对坐标是 (x2, y2),方向是 Orient2

如果我们想找出 P2 在顶层坐标系中的绝对坐标和方向,就需要把这两个 Transform 相链接(Concatenate)。Virtuoso 给我们提供了一个极其常用的 API:

absolute_transform = dbConcatTransform( list(x2 y2 Orient2) list(x1 y1 Orient1) )

注意 API 参数顺序dbConcatTransform( T1 T2 ) 表达的数学意思是:先对物体做 T1 变换,然后再做 T2 变换。即 $T_{result} = T2 \cdot T1$。它最终计算出的是一个新的 list(X Y Orient)

矩阵推导

一个变换 $T = (D, O)$ 作用于点 $P$ 表示为 $P_{new} = O * P + D$。 先做 $T1 = (D_1, O_1)$,再做 $T2 = (D_2, O_2)$: \(P_{final} = O_2 * (O_1 * P + D_1) + D_2\) 展开得: \(P_{final} = (O_2 * O_1) * P + (O_2 * D_1 + D_2)\) 所以:

  • 新旋转:$O_{result} = O_2 \times O_1$(即先 O1,再 O2)
  • 新坐标:$D_{result} = O_2 \times D_1 + D_2$(即将 D1 坐标通过 O2 的旋转后,再加上 D2)

🧮 交互式演示 2:dbConcatTransform 计算器与轨迹

在下面填入参数,即可推导出 dbConcatTransform(T1, T2) 的结果。图上会显示字母 F 在基础坐标系(浅蓝) -> 经过 T1 后(橙色) -> 最终经过 T2 后(红色)的坐标推导过程。

T1 (内部 Cell 坐标)

X1: Y1:
Orient1:

T2 (Top Instance 坐标)

X2: Y2:
Orient2:
运算结果: dbConcatTransform(...)

3. 常见报错与避坑

在使用 dbConcatTransform 时,许多工程师会因为搞混 T1T2 的顺序导致 Bug。 如果你是要计算底层 term_shape 相对于顶层的坐标,一定是:

  1. 底层自身的相对坐标与旋转:T1
  2. 上级 Instance 的坐标与旋转:T2
  3. 组合:dbConcatTransform( T1 T2 )

此外,dbConcatTransform 返回的是一个列表!在进行下一次运算或写入属性时,记得将列表解包。 例如:

let((abs_tf result_x result_y result_r)
    abs_tf = dbConcatTransform(inst_term_tf inst_tf)
    result_x = xCoord(abs_tf)
    result_y = yCoord(abs_tf)
    result_r = nth(2 abs_tf) ; 获取 Orient 字符串
)

希望通过以上两个图解和小程序,你能彻底告别 Virtuoso 中算不清楚坐标变换的噩梦!