Verilog -- SPI協議


Verilog -- SPI協議

簡介

SPI是一種全雙工通信,並且是一種同步傳輸方式(slave的接收clk需要master給出)

SPI總線是一種4線總線,因其硬件功能很強,所以與SPI有關的軟件就相當簡單,使中央處理器(Central Processing Unit,CPU)有更多的時間處理其他事務。正是因為這種簡單易用的特性,越來越多的芯片集成了這種通信協議,比如AT91RM9200。SPI是一種高速、高效率的串行接口技術。通常由一個主模塊和一個或多個從模塊組成,主模塊選擇一個從模塊進行同步通信,從而完成數據的交換。SPI是一個環形結構,通信時需要至少4根線(事實上在單向傳輸時3根線也可以)。
SPI的通信原理很簡單,它以主從方式工作,這種模式通常有一個主設備和一個或多個從設備,需要至少4根線,事實上3根也可以(單向傳輸時)。也是所有基於SPI的設備共有的,它們是MISO(主設備數據輸入)、MOSI(主設備數據輸出)、SCLK(時鍾)、CS(片選)。
(1)MISO– Master Input Slave Output,主設備數據輸入,從設備數據輸出;
(2)MOSI– Master Output Slave Input,主設備數據輸出,從設備數據輸入;
(3)SCLK – Serial Clock,時鍾信號,由主設備產生;
(4)CS – Chip Select,從設備使能信號,由主設備控制。
其中,CS是從芯片是否被主芯片選中的控制信號,也就是說只有片選信號為預先規定的使能信號時(高電位或低電位),主芯片對此從芯片的操作才有效。這就使在同一條總線上連接多個SPI設備成為可能。
(以上來自百度百科)

SPI最大傳輸速率

SPI是一種事實標准,由Motorola開發,並沒有一個官方標准。已知的有的器件SPI已達到50Mbps。具體到產品中SPI的速率主要看主從器件SPI控制器的性能限制。
受以下幾個條件影響:
SPI的最大時鍾頻率
CPU處理SPI數據的能力
輸出端驅動能力(PCB所允許的最大信號傳輸速率)

SPI的最大時鍾頻率

一般情況下,SPI模塊的最大時鍾頻率為系統時鍾頻率的1/2。雖然SPI的傳輸速率主要受限於CPU處理SPI數據的能力,但在同另一個非常高速率的SPI設備通訊時,SPI的最大時鍾頻率將有可能制約其傳輸速率。

CPU處理SPI數據的能力

通常情況下,考慮到系統中CPU有可能需要處理其他任務,以及對所接收SPI數據的具體運算處理方法,CPU處理SPI數據的能力將影響到整體的傳輸速率。

以上內容參考 https://www.cnblogs.com/liujinggang/p/9609739.html

下面是verilog代碼:


module spi_master
(
    input               I_clk       , // 全局時鍾50MHz
    input               I_rst_n     , // 復位信號,低電平有效
    input               I_en        , //  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_state      ; 

always @(posedge I_clk or negedge I_rst_n)
begin
    if(!I_rst_n)
        begin
            R_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  <=  8'd0    ;
        end 
    else if(I_en) // en信號打開的情況下
        begin
            O_spi_cs    <=  1'b0    ; // 把片選CS拉低
            R_state     <=  R_state + 1'b1   ;
            case(R_state)
                // 發送
                4'd0:    // 發送第7位
                    begin
                        O_spi_mosi  <=  I_data_in[7]        ;
                        O_spi_sck   <=  1'b0                ;
                        O_tx_done   <=  1'b0                ;
                      end
                4'd2:    // 發送第6位
                    begin
                        O_spi_mosi  <=  I_data_in[6]        ;
                        O_spi_sck   <=  1'b0                ;
                        O_tx_done   <=  1'b0                ;
                      end
                4'd4:    // 發送第5位
                    begin
                        O_spi_mosi  <=  I_data_in[5]        ;
                        O_spi_sck   <=  1'b0                ;
                        O_tx_done   <=  1'b0                ;
                    end 
                4'd6:    // 發送第4位
                    begin
                        O_spi_mosi  <=  I_data_in[4]        ;
                        O_spi_sck   <=  1'b0                ;
                        O_tx_done   <=  1'b0                ;
                    end 
                4'd8:    // 發送第3位
                    begin
                        O_spi_mosi  <=  I_data_in[3]        ;
                        O_spi_sck   <=  1'b0                ;
                        O_tx_done   <=  1'b0                ;
                    end                            
                4'd10:    // 發送第2位
                    begin
                        O_spi_mosi  <=  I_data_in[2]        ;
                        O_spi_sck   <=  1'b0                ;
                        O_tx_done   <=  1'b0                ;
                    end 
                4'd12:    // 發送第1位
                    begin
                        O_spi_mosi  <=  I_data_in[1]        ;
                        O_spi_sck   <=  1'b0                ;
                        O_tx_done   <=  1'b0                ;
                    end 
                4'd14:    // 發送第0位
                    begin
                        O_spi_mosi  <=  I_data_in[0]        ;
                        O_spi_sck   <=  1'b0                ;
                        O_tx_done   <=  1'b1                ;
                      end 

                // 接收
                4'd1:    // 接收第7位
                    begin
                        O_spi_sck       <=  1'b1                ; 
                        O_rx_done       <=  1'b0                ;
                        O_data_out[7]   <=  I_spi_miso          ;   
                      end
                4'd3:    // 接收第6位
                    begin
                        O_spi_sck       <=  1'b1                ;
                        O_rx_done       <=  1'b0                ;
                        O_data_out[6]   <=  I_spi_miso          ; 
                    end
                4'd5:    // 接收第5位
                    begin
                        O_spi_sck       <=  1'b1                ;
                        O_rx_done       <=  1'b0                ;
                        O_data_out[5]   <=  I_spi_miso          ; 
                    end 
                4'd7:    // 接收第4位
                    begin
                        O_spi_sck       <=  1'b1                ;
                        O_rx_done       <=  1'b0                ;
                        O_data_out[4]   <=  I_spi_miso          ; 
                    end 
                4'd9:    // 接收第3位
                    begin
                        O_spi_sck       <=  1'b1                ;
                        O_rx_done       <=  1'b0                ;
                        O_data_out[3]   <=  I_spi_miso          ; 
                    end                            
                4'd11:    // 接收第2位
                    begin
                        O_spi_sck       <=  1'b1                ;
                        O_rx_done       <=  1'b0                ;
                        O_data_out[2]   <=  I_spi_miso          ; 
                    end 
                4'd13:    // 接收第1位
                    begin
                        O_spi_sck       <=  1'b1                ;
                        O_rx_done       <=  1'b0                ;
                        O_data_out[1]   <=  I_spi_miso          ; 
                    end 
                4'd15:    // 接收第0位
                    begin
                        O_spi_sck       <=  1'b1                ;
                        O_rx_done       <=  1'b1                ;
                        O_data_out[0]   <=  I_spi_miso          ; 
                      end
                default:R_state  <=  4'd0                ;   
            endcase 
          end 
           
    else
        begin
            R_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 spi_master_tb();

reg I_clk;
reg I_rst_n;
reg I_en;
reg  [7:0]I_data_in; 
wire [7:0]O_data_out;
wire O_tx_done;  
wire O_rx_done;

wire I_spi_miso;
wire O_spi_sck;  
wire O_spi_cs;  
wire O_spi_mosi;

always #1 I_clk = ~ I_clk;

assign I_spi_miso = O_spi_mosi;

integer i;
initial begin
  I_clk = 1;
  I_en = 0;
  I_data_in = 0;
  I_rst_n = 1; #2 I_rst_n = 0; #2 I_rst_n = 1;

  #20;
  @(posedge I_clk) begin
      I_en <= 1;
      I_data_in <= 0;
    end
  for(i=1;i<20;i=i+1) begin
    @(negedge O_tx_done) begin
      I_data_in <= i;
    end
  end
  I_en <= 0;
end


spi_master U_SPI_MASTER_0(
    .I_clk                          ( I_clk                         ),
    .I_rst_n                        ( I_rst_n                       ),
    .I_en                           ( I_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
    $fsdbDumpvars();
    $fsdbDumpMDA();
    $dumpvars();
    #1000 $finish;
 end

endmodule

仿真波形:

測試程序中將MOSI和MISO短接了,所以spi發送的數據被自己接收,從波形圖中可以看到,發送和接收的邏輯正確。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM