1. 时序图这件小事,为什么值得单独拎出来讲
做 FPGA 的人都有一个共识:代码写得好不好,仿真跑一跑就知道;但时序关系讲不讲得清,一张图就能看出功底。你肯定遇到过这种场景——跟同事讨论一个跨时钟域握手协议,嘴上说了十分钟"先拉高 valid,等两个周期后 ready 有效,然后 data 保持到 ready 拉低后的下一个时钟沿",对方还是一脸茫然。这时候你打开 TimeGen,五分钟画一张时序图甩过去,沟通成本瞬间归零。
TimeGen 就是干这个的。它是一款专门用来画数字时序图的工具,定位非常明确:不搞花里胡哨的矢量绘图,就是让你快速、准确地画出时钟、信号、总线、状态这些数字设计里最常见的东西。3.2 这个版本在 FPGA 圈子里流传很广,体积小、启动快、上手门槛低,画出来的图直接能贴进设计文档、专利交底书、项目周报里。
这篇文章适合谁看?如果你是刚入行的 FPGA 工程师,正在为怎么写设计文档发愁;如果你是在校学生,做课程设计或参加嵌赛需要画时序说明;如果你是老手但一直用 Visio 或手绘凑合,想找个更专业的工具——那这篇内容你应该能从头看到尾。我会把 TimeGen 3.2 的完整使用流程、参数设置背后的逻辑、以及我这些年踩过的坑,全部摊开讲清楚。
需要先说明一点:网上流传的所谓"特殊版本"文件,来源不明,可能捆绑恶意程序或篡改过的可执行文件,对开发环境和个人信息都存在风险。我个人的建议是走正规渠道获取授权,学生可以看看有没有教育优惠,公司项目该买就买,省下来的排查时间远比授权费值钱。本文只讲工具本身的使用方法,不涉及任何获取途径的讨论。
2. 先搞清楚时序图到底在画什么
2.1 数字设计里时序图的三层信息
很多人画时序图,画了半天只是把信号波形摆上去,结果关键信息全丢了。一张合格的时序图,至少要承载三层信息。
第一层是信号的电平变化,也就是哪根线在哪个时刻从低变高、从高变低。这是最基础的,TimeGen 里用鼠标点几下就能画出来。第二层是时间关系,包括时钟周期、建立时间、保持时间、信号之间的先后顺序、某个信号需要维持多少个周期。这层信息决定了你的设计能不能正常工作,也是时序图真正的价值所在。第三层是约束和标注,比如某个信号必须在另一个信号之后 2ns 到 5ns 之间有效,或者某段数据需要保持稳定直到握手完成。
我见过太多人只画第一层,图看起来挺漂亮,但拿给验证工程师看,对方第一句话就是"这个 data 要保持几个周期?"——因为图上根本没标。TimeGen 的好处在于,它把这三层信息都做进了工具里,关键看你会不会用。
2.2 为什么不用 Visio 或手绘
有人会问,Visio 也能画时序图,手绘更快,为什么要专门用 TimeGen?
手绘的问题在于不精确。你画一条斜线表示信号上升,别人没法判断这是 1ns 还是 10ns 的边沿。Visio 的问题在于效率低,画一个时钟信号你得一根一根竖线去摆,改一个周期数就得全部重来。TimeGen 的核心优势是参数化:你告诉它时钟周期是 10ns,它自动帮你把整个时钟波形铺满;你告诉它某个信号延迟 3 个周期,它自动帮你对齐。改参数的时候,整张图跟着变,不用手动调。
还有一个隐性优势是一致性。团队里大家都用 TimeGen,画出来的图风格统一,信号命名规范一致,评审的时候不会因为图本身的问题浪费时间。这个价值在项目规模大了之后特别明显。
2.3 TimeGen 3.2 的界面逻辑
打开 TimeGen 3.2,界面分成几个区域:左边是信号列表,中间是波形绘制区,上方是工具栏,下方是时间轴。这个布局跟大多数波形工具类似,但有几个细节需要特别注意。
信号列表里每一行代表一根信号线,你可以给它命名、设置颜色、选择类型(时钟、普通信号、总线、状态机等)。波形绘制区是主战场,鼠标左键点击切换电平,右键拖拽可以框选区域。时间轴默认以格为单位,每一格代表一个时间单位,这个单位是可以自定义的——你可以设成 1ns、10ns 或者干脆设成"1 个时钟周期"。
工具栏里有几个高频按钮:时钟生成、总线编辑、文本标注、箭头工具。这几个是你 90% 时间都会用到的,建议先把快捷键记熟。
3. 从零开始画一张标准时序图
3.1 环境准备与基础设置
第一次打开 TimeGen 3.2,先别急着画。花两分钟把几个基础设置调好,后面能省很多事。
时间单位设置。在菜单里找到时间轴设置,把基本时间单位设成你项目里最常用的那个。做 FPGA 的话,一般设成 1ns 或者直接设成时钟周期。我个人的习惯是设成 1ns,因为这样画建立保持时间窗口的时候比较直观。
默认时钟参数。TimeGen 允许你预设几个常用时钟频率,比如 50MHz、100MHz、200MHz。设好之后,插入时钟信号的时候直接选就行,不用每次手算周期。50MHz 对应 20ns 周期,100MHz 对应 10ns,200MHz 对应 5ns,这几个数建议背下来,画图的时候心里有数。
信号命名规范。这个不是软件设置,是习惯问题。建议统一用项目里的实际信号名,大小写保持一致,低有效信号加 _n 或 _b 后缀。别在图上写"clk1""clk2"这种,过两天自己都忘了哪个是哪个。
网格和对齐。把网格打开,设置成合适的大小。画图的时候让信号边沿自动吸附到网格上,这样对齐起来快,图也整齐。网格太密看着眼花,太疏又对不齐,一般设成时钟周期的四分之一或者五分之一比较合适。
3.2 插入时钟信号与参数计算
时钟是时序图的骨架,先把时钟画好,其他信号才有参照。
在 TimeGen 里插入时钟很简单:点击工具栏的时钟按钮,在波形区点一下,一个标准时钟就出来了。但关键是参数怎么设。假设你的系统时钟是 100MHz,那周期就是 10ns,占空比 50%,高电平 5ns,低电平 5ns。在时钟属性里把这些数填进去,TimeGen 会自动生成对应的波形。
如果图上有多个时钟域,比如一个 100MHz 的系统时钟和一个 25MHz 的慢速时钟,你需要分别设置。这里有个技巧:把慢时钟的周期设成快时钟的整数倍,这样两个时钟的边沿会对齐,画跨时钟域信号的时候看起来清楚。25MHz 周期是 40ns,正好是 100MHz 的 4 倍,画出来就是每 4 个快时钟周期对应 1 个慢时钟周期。
注意:时钟的相位关系也要设对。如果两个时钟同源但相位不同,比如一个 0 度一个 90 度,在属性里把相位差填进去,别靠手动拖拽,那样不准。
3.3 绘制普通信号与总线
时钟画好之后,开始画控制信号和数据信号。
普通信号就是高低电平的切换。在 TimeGen 里,鼠标左键点击波形区可以在高、低、高阻三个状态之间切换。画的时候注意几点:信号的初始状态要明确,别留空;边沿要跟时钟对齐,除非你就是要画异步信号;信号名要跟 RTL 代码里一致。
总线信号的画法稍微不同。你需要先定义一个总线,给它起个名字比如 data[7:0],然后画的时候可以填入具体的数值。TimeGen 支持在总线上显示十六进制或二进制数值,这个功能在画数据通路的时候特别好用。比如你画一个 AXI 的写数据通道,总线上依次显示 0x1A、0x2B、0x3C,评审的人一眼就能看出数据流。
画总线的时候有个细节:总线在无效期间应该显示成什么?一般有两种画法,一种是画成两条平行线中间打叉,表示高阻;另一种是画成一条粗线,表示不确定值。TimeGen 里两种都支持,看你的项目规范。我一般用打叉的那种,因为更直观。
3.4 添加标注、箭头与时间刻度
信号画完了,但图还不能算完成。标注才是让图"会说话"的关键。
文本标注用来写说明文字,比如"握手完成""数据有效窗口""建立时间"。TimeGen 里可以插入文本框,拖到合适的位置。建议把标注放在信号附近,用引线连过去,别让读者猜。
箭头工具用来表示因果关系或时间关系。比如从 valid 的上升沿画一个箭头到 ready 的上升沿,表示"valid 拉高后 ready 才可能有效"。或者画一个双向箭头标注两个信号之间的时间间隔,写上"2 个时钟周期"。
时间刻度是很多人忽略的。TimeGen 可以在图上显示时间标尺,标出关键的时间点。比如在某个边沿处标一个 T1,在另一个边沿处标一个 T2,然后在图下方写"T2 - T1 = 3 个时钟周期"。这样即使图被单独截出来,读者也能准确理解时间关系。
提示:标注的颜色要跟信号颜色区分开。信号用深色,标注用浅色或者灰色,这样图看起来有层次,不会乱。
4. 进阶技巧:让时序图真正服务于设计
4.1 跨时钟域信号的画法
跨时钟域是 FPGA 设计里最容易出问题的地方,时序图画得好不好,直接影响你对问题的理解。
画跨时钟域信号,核心是把两个时钟域的信号分开画,然后用标注说明同步关系。比如一个信号从 100MHz 域传到 25MHz 域,经过两级触发器同步。图上应该画:源时钟域的信号变化、两级同步触发器的输出、目标时钟域的采样结果。每一级之间用箭头标注"同步级 1""同步级 2"。
TimeGen 里可以用不同的背景色区分时钟域,或者用一条竖线把两个域分开。我习惯用竖线加文字标注,简单明了。关键是让读者一眼看出信号是在哪个时钟域里变化的,以及跨域的时候经过了什么处理。
还有一个技巧:把亚稳态窗口画出来。虽然 TimeGen 不是仿真工具,画不出真正的亚稳态,但你可以用阴影区域表示"这个时间段内信号可能不稳定"。这个在写设计文档解释为什么需要同步器的时候特别有用。
4.2 握手协议的完整表达
握手协议是时序图的高频应用场景,AXI、AHB、自定义的 valid-ready 协议都属于这一类。
画握手协议,要抓住几个关键点:valid 和 ready 的先后关系、data 的有效窗口、握手完成的判定条件。以最经典的 valid-ready 握手为例,规则是:valid 拉高后必须保持到 ready 拉高;ready 可以在 valid 之前拉高;当 valid 和 ready 同时为高的那个时钟沿,握手完成,data 被采样。
在 TimeGen 里画的时候,建议把 valid、ready、data 三根信号放在一起,用箭头标出握手完成的时刻。如果协议允许 ready 先拉高,就画一个场景;如果 valid 先拉高,再画一个场景。把边界情况都画出来,评审的时候就不会有人问"如果 ready 先来怎么办"。
注意:握手协议的时序图一定要标清楚是在哪个时钟沿采样。是上升沿还是下降沿,是时钟沿到来之前还是之后,这些细节决定了设计对不对。
4.3 用 TimeGen 画状态机时序
状态机的时序图跟普通信号不太一样,它更关注状态的迁移和输出。
TimeGen 里可以把状态机的状态画成总线形式,每个状态对应一个编码。比如 IDLE=0x0、READ=0x1、WRITE=0x2、DONE=0x3。然后在状态总线上画出迁移过程,配合时钟信号和输入输出信号,完整展示状态机的工作过程。
画状态机时序图的时候,建议把状态迁移条件也标出来。比如从 IDLE 到 READ 的迁移,在图上用箭头标注"start 信号有效时"。这样看图的人不仅知道状态怎么变,还知道为什么变。
如果状态机比较复杂,可以分几张图画。一张画正常流程,一张画异常处理,一张画复位流程。别把所有情况塞进一张图,那样谁都看不清。
4.4 导出与文档集成
图画完了,最终是要放到文档里的。TimeGen 支持导出多种格式,常用的有 PNG、BMP、EMF。
PNG 适合放到 Word 或 Markdown 文档里,清晰度够,文件也不大。导出的时候把分辨率设高一点,一般 300dpi 以上,打印出来才清楚。EMF 是矢量格式,放到 Word 里可以无损缩放,适合需要打印的正式文档。BMP 基本不用考虑,文件太大。
导出之前记得检查几件事:信号名有没有拼错、时间标注对不对、颜色在黑白打印时能不能区分。我吃过亏,图上用红色和绿色区分两个信号,结果打印出来全是灰的,根本分不清。后来养成习惯,重要信号用实线和虚线区分,颜色只是辅助。
5. 常见问题与排查技巧实录
5.1 画图效率低的几个原因
很多人用 TimeGen 觉得慢,问题往往出在几个地方。
不用快捷键。TimeGen 的常用操作都有快捷键,比如插入时钟、切换电平、复制粘贴。花十分钟把快捷键过一遍,后面画图速度能快一倍。
不用模板。如果你们项目经常画类似的时序图,比如 SPI 读写、I2C 传输、UART 收发,建议做几个模板存起来。下次画的时候直接打开模板改参数,比从零开始快得多。
不用总线批量编辑。画数据总线的时候,一个一个填数值很慢。TimeGen 支持批量输入,比如你输入"0x00 0x01 0x02 0x03",它会自动分配到连续的时钟周期里。这个功能在画长数据流的时候特别省事。
网格设置不合理。网格太密,鼠标点不准;网格太疏,信号对不齐。花点时间找到适合你项目的网格大小,后面画图会顺很多。
5.2 时序图评审常被挑的问题
我参加过不少设计评审,时序图被挑毛病的地方翻来覆去就那么几个。
| 常见问题 | 具体表现 | 改进方法 |
|---|---|---|
| 时间关系不明确 | 信号边沿没有对齐时钟,或者没有标注延迟 | 所有同步信号边沿对齐时钟,异步信号标注延迟时间 |
| 缺少边界情况 | 只画了正常流程,没画复位、异常、背压 | 补充复位时序、错误处理、反压场景 |
| 信号命名不一致 | 图上写 clk,代码里叫 sys_clk | 图上信号名与 RTL 代码严格一致 |
| 缺少时间刻度 | 读者无法判断信号之间隔了多久 | 添加时间标尺或周期数标注 |
| 总线数值不清晰 | 总线上没有标数值,或者数值格式不统一 | 统一用十六进制,无效期间明确标注 |
| 跨时钟域处理未体现 | 信号直接从一个时钟域画到另一个 | 画出同步器,标注时钟域边界 |
这张表里的问题,我几乎每次评审都能遇到几个。提前检查一遍,能省不少返工时间。
5.3 文件兼容性与版本问题
TimeGen 3.2 是比较老的版本,在新系统上运行可能会遇到兼容性问题。常见的有:字体显示不正常、导出图片模糊、文件保存后打不开。
字体问题一般是系统缺少对应字体,装一个通用字体包基本能解决。导出模糊是分辨率设置的问题,在导出选项里把 DPI 调高。文件打不开可能是版本不匹配,TimeGen 的高版本文件低版本打不开,团队里最好统一版本。
还有一个坑:TimeGen 的文件扩展名是 .tgw,有些人会误存成其他格式,结果下次打不开。保存的时候注意看扩展名,别手滑。
提示:重要时序图建议同时保存源文件和导出的图片。源文件方便后续修改,图片方便直接使用。别只存一种,到时候着急用的时候抓瞎。
5.4 从时序图到 RTL 代码的对照检查
时序图画完之后,建议做一次对照检查:把图上的每个信号跟 RTL 代码里的信号对一遍,把图上的每个时间关系跟代码里的逻辑对一遍。
具体怎么做?打开你的 RTL 文件,找到对应的模块,逐个信号核对。比如图上画了 valid 拉高后两个周期 ready 才拉高,那代码里是不是真的有两个周期的延迟?图上画了 data 在 ready 拉低后保持一个周期,代码里是不是真的保持了一个周期?
这个检查看起来笨,但能发现很多隐藏的 bug。我自己的经验是,画图的时候觉得逻辑没问题,一对照代码就发现少了一个周期或者多了一个寄存器。时序图不只是给别人看的,也是给自己做设计检查的。
6. 我这些年积累的几个实用心得
6.1 建立个人时序图库
我从入行第二年开始,就有意识地积累时序图。每做一个项目,把关键的时序图整理出来,按协议类型分类:SPI、I2C、UART、AXI、自定义握手……几年下来攒了几十张图。
这个图库的价值在于:下次遇到类似协议,直接翻出以前的图改一改就能用,不用从头画。而且这些图经过实际项目验证,时间关系都是对的,比临时画的靠谱。建议你也建一个,用文件夹按协议分类,文件名写清楚项目名和日期。
6.2 时序图要跟代码一起版本管理
时序图是设计文档的一部分,应该跟 RTL 代码一起纳入版本管理。代码改了,时序图跟着改;时序图改了,代码也要检查是否一致。
我见过太多项目,代码更新了好几版,时序图还是第一版的,评审的时候拿出来的图跟实际设计对不上,尴尬不说,还容易误导人。把 .tgw 文件跟 .v 文件放在同一个仓库里,提交代码的时候顺便检查时序图要不要更新,养成习惯就好了。
6.3 给初学者的练习建议
如果你刚开始用 TimeGen,别一上来就画复杂的协议。从最简单的开始:画一个时钟和一个分频信号,画一个计数器的时序,画一个简单的状态机。把这些基础画熟了,再挑战握手协议和跨时钟域。
练习的时候可以对照现成的协议手册,比如 SPI 的时序图,手册上怎么画你就怎么画,画完对比一下差异。这个过程能帮你理解协议的本质,也能帮你熟悉 TimeGen 的操作。
还有一个小技巧:把你画好的图发给有经验的同事看,让他们挑毛病。别人挑出来的问题,往往是你自己看不到的盲区。被挑几次之后,你画图的时候就会自然而然地避开这些坑。
6.4 关于工具选择的个人看法
TimeGen 不是唯一的时序图工具,市面上还有 WaveDrom、TimingDiagram 等。WaveDrom 是文本驱动的,适合喜欢用代码描述波形的人;TimeGen 是图形界面的,适合习惯鼠标操作的人。
选哪个工具不重要,重要的是坚持用。工具换来换去,每换一次都要重新学,时间都浪费在适应工具上了。我选 TimeGen 是因为它够简单,打开就能画,画完就能用。对于 FPGA 工程师来说,时序图是沟通工具,不是艺术品,够用就行。
最后说一句关于版本的事。3.2 版本虽然老,但核心功能都有,画常规时序图完全够用。如果你有正规渠道获取的新版本,当然更好。但不管用什么版本,画图的思路和方法是一样的——先把时钟画对,再把信号画准,最后把标注写清楚。这三步做好了,图就不会差。