3、层次化设计:创建子图、端口映射、多层嵌套设计
说实话,刚入行那会儿,我画原理图喜欢“一图画到底”。
一个复杂的电源管理芯片,所有模块全塞在一张图里。结果呢?
连线密密麻麻,自己都看晕了。后来带我的老工程师看了一眼,只说了一句:“拆成子图吧。”
从那以后,层次化设计就成了我的“肌肉记忆”。
3.1 为什么要做层次化?
层次化设计,说白了就是把大系统拆成小模块。
每个模块画一张子图,然后用端口把它们连起来。
这样做的好处,我总结了几点:
- 可读性暴增:一张图只放20-30个器件,看着清爽
- 团队协作:每人负责一个子图,互不干扰
- 复用性:同一个子图,可以在顶层多次调用
- 仿真方便:可以单独仿真某个子模块
核心思想:顶层只负责“搭积木”,底层才管“积木内部长什么样”。
3.2 创建子图——从零开始
在华大九天工具里,创建子图其实很简单。
我个人习惯先规划好模块划分,再动手画。
步骤是这样的:
- 在工程管理器里右键 → 新建 → 原理图子图
- 给子图起个名字,比如
PLL_CORE或LDO_REG - 在子图里正常画电路,放器件、连导线
- 添加端口(Port),这是关键一步
我的小技巧:子图命名最好用英文大写+下划线,比如 ADC_DRIVER。中文命名虽然支持,但我在项目里遇到过编码问题,后来就只用英文了。
3.3 端口映射——子图的“门”
端口就是子图和外界通信的“门”。
没有端口,顶层就找不到子图的输入输出。
端口类型有这几种:
| 端口类型 | 符号 | 用途 |
|---|---|---|
| 输入端口 | → | 接收外部信号,比如时钟、使能 |
| 输出端口 | ← | 输出信号到外部,比如数据、状态 |
| 双向端口 | ↔ | 数据总线,可读可写 |
| 电源端口 | VDD/VSS | 供电,通常全局连接 |
添加端口时,我建议你注意这几点:
- 端口名要和子图内部网络名一致
- 输入输出方向别搞反了——我曾经犯过这个错,仿真半天没信号
- 电源端口建议用全局属性,不用每个子图都画
避坑指南:我曾经在做一个多电压域设计时,把3.3V和1.8V的电源端口都叫VDD。结果顶层调用时,两个电源网络短路了。后来我养成了习惯:电源端口名带上电压值,比如VDD_3V3、VDD_1V8。
3.4 多层嵌套设计——积木套积木
层次化不止一层。子图里还可以再套子图。
这就是多层嵌套设计。
举个例子:
- 顶层:SOC_TOP → 调用 CPU_CORE、MEMORY、PERIPH
- 第二层:CPU_CORE → 调用 ALU、CONTROL、REG_FILE
- 第三层:ALU → 调用 ADD_SUB、MULTIPLIER、SHIFTER
你想想看,如果所有逻辑都画在一张图上,那得乱成什么样?
多层嵌套的规则:
- 每一层都要有明确的端口映射
- 顶层只能看到下一层的端口,看不到内部细节
- 修改底层子图,顶层自动更新——这是工具帮我们做的
重要:嵌套层数不建议超过5层。层数太多,工具打开会变慢,而且调试时定位问题很痛苦。我一般控制在3层以内。
3.5 知识体系结构图
下面这张图,帮你理清层次化设计的核心逻辑:
3.6 实际操作中的注意事项
嗯,这里要注意几个容易踩坑的地方:
- 端口名冲突:不同子图里不要用相同的端口名,除非它们真的是同一个网络
- 悬空端口:顶层调用子图时,所有输入端口必须连接,输出端口可以悬空
- 层次化检查:画完后用工具的“层次化检查”功能跑一遍,能自动发现端口不匹配的问题
我的习惯:每画完一个子图,立刻在顶层调用一次,连上几个关键信号,看看有没有报错。不要等所有子图画完再检查——那时候问题堆在一起,排查起来很头疼。
3.7 一个简单的例子
假设我们要画一个简单的计数器模块。
顶层叫 COUNTER_TOP,里面调用一个子图 COUNTER_CORE。
子图 COUNTER_CORE 的端口:
端口名 方向 说明
CLK 输入 时钟信号
RST 输入 复位信号(高有效)
EN 输入 使能信号
Q[7:0] 输出 8位计数值
顶层调用时,代码大概长这样:
// 顶层原理图中放置子图实例
X_U1 COUNTER_CORE (
.CLK( sys_clk ),
.RST( sys_rst ),
.EN( count_en ),
.Q( count_value[7:0] )
);
你看,顶层只关心“这个模块有什么端口”,不关心内部是D触发器还是JK触发器实现的。这就是层次化的精髓。
总结一下:层次化设计不是花架子,是真正能提升效率的方法。从顶层规划开始,一层层拆下去,每层只关注自己的事。这样画出来的原理图,你自己看着舒服,别人接手也容易。
公众号:蓝海资料掘金营,微信deep3321