SPI总线verilog hdl实现(1)SPI通信

2023-11-14

SPI总线传输只需要4根线就能完成,这四根线的作用分别如下:
  SCK(Serial Clock):SCK是串行时钟线,作用是Master向Slave传输时钟信号,控制数据交换的时机和速率;
  MOSI(Master Out Slave in):在SPI Master上也被称为Tx-channel,作用是SPI主机给SPI从机发送数据;
  CS/SS(Chip Select/Slave Select):作用是SPI Master选择与哪一个SPI Slave通信,低电平表示从机被选中(低电平有效);
  MISO(Master In Slave Out):在SPI Master上也被称为Rx-channel,作用是SPI主机接收SPI从机传输过来的数据;
  SPI总线传输一共有4中模式,这4种模式分别由时钟极性(CPOL,Clock Polarity)和时钟相位(CPHA,Clock Phase)来定义,其中CPOL参数规定了SCK时钟信号空闲状态的电平,CPHA规定了数据是在SCK时钟的上升沿被采样还是下降沿被采样。
  模式0:CPOL= 0,CPHA=0。SCK串行时钟线空闲是为低电平,数据在SCK时钟的上升沿被采样,数据在SCK时钟的下降沿切换
  模式1:CPOL= 0,CPHA=1。SCK串行时钟线空闲是为低电平,数据在SCK时钟的下降沿被采样,数据在SCK时钟的上升沿切换
  模式2:CPOL= 1,CPHA=0。SCK串行时钟线空闲是为高电平,数据在SCK时钟的下降沿被采样,数据在SCK时钟的上升沿切换
  模式3:CPOL= 1,CPHA=1。SCK串行时钟线空闲是为高电平,数据在SCK时钟的上升沿被采样,数据在SCK时钟的下降沿切换
  以模式0为例
在这里插入图片描述
发送:当FPGA通过SPI总线往从机中发送一个字节(8-bit)的数据时,首先FPGA把CS/SS片选信号设置为0,表示准备开始发送数据,整个发送数据过程其实可以分为16个状态:
    状态0:SCK为0,MOSI为要发送的数据的最高位,即I_data_in[7]
    状态1:SCK为1,MOSI保持不变
    状态2:SCK为0,MOSI为要发送的数据的次高位,即I_data_in[6]
    状态3:SCK为1,MOSI保持不变
    状态4:SCK为0,MOSI为要发送的数据的下一位,即I_data_in[5]
    状态5:SCK为1,MOSI保持不变
    状态6:SCK为0,MOSI为要发送的数据的下一位,即I_data_in[4]
    状态7:SCK为1,MOSI保持不变
    状态8:SCK为0,MOSI为要发送的数据的下一位,即I_data_in[3]
    状态9:SCK为1,MOSI保持不变
    状态10:SCK为0,MOSI为要发送的数据的下一位,即I_data_in[2]
    状态11:SCK为1,MOSI保持不变
    状态12:SCK为0,MOSI为要发送的数据的下一位,即I_data_in[1]
    状态13:SCK为1,MOSI保持不变
    状态14:SCK为0,MOSI为要发送的数据的最低位,即I_data_in[0]
    状态15:SCK为1,MOSI保持不变  
一个字节数据发送完毕以后,产生一个发送完成标志位O_tx_done并把CS/SS信号拉高完成一次发送。
 接收:当FPGA通过SPI总线从从机中接收一个字节(8-bit)的数据时,首先FPGA把CS/SS片选信号设置为0,表示准备开始接收数据,整个接收数据过程其实也可以分为16个状态,但是与发送过程不同的是,为了保证接收到的数据准确,必须在数据的正中间采样,也就是说模式0时序图中灰色实线的地方才是代码中锁存数据的地方,所以接收过程的每个状态执行的操作为:
    状态0:SCK为0,不锁存MISO上的数据
    状态1:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[7]
    状态2:SCK为0,不锁存MISO上的数据
    状态3:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[6]
    状态4:SCK为0,不锁存MISO上的数据
    状态5:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[5]
    状态6:SCK为0,不锁存MISO上的数据
    状态7:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[4]
    状态8:SCK为0,不锁存MISO上的数据
    状态9:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[3]
    状态10:SCK为0,不锁存MISO上的数据
    状态11:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[2]
    状态12:SCK为0,不锁存MISO上的数据
    状态13:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[1]
    状态14:SCK为0,不锁存MISO上的数据
    状态15:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[0]
  一个字节数据接收完毕以后,产生一个接收完成标志位O_rx_done并把CS/SS信号拉高完成一次数据的接收。

I_clk是系统时钟;
  I_rst_n是系统复位;
  I_tx_en是主机给从机发送数据的使能信号,当I_tx_en为1时主机才能给从机发送数据;
  I_rx _en是主机从从机接收数据的使能信号,当I_rx_en为1时主机才能从从机接收数据;
  I_data_in是主机要发送的并行数据;
  O_data_out是把从机接收回来的串行数据并行化以后的并行数据;
  O_tx_done是主机给从机发送数据完成的标志位,发送完成后会产生一个高脉冲;
  O_rx_done是主机从从机接收数据完成的标志位,接收完成后会产生一个高脉冲;
  I_spi_miso、O_spi_cs、O_spi_sck和O_spi_mosi是标准SPI总线协议规定的四根线;
  完整代码如下:

module  spi_module
(
    input       I_clk,//系统时钟50MHz
    input       I_rst_n,//复位信号,低电平有效
    input       I_rx_en,//读使能信号
    input       I_tx_en,//发送使能信号
    input   [7:0]   I_data_in,//要发送的数据
    output  reg     [7:0]   O_data_out,//接收到的数据
    output  reg             O_tx_done,//发送一个字节完毕标志位
    output  reg             O_rx_done,//接收一个字节完毕标志位 
    //四线标准SPI信号定义
    input       I_spi_miso,//SPI串行输入,用来接收从机数据
    output  reg O_spi_sck, //SPI时钟
    output  reg O_spi_cs,  //SPI片选信号
    output  reg O_spi_mosi //SPI输出,用来给从机发数据       
);

reg [3:0]   R_tx_state  ;
reg [3:0]   R_rx_state  ;
always  @(posedge   I_clk   or  negedge I_rst_n)
begin
        if(!I_rst_n)
            begin
                R_tx_state  <=  4'd0    ;
                R_rx_state  <=  4'd0    ;
                O_spi_cs    <=  1'b1    ;
                O_spi_sck   <=  1'b0    ;
                O_spi_mosi  <=  1'b0    ;
                O_tx_done   <=  1'b0    ;
                O_rx_done   <=  1'b0    ;
                O_data_out  <=  1'b0    ;       
            end
        else    if(I_tx_en)//发送使能信号打开
            begin
                O_spi_cs    <=  1'b0;//把片选拉低
                case(R_tx_state)
                    4'd1,4'd3,4'd5,4'd7,4'd9,4'd11,4'd13,4'd15:  //奇数状态
                        begin
                            O_spi_sck   <=  1'b1;
                            R_tx_state  <=  R_tx_state  +   1'b1;
                            O_tx_done   <=  1'b0;    
                        end
                    4'd0://发送第7位
                        begin
                            O_spi_mosi  <=  I_data_in[7]        ;
                            O_spi_sck   <=  1'b0                ;
                            R_tx_state  <=  R_tx_state + 1'b1   ;
                            O_tx_done   <=  1'b0                ;        
                        end
                    4'd2://发送第6位
                        begin
                            O_spi_mosi  <=  I_data_in[6]        ;
                            O_spi_sck   <=  1'b0                ;
                            R_tx_state  <=  R_tx_state + 1'b1   ;
                            O_tx_done   <=  1'b0                ;        
                        end
                    4'd4://发送第5位
                        begin
                            O_spi_mosi  <=  I_data_in[5]        ;
                            O_spi_sck   <=  1'b0                ;
                            R_tx_state  <=  R_tx_state + 1'b1   ;
                            O_tx_done   <=  1'b0                ;        
                        end
                    4'd6://发送第4位
                        begin
                            O_spi_mosi  <=  I_data_in[4]        ;
                            O_spi_sck   <=  1'b0                ;
                            R_tx_state  <=  R_tx_state + 1'b1   ;
                            O_tx_done   <=  1'b0                ;        
                        end
                    4'd8://发送第3位
                        begin
                            O_spi_mosi  <=  I_data_in[3]        ;
                            O_spi_sck   <=  1'b0                ;
                            R_tx_state  <=  R_tx_state + 1'b1   ;
                            O_tx_done   <=  1'b0                ;        
                        end
                    4'd10://发送第2位
                        begin
                            O_spi_mosi  <=  I_data_in[2]        ;
                            O_spi_sck   <=  1'b0                ;
                            R_tx_state  <=  R_tx_state + 1'b1   ;
                            O_tx_done   <=  1'b0                ;        
                        end
                    4'd12://发送第1位
                        begin
                            O_spi_mosi  <=  I_data_in[1]        ;
                            O_spi_sck   <=  1'b0                ;
                            R_tx_state  <=  R_tx_state + 1'b1   ;
                            O_tx_done   <=  1'b0                ;        
                        end
                    4'd14://发送第0位
                        begin
                            O_spi_mosi  <=  I_data_in[0]        ;
                            O_spi_sck   <=  1'b0                ;
                            R_tx_state  <=  R_tx_state + 1'b1   ;
                            O_tx_done   <=  1'b1                ;        
                        end
                    default:R_tx_state  <=  4'd0                ;                                                                    
                endcase    
            end
        else    if(I_rx_en)//接收使能信号打开
            begin
                 O_spi_cs    <=  1'b0;//把片选拉低
                 case(R_rx_state)
                    4'd0,4'd2,4'd4,4'd6,4'd8,4'd10,4'd12,4'd14:
                        begin
                            O_spi_sck       <= 1'b0                 ;
                            R_rx_state      <=  R_rx_state  +   1'b1;
                            O_rx_done       <=  1'b0                ;
                        end
                    4'd1://接收第7位
                        begin
                            O_spi_sck       <=  1'b1                ;
                            R_rx_state      <=  R_rx_state + 1'b1   ;
                            O_rx_done       <=  1'b0                ;
                            O_data_out[7]   <=  I_spi_miso          ;         
                        end
                    4'd3:    // 接收第6位
                        begin
                            O_spi_sck       <=  1'b1                ;
                            R_rx_state      <=  R_rx_state + 1'b1   ;
                            O_rx_done       <=  1'b0                ;
                            O_data_out[6]   <=  I_spi_miso          ; 
                        end
                    4'd5:    // 接收第5位
                        begin
                            O_spi_sck       <=  1'b1                ;
                            R_rx_state      <=  R_rx_state + 1'b1   ;
                            O_rx_done       <=  1'b0                ;
                            O_data_out[5]   <=  I_spi_miso          ; 
                        end 
                    4'd7:    // 接收第4位
                        begin
                            O_spi_sck       <=  1'b1                ;
                            R_rx_state      <=  R_rx_state + 1'b1   ;
                            O_rx_done       <=  1'b0                ;
                            O_data_out[4]   <=  I_spi_miso          ; 
                        end 
                    4'd9:    // 接收第3位
                        begin
                            O_spi_sck       <=  1'b1                ;
                            R_rx_state      <=  R_rx_state + 1'b1   ;
                            O_rx_done       <=  1'b0                ;
                            O_data_out[3]   <=  I_spi_miso          ; 
                        end                            
                    4'd11:    // 接收第2位
                        begin
                            O_spi_sck       <=  1'b1                ;
                            R_rx_state      <=  R_rx_state + 1'b1   ;
                            O_rx_done       <=  1'b0                ;
                            O_data_out[2]   <=  I_spi_miso          ; 
                        end 
                    4'd13:    // 接收第1位
                        begin
                            O_spi_sck       <=  1'b1                ;
                            R_rx_state      <=  R_rx_state + 1'b1   ;
                            O_rx_done       <=  1'b0                ;
                            O_data_out[1]   <=  I_spi_miso          ; 
                        end 
                    4'd15:    // 接收第0位
                        begin
                            O_spi_sck       <=  1'b1                ;
                            R_rx_state      <=  R_rx_state + 1'b1   ;
                            O_rx_done       <=  1'b1                ;
                            O_data_out[0]   <=  I_spi_miso          ; 
                        end
                    default:R_rx_state  <=  4'd0                    ;                                                                   
                 endcase            
            end
        else
            begin
                R_tx_state  <=  4'd0    ;
                R_rx_state  <=  4'd0    ;
                O_tx_done   <=  1'b0    ;
                O_rx_done   <=  1'b0    ;
                O_spi_cs    <=  1'b1    ;
                O_spi_sck   <=  1'b0    ;
                O_spi_mosi  <=  1'b0    ;
                O_data_out  <=  8'd0    ;
            end                                            
end
endmodule

testbench文件

`timescale 1ns / 1ps
module tb_spi_module;
    // Inputs
    reg I_clk;
    reg I_rst_n;
    reg I_rx_en;
    reg I_tx_en;
    reg [7:0] I_data_in;
    reg I_spi_miso;
    // Outputs
    wire [7:0] O_data_out;
    wire O_tx_done;
    wire O_rx_done;
    wire O_spi_sck;
    wire O_spi_cs;
    wire O_spi_mosi;  
// Instantiate the Unit Under Test (UUT)
spi_module  tb_uut(
    .I_clk          (I_clk),
    .I_rst_n        (I_rst_n),
    .I_rx_en        (I_rx_en),
    .I_tx_en        (I_tx_en),
    .I_data_in      (I_data_in),
    .O_data_out     (O_data_out),
    .O_tx_done      (O_tx_done),
    .O_rx_done      (O_rx_done),
    .I_spi_miso     (I_spi_miso),
    .O_spi_sck      (O_spi_sck), 
    .O_spi_cs       (O_spi_cs), 
    .O_spi_mosi     (O_spi_mosi)      
);
    initial begin
        // Initialize Inputs
        I_clk = 0;
        I_rst_n = 0;
        I_rx_en = 0;
        I_tx_en = 1;
        I_data_in = 8'h00;
        I_spi_miso = 0;
        // Wait 100 ns for global reset to finish
        #100;
        I_rst_n = 1;  
    end
    always #10 I_clk = ~I_clk ;
     always @(posedge I_clk or negedge I_rst_n)
    begin
         if(!I_rst_n)
            I_data_in <= 8'h00;
         else if(I_data_in == 8'hff)
            begin
                I_data_in <= 8'hff;
                I_tx_en <= 0;
            end
         else if(O_tx_done)
            I_data_in <= I_data_in + 1'b1 ;            
    end
    endmodule

仿真波形
在这里插入图片描述
 在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SPI总线verilog hdl实现(1)SPI通信 的相关文章

  • 学习linux内核的经典书籍介绍

    有关内核的书籍可以用汗牛充栋来形容 不过只有一些经典的神作经住了考验 首先是5本久经考验的神作 个人概括为 2 1 2 第一个2是指2本全面讲解内核的书 中间的1指1本讲解驱动开发的书 后面的2则指2本有关内核具体子系统的书 你是否想到了某

随机推荐

  • vscode 静态语法检测插件C/C++ Advanced Lint,ubuntu20.04安装clang、cppcheck

    远程环境 ubuntu20 04 本地开发环境 windows 11 开发IDE vscode 一 ubuntu20 04安装clang 安装llvm apt get install llvm 2 安装clang apt get insta
  • 11-数据结构-双链表的创建-输出-初始化-插入-删除

    问题 双链表的创建 输出 初始化 插入 删除 思路 搞清楚双链表是怎么样的 多了一个前指针 此外每次链接的时候 都是先连接好后面的再连接前面的 直接看代码吧 代码如下 include
  • 思科实验 voip通信的配置(内附命令超详细)

    作者 小刘在C站 个人主页 小刘主页 每天分享云计算网络运维课堂笔记 努力不一定有回报 但一定会有收获加油 一起努力 共赴美好人生 夕阳下 是最美的绽放 树高千尺 落叶归根人生不易 人间真情 目录 一 实验目的和要求 二 实验设备
  • Python疫情数据爬取与可视化

    使用Python爬取腾讯新闻疫情数据 并使用pyecharts可视化 绘制增长人数地图 柱状图 折线图 文章目录 1 分析网页 2 导入模块 3 抓取数据 4 提取数据并写入Excel 5 国内各地区现有确诊人数地图 6 国内各地区现有确诊
  • 秒懂边缘云

    作者 辰舒 章节内容 如何配置解析记录 已有CNAME A记录时如何配置 如何检查解析配置是否生效 解析基本原理 前言 上个章节中我们学习了如何根据业务情况添加CDN域名并配置源站 但在添加完成后 域名尚未真正接入CDN服务 我们需要将域名
  • Kafka

    1 概述 1 1 定义 Kafka是一个分布式的基于发布 订阅模式的消息队列 主要应用于大数据实时处理领域 优势 kafka可以做到 使用非常普通的硬件 也可以支持每秒数百万的消息读写 MQ 代表消息队列 kafka只是众多mq中一款产品
  • Shell脚本编程入门--Day2

    文章目录 几个简单内置shell命令 shell字串的语法 计算变量长度的各种玩法 批量修改文件名 特殊shell扩展变量 实际应用 父子shell 创建进程列表 创建子shell 几个简单内置shell命令 echo n 不换行输出 e
  • 牛客面试高频算法题js(最长无重复子数组、删除链表的倒数第n个节点、大数加法、按之字形顺序打印二叉树、链表相加(二))

    最长无重复子数组 描述 给定一个长度为n的数组arr 返回arr的最长无重复元素子数组的长度 无重复指的是所有数字都不相同 子数组是连续的 比如 1 3 5 7 9 的子数组有 1 3 3 5 7 等等 但是 1 3 7 不是子数组 数据范
  • Java并发基础:变量的线程安全

    一 变量的线程安全分析 成员变量和静态变量是否线程安全 如果它们没有共享 则线程安全 如果它们被共享了 根据它们的状态是否能够改变 又分两种情况 如果只有读操作 则线程安全 如果有读写操作 则这段代码是临界区 需要考虑线程安全 局部变量是否
  • 解决Windows Server 2012 R2无法远程桌面

    通常使用Windows Server 的远程桌面 我们自然是在配置中启用远程桌面 系统会默认将所需要的防火墙对应的入站协议启用 这样我们就可使用远程桌面了 但是如果您尝试了Windows Server 2012 R2后 或许会碰到和我一样的
  • range遍历 channel 的小坑

    package main import fmt sync var wg sync WaitGroup func main c make chan int 无缓冲 wg Add 1 go send c for i range c send 协
  • 第六篇 在ROS工程中使用QT开发界面笔记之--混用线程和信号槽机制

    有个新任务 需要显示sub到的数据内容 看起来挺简单的 毕竟原来那个工程采用zmq数据流的发送与接收时用到了启动新线程接收数据的方法 照搬肯定不行 因为有区别 原来工程直接在新线程里面持续接收数据 与界面的主线程没有关系 现在这种要用界面s
  • MQ--6 Message queuing)>>>>微服务与dubbo以及Nexus

    MQ 1 Message queuing gt gt gt gt RabbitMQ MQ 2 Message queuing gt gt gt gt ZooKeeper MQ 3 Message queuing gt gt gt gt ka
  • Lua文件夹及文件操作(清空文件夹下所有文件、创建多级文件夹、删除文件夹所有文件等)

    在不适用lfs库的情况下对文件和文件夹进行简单的操作 引用 require FileLib 调用 fileLib createFolder path 功能 1 创建文件夹 2 连续创建文件夹 3 删除文件夹所有内容 4 删除空文件夹 文件
  • 蓝桥杯决赛之行的感悟

    这是一篇瞎写的感悟 想到哪写到哪 从去年11月份开始参加学校的训练团队 到现在5月份 也是半年多一个月了 期间就参加了比较水的蓝桥杯 另外还有就是百度之星 蓝桥杯确实跟大伙说的差不多 含金量并不高 适合大众玩的 不适合黑客玩 省赛的话 一共
  • 使用Gradle自动化构建多类型apk包

    作者 Ezio Shiki 链接 http www zhihu com question 22842123 answer 55675046 来源 知乎 著作权归作者所有 转载请联系作者获得授权 对于多种类型的apk包构建 通常有以下需求 各
  • 思维导图软件 XMind 2022

    XMind是一个高级且易于使用的思维导图工具 它为您的想法提供了可视化支持 因此您可以更轻松地组织任务或与团队一起参加头脑风暴会议 使用这个易于使用的应用程序创建头脑风暴会议的思维导图 该应用程序包装在具有直观选项的用户友好界面中 XMin
  • 在自动驾驶的领域里面如何玩仿真 【1】 概述和写在前面的一些话

    在自动驾驶的领域里面如何玩sil仿真 1 写在前面的一些话 停了两年 很久没登录CSDN了 之前野心勃勃开的题目随着自己进入一家初创公司开始后就没有时间和精力去更新了 发现这两三年有很多人喜欢这几个都不算太监 甚至头都没有写完 的文章 不少
  • UI设计新手指南之什么是UI设计?

    最直观的 只要用手机 打开界面就会接触到 UI 设计 UI 设计是什么 它是指对软件人机交互 操作逻辑 界面美观的整体设计 UI设计的目的是创造出简单 直观 易于使用的界面 为用户提供良好的体验 随着移动互联网的兴起 UI设计越来越受到重视
  • SPI总线verilog hdl实现(1)SPI通信

    SPI总线传输只需要4根线就能完成 这四根线的作用分别如下 SCK Serial Clock SCK是串行时钟线 作用是Master向Slave传输时钟信号 控制数据交换的时机和速率 MOSI Master Out Slave in 在SP