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

32BIT的SPI主机控制

SPI传输位数可参数化配置。

SPI_MASTER:

`timescale 1ns / 1ps
module SPI_Master #(parameter CLK_FREQ = 50,parameter SPI_CLK  = 1000,parameter CPOL = 0,parameter CPHA = 0 
)(input         clk,input         rst_n,input         WrRdReq,   //读/写数据请求output        WrRdReqAck,output        WrRdFinish,input  [6:0]  WrRdDataBits,  //输入数据位宽input  [31:0] WrData,        //要写入的数据 output [31:0] RdData,        //读取到的数据//SPI接口output        SCK,output        MOSI,input         MISO,output        CS
);//采样信号
wire datain_en;
wire dataout_en;SPI_Master_Clock#
(.CLK_FREQ(CLK_FREQ),.CPOL(CPOL),.CPHA(CPHA),.SPI_CLK_FREQ(SPI_CLK)
)
u_SPI_Master_Clock
(
.clk(clk),
.rst_n(rst_n),.datain_en(datain_en),
.dataout_en(dataout_en),
.SCK(SCK)
);SPI_Master_ctrl u_SPI_Master_ctrl
(.clk(clk),         .rst_n(rst_n),       .WrRdReq(WrRdReq),   .WrRdReqAck(WrRdReqAck),  .WrRdFinish(WrRdFinish),  .WrRdDataBits(WrRdDataBits),.WrData(WrData),      .RdData(RdData),      .datain_en(datain_en),   .dataout_en(dataout_en),  .MOSI(MOSI),     .MISO(MISO),        .CS(CS)          
);
endmodule

SPI_MATSER_CLOCK

module SPI_Master_Clock#(parameter CLK_FREQ = 50,  //MHZparameter CPOL = 1'b0,parameter CPHA = 1'b0,parameter SPI_CLK_FREQ = 1000   //Khz
)(input clk,input rst_n,output datain_en,  //数据采样控制信号output dataout_en, //数据输出控制信号output SCK
);localparam sck_idle = CPOL;
localparam CLK_DIV_CNT = (CLK_FREQ *1000)/SPI_CLK_FREQ; //分频计数器,此处分频时钟一个周期,分频时钟计数器要计数50,50个主时钟周期//位宽计算
function integer clogb2(input integer depth);beginfor(clogb2 = 0;depth > 0;clogb2= clogb2 + 1)depth = depth >> 1;       end
endfunction
reg[clogb2(CLK_DIV_CNT)-1:0]  clkdiv_cnt; //[(6-1):0]reg SPI_SCK;
reg datain_en_o = 1'b0,dataout_en_o = 1'b0;//分频时钟计数器,计数到最大值表明经历了一个SCK周期
always@(posedge clk or negedge rst_n)beginif(rst_n == 0)beginclkdiv_cnt <= 1'b0; end else if(clkdiv_cnt == CLK_DIV_CNT - 1'b1)beginclkdiv_cnt <= 1'b0;end else beginclkdiv_cnt <= clkdiv_cnt + 1'b1;end
end//sck 
always@(posedge clk or negedge rst_n)beginif(rst_n == 0)beginSPI_SCK <= sck_idle;end  else if((clkdiv_cnt == (CLK_DIV_CNT - 1'b1)) || (clkdiv_cnt == (CLK_DIV_CNT >> 1) -1'b1))beginSPI_SCK <= ~SPI_SCK;end else beginSPI_SCK <= SPI_SCK;end   
end   //输入输出使能
always@(posedge clk or negedge rst_n)beginif(!rst_n)begindatain_en_o <= 1'b0;dataout_en_o  <= 1'b0;end else begin        dataout_en_o <= (clkdiv_cnt == (CLK_DIV_CNT  - 'd3))?1'b1:1'b0;datain_en_o  <= (clkdiv_cnt == (CLK_DIV_CNT >> 1) - 'd1)?1'b1:1'b0;end endassign datain_en  = datain_en_o;assign dataout_en = dataout_en_o;assign SCK = SPI_SCK; 
endmodule

SPI_MATSER_CTRL:

module SPI_Master_ctrl
(input         clk,input         rst_n,input         WrRdReq,   //读/写数据请求output        WrRdReqAck,output        WrRdFinish,input  [6:0]  WrRdDataBits,  //输入数据位宽input  [31:0] WrData,        //要写入的数据 output [31:0] RdData,        //读取到的数据//SPI写读控制信号input         datain_en,input         dataout_en,//SPI接口output        MOSI,input         MISO,output        CS
);localparam [7:0] S_IDLE =8'd0;
localparam [7:0] S_ACK  =8'd1;
localparam [7:0] S_RUN  =8'd2;
localparam [7:0] S_END  =8'd3;
localparam [7:0] S_RST  =8'd4;reg [7:0] current_state = S_IDLE;
reg [7:0] next_state    = S_IDLE; 
reg       CS_O = 1'b1;
reg [7:0]  WrRdBitsCnt = 8'd0,WrRdBitsLatch = 8'd0;  //用于计数已经发送或接收的位数 //用于存储发送或接收的数据位数
reg [31:0] WrDataLatch = 32'd0,RdDataLatch  = 32'd0; //用于存储要发送的数据  //用于存储从 SPI 设备接收到的数据//reg MOSI_o;always@(posedge clk or negedge rst_n)beginif(!rst_n)begincurrent_state <= S_RST;end else begincurrent_state <= next_state;end
endalways@(*)beginnext_state = S_RST;case(current_state)S_RST:next_state = S_IDLE;S_IDLE:next_state = (WrRdReq)?S_ACK:S_IDLE;S_ACK:next_state = (WrRdReq)?S_ACK:S_RUN;S_RUN:next_state = (WrRdBitsCnt == WrRdBitsLatch)? S_END : S_RUN;S_END:next_state = S_IDLE;default:next_state = S_RST;endcase
end//发送数据模块 
always@(posedge clk)begincase(current_state)S_RST,S_IDLE: WrDataLatch <= 32'd0;S_ACK: WrDataLatch <= WrData;S_RUN:beginif(dataout_en)beginWrDataLatch <= {WrDataLatch[30:0],1'b0}; //先输出高位数据出来
//                MOSI_o <= WrDataLatch[31];end else beginWrDataLatch <= WrDataLatch;endenddefault:WrDataLatch <= 32'd0;endcase
end//接收数据模块 
always@(posedge clk)begincase(current_state)S_RST,S_ACK:RdDataLatch <=32'd0;S_RUN:beginif(datain_en)beginRdDataLatch <= {RdDataLatch[30:0],MISO}; //先把高位数据输入进去end else beginRdDataLatch <= RdDataLatch;endenddefault:RdDataLatch <= RdDataLatch;endcase
end//时钟边沿计数 已处理的位数计数器
always@(posedge clk)begincase(current_state)S_RST:WrRdBitsCnt <= 8'd0;S_RUN:beginif(datain_en || dataout_en)beginWrRdBitsCnt <= WrRdBitsCnt + 1'b1;end else beginWrRdBitsCnt <= WrRdBitsCnt;endenddefault:WrRdBitsCnt <= 8'd0;endcase
end//片选信号
always@(posedge clk)begincase(current_state)  //其余状态保持默认值1S_RUN:CS_O <= 1'b0;default:CS_O <= 1'b1;endcase
end//锁存要处理的数据位数
always@(posedge clk)begincase(current_state)S_RST:WrRdBitsLatch <= 8'd0;S_ACK:WrRdBitsLatch <= {WrRdDataBits,1'b0};default:WrRdBitsLatch <= WrRdBitsLatch;endcase
end assign WrRdFinish = (current_state == S_END);
assign RdData = RdDataLatch;
//请求ack信号
assign WrRdReqAck = (current_state == S_ACK);
assign MOSI = WrDataLatch[31];
assign CS =CS_O;
endmodule

TB:

`timescale 1ns / 1psmodule tb_SPI_Master;// 参数定义parameter CLK_FREQ = 50;  // 时钟频率 50 MHzparameter SPI_CLK = 1000; // SPI 时钟频率 1 MHzparameter CPOL = 0;       // 时钟极性parameter CPHA = 0;       // 时钟相位// 信号定义reg clk;reg rst_n;reg WrRdReq;wire WrRdReqAck;wire WrRdFinish;reg [6:0] WrRdDataBits;reg [31:0] SPIMasterWrData;  // 主机发送的数据wire [31:0] SPIMasterRdData; // 主机接收的数据wire SCK;wire MOSI;reg MISO;wire CS;// 从机接收数据寄存器reg [31:0] SPISlaveRdData = 0; // 从机接收的数据// 从机发送数据寄存器reg [31:0] SPISlaveWrData = 0; // 从机发送的数据// 实例化 SPI_Master 模块SPI_Master #(.CLK_FREQ(CLK_FREQ),.SPI_CLK(SPI_CLK),.CPOL(CPOL),.CPHA(CPHA)) uut (.clk(clk),.rst_n(rst_n),.WrRdReq(WrRdReq),.WrRdReqAck(WrRdReqAck),.WrRdFinish(WrRdFinish),.WrRdDataBits(WrRdDataBits),.WrData(SPIMasterWrData),.RdData(SPIMasterRdData),.SCK(SCK),.MOSI(MOSI),.MISO(MISO),.CS(CS));// 时钟生成always #10 clk = ~clk;// 模拟从机行为:接收数据always @(posedge SCK) beginif (CS == 0) beginSPISlaveRdData <= {SPISlaveRdData[30:0], MOSI}; // 从机接收数据endend//    reg [31:0] SPISlaveWrData_o;// 模拟从机行为:发送数据always @(posedge SCK) beginif (CS == 0) beginMISO <= SPISlaveWrData[31]; // 从机发送最高位SPISlaveWrData <= SPISlaveWrData << 1; // 数据左移endend// 测试过程initial begin// 初始化信号clk = 0;rst_n = 0;WrRdReq = 0;WrRdDataBits = 32;  // 发送 32 位数据SPIMasterWrData = 32'h82345678;  // 主机发送的数据SPISlaveWrData = 32'h12312345;MISO = 0;// 复位系统#20 rst_n = 0;#20 rst_n = 1;// 发送数据请求#100 WrRdReq = 1;  // 模拟发送请求#100 WrRdReq = 0;  // 结束发送请求// 等待发送完成wait(WrRdFinish == 1);// 检查从机接收到的数据if (SPISlaveRdData == 32'h82345678) begin$display("Test Passed: SPISlaveRdData = %h", SPISlaveRdData);end else begin$display("Test Failed: SPISlaveRdData = %h, Expected = %h", SPISlaveRdData, 32'h82345678);end// 等待主机接收完成#100;// 检查主机接收到的数据if (SPIMasterRdData == 32'h12312345) begin$display("Test Passed: SPIMasterRdData = %h", SPIMasterRdData);end else begin$display("Test Failed: SPIMasterRdData = %h, Expected = %h", SPIMasterRdData, 32'h12312345);end// 结束仿真#100 $stop;endendmodule

仿真图略

参考:Verilog实现的SPI通信协议(主机模式)_spi 控制器的状态机跳转-CSDN博客

http://www.xdnf.cn/news/114121.html

相关文章:

  • 架构-系统工程与信息系统基础
  • 【Spring Boot】深入解析:#{} 和 ${}
  • java Springboot使用扣子Coze实现实时音频对话智能客服
  • 低空AI系统的合规化与标准化演进路径
  • 考研英一学习笔记
  • 数据结构——二叉树,堆
  • 【农气项目】基于适宜度的产量预报
  • Unity性能优化实战:用Profiler揪出卡顿元凶 (CPU/GPU/内存/GC全面解析) (Day 37)
  • http协议、全站https
  • 特征存储的好处:特征存储在机器学习开发中的优势
  • 【Linux】基于阻塞队列的生产消费者模型
  • 水上与水下遥控技术要点对比
  • 利用软件I2C驱动OLED,点亮、熄灭OLED屏幕以及获取当前OLED屏幕开启状态
  • 水域陆地两相宜,便携漏电探测仪
  • 【数据分析】酵母实验多指标数据的 R 语言分析与可视化
  • YOLOv11改进-双Backbone架构:利用双backbone提高yolo11目标检测的精度
  • 26考研 | 王道 | 数据结构 | 第七章 查找
  • SpringBoot | 构建客户树及其关联关系的设计思路和实践Demo
  • WHAT - 区分 Git PR 和 MR
  • UML 活动图详解:以机票预订系统用户注册为例
  • 【蓝桥杯】水质检测
  • 5.第五章:数据分类的方法论
  • 30天通过软考高项-第二天
  • IDEA导入并启动若依项目步骤(SpringBoot+Vue3)
  • 从节点重排看React 与 Vue3 的 Diff 算法
  • OpenBMC:BmcWeb login认证
  • C#中用 OxyPlot 在 WinForms 实现波形图可视化(附源码教程)
  • OpenCV 图形API(61)图像特征检测------检测图像边缘的函数Canny()
  • 认识游戏循环
  • 把dll模块注入到游戏进程的方法_插APC注入