【接口時(shí)序】QSPI Flash的原理與QSPI時(shí)序的Verilog實(shí)現(xiàn)
一、 軟件平臺(tái)與硬件平臺(tái)
軟件平臺(tái):
1、操作系統(tǒng):Windows-8.1
2、開(kāi)發(fā)套件:ISE14.7
3、仿真工具:ModelSim-10.4-SE
4、Matlab版本:Matlab2014b/Matlab2016a
硬件平臺(tái):
1、 FPGA型號(hào):Xilinx公司的XC6SLX45-2CSG324
2、 Flash型號(hào):WinBond公司的W25Q128BV?? Quad SPI Flash存儲(chǔ)器
提示:如果圖片不清晰,請(qǐng)把圖片在瀏覽器的新建標(biāo)簽頁(yè)打開(kāi)或保存到本地打開(kāi)。
二、 原理介紹
上一篇博客《SPI總線的原理與FPGA實(shí)現(xiàn)》中已經(jīng)有關(guān)于標(biāo)準(zhǔn)SPI協(xié)議的原理與時(shí)序的介紹,這里不再贅述。本節(jié)主要是討論QSPI(Quad SPI,四線SPI總線)的相關(guān)內(nèi)容。我的開(kāi)發(fā)板上有一片型號(hào)是W25Q128BV的Quad SPI Flash存儲(chǔ)器,本文將以它為例子來(lái)說(shuō)明QSPI操作的一些內(nèi)容。
W25Q128BV的Quad SPI Flash存儲(chǔ)器的Top View如下圖所示
這塊芯片一共有8個(gè)有用的管腳,其每個(gè)管腳的功能定義如下圖所示
由上圖可知2號(hào)管腳DO(IO1),3號(hào)管腳 /WP(IO2),5號(hào)管腳DI(IO0)以及7號(hào)管腳/HOLD(IO3)均為雙向IO口,所以在編寫(xiě)Verilog代碼的時(shí)候要把它們定義為inout類(lèi)型,inout類(lèi)型的信號(hào)既可以做輸出也可以作為輸入,具體在代碼里面如何處理后文會(huì)有介紹。
QSPI Flash每個(gè)引腳的詳細(xì)描述如下:
1、Chip Select(/CS)
????? 片選信號(hào)Chip Select(/CS)的作用是使能或者不使能設(shè)備的操作,當(dāng)CS為高時(shí),表示設(shè)備未被選中,串行數(shù)據(jù)輸出線(DO或IO0,IO1,IO2,IO3)均處于高阻態(tài),當(dāng)CS為低時(shí),表示設(shè)備被選中,F(xiàn)PGA可以給QSPI Flash發(fā)送數(shù)據(jù)或從QSPI Flash接收數(shù)據(jù)。
2、串行數(shù)據(jù)輸入信號(hào)DI以及串行輸出信號(hào)DO
????? W25Q128BV支持標(biāo)準(zhǔn)SPI協(xié)議,雙線SPI(Dual SPI)協(xié)議與四線SPI(Quad SPI)協(xié)議。標(biāo)準(zhǔn)的SPI協(xié)議在串行時(shí)鐘信號(hào)(SCLK)的上升沿把串行輸入信號(hào)DI上的數(shù)據(jù)存入QSPI Flash中,在串行時(shí)鐘信號(hào)(SCLK)的下降沿把QSPI Flash中的數(shù)據(jù)串行化通過(guò)單向的DO引腳輸出。而在Dual SPI與Quad SPI中,DI與DO均為雙向信號(hào)(既可以作為輸入,也可以作為輸出)。
3、Write Project(/WP)
????? 寫(xiě)保護(hù)信號(hào)的作用是防止QSPI Flash的狀態(tài)寄存器被寫(xiě)入錯(cuò)誤的數(shù)據(jù),WP信號(hào)低電平有效,但是當(dāng)狀態(tài)寄存器2的QE位被置1時(shí),WP信號(hào)失去寫(xiě)保護(hù)功能,它變成Quad SPI的一個(gè)雙向數(shù)據(jù)傳輸信號(hào)。
4、HOLD(/HOLD)
????? HOLD信號(hào)的作用是暫停QSPI Flash的操作。當(dāng)HOLD信號(hào)為低,并且CS也為低時(shí),串行輸出信號(hào)DO將處于高阻態(tài),串行輸入信號(hào)DI與串行時(shí)鐘信號(hào)SCLK將被QSPI Flash忽略。當(dāng)HOLD拉高以后,QSPI Flash的讀寫(xiě)操作能繼續(xù)進(jìn)行。當(dāng)多個(gè)SPI設(shè)備共享同一組SPI總線相同的信號(hào)的時(shí)候,可以通過(guò)HOLD來(lái)切換信號(hào)的流向。和WP信號(hào)一樣,當(dāng)當(dāng)狀態(tài)寄存器2的QE位被置1時(shí),HOLD信號(hào)失去保持功能,它也變成Quad SPI的一個(gè)雙向數(shù)據(jù)傳輸信號(hào)。
5、串行時(shí)鐘線
????? 串行時(shí)鐘線用來(lái)提供串行輸入輸出操作的時(shí)鐘。
W25Q128BV的內(nèi)部結(jié)構(gòu)框圖如下圖所示:
更多詳細(xì)的內(nèi)容請(qǐng)閱讀W25Q128BV的芯片手冊(cè)。由于本文要進(jìn)行4線SPI的操作,但QSPI Flash默認(rèn)的操作模式是標(biāo)準(zhǔn)單線SPI模式,所以在每次進(jìn)行4線SPI操作的時(shí)候一定要先把狀態(tài)寄存器2的QE位(倒數(shù)第2位)置1,然后才能進(jìn)行QSPI操作。
最后介紹一下我的開(kāi)發(fā)板上QSPI Flash硬件原理圖如下圖所示:
三、 目標(biāo)任務(wù)
1、編寫(xiě)標(biāo)準(zhǔn)SPI 協(xié)議 Verilog代碼來(lái)操作QSPI Flash,并用ChipScope抓出各個(gè)指令的時(shí)序與芯片手冊(cè)提供的時(shí)序進(jìn)行對(duì)比
2、在標(biāo)準(zhǔn)SPI協(xié)議的基礎(chǔ)上增加Quad SPI的功能,并用ChipScope抓出Quad SPI的讀寫(xiě)數(shù)據(jù)的時(shí)序
3、對(duì)比標(biāo)準(zhǔn)SPI與Quad SPI讀寫(xiě)W25Q128BV的ChipScope時(shí)序,感受二者的效率差距
四、 設(shè)計(jì)思路與Verilog代碼編寫(xiě)
4.1、 命令類(lèi)型的定義
W25Q128BV一共有35條命令,這里不可能把所有命令的邏輯都寫(xiě)出來(lái),所以截取了一部分常用的命令作為示例來(lái)說(shuō)明QSPI Flash的操作方法。由于命令數(shù)目很多,所以在這個(gè)部分先對(duì)各個(gè)命令類(lèi)型做一個(gè)初步定義,下文的代碼就是按照這個(gè)定義來(lái)編寫(xiě)的。
命令編號(hào) |
命令類(lèi)型(自定義) |
命令碼(芯片手冊(cè)定義) |
命令功能 |
1 |
5’b0XXXX |
8’h00 |
無(wú) |
2 |
5’b10000 |
8’h90 |
讀設(shè)備ID |
3 |
5’b10001 |
8’h06 |
寫(xiě)使能 |
4 |
5’b10010 |
8’h20 |
扇區(qū)擦除 |
5 |
5’b10011 |
8’h05/8’h35 |
讀狀態(tài)寄存器1/2 |
6 |
5’b10100 |
8’h04 |
關(guān)閉寫(xiě)使能 |
7 |
5’b10101 |
8’h02 |
寫(xiě)數(shù)據(jù)操作(單線模式) |
8 |
5’b10110 |
8’h01 |
寫(xiě)狀態(tài)寄存器 |
9 |
5’b10111 |
8’h03 |
讀數(shù)據(jù)操作(單線模式) |
10 |
5’b11000 |
8’h32 |
寫(xiě)數(shù)據(jù)操作(四線模式) |
11 |
5’b11001 |
8’h6b |
讀數(shù)據(jù)操作(四線模式) |
說(shuō)明:
1、命令類(lèi)型是我自己隨便定義的,可以隨便修改。命令碼是芯片手冊(cè)上定義好,不能修改,更詳細(xì)的內(nèi)容請(qǐng)參考W25Q128芯片手冊(cè)。
2、命令類(lèi)型的最高位是使能位,只有當(dāng)最高位為1時(shí),命令才有效(在代碼里面寫(xiě)的就是只有當(dāng)最高位為1時(shí)才能進(jìn)入SPI操作的狀態(tài)機(jī))。
3、進(jìn)行四線讀寫(xiě)操作之前,一定要把四線讀寫(xiě)功能的使能位打開(kāi),方法是通過(guò)寫(xiě)狀態(tài)寄存器命令把狀態(tài)寄存器2的QE位(倒數(shù)第二位)置1。
4.2、 如何用Matlab產(chǎn)生存放在ROM中的.coe文件格式的數(shù)據(jù)
上一節(jié)設(shè)計(jì)了一個(gè)把存放在ROM中的數(shù)據(jù)用SPI總線發(fā)出來(lái)的例子,ROM里面只存放了10個(gè)數(shù)據(jù),所以可以直接把這10個(gè)數(shù)據(jù)填寫(xiě)到.coe文件就可以了,由于QSPI Flash的頁(yè)編程(寫(xiě)數(shù)據(jù))指令最大支持256字節(jié)的寫(xiě)操作,所以下面的例子的功能是把ROM中存放的256個(gè)字節(jié)(8-bit)數(shù)據(jù)先寫(xiě)入QSPI Flash中,然后在讀出來(lái)。由于數(shù)據(jù)太多(256個(gè)),所以一個(gè)一個(gè)填寫(xiě)肯定不現(xiàn)實(shí),所以可以利用Matlab來(lái)直接產(chǎn)生.coe文件,Matlab的完整代碼如下:
width=8; %rom中數(shù)據(jù)的寬度 depth=256; %rom的深度 y=0:255; y=fliplr(y); %產(chǎn)生要發(fā)送的數(shù)據(jù),255,254,253,...... ,2,1,0 fid = fopen('test_data.coe', 'w'); % 打開(kāi)一個(gè).coe文件 % 存放在ROM中的.coe文件第一行必須是這個(gè)字符串,16表示16進(jìn)制,可以改成其他進(jìn)制 fprintf(fid,'memory_initialization_radix=16;\n'); % 存放在ROM中的.coe文件第二行必須是這個(gè)字符串 fprintf(fid,'memory_initialization_vector=\n'); % 把前255個(gè)數(shù)據(jù)寫(xiě)入.coe文件中,并用逗號(hào)隔開(kāi),為了方便知道數(shù)據(jù)的個(gè)數(shù),每行只寫(xiě)一個(gè)數(shù)據(jù) fprintf(fid,'%x,\n',y(1:end-1)); % 把最后一個(gè)數(shù)據(jù)寫(xiě)入.coe文件中,并用分號(hào)結(jié)尾 fprintf(fid,'%x;\n',y(end)); fclose(fid); % 關(guān)閉文件指針
用Matlab2014b運(yùn)行上面的代碼以后會(huì)在與這個(gè).m文件相同的目錄下產(chǎn)生一個(gè).coe文件,這個(gè).coe文件可以導(dǎo)入到ROM中。
4.3、 標(biāo)準(zhǔn)SPI總線操作QSPI Flash思路與代碼編寫(xiě)
上一篇博客《SPI總線的原理與FPGA實(shí)現(xiàn)》已經(jīng)介紹過(guò)用spi_module這個(gè)模塊去讀取QSPI Flash的Manufacturer/Device? ID,事實(shí)上除了上篇博客提供的那種方法以外,還可以直接在時(shí)鐘信號(hào)的下降沿發(fā)送數(shù)據(jù),時(shí)鐘信號(hào)的上升沿來(lái)接受數(shù)據(jù)來(lái)完成讀ID的操作,當(dāng)FPGA在時(shí)鐘的下降沿發(fā)送數(shù)據(jù)的時(shí)候,那么時(shí)鐘的上升沿剛好在數(shù)據(jù)的正中間,QSPI Flash剛好可以在這個(gè)上升沿把數(shù)據(jù)讀進(jìn)來(lái),讀操作則正好相反。但是有很多有經(jīng)驗(yàn)的人告訴我在設(shè)計(jì)中如非必要最好不要使用時(shí)鐘下降沿觸發(fā)的設(shè)計(jì)方法,可能是因?yàn)榇蠖鄶?shù)FPGA里面的Flip Flops資源都是上升沿觸發(fā)的,如果在Verilog代碼采用下降沿觸發(fā)的話 ,綜合的時(shí)候會(huì)在CLK輸入信號(hào)前面綜合出一個(gè)反相器,這個(gè)反相器可能會(huì)對(duì)時(shí)鐘信號(hào)的質(zhì)量有影響,具體的原因等我再Google上繼續(xù)搜索一段時(shí)間在說(shuō)。這個(gè)例子由于狀態(tài)機(jī)相較前幾篇博客來(lái)說(shuō)相對(duì)復(fù)雜,所以接下來(lái)寫(xiě)代碼我還是采用下降沿發(fā)送數(shù)據(jù),上升沿接收數(shù)據(jù)的方式來(lái)描述這個(gè)狀態(tài)機(jī)。
接下來(lái)的任務(wù)就是抽象出一個(gè)狀態(tài)機(jī)。上一篇博客僅僅讀一個(gè)ID就用了6個(gè)狀態(tài),所以采用上一篇博客的設(shè)計(jì)思路顯然不太現(xiàn)實(shí),但對(duì)于初學(xué)者而言,上一篇博客仍然有一個(gè)基本的指引作用。通過(guò)閱讀QSPI Flash的芯片手冊(cè),可以發(fā)現(xiàn),所有的命令其實(shí)至多由以下三個(gè)部分組成:
1、發(fā)送8-bit的命令碼
2、發(fā)送24-bit的地址碼
3、發(fā)送數(shù)據(jù)或接收數(shù)據(jù)
所有命令的狀態(tài)跳變圖可由下圖描述
所以按照這個(gè)思路來(lái)思考的話抽象出來(lái)的狀態(tài)機(jī)的狀態(tài)并不多。單線模式的狀態(tài)為以下幾個(gè):
1、空閑狀態(tài):用來(lái)初始化各個(gè)寄存器的值
2、發(fā)送命令狀態(tài):用來(lái)發(fā)送8-bit的命令碼
3、發(fā)送地址狀態(tài):用來(lái)發(fā)送24-bit的地址碼
4、讀等待狀態(tài):當(dāng)讀數(shù)據(jù)操作正在進(jìn)行的時(shí)候進(jìn)入此狀態(tài)等待讀數(shù)據(jù)完畢
5、寫(xiě)數(shù)據(jù)狀態(tài)(單線模式):在這個(gè)狀態(tài)FPGA往QSPI Flash里面寫(xiě)數(shù)據(jù)
6、結(jié)束狀態(tài):一條指令操作結(jié)束,并給出一個(gè)結(jié)束標(biāo)志
完整的代碼如下:
`timescale 1ns / 1ps module qspi_driver ( output O_qspi_clk , // SPI總線串行時(shí)鐘線 output reg O_qspi_cs , // SPI總線片選信號(hào) output reg O_qspi_mosi , // SPI總線輸出信號(hào)線,也是QSPI Flash的輸入信號(hào)線 input I_qspi_miso , // SPI總線輸入信號(hào)線,也是QSPI Flash的輸出信號(hào)線 input I_rst_n , // 復(fù)位信號(hào) input I_clk_25M , // 25MHz時(shí)鐘信號(hào) input [4:0] I_cmd_type , // 命令類(lèi)型 input [7:0] I_cmd_code , // 命令碼 input [23:0] I_qspi_addr , // QSPI Flash地址 output reg O_done_sig , // 指令執(zhí)行結(jié)束標(biāo)志 output reg [7:0] O_read_data , // 從QSPI Flash讀出的數(shù)據(jù) output reg O_read_byte_valid , // 讀一個(gè)字節(jié)完成的標(biāo)志 output reg [3:0] O_qspi_state // 狀態(tài)機(jī),用于在頂層調(diào)試用 ); parameter C_IDLE = 4'b0000 ; // 空閑狀態(tài) parameter C_SEND_CMD = 4'b0001 ; // 發(fā)送命令碼 parameter C_SEND_ADDR = 4'b0010 ; // 發(fā)送地址碼 parameter C_READ_WAIT = 4'b0011 ; // 讀等待 parameter C_WRITE_DATA = 4'b0101 ; // 寫(xiě)數(shù)據(jù) parameter C_FINISH_DONE = 4'b0110 ; // 一條指令執(zhí)行結(jié)束 reg [7:0] R_read_data_reg ; // 從Flash中讀出的數(shù)據(jù)用這個(gè)變量進(jìn)行緩存,等讀完了在把這個(gè)變量的值給輸出 reg R_qspi_clk_en ; // 串行時(shí)鐘使能信號(hào) reg R_data_come_single ; // 單線操作讀數(shù)據(jù)使能信號(hào),當(dāng)這個(gè)信號(hào)為高時(shí) reg [7:0] R_cmd_reg ; // 命令碼寄存器 reg [23:0] R_address_reg ; // 地址碼寄存器 reg [7:0] R_write_bits_cnt ; // 寫(xiě)bit計(jì)數(shù)器,寫(xiě)數(shù)據(jù)之前把它初始化為7,發(fā)送一個(gè)bit就減1 reg [8:0] R_write_bytes_cnt ; // 寫(xiě)字節(jié)計(jì)數(shù)器,發(fā)送一個(gè)字節(jié)數(shù)據(jù)就把它加1 reg [7:0] R_read_bits_cnt ; // 寫(xiě)bit計(jì)數(shù)器,接收一個(gè)bit就加1 reg [8:0] R_read_bytes_cnt ; // 讀字節(jié)計(jì)數(shù)器,接收一個(gè)字節(jié)數(shù)據(jù)就把它加1 reg [8:0] R_read_bytes_num ; // 要接收的數(shù)據(jù)總數(shù) reg R_read_finish ; // 讀數(shù)據(jù)結(jié)束標(biāo)志位 wire [7:0] W_rom_addr ; wire [7:0] W_rom_out ; assign O_qspi_clk = R_qspi_clk_en ? I_clk_25M : 0 ; // 產(chǎn)生串行時(shí)鐘信號(hào) assign W_rom_addr = R_write_bytes_cnt ; // 功能:用時(shí)鐘的下降沿發(fā)送數(shù)據(jù) always @(negedge I_clk_25M) begin if(!I_rst_n) begin O_qspi_cs <= 1'b1 ; O_qspi_state <= C_IDLE ; R_cmd_reg <= 0 ; R_address_reg <= 0 ; R_qspi_clk_en <= 1'b0 ; //SPI clock輸出不使能 R_write_bits_cnt <= 0 ; R_write_bytes_cnt <= 0 ; R_read_bytes_num <= 0 ; R_address_reg <= 0 ; O_done_sig <= 1'b0 ; R_data_come_single <= 1'b0 ; end else begin case(O_qspi_state) C_IDLE: // 初始化各個(gè)寄存器,當(dāng)檢測(cè)到命令類(lèi)型有效(命令類(lèi)型的最高位位1)以后,進(jìn)入發(fā)送命令碼狀態(tài) begin R_qspi_clk_en <= 1'b0 ; O_qspi_cs <= 1'b1 ; O_qspi_mosi <= 1'b0 ; R_cmd_reg <= I_cmd_code ; R_address_reg <= I_qspi_addr ; O_done_sig <= 1'b0 ; if(I_cmd_type[4] == 1'b1) begin //如果flash操作命令請(qǐng)求 O_qspi_state <= C_SEND_CMD ; R_write_bits_cnt <= 7 ; R_write_bytes_cnt <= 0 ; R_read_bytes_num <= 0 ; end end C_SEND_CMD: // 發(fā)送8-bit命令碼狀態(tài) begin R_qspi_clk_en <= 1'b1 ; // 打開(kāi)SPI串行時(shí)鐘SCLK的使能開(kāi)關(guān) O_qspi_cs <= 1'b0 ; // 拉低片選信號(hào)CS if(R_write_bits_cnt > 0) begin //如果R_cmd_reg還沒(méi)有發(fā)送完 O_qspi_mosi <= R_cmd_reg[R_write_bits_cnt] ; //發(fā)送bit7~bit1位 R_write_bits_cnt <= R_write_bits_cnt-1'b1 ; end else begin //發(fā)送bit0 O_qspi_mosi <= R_cmd_reg[0] ; if ((I_cmd_type[3:0] == 4'b0001) | (I_cmd_type[3:0] == 4'b0100)) begin //如果是寫(xiě)使能指令(Write Enable)或者寫(xiě)不使能指令(Write Disable) O_qspi_state <= C_FINISH_DONE ; end else if (I_cmd_type[3:0] == 4'b0011) begin //如果是讀狀態(tài)寄存器指令(Read Register) O_qspi_state <= C_READ_WAIT ; R_write_bits_cnt <= 7 ; R_read_bytes_num <= 1 ;//讀狀態(tài)寄存器指令需要接收一個(gè)數(shù)據(jù) end else if( (I_cmd_type[3:0] == 4'b0010) || (I_cmd_type[3:0] == 4'b0101) || (I_cmd_type[3:0] == 4'b0111) || (I_cmd_type[3:0] == 4'b0000) ) begin // 如果是扇區(qū)擦除(Sector Erase),頁(yè)編程指令(Page Program),讀數(shù)據(jù)指令(Read Data),讀設(shè)備ID指令(Read Device ID) O_qspi_state <= C_SEND_ADDR ; R_write_bits_cnt <= 23 ; // 這幾條指令后面都需要跟一個(gè)24-bit的地址碼 end end end C_SEND_ADDR: // 發(fā)送地址狀態(tài) begin if(R_write_bits_cnt > 0) //如果R_cmd_reg還沒(méi)有發(fā)送完 begin O_qspi_mosi <= R_address_reg[R_write_bits_cnt] ; //發(fā)送bit23~bit1位 R_write_bits_cnt <= R_write_bits_cnt - 1 ; end else begin O_qspi_mosi <= R_address_reg[0] ; //發(fā)送bit0 if(I_cmd_type[3:0] == 4'b0010) // 扇區(qū)擦除(Sector Erase)指令 begin //扇區(qū)擦除(Sector Erase)指令發(fā)完24-bit地址碼就執(zhí)行結(jié)束了,所以直接跳到結(jié)束狀態(tài) O_qspi_state <= C_FINISH_DONE ; end else if (I_cmd_type[3:0] == 4'b0101) // 頁(yè)編程(Page Program)指令 begin O_qspi_state <= C_WRITE_DATA ; R_write_bits_cnt <= 7 ; end else if (I_cmd_type[3:0] == 4'b0000) // 讀Device ID指令 begin O_qspi_state <= C_READ_WAIT ; R_read_bytes_num <= 2 ; //接收2個(gè)數(shù)據(jù)的Device ID end else if (I_cmd_type[3:0] == 4'b0111) // 讀數(shù)據(jù)(Read Data)指令 begin O_qspi_state <= C_READ_WAIT ; R_read_bytes_num <= 256 ; //接收256個(gè)數(shù)據(jù) end end end C_READ_WAIT: // 讀等待狀態(tài) begin if(R_read_finish) begin O_qspi_state <= C_FINISH_DONE ; R_data_come_single <= 1'b0 ; end else begin R_data_come_single <= 1'b1 ; // 單線模式下讀數(shù)據(jù)標(biāo)志信號(hào),此信號(hào)為高標(biāo)志正在接收數(shù)據(jù) end end C_WRITE_DATA: // 寫(xiě)數(shù)據(jù)狀態(tài) begin if(R_write_bytes_cnt < 256) // 往QSPI Flash中寫(xiě)入 256個(gè)數(shù)據(jù) begin if(R_write_bits_cnt > 0) //如果數(shù)據(jù)還沒(méi)有發(fā)送完 begin O_qspi_mosi <= W_rom_out[R_write_bits_cnt] ; //發(fā)送bit7~bit1位 R_write_bits_cnt <= R_write_bits_cnt - 1'b1 ; end else begin O_qspi_mosi <= W_rom_out[0] ; //發(fā)送bit0 R_write_bits_cnt <= 7 ; R_write_bytes_cnt <= R_write_bytes_cnt + 1'b1 ; end end else begin O_qspi_state <= C_FINISH_DONE ; R_qspi_clk_en <= 1'b0 ; end end C_FINISH_DONE: begin O_qspi_cs <= 1'b1 ; O_qspi_mosi <= 1'b0 ; R_qspi_clk_en <= 1'b0 ; O_done_sig <= 1'b1 ; R_data_come_single <= 1'b0 ; O_qspi_state <= C_IDLE ; end default:O_qspi_state <= C_IDLE ; endcase end end // // 功能:接收QSPI Flash發(fā)送過(guò)來(lái)的數(shù)據(jù) // always @(posedge I_clk_25M) begin if(!I_rst_n) begin R_read_bytes_cnt <= 0 ; R_read_bits_cnt <= 0 ; R_read_finish <= 1'b0 ; O_read_byte_valid <= 1'b0 ; R_read_data_reg <= 0 ; O_read_data <= 0 ; end else if(R_data_come_single) // 此信號(hào)為高表示接收數(shù)據(jù)從QSPI Flash發(fā)過(guò)來(lái)的數(shù)據(jù) begin if(R_read_bytes_cnt < R_read_bytes_num) begin if(R_read_bits_cnt < 7) //接收一個(gè)Byte的bit0~bit6 begin O_read_byte_valid <= 1'b0 ; R_read_data_reg <= {R_read_data_reg[6:0],I_qspi_miso} ; R_read_bits_cnt <= R_read_bits_cnt + 1'b1 ; end else begin O_read_byte_valid <= 1'b1 ; //一個(gè)byte數(shù)據(jù)有效 O_read_data <= {R_read_data_reg[6:0],I_qspi_miso} ; //接收bit7 R_read_bits_cnt <= 0 ; R_read_bytes_cnt <= R_read_bytes_cnt + 1'b1 ; end end else begin R_read_bytes_cnt <= 0 ; R_read_finish <= 1'b1 ; O_read_byte_valid <= 1'b0 ; end end else begin R_read_bytes_cnt <= 0 ; R_read_bits_cnt <= 0 ; R_read_finish <= 1'b0 ; O_read_byte_valid <= 1'b0 ; R_read_data_reg <= 0 ; end end rom_data rom_data_inst ( .clka(I_clk_25M), // input clka .addra(W_rom_addr), // input [7 : 0] addra .douta(W_rom_out) // output [7 : 0] douta ); endmodule
接下來(lái)就是寫(xiě)一個(gè)測(cè)試代碼對(duì)這個(gè)單線模式SPI驅(qū)動(dòng),為了保證把上面的所有指令都測(cè)試一遍,測(cè)試代碼如下:
module qspi_top ( input I_clk , input I_rst_n , output O_qspi_clk , // SPI總線串行時(shí)鐘線 output O_qspi_cs , // SPI總線片選信號(hào) output O_qspi_mosi , // SPI總線輸出信號(hào)線,也是QSPI Flash的輸入信號(hào)線 input I_qspi_miso // SPI總線輸入信號(hào)線,也是QSPI Flash的輸出信號(hào)線 ); reg [3:0] R_state ; reg [7:0] R_flash_cmd ; reg [23:0] R_flash_addr ; reg R_clk_25M ; reg [4:0] R_cmd_type ; wire W_done_sig ; wire [7:0] W_read_data ; wire W_read_byte_valid ; wire [2:0] R_qspi_state ; //功能:二分頻邏輯 always @(posedge I_clk or negedge I_rst_n) begin if(!I_rst_n) R_clk_25M <= 1'b0 ; else R_clk_25M <= ~R_clk_25M ; end //功能:測(cè)試狀態(tài)機(jī) always @(posedge R_clk_25M or negedge I_rst_n) begin if(!I_rst_n) begin R_state <= 4'd0 ; R_flash_addr <= 24'd0 ; R_flash_cmd <= 8'h00 ; R_cmd_type <= 5'b0_0000 ; end else begin case(R_state) 4'd0://讀Device ID指令 begin if(W_done_sig) begin R_flash_cmd <= 8'h00 ; R_state <= R_state + 1'b1 ; R_cmd_type <= 5'b0_0000 ; end else begin R_flash_cmd <= 8'h90 ; R_flash_addr <= 24'd0 ; R_cmd_type <= 5'b1_0000 ; end end 4'd1://寫(xiě)Write disable instruction begin if(W_done_sig) begin R_flash_cmd <= 8'h00 ; R_state <= R_state + 1'b1 ; R_cmd_type <= 5'b0_0000 ; end else begin R_flash_cmd <= 8'h04 ; R_cmd_type <= 5'b1_0100 ; end end 4'd2://寫(xiě)使能(Write Enable)指令 begin if(W_done_sig) begin R_flash_cmd <= 8'h00 ; R_state <= R_state + 1'b1 ; R_cmd_type <= 5'b0_0000 ; end else begin R_flash_cmd <= 8'h06 ; R_cmd_type <= 5'b1_0001 ; end end 4'd3:// 扇區(qū)擦除(Sector Erase)指令 begin if(W_done_sig) begin R_flash_cmd <= 8'h00 ; R_state <= R_state + 1'b1 ; R_cmd_type <= 5'b0_0000 ; end else begin R_flash_cmd <= 8'h20 ; R_flash_addr<= 24'd0 ; R_cmd_type <= 5'b1_0010 ; end end 4'd4://讀狀態(tài)寄存器1, 當(dāng)Busy位(狀態(tài)寄存器1的最低位)為0時(shí)表示擦除操作完成 begin if(W_done_sig) begin if(W_read_data[0]==1'b0) begin R_flash_cmd <= 8'h00 ; R_state <= R_state + 1'b1 ; R_cmd_type <= 5'b0_0000 ; end else begin R_flash_cmd <= 8'h05 ; R_cmd_type <= 5'b1_0011 ; end end else begin R_flash_cmd <= 8'h05 ; R_cmd_type <= 5'b1_0011 ; end end 4'd5://寫(xiě)使能(Write Enable)指令 begin if(W_done_sig) begin R_flash_cmd <= 8'h00 ; R_state <= R_state + 1'b1 ; R_cmd_type <= 5'b0_0000 ; end else begin R_flash_cmd <= 8'h06 ; R_cmd_type <= 5'b1_0001 ; end end 4'd6: //頁(yè)編程操作(Page Program): 把存放在ROM中的數(shù)據(jù)寫(xiě)入QSPI Flash中 begin if(W_done_sig) begin R_flash_cmd <= 8'h00 ; R_state <= R_state + 1'b1 ; R_cmd_type <= 5'b0_0000 ; end else begin R_flash_cmd <= 8'h02 ; R_flash_addr<= 24'd0 ; R_cmd_type <= 5'b1_0101 ; end end 4'd7://讀狀態(tài)寄存器1, 當(dāng)Busy位(狀態(tài)寄存器1的最低位)為0時(shí)表示寫(xiě)操作完成 begin if(W_done_sig) begin if(W_read_data[0]==1'b0) begin R_flash_cmd <= 8'h00 ; R_state <= R_state + 1'b1 ; R_cmd_type <= 5'b0_0000 ; end else begin R_flash_cmd <= 8'h05 ; R_cmd_type <= 5'b1_0011 ; end end else begin R_flash_cmd <= 8'h05 ; R_cmd_type <= 5'b1_0011 ; end end 4'd8://讀256 Bytes begin if(W_done_sig) begin R_flash_cmd <= 8'h00 ; R_state <= R_state + 1'b1 ; R_cmd_type <= 5'b0_0000 ; end else begin R_flash_cmd <= 8'h03 ; R_flash_addr<= 24'd0 ; R_cmd_type <= 5'b1_0111 ; end end 4'd9:// 空閑狀態(tài) begin R_flash_cmd <= 8'h00 ; R_state <= 4'd9 ; R_cmd_type <= 5'b0_0000 ; end default : R_state <= 4'd0 ; endcase end end qspi_driver U_qspi_driver ( .O_qspi_clk (O_qspi_clk ), // SPI總線串行時(shí)鐘線 .O_qspi_cs (O_qspi_cs ), // SPI總線片選信號(hào) .O_qspi_mosi (O_qspi_mosi ), // SPI總線輸出信號(hào)線,也是QSPI Flash的輸入信號(hào)線 .I_qspi_miso (I_qspi_miso ), // SPI總線輸入信號(hào)線,也是QSPI Flash的輸出信號(hào)線 .I_rst_n (I_rst_n ), // 復(fù)位信號(hào) .I_clk_25M (R_clk_25M ), // 25MHz時(shí)鐘信號(hào) .I_cmd_type (R_cmd_type ), // 命令類(lèi)型 .I_cmd_code (R_flash_cmd ), // 命令碼 .I_qspi_addr (R_flash_addr ), // QSPI Flash地址 .O_done_sig (W_done_sig ), // 指令執(zhí)行結(jié)束標(biāo)志 .O_read_data (W_read_data ), // 從QSPI Flash讀出的數(shù)據(jù) .O_read_byte_valid (W_read_byte_valid ), // 讀一個(gè)字節(jié)完成的標(biāo)志 .O_qspi_state (R_qspi_state ) // 狀態(tài)機(jī),用于在頂層調(diào)試用 ); wire [35:0] CONTROL0 ; wire [69:0] TRIG0 ; icon icon_inst ( .CONTROL0(CONTROL0) // INOUT BUS [35:0] ); ila ila_inst ( .CONTROL(CONTROL0) , // INOUT BUS [35:0] .CLK(I_clk) , // IN .TRIG0(TRIG0) // IN BUS [255:0] ); assign TRIG0[7:0] = W_read_data ; assign TRIG0[8] = W_read_byte_valid ; assign TRIG0[12:9] = R_state ; assign TRIG0[16:13] = R_qspi_state ; assign TRIG0[17] = W_done_sig ; assign TRIG0[18] = I_qspi_miso ; assign TRIG0[19] = O_qspi_mosi ; assign TRIG0[20] = O_qspi_cs ; assign TRIG0[21] = O_qspi_clk ; assign TRIG0[26:22] = R_cmd_type ; assign TRIG0[34:27] = R_flash_cmd ; assign TRIG0[58:35] = R_flash_addr ; assign TRIG0[59] = I_rst_n ; endmodule
接下來(lái)主要看看用ChipScope抓出來(lái)的時(shí)序圖和芯片手冊(cè)規(guī)定的時(shí)序圖是否完全一致。
1、讀ID指令
??????? 芯片手冊(cè)指令的讀ID指令時(shí)序圖:
ChipScope抓出來(lái)的時(shí)序圖
2、寫(xiě)不使能(Write Disable)指令
??????? 芯片手冊(cè)指令的寫(xiě)不使能時(shí)序圖:
??????? ChipScope抓出來(lái)的寫(xiě)不使能時(shí)序圖:
3、寫(xiě)使能(Write Enable)指令
??????? 芯片手冊(cè)指令的寫(xiě)使能時(shí)序圖:
???????? ChipScope抓出來(lái)的寫(xiě)使能時(shí)序圖
3、扇區(qū)擦除(Sector Erase)指令
??????? 芯片手冊(cè)的扇區(qū)擦除指令時(shí)序圖:
??????? 芯片手冊(cè)的扇區(qū)擦除指令時(shí)序圖:
4、讀狀態(tài)寄存器(Read Status Register)指令
??????? 芯片手冊(cè)的讀狀態(tài)寄存器指令時(shí)序圖:
??????? ChipScope抓回來(lái)的讀狀態(tài)寄存器指令時(shí)序圖:
由于在擦除操作和寫(xiě)操作(Page Program)操作指令發(fā)送完畢以后,芯片內(nèi)部會(huì)自己執(zhí)行相關(guān)的操作并把狀態(tài)寄存器1中的最低位Busy位拉高,狀態(tài)寄存器1的各位如下:
所以讀狀態(tài)寄存器指令的目的是為了檢測(cè)Busy是否為高,Busy為高的話說(shuō)明擦除操作或?qū)懖僮?Page Program)操作指令并未完成,這種情況應(yīng)該等待不發(fā)送其他指令,等到Busy位拉低以后說(shuō)明擦除操作或?qū)懖僮?Page Program)操作指令已經(jīng)完成,這時(shí)才可以執(zhí)行后續(xù)的操作。值得注意的是,在上圖中讀到8個(gè)bit數(shù)據(jù)(上圖讀到的數(shù)據(jù)是0000_0011(8’h03))以后,O_qpsi_cs和O_qspi_clk信號(hào)仍然持續(xù)了2個(gè)周期的時(shí)間才變?yōu)榭臻e狀態(tài),實(shí)際上,回過(guò)頭去看代碼的話很容易解釋這種情況,由于代碼里面是用時(shí)鐘的下降沿發(fā)送數(shù)據(jù),而用時(shí)鐘的上升沿來(lái)接收數(shù)據(jù),發(fā)送數(shù)據(jù)與接收數(shù)據(jù)的狀態(tài)切換是通過(guò)R_data_coming_signal和R_read_finish信號(hào)來(lái)轉(zhuǎn)換的,所以導(dǎo)致了這種情況的發(fā)生。但是讀數(shù)據(jù)相關(guān)的指令并不要求完全8-bit對(duì)齊,如果在讀數(shù)據(jù)的過(guò)程中只要O_qpsi_cs一直為低,那么會(huì)一直重復(fù)讀下去,直至O_qpsi_cs拉高為止才停止讀數(shù)據(jù),所以上面多出來(lái)的兩個(gè)時(shí)鐘并不會(huì)影響結(jié)果的正確性。
5、寫(xiě)數(shù)據(jù)(Page Program)指令(單線模式)
??????? 芯片手冊(cè)的寫(xiě)數(shù)據(jù)指令時(shí)序圖:
ChipScope抓回來(lái)的時(shí)序圖
6、讀數(shù)據(jù)(Read Data)指令(單線模式)
??????? 芯片手冊(cè)的讀數(shù)據(jù)指令時(shí)序圖:
ChipScope抓回來(lái)的時(shí)序圖:
通過(guò)上面用ChipScope抓回來(lái)的時(shí)序圖與芯片手冊(cè)的時(shí)序圖進(jìn)行對(duì)比可以很清晰的理解整個(gè)QSPI Flash的操作流程與時(shí)序細(xì)節(jié)。
4.4、 如何處理雙向信號(hào)(Verilog中用關(guān)鍵詞inout定義的信號(hào)都是雙向信號(hào))
四線操作中IO0,IO1,IO2和IO3全部都可以用來(lái)發(fā)送數(shù)據(jù)以及接收數(shù)據(jù),所以在編寫(xiě)代碼的時(shí)候要把它們?nèi)慷x成雙向類(lèi)型的信號(hào),即inout類(lèi)型。所以在編寫(xiě)四線操作的代碼之前有必要提前熟悉一下inout信號(hào)的處理方法。
總的來(lái)說(shuō),inout信號(hào)大致有以下4個(gè)特征:
1、inout端口不能被賦值為reg型,因此,不能用于always語(yǔ)句中。
2、對(duì)于inout端口的邏輯判斷,要用到?:條件表達(dá)式,來(lái)控制高阻的賦值
3、inout信號(hào)需要有一個(gè)中轉(zhuǎn)的寄存器,在always語(yǔ)句中才可以將輸入的信號(hào)賦給輸出(用inout代替純output)
4、高阻態(tài)不要用于芯片內(nèi)部,應(yīng)該用邏輯引到引腳處,然后用高阻來(lái)實(shí)現(xiàn)。
實(shí)際上,F(xiàn)PGA內(nèi)部的雙向信號(hào)是通過(guò)一個(gè)三態(tài)門(mén)來(lái)實(shí)現(xiàn)的,一個(gè)典型的三態(tài)門(mén)結(jié)構(gòu)如下
描述這個(gè)三態(tài)門(mén)的Verilog代碼如下:
module Test_inout ( input I_clk, input I_rst_n, . . . inout IO_data, . . . ) reg R_data_out ; wire I_data_in ; assign IO_data = Control ? R_data_out : 1'bz ; assign I_data_in = IO_data ; always @(posedge I_clk or negedge I_rst_n) begin . . . ; end endmodule
? 上面的代碼表達(dá)的意思是:當(dāng)Control為1是,IO_data信號(hào)作為輸出,輸出R_data_out的值,當(dāng)Control為0時(shí),IO_data信號(hào)作為輸入,輸入的值賦給I_data_in變量。
值得注意的是,inout作為輸出的時(shí)候不能直接給他賦值,而需要通過(guò)一個(gè)中間變量R_data_out來(lái)間接賦值,同時(shí)inout變量的輸入輸出屬性必須通過(guò)一個(gè)控制信號(hào)Control來(lái)控制,控制性號(hào)為高時(shí)為inout信號(hào)作為輸出,輸出R_data_out的值,為低時(shí)inout處于高阻狀態(tài),這時(shí)才作為輸入接收外部的數(shù)據(jù)。
4.5、 四線SPI總線操作QSPI Flash思路與代碼編寫(xiě)
上一小節(jié)已經(jīng)完成了單線模式QSPI Flash的讀寫(xiě)以及其他相關(guān)操作,這一節(jié)將在上一節(jié)的基礎(chǔ)上加上四線讀寫(xiě)功能。通過(guò)進(jìn)一步閱讀W25Q128BV的芯片手冊(cè)可知,在進(jìn)行四線讀寫(xiě)操作之前一定要把QE(Quad Enable)位置為1,而QE位在狀態(tài)寄存器2的倒數(shù)第二位,見(jiàn)下圖
除此以外,四線讀操作還需要在讀數(shù)據(jù)之前等待8個(gè)dummy clock用來(lái)加快讀數(shù)據(jù)的速度(詳細(xì)內(nèi)容請(qǐng)閱讀芯片手冊(cè))。所以,四線操作相對(duì)于單線操作而言除了要增加四線模式讀寫(xiě)數(shù)據(jù)的狀態(tài)以外還要增加一個(gè)寫(xiě)狀態(tài)寄存器的功能用來(lái)開(kāi)啟QE(Quad Enable)位以及一個(gè)dummy用來(lái)等待8個(gè)clock。
綜上所述,四線模式的狀態(tài)為以下幾個(gè):
1、空閑狀態(tài):用來(lái)初始化各個(gè)寄存器的值
2、發(fā)送命令狀態(tài):用來(lái)發(fā)送8-bit的命令碼
3、發(fā)送地址狀態(tài):用來(lái)發(fā)送24-bit的地址碼
4、讀等待狀態(tài)(單線模式):當(dāng)讀數(shù)據(jù)操作正在進(jìn)行的時(shí)候進(jìn)入此狀態(tài)等待讀數(shù)據(jù)完畢
5、寫(xiě)數(shù)據(jù)狀態(tài)(單線模式):在這個(gè)狀態(tài)FPGA往QSPI Flash里面寫(xiě)數(shù)據(jù)
6、寫(xiě)狀態(tài)寄存器狀態(tài):用來(lái)把狀態(tài)寄存器的QE(Quad Enable)置1
7、Dummy Clock狀態(tài):四線讀數(shù)據(jù)之前需要等待8個(gè)dummy clock
8、寫(xiě)數(shù)據(jù)狀態(tài)(四線模式):在這個(gè)狀態(tài)FPGA往QSPI Flash里面通過(guò)四線模式寫(xiě)數(shù)據(jù)
9、讀等待狀態(tài)(四線模式):在這個(gè)狀態(tài)等待FPGA從QSPI Flash里面通過(guò)四線模式讀數(shù)據(jù)完成
10、結(jié)束狀態(tài):一條指令操作結(jié)束,并給出一個(gè)結(jié)束標(biāo)志
其中標(biāo)紅的狀態(tài)是四線模式的代碼在單線模式代碼的基礎(chǔ)上增加的四個(gè)狀態(tài)。
四線模式的完整代碼如下所示:
`timescale 1ns / 1ps module qspi_driver ( output O_qspi_clk , // QSPI Flash Quad SPI(QPI)總線串行時(shí)鐘線 output reg O_qspi_cs , // QPI總線片選信號(hào) inout IO_qspi_io0 , // QPI總線輸入/輸出信號(hào)線 inout IO_qspi_io1 , // QPI總線輸入/輸出信號(hào)線 inout IO_qspi_io2 , // QPI總線輸入/輸出信號(hào)線 inout IO_qspi_io3 , // QPI總線輸入/輸出信號(hào)線 input I_rst_n , // 復(fù)位信號(hào) input I_clk_25M , // 25MHz時(shí)鐘信號(hào) input [4:0] I_cmd_type , // 命令類(lèi)型 input [7:0] I_cmd_code , // 命令碼 input [23:0] I_qspi_addr , // QSPI Flash地址 input [15:0] I_status_reg , // QSPI Flash狀態(tài)寄存器的值 output reg O_done_sig , // 指令執(zhí)行結(jié)束標(biāo)志 output reg [7:0] O_read_data , // 從QSPI Flash讀出的數(shù)據(jù) output reg O_read_byte_valid , // 讀一個(gè)字節(jié)完成的標(biāo)志 output reg [3:0] O_qspi_state // 狀態(tài)機(jī),用于在頂層調(diào)試用 ); parameter C_IDLE = 4'b0000 ; // 空閑狀態(tài) parameter C_SEND_CMD = 4'b0001 ; // 發(fā)送命令碼 parameter C_SEND_ADDR = 4'b0010 ; // 發(fā)送地址碼 parameter C_READ_WAIT = 4'b0011 ; // 單線模式讀等待 parameter C_WRITE_DATA = 4'b0101 ; // 單線模式寫(xiě)數(shù)據(jù)到QSPI Flash parameter C_FINISH_DONE = 4'b0110 ; // 一條指令執(zhí)行結(jié)束 parameter C_WRITE_STATE_REG = 4'b0111 ; // 寫(xiě)狀態(tài)寄存器 parameter C_WRITE_DATA_QUAD = 4'b1000 ; // 四線模式寫(xiě)數(shù)據(jù)到QSPI Flash parameter C_DUMMY = 4'b1001 ; // 四線模式讀數(shù)據(jù)需要8個(gè)時(shí)鐘周期的dummy clock,這可以加快讀數(shù)據(jù)的速度 parameter C_READ_WAIT_QUAD = 4'b1010 ; // 四線模式讀等待狀態(tài) // QSPI Flash IO輸入輸出狀態(tài)控制寄存器 reg R_qspi_io0 ; reg R_qspi_io1 ; reg R_qspi_io2 ; reg R_qspi_io3 ; reg R_qspi_io0_out_en ; reg R_qspi_io1_out_en ; reg R_qspi_io2_out_en ; reg R_qspi_io3_out_en ; reg [7:0] R_read_data_reg ; // 從Flash中讀出的數(shù)據(jù)用這個(gè)變量進(jìn)行緩存,等讀完了在把這個(gè)變量的值給輸出 reg R_qspi_clk_en ; // 串行時(shí)鐘使能信號(hào) reg R_data_come_single ; // 單線操作讀數(shù)據(jù)使能信號(hào),當(dāng)這個(gè)信號(hào)為高時(shí) reg R_data_come_quad ; // 單線操作讀數(shù)據(jù)使能信號(hào),當(dāng)這個(gè)信號(hào)為高時(shí) reg [7:0] R_cmd_reg ; // 命令碼寄存器 reg [23:0] R_address_reg ; // 地址碼寄存器 reg [15:0] R_status_reg ; // 狀態(tài)寄存器 reg [7:0] R_write_bits_cnt ; // 寫(xiě)bit計(jì)數(shù)器,寫(xiě)數(shù)據(jù)之前把它初始化為7,發(fā)送一個(gè)bit就減1 reg [8:0] R_write_bytes_cnt ; // 寫(xiě)字節(jié)計(jì)數(shù)器,發(fā)送一個(gè)字節(jié)數(shù)據(jù)就把它加1 reg [7:0] R_read_bits_cnt ; // 寫(xiě)bit計(jì)數(shù)器,接收一個(gè)bit就加1 reg [8:0] R_read_bytes_cnt ; // 讀字節(jié)計(jì)數(shù)器,接收一個(gè)字節(jié)數(shù)據(jù)就把它加1 reg [8:0] R_read_bytes_num ; // 要接收的數(shù)據(jù)總數(shù) reg R_read_finish ; // 讀數(shù)據(jù)結(jié)束標(biāo)志位 wire [7:0] W_rom_addr ; wire [7:0] W_rom_out ; assign O_qspi_clk = R_qspi_clk_en ? I_clk_25M : 0 ; // 產(chǎn)生串行時(shí)鐘信號(hào) assign W_rom_addr = R_write_bytes_cnt ; // QSPI IO方向控制 assign IO_qspi_io0 = R_qspi_io0_out_en ? R_qspi_io0 : 1'bz ; assign IO_qspi_io1 = R_qspi_io1_out_en ? R_qspi_io1 : 1'bz ; assign IO_qspi_io2 = R_qspi_io2_out_en ? R_qspi_io2 : 1'bz ; assign IO_qspi_io3 = R_qspi_io3_out_en ? R_qspi_io3 : 1'bz ; // 功能:用時(shí)鐘的下降沿發(fā)送數(shù)據(jù) always @(negedge I_clk_25M) begin if(!I_rst_n) begin O_qspi_cs <= 1'b1 ; O_qspi_state <= C_IDLE ; R_cmd_reg <= 0 ; R_address_reg <= 0 ; R_qspi_clk_en <= 1'b0 ; //QSPI clock輸出不使能 R_write_bits_cnt <= 0 ; R_write_bytes_cnt <= 0 ; R_read_bytes_num <= 0 ; R_address_reg <= 0 ; O_done_sig <= 1'b0 ; R_data_come_single <= 1'b0 ; R_data_come_quad <= 1'b0 ; end else begin case(O_qspi_state) C_IDLE: // 初始化各個(gè)寄存器,當(dāng)檢測(cè)到命令類(lèi)型有效(命令類(lèi)型的最高位位1)以后,進(jìn)入發(fā)送命令碼狀態(tài) begin R_qspi_clk_en <= 1'b0 ; O_qspi_cs <= 1'b1 ; R_qspi_io0 <= 1'b0 ; R_cmd_reg <= I_cmd_code ; R_address_reg <= I_qspi_addr ; R_status_reg <= I_status_reg ; O_done_sig <= 1'b0 ; R_qspi_io3_out_en <= 1'b0 ; // 設(shè)置IO_qspi_io3為高阻 R_qspi_io2_out_en <= 1'b0 ; // 設(shè)置IO_qspi_io2為高阻 R_qspi_io1_out_en <= 1'b0 ; // 設(shè)置IO_qspi_io1為高阻 R_qspi_io0_out_en <= 1'b0 ; // 設(shè)置IO_qspi_io0為高阻 if(I_cmd_type[4] == 1'b1) begin //如果flash操作命令請(qǐng)求 O_qspi_state <= C_SEND_CMD ; R_write_bits_cnt <= 7 ; R_write_bytes_cnt <= 0 ; R_read_bytes_num <= 0 ; end end C_SEND_CMD: // 發(fā)送8-bit命令碼狀態(tài) begin R_qspi_io0_out_en <= 1'b1 ; // 設(shè)置IO_qspi_io0為輸出 R_qspi_clk_en <= 1'b1 ; // 打開(kāi)SPI串行時(shí)鐘SCLK的使能開(kāi)關(guān) O_qspi_cs <= 1'b0 ; // 拉低片選信號(hào)CS if(R_write_bits_cnt > 0) begin //如果R_cmd_reg還沒(méi)有發(fā)送完 R_qspi_io0 <= R_cmd_reg[R_write_bits_cnt] ; //發(fā)送bit7~bit1位 R_write_bits_cnt <= R_write_bits_cnt-1'b1 ; end else begin //發(fā)送bit0 R_qspi_io0 <= R_cmd_reg[0] ; if ((I_cmd_type[3:0] == 4'b0001) | (I_cmd_type[3:0] == 4'b0100)) begin //如果是寫(xiě)使能指令(Write Enable)或者寫(xiě)不使能指令(Write Disable) O_qspi_state <= C_FINISH_DONE ; end else if (I_cmd_type[3:0] == 4'b0011) begin //如果是讀狀態(tài)寄存器指令(Read Register) O_qspi_state <= C_READ_WAIT ; R_write_bits_cnt <= 7 ; R_read_bytes_num <= 1 ;//讀狀態(tài)寄存器指令需要接收一個(gè)數(shù)據(jù) end else if( (I_cmd_type[3:0] == 4'b0010) || // 如果是扇區(qū)擦除(Sector Erase) (I_cmd_type[3:0] == 4'b0101) || // 如果是頁(yè)編程指令(Page Program) (I_cmd_type[3:0] == 4'b0111) || // 如果是讀數(shù)據(jù)指令(Read Data) (I_cmd_type[3:0] == 4'b0000) || // 如果是讀設(shè)備ID指令(Read Device ID) (I_cmd_type[3:0] == 4'b1000) || // 如果是四線模式頁(yè)編程指令(Quad Page Program) (I_cmd_type[3:0] == 4'b1001) // 如果是四線模式讀數(shù)據(jù)指令(Quad Read Data) ) begin O_qspi_state <= C_SEND_ADDR ; R_write_bits_cnt <= 23 ; // 這幾條指令后面都需要跟一個(gè)24-bit的地址碼 end else if (I_cmd_type[3:0] == 4'b0110) begin //如果是Write Status Register O_qspi_state <= C_WRITE_STATE_REG ; R_write_bits_cnt <= 15 ; end end end C_WRITE_STATE_REG : begin R_qspi_io0_out_en <= 1'b1 ; // 設(shè)置IO0為輸出 if(R_write_bits_cnt > 0) begin //如果R_cmd_reg還沒(méi)有發(fā)送完 R_qspi_io0 <= R_status_reg[R_write_bits_cnt] ; //發(fā)送bit15~bit1位 R_write_bits_cnt <= R_write_bits_cnt - 1 ; end else begin //發(fā)送bit0 R_qspi_io0 <= R_status_reg[0] ; O_qspi_state <= C_FINISH_DONE ; end end C_SEND_ADDR: // 發(fā)送地址狀態(tài) begin R_qspi_io0_out_en <= 1'b1 ; if(R_write_bits_cnt > 0) //如果R_cmd_reg還沒(méi)有發(fā)送完 begin R_qspi_io0 <= R_address_reg[R_write_bits_cnt] ; //發(fā)送bit23~bit1位 R_write_bits_cnt <= R_write_bits_cnt - 1 ; end else begin R_qspi_io0 <= R_address_reg[0] ; //發(fā)送bit0 if(I_cmd_type[3:0] == 4'b0010) // 扇區(qū)擦除(Sector Erase)指令 begin //扇區(qū)擦除(Sector Erase)指令發(fā)完24-bit地址碼就執(zhí)行結(jié)束了,所以直接跳到結(jié)束狀態(tài) O_qspi_state <= C_FINISH_DONE ; end else if (I_cmd_type[3:0] == 4'b0101) // 頁(yè)編程(Page Program)指令,頁(yè)編程指令和寫(xiě)數(shù)據(jù)指令是一個(gè)意思 begin O_qspi_state <= C_WRITE_DATA ; R_write_bits_cnt <= 7 ; end else if (I_cmd_type[3:0] == 4'b0000) // 讀Device ID指令 begin O_qspi_state <= C_READ_WAIT ; R_read_bytes_num <= 2 ; //接收2個(gè)數(shù)據(jù)的Device ID end else if (I_cmd_type[3:0] == 4'b0111) // 讀數(shù)據(jù)(Read Data)指令 begin O_qspi_state <= C_READ_WAIT ; R_read_bytes_num <= 256 ; //接收256個(gè)數(shù)據(jù) end else if (I_cmd_type[3:0] == 4'b1000) begin //如果是四線模式頁(yè)編程指令(Quad Page Program) O_qspi_state <= C_WRITE_DATA_QUAD ; R_write_bits_cnt <= 7 ; end else if (I_cmd_type[3:0] == 4'b1001) begin //如果是四線讀操作 O_qspi_state <= C_DUMMY ; R_read_bytes_num <= 256 ; //接收256個(gè)數(shù)據(jù) R_write_bits_cnt <= 7 ; end end end C_DUMMY: // 四線讀操作之前需要等待8個(gè)dummy clock begin R_qspi_io3_out_en <= 1'b0 ; // 設(shè)置IO_qspi_io3為高阻 R_qspi_io2_out_en <= 1'b0 ; // 設(shè)置IO_qspi_io2為高阻 R_qspi_io1_out_en <= 1'b0 ; // 設(shè)置IO_qspi_io1為高阻 R_qspi_io0_out_en <= 1'b0 ; // 設(shè)置IO_qspi_io0為高阻 if(R_write_bits_cnt > 0) R_write_bits_cnt <= R_write_bits_cnt - 1 ; else O_qspi_state <= C_READ_WAIT_QUAD ; end C_READ_WAIT: // 單線模式讀等待狀態(tài) begin if(R_read_finish) begin O_qspi_state <= C_FINISH_DONE ; R_data_come_single <= 1'b0 ; end else begin R_data_come_single <= 1'b1 ; // 單線模式下讀數(shù)據(jù)標(biāo)志信號(hào),此信號(hào)為高標(biāo)志正在接收數(shù)據(jù) R_qspi_io1_out_en <= 1'b0 ; end end C_READ_WAIT_QUAD: // 四線模式讀等待狀態(tài) begin if(R_read_finish) begin O_qspi_state <= C_FINISH_DONE ; R_data_come_quad <= 1'b0 ; end else R_data_come_quad <= 1'b1 ; end C_WRITE_DATA: // 寫(xiě)數(shù)據(jù)狀態(tài) begin if(R_write_bytes_cnt < 256) // 往QSPI Flash中寫(xiě)入 256個(gè)數(shù)據(jù) begin if(R_write_bits_cnt > 0) //如果數(shù)據(jù)還沒(méi)有發(fā)送完 begin R_qspi_io0 <= W_rom_out[R_write_bits_cnt] ; //發(fā)送bit7~bit1位 R_write_bits_cnt <= R_write_bits_cnt - 1'b1 ; end else begin R_qspi_io0 <= W_rom_out[0] ; //發(fā)送bit0 R_write_bits_cnt <= 7 ; R_write_bytes_cnt <= R_write_bytes_cnt + 1'b1 ; end end else begin O_qspi_state <= C_FINISH_DONE ; R_qspi_clk_en <= 1'b0 ; end end C_WRITE_DATA_QUAD : begin R_qspi_io0_out_en <= 1'b1 ; // 設(shè)置IO0為輸出 R_qspi_io1_out_en <= 1'b1 ; // 設(shè)置IO1為輸出 R_qspi_io2_out_en <= 1'b1 ; // 設(shè)置IO2為輸出 R_qspi_io3_out_en <= 1'b1 ; // 設(shè)置IO3為輸出 if(R_write_bytes_cnt == 9'd256) begin O_qspi_state <= C_FINISH_DONE ; R_qspi_clk_en <= 1'b0 ; end else begin if(R_write_bits_cnt == 8'd3) begin R_write_bytes_cnt <= R_write_bytes_cnt + 1'b1 ; R_write_bits_cnt <= 8'd7 ; R_qspi_io3 <= W_rom_out[3] ; // 分別發(fā)送bit3 R_qspi_io2 <= W_rom_out[2] ; // 分別發(fā)送bit2 R_qspi_io1 <= W_rom_out[1] ; // 分別發(fā)送bit1 R_qspi_io0 <= W_rom_out[0] ; // 分別發(fā)送bit0 end else begin R_write_bits_cnt <= R_write_bits_cnt - 4 ; R_qspi_io3 <= W_rom_out[R_write_bits_cnt - 0] ; // 分別發(fā)送bit7 R_qspi_io2 <= W_rom_out[R_write_bits_cnt - 1] ; // 分別發(fā)送bit6 R_qspi_io1 <= W_rom_out[R_write_bits_cnt - 2] ; // 分別發(fā)送bit5 R_qspi_io0 <= W_rom_out[R_write_bits_cnt - 3] ; // 分別發(fā)送bit4 end end end C_FINISH_DONE: begin O_qspi_cs <= 1'b1 ; R_qspi_io0 <= 1'b0 ; R_qspi_clk_en <= 1'b0 ; O_done_sig <= 1'b1 ; R_qspi_io3_out_en <= 1'b0 ; // 設(shè)置IO_qspi_io3為高阻 R_qspi_io2_out_en <= 1'b0 ; // 設(shè)置IO_qspi_io2為高阻 R_qspi_io1_out_en <= 1'b0 ; // 設(shè)置IO_qspi_io1為高阻 R_qspi_io0_out_en <= 1'b0 ; // 設(shè)置IO_qspi_io0為高阻 R_data_come_single <= 1'b0 ; R_data_come_quad <= 1'b0 ; O_qspi_state <= C_IDLE ; end default:O_qspi_state <= C_IDLE ; endcase end end // // 功能:接收QSPI Flash發(fā)送過(guò)來(lái)的數(shù)據(jù) // always @(posedge I_clk_25M) begin if(!I_rst_n) begin R_read_bytes_cnt <= 0 ; R_read_bits_cnt <= 0 ; R_read_finish <= 1'b0 ; O_read_byte_valid <= 1'b0 ; R_read_data_reg <= 0 ; O_read_data <= 0 ; end else if(R_data_come_single) // 此信號(hào)為高表示接收數(shù)據(jù)從QSPI Flash發(fā)過(guò)來(lái)的數(shù)據(jù) begin if(R_read_bytes_cnt < R_read_bytes_num) begin if(R_read_bits_cnt < 7) //接收一個(gè)Byte的bit0~bit6 begin O_read_byte_valid <= 1'b0 ; R_read_data_reg <= {R_read_data_reg[6:0],IO_qspi_io1} ; R_read_bits_cnt <= R_read_bits_cnt + 1'b1 ; end else begin O_read_byte_valid <= 1'b1 ; //一個(gè)byte數(shù)據(jù)有效 O_read_data <= {R_read_data_reg[6:0],IO_qspi_io1} ; //接收bit7 R_read_bits_cnt <= 0 ; R_read_bytes_cnt <= R_read_bytes_cnt + 1'b1 ; end end else begin R_read_bytes_cnt <= 0 ; R_read_finish <= 1'b1 ; O_read_byte_valid <= 1'b0 ; end end else if(R_data_come_quad) begin if(R_read_bytes_cnt < R_read_bytes_num) begin //接收數(shù)據(jù) if(R_read_bits_cnt < 8'd1) begin O_read_byte_valid <= 1'b0 ; R_read_data_reg <= {R_read_data_reg[3:0],IO_qspi_io3,IO_qspi_io2,IO_qspi_io1,IO_qspi_io0};//接收前四位 R_read_bits_cnt <= R_read_bits_cnt + 1 ; end else begin O_read_byte_valid <= 1'b1 ; O_read_data <= {R_read_data_reg[3:0],IO_qspi_io3,IO_qspi_io2,IO_qspi_io1,IO_qspi_io0}; //接收后四位 R_read_bits_cnt <= 0 ; R_read_bytes_cnt <= R_read_bytes_cnt + 1'b1 ; end end else begin R_read_bytes_cnt <= 0 ; R_read_finish <= 1'b1 ; O_read_byte_valid <= 1'b0 ; end end else begin R_read_bytes_cnt <= 0 ; R_read_bits_cnt <= 0 ; R_read_finish <= 1'b0 ; O_read_byte_valid <= 1'b0 ; R_read_data_reg <= 0 ; end end rom_data rom_data_inst ( .clka(I_clk_25M), // input clka .addra(W_rom_addr), // input [7 : 0] addra .douta(W_rom_out) // output [7 : 0] douta ); endmodule
頂層測(cè)試狀態(tài)機(jī)的完整代碼如下:
module qspi_top ( input I_clk , input I_rst_n , output O_qspi_clk , // QPI總線串行時(shí)鐘線 output O_qspi_cs , // QPI總線片選信號(hào) inout IO_qspi_io0 , // QPI總線輸入/輸出信號(hào)線 inout IO_qspi_io1 , // QPI總線輸入/輸出信號(hào)線 inout IO_qspi_io2 , // QPI總線輸入/輸出信號(hào)線 inout IO_qspi_io3 // QPI總線輸入/輸出信號(hào)線 ); reg [3:0] R_state ; reg [7:0] R_flash_cmd ; reg [23:0] R_flash_addr ; reg R_clk_25M ; reg [4:0] R_cmd_type ; reg [15:0] R_status_reg ; wire W_done_sig ; wire [7:0] W_read_data ; wire W_read_byte_valid ; wire [2:0] R_qspi_state ; //功能:二分頻邏輯 always @(posedge I_clk or negedge I_rst_n) begin if(!I_rst_n) R_clk_25M <= 1'b0 ; else R_clk_25M <= ~R_clk_25M ; end //功能:測(cè)試狀態(tài)機(jī) always @(posedge R_clk_25M or negedge I_rst_n) begin if(!I_rst_n) begin R_state <= 4'd0 ; R_flash_addr <= 24'd0 ; R_flash_cmd <= 8'h00 ; R_cmd_type <= 5'b0_0000 ; end else begin case(R_state) 4'd0://讀Device ID指令 begin if(W_done_sig) begin R_flash_cmd <= 8'h00 ; R_state <= R_state + 1'b1 ; R_cmd_type <= 5'b0_0000 ; end else begin R_flash_cmd <= 8'h90 ; R_flash_addr <= 24'd0 ; R_cmd_type <= 5'b1_0000 ; end end 4'd1://寫(xiě)不使能(Write disable)指令 begin if(W_done_sig) begin R_flash_cmd <= 8'h00 ; R_state <= R_state + 1'b1 ; R_cmd_type <= 5'b0_0000 ; end else begin R_flash_cmd <= 8'h04 ; R_cmd_type <= 5'b1_0100 ; end end 4'd2://寫(xiě)使能(Write Enable)指令 begin if(W_done_sig) begin R_flash_cmd <= 8'h00 ; R_state <= R_state + 1'b1 ; R_cmd_type <= 5'b0_0000 ; end else begin R_flash_cmd <= 8'h06 ; R_cmd_type <= 5'b1_0001 ; end end 4'd3:// 扇區(qū)擦除(Sector Erase)指令 begin if(W_done_sig) begin R_flash_cmd <= 8'h00 ; R_state <= R_state + 1'b1 ; R_cmd_type <= 5'b0_0000 ; end else begin R_flash_cmd <= 8'h20 ; R_flash_addr<= 24'd0 ; R_cmd_type <= 5'b1_0010 ; end end 4'd4://讀狀態(tài)寄存器1, 當(dāng)Busy位(狀態(tài)寄存器1的最低位)為0時(shí)表示擦除操作完成 begin if(W_done_sig) begin if(W_read_data[0]==1'b0) begin R_flash_cmd <= 8'h00 ; R_state <= R_state + 1'b1 ; R_cmd_type <= 5'b0_0000 ; end else begin R_flash_cmd <= 8'h05 ; R_cmd_type <= 5'b1_0011 ; end end else begin R_flash_cmd <= 8'h05 ; R_cmd_type <= 5'b1_0011 ; end end 4'd5://寫(xiě)狀態(tài)寄存器2(Write Status Register2)指令,用來(lái)把QE(Quad Enable)位置1 begin if(W_done_sig) begin R_flash_cmd <= 8'h00 ; R_state <= R_state + 1'b1 ; R_cmd_type <= 5'b0_0000 ; end else begin R_flash_cmd <= 8'h01 ; R_cmd_type <= 5'b1_0110 ; R_status_reg<= 16'h0002 ; end end 4'd6://寫(xiě)使能(Write Enable)指令 begin if(W_done_sig) begin R_flash_cmd <= 8'h00 ; R_state <= R_state + 1'b1 ; R_cmd_type <= 5'b0_0000 ; end else begin R_flash_cmd <= 8'h06 ; R_cmd_type <= 5'b1_0001 ; end end 4'd7: //四線模式頁(yè)編程操作(Quad Page Program): 把存放在ROM中的數(shù)據(jù)寫(xiě)入QSPI Flash中 begin if(W_done_sig) begin R_flash_cmd <= 8'h00 ; R_state <= R_state + 1'b1 ; R_cmd_type <= 5'b0_0000 ; end else begin R_flash_cmd <= 8'h32 ; R_flash_addr<= 24'd0 ; R_cmd_type <= 5'b1_1000 ; end end 4'd8://讀狀態(tài)寄存器1, 當(dāng)Busy位(狀態(tài)寄存器1的最低位)為0時(shí)表示寫(xiě)操作完成 begin if(W_done_sig) begin if(W_read_data[0]==1'b0) begin R_flash_cmd <= 8'h00 ; R_state <= R_state + 1'b1 ; R_cmd_type <= 5'b0_0000 ; end else begin R_flash_cmd <= 8'h05 ; R_cmd_type <= 5'b1_0011 ; end end else begin R_flash_cmd <= 8'h05 ; R_cmd_type <= 5'b1_0011 ; end end 4'd9://四線模式讀256 Bytes數(shù)據(jù) begin if(W_done_sig) begin R_flash_cmd <= 8'h00 ; R_state <= R_state + 1'b1 ; R_cmd_type <= 5'b0_0000 ; end else begin R_flash_cmd <= 8'h6b ; R_flash_addr<= 24'd0 ; R_cmd_type <= 5'b1_1001 ; end end 4'd10:// 結(jié)束狀態(tài) begin R_flash_cmd <= 8'h00 ; R_state <= 4'd10 ; R_cmd_type <= 5'b0_0000 ; end default : R_state <= 4'd0 ; endcase end end qspi_driver U_qspi_driver ( .O_qspi_clk (O_qspi_clk ), // QPI總線串行時(shí)鐘線 .O_qspi_cs (O_qspi_cs ), // QPI總線片選信號(hào) .IO_qspi_io0 (IO_qspi_io0 ), // QPI總線輸入/輸出信號(hào)線 .IO_qspi_io1 (IO_qspi_io1 ), // QPI總線輸入/輸出信號(hào)線 .IO_qspi_io2 (IO_qspi_io2 ), // QPI總線輸入/輸出信號(hào)線 .IO_qspi_io3 (IO_qspi_io3 ), // QPI總線輸入/輸出信號(hào)線 .I_rst_n (I_rst_n ), // 復(fù)位信號(hào) .I_clk_25M (R_clk_25M ), // 25MHz時(shí)鐘信號(hào) .I_cmd_type (R_cmd_type ), // 命令類(lèi)型 .I_cmd_code (R_flash_cmd ), // 命令碼 .I_qspi_addr (R_flash_addr ), // QSPI Flash地址 .I_status_reg (R_status_reg ), // QSPI Flash狀態(tài)寄存器 .O_done_sig (W_done_sig ), // 指令執(zhí)行結(jié)束標(biāo)志 .O_read_data (W_read_data ), // 從QSPI Flash讀出的數(shù)據(jù) .O_read_byte_valid (W_read_byte_valid ), // 讀一個(gè)字節(jié)完成的標(biāo)志 .O_qspi_state (R_qspi_state ) // 狀態(tài)機(jī),用于在頂層調(diào)試用 ); wire [35:0] CONTROL0 ; wire [99:0] TRIG0 ; icon icon_inst ( .CONTROL0(CONTROL0) // INOUT BUS [35:0] ); ila ila_inst ( .CONTROL(CONTROL0) , // INOUT BUS [35:0] .CLK(I_clk) , // IN .TRIG0(TRIG0) // IN BUS [255:0] ); assign TRIG0[7:0] = W_read_data ; assign TRIG0[8] = W_read_byte_valid ; assign TRIG0[12:9] = R_state ; assign TRIG0[16:13] = R_qspi_state ; assign TRIG0[17] = W_done_sig ; assign TRIG0[18] = IO_qspi_io0 ; assign TRIG0[19] = IO_qspi_io1 ; assign TRIG0[20] = IO_qspi_io2 ; assign TRIG0[21] = IO_qspi_io3 ; assign TRIG0[22] = O_qspi_cs ; assign TRIG0[23] = O_qspi_clk ; assign TRIG0[28:24] = R_cmd_type ; assign TRIG0[36:29] = R_flash_cmd ; assign TRIG0[60:37] = R_flash_addr ; assign TRIG0[61] = I_rst_n ; assign TRIG0[77:62] = R_status_reg ; endmodule
?
接下來(lái)就對(duì)比一下各個(gè)指令的時(shí)序圖,由于有一部分時(shí)序圖在單線模式已經(jīng)對(duì)比過(guò)了,這里只對(duì)比寫(xiě)狀態(tài)寄存器,四線讀操作,四線寫(xiě)操作三個(gè)指令的時(shí)序圖
1、寫(xiě)寫(xiě)狀態(tài)寄存器(Write Status Register)指令
??????? 芯片手冊(cè)寫(xiě)狀態(tài)寄存器時(shí)序圖:
??????? ChipScope抓出來(lái)的寫(xiě)狀態(tài)寄存器時(shí)序圖:
2、四線寫(xiě)數(shù)據(jù)(Quad Input Page Program)指令
??????? 芯片手冊(cè)四線寫(xiě)數(shù)據(jù)時(shí)序圖:
??????? ChipScope抓回來(lái)的四線寫(xiě)數(shù)據(jù)時(shí)序圖:
3、四線讀數(shù)據(jù)(Quad Read Data)指令
??????? 芯片手冊(cè)四線讀數(shù)據(jù)(Fast Read Quad Output)時(shí)序圖:
ChipScope抓回來(lái)的四線讀數(shù)據(jù)(Fast Read Quad Output)時(shí)序圖:
通過(guò)對(duì)比各個(gè)指令與芯片手冊(cè)的指令,可以發(fā)現(xiàn)各個(gè)指令的時(shí)序與芯片手冊(cè)完全吻合,至此四線SPI操作QSPI Flash的代碼全部測(cè)試通過(guò)。你可以把ROM里面的數(shù)據(jù)換成你自己的數(shù)據(jù)發(fā)給QSPI Flash,ROM里面數(shù)據(jù)則可以通過(guò)4.2小節(jié)的Matlab自定義產(chǎn)生。
4.6、 四線模式與單線模式讀寫(xiě)數(shù)據(jù)時(shí)的性能對(duì)比
由于QSPI Flash四線模式中共有四根信號(hào)線,它們?nèi)靠梢杂米鲚斎胼敵鰜?lái)傳輸數(shù)據(jù),所以傳輸同樣大小的數(shù)據(jù)塊時(shí)候,四線模式的速度是單線模式的四倍。下面列出我在最初調(diào)試QSPI Flash的時(shí)候抓出來(lái)的兩張圖,分別是四線模式與單線模式寫(xiě)數(shù)據(jù)對(duì)比圖與 四線模式與單線模式讀數(shù)據(jù)對(duì)比圖
四線模式與單線模式寫(xiě)數(shù)據(jù)對(duì)比圖:
四線模式與單線模式讀數(shù)據(jù)對(duì)比圖:
從上面可以很清晰的看出讀寫(xiě)同一塊數(shù)據(jù)的時(shí)候,四線模式相較于單線模式而言,速度有了4倍的提升。
五、 進(jìn)一步思考
5.1、 用Verilog編寫(xiě)QSPI Flash驅(qū)動(dòng)的意義何在?
事實(shí)上,Xilinx的FPGA的已經(jīng)有專(zhuān)門(mén)的引腳用來(lái)與QSPI Flash相連,當(dāng)我們把.bin文件或者.mcs文件通過(guò)Jtag固化到QSPI Flash中的時(shí)候,ISE軟件中的iMPACT會(huì)自動(dòng)把.bin文件或者.mcs文件以SPI協(xié)議的格式寫(xiě)入到QSPI Flash中,由于QSPI Flash是一種非易失性存儲(chǔ)器,所以斷電后,數(shù)據(jù)并不會(huì)丟失,當(dāng)FPGA斷電重啟以后,它會(huì)自動(dòng)從QSPI Flash中加載事先固化到QSPI Flash里面的程序到內(nèi)部的RAM中執(zhí)行,而不像.bit掉電程序就會(huì)丟失。既然FPGA已經(jīng)支持QSPI Flash的協(xié)議,為什么還要寫(xiě)QSPI Flash的驅(qū)動(dòng)呢?原因是在實(shí)際的項(xiàng)目中,如果要遠(yuǎn)程對(duì)FPGA的代碼進(jìn)行在線升級(jí)的話就必須有一套QSPI Flash的驅(qū)動(dòng)來(lái)配合CPU進(jìn)行升級(jí)操作。
比如,某廠家生產(chǎn)了幾千臺(tái)4G基站,而且已經(jīng)發(fā)貨到國(guó)外各個(gè)國(guó)家商用。而后期研發(fā)人員調(diào)試的時(shí)候發(fā)現(xiàn)FPGA部分有一個(gè)小Bug需要修復(fù)或者說(shuō)需要增加一個(gè)新功能,代碼寫(xiě)好了以后如果沒(méi)有在線升級(jí)的功能,你只能一個(gè)一個(gè)去用jtag重新固化程序,這樣會(huì)耗費(fèi)大量的人力成本而且極其不方便,而如果有在線升級(jí)的功能只需要通過(guò)遠(yuǎn)程登錄連接到CPU上發(fā)送幾條指令就可以完成升級(jí)功能,大大提高了工作效率。
5.2、 關(guān)于在代碼中同時(shí)使用時(shí)鐘的上升沿和下降沿操作時(shí)有什么風(fēng)險(xiǎn)
關(guān)于這個(gè)問(wèn)題目前還沒(méi)有找到比較權(quán)威的說(shuō)法,以下兩個(gè)說(shuō)法是提的最多的:
1、FPGA內(nèi)部的觸發(fā)器都是上升沿觸發(fā)的,所以,如果在代碼中用下降沿觸發(fā)的話會(huì)在時(shí)鐘引腳的前面綜合出一個(gè)反相器,這個(gè)反相器可能會(huì)對(duì)時(shí)鐘信號(hào)的質(zhì)量有影響文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-734105.html
2、集成電路設(shè)計(jì)的書(shū)中有對(duì)晶振的描述,外部晶振產(chǎn)生的時(shí)鐘信號(hào)上升沿的時(shí)間幾乎是一樣的,但是每個(gè)周期的下降沿的時(shí)間卻無(wú)法保證完全一致,這與工藝有很大關(guān)系,所以采用下降沿觸發(fā)有可能存在風(fēng)險(xiǎn)。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-734105.html
到了這里,關(guān)于FPGA怎么讀寫(xiě)外部FLASH中的用戶(hù)數(shù)據(jù)?(超詳細(xì))的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!