基于AM335X与FPGA的SPI通讯设计

在2013年的工作中,涉及到了AM3359与XC7K325T之间的相互通信,其目的是为了获取FPGA设计版本号,该FPGA版本号保存在FPGA的寄存器0xFFFF中,FPGA的版本值随着加载程序发生变化,当时的版本信息为0x1003.

需要说明的是,在本文中的代码风格是刚工作两年的时候的代码风格,现在回看,这些代码风格实在难以阅读。尤其是SPI的verilog程序等。并不代表现在的编程水平与代码风格。


设计框图如下:

基于AM335X与FPGA的SPI通讯设计_第1张图片

本文主要从三个方面介绍AM3359与FPGA的通讯

第一部分:SPI通信的基础定义

第二部分:AM335x的SPI通信编程

第三部分:FPGA从机SPI设计


SPI通信基础定义

通信是怎么发生

SPI接口是一种典型的全双工接口。通过同步时钟SCLK的脉冲将数据一位一位地在主机和从机之间交换。所以在开始通信之前,Master首先需要配置接口时钟,在Master配置时钟的时候,不需要通知从机它所配置的时钟频率具体是多少,设计人员只需要确保通讯频率是从机所支持的即可。


当Master通过片选信号(SS,低电平有效)选定一个Slave的时候,每向Slave发送一个周期的SCLK信号,都会有1bit的数据从MOSI引脚发送给Slave,Slave只需要在对应的引脚接收数据即可;同时,slave每收到一个周期的SCLK信号,都会从MISO想Master发送1bit的数据。


基于AM335X与FPGA的SPI通讯设计_第2张图片


从上段描述中可以分析出一下两点:

1.无论Master还是Slave,都是按照bit来传输的,那么对于需要发送或者接收的数据,必须在Master或Slave中有一个移位寄存器,这些是由硬件来保证的,普通的SPI接口设计者不需要考虑移位寄存器的因素。

2.在通信中,Master发送数据后,一般需要保证Slave收到数据,这样才能确定数据在收发的过程中不发生因为硬件而导致的bit丢失。在SPI中,数据传输以“位交换”的方式传输,这样能根据从机返回的数据来确定从机已经收到数据了。SPI同样与其他基础通信方式(USB,I2C,UART等)一样无法确保传输数据的正确性。


SPI是一个相对比较开放的接口,具体表现在时钟极性/相位、帧大小、传输速度、LSB/MSB等规则没有一个确定的定义,需要根据不同的通信环境由设计开发者进行定义。


SPI的接口时序

在实际开发使用SPI的时候,需要注意使Master和Slave处于相同的Mode下工作。不同mode的定义主要是针对时钟的相关特性。

SCLK极性(CPOL):clock Polarity

SCLK相位(CPHA):clock Phase

基于AM335X与FPGA的SPI通讯设计_第3张图片


CPOL

在解释CPOL之前先要介绍什么是SCLK的空闲时刻。在SPI通讯传输的时候,SCLK并不是时刻都有。在SCLK发送数据之前和发送数据之后,都会回到空闲状态,这个状态下,SCLK要么保持在高电平,要么保持在低电平。这个是需要设计者来指定的,CPOL的作用就是来指定SPI在IDLE状态下的点评状态。

  • CPOL = 0 :时钟空闲状态(IDLE)的电平为低电平(0)。
  • CPOL = 1 :时钟空闲状态(IDLE)的点评是高电平(1)。


CPHA

CPHA表示数据采样,数据有效的时刻。对应的数据采样是在第几个边沿进行采样。

  • CPHA = 0 :在时钟第一个边沿采样。
    1. 对于CPOL = 0 :因为IDLE为低电平,那么第一个边沿就是从低电平到高电平,即为上升沿
    2. 对于CPOL = 1 :因为IDLE为高电平,那么第一个边沿就是从高电平到低电平,即为下降沿。

  • CPHA = 1 :在时钟第二个边沿采样。
    1. 对于CPOL = 0 :因为IDLE为低电平,那么第二个边沿就是从高电平到低电平,即为下降沿。
    2. 对于CPOL = 1 :因为IDLE为高电平,那么第二个边沿就是从低电平到高电平,即为上升沿。

基于AM335X与FPGA的SPI通讯设计_第4张图片

需要注意的是:采样一定是需要先准备好数据,才用时钟的有效沿将数据打到对应的引脚上。


Mode选择参考

SPI没有一个通用的推荐模式,但是基于工程设计的时候是否有一个推荐的Mode选择呢?在CSDN博友的一篇《SPI接口扫盲 SPI定义/SPI时序(CPHA CPOL)》中,作者从功耗的角度分析,建议应该多选择SCLK在空闲状态下处于低电平,即CPOL保持在IDLE状态下为0。这是一个很好的分析方法。对于CPHA的选择分析,我更赞成根据实际的应用来做设计,而不是根据习惯来设计。


AM335x的SPI通信编程

在本工程中所使用的SPI为AM335x的SPI外设,即在Linux下,只需要对spidev进行文件操作。所用到的文件操作函数有以下四个:

open()

write()

read()

close()

相关的函数说明,请参考网络,在此不赘述。

在编写SPI驱动的时候,还需用到ioctl()函数,下面对ioctl做一些简要介绍。


什么是ioctl

ioctl是设备驱动程序中对设备的IO通道进行管理的函数。即是对设备的一些特性进行控制,例如对串口设备设置波特率,对SPI设备设置字长,通讯速率等。函数的调用方式如下:


int ioctl(int fd,unsigned long cmd,...);
/*
fd:文件描述符
cmd:控制命令
...:可选参数:插入*argp,具体内容依赖于cmd
*/


其中fd是用户程序打开设备是使用open()函数返回的文件标识符(句柄),cmd是用户程序对设备的控制命令,后面的省略号,表示该函数可能还有其他参数,该参数与cmd相关。


对于ioctl的详细描述,可以参考文末链接1来详细阅读。


AM335x驱动程序源码设计


static uint8_t  mode  = 0;
static uint8_t  bits  = 8;
static uint32_t speed = 16000000;
static uint8_t  cs    = 1;
	
int fpga_fd = 0;
uint8_t  file_name_buf[FILE_NAME_MAX] = "/dev/spidev2.1";
//==
int fpga_config(uint8_t  *file_name)
{
    if (strlen(file_name) > FILE_NAME_MAX)
    {
        printf("File name length error\r\n");
        return 0;
    }
    strcpy(file_name_buf, file_name);
    return 1;
}
 
int fpga_spi_open(uint8_t  *file_name)
{
    int ret = 0;
    fpga_fd = open(file_name, O_RDWR);
    if (fpga_fd < 0)
    {
        printf("in fpga_spi_open, can't open device\r\n");
				return nQAM_ERROR_CAS_SPI_OPEN;
    }
    else
    {
    	printf("in fpga_spi_open, open device success\r\n");
    }
    /*
     * spi mode
     */
    ret = ioctl(fpga_fd, SPI_IOC_WR_MODE, &mode);
    if (ret == -1)
    {
			printf("in fpga_spi_open, can't set spi mode\r\n");
			return nQAM_ERROR_CAS_SPI_CONFIG;
    }
    else
    {
    	printf("in fpga_spi_open, set spi mode success\r\n");
    }
    /*
     * bits per word
     */
    ret = ioctl(fpga_fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    if (ret == -1)
    {
      printf("in fpga_spi_open, can't set bits per word\r\n");
			return nQAM_ERROR_CAS_SPI_CONFIG;
    }
    else
    {
    	printf("in fpga_spi_open, set bits per word success\r\n");
    }
    /*
     * max speed hz
     */
    ret = ioctl(fpga_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
    if (ret == -1)
    {
			printf("in fpga_spi_open, can't set max speed hz\r\n");
			return nQAM_ERROR_CAS_SPI_CONFIG;
    }
    else
    {
    	printf("in fpga_spi_open, set max speed success\r\n");
    }
    /*
     * chip select
     */	
    ret = ioctl(fpga_fd,SPI_IOC_WR_CHIP_SELECT,&cs);
    if(ret == -1)
    {
	printf("in fpga_spi_configure,can't set chip  select\r\n");
	return nQAM_ERROR_CAS_SPI_CONFIG;
    }	
    return nQAM_ERROR_NOERROR;
}

int fpga_spi_close()
{
    return close(fpga_fd);	// close device
}
//数据交换 
uint8_t transfer(uint8_t data)
{
    uint8_t sbuf = data;
    if(write(fpga_fd,&sbuf,1) != -1)
    {
        if(read(fpga_fd, &sbuf, 1) != -1)
 				{
 	    		printf("In transfer, transfer data over\r\n");
 	    		return sbuf;
 				}
 				else
 				{
 	    		printf("In transfer, read data from spi device failed!\r\n");
        }
    }
    else
    {
 			printf("In transfer, write data to spi device failed!\r\n");
    }
    return 0;
}
//通过SPI获取版本号 
uint8_t get_fpga_version()
{
    uint8_t buf;
    if(fpga_spi_open(file_name_buf) != nQAM_ERROR_NOERROR)
    {
    	printf("in get_fpga_vesion, fpga spi open failed\r\n");
        return 0;
    }
    buf = transfer(READ_VESION);
    fpga_spi_close();
    printf("Read version done.\r\n");
    return buf;
}
//测试物理连接状态 
uint8_t test_fpga_connect()
{
    uint8_t buf;
    if(fpga_spi_open(file_name_buf) != nQAM_ERROR_NOERROR)
    {
    	printf("in test_spi_open, fpga spi open failed\r\n");
 			return 0;
    }
    buf = transfer(TEST_CONNECT);
    fpga_spi_close();
    printf("FPGA spi test connect done.\r\n");
    return buf;
}

附:spidev的ioctl命令。


SPI_IOC_RD_MODE:               读取spi_device的mode。
SPI_IOC_RD_LSB_FIRST:            如果是SPI_LSB_FIRST的方式则返回1。
SPI_IOC_RD_BITS_PER_WORD:         读取spi_device的bits_per_word.
SPI_IOC_RD_MAX_SPEED_HZ:          读取spi_device的max_speed_hz.
SPI_IOC_WR_MODE:               设置spi_device的mode,并调用spi_setup立即使设置生效。
SPI_IOC_WR_LSB_FIRST:            设置spi使用SPI_LSB_FIRST的传输模式。立即生效。
SPI_IOC_WR_BITS_PER_WORD:         读取字长。
SPI_IOC_WR_MAX_SPEED_HZ:          设置时钟速率。


AM335x应用程序设计

int main(int argc, char *argv )
{
    uint8_t connect;
    uint8_t version;
    connect = test_fpga_connect();
    printf("Test Value is %02X\r\n", connect); //测试连接状态
    
    version = get_fpga_version();
    printf("the fpga version is %02X\r\n", version);
    
    printf("****fpga app test run over!****\r\n");	
    return 1;		
}



FPGA从机SPI设计


SPI从机的Verilog实现

module spi_slave(clk,rst,data_i,wr_en_i,data_o,tx_valid,valid_o,start_o,end_o,we_ack_o,ss_i,sclk_i,mosi_i,miso_o
    );
   
  input                 clk;          // master clock input
  input                 rst;          // synchronous active high reset
  input   [7:0]         data_i;       // data bus input
  input                 wr_en_i;      // write enable input
  output  [7:0]         data_o;       // data bus output
  
  output                valid_o;      // request signal output
  output                tx_valid;
  output                start_o;
  output                end_o;
  output  reg           we_ack_o;
  
  //spi signals
  input                 ss_i;         // slave select
  input                 sclk_i;       // serial clock
  input                 mosi_i;
  output                miso_o;
  
  reg     [7:0]         tx_data;
  reg     [7:0]         rx_data; 
  
  reg                   tx_tip;       //tx in progress
  reg                   rx_tip;       //rx in progress
  
  wire                  rx_negedge;   //miso is sampled on negative edge
  wire                  tx_negedge;   //mosi is driven on nesedge edge
  wire    [2:0]         len;          //char length
  wire                  lsb;          //lsb first on line
  
  wire                  pos_edge;     //recognize posedge of sclk
  wire                  neg_edge;     //recognize negdege of sclk
  
  reg                   s_out;
  reg                   s_in;
  reg     [2:0]         s_sel;
  reg     [2:0]         s_clk;
  
  assign    rx_negedge  = 0;            // decided by CPOL and CPHA
  assign    tx_negedge  = 1;            // means mode == 00
  assign    len         = 7;
  assign    lsb         = 0;
  
  assign    miso_o      = s_out;
  assign    valid_o     = rst?  1'b0  : (!rx_tip);
  assign    data_o      = rst?  8'h00 : rx_data;
  
  //sync SCK to the FPGA clock using a 3-bits shift register
  always @ (posedge clk)
  begin 
    s_clk <= {s_clk[1:0], sclk_i};  //sample the sclk_i using clk,when finding the first posedge the value is 001,when finding the first negedge the value is 110 or 010
  end
  
  assign pos_edge       =   (s_clk[1:0] == 2'b01);      // posedge when s_clk[1:0] == 2'b01 or s_clk[2:0] = 3'b001
  assign neg_edge       =   (s_clk[1:0] == 2'b10);      // negedge when s_clk[1:0] == 2'b10 or s_clk[2:0] = 3'b110
  
  //SSEL
  always @ (posedge clk)
  begin
    s_sel <= {s_sel[1:0], ss_i};    //sample the ss signal 
  end
  
  wire                  sel_active;                     // from start_o is high to end_o is high sel_active is active 
  assign sel_active     =   ~s_sel[1];                  // sel[2:0] = 000 001 011 111 110 100 000 when 100 000 001 .0 is high
  assign start_o        =   (s_sel[1:0] == 2'b10);      // start_o when s_sel[1:0] = 2'b10 or s_sel[2:0]= 3'b110 
  assign end_o          =   (s_sel[2:1] == 2'b01);      // end_o when s_sel[2:1] = 2'b01 or s_sel[2:0] = 3'b011
  
  //---------------------receiving bits from line---------------------
  wire                  rx_clk;
  wire                  rx_lst_bit;
  reg   [2:0]           rx_cnt;                         // rx data bit count
  
  assign rx_clk         =   rx_negedge? neg_edge : pos_edge;    // question is the beginning value is "X"
  assign rx_lst_bit     = !(|rx_cnt);
  
  always @(posedge clk)
  begin
    s_in  <=  mosi_i;
  end
  
  always @(posedge clk or posedge rst)
  begin
    if (rst)
      rx_cnt <= len;
    else begin
      if(!rx_tip || end_o || start_o)
        rx_cnt  <=  len;
      else
        rx_cnt  <=  rx_clk ? (rx_cnt - 1'b1) : rx_cnt;        //question is the rx_cnt always is 7?
    end
  end
  
  //Transfer in process
  always @(posedge clk or posedge rst)
  begin
    if(rst)
      rx_tip  <= 1'b0;
    else if(!rx_tip)
      rx_tip  <= 1'b1;
    else if(rx_tip && rx_lst_bit && rx_clk)
      rx_tip  <= 1'b0;
  end
  
  always @(posedge clk or posedge rst)
  begin
    if(rst)
      rx_data <= {8{1'b0}};
    else begin
      if(sel_active && rx_clk)
        rx_data <= lsb ? {s_in,rx_data[7:1]} : {rx_data[6:0],s_in}; // if lsb = 0 rx_data = {rx_data[6:0],s_in}
                                                  // if lsb = 1 rx_data = {s_in,rx_data[7:1]}
    end                                           // {s_in,rx_data[7:1]} shift right
  end                                             // {rx_data[6:0],s_in} shift left
    
  
  
  //---------------------sending bits to line---------------------
  wire                  tx_clk;
  wire                  tx_lsb_bit;
  reg [2:0]             tx_cnt;
  assign tx_clk         =   tx_negedge? neg_edge : pos_edge;        // tx_negedge = 1 negedge transfer data
  assign tx_lsb_bit     = !(|tx_cnt);
  assign tx_valid       = ((s_sel[2:0] == 3'b111) && (tx_tip == 1'b0)) ? 1'b1 : 1'b0;
  
//  always @(posedge clk or posedge rst)
//  begin
//    if(rst)
//      tx_valid  <=  1'b0;
//    else if((s_sel[2:0] == 3'b111) && (tx_tip == 1'b0))
//      tx_valid  <=  1'b1;
//    else
//      tx_valid  <=  1'b0;
//  end
  // character bit counter
  always @(posedge clk or posedge rst)
  begin
    if(rst)
      tx_cnt  <= len;
    else begin
      if(!tx_tip || end_o || start_o)
        tx_cnt  <= len;
      else
        tx_cnt  <= tx_clk? (tx_cnt-1'b1):tx_cnt;
    end
  end
  
  //transfer in process
  always @(posedge clk or posedge rst)
  begin
    if(rst) begin
      tx_tip  <= 1'b0;
    end
    else if(wr_en_i && (!tx_tip)) begin               //wr_en_i is high when transfer the data
      tx_tip  <= 1'b1;
    end
    else if(tx_tip && tx_lsb_bit && tx_clk) begin
      tx_tip  <= 1'b0;
    end
  end
  
  always @(posedge clk or posedge rst)
  begin
    if(rst) begin
      tx_data <= 8'hff;
      we_ack_o  <=  1'b0;
    end
    else begin
      we_ack_o <= 1'b0;
      if(wr_en_i && (!tx_tip)) begin
        tx_data[7:0]  <=  data_i[7:0];
        we_ack_o  <= 1'b1;
      end
      else begin
        if(sel_active && rx_clk) begin
          s_out <=  lsb?  tx_data[0] : tx_data[7];
          tx_data <= lsb? {1'b1,tx_data[7:1]} : {tx_data[6:0],1'b1};
        end
      end
    end
  end     
endmodule

FPGA的SPI command verilog设计

`timescale 1 ns / 1 ps
module spi_cmd #(
  parameter   BUS_DATA_WIDTH            = 8,
  parameter   BUS_ADDR_WIDTH            = 16
)(
  input                                 clk,
  input                                 rst,
  input       [BUS_DATA_WIDTH-1:0]      rx_data,
  input                                 rx_valid,
  input                                 rx_start,
  input                                 rx_end,
  input                                 tx_valid,
  
  input                                 tx_ack,
  output  reg [BUS_DATA_WIDTH-1:0]      tx_data,                      
  output  reg                           tx_req
);

  localparam    CON_WR_REG              = 8'h51;
  localparam    CON_RD_REG              = 8'h52;
  localparam    RD_DATA_REG             = 8'h96;
  localparam    RST_CMD                 = 8'h55;
  
  localparam    ADDR_FPGA_VERSION       = 16'hFFFF;
  localparam    ADDR_SPI_TEST           = 16'hF000;
  localparam    FPGA_VERSION            = 16'h1003;
  
  localparam    REC_NO_ERR              = 8'h90;
  localparam    REC_INS_ERR             = 8'hF1;
  localparam    REC_LEN_ERR             = 8'hF2;
  localparam    REC_BUSY_ERR            = 8'hF3;
  localparam    REC_WR_ERR              = 8'hF4;
  
  localparam    REC_START               = 1;
  localparam    REC_TYPE_REG            = 2;
  localparam    REC_ADDR_REGH           = 3;
  localparam    REC_ADDR_REGL           = 4;
  localparam    REC_ADDR_LEN            = 5;
  localparam    REC_CON_WRH             = 6;
  localparam    REC_CON_WRL             = 7;
  localparam    RSP_STATUS              = 8;
  localparam    RSP_LEN                 = 9;
  localparam    RSP_DIN                 = 10;
  localparam    RSP_DOUT                = 11;
  localparam    READ_DATAH              = 12;
  localparam    READ_DATAL              = 13;
  localparam    READ_DONE               = 14;
  
  reg   [7:0]                           rec_type;
  reg   [15:0]                          rec_addr;
  reg   [7:0]                           addr_len;
  reg   [7:0]                           len_count;
  reg   [3:0]                           cmd_state;
  reg   [7:0]                           err_code;
  reg   [15:0]                          rsp_dout;
  reg                                   rsp_rd; 
  reg   [15:0]                          spi_test;
  reg   [3:0]                           test_flag;
  
  always@(posedge clk or posedge rst)
  begin
    if(rst) begin
      rec_type          <=  0;
      rec_addr          <=  0;
      addr_len          <=  0;
      len_count         <=  0;
      err_code          <=  REC_NO_ERR;
      tx_data           <=  0;
      tx_req            <=  0;
      rsp_rd            <= 1'b0;
      cmd_state         <=  REC_START;
      test_flag         <=  4'h0;
    end
    else begin
      if(tx_ack) begin
        tx_req  <=  1'b0;
      end
      case(cmd_state)
        REC_START : begin
          len_count   <=  0;
          if(rx_start) begin
            cmd_state <=  REC_TYPE_REG;
            test_flag <=  4'h1;
          end
        end
        REC_TYPE_REG : begin
          if(rx_valid) begin
            case(rx_data)
              CON_WR_REG, CON_RD_REG : begin
                err_code    <=  REC_NO_ERR;
                rec_type    <=  rx_data;
                cmd_state   <=  REC_ADDR_REGH;
                test_flag   <=  4'h2;
              end
              RD_DATA_REG : begin
                cmd_state   <=  RSP_STATUS;
                test_flag   <=  4'h6;
              end
              default : cmd_state <=  REC_START;
            endcase
          end
        end

        REC_ADDR_REGH : begin
          if(rx_valid) begin
            rec_addr[15:8]  <=  rx_data;
            cmd_state   <=  REC_ADDR_REGL;
            test_flag   <=  4'h3;
          end
        end
        REC_ADDR_REGL : begin
          if(rx_valid) begin
            rec_addr[7:0] <=  rx_data;
            cmd_state   <=  REC_ADDR_LEN;
            test_flag   <=  4'h4;
          end
        end
        
        REC_ADDR_LEN : begin
          if(rx_valid) begin
            if(rx_data != 0) begin
              addr_len    <=  rx_data;
              test_flag <=  4'h5;
              case(rec_type)
                CON_WR_REG : cmd_state  <=  REC_CON_WRH;
                CON_RD_REG : cmd_state  <=  REC_START;
                default : cmd_state <=  REC_START;
              endcase
            end
            else begin
              err_code  <=  REC_LEN_ERR;
              cmd_state <=  REC_START;
            end
          end
        end
        REC_CON_WRH : begin
          if(len_count == addr_len) begin
            cmd_state <=  REC_START;
          end
          else begin
            if(rx_valid) begin
              if(rec_addr == ADDR_FPGA_VERSION) begin
                err_code  <=  REC_WR_ERR;
                cmd_state <=  REC_START;
              end
              else if(rec_addr == ADDR_SPI_TEST) begin
                spi_test[15:8]  <=  rx_data;
              end
//              else begin
//                rec_buf_data[15:8]  <=   rx_data;
//              end
              cmd_state <=  REC_CON_WRL;
            end
          end
        end       
        REC_CON_WRL : begin
          if(rx_valid) begin
            if(rec_addr == ADDR_SPI_TEST) begin
              spi_test[7:0] <=  rx_data;
            end
//            else begin
//              rec_buf_data[7:0] <=  rx_data;
//              rec_addr  <=  rec_addr + 1'b1;
//            end
            len_count <=  len_count + 1'b1;
          end
        end
        
        RSP_STATUS : begin
          if(tx_valid) begin
            if(tx_ack) begin
              tx_req  <=  1'b0;
            end
            else begin
              tx_req  <=  1'b1;
            end
            rsp_rd    <=  1'b1;
            tx_data   <=  err_code;
            cmd_state <=  RSP_LEN;
            test_flag <=  4'h7;
          end
        end

        RSP_LEN : begin
          if(tx_valid) begin
            if(tx_ack) begin
              tx_req  <=  1'b0;
            end
            else begin
              tx_req  <=  1'b1;
            end
            case(rec_type)
              CON_WR_REG : begin
                tx_data   <=  0;
                cmd_state <=  REC_START;
              end
              CON_RD_REG : begin
                tx_data   <=  addr_len;
                cmd_state <=  RSP_DIN;
              end
              default : begin
                tx_data   <=  0;
                cmd_state <=  REC_START;
              end
            endcase
            test_flag <=  4'h8;
          end
        end
        
        RSP_DIN : begin
          if(tx_valid) begin
            if(tx_ack) begin
              tx_req  <=  1'b0;
            end
            else begin
              tx_req  <=  1'b1;
            end
            if(len_count == addr_len) begin
              cmd_state <=  READ_DONE;
            end
            else begin
              cmd_state <=  RSP_DOUT;
              rsp_rd    <=  1'b0;
            end
            test_flag <=  4'h9;
          end
        end
        
        RSP_DOUT : begin
          if(tx_valid) begin
            if(tx_ack) begin
              tx_req  <=  1'b0;
            end
            else begin
              tx_req  <=  1'b1;
            end
            cmd_state <=  READ_DATAH;
          end
        end
              
        READ_DATAH : begin
          if(tx_valid) begin
            if(tx_ack) begin
              tx_req  <=  1'b0;
            end
            else begin
              tx_req  <=  1'b1;
            end
            tx_data   <=  rsp_dout[15:8];
            cmd_state <=  READ_DATAL;
            test_flag <=  4'hA;
          end
        end
        READ_DATAL : begin
        if(tx_valid) begin
          if(tx_ack) begin
            tx_req  <=  1'b0;
          end
          else begin
            tx_req  <=  1'b1;
          end
          tx_data   <=  rsp_dout[7:0];
          len_count <=  len_count + 1'b1;
          cmd_state <=  RSP_DIN;
          test_flag <=  4'hB;
          end
        end
        
        READ_DONE : begin
          if(tx_valid) begin
            if(tx_ack) begin
              tx_req  <=  1'b0;
            end
            else begin
              tx_req  <=  1'b1;
            end
            tx_req    <= 1'b0;
            len_count <=  0;
            tx_data   <=  0;
            rec_type    <=  0;
            cmd_state <=  REC_START;
          end
        end
      endcase
    end
  end

always @(posedge clk or posedge rst)
begin
  if(rst == 1'b1)
  begin
    rsp_dout  <=  {16{1'b0}};
  end
  else if((rsp_rd == 1'b1) && (test_flag  ==  4'h7)) begin
    case(rec_addr)
      ADDR_FPGA_VERSION : rsp_dout  <=  FPGA_VERSION;
      ADDR_SPI_TEST :   rsp_dout  <=  ~spi_test;
      default : ;
    endcase
  end
end

endmodule

以上项目总结仅作参考,适用于本公司后继者对此工程的修改参考。


链接1 :点击打开链接

你可能感兴趣的:(Interface)