在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

FPGA中如何設計SPI協議驅動?

電子工程師 ? 來源:FPGA技術江湖 ? 作者:郝旭帥 ? 2021-05-03 15:37 ? 次閱讀

SANXIN-B01 Verilog教程-郝旭帥團隊

SPI是串行外設接口(Serial Peripheral Interface)的縮寫。SPI,是一種高速的,全雙工,同步的通信總線,并且在芯片的管腳上只占用四根線,節約了芯片的管腳,同時為PCB的布局上節省空間,提供方便,正是出于這種簡單易用的特性,如今越來越多的芯片集成了這種通信協議。

SPI的通信原理很簡單,它以主從方式工作,這種模式通常有一個主設備和一個或多個從設備,中間靠三線或者四線連接(三線時為單向傳輸或者數據線雙向傳輸)。所有基于SPI的設備共有的,它們是MISO、MOSI、SCLK、CS。

MISO– Master Input Slave Output,主設備數據輸入,從設備數據輸出。

MOSI– Master Output Slave Input,主設備數據輸出,從設備數據輸入。

SCLK – Serial Clock,時鐘信號,由主設備產生。

CS – Chip Select,從設備使能信號,由主設備控制。

787f2718-9dac-11eb-8b86-12bb97331649.png

cs是從芯片是否被主芯片選中的控制信號,也就是說只有片選信號為預先規定的使能信號時(高電位或低電位),主芯片對此從芯片的操作才有效。這就使在同一條總線上連接多個spi設備成為可能。

7897afc2-9dac-11eb-8b86-12bb97331649.png

通訊是通過數據交換完成的,由sclk提供時鐘脈沖,mosi、miso則基于此脈沖完成數據傳輸。數據輸出通過 mosi線,數據在時鐘上升沿或下降沿時改變,在緊接著的下降沿或上升沿被讀取。完成一位數據傳輸,輸入也使用同樣原理。因此,至少需要N次時鐘信號的改變(上沿和下沿為一次),才能完成N位數據的傳輸。

spi通信有四種不同的模式,不同的從設備可能在出廠時就已經配置為某種模式。通信的雙方必須是工作在同一模式下,所以我們可以對主設備的spi模式進行配置,通過CPOL(時鐘極性)和CPHA(時鐘相位)來控制我們主設備的通信模式。

mode0:CPOL=0,CPHA=0;

mode1:CPOL=0,CPHA=1;

mode2:CPOL=1,CPHA=0;

mode3:CPOL=1,CPHA=1;

時鐘極性CPOL是用來配置SCLK在空閑時,應該處于的狀態;時鐘相位CPHA用來配置在第幾個邊沿進行采樣。

CPOL=0,表示在空閑狀態時,時鐘SCLK為低電平。

CPOL=1,表示在空閑狀態時,時鐘SCLK為高電平。

CPHA=0,表示數據采樣是在第1個邊沿。

CPHA=1,表示數據采樣是在第2個邊沿。

即:

CPOL=0,CPHA=0:此時空閑態時,SCLK處于低電平,數據采樣是在第1個邊沿,也就是SCLK由低電平到高電平的跳變,所以數據采樣是在上升沿,數據發送是在下降沿。

CPOL=0,CPHA=1:此時空閑態時,SCLK處于低電平,數據發送是在第1個邊沿,也就是SCLK由低電平到高電平的跳變,所以數據采樣是在下降沿,數據發送是在上升沿。

CPOL=1,CPHA=0:此時空閑態時,SCLK處于高電平,數據采集是在第1個邊沿,也就是SCLK由高電平到低電平的跳變,所以數據采集是在下降沿,數據發送是在上升沿。

CPOL=1,CPHA=1:此時空閑態時,SCLK處于高電平,數據發送是在第1個邊沿,也就是SCLK由高電平到低電平的跳變,所以數據采集是在上升沿,數據發送是在下降沿。

78b60242-9dac-11eb-8b86-12bb97331649.png

硬件簡介

FLASH閃存 的英文名稱是“Flash Memory”,一般簡稱為“Flash”,它屬于內存器件的一種,是一種非易失性( Non-Volatile )內存。

在開發板上有一塊flash(M25P16),用來保存FPGA的硬件配置信息,也可以用來存儲用戶的應用程序或數據。

78fb7b88-9dac-11eb-8b86-12bb97331649.png

790f6472-9dac-11eb-8b86-12bb97331649.png

M25P16是一款帶有寫保護機制和高速SPI總線訪問的2M字節串行Flash存儲器,該存儲器主要特點:2M字節的存儲空間,分32個扇區,每個扇區256頁,每頁256字節;能單個扇區擦除和整片擦除;每扇區擦寫次數保證10萬次、數據保存期限至少20年。

792d75ca-9dac-11eb-8b86-12bb97331649.png

C(serial clock:串行時鐘)為D和Q提供了數據輸入或者輸出的時序。D的數據總是在C的上升沿被采樣。Q的數據 在C的下降沿被輸出。

79688318-9dac-11eb-8b86-12bb97331649.png(Chip Select:芯片選擇端),當輸入為低時,該芯片被選中,可以允許進行讀寫操作。當輸入為高時,該芯片被釋放,不能夠進行操作。

對于H——o——l——d——和W——, 為保持功能和硬件寫保護功能,在本設計中不使用此管腳,在硬件設計時,這兩個管腳全部被拉高了,即全部失效。

flash采用spi的通信協議,flash當做從機。serial clcok等效于spi中的sclk,chip select等效于spi中的cs,D等效于spi中的mosi,Q等效于spi中的miso。

flash可以支持mode0和mode3,這兩種模式中,都是在時鐘的上升沿采樣,在時鐘的下降沿發送數據。

7981ff3c-9dac-11eb-8b86-12bb97331649.png

flash的每一頁都可以被寫入,但是寫入只能是把1改變為0。擦除可以把0改變為1。所以在正常寫入數據之前,都要將flash進行擦除。

flash的命令表如下:

798ca2b6-9dac-11eb-8b86-12bb97331649.png

下面介紹幾個常用的命令。

RDID(Read Identification :讀ID):發送命令RDID(9F),然后接收第1個字節的memory type(20H),第二個字節的memory capacity(15H)。后續的字節暫不關心。

79b08762-9dac-11eb-8b86-12bb97331649.png

WREN(Write Enable :寫使能):在任何寫或者擦除的命令之前,都必須首先打開寫使能。打開寫使能為發送命令WREN(06h)。

79ecd30c-9dac-11eb-8b86-12bb97331649.png

RDSR(Read Status Register:讀狀態寄存器):發送命令RDSR(05h),然后返回一個字節的狀態值。

7a153978-9dac-11eb-8b86-12bb97331649.png

狀態寄存器的格式如下:

7a24f408-9dac-11eb-8b86-12bb97331649.png

WIP(Write In Progress bit)表示flash內部是否正在進行內部操作,寫和擦除都會導致flash內部進行一段時間的工作,在內部工作期間,外部的命令會被忽略,所以在進行任何命令之前,都需要查看flash內部是否正在工作。WIP為1時,表示flash內部正在工作;WIP為0時,表示flash內部沒有在工作。

READ(Read DATA Bytes:讀數據):發送命令READ(03H),后續發送3個字節的地址,然后就可以接收數據,內部的地址會不斷遞增。一個讀命令就可以把整個flash全部讀完。

7a51d50e-9dac-11eb-8b86-12bb97331649.png

PP(Page Program :頁編寫):發送命令PP(02H),接著發送3個字節的地址,然后發送數據即可。切記所寫的數據不能超過本頁的地址范圍。

7a72780e-9dac-11eb-8b86-12bb97331649.png

SE(Sector Erase :扇區擦除):發送命令SE(D8H),接著發送3個字節的地址。

7aa388ea-9dac-11eb-8b86-12bb97331649.png

BE(Bulk Erase:整片擦除):發送命令BE(C7H)。

7aaf1d4a-9dac-11eb-8b86-12bb97331649.png

關于flash的其他的介紹,可以參考03_芯片手冊-》FLASH-》M25P16.pdf。

設計要求

設計flash(M25P16)控制器

設計分析

根據M25P16的數據手冊得知,其接口為spi接口,且支持模式0和模式3,本設計中選擇模式0。

輸入時序圖如下:

7aea7a48-9dac-11eb-8b86-12bb97331649.png

輸出時序如下:

7af6cb18-9dac-11eb-8b86-12bb97331649.png

時序圖中所對應的符號說明:

7b21354c-9dac-11eb-8b86-12bb97331649.png

7b2dc8de-9dac-11eb-8b86-12bb97331649.png

根據輸入和輸出的時序圖以及參數表,將SPI的時鐘的頻率定為10MHz。

在設計中,FPGA作為主機,M25P16作為從機。

架構設計和信號說明

此模塊命名為m25p16_drive。

7b69071e-9dac-11eb-8b86-12bb97331649.png

二級模塊(分模塊)(第一頁)

7b870278-9dac-11eb-8b86-12bb97331649.png

二級模塊(分模塊)(第二頁)

7b9206c8-9dac-11eb-8b86-12bb97331649.png

7bb4f7d2-9dac-11eb-8b86-12bb97331649.png

設計中,各個命令單獨寫出控制器,通過多路選擇器選擇出對應的命令,然后控制spi_8bit_drive將數據按照spi的協議發送出去。各個命令的脈沖通過ctrl模塊進行控制各個命令控制器,寫入的數據首先寫入到寫緩沖區,讀出的數據讀出后寫入到讀緩沖區。

暫不分配的端口,在應用時都是由上游模塊進行控制,本設計測試時,編寫上游模塊進行測試。

各個模塊的功能,和連接線的功能在各個模塊設計中說明。

spi_8bit_drive設計實現

本模塊負責將8bit的并行數據按照spi協議發送出去,以及負責按照spi協議接收數據,將接收的數據(8bit)并行傳輸給各個模塊。

spi_send_en為發送數據使能信號(脈沖信號),spi_send_data為所要發送數據,spi_send_done為發送完成信號(脈沖信號)。

spi_read_en為接收數據使能信號(脈沖信號),spi_read_data為所接收的數據,spi_read_done為接收完成信號(脈沖信號)。

在發送邏輯控制中,全部的信號采用下降沿驅動。利用外部給予的spi_send_en作為啟動信號,啟動send_cnt。send_cnt在不發送數據時為8,發送數據時,從0到7。

在接收邏輯中,全部的信號采用上升沿驅動。利用外部給予的spi_read_en作為啟動信號,啟動rec_en,經過移位接收數據。

在spi_sclk輸出時,采用組合邏輯。由于設計采用spi的模式0,故而spi_sclk不發送或者接收數據時為0,接收數據時為時鐘信號。因為要求為模式0,所以在接收數據時,spi_sclk的輸出不能夠先有下降沿,即要求spi_sclk的控制信號不能由上升沿信號驅動,所以將rec_en同步到下降沿的rec_en_n。

仿真代碼為:

`timescale 1ns/1ps

module spi_8bit_drive_tb;

reg clk; reg rst_n; reg spi_send_en; reg [7:0] spi_send_data; wire spi_send_done; reg spi_read_en; wire [7:0] spi_read_data; wire spi_read_done; wire spi_sclk; wire spi_mosi; reg spi_miso;

spi_8bit_drive spi_8bit_drive_inst(

.clk (clk), .rst_n (rst_n), .spi_send_en (spi_send_en), .spi_send_data (spi_send_data), .spi_send_done (spi_send_done), .spi_read_en (spi_read_en), .spi_read_data (spi_read_data), .spi_read_done (spi_read_done), .spi_sclk (spi_sclk), .spi_mosi (spi_mosi), .spi_miso (spi_miso) ); initial clk = 1‘b0; always # 50 clk = ~clk; initial begin rst_n = 1’b0; spi_send_en = 1‘b0; spi_send_data = 8’d0; spi_read_en = 1‘b0; spi_miso = 1’b0; # 201 rst_n = 1‘b1; # 200 @ (posedge clk); # 2; spi_send_en = 1’b1; spi_send_data = {$random} % 256; @ (posedge clk); # 2; spi_send_en = 1‘b0; spi_send_data = 8’d0; @ (posedge spi_send_done); # 2000 @ (posedge clk); # 2; spi_read_en = 1‘b1; @ (posedge clk); # 2; spi_read_en = 1’b0; @ (posedge spi_read_done); # 200 $stop; end always @ (negedge clk) spi_miso 《= {$random} % 2;

endmodule

在仿真中,將時鐘設置為10MHz。

所有的信號采用上升沿驅動。發送一個8bit的隨機數值,接收一個8bit的隨機數值。

spi_miso信號為從機下降沿驅動信號。

通過RTL仿真,可以看出發送和接收全部正常。

mux7_1設計實現

本模塊負責將7個命令模塊發出的命令(寫使能、寫數據和讀使能)經過選擇發送給spi_8bit_drive模塊。

module mux7_1 (

input wire rdsr_send_en, input wire [7:0] rdsr_send_data, input wire rdsr_read_en, input wire pp_send_en, input wire [7:0] pp_send_data, input wire wren_send_en, input wire [7:0] wren_send_data, input wire be_send_en, input wire [7:0] be_send_data, input wire se_send_en, input wire [7:0] se_send_data, input wire rdid_send_en, input wire [7:0] rdid_send_data, input wire rdid_read_en, input wire read_send_en, input wire [7:0] read_send_data, input wire read_read_en, input wire [2:0] mux_sel, output reg spi_send_en, output reg [7:0] spi_send_data, output reg spi_read_en);

always @ * begin case (mux_sel) 3‘d0 : begin spi_send_en = rdsr_send_en; spi_send_data = rdsr_send_data; spi_read_en = rdsr_read_en; end 3’d1 : begin spi_send_en = pp_send_en; spi_send_data = pp_send_data; spi_read_en = 1‘b0; end 3’d2 : begin spi_send_en = wren_send_en; spi_send_data = wren_send_data; spi_read_en = 1‘b0; end 3’d3 : begin spi_send_en = be_send_en; spi_send_data = be_send_data; spi_read_en = 1‘b0; end 3’d4 : begin spi_send_en = se_send_en; spi_send_data = se_send_data; spi_read_en = 1‘b0; end 3’d5 : begin spi_send_en = rdid_send_en; spi_send_data = rdid_send_data; spi_read_en = rdid_read_en; end 3‘d6 : begin spi_send_en = read_send_en; spi_send_data = read_send_data; spi_read_en = read_read_en; end default : begin spi_send_en = 1’b0; spi_send_data = 8‘d0; spi_read_en = 1’b0; end endcase end

endmodule

在設計中,有的命令模塊不需要進行讀取(pp和se等等),此時將輸出的讀使能信號輸出為低電平。

be設計實現

該模塊接收到be_en(整片擦除的脈沖信號)信號后,發送對應的使能和數據,等待發送完成脈沖。發送完成后,輸出擦除完成的脈沖。

module be (

input wire clk, input wire rst_n, input wire be_en, output reg be_done, output reg be_send_en, output wire [7:0] be_send_data, input wire spi_send_done);

always @ (posedge clk, negedge rst_n) begin if (rst_n == 1‘b0) be_send_en 《= 1’b0; else be_send_en 《= be_en; end assign be_send_data = 8‘hc7;

always @ (posedge clk, negedge rst_n) begin if (rst_n == 1’b0) be_done 《= 1‘b0; else be_done 《= spi_send_done; end endmodule

整片擦除的命令為8’hc7。

wren設計實現

該模塊接收到wren_en(打開flash內部的寫使能的脈沖信號)信號后,發送對應的使能和數據,等待發送完成脈沖。發送完成后,輸出擦除完成的脈沖。

module wren (

input wire clk, input wire rst_n, input wire wren_en, output reg wren_done, output reg wren_send_en, output wire [7:0] wren_send_data, input wire spi_send_done);

always @ (posedge clk, negedge rst_n) begin if (rst_n == 1’b0) wren_send_en 《= 1‘b0; else wren_send_en 《= wren_en; end assign wren_send_data = 8’h06;

always @ (posedge clk, negedge rst_n) begin if (rst_n == 1‘b0) wren_done 《= 1’b0; else wren_done 《= spi_send_done; end endmodule

打開flash內部寫使能的命令碼為8’h06。

se設計實現

該模塊接收到se_en(擦除扇區的寫使能的脈沖信號)信號后,發送對應的使能和數據,等待發送完成脈沖。發送完成后,接著發送高八位地址,中間八位地址和低八位地址。全部發送完成后,發送se_done信號。

該模塊采用狀態機實現。SE_STATE(扇區擦除命令發送)、H_ADDR(高八位地址發送)、M_ADDR(中間八位地址發送)、L_ADDR(低八位地址發送)、SE_DONE(扇區擦除完成)。所有的脈沖信號在未標注的時刻,輸出全部為0。

7bbea5a2-9dac-11eb-8b86-12bb97331649.png

在發送過程中,由于是每8bit發送一次,所以在時序上將看到發送時,每8個脈沖一組,中間會有明顯的間隔。

pp設計實現

該模塊負責將外部寫fifo中的數據寫入到flash中。wr_fifo_rd為寫fifo的讀使能信號,wrdata為從寫fifo中讀出的數據,wr_len為需要寫入flash中數據的長度,wr_addr為寫入地址。

該模塊采用狀態機實現。PP_STATE(發送pp命令),H_ADDR(發送高八位地址)、M_ADDR(發送中間八位地址),L_ADDR(發送低八位地址)、RDFIFO(讀寫fifo)、FIFO_WAIT(等待讀寫fifo的數據輸出)、SEND(發送8bit數據)、SEND_WAIT(發送等待,發送完成后判斷是否發送完成)。對于所有的脈沖信號,沒有賦值的位置,全部賦值為0。

cnt為記錄已經發送的數據個數。

7be60b7e-9dac-11eb-8b86-12bb97331649.png

rdsr設計實現

本模塊的功能為讀取m25p16的狀態寄存器,主要檢測狀態寄存器的最低位(WIP)。

WIP(write in progress :正在進行寫進程),該bie位表示了flash內部是否在進行寫進程。如果處于寫進程時,flash忽略外部所有的命令,所以建議在執行任何命令前,首先進行檢測該位。1表示正在寫進程中,0表示不處于寫進程。

如果檢測到正在寫進程中,進行延遲1ms,然后再次讀取該位狀態。直到寫進程結束。

本模塊采用狀態機設計實現。ILDE(發送讀狀態寄存器命令)、RDSRSTATE(發送讀使能)、WIP(判斷wip位)、 DELAY1ms(延遲1ms)。cnt為延遲1ms的計數器。

7bef950e-9dac-11eb-8b86-12bb97331649.png

rdid設計實現

該模塊負責讀取flash的ID(2015),驗證ID的正確性。

該模塊采用狀態機的方式實現。IDLE(等待讀取ID的命令)、IDSTATE1(讀取高八位ID)、IDSTATE2(讀取中間八位ID)、IDSTATE3(讀取低八位ID)、ID_CHECK(檢測ID的正確性)。

狀態轉移圖如下:

7c1c8ca8-9dac-11eb-8b86-12bb97331649.png

設計代碼為:

module rdid (

input wire clk, input wire rst_n, input wire rdid_en, output reg rdid_done, output reg rdid_send_en, output reg [7:0] rdid_send_data, input wire spi_send_done, output reg rdid_read_en, input wire spi_read_done, input wire [7:0] spi_read_data);

localparam IDLE = 5’b00001; localparam IDSTATE1 = 5‘b00010; localparam IDSTATE2 = 5’b00100; localparam IDSTATE3 = 5‘b01000; localparam ID_CHECK = 5’b10000; reg [4:0] c_state; reg [4:0] n_state; always @ (posedge clk, negedge rst_n) begin if (rst_n == 1‘b0) c_state 《= IDLE; else c_state 《= n_state; end always @ * begin case (c_state) IDLE : begin if (rdid_en == 1’b1) n_state = IDSTATE1; else n_state = IDLE; end IDSTATE1 : begin if (spi_send_done == 1‘b1) n_state = IDSTATE2; else n_state = IDSTATE1; end IDSTATE2 : begin if (spi_read_done == 1’b1 && spi_read_data == 8‘h20) n_state = IDSTATE3; else n_state = IDSTATE2; end IDSTATE3 : begin if (spi_read_done == 1’b1 && spi_read_data == 8‘h20) n_state = ID_CHECK; else n_state = IDSTATE3; end ID_CHECK : begin if (spi_read_done == 1’b1 && spi_read_data == 8‘h15) n_state = IDLE; else n_state = ID_CHECK; end default : n_state = IDLE; endcase end

always @ (posedge clk, negedge rst_n) begin if (rst_n == 1’b0) rdid_send_data 《= 8‘d0; else if (c_state == IDLE && rdid_en == 1’b1) rdid_send_data 《= 8‘h9f; else rdid_send_data 《= 8’d0; end always @ (posedge clk, negedge rst_n) begin if (rst_n == 1‘b0) rdid_send_en 《= 1’b0; else if (c_state == IDLE && rdid_en == 1‘b1) rdid_send_en 《= 1’b1; else rdid_send_en 《= 1‘b0; end always @ (posedge clk, negedge rst_n) begin if (rst_n == 1’b0) rdid_read_en 《= 1‘b0; else if ((c_state == IDSTATE1 && spi_send_done == 1’b1) || (c_state == IDSTATE2 && spi_read_done == 1‘b1 && spi_read_data == 8’h20) || (c_state == IDSTATE3 && spi_read_done == 1‘b1 && spi_read_data == 8’h20)) rdid_read_en 《= 1‘b1; else rdid_read_en 《= 1’b0; end always @ (posedge clk, negedge rst_n) begin if (rst_n == 1‘b0) rdid_done 《= 1’b0; else if (c_state == ID_CHECK && spi_read_done == 1‘b1 && spi_read_data == 8’h15) rdid_done 《= 1‘b1; else rdid_done 《= 1’b0; end endmodule

read_ctrl設計實現

該模塊負責將flash的數據讀出,寫入到輸出緩存中。

該模塊采用狀態機實現。RD_STATE(等待讀命令)、H_ADDR(發送高八位地址)、M_ADDR(發送中間八位地址)、L_ADDR(發送低八位地址)、RDDATA(讀取數據)、WRFIFO(將讀出的數據寫入到FIFO中)、CHECK_LEN(判斷讀取的長度)。

狀態轉移圖如下:

7c570e6e-9dac-11eb-8b86-12bb97331649.png

wr_fifo和rd_fifo調用

兩個fifo的寬度設置為8,深度設置為256,同步fifo,帶有復位。

ctrl設計實現

該模塊根據外部的命令,按照m25p16的執行規則,進行控制各個模塊的執行。

該模塊采用狀態機實現。INIT_RDSR(讀WIP),INIT_RDID(讀ID),INIT_ID(判斷ID),WIP(讀WIP),WIP_DONE(等待WIP),IDLE(空閑狀態),**STATE(執行對應的命令),**WREN(打開flash的寫使能)。在進行任何命令前,都檢查wip。

狀態轉移圖如下:

7c688392-9dac-11eb-8b86-12bb97331649.png

在不同的狀態,mux_sel選擇對應的命令通過。

drive_busy只有在IDLE狀態才是低電平。

spi_cs_n信號, DLE狀態為高電平、WIP_DONE(INIT_RDID)中spi_read_done信號為高時 (保證能夠多次讀取狀態寄存器)、在其他狀態發生切換時,spi_cs_n 為高電平,否則為低電平。

m25p16_drive設計實現

本模塊負責連接所有二級模塊,實現所有的功能。

module m25p16_drive (

input wire clk, input wire rst_n, input wire wrfifo_wr, input wire [7:0] wrfifo_data, input wire flag_be, input wire flag_se, input wire flag_wr, input wire flag_rd, input wire [23:0] addr, input wire [8:0] len, input wire rdfifo_rd, output wire [7:0] rdfifo_rdata, output wire rdfifo_rdempty, output wire drive_busy, output wire spi_cs_n, output wire spi_sclk, output wire spi_mosi, input wire spi_miso);

wire spi_send_en; wire [7:0] spi_send_data; wire spi_send_done; wire spi_read_en; wire [7:0] spi_read_data; wire spi_read_done; wire rdsr_send_en; wire [7:0] rdsr_send_data; wire rdsr_read_en; wire pp_send_en; wire [7:0] pp_send_data; wire wren_send_en; wire [7:0] wren_send_data; wire be_send_en; wire [7:0] be_send_data; wire se_send_en; wire [7:0] se_send_data; wire rdid_send_en; wire [7:0] rdid_send_data; wire rdid_read_en; wire read_send_en; wire [7:0] read_send_data; wire read_read_en; wire [2:0] mux_sel; wire be_en; wire be_done; wire wren_en; wire wren_done; wire se_en; wire [23:0] se_addr; wire se_done; wire pp_en; wire pp_done; wire wr_fifo_rd; wire [7:0] wrdata; wire [8:0] wr_len; wire [23:0] wr_addr; wire rdsr_en; wire rdsr_done; wire rdid_en; wire rdid_done; wire read_en; wire [23:0] rd_addr; wire [8:0] rd_len; wire [7:0] rddata; wire rd_fifo_wr; wire read_done; ctrl ctrl_inst(

.clk (clk), .rst_n (rst_n), .flag_be (flag_be), .flag_se (flag_se), .flag_wr (flag_wr), .flag_rd (flag_rd), .addr (addr), .len (len), .drive_busy (drive_busy), .spi_cs_n (spi_cs_n), .spi_read_done (spi_read_done), .rdsr_en (rdsr_en), .rdsr_done (rdsr_done), .wren_en (wren_en), .wren_done (wren_done), .pp_en (pp_en), .wr_addr (wr_addr), .wr_len (wr_len), .pp_done (pp_done), .be_en (be_en), .be_done (be_done), .se_en (se_en), .se_addr (se_addr), .se_done (se_done), .rdid_en (rdid_en), .rdid_done (rdid_done), .read_en (read_en), .rd_addr (rd_addr), .rd_len (rd_len), .read_done (read_done), .mux_sel (mux_sel) ); rd_fifo rd_fifo_inst ( .aclr ( ~rst_n ), .clock ( clk ), .data ( rddata ), .rdreq ( rdfifo_rd ), .wrreq ( rd_fifo_wr ), .empty ( rdfifo_rdempty ), .q ( rdfifo_rdata ) ); wr_fifo wr_fifo_inst ( .aclr ( ~rst_n ), .clock ( clk ), .data ( wrfifo_data ), .rdreq ( wr_fifo_rd ), .wrreq ( wrfifo_wr ), .q ( wrdata ) ); read_ctrl read_ctrl_inst(

.clk (clk), .rst_n (rst_n), .read_en (read_en), .rd_addr (rd_addr), .rd_len (rd_len), .rddata (rddata), .rd_fifo_wr (rd_fifo_wr), .read_done (read_done), .read_send_en (read_send_en), .read_send_data (read_send_data), .spi_send_done (spi_send_done), .read_read_en (read_read_en), .spi_read_done (spi_read_done), .spi_read_data (spi_read_data) );

rdid rdid_inst(

.clk (clk), .rst_n (rst_n), .rdid_en (rdid_en), .rdid_done (rdid_done), .rdid_send_en (rdid_send_en), .rdid_send_data (rdid_send_data), .spi_send_done (spi_send_done), .rdid_read_en (rdid_read_en), .spi_read_done (spi_read_done), .spi_read_data (spi_read_data) ); rdsr rdsr_inst(

.clk (clk), .rst_n (rst_n), .rdsr_en (rdsr_en), .rdsr_done (rdsr_done), .rdsr_send_en (rdsr_send_en), .rdsr_send_data (rdsr_send_data), .spi_send_done (spi_send_done), .rdsr_read_en (rdsr_read_en), .spi_read_data (spi_read_data), .spi_read_done (spi_read_done) ); pp pp_inst(

.clk (clk), .rst_n (rst_n), .pp_en (pp_en), .pp_done (pp_done), .wr_fifo_rd (wr_fifo_rd), .wrdata (wrdata), .wr_len (wr_len), .wr_addr (wr_addr), .pp_send_en (pp_send_en), .pp_send_data (pp_send_data), .spi_send_done (spi_send_done) ); se se_inst(

.clk (clk), .rst_n (rst_n), .se_en (se_en), .se_addr (se_addr), .se_done (se_done), .se_send_en (se_send_en), .se_send_data (se_send_data), .spi_send_done (spi_send_done) ); wren wren_inst(

.clk (clk), .rst_n (rst_n), .wren_en (wren_en), .wren_done (wren_done), .wren_send_en (wren_send_en), .wren_send_data (wren_send_data), .spi_send_done (spi_send_done) );

be be_inst(

.clk (clk), .rst_n (rst_n), .be_en (be_en), .be_done (be_done), .be_send_en (be_send_en), .be_send_data (be_send_data), .spi_send_done (spi_send_done) ); mux7_1 mux7_1_inst(

.rdsr_send_en (rdsr_send_en), .rdsr_send_data (rdsr_send_data), .rdsr_read_en (rdsr_read_en), .pp_send_en (pp_send_en), .pp_send_data (pp_send_data), .wren_send_en (wren_send_en), .wren_send_data (wren_send_data), .be_send_en (be_send_en), .be_send_data (be_send_data), .se_send_en (se_send_en), .se_send_data (se_send_data), .rdid_send_en (rdid_send_en), .rdid_send_data (rdid_send_data), .rdid_read_en (rdid_read_en), .read_send_en (read_send_en), .read_send_data (read_send_data), .read_read_en (read_read_en), .mux_sel (mux_sel), .spi_send_en (spi_send_en), .spi_send_data (spi_send_data), .spi_read_en (spi_read_en) ); spi_8bit_drive spi_8bit_drive_inst(

.clk (clk), .rst_n (rst_n), .spi_send_en (spi_send_en), .spi_send_data (spi_send_data), .spi_send_done (spi_send_done), .spi_read_en (spi_read_en), .spi_read_data (spi_read_data), .spi_read_done (spi_read_done), .spi_sclk (spi_sclk), .spi_mosi (spi_mosi), .spi_miso (spi_miso) ); endmodule

RTL仿真

本次設計涉及到讀取flash的id以及狀態寄存器,所以在仿真時需要加入仿真模型。仿真模型放在msim的m25p16_sim_module中。m25p16為仿真模型的頂層文件。

由于讀寫和擦除的時間較長,RTL仿真中,將只仿真RDSR和RDID,其他的功能測試在板級測試時進行。

仿真代碼如下:

`timescale 1ns/1ps

module m25p16_drive_tb;

reg clk; reg rst_n; wire drive_busy; wire spi_cs_n; wire spi_sclk; wire spi_mosi; wire spi_miso;

m25p16_drive m25p16_drive_inst(

.clk (clk), .rst_n (rst_n), .wrfifo_wr (1’b0), .wrfifo_data (8‘d0), .flag_be (1’b0), .flag_se (1‘b0), .flag_wr (1’b0), .flag_rd (1‘b0), .addr (24’d0), .len (9‘d0), .rdfifo_rd (1’b0), .rdfifo_rdata (), .rdfifo_rdempty (), .drive_busy (drive_busy), .spi_cs_n (spi_cs_n), .spi_sclk (spi_sclk), .spi_mosi (spi_mosi), .spi_miso (spi_miso) );

m25p16 m25p16_inst( .c (spi_sclk), .data_in (spi_mosi), .s (spi_cs_n), .w (1‘b1), .hold (1’b1), .data_out (spi_miso) );

initial clk = 1‘b0; always # 50 clk = ~clk; initial begin rst_n = 1’b0; # 201 rst_n = 1‘b1; @ (negedge drive_busy); # 2000 $stop; end

endmodule

在設置testbench時,注意將所有文件全部添加到文件中。

7c7deb6a-9dac-11eb-8b86-12bb97331649.png

選擇testbench時,注意選中設置的m25p16_drive_tb。

7c8a6192-9dac-11eb-8b86-12bb97331649.png

利用modelsim仿真,可以得出如下RTL仿真波形。

7c994770-9dac-11eb-8b86-12bb97331649.png

讀到ID,以及檢測WIP都是正確的。

板級測試

由于m25p16的時序原因,整個設計工作在10MHz(利用PLL產生)。

在進行測試控制時,對最后一個扇區進行擦除;對最后一個扇區的第一頁進行寫入數據100個(1至100);對最后一個扇區的第一個進行讀取,驗證數據是否為1至100。

測試的控制模塊命名為test_ctrl。

此模塊采用狀態機實現。WRFIFO(將1至100寫入wrfifo中)、SE(扇區擦除)、PP(寫入flash)、RD(讀出flash)、WAIT_RD(等待讀取)、CHECK( 檢測讀出的數據的正確性)。

7caa3df0-9dac-11eb-8b86-12bb97331649.png

設計代碼為:

module test_ctrl (

input wire clk, input wire rst_n, output reg wrfifo_wr, output reg [7:0] wrfifo_data, output reg flag_se, output reg flag_wr, output reg flag_rd, input wire drive_busy, output reg rdfifo_rd, input wire [7:0] rdfifo_rdata);

localparam WRFIFO = 6’b000_001; localparam SE = 6‘b000_010; localparam PP = 6’b000_100; localparam RD = 6‘b001_000; localparam WAIT_RD = 6’b010_000; localparam CHECK = 6‘b100_000; reg [5:0] c_state; reg [5:0] n_state; always @ (posedge clk, negedge rst_n) begin if (rst_n == 1’b0) c_state 《= WRFIFO; else c_state 《= n_state; end always @ * begin case (c_state) WRFIFO : begin if (wrfifo_data == 8‘d100) n_state = SE; else n_state = WRFIFO; end SE : begin if (drive_busy == 1’b0) n_state = PP; else n_state = SE; end PP : begin if (drive_busy == 1‘b0) n_state = RD; else n_state = PP; end RD : begin if (drive_busy == 1’b0) n_state = WAIT_RD; else n_state = RD; end WAIT_RD : begin if (drive_busy == 1‘b0) n_state = CHECK; else n_state = WAIT_RD; end

CHECK : begin n_state = CHECK; end

default : n_state = WRFIFO; endcase end always @ (posedge clk, negedge rst_n) begin if (rst_n == 1’b0) wrfifo_data 《= 8‘d0; else if (c_state == WRFIFO && wrfifo_data 《 8’d100) wrfifo_data 《= wrfifo_data + 1‘b1; else wrfifo_data 《= 8’d0; end always @ (posedge clk, negedge rst_n) begin if (rst_n == 1‘b0) wrfifo_wr 《= 1’d0; else if (c_state == WRFIFO && wrfifo_data 《 8‘d100) wrfifo_wr 《= 1’d1; else wrfifo_wr 《= 1‘d0; end always @ (posedge clk, negedge rst_n) begin if (rst_n == 1’b0) flag_se 《= 1‘b0; else if (c_state == SE && drive_busy == 1’b0) flag_se 《= 1‘b1; else flag_se 《= 1’b0; end always @ (posedge clk, negedge rst_n) begin if (rst_n == 1‘b0) flag_wr 《= 1’b0; else if (c_state == PP && drive_busy == 1‘b0) flag_wr 《= 1’b1; else flag_wr 《= 1‘b0; end

always @ (posedge clk, negedge rst_n) begin if (rst_n == 1’b0) flag_rd 《= 1‘b0; else if (c_state == RD && drive_busy == 1’b0) flag_rd 《= 1‘b1; else flag_rd 《= 1’b0; end always @ (posedge clk, negedge rst_n) begin if (rst_n == 1‘b0) rdfifo_rd 《= 1’b0; else if (c_state == WAIT_RD && drive_busy == 1‘b0) rdfifo_rd 《= 1’b1; else if (c_state == CHECK && rdfifo_rdata == 8‘d99) rdfifo_rd 《= 1’b0; else rdfifo_rd 《= rdfifo_rd; end endmodule

將test模塊設置為頂層。在test模塊中,m25p16_drive例化中,對于整片擦除不做控制,對于addr直接指向最后一個扇區的第一頁,len指定為100。

代碼為:

module test (

input wire clk, input wire rst_n, output wire spi_cs_n, output wire spi_sclk, output wire spi_mosi, input wire spi_miso);

wire wrfifo_wr; wire [7:0] wrfifo_data; wire flag_rd; wire flag_se; wire flag_wr; wire drive_busy; wire rdfifo_rd; wire [7:0] rdfifo_rdata; wire clk_10m; wire pll_locked; pll_test pll_test_inst ( .areset ( ~rst_n ), .inclk0 ( clk ), .c0 ( clk_10m ), .locked ( pll_locked ) );

test_ctrl test_ctrl_inst(

.clk (clk_10m), .rst_n (pll_locked), .wrfifo_wr (wrfifo_wr), .wrfifo_data (wrfifo_data), .flag_se (flag_se), .flag_wr (flag_wr), .flag_rd (flag_rd), .drive_busy (drive_busy), .rdfifo_rd (rdfifo_rd), .rdfifo_rdata (rdfifo_rdata) ); m25p16_drive m25p16_drive_inst(

.clk (clk_10m), .rst_n (pll_locked), .wrfifo_wr (wrfifo_wr), .wrfifo_data (wrfifo_data), .flag_be (1‘b0), .flag_se (flag_se), .flag_wr (flag_wr), .flag_rd (flag_rd), .addr (24’hff0000), .len (9‘d100), .rdfifo_rd (rdfifo_rd), .rdfifo_rdata (rdfifo_rdata), .rdfifo_rdempty (), .drive_busy (drive_busy), .spi_cs_n (spi_cs_n), .spi_sclk (spi_sclk), .spi_mosi (spi_mosi), .spi_miso (spi_miso) ); endmodule

由于開發板上的flash是為FPGA進行保存配置信息的,所以管腳都連接在專用管腳上,本次實驗需要將這專用管腳配置為普通io。

右擊器件型號,選擇device。

7cba9bdc-9dac-11eb-8b86-12bb97331649.png

點擊device and pin options。

7cf1fa46-9dac-11eb-8b86-12bb97331649.png

選擇Dual-purpose pins,將其中所有的功能改為普通IO。

7d102426-9dac-11eb-8b86-12bb97331649.png

點擊ok后,即可進行綜合分析。

連接開發板和PC,打開邏輯分析儀。

采樣時鐘選擇10MHz(PLL 的c0),采樣深度設置為2K。

7d232008-9dac-11eb-8b86-12bb97331649.png

觀測信號如下圖所示。

7d2f204c-9dac-11eb-8b86-12bb97331649.png

首先將wrfifo_wr的觸發條件設置為上升沿。點擊觸發后,按下復位按鍵。觸發后,可以看到寫入數據1至100后,然后進行SE命令。

7d4436ee-9dac-11eb-8b86-12bb97331649.png

將rdfifo_rd的觸發條件設置為上升沿(將wrfifo_wr觸發條件修改為donot care)。點擊觸發后,按下復位按鍵。

7d7eff72-9dac-11eb-8b86-12bb97331649.png

通過仿真和下板實測,驗證控制器設計正確。

原文標題:FPGA零基礎學習精選 | SPI 協議驅動設計

文章出處:【微信公眾號:FPGA技術江湖】歡迎添加關注!文章轉載請注明出處。

責任編輯:haq

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • FPGA
    +關注

    關注

    1630

    文章

    21759

    瀏覽量

    604377
  • 驅動
    +關注

    關注

    12

    文章

    1844

    瀏覽量

    85367
  • SPI
    SPI
    +關注

    關注

    17

    文章

    1711

    瀏覽量

    91774

原文標題:FPGA零基礎學習精選 | SPI 協議驅動設計

文章出處:【微信號:HXSLH1010101010,微信公眾號:FPGA技術江湖】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    ADS7223用的是spi協議,將SDI數據讀入寄存器,可是spi的clk是一直存在的,怎么判斷第20個時鐘到來的時刻呢?

    1、ADS7223用的是spi協議,要往寄存器里面寫值,在第20個spi clk加RD信號,將SDI數據讀入寄存器,可是spi 的clk
    發表于 01-08 07:56

    spi master接口的fpga實現

    串行外圍接口 大致了解: spi是個同步協議,數據在master和slaver間交換通過時鐘sck,由于它是同步協議,時鐘速率就可以各種變換。 sck:主機提供,從機不能操控,從器件由主機產生的時鐘控制。數據只有在sck來了的
    的頭像 發表于 11-16 10:35 ?467次閱讀
    <b class='flag-5'>spi</b> master接口的<b class='flag-5'>fpga</b>實現

    采用Xilinx FPGA的AFE79xx SPI啟動指南

    電子發燒友網站提供《采用Xilinx FPGA的AFE79xx SPI啟動指南.pdf》資料免費下載
    發表于 11-15 15:28 ?0次下載
    采用Xilinx <b class='flag-5'>FPGA</b>的AFE79xx <b class='flag-5'>SPI</b>啟動指南

    PCM5142如何在FPGA通過SPI配置寄存器?

    工作正常)。左右通道均沒有輸出;(xsmt/mode1已拉高) 2、如何在FPGA通過SPI配置寄存器,文檔只有page0 R1,并沒有具體寄存器的地址。通過PPS生成的image
    發表于 10-31 07:29

    DS1302芯片與FPGA之間SPI通信原理

    本文通過以DS1302芯片為基礎,介紹該芯片與FPGA之間SPI通信原理,詳細描述硬件設計原理及FPGA SPI接口驅動設計。
    的頭像 發表于 10-24 14:16 ?526次閱讀
    DS1302芯片與<b class='flag-5'>FPGA</b>之間<b class='flag-5'>SPI</b>通信原理

    瀚海微SD NAND之SD 協議(36)SPI模式

    簡介 SPI模式由基于flash的SD存儲卡提供的輔助通信協議組成。 這種模式是SD存儲卡協議的一個子集,設計用于與SPI通道通信,通常在摩托羅拉(以及最近一些其他供應商)的微控制器中
    的頭像 發表于 10-08 10:13 ?333次閱讀
    瀚海微SD NAND之SD <b class='flag-5'>協議</b>(36)<b class='flag-5'>SPI</b>模式

    SPI通信協議的基本概念和工作模式

    SPI(Serial Peripheral Interface)通信協議,即串行外圍設備接口,是一種高速、全雙工、同步通信總線,由摩托羅拉公司提出并廣泛應用。SPI以其高效的數據傳輸能力和簡單的硬件
    的頭像 發表于 09-09 17:04 ?1762次閱讀

    Verilog:【8】基于FPGA實現SD NAND FLASH的SPI協議讀寫

      2 SD NAND FLASH背景介紹   3 樣品申請   4 電路結構與接口協議   4.1 SD NAND   4.2 SD NAND測試板   4.3 FPGA開發板   5 SD卡協議
    發表于 06-21 17:58

    FPGA的單總線協議設計(附示例代碼)

    FPGA(現場可編程門陣列)是一種高度靈活的集成電路,通過編程可以實現多種數字功能。在FPGA實現單總線協議可以有效地簡化模塊之間的通信。單總線
    的頭像 發表于 05-31 08:21 ?732次閱讀
    <b class='flag-5'>FPGA</b><b class='flag-5'>中</b>的單總線<b class='flag-5'>協議</b>設計(附示例代碼)

    FPGA設計,對SPI進行參數化結構設計

    今天給大俠帶來FPGA設計,對SPI進行參數化結構設計,話不多說,上貨。 為了避免每次SPI驅動重寫,直接參數化,盡量一勞永逸。
    發表于 05-07 16:09

    SPI和I2C通信協議:應用與區別

    本文深入解析了SPI和I2C這兩種通信協議的特點、工作原理和應用場景。SPI適用于高速數據傳輸,常用于存儲器芯片和顯示器驅動等領域;I2C適用于低速控制和傳感器數據傳輸,常用于溫度傳感
    的頭像 發表于 04-22 16:45 ?1887次閱讀

    FPGA設計,對SPI進行參數化結構設計

    今天給大俠帶來FPGA設計,對SPI進行參數化結構設計,話不多說,上貨。 為了避免每次SPI驅動重寫,直接參數化,盡量一勞永逸。
    發表于 04-11 18:29

    FPGA設計SPI的參數化結構設計方法

    為了避免每次SPI驅動重寫,直接參數化,盡量一勞永逸。SPI master有啥用呢,你發現各種外圍芯片的配置一般都是通過SPI配置的,只不過有三線和四線。
    發表于 04-02 10:01 ?913次閱讀
    <b class='flag-5'>FPGA</b>設計<b class='flag-5'>中</b><b class='flag-5'>SPI</b>的參數化結構設計方法

    基于UVC協議FPGA傳過來的數據在Cyusb3014怎么獲取?

    基于UVC協議FPGA傳過來的數據在Cyusb3014怎么獲取,想讀取這部分視頻數據,謝謝
    發表于 02-29 07:44

    在PSoC? 6SPI主控如何在SPI協議手動控制SC引腳?

    嗨,我在我的項目中使用 PSoC? 6 和 Wiznet w5500 以太網模塊。 根據 W5500 數據表,我們需要手動控制 SC 引腳或使用 SPI 協議進行軟件驅動。當我將 PDL 庫用于
    發表于 01-18 08:53
    主站蜘蛛池模板: 红色一级毛片| www.99色.com| 欧美一区二区三区四区视频| 欧美一区二区三区黄色| 女人张开腿让男人做爽爽| 美女18黄| 国产精品久久新婚兰兰| 9999毛片免费看| 天天拍天天色| 国产免费资源| 深爱五月婷婷| 羞涩妩媚玉腿呻吟嗯啊销魂迎合| 亚洲六月婷婷| 神马电影天堂网| 你懂的在线视频播放| 久久免费视频精品| 国产国产人免费人成成免视频 | 免费男女| 一级黄色日本| 日韩一二三级| 国产日韩欧美综合色视频在线| 成人a在线| 国产一卡二卡≡卡四卡无人| 成人欧美一区二区三区视频不卡 | 色窝网| 久青草视频免费视频播放线路1| 国产va精品免费观看| 美女免费视频是黄的| 国产美女在线精品免费观看| 亚洲精品午夜视频| 国产伦精品一区二区三区| 六月婷婷综合网| 激情六月婷婷| 午夜视频免费看| 97天天做天天爱夜夜爽| 日本不卡一区二区三区在线观看| 亚洲乱码中文字幕综合| 免费高清一级欧美片在线观看| 一级特黄视频| 日本人xxxxxxxxxⅹ68| 一级日本高清视频免费观看|