[米联客-安路飞龙DR1-FPSOC] FPGA基础篇连载-15 SPI接收程序设计

news/2024/11/15 16:01:07/文章来源:https://www.cnblogs.com/milianke/p/18330296

软件版本:Anlogic -TD5.9.1-DR1_ES1.1

操作系统:WIN10 64bit

硬件平台:适用安路(Anlogic)FPGA

实验平台:米联客-MLK-L1-CZ06-DR1M90G开发板

板卡获取平台:https://milianke.tmall.com/

登录"米联客"FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!

1概述

SPI的接收器驱动程序主要为SPI_CLKSPI_RX接收数据总线的时序来设计。通过前面的SPI协议学习,我们这里设计的SPI驱动程序需要支持CPHA=0 CPOL=0;CPHA=1 CPOL=0; CPHA=0 CPOL=1; CPHA=1 CPOL=1四种情况。CPHA用于控制SPI接收器的采样时钟位置,CPOL用于设置SPI_CLK的初始电平是高电平还是低电平。

2程序设计

2.1 SPI SLAVE接收驱动器设计

SPI 接收驱动程序包含去毛刺采集、spi_cap stroble模块、bits counter计数器、串并移位模块。

去毛刺:

信号在FPGA内通过连线和逻辑单元时,都会产生延时。延时产生的原因:连线的长短和逻辑单元的数目;受器件的制造工艺、工作电压、温度等条件的影响,所以在信号变化的瞬间,组合逻辑的输出有先后顺序,信号到达端口的时间不一样这种状况成为"竞争",一般在电气特性上表现为高频率的尖脉冲信号,这些信号称为毛刺。然而异步电路没办法做到真正意义上的毛刺消除,只能通过寄存器延迟转成同步电路才能处理毛刺问题。

SPI的时钟以及选通总线进行采样是异步采样,我们采用多次寄存的方法消除亚稳态

//I_spi_clk去毛刺

always @(posedge I_clk ornegedge I_rstn)begin                       //SPI时钟信号,进行异步转同步处理

   if(I_rstn == 1'b0)

      spi_clk_r <= 4'd0;

   else

      spi_clk_r <= {spi_clk_r[2:0],I_spi_clk};

end

//I_spi_ss去毛刺

always @(posedge I_clk ornegedge I_rstn)begin

   if(I_rstn == 1'b0)

      spi_ss_r <= 4'd0;

   else

      spi_ss_r <= {spi_ss_r[2:0],I_spi_ss};                     //I_spi_ss接收到的数据进行缓存

end

SPI-CAP模块:

CHPACPOL控制spi_cap,根据CHPACPOL设置决定是时钟的上升沿,下降沿亦或者第一个时钟,或者第二个时钟采样。

assign spi_clkp   = spi_clk_r[3:2]==2'b01;                              //SPI时钟信号上升沿

assign spi_clkn   = spi_clk_r[3:2]==2'b10;                              //SPI时钟信号下降沿

 

//CPOL用于控制第一时钟样本或第二时钟样本

//capture stroble 设置

always @(*)begin

      if(CPHA)begin

         if(CPOL) spi_cap = spi_clkp;//CPHA=1  CPOL=1

         else  spi_cap = spi_clkn;   //CPHA=1  CPOL=0

      end

      elsebegin

         if(CPOL) spi_cap = spi_clkn;//CPHA=0  CPOL=1

         else  spi_cap = spi_clkp;   //CPHA=0  CPOL=0    

      end

end

bits counter

Bit Counter计数器用于计数了多少bits的采样,对于SPI接收程序,我们增加了对任意单次传输长度的计算,可以支持不仅仅是8bit单字节的传输。

//spi bit counter

always @(posedge I_clk)begin

    if(spi_rx_en&&spi_cap&&(spi_bit_cnt < BITS_LEN))       //计数到未到达参数BITS_LEN设定值

       spi_bit_cnt <= spi_bit_cnt + 1'b1;                    //spi_bit_cnt计数器+1

    elseif(spi_rx_en==0||spi_bit_cnt == BITS_LEN)          //单次传输的长度由参数BITS_LEN来控制

       spi_bit_cnt <= 0;                                        //计数到达设定值,计数清零

end          

移位模块:

SPI接收移位模块,在每一个spi cap 有效的时候完成一次数据采样。这里并没有对spi的接收总线进行去除亚稳态处理,因为我们SPI采集可以通过CPHA CPOL的控制确保采样时刻总线数据必然是稳定的。

//spi bit shift

always @(posedge I_clk)begin

     if(spi_rx_en&&spi_cap)                                         //spi_cap信号有效时,进行数据采样

        spi_rx_r1 <= {spi_rx_r1[BITS_LEN-2:0],I_spi_rx};         //采样的数据进行移位,准备进行下次采样  

     elseif(spi_rx_en == 1'b0)                                    //spi_rx_en拉低,采样结束

        spi_rx_r1 <= 0;                                             //spi_rx_r1清零

end

2.2 程序源码

`timescale 1ns / 1ns//仿真时间刻度/精度

 

moduleuispi_rx#

(

parameter BITS_LEN = 8,

parameter CPOL = 1'b0,

parameter CPHA = 1'b0

)

(

input        I_clk,//系统时钟输入

input        I_rstn,//系统复位输入

input        I_spi_clk,//SPI时钟输入

input        I_spi_rx,//SPI rx数据输入

input        I_spi_ss,//SPI片选信号

output       O_spi_rvalid, //SPI rx 接收数据有效信号,当为1的时候spi_rdata数据有效

output [BITS_LEN-1'b1:0] O_spi_rdata//SPI rx接收到的数据输出

 );

 

reg  spi_cap   = 1'b0;

reg  [3:0]spi_clk_r = 4'd0;

reg  [4:0] spi_bit_cnt = 5'd0;

reg  [BITS_LEN-1'b1:0] spi_rx_r1;

reg  [3:0]spi_ss_r=4'd0;

 

wire spi_rx_en ;

wire spi_clkp ;

wire spi_clkn ;

 

assign O_spi_rdata  = spi_rx_r1;

assign O_spi_rvalid = (spi_bit_cnt == BITS_LEN);

 

assign spi_clkp   = spi_clk_r[3:2]==2'b01;                              //SPI时钟信号上升沿

assign spi_clkn   = spi_clk_r[3:2]==2'b10;                              //SPI时钟信号下降沿

 

assign spi_rx_en  = (~spi_ss_r[3]);                          //I_spi_ss片选信号持续拉低,使能拉高,接收启动

 

always @(posedge I_clk ornegedge I_rstn)begin                       //SPI时钟信号,进行异步转同步处理

   if(I_rstn == 1'b0)

      spi_clk_r <= 4'd0;

   else

      spi_clk_r <= {spi_clk_r[2:0],I_spi_clk};

end

 

always @(posedge I_clk ornegedge I_rstn)begin

   if(I_rstn == 1'b0)

      spi_ss_r <= 4'd0;

   else

      spi_ss_r <= {spi_ss_r[2:0],I_spi_ss};                               //I_spi_ss接收到的数据进行缓存

end

//当总线空闲时,当CPHA1时,SCL1;当CPHA=0时,则SCL=0

//CPOL用于控制第一时钟样本或第二时钟样本

//capture stroble 设置

always @(*)begin

      if(CPHA)begin

         if(CPOL) spi_cap = spi_clkp;//CPHA=1  CPOL=1

         else  spi_cap = spi_clkn;   //CPHA=1  CPOL=0

      end

      elsebegin

         if(CPOL) spi_cap = spi_clkn;//CPHA=0  CPOL=1

         else  spi_cap = spi_clkp;   //CPHA=0  CPOL=0    

      end

end

 

//spi bit counter

always @(posedge I_clk)begin

    if(spi_rx_en&&spi_cap&&(spi_bit_cnt < BITS_LEN))       //计数到未到达参数BITS_LEN设定值

       spi_bit_cnt <= spi_bit_cnt + 1'b1;                    //spi_bit_cnt计数器+1

    elseif(spi_rx_en==0||spi_bit_cnt == BITS_LEN)          //单次传输的长度由参数BITS_LEN来控制

       spi_bit_cnt <= 0;                                        //计数到达设定值,计数清零

end          

 

//spi bit shift

always @(posedge I_clk)begin

     if(spi_rx_en&&spi_cap)                                         //spi_cap信号有效时,进行数据采样

        spi_rx_r1 <= {spi_rx_r1[BITS_LEN-2:0],I_spi_rx};         //采样的数据进行移位,准备进行下次采样  

     elseif(spi_rx_en == 1'b0)                                    //spi_rx_en拉低,采样结束

        spi_rx_r1 <= 0;                                             //spi_rx_r1清零

end

endmodule

4 RTL仿真

4.1仿真激励文件

Modelsim仿真的创建过程不再重复,如有不清楚的请看前面实验

 

本实验以仿真的方式演示,仿真激励信号提供一个系统时钟即可

`timescale 1ns / 1ps

 

modulemaster_spi_tb;

 

localparam  BYTES = 8;

localparam  TCNT  = BYTES*8*2-1;

 

localparam  CPOL = 1;

localparam  CPHA = 1;

 

reg I_clk; //系统时钟

reg [7:0] i;//计数器,用于产生SPI时钟数量

reg I_rstn; //系统复位

reg I_spi_clk;//SPI时钟

reg I_spi_ss; //SPISlave选通信号

reg [3:0]bit_cnt; //bit计数器

reg [7:0]spi_tx_buf; //发送缓存(移位寄存器)

reg [7:0]spi_tx_buf_r; //发送化缓存,用于产生测试数据

reg first_data_flag; //是否一个时钟改变数据

 

wire O_spi_rvalid; //SPI 数据接收有效,当该信号有效代表接收到一个有效数据

wire [7:0]O_spi_rdata; //SPI读数据

wire I_spi_rx;//SPI数据总线

 

//tb模拟的SPI测试数据接到I_spi_rx

assign I_spi_rx = spi_tx_buf[7];

 

//例化SPI 接收模块

uispi_rx#

(

.BITS_LEN(8),

.CPOL(CPOL),

.CPHA(CPHA)

)

I_spi_rxnst(

.I_clk(I_clk),

.I_rstn(I_rstn),

.I_spi_clk(I_spi_clk),

.I_spi_rx(I_spi_rx),

.I_spi_ss(I_spi_ss),

.O_spi_rvalid(O_spi_rvalid),

.O_spi_rdata(O_spi_rdata)

);

 

initialbegin

    I_clk  = 1'b0;

    I_rstn = 1'b0;

    #100;

    I_rstn = 1'b1;

end

 

always#10   I_clk  = ~I_clk;   //时钟信号翻转,产生系统时钟

 

initialbegin

    #100;

    i = 0;

     

    foreverbegin

        I_spi_clk = CPOL; //设置时钟极性

        I_spi_ss  = 1; // 设置SPISS控制信号

        #2000;

        I_spi_ss  = 0;

        for(i=0;i<TCNT;i=i+1) #1000 I_spi_clk = ~ I_spi_clk; //产生SPI时钟

        #2000;

        I_spi_ss  = 1;

 

    end

end

 

initialbegin

        #100;

        bit_cnt = 0;

        first_data_flag =0;

        spi_tx_buf[7:0] = 8'ha0;

        spi_tx_buf_r[7:0] = 8'ha0;

    foreverbegin

//spi ss 控件用于启用传输

        wait(I_spi_ss);//spi ss

        bit_cnt = 0;

        spi_tx_buf[7:0] = 8'ha0;

        spi_tx_buf_r[7:0] = 8'ha0;

 

        if((CPHA == 1 && CPOL ==0)||(CPHA == 1 && CPOL ==1))//第一个时钟沿改变数据的情况

            first_data_flag = 1; //设置first_data_flag=1 下面的发送时序对应情况跳过第一个沿

 

//ss低时开始数据传输          

        wait(!I_spi_ss);

 

        while(!I_spi_ss)begin

 

//COPL=0 CPHA=0默认SCLK为低电平,对于发送方,在对于第1bit数据提前放到总线

            if(CPHA == 0 && CPOL ==0)begin

             @(negedge I_spi_clk)  begin//每个时钟的下降沿更新需要发送的BIT

                if(bit_cnt == 7)begin//连续发送过程中,8bits 发送完毕后更新数据

                    bit_cnt = 0;

                    spi_tx_buf_r = spi_tx_buf_r + 1'b1;//产生新的测试数据

                    spi_tx_buf = spi_tx_buf_r;//重新跟新发送寄存器

                end

                elsebegin

                    spi_tx_buf = {spi_tx_buf[6:0],1'b0};//数据移位,更新数据

                    bit_cnt = bit_cnt + 1'b1;//SPI发送位数计数器

                end

             end

            end

 

//CPHA=0 COPL=1 默认SCLK为高电平,对于发送方,在对于第1bit数据提前放到总线

            if(CPHA == 0 && CPOL ==1)begin

             @(posedge I_spi_clk)  begin//每个时钟的上升沿更新需要发送的BIT

                if(bit_cnt == 7)begin//连续发送过程中,8bits 发送完毕后更新数据

                    bit_cnt = 0;

                    spi_tx_buf_r = spi_tx_buf_r + 1'b1;//产生新的测试数据

                    spi_tx_buf = spi_tx_buf_r; //重新跟新发送寄存器

                end

                elsebegin

                    spi_tx_buf = {spi_tx_buf[6:0],1'b0};//数据移位,更新数据

                    bit_cnt = bit_cnt + 1'b1;//SPI发送位数计数器

                end

             end

            end

 

//CPHA=1 COPL=0 默认SCLK为低电平,对于发送方,在第1SCLK的跳变沿更新

            if(CPHA == 1 && CPOL ==0)begin

             @(posedge I_spi_clk)  begin

                if(first_data_flag == 1'b1)begin//第一个时钟沿,由于前面已经提前初始化第一个需要发送的数据,因此,这里跳过第一个跳变沿沿

                    first_data_flag = 0;

                    //spi_tx_buf[7:0] = 8'ha0;//也可以在第一个跳变沿初始化第一个发送的数据

                end

                elsebegin

                    if(bit_cnt == 7)begin

                        bit_cnt = 0;

                        spi_tx_buf_r = spi_tx_buf_r + 1'b1;//产生新的测试数据

                        spi_tx_buf = spi_tx_buf_r;//重新跟新发送寄存器

                    end

                    elsebegin

                        spi_tx_buf = {spi_tx_buf[6:0],1'b0}; //数据移位,更新数据

                        bit_cnt = bit_cnt + 1'b1;//SPI发送位数计数器

                    end

                end

             end

            end

 

//CPHA=1 COPL=1 默认SCLK为高电平,对于发送方,在第1SCLK的跳变沿更新

            if(CPHA == 1 && CPOL ==1)begin

             @(negedge I_spi_clk)  begin

                if(first_data_flag == 1'b1)begin//第一个时钟沿,由于前面已经提前初始化第一个需要发送的数据,因此,这里跳过第一个跳变沿沿

                    first_data_flag = 0;

                    //spi_tx_buf[7:0] = 8'ha0;//也可以在第一个跳变沿初始化第一个发送的数据

                end

                elsebegin

                    if(bit_cnt == 7)begin

                        bit_cnt = 0;

                        spi_tx_buf_r = spi_tx_buf_r + 1'b1;//产生新的测试数据

                        spi_tx_buf = spi_tx_buf_r;//重新跟新发送寄存器

                    end

                    elsebegin

                        spi_tx_buf = {spi_tx_buf[6:0],1'b0};//数据移位,更新数据

                        bit_cnt = bit_cnt + 1'b1;//SPI发送位数计数器

                    end

                end

             end

            end

 

        end

    end

end

 

endmodule

以下启动modelsim仿真

4.2 SPI接收驱动代码仿真CPHA=0 CPOL=0

如下图所示,当CPHA=0 CPOL=0,代表SPISCLK默认是低电平,SPI接收器在SCLK1个时钟沿采样。SPI发送驱动器数据在SCLK的第2个时钟沿更新,确保SPI下一个SCLK的第1个时钟沿数据有足够的建立和保持时间。下图以发送8'ha0为例。

4.3 SPI发送驱动代码仿真CPHA=1 CPOL=0

如下图所示,当CPHA=1 CPOL=0,代表SPISCLK默认是低电平,SPI接收器在SCLK2个时钟沿采样。SPI发送驱动器数据在下一个SCLK的第1个时钟沿更新,确保SPI下一个SCLK的第2个时钟沿数据有足够的建立和保持时间。下图以发送8'h02为例。

4.4 SPI接收驱动代码仿真CPHA=0 CPOL=1

CPHA=0 CPOL=0这种设置相比,时钟SCLK取反

4.5 SPI接收驱动代码仿真CPHA=1 CPOL=1

CPHA=1 CPOL=0这种设置相比,时钟SCLK取反

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/773646.html

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

相关文章

自写Json转换工具

前面写了简单的API测试工具ApiTools,返回的json有时需要做很多转换,于是开发了这个工具。 功能包括 1、json字符串转为表格,可以直观的展示,也可以复制,并支持转换后的表格点击列头进行排序,比较方便地定位数据。2、表格转为EXCEL,就是导出Excel文件,支持2003和2007格式…

[米联客-安路飞龙DR1-FPSOC] FPGA基础篇连载-13 SPI通信协议原理

SPI(串行外围设备接口)通信协议是一种高速、全双工、同步通信总线。它通过四根管脚实现控制和数据传输,广泛应用于EEPROM、Flash、RTC、ADC、DSP等设备,是一种重要的低速通讯协议。SPI协议具有高速、全双工、同步、主从式等特点,接口速度最高可达上百兆,因此不仅适用于低…

DiffSeq

目录概 符号说明 流程 代码 Gong S., Li M., Feng J., Wu Z. and Kong L. DiffuSeq: Sequence to sequence text generation with diffusion models. In International Conference on Learning Representations (ICLR), 2023概 本文提出了一种用于 Seq2Seq 的不需要 classifier…

嵌入式测试手册——基于NXP iMX6ULL开发板(2)

基于测试板卡:创龙科技TLIMX6U-EVM是一款基于NXP i.MX 6ULL的ARM Cortex-A7高性能低功耗处理器设计的评估板,由核心板和评估底板组成。核心板经过专业的PCB Layout和高低温测试验证,稳定可靠,可满足各种工业应用环境。评估板接口资源丰富,引出双路网口、双路RS485、双路CA…

广东注塑行业MES系统——优化生产管理的利器

注塑行业MES系统是优化生产管理的利器,具有以下优势和作用: 实现生产过程的数字化管理:注塑行业MES系统可以实现生产过程的数字化管理,实时监控生产数据、设备状态和生产进度,提高生产过程的可视化和透明度。提高生产效率:通过优化生产计划、资源调度和工艺流程,注塑MES…

嵌入式基础测试手册——基于NXP iMX6ULL开发板(4)

前 言 本文档适用开发环境: Windows开发环境:Windows 7 64bit、Windows 10 64bit 虚拟机:VMware15.1.0 Linux开发环境:Ubuntu18.04.4 64bit U-Boot:U-Boot-2020.04 Kernel:Linux-5.4.70 Linux SDK:5.4.70_2.3.0 基于测试板卡:创龙科技TLIMX6U-EVM是一款基于NXP i.MX 6…

Markdown 常用语法

上一篇简单介绍了 Markdown 是什么,以及如何使用,本篇就来详细说说 Markdown 的常见语法。上一篇简单介绍了 Markdown 是什么,以及如何使用,本篇就来详细说说 Markdown 的常见语法。 ‍ 注意,并不是所有编辑器,都支持所有的 Markdown 语法,如果遇到失效的也不必疑惑。 此…

软件无线电系统基带信号处理卡:612-基于6UVPX C6678+XCVU9P的4路2Gsps AD 8路2Gsps DA 信号处理板卡

基于6UVPX C6678+XCVU9P的4路2Gsps AD 8路2Gsps DA 信号处理板卡 一、板卡概述板卡基于6U VPX标准结构,包含一个C6678 DSP芯片,一个XCVU9P 高性能FPGA,8路DA ,4路AD。 二、技术指标● DSP处理器采用TI 8核处理器TMS320C6678; ● DSP 外挂一组64bit DDR3颗粒,总容量2G…

AM62x相比AM335x,到底升级了什么?

Sitara作为TI处理器经典系列,曾推出众多优秀处理器型号(如AM335x)。因其能在相同价位下,提供比市面上其他厂商处理器更优良的性能,并凭借GPMC高速并口、PRU协处理器等个性化硬件资源,可提供更便利的拓展性,在工业控制、能源电力、轨道交通、智慧医疗等领域广受用户欢迎。…

NXP IMX8M Plus工业核心板规格书

核心板简介 创龙科技SOM-TLIMX8MP是一款基于NXP i.MX 8M Plus的四核ARM Cortex-A53 + 单核ARM Cortex-M7异构多核处理器设计的高端工业核心板,ARM Cortex-A53(64-bit)主处理单元主频高达1.6GHz,ARM Cortex-M7实时处理单元主频高达800MHz。 处理器采用14nm最新工艺,内置2.3TO…

数据驱动测试-多组数据测试

背景:测试多组数据 解决方法:使用文件写入参数化具体操作具体设置