架构设计:芯片的“骨架”——模块划分、总线架构选择、数据流与控制流设计
做芯片设计这么多年,我越来越觉得架构设计就像盖房子时的结构图纸。图纸画歪了,后面装修再漂亮也白搭。今天咱们就来聊聊这个“骨架”该怎么搭。
模块划分:别把功能揉成一团
模块划分这事儿,说白了就是“分而治之”。我见过不少新手,恨不得把所有逻辑塞进一个模块里。结果呢?仿真跑不动,调试想撞墙。
我个人习惯遵循几个原则:
- 功能内聚:一个模块只干一件事。比如DMA控制器,就管数据搬运,别掺和中断管理。
- 接口清晰:模块之间的握手信号要简单。我建议用valid-ready这种标准握手机制,别自己发明协议。
- 规模可控:单个模块的代码量控制在2000行以内。超过这个数,综合工具会抱怨,人也容易看花眼。
重要提醒:模块划分时,一定要考虑复用性。我在项目中吃过亏——为了赶进度,把特定功能写死在模块里。结果下一款芯片想复用,改得比重新写还痛苦。
总线架构:AHB、APB、AXI怎么选?
总线选型这事儿,其实没那么玄乎。你想想看,不同总线就像不同规格的马路:
| 总线类型 | 典型场景 | 带宽需求 | 复杂度 |
|---|---|---|---|
| APB | 寄存器配置、低功耗外设 | 低(< 100MHz) | 极低 |
| AHB | 中等带宽数据通路 | 中(100-500MHz) | 中等 |
| AXI | 高性能数据搬运、DDR控制器 | 高(> 500MHz) | 较高 |
我记得有个项目,团队非要给一个简单的UART配AXI总线。结果呢?面积大了30%,功耗也上去了。其实APB完全够用,何必杀鸡用牛刀?
我的经验:系统里至少保留一个APB总线域,专门挂配置寄存器。这样调试时可以直接通过JTAG写寄存器,不用走复杂的数据通路。
数据流与控制流:两条腿走路
架构设计里最容易犯的错,就是把数据流和控制流混在一起。我刚开始做设计时也犯过这毛病——一个状态机既管数据搬运,又管协议解析。结果状态图画得跟蜘蛛网似的。
正确的做法是:
- 数据流:用FIFO或乒乓缓冲处理,保证吞吐率。控制信号尽量少介入数据通路。
- 控制流:用状态机或微序列器,只负责发命令、收状态。别碰数据本身。
举个例子,一个简单的DMA传输:
// 控制流:状态机
always @(posedge clk or negedge rst_n) begin
if (!rst_n) state <= IDLE;
else case (state)
IDLE: if (start) state <= READ_ADDR;
READ_ADDR: state <= READ_DATA;
READ_DATA: state <= WRITE_DATA;
WRITE_DATA: if (done) state <= IDLE;
endcase
end
// 数据流:FIFO直通
assign fifo_wr_en = (state == READ_DATA) & src_valid;
assign fifo_rd_en = (state == WRITE_DATA) & dst_ready;
看到了吗?控制流只管“什么时候做”,数据流只管“怎么做”。这样分开后,调试时定位问题快得多。
避坑指南:我曾经在一个项目里,把数据通路上的握手信号直接连到状态机的组合逻辑上。结果时序收敛不了,最后不得不重写。记住:数据通路和控制通路之间,一定要加寄存器打拍隔离。
架构图:一图胜千言
下面这张图是我做架构设计时常用的模板。它把模块、总线、数据流、控制流都画清楚了。
这张图里,CPU通过AXI总线访问DMA和DDR控制器(数据流),通过APB总线配置UART、SPI等外设的寄存器(控制流)。两条路径互不干扰,调试时一目了然。
总结一下
架构设计这事儿,说白了就是三个字:分、选、隔。
- 分:模块划分要功能内聚、接口清晰。
- 选:总线选型要匹配带宽,别大材小用。
- 隔:数据流和控制流要物理隔离,别混在一起。
嗯,做到这三点,你的芯片架构至少不会跑偏。剩下的细节,咱们后面章节慢慢聊。