当前位置: 首页 > news >正文

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

一、模块概述

spi_clgen是 SPI 控制器的时钟生成模块,负责从系统时钟产生 SPI 时钟(SCLK),并提供边沿检测信号供移位寄存器使用。

该模块通过一个可配置的计数器实现分频,支持不同的 SPI 传输速率,并能在传输结束时精确控制最后一个时钟沿。

二、接口信号

信号名方向位宽说明
时钟与复位
clk_in输入1系统时钟(APB PCLK)
rst输入1复位(高有效)
控制信号
enable输入1SPI 传输使能(来自 shift 模块的 tip)
go输入1启动传输信号(来自控制寄存器)
last_clk输入1最后一位时钟标志
divider输入SPI_DIVIDER_LEN分频系数(0 = 最小分频)
输出信号
clk_out输出1SPI 时钟(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

计数器行为

条件操作说明
复位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 1'b0; 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_out=01(!last_clk=1翻转为高
正常传输,clk_out=11(!last_clk=1翻转为低
最后一位,clk_out=00(last_clk=1, clk_out=0保持低
最后一位,clk_out=11(clk_out=1翻转为低

效果:最后一位只输出半个时钟的高电平,然后保持在低电平(空闲状态),避免传输结束后出现多余时钟沿。

六、边沿信号生成

边沿信号提前一个系统时钟周期产生,让移位寄存器有足够时间准备数据。

条件场景说明
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{1'b0}}; // cnt_one = 最低位为 1,其余位为 0 (即数值 1) assign cnt_one = cnt == {{`SPI_DIVIDER_LEN-1{1'b0}}, 1'b1}; // ==================================================================== // 半周期计数器 // 复位时:cnt = 全1 (最大值,SPI 时钟最慢) // 计数过程: // - 若 !enable 或 cnt_zero (空闲或半周期结束),则加载 divider // - 否则 cnt 递减 1 // ==================================================================== 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 // 递减操作:原代码 {{...}} 构造了位宽匹配的 1,简化理解即 cnt - 1 cnt <= #Tp cnt - {{`SPI_DIVIDER_LEN-1{1'b0}}, 1'b1}; end end // ==================================================================== // SPI 时钟输出 (clk_out) // 翻转条件:enable && cnt_zero && (!last_clk || clk_out) // - enable 为高:传输进行中 // - cnt_zero 为高:半周期结束 // - (!last_clk || clk_out) 控制最后一位只输出半个高电平: // 最后一位时 (last_clk=1),若 clk_out=0 则条件不成立 → 不翻转,保持低; // 若 clk_out=1 则条件成立 → 翻转为低。从而实现最后半个周期高电平后停在高电平?实际是最后高后翻低再保持低。 // 正常传输时,last_clk=0,条件恒真,每 cnt_zero 翻转一次。 // ==================================================================== always @(posedge clk_in or posedge rst) begin if(rst) clk_out <= #Tp 1'b0; 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. 最小分频模式 (divider=0): // 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 1'b0; neg_edge <= #Tp 1'b0; 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
http://www.jsqmd.com/news/580725/

相关文章:

  • Full Page Screen Capture技术深度解析:构建高效网页截图解决方案的架构设计与性能优化
  • 联想迎来营收“历史最佳”之年: 三大策略驱动“双位数”增长
  • Pixel Couplet Gen惊艳案例:高校计算机系毕业设计用Pixel Couplet Gen答辩
  • 智能纳米颗粒实现精准药物递送
  • 如何快速除螨虫?2026高效除螨喷雾剂5款测评,仙贝宁医护级速杀螨虫深层清洁 - 博客万
  • Qwen3-TTS-VoiceDesign保姆级教学:Web界面响应超时(timeout)参数调优指南
  • KLayout:开源版图设计的革新性解决方案
  • 如何用d2s-editor解决暗黑2玩家的三大痛点?一站式存档修改方案
  • 构建企业级日志中枢:从架构设计到智能运维
  • 【技术解析】OpenCore Legacy Patcher:macOS硬件兼容性深度解析与实现方案
  • Windows 11任务栏拖放功能终极修复指南:3分钟恢复高效操作体验
  • 解决Windows运行库难题:VisualCppRedist AIO工具全面指南
  • 2026年护发精油排行榜测评:护发精油哪个牌子好? - 博客万
  • 终极解决方案:XGP存档提取器实现游戏存档跨平台迁移
  • Midjourney Tasks API 的集成与使用
  • 本科毕业论文通关指南:用 AI 工具把 “熬夜赶稿” 变成 “高效出稿”
  • OZON小白卖家的选品焦虑:每天刷热销榜,就是选不出一个品
  • AKHQ连接器管理架构深度剖析:企业级Kafka Connect运维解决方案
  • 最新护发精油排名对比:暨护发精油哪个牌子好分析 - 博客万
  • 三步快速配置:极简二维码插件让你的浏览器变身智能跨设备助手
  • 酒店全光解决方案如何提升用户体验?
  • 3步优化副本体验:FF14动画跳过工具技术指南
  • Linux中的head与tail命令及用法详解
  • 如何借助League-Toolkit提升英雄联盟对局表现?全功能使用指南
  • LFM2.5-1.2B-Thinking-GGUF快速入门:使用MobaXterm远程连接Linux服务器部署
  • 1.小组分工与项目框架搭建
  • 不同发质护发精油测评:6款2026年护发精油推荐 - 博客万
  • 终极指南:如何用Applite免费实现Mac软件高效管理
  • 福建领航国际物流多少钱,在福州地区有哪些优势? - mypinpai
  • MTTAIBOOK预装“龙虾”,摩尔线程想把AIPC往前推一步