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

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博客

相关文章:

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 …...

2025蓝桥省赛c++B组第二场题解

前言 这场的题目非常的简单啊&#xff0c;至于为什么有第二场&#xff0c;因为当时河北正在刮大风被迫停止了QwQ&#xff0c;个人感觉是历年来最简单的一场&#xff0c;如果有什么不足之处&#xff0c;还望补充。 试题 A: 密密摆放 【问题描述】 小蓝有一个大箱子&#xff0…...

vue3 vite打包后动态修改打包后的请求路径,无需打多个包给后端

整体思路和需求 部署多个服务器环境的时候&#xff0c;需要多次打包很麻烦&#xff0c;所以需要打包之后动态的修改 1.创建一个webconfig文件夹 2.在自己封装的接口文件中 判断是否在生产环境&#xff0c;然后将数据保存到vuex 中 代码&#xff1a; // 创建axios服务的函数 …...

Nacos-SpringBoot 配置无法自动刷新问题排查

背景 Nacos SpringBoot版本中&#xff0c;提供了NacosValue注解&#xff0c;支持控制台修改值时&#xff0c;自动刷新&#xff0c;但是今天遇见了无法自动刷新的问题。 环境 SpringBoot 2.2.x nacos-client&#xff1a;2.1.0 nacos-config-spring-boot-starter&#xff1a;0…...

【RabbitMQ消息队列】详解(一)

初识RabbitMQ RabbitMQ 是一个开源的消息代理软件&#xff0c;也被称为消息队列中间件&#xff0c;它遵循 AMQP&#xff08;高级消息队列协议&#xff09;&#xff0c;并且支持多种其他消息协议。 核心概念 生产者&#xff08;Producer&#xff09;&#xff1a;创建消息并将其…...

FFmpeg之三 录制音频并保存, API编解码从理论到实战

在学习FFmpeg的时候&#xff0c;想拿demo来练习&#xff0c;官方虽有示例&#xff0c;但更像是工具演示&#xff0c;新手不好掌握&#xff0c;在网上找不到有文章&#xff0c;能给出完整的示例和关键点的分析说明&#xff0c;一步一个错误&#xff0c;慢慢啃过来的&#xff0c;…...

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

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

Jenkins Pipeline 构建 CI/CD 流程

文章目录 jenkins 安装jenkins 配置jenkins 快速上手在 jenkins 中创建一个新的 Pipeline 作业配置Pipeline运行 Pipeline 作业 Pipeline概述Declarative PipelineScripted Pipeline jenkins 安装 安装环境&#xff1a; Linux CentOS 10&#xff1a;Linux CentOS9安装配置Jav…...

蓝桥杯 8. 移动距离

移动距离 原题目链接 题目描述 X 星球居民小区的楼房全是一样的&#xff0c;并且按矩阵样式排列。楼房的编号为 1, 2, 3, ⋯⋯。 当排满一行时&#xff0c;从下一行相邻的楼往反方向排号。 例如&#xff0c;当小区排号宽度为 6 时&#xff0c;排列如下&#xff1a; 1 2 …...

AJAX 介绍

一、什么是AJAX ? AJAX 是 异步的 JavaScript 和 XML&#xff08;Asynchronous JavaScript And XML&#xff09; 的缩写&#xff0c;是一种实现浏览器与服务器进行数据通信的技术。其核心是通过 XMLHttpRequest 对象在不重新刷新页面的前提下&#xff0c;与服务器交换数据并更…...

硬盘损坏数据恢复后对python程序的影响

最近硬盘突然间坏掉了&#xff0c;让数据商恢复了2个月今天终于拿到了恢复后的数据。 但是一测试问题就来了&#xff1a; PS E:\geosystem> python manage.py runserver 0.0.0.0:5000 Unhandled exception in thread started by <function check_errors.<locals>.…...

promis(resolve,reject)入门级别

JavaScript Promise 的定义 Promise 是一种用于处理异步操作的对象&#xff0c;表示一个可能已经完成或者尚未完成的操作的结果。它的核心作用在于简化复杂的回调嵌套问题&#xff08;即所谓的“回调地狱”&#xff09;&#xff0c;使异步代码更加清晰易读。 Promise 的状态 …...

w~嵌入式C语言~合集6

我自己的原文哦~ https://blog.51cto.com/whaosoft/13870384 一、开源MCU简易数字示波器项目 这是一款采用STC8A8K MCU制造的简单示波器&#xff0c;只有零星组件&#xff0c;易于成型。这些功能可以涵盖简单的测量&#xff1a; 该作品主要的规格如下&#xff1a; 单片机…...

学习海康VisionMaster之路径提取

一&#xff1a;进一步学习了 今天学习下VisionMaster中的路径提取&#xff1a;可在绘制的路径上等间隔取点或查找边缘点 二&#xff1a;开始学习 1&#xff1a;什么是路径提取&#xff1f; 相当于事先指定一段路径&#xff0c;然后在对应的路径上查找边缘&#xff0c;这个也是…...

第十二章-PHP文件上传

第十二章-PHP文件上传 一&#xff0c;文件上传原理 一、HTTP协议与文件上传 1. 请求体结构 当表单设置enctype"multipart/form-data"时&#xff0c;浏览器会将表单数据编码为多部分&#xff08;multipart&#xff09;格式。 Boundary分隔符&#xff1a;随机生成的…...

第35课 常用快捷操作——用“鼠标左键”拖动图元

概述 拖动某个图元&#xff0c;是设计过程中常需要用到的操作&#xff0c;我们可以在原理图中拖动某个元器件符号&#xff0c;也可以在PCB图中拖动某个焊盘。 和常用的软件类似&#xff0c;用按住鼠标左键的方式来完成拖动操作。 用鼠标左键拖动图元 在想要拖动的图元上&…...

qt.qpa.plugin: Could not find the Qt platform plugin “cocoa“ in “ “

开发的pyqt项目在Windows运行时没啥问题&#xff0c;移植到Mac中时&#xff0c;发现一直报错qt.qpa.plugin: Could not find the Qt platform plugin “cocoa” in " "&#xff0c;一开始认为是pyqt版本问题&#xff0c;换了版本依旧不行。后续按照网上pip install o…...

二、Web服务常用的I/O操作

一、单个或者批量上传文件 前端&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>文件…...

「Mac畅玩AIGC与多模态04」开发篇01 - 创建第一个 LLM 对话应用

一、概述 本篇介绍如何在 macOS 环境下&#xff0c;基于已部署完成的 Dify 平台和本地 LLM 模型&#xff08;如 DeepSeek&#xff09;&#xff0c;创建并测试第一个基础对话应用&#xff0c;实现快速验证推理服务与平台交互功能。 二、应用创建流程 1. 通过首页创建应用 打…...

深度探究获取淘宝商品数据的途径|API接口|批量自动化采集商品数据

在电商行业竞争日益激烈的今天&#xff0c;淘宝商品数据如同蕴藏巨大价值的宝藏&#xff0c;无论是商家进行竞品分析、优化商品策略&#xff0c;还是数据分析师挖掘市场趋势&#xff0c;都离不开对这些数据的获取与分析。本文将深入探讨获取淘宝商品数据的多种途径&#xff0c;…...

马哥教育Linux云计算运维课程

课程大小&#xff1a;19.1G 课程下载&#xff1a;https://download.csdn.net/download/m0_66047725/90640128 更多资源下载&#xff1a;关注我 你是否找了很多资料看了很多视频聊了很多群友&#xff0c;却发现自己技术仍然原地踏步&#xff1f;本教程联合BAT一线导师倾囊相授…...

FPGA与边缘AI:计算革命的前沿力量

在数字化转型浪潮中&#xff0c;边缘计算和人工智能正引领着技术革命。而FPGA&#xff08;现场可编程门阵列&#xff09;作为一种独特的硬件架构&#xff0c;正逐渐成为边缘AI领域的关键推动力。本文将探讨FPGA与边缘AI的结合如何重塑我们的数字世界&#xff0c;以及这一技术融…...

Kafka 架构设计和组件介绍

什么是Apache Kafka&#xff1f; Apache Kafka 是一个强大的开源分布式事件流平台。它最初由 LinkedIn 开发&#xff0c;最初是一个消息队列&#xff0c;后来发展成为处理各种场景数据流的工具。 Kafka 的分布式系统架构支持水平扩展&#xff0c;使消费者能够按照自己的节奏检…...

虚函数表的设计和多态的实现

虚函数表 1.包含虚函数的类会有对应的虚函数表&#xff0c;这个表在编译时就初始化好了 2.本质是一个函数指针数组&#xff0c;里面是虚函数的指针 3.该类实例化的对象共用一张虚函数表 4.子类的虚函数表会继承父类的虚函数&#xff0c;如果继承多个父类那就把父类的虚函数…...

【Node.js 】在Windows 下搭建适配 DPlayer 的轻量(简陋)级弹幕后端服务

一、引言 DPlayer官网&#xff1a;DPlayer 官方弹幕后端服务&#xff1a;DPlayer-node MoePlayer/DPlayer-node&#xff1a;使用 Docker for DPlayer Node.js 后端&#xff08;https://github.com/DIYgod/DPlayer&#xff09; 本来想直接使用官网提供的DPlayer-node直接搭建…...

OpenSSH配置连接远程服务器MS ODBC驱动与Navicat数据库管理

OpenSSH配置连接远程服务器MS ODBC驱动与Navicat数据库管理 目录 OpenSSH配置连接远程服务器MS ODBC驱动与Navicat数据库管理 一、MS ODBC驱动 1.1、安装到Windows后的表现形式 1.2、版本的互斥性 1.3、安装程序 1.4、配置后才可用 二、Navicat数据库管理工具 2.1、安…...

织梦dedecms调用会员详细字段信息

织梦如何调用会员详细信息&#xff1a; 在include/extend.func.php function GetMemberInfos($fields,$mid){ global $dsql; if($mid < 0){ $revalue "Error"; } else{ $row$dsql->GetOne("sele ct * fr…...

MySQL 8.0 忘记登录密码 mysqld --init-file重置

看到了很多跳过授权表的办法&#xff0c;这里通过mysqld --init-file办法。 适用情况&#xff1a; 服务器可以启动但无法登录/忘记登录密码。 一、首先停止 MySQL 服务&#xff1a; 按下 Win R 组合键&#xff0c;输入 services.msc 并点击“确定”&#xff0c;打开“服务”…...

Python 学习路线与笔记跳转(持续更新笔记链接)

这里写目录标题 Python 学习路线与笔记Python 简介学习路线第一阶段&#xff1a;Python 基础第二阶段&#xff1a;Python 进阶第三阶段&#xff1a;实用库与框架第四阶段&#xff1a;DevOps 与 Python第五阶段&#xff1a;最佳实践与高级技巧 学习资源官方资源在线学习平台书籍…...

操作系统:计算机世界的基石与演进

一、操作系统的本质与核心功能 操作系统如同计算机系统的"总管家"&#xff0c;在硬件与应用之间架起关键桥梁。从不同视角观察&#xff0c;其核心功能呈现多维价值&#xff1a; 硬件视角的双重使命&#xff1a; 硬件管理者&#xff1a;通过内存管理、进程调度和设…...