news 2026/10/6 1:05:25

片上眼图实现原理:从EOM到2-D Eye Scan的完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
片上眼图实现原理:从EOM到2-D Eye Scan的完整指南

当信号速率跑到16Gbps、32Gbps甚至更高之后,很多做高速接口的工程师都会遇到同一个尴尬:实验室里用示波器测出来的眼图明明是张开的,可芯片一到系统里就跑不稳,误码率居高不下。问题出在哪?很多时候是因为你测的是封装引脚或者PCB走线末端的状态,而真正决定系统能不能工作的,是芯片内部接收器那一端的信号质量。这两年我花了不少时间在片上眼图(On-Chip Eye Monitor)相关的项目上,从最基础的EOM(Eye Opening Monitor,眼图张开度监视器)一路做到2-D Eye Scan的完整实现,踩了不少坑,也把原理彻底摸了一遍。这篇就来把片上眼图从EOM到2-D Eye Scan的实现原理讲清楚,顺便聊聊哪些地方是真正容易翻车的。

想看懂这篇文章,你最好对高速SerDes接收端的基本结构有个概念,了解CDR(时钟数据恢复)和误码率测试大概是怎么回事。如果你只是听说过“眼图”但不知道片上眼图的价值,也没关系,我尽量把背景讲透,内容从原理到工程实现都覆盖,适合做接口IP验证、芯片测试、信号完整性分析的工程师参考。

1. 什么是片上眼图,为什么非做不可

片上眼图,本质上就是把“示波器”塞进芯片内部,在接收器的模拟前端之后、CDR采样判决之前,把信号的电压裕度和时间裕度测出来。它不像外部示波器那样能看到完整的波形轨迹,而是通过采样统计的方式,重建出一个类似眼图的“质量地图”。

1.1 外部仪器眼图与片上眼图的本质区别

传统测眼图的方式大家都很熟:用高速示波器在芯片的接收引脚上测量差分信号,触发后叠加成千上万个UI(Unit Interval,单位间隔)的波形,得到六边形一样的眼图。这个画面能直观反映信号的幅值噪声、上升下降沿、过冲和抖动,但对芯片设计者来说有个致命短板——它测不到芯片内部真正的判决点。

外部示波器看到的是信号在芯片引脚上的样子,而信号经过封装寄生参数、ESD结构、接收器输入缓冲之后,波形会被明显劣化。尤其在超高速接口上,封装带来的反射和损耗可能让内部眼图比外部看起来小20%甚至更多。我做过的某个项目里,外部眼高有180mV,看起来很好,但片内EOM测出来只剩90mV,差点不满足接收灵敏度规范。这就是必须做片上眼图的第一个原因:你真正要关心的是芯片判决电路看到的信号,不是引脚上的信号。

第二个原因是可观测性。芯片封装好之后,高速信号引脚不可能再拉出来测,探针也点不到内部节点。片上眼图提供了一条在系统级测试和量产测试中都能使用的观测通道,不需要拆封装、不需要高频探针台,通过低速数字接口就能把眼图信息读出来。

1.2 片上眼图到底能干什么

说得具体一点,片上眼图技术能解决三类实际问题:

  • 通道评估:芯片实装到系统板上之后,用片上眼图快速判断PCB走线、连接器、背板对信号的损伤程度。
  • 接收器性能验证:测量DUT内部判决点的眼高、眼宽,验证接收灵敏度是否满足设计指标,尤其在工艺角、温度、电压变化下做特性化测试。
  • 系统链路调试:当系统误码率超标时,用眼图扫描结果定位问题是幅值裕度不足还是时序裕度不足,辅助判断是否需要调整接收均衡参数(CTLE、DFE)。

EOM就是解决这些问题的第一种片上实现形态,它只量化“眼高”和“眼宽”两个标量指标,相当于在眼图中央区域取一个点做判决裕度测试,输出两个数字。2-D Eye Scan则是把EOM从“单点评估”扩展成“二维扫描图”,在电压和时间两个维度上逐点测试误码率,生成类似示波器眼图的二维等高线图。两者一脉相承,实现上2-D要复杂得多。

2. 从EOM:眼高眼宽监视器的实现逻辑

EOM的实现思想其实很朴素:与其盯着完整波形看,不如直接去问接收判决电路“这个位置的信号你判对了吗”。它把测量问题转化成了误码率检测问题。

2.1 基本结构:一个可移动的“虚拟判决点”

先看接收端正常的工作路径。输入信号经过CTLE和DFE之后,进入一个判决器(slicer),判决器在CDR恢复出来的时钟节拍下,将模拟信号量化为0或1。这里的时钟相位通常是CDR锁定后的最优采样点,电压判决门限一般是交流共模电平附近。

EOM的做法是:在同一个输入信号上,再挂一路(或两路)额外的判决器,称为“监视判决器”(monitor slicer)。这路判决器有两个旋钮:

  • 电压旋钮:可以编程调整判决门限,从正电压到负电压连续或步进可调。
  • 时间旋钮:可以编程调整采样时钟的相位,相对于主数据通道的采样点提前或延后。

实际操作时,把监视判决器的电压门限设为Vmon,采样相位设为Tmon,然后让它对进来的PRBS数据流做判决,再把判决结果和已知发送序列比对(通常由PRBS生成器本地复现),统计误码率。如果Vmon和Tmon落在眼图张开的区域内,误码率就会很低;如果偏移到了眼图边界之外,误码率就会急剧上升。通过二分搜索找到“误码率从低变高”的边界点,就得到了眼图的轮廓。

2.2 眼高眼宽怎么“搜”出来

EOM搜眼高和眼宽的逻辑,和我们肉眼判断示波器眼图差不多,但用的是算法:

眼高的搜索流程是这样的:先固定采样相位在最佳采样点,然后用监视判决器的门限从零开始往上抬,在每个门限值下统计一段时间内的误码数,计算误码率。当BER超过某个阈值(比如1e-6),就认为这个门限已经碰到了眼的“上眼皮”;同样,把门限往下压找到“下眼皮”。上眼皮电压减下眼皮电压,就是当前时序条件下的眼高。

眼宽搜索对称进行:固定门限在最佳判决电压,把采样时钟相位提前,找到BER超过阈值的左边界,再延后找到右边界,两边界的相位差就是眼宽。

实际工程中不会真的只测高和宽两条线,至少会测多个电压档位下的宽度,形成一个类似“眼图轮廓”的粗略形状。但核心输出的标量仍然是眼高和眼宽,所以叫EOM。

2.3 为什么EOM测出来的眼高不能直接用示波器眼图去对比

这里有个很容易踩的坑:EOM测量结果和示波器眼图完全对不上。我在做相关性测试时就吃过这个亏,EOM报眼高120mV,示波器眼图显示眼高160mV,差点怀疑片内测量电路坏了。后来才搞明白,EOM的判决门限是以接收器输入共模为参考的直流电压,而示波器的眼高是波形分布的统计结果,两者不仅参考点不同,带宽、均衡效果也不一样。

更关键的一点是,EOM测量的是“判决裕度”,它隐含了接收器自身均衡和CDR已经生效后的效果。示波器在引脚上测到的是未经均衡的原始信号,眼图当然看起来不一样。做SoC测试规范时,通常不会要求EOM和示波器眼图完全相等,而是要求它们随通道损耗、均衡配置变化的趋势一致。这一点你一定要和验证团队提前对齐口径,否则后面天天扯皮。

3. 2-D Eye Scan:把单点监视变成全眼扫描

EOM只能给两个标量,用来做量产测试和粗检足够了,但调系统时远远不够。比如链路误码,到底是高电平噪声太大还是时钟抖动太大?EOM分不清。2-D Eye Scan就是解决这个问题的进阶形态。

3.1 二维扫描的核心思想

2-D Eye Scan的做法,是把眼图在电压-时间平面上划分成密密麻麻的网格,每个网格点(V,T)都对应一个“虚拟判决点”。控制逻辑让监视判决器依次扫描这些网格点,在每个点统计误码率,最后把误码率数值映射成颜色或等高线,就得到了一张和示波器眼图非常相似的图。

这里最核心的概念是:2-D Eye Scan本质上是BER(误码率)等高线图。它画出来的不是信号的绝对幅度,而是一个判决点在某个电压和相位配置下的判决可靠性。外圈颜色深的地方,代表信号大概率不会越过这个区域,误码率低;内圈“眼睛”张开的区域,误码率更低;一旦扫描点超出眼图边界,误码率迅速恶化,颜色就变成高误码区。

3.2 网格划分怎么定

网格密度直接决定了扫描时间和精度。假设我们要在350mV的电压窗口内做64个电压步进,在1个UI(比如100ps,对应10Gbps速率)内做64个相位步进,那么总共有64×64=4096个网格点。每个网格点如果要统计到10个误码才能可信地得出1e-6的BER的话(统计置信度后面细说),每个点至少要测10^7个bit。10Gbps下一个点的测试时间就是1ms,全图就是4.096秒。听起来还行?

但如果你要做1e-8的误码率检测,一个点就要测10^9个bit,全图就是4096秒,一个多小时。所以实际工程中必须做取舍:

  • 粗扫阶段:64×32网格,每点测到1e-6即可,单次全图控制在几十秒内。
  • 精扫阶段:只对眼睛边界附近的小区域加密,用更小的步进和更长的统计时间,测量最低可测误码率。
  • 自适应扫描:先粗扫定位眼睛边界,再在边界区域自动插入更细的扫描点,兼顾速度和精度。

我们项目里用的是两步扫描法:第一阶段128×64网格、每点BER到1e-6,大约耗时15秒;第二阶段在眼图边界动态加密到512×128精度的局部扫描,耗时2分钟以内。这个方案在测试时间和信息量之间平衡得不错。

3.3 顺序扫描和并行扫描两种架构

在硬件实现上,2-D Eye Scan有两种典型架构:

顺序扫描架构:只有一个监视判决器,控制状态机让它按行扫描整个网格。逻辑简单,面积开销极小,就是EOM硬件外加一个扫描控制器。缺点是扫描速度慢,每个网格点都是串行测量。

并行扫描架构:在硅片上放置多个监视判决器,每个负责一个电压档位或多个相位档位,同时测量。理论扫描时间可以缩短到原来的1/N。代价是面积和功耗明显增加,而且多个监视判决器之间还有失配校准问题,处理不好会把测量误差直接带进结果。

我们做的是折中方案:两个监视判决器同时工作,一个固定在当前最优采样点测参考BER,另一个负责扫描。这样扫描过程中能实时监测测试环境是否稳定,防止因为电源漂移导致测量失真。参考BER通道在扫描结束后也可以用来做数据校正。

4. 核心模块:电压与相位怎么精确控制

2-D Eye Scan原理上不复杂,真正难的是模拟前端的精确控制。之前说过,监视判决器的核心是两个可调参数:电压门限和采样时钟相位。这两个模块的精度和线性度,直接决定了扫描图的质量。这也是EOM和2-D Eye Scan项目里技术含量最高的部分。

4.1 电压步进:从电流舵DAC到树形开关

片上眼图通常需要一个高精度DAC来产生电压门限。这个DAC和普通的数字DAC不太一样,它输出的电压直接接到比较器的参考端,任何毛刺和噪声都会被当成测量误差。

常用的结构是R-2R梯形网络或者电流舵(current-steering)结构。R-2R的好处是面积小、单调性好,但速度慢,适合EOM这种低频更新场景。电流舵速度快,但电流源匹配性受工艺影响大,需要额外的校准电路。

对DAC来说,单调性比绝对精度更重要。眼图扫描时,我们希望电压从低往高扫,门限依次递增,如果DAC在某一段出现非单调,扫描结果就会产生假边界,重现性变差。实际项目中我们对DAC做了INL/DNL测试,要求DNL小于±0.5 LSB,INL小于±2 LSB,否则在画等高线时会出现锯齿。

电压步进具体取多少呢?经验公式是:扫描电压范围需要覆盖信号摆幅的±20%以上,电压步进要小于最小可接受眼高的1/10。举个例子,接收灵敏度要求眼高不低于100mV,那么电压步进最好小于10mV。如果信号差分摆幅是600mVpp,扫描范围取±200mV左右,200mV除以10mV就是20个步进。但为了把眼图轮廓画平滑,实际建议至少做到64步。

4.2 相位插值器:从分频时钟到精细延迟

相位控制是2-D Eye Scan另一个关键点。我们不可能给每个扫描相位都生成一个独立时钟,太奢侈了。通用方案是使用相位插值器(Phase Interpolator,PI),从一个多相位时钟源(比如4相或8相时钟)中,通过电流权重插值生成任意中间相位。

PI的基本原理可以这样理解:假设我们有0°和90°两相时钟,它们都是方波。给两路时钟分别配置不同的电流权重,再经过模拟加法器合成,输出信号的过零点就会在0°和90°之间线性移动。权重的组合由数字码控制(比如8-bit分辨率就是256级),这样在90°范围内能实现约0.35°的分辨率,在10Gbps下对应约0.1ps。

PI最怕的问题是增益失配和非线性。实际测下来,PI的相位非线性会影响扫描图的横向比例,导致边界位置偏移。通常做法是在出厂前做一次一维校准,测出相位码和实际延迟的对应关系,存进查找表,扫描时通过查找表反查需要的码值。

4.3 数据比较与误码统计

监视判决器判决出来的数据和本地PRBS发生器做异或比对,如果不一致就产生一个error pulse。这个error pulse喂给一个计数器,扫描控制器定期读取计数值,除以总比特数得到BER。

这里要特别注意一个细节:错误计数器的位数要够。如果采样率是10Gbps,BER检测目标是1e-12,那么期望的错误间隔是10^12 bit,约100秒一个错。计数器的溢出问题设计时要考虑。

还有一个容易忽略的点是比较器的亚稳态。当输入信号幅度接近判决门限时,比较器输出可能进入亚稳态,判决结果随机化,表现为误码率“虚假”升高。这个在某些场景下会影响边界搜索,但反过来想,亚稳态本身也是判决不确定性的体现,从测量角度看它恰好反映了真实BER。所以多数设计不特别处理比较器亚稳态,除非要做极低BER的外推。

4.4 校准与精度:测量之前必须做的功课

模拟前端再精准,不校准也是白搭。我强烈建议在2-D Eye Scan模块中至少内置三种校准模式:

电压失调校准:将监视判决器输入端短接到共模电压,扫描DAC码值,找到输出发生翻转的码值作为零失调点,记录下来。

PI相位校准:通过数字可控延迟线和已知的参考时钟对齐,测量PI每个相位码的实际延迟,建立延迟查找表。

温度漂移补偿:校准结果不能只测一次,至少要做一阶温度补偿,或者安排一个后台校准周期,在扫描任务空闲时自动重校准。

我们实测过,不做电压校准之前,扫描出来的眼睛上下眼皮不对称,中间点偏了约15mV。做了失调校准之后,对称性恢复正常,眼高测量误差从约10%降到2%以内。校准不是可选项,是必选项。

5. 完整实现流程:从指标到量产

前面讲的都是原理和模块,现在把整个实现流程串一遍。一个完整的片上眼图项目,从立项到量产的路径大概分这么几步。

5.1 指标分解与硬件架构确定

先回答几个问题:最高支持速率是多少?目标BER检测范围是多少?扫描一张全图允许多久?允许的面积和功耗预算呢?这些直接决定架构选择。

比如目标速率是3.2Gbps到16Gbps可调,那么PI的工作频率范围必须覆盖对应时钟频率,比较器带宽必须支持最高速率的信号分量。如果只测NRZ信号,链条会简单一些;如果要兼容PAM4,判决逻辑和眼图解释方式就得重做。

功率和面积的测算也有大致的经验值。单路比较器加DAC和PI大概占0.01mm²(在先进工艺下),64路并行结构面积就会放大到0.5mm²以上。做之前先画一个面积/速度权衡表,再决定并行度。

5.2 扫描流程与数字控制状态机

扫描控制逻辑确定后,状态机的设计要覆盖这几个动作:初始化校准、设定扫描起点和步进、启动误码计数器、等待统计时间、读取计数值、存入SRAM、移动扫描点、循环直到扫完。

一个实用的流程是:

  1. 初始化:配置PRBS多项式阶数(常用PRBS7、PRBS15、PRBS31),锁定DUT的CDR。
  2. 定位眼中心:先用EOM模式粗搜眼高眼宽,找到眼睛中心点作为扫描原点。
  3. 定义扫描窗口:根据粗搜结果,确定电压和时间的扫描范围,一般取眼图边界的1.5倍范围。
  4. 执行二维扫描:逐行扫描,每行扫描结束后可选做SPI回读,实时刷新进度。
  5. 数据处理:把原始BER矩阵上传到上位机,用软件做2D插值和平滑,生成等高线图。

5.3 测试模式与系统集成

片上眼图模块在芯片里有两种工作模式,一个叫自测模式,一个叫观测模式。

自测模式:发送端和接收端都集成在芯片里,发送PRBS信号通过内部回环路径送到接收端,接收端眼图模块测量。这是芯片出厂测试和调试时的标准用法。

观测模式:发送端不参与,接收端测量来自外部通道的真实信号。此时要求外部信号必须携带和本地PRBS生成器同步的码型,否则误码比对没法做。实践中用外部误码仪发送时钟同步的PRBS信号,或者通过协议握手实现序列同步。

集成时另一个常见问题是寄存器接口设计。眼图扫描会产出一大块数据(比如64×64的BER矩阵,每个点一个32位计数值,总计约16KB),需要通过慢速接口读出去。SPI速度如果是10MHz,传完这些数据大约13ms,影响不大。关键是寄存器的读操作要和扫描状态机互锁,别扫描还没完成就读到半截数据。

5.4 上位机处理和可视化

硬件只负责把BER矩阵吐出来,真正形成眼图还要靠上位机。我用的是Python脚本,从SPI读出原始数据,清洗后做如下处理:

  • 把原始误码数除以总比特数得到BER矩阵。
  • 对BER进行对数变换,以1e-3到1e-9为标尺映射颜色。
  • 用scipy的griddata做插值,让边界更光滑。
  • 叠加标记眼高、眼宽、中心点等关键指标。

实际做出来的图,对调试真的帮助巨大。有次系统跑不稳定,外部示波器看眼图还挺开,我们2-D Eye Scan一扫,发现眼睛左边界明显向内塌了一块,再一看CDR的相位裕度快没了,才定位到是参考时钟的抖动偏大。这种问题用EOM很难发现,因为EOM只看中心点附近的裕度。

6. 常见问题排查与实测经验

这片内容是我最想分享的,因为原理看完都能懂,但真正做项目时遇到的九成问题都出在“没想到”上。

6.1 扫描结果重现性差

一个典型的故障表现是:同一块芯片,同一个配置,跑两次2-D Eye Scan,结果眼高相差20%以上。先排除测试环境因素:电源供电是否稳定、温度是否变化、参考时钟源是否换了。然后看扫描顺序——如果你每次扫描都从同一方向扫,而系统存在长期漂移,结果就会带上方向性偏差。

解决方法是轮换扫描方向,比如第一次从左上到右下,第二次从右下到左上,两次结果取平均。我们项目里加上这个措施后,重复性标准差从8mV降到3mV以内。

6.2 眼图边界出现“毛刺”

扫描出来的BER等高线边界不光滑,局部出现尖刺或者小岛,多数时候是统计样本不足导致的随机波动。BER本身就是随机变量,在低于1e-9的量级时,一个误码的有无就能让该点BER跳一个数量级。解决办法很简单:在感兴趣边界区域增加每个点的统计比特数,并把相邻点的BER做3×3中值滤波。

除非毛刺集中在某个固定电压附近,那就要怀疑DAC或比较器有局部非单调性,检查校准数据。

6.3 测量结果和外部示波器差太远

前面已经提过一次,这里再细化。除了参考点和均衡差异,还有一个重要因素是片上眼图测的是BER,示波器测的是波形直方图。波形分布的前沿对应BER的积分,两者边界并不重合。示波器眼图的“眼高”一般定义为上下电平分布均值减三倍标准差,对应大约1e-9量级的BER轮廓;而EOM和2-D Eye Scan如果设置BER阈值是1e-6,边界自然比示波器眼睛看起来更大。

所以做相关性对比时,一定要先校准BER阈值。比如你说“2-D Eye Scan上的1e-9等高线应该等于示波器3σ眼高”,对比才有意义。

6.4 低速接口读数据卡顿

这个属于集成细节了。扫描控制器在每行结束时会在状态寄存器里写一个标志位,上位机如果轮询太频繁,SPI带宽会被无效读操作占满。建议每次扫描结束后一次性把所有数据读走,不要边扫边读。另外注意大端小端问题,不同EDA工具链对寄存器字节序的处理不一样,吃过大亏。

6.5 扫描时间过长怎么办

除了降低精扫范围和BER阈值之外,还有一个工程上常用的小技巧:利用PRBS产生的时间间隔做非均匀统计。眼睛中心区域信号可靠性高,可以快速跳过,把统计时间集中放在边界附近。这个叫自适应重点采样,做得好可以缩短一半扫描时间而不损失信息量。

7. 写在后面的经验

做片上眼图这个方向,给我最大的体会是:它是那种“原理一句话,工程万重山”的事情。EOM相对简单,一两个比较器加一个状态机就能跑,适合做量产筛片;2-D Eye Scan则是把测量能力提到了一个层级,让你能看到眼睛内部的全貌,调试高速链路时真的离不开它。

如果你正准备在新项目里加这个功能,我建议不要一上来就奔着最全的2-D扫描去。先搭一个EOM跑通链路,验证比较器、DAC、PI这些模拟模块的工作正常,校准算法稳定之后,再在同一个架构上扩展扫描状态机和数据处理逻辑。这样每一步的风险都可控,后面出问题也好定位。

另外,做片上眼图模块务必从第一天就考虑可测试性。片上眼图本身是测别人的,但它自己也是需要被测的。工厂测试模式里要给模拟前端留出直通测试通道,不然出厂连校准都做不了。

先写到这。如果你手头也在做类似的片上监测功能,欢迎交流具体实现细节,尤其是校准和扫描时间优化这两块,我一直觉得还有不少可以折腾的空间。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 1:05:24

TMC4671:单芯片FOC运动控制SoC原理与实战调优

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 1:04:35

STM32参考设计查找指南:平台、验证与落地实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 1:04:13

Allegro差分走线优化:引脚交换解决BGA极性反接与反向标注实操

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 1:03:42

ESP32-C5硬件设计指南:原理图、PCB布局与调试实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 1:03:42

乐鑫ESP-Mosaico:面向量产的固件分发标准框架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 1:03:03

SC8815数控电源实战:从立创EDA到I2C抗干扰布线

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华