4、SPI控制器顶层架构:系统框图、AHB/APB总线接口、时钟域划分、FIFO深度设计考量
好,咱们今天聊聊SPI控制器的顶层架构。这部分内容,说白了就是整个芯片的“骨架”和“血管”。你想想看,一个SPI模块要正常工作,它得跟CPU打交道,得跟外部设备通信,还得处理好数据流。这三件事,分别对应着总线接口、SPI物理协议和内部的数据通路。
我个人习惯,在设计顶层架构之前,先画一张系统框图。这张图能帮你理清所有模块的边界和交互关系。来,我们先看一张我画的SPI控制器顶层架构图。
嗯,这张图里有两个虚线框,代表两个不同的时钟域。左边是HCLK域,右边是SCLK域。中间那个FIFO,就是连接两个世界的桥梁。这个设计思路,我在好几个项目里都用过,效果不错。
4.1 系统框图与模块划分
从上面这张图你能看到,SPI控制器主要包含这几个模块:
- 总线接口模块:负责跟AHB或APB总线握手。我个人更倾向于用APB,因为它协议简单,面积小。但如果你需要高带宽的配置,比如DMA方式搬运数据,那AHB是更好的选择。
- 寄存器组:所有可编程的配置位都在这儿。包括控制寄存器、状态寄存器、数据寄存器。我记得有一次,一个同事把数据寄存器的地址译码搞错了,结果CPU写进去的数据总是对不上号。排查了半天,最后发现是地址位宽少算了一位。
- 时钟分频器:从系统时钟HCLK分频出SPI的串行时钟SCLK。这里要注意,分频系数必须是偶数,否则占空比会出问题。
- 异步FIFO:跨时钟域数据交换的核心。后面我会详细讲它的深度设计。
- SPI核心状态机:这是整个模块的大脑。它控制着数据移位的节奏、片选信号的拉高拉低、以及传输模式的切换。
- 输入/输出驱动:负责把内部信号驱动到芯片引脚上,同时把外部进来的信号同步到内部时钟域。
4.2 AHB/APB总线接口设计
总线接口这块,其实没什么花哨的。关键是要把协议翻译成寄存器读写操作。我拿APB举个例子,它的状态机就三个状态:IDLE、SETUP、ACCESS。
// APB 从机接口状态机示例
always @(posedge HCLK or negedge HRESETn) begin
if (!HRESETn) begin
apb_state <= IDLE;
reg_we <= 1'b0;
reg_addr <= 'b0;
reg_wdata <= 'b0;
end else begin
case (apb_state)
IDLE: begin
if (PSEL & !PENABLE) begin
apb_state <= SETUP;
reg_addr <= PADDR;
reg_wdata <= PWDATA;
reg_we <= PWRITE;
end
end
SETUP: begin
apb_state <= ACCESS;
end
ACCESS: begin
// 这里产生寄存器写使能或读数据
if (PENABLE & PREADY) begin
apb_state <= IDLE;
end
end
endcase
end
end
这里有个小细节:PREADY信号。如果你的寄存器读取需要多个周期(比如从FIFO读数据),你得拉低PREADY来插入等待周期。否则,总线会认为你一次就完成了传输。
4.3 时钟域划分与同步策略
时钟域划分,这是跨时钟域设计的核心问题。SPI控制器天然就有两个时钟:系统时钟HCLK和串行时钟SCLK。HCLK通常跑得很快(比如100MHz),而SCLK取决于你的SPI通信速率(可能只有10MHz甚至更低)。
这两个时钟之间没有任何相位关系。所以,所有跨时钟域的信号都必须经过同步处理。我常用的方法有两种:
- 双级触发器同步:用于同步单比特控制信号,比如中断请求、FIFO空满标志。
- 异步FIFO:用于传输多比特数据,比如你要发送的字节。
我曾经在一个项目里犯过傻——直接用双级触发器去同步一个8位的数据总线。结果数据偶尔会错位,因为每个比特到达的时间不一样。后来改成异步FIFO,问题就解决了。说白了,多比特数据必须用FIFO或者握手协议,不能偷懒。
4.4 FIFO深度设计考量
FIFO深度,这是个经典问题。深度太浅,容易溢出;深度太深,浪费面积。那到底选多深合适?
我个人习惯从两个角度来考虑:
- CPU响应延迟:假设CPU每100个时钟周期才能响应一次中断并读取FIFO。那么在这100个周期内,SPI可能已经接收了N个字节。FIFO深度至少要比N大,否则数据会丢。
- SPI传输速率:如果SCLK是HCLK的1/10,那么每10个HCLK周期,SPI才能移出一个比特。一个字节需要80个HCLK周期。如果CPU响应延迟是100个周期,那FIFO深度设为2就够用了。
我整理了一个表格,方便你参考:
| 应用场景 | HCLK频率 | SCLK频率 | CPU响应延迟 | 推荐FIFO深度 |
|---|---|---|---|---|
| 低速传感器 | 50 MHz | 1 MHz | 200 cycles | 4 |
| 中速ADC | 100 MHz | 10 MHz | 100 cycles | 8 |
| 高速Flash | 200 MHz | 50 MHz | 50 cycles | 16 |
| DMA模式 | 200 MHz | 100 MHz | DMA延迟 | 32 |
你看,对于低速场景,深度4就绰绰有余了。但对于高速Flash,深度16是起步。如果你用DMA模式,深度32会更安全,因为DMA的启动延迟比CPU中断要长一些。
嗯,关于顶层架构,我就讲这么多。记住,系统框图是设计的蓝图,时钟域划分是安全的保障,FIFO深度是性能的平衡点。把这三点想清楚,你的SPI控制器就成功了一大半。