APB_I2C验证平台3————SPI 时钟生成模块设计

张开发
2026/5/3 3:23:28 15 分钟阅读
APB_I2C验证平台3————SPI 时钟生成模块设计
一、模块概述spi_clgen是 SPI 控制器的时钟生成模块负责从系统时钟产生 SPI 时钟SCLK并提供边沿检测信号供移位寄存器使用。该模块通过一个可配置的计数器实现分频支持不同的 SPI 传输速率并能在传输结束时精确控制最后一个时钟沿。二、接口信号信号名方向位宽说明时钟与复位clk_in输入1系统时钟APB PCLKrst输入1复位高有效控制信号enable输入1SPI 传输使能来自 shift 模块的 tipgo输入1启动传输信号来自控制寄存器last_clk输入1最后一位时钟标志divider输入SPI_DIVIDER_LEN分频系数0 最小分频输出信号clk_out输出1SPI 时钟SCLKpos_edge输出1上升沿脉冲提前一个系统时钟周期neg_edge输出1下降沿脉冲提前一个系统时钟周期三、核心原理SPI 时钟通过计数器分频产生计数器从divider递减到 0每减到 0 一次clk_out翻转一次。两个cnt0产生一个完整的 SPI 时钟周期。时钟周期计算textSPI 时钟周期 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{1b0}}; assign cnt_one cnt {{SPI_DIVIDER_LEN-1{1b0}}, 1b1}; // Counter counts half period always (posedge clk_in or posedge rst) begin if(rst) cnt #Tp {SPI_DIVIDER_LEN{1b1}}; else begin if(!enable || cnt_zero) cnt #Tp divider; else cnt #Tp cnt - {{SPI_DIVIDER_LEN-1{1b0}}, 1b1}; end end计数器行为条件操作说明复位cnt 全1最慢时钟安全状态!enablecnt divider空闲时加载分频值cnt_zerocnt divider计数到 0重新加载其他cnt cnt - 1正常递减五、时钟输出逻辑// clk_out is asserted every other half period always (posedge clk_in or posedge rst) begin if(rst) clk_out #Tp 1b0; else clk_out #Tp (enable cnt_zero (!last_clk || clk_out)) ? ~clk_out : clk_out; end翻转条件翻转 enable cnt_zero (!last_clk || clk_out)条件含义enable传输进行中cnt_zero计数器到 0半周期结束!last_clk || clk_out非最后一位或当前为高电平最后一位控制场景clk_out 状态!last_clk || clk_out动作正常传输clk_out0低1!last_clk1翻转为高正常传输clk_out1高1!last_clk1翻转为低最后一位clk_out0低0last_clk1, clk_out0保持低最后一位clk_out1高1clk_out1翻转为低效果最后一位只输出半个时钟的高电平然后保持在低电平空闲状态避免传输结束后出现多余时钟沿。六、边沿信号生成边沿信号提前一个系统时钟周期产生让移位寄存器有足够时间准备数据。条件场景说明enable !clk_out cnt_one正常模式时钟为低计数器1 → 即将产生上升沿enable clk_out cnt_one正常模式时钟为高计数器1 → 即将产生下降沿!(|divider) clk_out最小分频分频器0时钟为高 → 产生上升沿!(|divider) !clk_out enable最小分频分频器0时钟为低 → 产生下降沿!(|divider) go !enable最小分频启动分频器0刚启动 → 产生第一个上升沿分频器为 0 的特殊处理!(|divider)表示divider全为 0最小分频模式。此时 SPI 时钟等于系统时钟/2边沿信号需要特殊处理确保第一时间产生。七.完整spi_clgen.v代码include spi_defines.v include timescale.v module spi_clgen (clk_in, rst, go, enable, last_clk, divider, clk_out, pos_edge, neg_edge); parameter Tp 1; // -------------------------------------------------------------------- // 时钟生成模块从系统时钟产生 SPI 时钟 (sclk) 及其边沿预告信号 // 输入 // clk_in : 系统时钟 (APB PCLK) // rst : 复位 (高有效) // enable : SPI 传输使能 (来自 spi_shift 的 tip) // go : 启动传输信号 (来自控制寄存器) // last_clk : 最后一位时钟标志 (来自 spi_shift 的 last) // divider : 分频系数 (0 表示最小分频) // 输出 // clk_out : SPI 时钟 (sclk) // pos_edge : 上升沿预告脉冲 (提前一个系统时钟周期) // neg_edge : 下降沿预告脉冲 // -------------------------------------------------------------------- input clk_in; input rst; input enable; input go; input last_clk; input [SPI_DIVIDER_LEN-1:0] divider; output clk_out; output pos_edge; output neg_edge; reg clk_out; reg pos_edge; reg neg_edge; reg [SPI_DIVIDER_LEN-1:0] cnt; // 半周期计数器 wire cnt_zero; // 计数器为 0 (半周期结束) wire cnt_one; // 计数器为 1 (下一个周期将到 0) // cnt_zero 所有位为 0 assign cnt_zero cnt {SPI_DIVIDER_LEN{1b0}}; // cnt_one 最低位为 1其余位为 0 (即数值 1) assign cnt_one cnt {{SPI_DIVIDER_LEN-1{1b0}}, 1b1}; // // 半周期计数器 // 复位时cnt 全1 (最大值SPI 时钟最慢) // 计数过程 // - 若 !enable 或 cnt_zero (空闲或半周期结束)则加载 divider // - 否则 cnt 递减 1 // always (posedge clk_in or posedge rst) begin if(rst) cnt #Tp {SPI_DIVIDER_LEN{1b1}}; else begin if(!enable || cnt_zero) cnt #Tp divider; else // 递减操作原代码 {{...}} 构造了位宽匹配的 1简化理解即 cnt - 1 cnt #Tp cnt - {{SPI_DIVIDER_LEN-1{1b0}}, 1b1}; end end // // SPI 时钟输出 (clk_out) // 翻转条件enable cnt_zero (!last_clk || clk_out) // - enable 为高传输进行中 // - cnt_zero 为高半周期结束 // - (!last_clk || clk_out) 控制最后一位只输出半个高电平 // 最后一位时 (last_clk1)若 clk_out0 则条件不成立 → 不翻转保持低 // 若 clk_out1 则条件成立 → 翻转为低。从而实现最后半个周期高电平后停在高电平实际是最后高后翻低再保持低。 // 正常传输时last_clk0条件恒真每 cnt_zero 翻转一次。 // always (posedge clk_in or posedge rst) begin if(rst) clk_out #Tp 1b0; else clk_out #Tp (enable cnt_zero (!last_clk || clk_out)) ? ~clk_out : clk_out; end // // 边沿预告信号 (pos_edge, neg_edge) // 这些脉冲在系统时钟域产生比实际 SPI 时钟边沿提前一个周期 // 用于通知 spi_shift 模块准备采样或驱动数据。 // // pos_edge 产生条件 // 1. 正常模式enable !clk_out cnt_one // (传输中、当前时钟为低、计数器为 1 → 即将产生上升沿) // 2. 最小分频模式 (divider0) // a) !(|divider) clk_out (时钟为高时产生上升沿) // b) !(|divider) go !enable (刚启动时产生第一个上升沿) // // neg_edge 产生条件 // 1. 正常模式enable clk_out cnt_one // (传输中、当前时钟为高、计数器为 1 → 即将产生下降沿) // 2. 最小分频模式!(|divider) !clk_out enable // (时钟为低且传输中产生下降沿) // always (posedge clk_in or posedge rst) begin if(rst) begin pos_edge #Tp 1b0; neg_edge #Tp 1b0; end else begin pos_edge #Tp (enable !clk_out cnt_one) || (!(|divider) clk_out) || (!(|divider) go !enable); neg_edge #Tp (enable clk_out cnt_one) || (!(|divider) !clk_out enable); end end endmodule

更多文章