一、模块概述
spi_clgen是 SPI 控制器的时钟生成模块,负责从系统时钟产生 SPI 时钟(SCLK),并提供边沿检测信号供移位寄存器使用。
该模块通过一个可配置的计数器实现分频,支持不同的 SPI 传输速率,并能在传输结束时精确控制最后一个时钟沿。
二、接口信号
| 信号名 | 方向 | 位宽 | 说明 |
|---|---|---|---|
| 时钟与复位 | |||
| clk_in | 输入 | 1 | 系统时钟(APB PCLK) |
| rst | 输入 | 1 | 复位(高有效) |
| 控制信号 | |||
| enable | 输入 | 1 | SPI 传输使能(来自 shift 模块的 tip) |
| go | 输入 | 1 | 启动传输信号(来自控制寄存器) |
| last_clk | 输入 | 1 | 最后一位时钟标志 |
| divider | 输入 | SPI_DIVIDER_LEN | 分频系数(0 = 最小分频) |
| 输出信号 | |||
| clk_out | 输出 | 1 | SPI 时钟(SCLK) |
| pos_edge | 输出 | 1 | 上升沿脉冲(提前一个系统时钟周期) |
| neg_edge | 输出 | 1 | 下降沿脉冲(提前一个系统时钟周期) |
三、核心原理
SPI 时钟通过计数器分频产生:计数器从divider递减到 0,每减到 0 一次,clk_out翻转一次。两个cnt=0产生一个完整的 SPI 时钟周期。
时钟周期计算
text
SPI 时钟周期 = 2 × (divider + 1) × 系统时钟周期 举例(系统时钟 50MHz): - divider = 0 → SCLK = 25MHz(最小分频) - divider = 1 → SCLK = 12.5MHz - divider = 255 → SCLK ≈ 97.7kHz(最大分频)
四、计数器逻辑
assign cnt_zero = cnt == {`SPI_DIVIDER_LEN{1'b0}}; assign cnt_one = cnt == {{`SPI_DIVIDER_LEN-1{1'b0}}, 1'b1}; // Counter counts half period always @(posedge clk_in or posedge rst) begin if(rst) cnt <= #Tp {`SPI_DIVIDER_LEN{1'b1}}; else begin if(!enable || cnt_zero) cnt <= #Tp divider; else cnt <= #Tp cnt - {{`SPI_DIVIDER_LEN-1{1'b0}}, 1'b1}; end end计数器行为
| ||||||||||||||||||||||||||||||||||||||||||||||
五、时钟输出逻辑翻转条件翻转 = enable && cnt_zero && (!last_clk || clk_out)
最后一位控制
效果:最后一位只输出半个时钟的高电平,然后保持在低电平(空闲状态),避免传输结束后出现多余时钟沿。 六、边沿信号生成边沿信号提前一个系统时钟周期产生,让移位寄存器有足够时间准备数据。
分频器为 0 的特殊处理
七.完整spi_clgen.v代码 | ||||||||||||||||||||||||||||||||||||||||||||||