1. 程式人生 > >基於AM335X與FPGA的SPI通訊設計

基於AM335X與FPGA的SPI通訊設計

在2013年的工作中,涉及到了AM3359與XC7K325T之間的相互通訊,其目的是為了獲取FPGA設計版本號,該FPGA版本號儲存在FPGA的暫存器0xFFFF中,FPGA的版本值隨著載入程式發生變化,當時的版本資訊為0x1003.

需要說明的是,在本文中的程式碼風格是剛工作兩年的時候的程式碼風格,現在回看,這些程式碼風格實在難以閱讀。尤其是SPI的verilog程式等。並不代表現在的程式設計水平與程式碼風格。

設計框圖如下:


本文主要從三個方面介紹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的資料。


從上段描述中可以分析出一下兩點:

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


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為高電平,那麼第二個邊沿就是從低電平到高電平,即為上升沿。


需要注意的是:取樣一定是需要先準備好資料,才用時鐘的有效沿將資料打到對應的引腳上。

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;
  
  [email protected](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

以上專案總結僅作參考,適用於本公司後繼者對此工程的修改參考。

相關推薦

基於AM335XFPGA的SPI通訊設計

在2013年的工作中,涉及到了AM3359與XC7K325T之間的相互通訊,其目的是為了獲取FPGA設計版本號,該FPGA版本號儲存在FPGA的暫存器0xFFFF中,FPGA的版本值隨著載入程式發生變化,當時的版本資訊為0x1003. 需要說明的是,在本文中的程式碼風格是剛

基於 Java Web 的畢業設計選題管理平臺--測試報告用戶手冊

對話 不能 彈出 src 註冊 兼容性 優化 spa borde 一、測試報告 1、兼容性測試 功能 描述 效果 Chrome瀏覽器 FireFox瀏覽器 IE瀏覽器 war 端瀏覽器 管理員登錄 管理員用戶登錄功能 彈出“

找java設計,基於ssh,j2ee管理系統,設計,管理系統的設計思路技巧

ava 畢設 框架 僅供參考 andro 培訓 中一 畢業 遠程 關於基於ssh,ssm,javaee等等管理系統的設計思路與框架搭建,很多同學都是一知半解,甚至是知之甚少。為了大家能快速的開發設計一套這樣的java設計,我們提供下面的一些方法僅供參考。不足之處大家可以相互

時間觸發嵌入式系統設計模式 第18章 筆記 通過RS-232 PC通訊

時間觸發嵌入式系統設計模式 第18章 筆記 PC上 軟體 例子: 參考 : Axelson(1998) Serial Port Complete: Programming and Circuits for Rs-232 and Rs-485 Links and Netwo

基於Android簡單備忘錄的設計實現(附git原始碼連結)

前言 課程作業需要,於是忙活兩天寫了一個簡單的備忘錄,使用了ListView,SQLite。 開發環境:Android Studio 原始碼連結:https://gitee.com/zg0212/Memoire 功能截圖 主頁面 新建頁面

[原始碼和文件分享]基於組合語言的音樂盒設計實現

基於組合語言的音樂盒設計與實現—彙編課設 一 需求分析 設計一個音樂盒,可用在諸如生日禮品等場景裡。 包含的功能有播放音樂、切換音樂。預設播放第一首音樂,單曲迴圈。當撥動控制開關時切換歌曲,總共三首,分別由三個開關控制。當且僅當一個開關開啟其它開關關閉時有效,多個開關同時開啟時無效。

基於win32api的PLCMFC通訊

作者使用歐姆龍PLC,採用FCS校驗,用Win32API,在MFC上實現通訊。 話說,這個賊簡單,直接上程式碼 (1)首先,建立埠,並初始化引數。 hCom=CreateFile("COM4",

基於委託事件的有限狀態機設計實現(Unity3d)

有限狀態機設計與實現 前言 最近看到一句話,就是優秀是一種習慣,所以突然就總想寫點什麼,當作是遊戲開發這條路上的學習筆記吧,另外也時刻提醒自己需要不斷努力。 什麼是狀態機? 先貼百度百科的概念內容吧: 有限狀態機,(英語:Finite-state machine

基於WEB的案件資訊查詢分析系統設計,java專案設計

**基於WEB的案件資訊查詢與分析系統設計,java專案設計** 基於WEB的案件資訊查詢與分析系統設計mysql資料庫建立語句 基於WEB的案件資訊查詢與分析系統設計oracle資料庫建立語句 基於WEB的案件資訊查詢與分析系統設計sqlserver資料庫建立語句

模組模組之間通訊設計-元件設計思想

 --模組與模組之間的設計,除了大範圍的設計模式,更多不屬於模式的多種方法呼叫都可以通訊。         -- 如何設計低耦合的,高內聚的模組之間通訊?原則:         -- 1.用管理器用介

基於JAVAJSP下的網上商城設計

基於JAVA的網上商城系統的開發與實現     摘要:網上商城系統是基於JAVA的一個電子商務系統,其主要作用就是實現商城的一些功能,如:買賣商品,計算積分,檢視產品的具體資訊,與賣家聯絡,交友等功能! 最後對整個網上商城系統作了一個簡要的總結並附錄上了一些功能模組的

基於SpringCloud的微服務設計實現

Spring Boot是在 2013年推出的新專案,主要用來簡化Spring 開發框架的開發、配置、除錯、部署工作,同時在專案內集成了大量易於使用且實用的基礎框架[[i]]。使用Spring Boot開發專案,可以做到一鍵啟動和部署,整個開發過程得到了很大的簡化。Sp

基於Active-HDL的HDL設計錄入模擬

作者:毛蘢瑋 / Saint 掘金:https://juejin.im/user/5aa1f89b6fb9a028bb18966a 微博:https://weibo.com/5458277467/profile?topnav=1&wvr=6&is_all=1 GitHub:

基於SpringMvc的定時任務設計踩的坑

需求:系統內有個sla計時器需要每隔一分鐘執行一次 思路:專案啟動的時候將sla定時任務存入資料庫並呼叫定時任務群啟動方法完成 配置系統啟動Listener <listener> <listener-class>com.d

基於Java軟體開發平臺設計實現溫溼度檢測系統

**基於Java軟體開發平臺設計與實現溫溼度檢測系統** 基於Java軟體開發平臺設計與實現溫溼度檢測系統mysql資料庫建立語句 基於Java軟體開發平臺設計與實現溫溼度檢測系統oracle資料庫建立語句 基於Java軟體開發平臺設計與實現溫溼度檢測系統sqlserve

基於Simulink的FIR濾波器設計模擬--初識matlab

一直對訊號分析與處理有著比較濃厚的興趣,只可惜數學水平挺一般,難以將興趣發展為job,因此就蜻蜓點水了。 公司裡的幾乎人人都會simulink,而我是十足的門外漢。看別人用得行雲流水總是挺眼饞的,於是也班門弄斧試試。 實現的功能是將三個幅度都為1初相位0,頻率分別為1

基於射頻CC2520 實現的ZigBee 通訊設計

引言 通訊是多機器人之間進行資訊互動並實現協作的基礎。ZigBee 作為一種新興的短距離無線網路技術,採用IEEE802.15.4標準,利用全球共用的公共頻率2.4GHz,具有低複雜度、低成本、低功耗、網路結點多、傳輸距離遠等優點,非常適合多機器人系統的應用。為了實現多機器

基於curl的linuxwindows通訊服務搭建

一、環境準備 windows:curl、json、base64、visual studio linux:自帶的swgi模組、base64、json 二、curl配置 下載最新的curl版本:http://curl.haxx.se/latest.cg

基於Spring Boot的“課程設計”的設計實現

這是一個集電影,音樂和書籍於一體的Java web應用 Java 1.8 框架:使用Spring Boot 整合Spring,Spring MVC,MyBatis(前期),Spring Data(後期) 資料庫:MySQL 5.6 快取:

C#——基於委託事件的多執行緒通訊(同樣適用於非UI執行緒間通訊

在研究c# 執行緒之間通訊時,發現傳統的方法大概有三種 ①全域性變數,由於同一程序下的多個程序之間共享資料空間,所以使用全域性變數是最簡單的方法,但要記住使用volatile進行限制。 ②執行緒之間傳送訊息(這個隨後文章中會討論到)。 ③CEvent為MFC中的一