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.mzph.cn/pingmian/78466.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue响应式原理——vue2和vue3的响应式实现区别

Vue的核心功能点之一是响应式&#xff1a;Vue 会自动跟踪 JavaScript 状态并在其发生变化时响应式地更新 DOM。 简单的来说就是&#xff0c;页面的渲染效果会随着数据变化而变化&#xff0c;不用我们去手动操作DOM树进行数据变化后的渲染。为了实现这一目的&#xff0c;我们最简…

Kaamel白皮书:2025版COPPA落地实操指南

COPPA简介 《儿童在线隐私保护法案》&#xff08;COPPA&#xff09;于1998年在美国颁布&#xff0c;其最初的动因源于人们日益增长的对互联网上收集儿童个人信息的担忧。为了响应这一问题&#xff0c;联邦贸易委员会&#xff08;FTC&#xff09;被授权制定并执行相关法规。COP…

测试基础笔记第十四天

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、字符串1.字符串2.字符串切片3.查找find&#xff08;&#xff09;4.去除两端空白字符 strip5.字符串转换大小写 lower、upper5.拆分 split()6.字符串的其他常见方…

什么是Lua模块?你会如何使用NGINX的Lua模块来定制请求处理流程?

大家好&#xff0c;我是锋哥。今天分享关于【什么是Lua模块&#xff1f;你会如何使用NGINX的Lua模块来定制请求处理流程&#xff1f;】面试题。希望对大家有帮助&#xff1b; 什么是Lua模块&#xff1f;你会如何使用NGINX的Lua模块来定制请求处理流程&#xff1f; 1000道 互联…

ubuntu扩展逻辑卷并调整文件系统大小步骤

安装好ubuntu如果没有调整磁盘空间,一般默认给你100G的空间,在用完时再调整也还来得及,下面是 ubuntu扩展逻辑卷并调整文件系统大小步骤&#xff1a; 1. 扩展逻辑卷 运行以下命令来扩展逻辑卷 /dev/ubuntu-vg/ubuntu-lv&#xff0c;使其使用卷组中所有未分配的空间&#xff…

复杂背景下无人机影像小目标检测:MPE-YOLO抗遮挡与抗背景干扰设计

目录 一、引言 二、挑战和贡献 密集小目标和遮挡 实时性要求与精度权衡 复杂背景 三、MPE-YOLO模型细节 多级特征集成器&#xff08;MFI&#xff09; 感知增强卷积&#xff08;PEC&#xff09; 增强范围C2f模块&#xff08;ES-C2f&#xff09; 四、Coovally AI模型训…

【C++】13.list的模拟实现

首先&#xff0c;我们需要把链表管理起来&#xff0c;也就是把一个个节点管理起来&#xff0c;但是每个节点的信息我们也需要管理&#xff0c;例如节点的前驱指针和后驱指针&#xff0c;以及节点的值&#xff0c;所以我们这里先封装两个类来管理节点和链表。 namespace Ro {te…

TinyVue v3.22.0 正式发布:深色模式上线!集成 UnoCSS 图标库!TypeScript 类型支持全面升级!

我们非常高兴地宣布&#xff0c;2025年4月7日&#xff0c;TinyVue发布了v3.22.0&#x1f389;。 本次 3.22.0 版本主要有以下重大变更&#xff1a; 支持深色模式增加基于 UnoCSS 的图标库更丰富的 TypeScript 类型声明支持 XSS 配置 详细的 Release Notes 请参考&#xff1a…

超级创新思路:基于CBAM-Transformer的强化学习时间序列预测模型(Python\matlab实现)

首先声明,该模型为原创!原创!原创!且该思路还未有成果发表,感兴趣的小伙伴可以借鉴!需要完整代码可私信或评论! 本方案可用于医疗、金融、交通、零售、光伏功率预测、估计预测、天气预测、流量预测、故障检测等领域! 目录 首先声明,该模型为原创!原创!原创!且该思…

Apache Sqoop数据采集问题

Sqoop数据采集格式问题 一、Sqoop工作原理二、Sqoop命令格式三、Oracle数据采集格式问题四、Sqoop增量采集方案 Apache Sqoop是一款开源的工具&#xff0c;主要用于在Hadoop(Hive)与传统的数据库(mysql、postgresql…)间进行数据的传递&#xff0c;可以将一个关系型数据库&…

Grok发布了Grok Studio 和 Workspaces两个强大的功能。该如何使用?如何使用Grok3 API?

最近Grok又更新了几个功能&#xff1a;Grok Studio 和 Workspaces。 其中 Grok Studio 主要功能包括&#xff1a; 代码执行&#xff1a;在预览标签中运行 HTML 片段、Python、JavaScript 等。 Google Drive 集成&#xff1a;附加并处理 Docs、Sheets、Slides等文件。 协作工…

Vue选项式 API 与组合式 API

选项式 API 与组合式 API 选项式 API 选项式 API 是 Vue 2 中常用的开发方式&#xff0c;在 Vue 3 里依旧得到支持。它把组件逻辑划分为不同的选项&#xff0c;像 data、methods、computed 等。 <template><div><p>Count: {{ count }}</p><button…

SiamMask中的分类分支、回归分支与Mask分支,有何本质差异?

SiamMask中的分类分支、回归分支与Mask分支&#xff0c;有何本质差异&#xff1f; 一、引言二、分支定位与任务目标三、网络结构与感受野设计3.1 分类分支&#xff08;Classification Head&#xff09;3.2 回归分支&#xff08;Regression Head&#xff09;3.3 Mask分支&#x…

threejs学习day02

场景、相机、渲染器 一、创建3D场景 // 引入threejs import * as THREE from three// 创建一个三维场景scene const scene new THREE.Scene();// 给三维场景添加物品 const geometry new THREE.BoxGeometry(100,100,100) // 形状 const meterial new THREE.MeshBasicMat…

K8S Pod 常见数据存储方案

假设有如下三个节点的 K8S 集群&#xff1a; k8s31master 是控制节点 k8s31node1、k8s31node2 是工作节点 容器运行时是 containerd 一、理论介绍 1.1、Volumes 卷 Kubernetes 的卷是 pod 的⼀个组成部分&#xff0c;因此像容器⼀样在 pod 的规范&#xff08;pod.spec&#x…

【MySQL数据库】函数操作

目录 1&#xff0c;日期函数 2&#xff0c;字符串函数 3&#xff0c;数学函数 1&#xff0c;日期函数 样例&#xff1a; 获得年月日 select current_date(); 获取时分秒 select current_time(); 获得时间戳 select current_timestamp(); 在日期的基础上加日期 在2025年4月27…

【每日随笔】文化属性 ① ( 天机 | 强势文化与弱势文化 | 文化属性的形成与改变 | 强势文化 具备的特点 )

文章目录 一、文化属性1、天机2、文化属性的强势文化与弱势文化强势文化弱势文化 二、文化属性的形成与改变1、文化属性形成2、文化属性改变3、文化知识的阶层 三、强势文化 具备的 特点 一、文化属性 1、天机 如果想要 了解这个世界的 底层架构 , 就需要掌握 洞察事物本质 的能…

【Fifty Project - D18】

感觉自己就不是计划星球人&#xff0c;虽然fifty project要求每天早上完成一天的计划&#xff0c;但是对于一个p人脑子&#xff0c;强制自己按照计划行事真的太难了。我也理解在早晨花费时间做好一天的计划有很多好处&#xff0c;但是实际行动起来完成率极低。p人的世界里变动太…

Linux系统编程 day11 锁 (两天没有更新了,中期完就休息了)

锁的注意事项 1、尽量保证锁的粒度&#xff0c;越小越好。(访问共享数据前&#xff0c;加锁&#xff0c;访问结束后立即解锁) 2、互斥锁&#xff0c;本质是结构体&#xff0c;但是可以看成整数&#xff0c;初值为1。(pthread_mutex_init调用成功) 3、加锁&#xff1a; --操作…

【Maven】特殊pom.xml配置文件 - BOM

文章目录 特殊pom.xml配置文件 - BOM一、例子二、注意事项1.特殊的子pom.xml文件2.dependencyManagement 特殊pom.xml配置文件 - BOM 仅用于集中管理项目依赖版本 在 Maven 中&#xff0c;BOM 用于定义一个项目的依赖版本的集合&#xff0c;通常用于管理一组共享的依赖版本。这…