基于AM335X与FPGA的SPI通讯设计

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

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


设计框图如下:

基于AM335X与FPGA的SPI通讯设计

本文主要从三个方面介绍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通讯设计


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

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通讯设计


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通讯设计

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


Mode选择参考

SPI没有一个通用的推荐模式,但是基于工程设计的时候是否有一个推荐的Mode选择呢?在****博友的一篇《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设备设置字长,通讯速率等。函数的调用方式如下:


[cpp] view plain copy
  1. int ioctl(int fd,unsigned long cmd,...);  
  2. /* 
  3. fd:文件描述符 
  4. cmd:控制命令 
  5. ...:可选参数:插入*argp,具体内容依赖于cmd 
  6. */  


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


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


AM335x驱动程序源码设计


[cpp] view plain copy
  1. static uint8_t  mode  = 0;  
  2. static uint8_t  bits  = 8;  
  3. static uint32_t speed = 16000000;  
  4. static uint8_t  cs    = 1;  
  5.       
  6. int fpga_fd = 0;  
  7. uint8_t  file_name_buf[FILE_NAME_MAX] = "/dev/spidev2.1";  
  8. //==  
  9. int fpga_config(uint8_t  *file_name)  
  10. {  
  11.     if (strlen(file_name) > FILE_NAME_MAX)  
  12.     {  
  13.         printf("File name length error\r\n");  
  14.         return 0;  
  15.     }  
  16.     strcpy(file_name_buf, file_name);  
  17.     return 1;  
  18. }  
  19.    
  20. int fpga_spi_open(uint8_t  *file_name)  
  21. {  
  22.     int ret = 0;  
  23.     fpga_fd = open(file_name, O_RDWR);  
  24.     if (fpga_fd < 0)  
  25.     {  
  26.         printf("in fpga_spi_open, can't open device\r\n");  
  27.                 return nQAM_ERROR_CAS_SPI_OPEN;  
  28.     }  
  29.     else  
  30.     {  
  31.         printf("in fpga_spi_open, open device success\r\n");  
  32.     }  
  33.     /* 
  34.      * spi mode 
  35.      */  
  36.     ret = ioctl(fpga_fd, SPI_IOC_WR_MODE, &mode);  
  37.     if (ret == -1)  
  38.     {  
  39.             printf("in fpga_spi_open, can't set spi mode\r\n");  
  40.             return nQAM_ERROR_CAS_SPI_CONFIG;  
  41.     }  
  42.     else  
  43.     {  
  44.         printf("in fpga_spi_open, set spi mode success\r\n");  
  45.     }  
  46.     /* 
  47.      * bits per word 
  48.      */  
  49.     ret = ioctl(fpga_fd, SPI_IOC_WR_BITS_PER_WORD, &bits);  
  50.     if (ret == -1)  
  51.     {  
  52.       printf("in fpga_spi_open, can't set bits per word\r\n");  
  53.             return nQAM_ERROR_CAS_SPI_CONFIG;  
  54.     }  
  55.     else  
  56.     {  
  57.         printf("in fpga_spi_open, set bits per word success\r\n");  
  58.     }  
  59.     /* 
  60.      * max speed hz 
  61.      */  
  62.     ret = ioctl(fpga_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);  
  63.     if (ret == -1)  
  64.     {  
  65.             printf("in fpga_spi_open, can't set max speed hz\r\n");  
  66.             return nQAM_ERROR_CAS_SPI_CONFIG;  
  67.     }  
  68.     else  
  69.     {  
  70.         printf("in fpga_spi_open, set max speed success\r\n");  
  71.     }  
  72.     /* 
  73.      * chip select 
  74.      */   
  75.     ret = ioctl(fpga_fd,SPI_IOC_WR_CHIP_SELECT,&cs);  
  76.     if(ret == -1)  
  77.     {  
  78.     printf("in fpga_spi_configure,can't set chip  select\r\n");  
  79.     return nQAM_ERROR_CAS_SPI_CONFIG;  
  80.     }     
  81.     return nQAM_ERROR_NOERROR;  
  82. }  
  83.   
  84. int fpga_spi_close()  
  85. {  
  86.     return close(fpga_fd);  // close device  
  87. }  
  88. //数据交换   
  89. uint8_t transfer(uint8_t data)  
  90. {  
  91.     uint8_t sbuf = data;  
  92.     if(write(fpga_fd,&sbuf,1) != -1)  
  93.     {  
  94.         if(read(fpga_fd, &sbuf, 1) != -1)  
  95.                 {  
  96.                 printf("In transfer, transfer data over\r\n");  
  97.                 return sbuf;  
  98.                 }  
  99.                 else  
  100.                 {  
  101.                 printf("In transfer, read data from spi device failed!\r\n");  
  102.         }  
  103.     }  
  104.     else  
  105.     {  
  106.             printf("In transfer, write data to spi device failed!\r\n");  
  107.     }  
  108.     return 0;  
  109. }  
  110. //通过SPI获取版本号   
  111. uint8_t get_fpga_version()  
  112. {  
  113.     uint8_t buf;  
  114.     if(fpga_spi_open(file_name_buf) != nQAM_ERROR_NOERROR)  
  115.     {  
  116.         printf("in get_fpga_vesion, fpga spi open failed\r\n");  
  117.         return 0;  
  118.     }  
  119.     buf = transfer(READ_VESION);  
  120.     fpga_spi_close();  
  121.     printf("Read version done.\r\n");  
  122.     return buf;  
  123. }  
  124. //测试物理连接状态   
  125. uint8_t test_fpga_connect()  
  126. {  
  127.     uint8_t buf;  
  128.     if(fpga_spi_open(file_name_buf) != nQAM_ERROR_NOERROR)  
  129.     {  
  130.         printf("in test_spi_open, fpga spi open failed\r\n");  
  131.             return 0;  
  132.     }  
  133.     buf = transfer(TEST_CONNECT);  
  134.     fpga_spi_close();  
  135.     printf("FPGA spi test connect done.\r\n");  
  136.     return buf;  
  137. }  

附: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应用程序设计

[cpp] view plain copy
  1. int main(int argc, char *argv )  
  2. {  
  3.     uint8_t connect;  
  4.     uint8_t version;  
  5.     connect = test_fpga_connect();  
  6.     printf("Test Value is %02X\r\n", connect); //测试连接状态  
  7.       
  8.     version = get_fpga_version();  
  9.     printf("the fpga version is %02X\r\n", version);  
  10.       
  11.     printf("****fpga app test run over!****\r\n");    
  12.     return 1;         
  13. }  



FPGA从机SPI设计


SPI从机的Verilog实现

[plain] view plain copy
  1. 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  
  2.     );  
  3.      
  4.   input                 clk;          // master clock input  
  5.   input                 rst;          // synchronous active high reset  
  6.   input   [7:0]         data_i;       // data bus input  
  7.   input                 wr_en_i;      // write enable input  
  8.   output  [7:0]         data_o;       // data bus output  
  9.     
  10.   output                valid_o;      // request signal output  
  11.   output                tx_valid;  
  12.   output                start_o;  
  13.   output                end_o;  
  14.   output  reg           we_ack_o;  
  15.     
  16.   //spi signals  
  17.   input                 ss_i;         // slave select  
  18.   input                 sclk_i;       // serial clock  
  19.   input                 mosi_i;  
  20.   output                miso_o;  
  21.     
  22.   reg     [7:0]         tx_data;  
  23.   reg     [7:0]         rx_data;   
  24.     
  25.   reg                   tx_tip;       //tx in progress  
  26.   reg                   rx_tip;       //rx in progress  
  27.     
  28.   wire                  rx_negedge;   //miso is sampled on negative edge  
  29.   wire                  tx_negedge;   //mosi is driven on nesedge edge  
  30.   wire    [2:0]         len;          //char length  
  31.   wire                  lsb;          //lsb first on line  
  32.     
  33.   wire                  pos_edge;     //recognize posedge of sclk  
  34.   wire                  neg_edge;     //recognize negdege of sclk  
  35.     
  36.   reg                   s_out;  
  37.   reg                   s_in;  
  38.   reg     [2:0]         s_sel;  
  39.   reg     [2:0]         s_clk;  
  40.     
  41.   assign    rx_negedge  = 0;            // decided by CPOL and CPHA  
  42.   assign    tx_negedge  = 1;            // means mode == 00  
  43.   assign    len         = 7;  
  44.   assign    lsb         = 0;  
  45.     
  46.   assign    miso_o      = s_out;  
  47.   assign    valid_o     = rst?  1'b0  : (!rx_tip);  
  48.   assign    data_o      = rst?  8'h00 : rx_data;  
  49.     
  50.   //sync SCK to the FPGA clock using a 3-bits shift register  
  51.   always @ (posedge clk)  
  52.   begin   
  53.     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  
  54.   end  
  55.     
  56.   assign pos_edge       =   (s_clk[1:0] == 2'b01);      // posedge when s_clk[1:0] == 2'b01 or s_clk[2:0] = 3'b001  
  57.   assign neg_edge       =   (s_clk[1:0] == 2'b10);      // negedge when s_clk[1:0] == 2'b10 or s_clk[2:0] = 3'b110  
  58.     
  59.   //SSEL  
  60.   always @ (posedge clk)  
  61.   begin  
  62.     s_sel <= {s_sel[1:0], ss_i};    //sample the ss signal   
  63.   end  
  64.     
  65.   wire                  sel_active;                     // from start_o is high to end_o is high sel_active is active   
  66.   assign sel_active     =   ~s_sel[1];                  // sel[2:0] = 000 001 011 111 110 100 000 when 100 000 001 .0 is high  
  67.   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   
  68.   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  
  69.     
  70.   //---------------------receiving bits from line---------------------  
  71.   wire                  rx_clk;  
  72.   wire                  rx_lst_bit;  
  73.   reg   [2:0]           rx_cnt;                         // rx data bit count  
  74.     
  75.   assign rx_clk         =   rx_negedge? neg_edge : pos_edge;    // question is the beginning value is "X"  
  76.   assign rx_lst_bit     = !(|rx_cnt);  
  77.     
  78.   always @(posedge clk)  
  79.   begin  
  80.     s_in  <=  mosi_i;  
  81.   end  
  82.     
  83.   always @(posedge clk or posedge rst)  
  84.   begin  
  85.     if (rst)  
  86.       rx_cnt <= len;  
  87.     else begin  
  88.       if(!rx_tip || end_o || start_o)  
  89.         rx_cnt  <=  len;  
  90.       else  
  91.         rx_cnt  <=  rx_clk ? (rx_cnt - 1'b1) : rx_cnt;        //question is the rx_cnt always is 7?  
  92.     end  
  93.   end  
  94.     
  95.   //Transfer in process  
  96.   always @(posedge clk or posedge rst)  
  97.   begin  
  98.     if(rst)  
  99.       rx_tip  <= 1'b0;  
  100.     else if(!rx_tip)  
  101.       rx_tip  <= 1'b1;  
  102.     else if(rx_tip && rx_lst_bit && rx_clk)  
  103.       rx_tip  <= 1'b0;  
  104.   end  
  105.     
  106.   always @(posedge clk or posedge rst)  
  107.   begin  
  108.     if(rst)  
  109.       rx_data <= {8{1'b0}};  
  110.     else begin  
  111.       if(sel_active && rx_clk)  
  112.         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}  
  113.                                                   // if lsb = 1 rx_data = {s_in,rx_data[7:1]}  
  114.     end                                           // {s_in,rx_data[7:1]} shift right  
  115.   end                                             // {rx_data[6:0],s_in} shift left  
  116.       
  117.     
  118.     
  119.   //---------------------sending bits to line---------------------  
  120.   wire                  tx_clk;  
  121.   wire                  tx_lsb_bit;  
  122.   reg [2:0]             tx_cnt;  
  123.   assign tx_clk         =   tx_negedge? neg_edge : pos_edge;        // tx_negedge = 1 negedge transfer data  
  124.   assign tx_lsb_bit     = !(|tx_cnt);  
  125.   assign tx_valid       = ((s_sel[2:0] == 3'b111) && (tx_tip == 1'b0)) ? 1'b1 : 1'b0;  
  126.     
  127. //  always @(posedge clk or posedge rst)  
  128. //  begin  
  129. //    if(rst)  
  130. //      tx_valid  <=  1'b0;  
  131. //    else if((s_sel[2:0] == 3'b111) && (tx_tip == 1'b0))  
  132. //      tx_valid  <=  1'b1;  
  133. //    else  
  134. //      tx_valid  <=  1'b0;  
  135. //  end  
  136.   // character bit counter  
  137.   always @(posedge clk or posedge rst)  
  138.   begin  
  139.     if(rst)  
  140.       tx_cnt  <= len;  
  141.     else begin  
  142.       if(!tx_tip || end_o || start_o)  
  143.         tx_cnt  <= len;  
  144.       else  
  145.         tx_cnt  <= tx_clk? (tx_cnt-1'b1):tx_cnt;  
  146.     end  
  147.   end  
  148.     
  149.   //transfer in process  
  150.   always @(posedge clk or posedge rst)  
  151.   begin  
  152.     if(rst) begin  
  153.       tx_tip  <= 1'b0;  
  154.     end  
  155.     else if(wr_en_i && (!tx_tip)) begin               //wr_en_i is high when transfer the data  
  156.       tx_tip  <= 1'b1;  
  157.     end  
  158.     else if(tx_tip && tx_lsb_bit && tx_clk) begin  
  159.       tx_tip  <= 1'b0;  
  160.     end  
  161.   end  
  162.     
  163.   always @(posedge clk or posedge rst)  
  164.   begin  
  165.     if(rst) begin  
  166.       tx_data <= 8'hff;  
  167.       we_ack_o  <=  1'b0;  
  168.     end  
  169.     else begin  
  170.       we_ack_o <= 1'b0;  
  171.       if(wr_en_i && (!tx_tip)) begin  
  172.         tx_data[7:0]  <=  data_i[7:0];  
  173.         we_ack_o  <= 1'b1;  
  174.       end  
  175.       else begin  
  176.         if(sel_active && rx_clk) begin  
  177.           s_out <=  lsb?  tx_data[0] : tx_data[7];  
  178.           tx_data <= lsb? {1'b1,tx_data[7:1]} : {tx_data[6:0],1'b1};  
  179.         end  
  180.       end  
  181.     end  
  182.   end       
  183. endmodule  

FPGA的SPI command verilog设计

[plain] view plain copy
  1. `timescale 1 ns / 1 ps  
  2. module spi_cmd #(  
  3.   parameter   BUS_DATA_WIDTH            = 8,  
  4.   parameter   BUS_ADDR_WIDTH            = 16  
  5. )(  
  6.   input                                 clk,  
  7.   input                                 rst,  
  8.   input       [BUS_DATA_WIDTH-1:0]      rx_data,  
  9.   input                                 rx_valid,  
  10.   input                                 rx_start,  
  11.   input                                 rx_end,  
  12.   input                                 tx_valid,  
  13.     
  14.   input                                 tx_ack,  
  15.   output  reg [BUS_DATA_WIDTH-1:0]      tx_data,                        
  16.   output  reg                           tx_req  
  17. );  
  18.   
  19.   localparam    CON_WR_REG              = 8'h51;  
  20.   localparam    CON_RD_REG              = 8'h52;  
  21.   localparam    RD_DATA_REG             = 8'h96;  
  22.   localparam    RST_CMD                 = 8'h55;  
  23.     
  24.   localparam    ADDR_FPGA_VERSION       = 16'hFFFF;  
  25.   localparam    ADDR_SPI_TEST           = 16'hF000;  
  26.   localparam    FPGA_VERSION            = 16'h1003;  
  27.     
  28.   localparam    REC_NO_ERR              = 8'h90;  
  29.   localparam    REC_INS_ERR             = 8'hF1;  
  30.   localparam    REC_LEN_ERR             = 8'hF2;  
  31.   localparam    REC_BUSY_ERR            = 8'hF3;  
  32.   localparam    REC_WR_ERR              = 8'hF4;  
  33.     
  34.   localparam    REC_START               = 1;  
  35.   localparam    REC_TYPE_REG            = 2;  
  36.   localparam    REC_ADDR_REGH           = 3;  
  37.   localparam    REC_ADDR_REGL           = 4;  
  38.   localparam    REC_ADDR_LEN            = 5;  
  39.   localparam    REC_CON_WRH             = 6;  
  40.   localparam    REC_CON_WRL             = 7;  
  41.   localparam    RSP_STATUS              = 8;  
  42.   localparam    RSP_LEN                 = 9;  
  43.   localparam    RSP_DIN                 = 10;  
  44.   localparam    RSP_DOUT                = 11;  
  45.   localparam    READ_DATAH              = 12;  
  46.   localparam    READ_DATAL              = 13;  
  47.   localparam    READ_DONE               = 14;  
  48.     
  49.   reg   [7:0]                           rec_type;  
  50.   reg   [15:0]                          rec_addr;  
  51.   reg   [7:0]                           addr_len;  
  52.   reg   [7:0]                           len_count;  
  53.   reg   [3:0]                           cmd_state;  
  54.   reg   [7:0]                           err_code;  
  55.   reg   [15:0]                          rsp_dout;  
  56.   reg                                   rsp_rd;   
  57.   reg   [15:0]                          spi_test;  
  58.   reg   [3:0]                           test_flag;  
  59.     
  60.   [email protected](posedge clk or posedge rst)  
  61.   begin  
  62.     if(rst) begin  
  63.       rec_type          <=  0;  
  64.       rec_addr          <=  0;  
  65.       addr_len          <=  0;  
  66.       len_count         <=  0;  
  67.       err_code          <=  REC_NO_ERR;  
  68.       tx_data           <=  0;  
  69.       tx_req            <=  0;  
  70.       rsp_rd            <= 1'b0;  
  71.       cmd_state         <=  REC_START;  
  72.       test_flag         <=  4'h0;  
  73.     end  
  74.     else begin  
  75.       if(tx_ack) begin  
  76.         tx_req  <=  1'b0;  
  77.       end  
  78.       case(cmd_state)  
  79.         REC_START : begin  
  80.           len_count   <=  0;  
  81.           if(rx_start) begin  
  82.             cmd_state <=  REC_TYPE_REG;  
  83.             test_flag <=  4'h1;  
  84.           end  
  85.         end  
  86.         REC_TYPE_REG : begin  
  87.           if(rx_valid) begin  
  88.             case(rx_data)  
  89.               CON_WR_REG, CON_RD_REG : begin  
  90.                 err_code    <=  REC_NO_ERR;  
  91.                 rec_type    <=  rx_data;  
  92.                 cmd_state   <=  REC_ADDR_REGH;  
  93.                 test_flag   <=  4'h2;  
  94.               end  
  95.               RD_DATA_REG : begin  
  96.                 cmd_state   <=  RSP_STATUS;  
  97.                 test_flag   <=  4'h6;  
  98.               end  
  99.               default : cmd_state <=  REC_START;  
  100.             endcase  
  101.           end  
  102.         end  
  103.   
  104.         REC_ADDR_REGH : begin  
  105.           if(rx_valid) begin  
  106.             rec_addr[15:8]  <=  rx_data;  
  107.             cmd_state   <=  REC_ADDR_REGL;  
  108.             test_flag   <=  4'h3;  
  109.           end  
  110.         end  
  111.         REC_ADDR_REGL : begin  
  112.           if(rx_valid) begin  
  113.             rec_addr[7:0] <=  rx_data;  
  114.             cmd_state   <=  REC_ADDR_LEN;  
  115.             test_flag   <=  4'h4;  
  116.           end  
  117.         end  
  118.           
  119.         REC_ADDR_LEN : begin  
  120.           if(rx_valid) begin  
  121.             if(rx_data != 0) begin  
  122.               addr_len    <=  rx_data;  
  123.               test_flag <=  4'h5;  
  124.               case(rec_type)  
  125.                 CON_WR_REG : cmd_state  <=  REC_CON_WRH;  
  126.                 CON_RD_REG : cmd_state  <=  REC_START;  
  127.                 default : cmd_state <=  REC_START;  
  128.               endcase  
  129.             end  
  130.             else begin  
  131.               err_code  <=  REC_LEN_ERR;  
  132.               cmd_state <=  REC_START;  
  133.             end  
  134.           end  
  135.         end  
  136.         REC_CON_WRH : begin  
  137.           if(len_count == addr_len) begin  
  138.             cmd_state <=  REC_START;  
  139.           end  
  140.           else begin  
  141.             if(rx_valid) begin  
  142.               if(rec_addr == ADDR_FPGA_VERSION) begin  
  143.                 err_code  <=  REC_WR_ERR;  
  144.                 cmd_state <=  REC_START;  
  145.               end  
  146.               else if(rec_addr == ADDR_SPI_TEST) begin  
  147.                 spi_test[15:8]  <=  rx_data;  
  148.               end  
  149. //              else begin  
  150. //                rec_buf_data[15:8]  <=   rx_data;  
  151. //              end  
  152.               cmd_state <=  REC_CON_WRL;  
  153.             end  
  154.           end  
  155.         end         
  156.         REC_CON_WRL : begin  
  157.           if(rx_valid) begin  
  158.             if(rec_addr == ADDR_SPI_TEST) begin  
  159.               spi_test[7:0] <=  rx_data;  
  160.             end  
  161. //            else begin  
  162. //              rec_buf_data[7:0] <=  rx_data;  
  163. //              rec_addr  <=  rec_addr + 1'b1;  
  164. //            end  
  165.             len_count <=  len_count + 1'b1;  
  166.           end  
  167.         end  
  168.           
  169.         RSP_STATUS : begin  
  170.           if(tx_valid) begin  
  171.             if(tx_ack) begin  
  172.               tx_req  <=  1'b0;  
  173.             end  
  174.             else begin  
  175.               tx_req  <=  1'b1;  
  176.             end  
  177.             rsp_rd    <=  1'b1;  
  178.             tx_data   <=  err_code;  
  179.             cmd_state <=  RSP_LEN;  
  180.             test_flag <=  4'h7;  
  181.           end  
  182.         end  
  183.   
  184.         RSP_LEN : begin  
  185.           if(tx_valid) begin  
  186.             if(tx_ack) begin  
  187.               tx_req  <=  1'b0;  
  188.             end  
  189.             else begin  
  190.               tx_req  <=  1'b1;  
  191.             end  
  192.             case(rec_type)  
  193.               CON_WR_REG : begin  
  194.                 tx_data   <=  0;  
  195.                 cmd_state <=  REC_START;  
  196.               end  
  197.               CON_RD_REG : begin  
  198.                 tx_data   <=  addr_len;  
  199.                 cmd_state <=  RSP_DIN;  
  200.               end  
  201.               default : begin  
  202.                 tx_data   <=  0;  
  203.                 cmd_state <=  REC_START;  
  204.               end  
  205.             endcase  
  206.             test_flag <=  4'h8;  
  207.           end  
  208.         end  
  209.           
  210.         RSP_DIN : begin  
  211.           if(tx_valid) begin  
  212.             if(tx_ack) begin  
  213.               tx_req  <=  1'b0;  
  214.             end  
  215.             else begin  
  216.               tx_req  <=  1'b1;  
  217.             end  
  218.             if(len_count == addr_len) begin  
  219.               cmd_state <=  READ_DONE;  
  220.             end  
  221.             else begin  
  222.               cmd_state <=  RSP_DOUT;  
  223.               rsp_rd    <=  1'b0;  
  224.             end  
  225.             test_flag <=  4'h9;  
  226.           end  
  227.         end  
  228.           
  229.         RSP_DOUT : begin  
  230.           if(tx_valid) begin  
  231.             if(tx_ack) begin  
  232.               tx_req  <=  1'b0;  
  233.             end  
  234.             else begin  
  235.               tx_req  <=  1'b1;  
  236.             end  
  237.             cmd_state <=  READ_DATAH;  
  238.           end  
  239.         end  
  240.                 
  241.         READ_DATAH : begin  
  242.           if(tx_valid) begin  
  243.             if(tx_ack) begin  
  244.               tx_req  <=  1'b0;  
  245.             end  
  246.             else begin  
  247.               tx_req  <=  1'b1;  
  248.             end  
  249.             tx_data   <=  rsp_dout[15:8];  
  250.             cmd_state <=  READ_DATAL;  
  251.             test_flag <=  4'hA;  
  252.           end  
  253.         end  
  254.         READ_DATAL : begin  
  255.         if(tx_valid) begin  
  256.           if(tx_ack) begin  
  257.             tx_req  <=  1'b0;  
  258.           end  
  259.           else begin  
  260.             tx_req  <=  1'b1;  
  261.           end  
  262.           tx_data   <=  rsp_dout[7:0];  
  263.           len_count <=  len_count + 1'b1;  
  264.           cmd_state <=  RSP_DIN;  
  265.           test_flag <=  4'hB;  
  266.           end  
  267.         end  
  268.           
  269.         READ_DONE : begin  
  270.           if(tx_valid) begin  
  271.             if(tx_ack) begin  
  272.               tx_req  <=  1'b0;  
  273.             end  
  274.             else begin  
  275.               tx_req  <=  1'b1;  
  276.             end  
  277.             tx_req    <= 1'b0;  
  278.             len_count <=  0;  
  279.             tx_data   <=  0;  
  280.             rec_type    <=  0;  
  281.             cmd_state <=  REC_START;  
  282.           end  
  283.         end  
  284.       endcase  
  285.     end  
  286.   end  
  287.   
  288. always @(posedge clk or posedge rst)  
  289. begin  
  290.   if(rst == 1'b1)  
  291.   begin  
  292.     rsp_dout  <=  {16{1'b0}};  
  293.   end  
  294.   else if((rsp_rd == 1'b1) && (test_flag  ==  4'h7)) begin  
  295.     case(rec_addr)  
  296.       ADDR_FPGA_VERSION : rsp_dout  <=  FPGA_VERSION;  
  297.       ADDR_SPI_TEST :   rsp_dout  <=  ~spi_test;  
  298.       default : ;  
  299.     endcase  
  300.   end  
  301. end  
  302.   
  303. endmodule