4. 电平转换器(Level Shifter):为什么需要电平转换?LS类型(H2L/L2H)、插入策略、UPF中set_level_shifter用法

各位同学,咱们今天聊聊电平转换器。说实话,这东西在单电压域设计里根本用不上,但一旦你开始做多电压域,它就绕不开了。

我记得刚入行那会儿,第一次接触多电压项目,看到网表里密密麻麻的LS cell,心里直犯嘀咕:这玩意儿到底是干嘛的?后来被老工程师带着调了一个bug,才真正明白——没有电平转换,芯片根本没法正常工作。

为什么需要电平转换?

道理其实很简单。你想想看,一个模块工作在0.8V,另一个模块工作在1.2V。0.8V模块输出的高电平,也就0.8V左右。这个信号直接送到1.2V模块的输入端,能可靠地被识别为高电平吗?

不一定。1.2V模块的输入缓冲器,它的翻转阈值可能设计在0.6V~0.7V左右。0.8V的信号勉强能过,但噪声余量太小了。万一有IR drop,0.8V变成0.75V,那就直接误判了。

反过来呢?1.2V模块输出1.2V信号,送到0.8V模块。0.8V模块的管子栅氧化层可能只有1.0V的耐压。1.2V怼上去,时间长了栅氧击穿,芯片就废了。

所以,电平转换器的核心作用就两个:

  • 保证逻辑电平正确识别——低电压域到高电压域,把信号抬上去
  • 防止过压损坏器件——高电压域到低电压域,把信号降下来

核心原则:跨电压域的信号,必须经过电平转换。这不是可选项,是必选项。

LS类型:H2L与L2H

电平转换器分两种,方向不同,结构也不同。

类型 方向 典型应用场景 结构特点
L2H (Low to High) 低电压域 → 高电压域 核心逻辑(0.8V) → IO接口(1.8V) 交叉耦合PMOS对,需要两个电源
H2L (High to Low) 高电压域 → 低电压域 IO接口(1.8V) → 核心逻辑(0.8V) 简单反相器链或特殊降压电路

我个人习惯把L2H叫做「升压型」,H2L叫做「降压型」。L2H的结构相对复杂,因为它需要把低摆幅信号转换成高摆幅信号,内部有正反馈环路。H2L反而简单,有时候用几个反相器串联,配合电源电压就能实现。

不过要注意,H2L虽然结构简单,但功耗不一定小。我在项目中遇到过一种情况:H2L cell的短路电流很大,因为输入信号摆幅小,PMOS和NMOS同时导通的时间变长了。所以选型的时候,不能只看面积,还得看功耗。

插入策略:什么时候插?谁来插?

这个问题,很多初学者搞不清楚。电平转换器到底是谁负责插入?是RTL设计师?还是后端工程师?

答案是:都可以,但通常由后端工具自动插入

RTL设计师可以在代码里例化电平转换器,但我不推荐这么做。原因很简单:

  • RTL里硬编码LS cell,灵活性差,后期换工艺库得改代码
  • LS的插入位置和驱动强度,需要结合物理信息才能优化
  • UPF里用set_level_shifter,工具会自动处理,省心

那后端工具怎么插?一般分两步:

  1. 综合阶段:DC或者Genus根据UPF约束,在跨域路径上插入LS cell
  2. 布局布线阶段:ICC2或者Innovus在place阶段,根据物理位置优化LS的摆放

我曾经踩过一个坑:综合阶段插了LS,但布局阶段发现LS cell被放到了很远的地方,导致跨域路径绕线过长。后来我学乖了,在UPF里加了约束,让工具把LS cell放在电压域边界附近。

个人建议:LS的插入策略,优先考虑「边界插入」。也就是把LS cell放在电压域边界,靠近接收端。这样跨域走线是低电压信号,功耗更低。

UPF中set_level_shifter用法

好了,到了实操环节。UPF里控制电平转换器的命令,就是set_level_shifter。这个命令的语法,我给大家拆解一下。

set_level_shifter <ls_name> \
  -domain <pd_name> \
  -applies_to <input|output|both> \
  -rule <low_to_high|high_to_low|both> \
  -location <self|parent|fanout> \
  -threshold <voltage> \
  -source <primary|secondary>

参数解释:

  • -domain:指定哪个电压域需要插入LS。一般是低电压域
  • -applies_to:LS插在域的输入端口还是输出端口。input表示从外部进入本域的信号需要LS,output表示本域输出到外部的信号需要LS
  • -rule:指定转换方向。low_to_high就是L2H,high_to_low就是H2L
  • -location:LS cell放在哪里。self表示放在本域内,parent表示放在父域内,fanout表示放在扇出端
  • -threshold:电压阈值。当两个域的电压差超过这个值,才插入LS
  • -source:LS cell使用哪个电源。primary用主电源,secondary用备用电源

举个例子,实际项目中我常用的写法:

# 在PD_CORE域的所有输入端口上,插入L2H电平转换器
set_level_shifter ls_core_input \
  -domain PD_CORE \
  -applies_to input \
  -rule low_to_high \
  -location self \
  -threshold 0.3 \
  -source primary

这条命令的意思是:凡是进入PD_CORE域的信号,如果来自更低电压的域,并且电压差超过0.3V,就在PD_CORE域内部插入L2H电平转换器。

注意:-threshold参数很关键。如果设得太小,不必要的路径也会插LS,浪费面积和功耗。设得太大,该插的路径没插,功能出问题。我一般设0.2V~0.3V,具体看工艺库的容忍范围。

另外,-location参数也值得多说两句。我习惯用self,也就是LS放在接收域内部。这样做的好处是:

  • 跨域走线是低电压信号,动态功耗小
  • LS cell靠近接收寄存器,时序更容易收敛

但有些设计团队喜欢用parent,把LS放在发送域。各有优劣,没有绝对的对错。我个人偏向self,因为低电压走线对IR drop更敏感,走线长了容易出问题。

避坑指南

最后,分享几个我踩过的坑:

  • 漏插LS:UPF里忘了设set_level_shifter,工具默认不插。结果仿真没问题,流片回来跨域信号全乱套。嗯,从那以后我每次都要检查UPF的LS覆盖率。
  • LS驱动强度不够:工具自动选的LS cell驱动能力偏小,高负载路径上信号斜率变差。我后来在UPF里加了驱动强度约束,或者手动例化大驱动LS cell。
  • LS和ISO顺序搞反:有些路径既需要电平转换,又需要隔离。顺序很重要——先隔离再电平转换,还是先电平转换再隔离?这个得看具体场景,我一般遵循「先处理电压,再处理逻辑」的原则。

好了,关于电平转换器,今天就聊到这儿。下一章咱们讲隔离单元,这两个东西经常一起出现,到时候再细说它们之间的配合关系。