作者:郝旭帥??校對(duì):陸輝
大俠好,歡迎來到FPGA技術(shù)江湖,江湖偌大,相見即是緣分。大俠可以關(guān)注FPGA技術(shù)江湖,在“闖蕩江湖”、"行俠仗義"欄里獲取其他感興趣的資源,或者一起煮酒言歡。
本系列將帶來FPGA的系統(tǒng)性學(xué)習(xí),從最基本的數(shù)字電路基礎(chǔ)開始,最詳細(xì)操作步驟,最直白的言語描述,手把手的“傻瓜式”講解,讓電子、信息、通信類專業(yè)學(xué)生、初入職場(chǎng)小白及打算進(jìn)階提升的職業(yè)開發(fā)者都可以有系統(tǒng)性學(xué)習(xí)的機(jī)會(huì)。
系統(tǒng)性的掌握技術(shù)開發(fā)以及相關(guān)要求,對(duì)個(gè)人就業(yè)以及職業(yè)發(fā)展都有著潛在的幫助,希望對(duì)大家有所幫助。后續(xù)會(huì)陸續(xù)更新 Xilinx 的 Vivado、ISE 及相關(guān)操作軟件的開發(fā)的相關(guān)內(nèi)容,學(xué)習(xí)FPGA設(shè)計(jì)方法及設(shè)計(jì)思想的同時(shí),實(shí)操結(jié)合各類操作軟件,會(huì)讓你在技術(shù)學(xué)習(xí)道路上無比的順暢,告別技術(shù)學(xué)習(xí)小BUG卡破腦殼,告別目前忽悠性的培訓(xùn)誘導(dǎo),真正的去學(xué)習(xí)去實(shí)戰(zhàn)應(yīng)用。話不多說,上貨。
SPI 協(xié)議驅(qū)動(dòng)設(shè)計(jì)
本篇實(shí)現(xiàn)基于叁芯智能科技的SANXIN -B01 FPGA開發(fā)板,以下為配套的教程,如有入手開發(fā)板,可以登錄官方淘寶店購(gòu)買,還有配套的學(xué)習(xí)視頻。
SANXIN-B01 Verilog教程-郝旭帥團(tuán)隊(duì)
SPI是串行外設(shè)接口(Serial Peripheral Interface)的縮寫。SPI,是一種高速的,全雙工,同步的通信總線,并且在芯片的管腳上只占用四根線,節(jié)約了芯片的管腳,同時(shí)為PCB的布局上節(jié)省空間,提供方便,正是出于這種簡(jiǎn)單易用的特性,如今越來越多的芯片集成了這種通信協(xié)議。
SPI的通信原理很簡(jiǎn)單,它以主從方式工作,這種模式通常有一個(gè)主設(shè)備和一個(gè)或多個(gè)從設(shè)備,中間靠三線或者四線連接(三線時(shí)為單向傳輸或者數(shù)據(jù)線雙向傳輸)。所有基于SPI的設(shè)備共有的,它們是MISO、MOSI、SCLK、CS。
MISO– Master Input Slave Output,主設(shè)備數(shù)據(jù)輸入,從設(shè)備數(shù)據(jù)輸出。
MOSI– Master Output Slave Input,主設(shè)備數(shù)據(jù)輸出,從設(shè)備數(shù)據(jù)輸入。
SCLK – Serial Clock,時(shí)鐘信號(hào),由主設(shè)備產(chǎn)生。
CS – Chip Select,從設(shè)備使能信號(hào),由主設(shè)備控制。
cs是從芯片是否被主芯片選中的控制信號(hào),也就是說只有片選信號(hào)為預(yù)先規(guī)定的使能信號(hào)時(shí)(高電位或低電位),主芯片對(duì)此從芯片的操作才有效。這就使在同一條總線上連接多個(gè)spi設(shè)備成為可能。
通訊是通過數(shù)據(jù)交換完成的,由sclk提供時(shí)鐘脈沖,mosi、miso則基于此脈沖完成數(shù)據(jù)傳輸。數(shù)據(jù)輸出通過 mosi線,數(shù)據(jù)在時(shí)鐘上升沿或下降沿時(shí)改變,在緊接著的下降沿或上升沿被讀取。完成一位數(shù)據(jù)傳輸,輸入也使用同樣原理。因此,至少需要N次時(shí)鐘信號(hào)的改變(上沿和下沿為一次),才能完成N位數(shù)據(jù)的傳輸。
spi通信有四種不同的模式,不同的從設(shè)備可能在出廠時(shí)就已經(jīng)配置為某種模式。通信的雙方必須是工作在同一模式下,所以我們可以對(duì)主設(shè)備的spi模式進(jìn)行配置,通過CPOL(時(shí)鐘極性)和CPHA(時(shí)鐘相位)來控制我們主設(shè)備的通信模式。
mode0:CPOL=0,CPHA=0;
mode1:CPOL=0,CPHA=1;
mode2:CPOL=1,CPHA=0;
mode3:CPOL=1,CPHA=1;
時(shí)鐘極性CPOL是用來配置SCLK在空閑時(shí),應(yīng)該處于的狀態(tài);時(shí)鐘相位CPHA用來配置在第幾個(gè)邊沿進(jìn)行采樣。
CPOL=0,表示在空閑狀態(tài)時(shí),時(shí)鐘SCLK為低電平。
CPOL=1,表示在空閑狀態(tài)時(shí),時(shí)鐘SCLK為高電平。
CPHA=0,表示數(shù)據(jù)采樣是在第1個(gè)邊沿。
CPHA=1,表示數(shù)據(jù)采樣是在第2個(gè)邊沿。
即:
CPOL=0,CPHA=0:此時(shí)空閑態(tài)時(shí),SCLK處于低電平,數(shù)據(jù)采樣是在第1個(gè)邊沿,也就是SCLK由低電平到高電平的跳變,所以數(shù)據(jù)采樣是在上升沿,數(shù)據(jù)發(fā)送是在下降沿。
CPOL=0,CPHA=1:此時(shí)空閑態(tài)時(shí),SCLK處于低電平,數(shù)據(jù)發(fā)送是在第1個(gè)邊沿,也就是SCLK由低電平到高電平的跳變,所以數(shù)據(jù)采樣是在下降沿,數(shù)據(jù)發(fā)送是在上升沿。
CPOL=1,CPHA=0:此時(shí)空閑態(tài)時(shí),SCLK處于高電平,數(shù)據(jù)采集是在第1個(gè)邊沿,也就是SCLK由高電平到低電平的跳變,所以數(shù)據(jù)采集是在下降沿,數(shù)據(jù)發(fā)送是在上升沿。
CPOL=1,CPHA=1:此時(shí)空閑態(tài)時(shí),SCLK處于高電平,數(shù)據(jù)發(fā)送是在第1個(gè)邊沿,也就是SCLK由高電平到低電平的跳變,所以數(shù)據(jù)采集是在上升沿,數(shù)據(jù)發(fā)送是在下降沿。
硬件簡(jiǎn)介
FLASH閃存 的英文名稱是"Flash Memory",一般簡(jiǎn)稱為"Flash",它屬于內(nèi)存器件的一種,是一種非易失性( Non-Volatile )內(nèi)存。
在開發(fā)板上有一塊flash(M25P16),用來保存FPGA的硬件配置信息,也可以用來存儲(chǔ)用戶的應(yīng)用程序或數(shù)據(jù)。
M25P16是一款帶有寫保護(hù)機(jī)制和高速SPI總線訪問的2M字節(jié)串行Flash存儲(chǔ)器,該存儲(chǔ)器主要特點(diǎn):2M字節(jié)的存儲(chǔ)空間,分32個(gè)扇區(qū),每個(gè)扇區(qū)256頁,每頁256字節(jié);能單個(gè)扇區(qū)擦除和整片擦除;每扇區(qū)擦寫次數(shù)保證10萬次、數(shù)據(jù)保存期限至少20年。
C(serial clock:串行時(shí)鐘)為D和Q提供了數(shù)據(jù)輸入或者輸出的時(shí)序。D的數(shù)據(jù)總是在C的上升沿被采樣。Q的數(shù)據(jù) 在C的下降沿被輸出。
(Chip Select:芯片選擇端),當(dāng)輸入為低時(shí),該芯片被選中,可以允許進(jìn)行讀寫操作。當(dāng)輸入為高時(shí),該芯片被釋放,不能夠進(jìn)行操作。
對(duì)于H——o——l——d——和W——, 為保持功能和硬件寫保護(hù)功能,在本設(shè)計(jì)中不使用此管腳,在硬件設(shè)計(jì)時(shí),這兩個(gè)管腳全部被拉高了,即全部失效。
flash采用spi的通信協(xié)議,flash當(dāng)做從機(jī)。serial clcok等效于spi中的sclk,chip select等效于spi中的cs,D等效于spi中的mosi,Q等效于spi中的miso。
flash可以支持mode0和mode3,這兩種模式中,都是在時(shí)鐘的上升沿采樣,在時(shí)鐘的下降沿發(fā)送數(shù)據(jù)。
flash的每一頁都可以被寫入,但是寫入只能是把1改變?yōu)?。擦除可以把0改變?yōu)?。所以在正常寫入數(shù)據(jù)之前,都要將flash進(jìn)行擦除。
flash的命令表如下:
下面介紹幾個(gè)常用的命令。
RDID(Read Identification :讀ID):發(fā)送命令RDID(9F),然后接收第1個(gè)字節(jié)的memory type(20H),第二個(gè)字節(jié)的memory capacity(15H)。后續(xù)的字節(jié)暫不關(guān)心。
WREN(Write Enable :寫使能):在任何寫或者擦除的命令之前,都必須首先打開寫使能。打開寫使能為發(fā)送命令WREN(06h)。
RDSR(Read Status Register:讀狀態(tài)寄存器):發(fā)送命令RDSR(05h),然后返回一個(gè)字節(jié)的狀態(tài)值。
狀態(tài)寄存器的格式如下:
WIP(Write In Progress bit)表示flash內(nèi)部是否正在進(jìn)行內(nèi)部操作,寫和擦除都會(huì)導(dǎo)致flash內(nèi)部進(jìn)行一段時(shí)間的工作,在內(nèi)部工作期間,外部的命令會(huì)被忽略,所以在進(jìn)行任何命令之前,都需要查看flash內(nèi)部是否正在工作。WIP為1時(shí),表示flash內(nèi)部正在工作;WIP為0時(shí),表示flash內(nèi)部沒有在工作。
READ(Read DATA Bytes:讀數(shù)據(jù)):發(fā)送命令READ(03H),后續(xù)發(fā)送3個(gè)字節(jié)的地址,然后就可以接收數(shù)據(jù),內(nèi)部的地址會(huì)不斷遞增。一個(gè)讀命令就可以把整個(gè)flash全部讀完。
PP(Page Program :頁編寫):發(fā)送命令PP(02H),接著發(fā)送3個(gè)字節(jié)的地址,然后發(fā)送數(shù)據(jù)即可。切記所寫的數(shù)據(jù)不能超過本頁的地址范圍。
SE(Sector Erase :扇區(qū)擦除):發(fā)送命令SE(D8H),接著發(fā)送3個(gè)字節(jié)的地址。
BE(Bulk Erase:整片擦除):發(fā)送命令BE(C7H)。
關(guān)于flash的其他的介紹,可以參考03_芯片手冊(cè)->FLASH->M25P16.pdf。
設(shè)計(jì)要求
設(shè)計(jì)flash(M25P16)控制器。
設(shè)計(jì)分析
根據(jù)M25P16的數(shù)據(jù)手冊(cè)得知,其接口為spi接口,且支持模式0和模式3,本設(shè)計(jì)中選擇模式0。
輸入時(shí)序圖如下:
輸出時(shí)序如下:
時(shí)序圖中所對(duì)應(yīng)的符號(hào)說明:
根據(jù)輸入和輸出的時(shí)序圖以及參數(shù)表,將SPI的時(shí)鐘的頻率定為10MHz。
在設(shè)計(jì)中,F(xiàn)PGA作為主機(jī),M25P16作為從機(jī)。
架構(gòu)設(shè)計(jì)和信號(hào)說明
此模塊命名為m25p16_drive。
二級(jí)模塊(分模塊)(第一頁)
二級(jí)模塊(分模塊)(第二頁)
設(shè)計(jì)中,各個(gè)命令單獨(dú)寫出控制器,通過多路選擇器選擇出對(duì)應(yīng)的命令,然后控制spi_8bit_drive將數(shù)據(jù)按照spi的協(xié)議發(fā)送出去。各個(gè)命令的脈沖通過ctrl模塊進(jìn)行控制各個(gè)命令控制器,寫入的數(shù)據(jù)首先寫入到寫緩沖區(qū),讀出的數(shù)據(jù)讀出后寫入到讀緩沖區(qū)。
暫不分配的端口,在應(yīng)用時(shí)都是由上游模塊進(jìn)行控制,本設(shè)計(jì)測(cè)試時(shí),編寫上游模塊進(jìn)行測(cè)試。
各個(gè)模塊的功能,和連接線的功能在各個(gè)模塊設(shè)計(jì)中說明。
spi_8bit_drive設(shè)計(jì)實(shí)現(xiàn)
本模塊負(fù)責(zé)將8bit的并行數(shù)據(jù)按照spi協(xié)議發(fā)送出去,以及負(fù)責(zé)按照spi協(xié)議接收數(shù)據(jù),將接收的數(shù)據(jù)(8bit)并行傳輸給各個(gè)模塊。
spi_send_en為發(fā)送數(shù)據(jù)使能信號(hào)(脈沖信號(hào)),spi_send_data為所要發(fā)送數(shù)據(jù),spi_send_done為發(fā)送完成信號(hào)(脈沖信號(hào))。
spi_read_en為接收數(shù)據(jù)使能信號(hào)(脈沖信號(hào)),spi_read_data為所接收的數(shù)據(jù),spi_read_done為接收完成信號(hào)(脈沖信號(hào))。
module spi_8bit_drive (
input wire clk,
input wire rst_n,
input wire spi_send_en,
input wire [7:0] spi_send_data,
output reg spi_send_done,
input wire spi_read_en,
output reg [7:0] spi_read_data,
output reg spi_read_done,
output wire spi_sclk,
output wire spi_mosi,
input wire spi_miso
);
reg [8:0] send_data_buf;
reg [3:0] send_cnt;
reg rec_en;
reg rec_en_n;
reg [3:0] rec_cnt;
reg [7:0] rec_data_buf;
always @ (negedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
send_data_buf <= 9'd0;
else
if (spi_send_en == 1'b1)
send_data_buf <= {spi_send_data, 1'b0};
else
send_data_buf <= send_data_buf;
end
always @ (negedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
send_cnt <= 4'd8;
else
if (spi_send_en == 1'b1)
send_cnt <= 4'd0;
else
if (send_cnt < 4'd8)
send_cnt <= send_cnt + 1'b1;
else
send_cnt <= send_cnt;
end
assign spi_mosi = send_data_buf[8 - send_cnt];
assign spi_sclk = (send_cnt < 4'd8 || rec_en_n == 1'b1) ? clk : 1'b0;
always @ (negedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
spi_send_done <= 1'b0;
else
if (send_cnt == 4'd7)
spi_send_done <= 1'b1;
else
spi_send_done <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
rec_en <= 1'b0;
else
if (spi_read_en == 1'b1)
rec_en <= 1'b1;
else
if (rec_cnt == 4'd7)
rec_en <= 1'b0;
else
rec_en <= rec_en;
end
always @ (negedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
rec_en_n <= 1'b0;
else
rec_en_n <= rec_en;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
rec_data_buf <= 8'd0;
else
if (rec_en == 1'b1)
rec_data_buf <= {rec_data_buf[6:0], spi_miso};
else
rec_data_buf <=rec_data_buf;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
rec_cnt <= 4'd0;
else
if (rec_en == 1'b1)
rec_cnt <= rec_cnt + 1'b1;
else
rec_cnt <= 4'd0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
spi_read_done <= 1'b0;
else
if (rec_cnt == 4'd8)
spi_read_done <= 1'b1;
else
spi_read_done <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
spi_read_data <= 8'd0;
else
if (rec_cnt == 4'd8)
spi_read_data <= rec_data_buf;
else
spi_read_data <= spi_read_data;
end
endmodule
在發(fā)送邏輯控制中,全部的信號(hào)采用下降沿驅(qū)動(dòng)。利用外部給予的spi_send_en作為啟動(dòng)信號(hào),啟動(dòng)send_cnt。send_cnt在不發(fā)送數(shù)據(jù)時(shí)為8,發(fā)送數(shù)據(jù)時(shí),從0到7。
在接收邏輯中,全部的信號(hào)采用上升沿驅(qū)動(dòng)。利用外部給予的spi_read_en作為啟動(dòng)信號(hào),啟動(dòng)rec_en,經(jīng)過移位接收數(shù)據(jù)。
在spi_sclk輸出時(shí),采用組合邏輯。由于設(shè)計(jì)采用spi的模式0,故而spi_sclk不發(fā)送或者接收數(shù)據(jù)時(shí)為0,接收數(shù)據(jù)時(shí)為時(shí)鐘信號(hào)。因?yàn)橐鬄槟J?,所以在接收數(shù)據(jù)時(shí),spi_sclk的輸出不能夠先有下降沿,即要求spi_sclk的控制信號(hào)不能由上升沿信號(hào)驅(qū)動(dòng),所以將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
在仿真中,將時(shí)鐘設(shè)置為10MHz。
所有的信號(hào)采用上升沿驅(qū)動(dòng)。發(fā)送一個(gè)8bit的隨機(jī)數(shù)值,接收一個(gè)8bit的隨機(jī)數(shù)值。
spi_miso信號(hào)為從機(jī)下降沿驅(qū)動(dòng)信號(hào)。
通過RTL仿真,可以看出發(fā)送和接收全部正常。
mux7_1設(shè)計(jì)實(shí)現(xiàn)
本模塊負(fù)責(zé)將7個(gè)命令模塊發(fā)出的命令(寫使能、寫數(shù)據(jù)和讀使能)經(jīng)過選擇發(fā)送給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
在設(shè)計(jì)中,有的命令模塊不需要進(jìn)行讀?。╬p和se等等),此時(shí)將輸出的讀使能信號(hào)輸出為低電平。
be設(shè)計(jì)實(shí)現(xiàn)
該模塊接收到be_en(整片擦除的脈沖信號(hào))信號(hào)后,發(fā)送對(duì)應(yīng)的使能和數(shù)據(jù),等待發(fā)送完成脈沖。發(fā)送完成后,輸出擦除完成的脈沖。
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設(shè)計(jì)實(shí)現(xiàn)
該模塊接收到wren_en(打開flash內(nèi)部的寫使能的脈沖信號(hào))信號(hào)后,發(fā)送對(duì)應(yīng)的使能和數(shù)據(jù),等待發(fā)送完成脈沖。發(fā)送完成后,輸出擦除完成的脈沖。
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內(nèi)部寫使能的命令碼為8’h06。
se設(shè)計(jì)實(shí)現(xiàn)
該模塊接收到se_en(擦除扇區(qū)的寫使能的脈沖信號(hào))信號(hào)后,發(fā)送對(duì)應(yīng)的使能和數(shù)據(jù),等待發(fā)送完成脈沖。發(fā)送完成后,接著發(fā)送高八位地址,中間八位地址和低八位地址。全部發(fā)送完成后,發(fā)送se_done信號(hào)。
該模塊采用狀態(tài)機(jī)實(shí)現(xiàn)。SE_STATE(扇區(qū)擦除命令發(fā)送)、H_ADDR(高八位地址發(fā)送)、M_ADDR(中間八位地址發(fā)送)、L_ADDR(低八位地址發(fā)送)、SE_DONE(扇區(qū)擦除完成)。所有的脈沖信號(hào)在未標(biāo)注的時(shí)刻,輸出全部為0。
設(shè)計(jì)代碼為:
module se (
input wire clk,
input wire rst_n,
input wire se_en,
input wire [23:0] se_addr,
output reg se_done,
output reg se_send_en,
output reg [7:0] se_send_data,
input wire spi_send_done
);
localparam SE_STATE = 5'b00001;
localparam H_ADDR = 5'b00010;
localparam M_ADDR = 5'b00100;
localparam L_ADDR = 5'b01000;
localparam SE_DONE = 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 <= SE_STATE;
else
c_state <= n_state;
end
always @ * begin
case (c_state)
SE_STATE : begin
if (se_en == 1'b0)
n_state = SE_STATE;
else
n_state = H_ADDR;
end
H_ADDR : begin
if (spi_send_done == 1'b0)
n_state = H_ADDR;
else
n_state = M_ADDR;
end
M_ADDR : begin
if (spi_send_done == 1'b0)
n_state = M_ADDR;
else
n_state = L_ADDR;
end
L_ADDR : begin
if (spi_send_done == 1'b0)
n_state = L_ADDR;
else
n_state = SE_DONE;
end
SE_DONE : begin
if (spi_send_done == 1'b0)
n_state = SE_DONE;
else
n_state = SE_STATE;
end
default : n_state = SE_STATE;
endcase
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
se_send_en <= 1'b0;
else
case (c_state)
SE_STATE : begin
if (se_en == 1'b1)
se_send_en <= 1'b1;
else
se_send_en <= 1'b0;
end
H_ADDR : begin
if (spi_send_done == 1'b1)
se_send_en <= 1'b1;
else
se_send_en <= 1'b0;
end
M_ADDR : begin
if (spi_send_done == 1'b1)
se_send_en <= 1'b1;
else
se_send_en <= 1'b0;
end
L_ADDR : begin
if (spi_send_done == 1'b1)
se_send_en <= 1'b1;
else
se_send_en <= 1'b0;
end
SE_DONE : begin
se_send_en <= 1'b0;
end
default : se_send_en <= 1'b0;
endcase
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
se_send_data <= 8'd0;
else
case (c_state)
SE_STATE : begin
if (se_en == 1'b1)
se_send_data <= 8'hd8;
else
se_send_data <= 8'd0;
end
H_ADDR : begin
if (spi_send_done == 1'b1)
se_send_data <= se_addr[23:16];
else
se_send_data <= 8'd0;
end
M_ADDR : begin
if (spi_send_done == 1'b1)
se_send_data <= se_addr[15:8];
else
se_send_data <= 8'd0;
end
L_ADDR : begin
if (spi_send_done == 1'b1)
se_send_data <= se_addr[7:0];
else
se_send_data <= 8'd0;
end
SE_DONE : begin
se_send_data <= 8'd0;
end
default : se_send_data <= 8'd0;
endcase
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
se_done <= 1'b0;
else
if (c_state == SE_DONE && spi_send_done == 1'b1)
se_done <= 1'b1;
else
se_done <= 1'b0;
end
endmodule
在發(fā)送過程中,由于是每8bit發(fā)送一次,所以在時(shí)序上將看到發(fā)送時(shí),每8個(gè)脈沖一組,中間會(huì)有明顯的間隔。
pp設(shè)計(jì)實(shí)現(xiàn)
該模塊負(fù)責(zé)將外部寫fifo中的數(shù)據(jù)寫入到flash中。wr_fifo_rd為寫fifo的讀使能信號(hào),wrdata為從寫fifo中讀出的數(shù)據(jù),wr_len為需要寫入flash中數(shù)據(jù)的長(zhǎng)度,wr_addr為寫入地址。
該模塊采用狀態(tài)機(jī)實(shí)現(xiàn)。PP_STATE(發(fā)送pp命令),H_ADDR(發(fā)送高八位地址)、M_ADDR(發(fā)送中間八位地址),L_ADDR(發(fā)送低八位地址)、RDFIFO(讀寫fifo)、FIFO_WAIT(等待讀寫fifo的數(shù)據(jù)輸出)、SEND(發(fā)送8bit數(shù)據(jù))、SEND_WAIT(發(fā)送等待,發(fā)送完成后判斷是否發(fā)送完成)。對(duì)于所有的脈沖信號(hào),沒有賦值的位置,全部賦值為0。
cnt為記錄已經(jīng)發(fā)送的數(shù)據(jù)個(gè)數(shù)。
設(shè)計(jì)代碼為:
module pp (
input wire clk,
input wire rst_n,
input wire pp_en,
output reg pp_done,
output reg wr_fifo_rd,
input wire [7:0] wrdata,
input wire [8:0] wr_len,
input wire [23:0] wr_addr,
output reg pp_send_en,
output reg [7:0] pp_send_data,
input wire spi_send_done
);
localparam PP_STATE = 8'b0000_0001;
localparam H_ADDR = 8'b0000_0010;
localparam M_ADDR = 8'b0000_0100;
localparam L_ADDR = 8'b0000_1000;
localparam RDFIFO = 8'b0001_0000;
localparam FIFO_WAIT = 8'b0010_0000;
localparam SEND = 8'b0100_0000;
localparam SEND_WAIT = 8'b1000_0000;
reg [7:0] c_state;
reg [7:0] n_state;
reg [8:0] cnt;
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
c_state <= PP_STATE;
else
c_state <= n_state;
end
always @ * begin
case (c_state)
PP_STATE : begin
if (pp_en == 1'b0)
n_state = PP_STATE;
else
n_state = H_ADDR;
end
H_ADDR : begin
if (spi_send_done == 1'b1)
n_state = M_ADDR;
else
n_state = H_ADDR;
end
M_ADDR : begin
if (spi_send_done == 1'b1)
n_state = L_ADDR;
else
n_state = M_ADDR;
end
L_ADDR : begin
if (spi_send_done == 1'b1)
n_state = RDFIFO;
else
n_state = L_ADDR;
end
RDFIFO : begin
if (spi_send_done == 1'b1)
n_state = FIFO_WAIT;
else
n_state = RDFIFO;
end
FIFO_WAIT : begin
n_state = SEND;
end
SEND : begin
n_state = SEND_WAIT;
end
SEND_WAIT : begin
if (spi_send_done == 1'b1)
if (cnt == wr_len)
n_state = PP_STATE;
else
n_state = FIFO_WAIT;
else
n_state = SEND_WAIT;
end
default : n_state = PP_STATE;
endcase
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
pp_send_en <= 1'b0;
else
case (c_state)
PP_STATE : begin
if (pp_en == 1'b1)
pp_send_en <= 1'b1;
else
pp_send_en <= 1'b0;
end
H_ADDR : begin
if (spi_send_done == 1'b1)
pp_send_en <= 1'b1;
else
pp_send_en <= 1'b0;
end
M_ADDR : begin
if (spi_send_done == 1'b1)
pp_send_en <= 1'b1;
else
pp_send_en <= 1'b0;
end
L_ADDR : begin
if (spi_send_done == 1'b1)
pp_send_en <= 1'b1;
else
pp_send_en <= 1'b0;
end
SEND : begin
pp_send_en <= 1'b1;
end
default : pp_send_en <= 1'b0;
endcase
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
pp_send_data <= 8'd0;
else
case (c_state)
PP_STATE : begin
if (pp_en == 1'b1)
pp_send_data <= 8'h02;
else
pp_send_data <= 8'd0;
end
H_ADDR : begin
if (spi_send_done == 1'b1)
pp_send_data <= wr_addr[23:16];
else
pp_send_data <= 8'd0;
end
M_ADDR : begin
if (spi_send_done == 1'b1)
pp_send_data <= wr_addr[15:8];
else
pp_send_data <= 8'd0;
end
L_ADDR : begin
if (spi_send_done == 1'b1)
pp_send_data <= wr_addr[7:0];
else
pp_send_data <= 8'd0;
end
SEND : begin
pp_send_data <= wrdata;
end
default : pp_send_data <= 8'd0;
endcase
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
pp_done <= 1'b0;
else
if (c_state == SEND_WAIT && spi_send_done == 1'b1 && cnt == wr_len)
pp_done <= 1'b1;
else
pp_done <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
cnt <= 9'd0;
else
if ((c_state == RDFIFO && spi_send_done == 1'b1) || (c_state == SEND_WAIT && spi_send_done == 1'b1 && cnt < wr_len))
cnt <= cnt + 1'b1;
else
if (c_state == PP_STATE)
cnt <= 9'd0;
else
cnt <= cnt;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
wr_fifo_rd <= 1'b1;
else
if ((c_state == RDFIFO && spi_send_done == 1'b1) || (c_state == SEND_WAIT && spi_send_done == 1'b1 && cnt < wr_len))
wr_fifo_rd <= 1'b1;
else
wr_fifo_rd <= 1'b0;
end
endmodule
rdsr設(shè)計(jì)實(shí)現(xiàn)
本模塊的功能為讀取m25p16的狀態(tài)寄存器,主要檢測(cè)狀態(tài)寄存器的最低位(WIP)。
WIP(write in progress :正在進(jìn)行寫進(jìn)程),該bie位表示了flash內(nèi)部是否在進(jìn)行寫進(jìn)程。如果處于寫進(jìn)程時(shí),flash忽略外部所有的命令,所以建議在執(zhí)行任何命令前,首先進(jìn)行檢測(cè)該位。1表示正在寫進(jìn)程中,0表示不處于寫進(jìn)程。
如果檢測(cè)到正在寫進(jìn)程中,進(jìn)行延遲1ms,然后再次讀取該位狀態(tài)。直到寫進(jìn)程結(jié)束。
本模塊采用狀態(tài)機(jī)設(shè)計(jì)實(shí)現(xiàn)。ILDE(發(fā)送讀狀態(tài)寄存器命令)、RDSRSTATE(發(fā)送讀使能)、WIP(判斷wip位)、 DELAY1ms(延遲1ms)。cnt為延遲1ms的計(jì)數(shù)器。
設(shè)計(jì)代碼為:
module rdsr (
input wire clk,
input wire rst_n,
input wire rdsr_en,
output reg rdsr_done,
output reg rdsr_send_en,
output reg [7:0] rdsr_send_data,
input wire spi_send_done,
output reg rdsr_read_en,
input wire [7:0] spi_read_data,
input wire spi_read_done
);
parameter T_1ms = 50_000;
localparam IDLE = 4'b0001;
localparam RDSRSTATE = 4'b0010;
localparam WIP = 4'b0100;
localparam DELAY1ms = 4'b1000;
reg [3:0] c_state;
reg [3:0] n_state;
reg [15:0] cnt;
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 (rdsr_en == 1'b0)
n_state = IDLE;
else
n_state = RDSRSTATE;
end
RDSRSTATE : begin
if (spi_send_done == 1'b1)
n_state = WIP;
else
n_state = RDSRSTATE;
end
WIP : begin
if (spi_read_done == 1'b0)
n_state = WIP;
else
if (spi_read_data[0] == 1'b0)
n_state = IDLE;
else
n_state = DELAY1ms;
end
DELAY1ms : begin
if (cnt < T_1ms - 1'b1)
n_state = DELAY1ms;
else
n_state = RDSRSTATE;
end
default : n_state = IDLE;
endcase
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
cnt <= 16'd0;
else
if (c_state == DELAY1ms && cnt < T_1ms - 1'b1)
cnt <= cnt + 1'b1;
else
cnt <= 16'd0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
rdsr_done <= 1'b0;
else
if (c_state == WIP && spi_read_done == 1'b1 && spi_read_data[0] == 1'b0)
rdsr_done <= 1'b1;
else
rdsr_done <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
rdsr_send_data <= 8'd0;
else
if ((c_state == IDLE && rdsr_en == 1'b1) || (c_state == DELAY1ms && cnt == T_1ms - 1'b1))
rdsr_send_data <= 8'h05;
else
rdsr_send_data <= 8'd0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
rdsr_send_en <= 1'b0;
else
if ((c_state == IDLE && rdsr_en == 1'b1) || (c_state == DELAY1ms && cnt == T_1ms - 1'b1))
rdsr_send_en <= 1'b1;
else
rdsr_send_en <= 1'd0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
rdsr_read_en <= 1'b0;
else
if (c_state == RDSRSTATE && spi_send_done == 1'b1)
rdsr_read_en <= 1'b1;
else
rdsr_read_en <= 1'b0;
end
endmodule
rdid設(shè)計(jì)實(shí)現(xiàn)
該模塊負(fù)責(zé)讀取flash的ID(2015),驗(yàn)證ID的正確性。
該模塊采用狀態(tài)機(jī)的方式實(shí)現(xiàn)。IDLE(等待讀取ID的命令)、IDSTATE1(讀取高八位ID)、IDSTATE2(讀取中間八位ID)、IDSTATE3(讀取低八位ID)、ID_CHECK(檢測(cè)ID的正確性)。
狀態(tài)轉(zhuǎn)移圖如下:
設(shè)計(jì)代碼為:
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設(shè)計(jì)實(shí)現(xiàn)
該模塊負(fù)責(zé)將flash的數(shù)據(jù)讀出,寫入到輸出緩存中。
該模塊采用狀態(tài)機(jī)實(shí)現(xiàn)。RD_STATE(等待讀命令)、H_ADDR(發(fā)送高八位地址)、M_ADDR(發(fā)送中間八位地址)、L_ADDR(發(fā)送低八位地址)、RDDATA(讀取數(shù)據(jù))、WRFIFO(將讀出的數(shù)據(jù)寫入到FIFO中)、CHECK_LEN(判斷讀取的長(zhǎng)度)。
狀態(tài)轉(zhuǎn)移圖如下:
設(shè)計(jì)代碼為:
module read_ctrl (
input wire clk,
input wire rst_n,
input wire read_en,
input wire [23:0] rd_addr,
input wire [8:0] rd_len,
output reg [7:0] rddata,
output reg rd_fifo_wr,
output reg read_done,
output reg read_send_en,
output reg [7:0] read_send_data,
input wire spi_send_done,
output reg read_read_en,
input wire spi_read_done,
input wire [7:0] spi_read_data
);
localparam RD_STATE = 7'b000_0001;
localparam H_ADDR = 7'b000_0010;
localparam M_ADDR = 7'b000_0100;
localparam L_ADDR = 7'b000_1000;
localparam RDDATA = 7'b001_0000;
localparam WRFIFO = 7'b010_0000;
localparam CHECK_LEN = 7'b100_0000;
reg [6:0] c_state;
reg [6:0] n_state;
reg [8:0] cnt;
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
c_state <= RD_STATE;
else
c_state <= n_state;
end
always @ * begin
case (c_state)
RD_STATE : begin
if (read_en == 1'b1)
n_state = H_ADDR;
else
n_state = RD_STATE;
end
H_ADDR : begin
if (spi_send_done == 1'b1)
n_state = M_ADDR;
else
n_state = H_ADDR;
end
M_ADDR : begin
if (spi_send_done == 1'b1)
n_state = L_ADDR;
else
n_state = M_ADDR;
end
L_ADDR : begin
if (spi_send_done == 1'b1)
n_state = RDDATA;
else
n_state = L_ADDR;
end
RDDATA : begin
if (spi_send_done == 1'b1)
n_state = WRFIFO;
else
n_state = RDDATA;
end
WRFIFO : begin
if (spi_read_done == 1'b1)
n_state = CHECK_LEN;
else
n_state = WRFIFO;
end
CHECK_LEN : begin
if (cnt == rd_len)
n_state = RD_STATE;
else
n_state = WRFIFO;
end
default : n_state = RD_STATE;
endcase
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
cnt <= 9'd0;
else
if ((c_state == RDDATA && spi_send_done == 1'b1) || (c_state == CHECK_LEN && cnt < rd_len))
cnt <= cnt + 1'b1;
else
if (c_state == RD_STATE)
cnt <= 9'd0;
else
cnt <= cnt;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
read_read_en <= 1'b0;
else
if ((c_state == RDDATA && spi_send_done == 1'b1) || (c_state == CHECK_LEN && cnt < rd_len))
read_read_en <= 1'b1;
else
read_read_en <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
read_done <= 1'b0;
else
if (c_state == CHECK_LEN && cnt == rd_len)
read_done <= 1'b1;
else
read_done <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
rd_fifo_wr <= 1'b0;
else
if (c_state == WRFIFO && spi_read_done == 1'b1)
rd_fifo_wr <= 1'b1;
else
rd_fifo_wr <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
rddata <= 8'd0;
else
if (c_state == WRFIFO && spi_read_done == 1'b1)
rddata <= spi_read_data;
else
rddata <= 8'd0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
read_send_en <= 1'b0;
else
case (c_state)
RD_STATE : begin
if (read_en == 1'b1)
read_send_en <= 1'b1;
else
read_send_en <= 1'b0;
end
H_ADDR : begin
if (spi_send_done == 1'b1)
read_send_en <= 1'b1;
else
read_send_en <= 1'b0;
end
M_ADDR : begin
if (spi_send_done == 1'b1)
read_send_en <= 1'b1;
else
read_send_en <= 1'b0;
end
L_ADDR : begin
if (spi_send_done == 1'b1)
read_send_en <= 1'b1;
else
read_send_en <= 1'b0;
end
default : read_send_en <= 1'b0;
endcase
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
read_send_data <= 8'd0;
else
case (c_state)
RD_STATE : begin
if (read_en == 1'b1)
read_send_data <= 8'h03;
else
read_send_data <= 8'd0;
end
H_ADDR : begin
if (spi_send_done == 1'b1)
read_send_data <= rd_addr[23:16];
else
read_send_data <= 8'd0;
end
M_ADDR : begin
if (spi_send_done == 1'b1)
read_send_data <= rd_addr[15:8];
else
read_send_data <= 8'd0;
end
L_ADDR : begin
if (spi_send_done == 1'b1)
read_send_data <= rd_addr[7:0];
else
read_send_data <= 8'd0;
end
default : read_send_data <= 8'd0;
endcase
end
endmodule
wr_fifo和rd_fifo調(diào)用
兩個(gè)fifo的寬度設(shè)置為8,深度設(shè)置為256,同步fifo,帶有復(fù)位。
ctrl設(shè)計(jì)實(shí)現(xiàn)
該模塊根據(jù)外部的命令,按照m25p16的執(zhí)行規(guī)則,進(jìn)行控制各個(gè)模塊的執(zhí)行。
該模塊采用狀態(tài)機(jī)實(shí)現(xiàn)。INIT_RDSR(讀WIP),INIT_RDID(讀ID),INIT_ID(判斷ID),WIP(讀WIP),WIP_DONE(等待WIP),IDLE(空閑狀態(tài)),**STATE(執(zhí)行對(duì)應(yīng)的命令),**WREN(打開flash的寫使能)。在進(jìn)行任何命令前,都檢查wip。
狀態(tài)轉(zhuǎn)移圖如下:
在不同的狀態(tài),mux_sel選擇對(duì)應(yīng)的命令通過。
drive_busy只有在IDLE狀態(tài)才是低電平。
spi_cs_n信號(hào), DLE狀態(tài)為高電平、WIP_DONE(INIT_RDID)中spi_read_done信號(hào)為高時(shí) (保證能夠多次讀取狀態(tài)寄存器)、在其他狀態(tài)發(fā)生切換時(shí),spi_cs_n 為高電平,否則為低電平。
設(shè)計(jì)代碼為:
module ctrl (
input wire clk,
input wire rst_n,
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,
output wire drive_busy,
output reg spi_cs_n,
input wire spi_read_done,
output reg rdsr_en,
input wire rdsr_done,
output reg wren_en,
input wire wren_done,
output reg pp_en,
output reg [23:0] wr_addr,
output reg [8:0] wr_len,
input wire pp_done,
output reg be_en,
input wire be_done,
output reg se_en,
output reg [23:0] se_addr,
input wire se_done,
output reg rdid_en,
input wire rdid_done,
output reg read_en,
output reg [23:0] rd_addr,
output reg [8:0] rd_len,
input wire read_done,
output reg [2:0] mux_sel
);
localparam INIT_RDSR = 13'b0000_0000_00001;
localparam INIT_RDID = 13'b0000_0000_00010;
localparam INIT_ID = 13'b0000_0000_00100;
localparam WIP = 13'b0000_0000_01000;
localparam WIP_DONE = 13'b0000_0000_10000;
localparam IDLE = 13'b0000_0001_00000;
localparam RDSTATE = 13'b0000_0010_00000;
localparam PPWREN = 13'b0000_0100_00000;
localparam PPSTATE = 13'b0000_1000_00000;
localparam SEWREN = 13'b0001_0000_00000;
localparam SESTATE = 13'b0010_0000_00000;
localparam BEWREN = 13'b0100_0000_00000;
localparam BESTATE = 13'b1000_0000_00000;
reg [12:0] c_state;
reg [12:0] n_state;
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
c_state <= INIT_RDSR;
else
c_state <= n_state;
end
always @ * begin
case (c_state)
INIT_RDSR : begin
n_state = INIT_RDID;
end
INIT_RDID : begin
if (rdsr_done == 1'b1)
n_state = INIT_ID;
else
n_state = INIT_RDID;
end
INIT_ID : begin
if (rdid_done == 1'b1)
n_state = WIP;
else
n_state = INIT_ID;
end
WIP : begin
n_state = WIP_DONE;
end
WIP_DONE : begin
if (rdsr_done == 1'b1)
n_state = IDLE;
else
n_state = WIP_DONE;
end
IDLE : begin
if (flag_wr == 1'b1)
n_state = PPWREN;
else
if (flag_rd == 1'b1)
n_state = RDSTATE;
else
if (flag_be == 1'b1)
n_state = BEWREN;
else
if (flag_se == 1'b1)
n_state = SEWREN;
else
n_state = IDLE;
end
RDSTATE : begin
if (read_done == 1'b1)
n_state = WIP;
else
n_state = RDSTATE;
end
PPWREN : begin
if (wren_done == 1'b1)
n_state = PPSTATE;
else
n_state = PPWREN;
end
PPSTATE : begin
if (pp_done == 1'b1)
n_state = WIP;
else
n_state = PPSTATE;
end
SEWREN : begin
if (wren_done == 1'b1)
n_state = SESTATE;
else
n_state = SEWREN;
end
SESTATE : begin
if (se_done == 1'b1)
n_state = WIP;
else
n_state = SESTATE;
end
BEWREN : begin
if (wren_done == 1'b1)
n_state = BESTATE;
else
n_state = BEWREN;
end
BESTATE : begin
if (be_done == 1'b1)
n_state = WIP;
else
n_state = BESTATE;
end
default : n_state = INIT_RDSR;
endcase
end
assign drive_busy = (c_state != IDLE || flag_be == 1'b1 || flag_rd == 1'b1 || flag_se == 1'b1 || flag_wr == 1'b1);
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
spi_cs_n <= 1'b1;
else
case (c_state)
INIT_RDSR : spi_cs_n <= 1'b1;
INIT_RDID : if (spi_read_done == 1'b1)
spi_cs_n <= 1'b1;
else
spi_cs_n <= 1'b0;
INIT_ID : if (rdid_done == 1'b1)
spi_cs_n <= 1'b1;
else
spi_cs_n <= 1'b0;
WIP : spi_cs_n <= 1'b1;
WIP_DONE : if (spi_read_done == 1'b1)
spi_cs_n <= 1'b1;
else
spi_cs_n <= 1'b0;
IDLE : spi_cs_n <= 1'b1;
RDSTATE : if (read_done == 1'b1)
spi_cs_n <= 1'b1;
else
spi_cs_n <= 1'b0;
PPWREN : if (wren_done == 1'b1)
spi_cs_n <= 1'b1;
else
spi_cs_n <= 1'b0;
PPSTATE : if (pp_done == 1'b1)
spi_cs_n <= 1'b1;
else
spi_cs_n <= 1'b0;
SEWREN : if (wren_done == 1'b1)
spi_cs_n <= 1'b1;
else
spi_cs_n <= 1'b0;
SESTATE : if (se_done == 1'b1)
spi_cs_n <= 1'b1;
else
spi_cs_n <= 1'b0;
BEWREN : if (wren_done == 1'b1)
spi_cs_n <= 1'b1;
else
spi_cs_n <= 1'b0;
BESTATE : if (be_done == 1'b1)
spi_cs_n <= 1'b1;
else
spi_cs_n <= 1'b0;
default : spi_cs_n <= 1'b1;
endcase
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
rdsr_en <= 1'b0;
else
if (c_state == INIT_RDSR || c_state == WIP)
rdsr_en <= 1'b1;
else
rdsr_en <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
wren_en <= 1'b0;
else
if (c_state == IDLE && (flag_be == 1'b1 || flag_se == 1'b1 || flag_wr == 1'b1))
wren_en <= 1'b1;
else
wren_en <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
pp_en <= 1'b0;
else
if (c_state == PPWREN && wren_done == 1'b1)
pp_en <= 1'b1;
else
pp_en <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
wr_len <= 9'd0;
else
if (c_state == IDLE && flag_wr == 1'b1)
wr_len <= len;
else
wr_len <= wr_len;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
wr_addr <= 24'd0;
else
if (c_state == IDLE && flag_wr == 1'b1)
wr_addr <= addr;
else
wr_addr <= wr_addr;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
be_en <= 1'b0;
else
if (c_state == BEWREN && wren_done == 1'b1)
be_en <= 1'b1;
else
be_en <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
se_en <= 1'b0;
else
if (c_state == SEWREN && wren_done == 1'b1)
se_en <= 1'b1;
else
se_en <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
se_addr <= 24'd0;
else
if (c_state == IDLE && flag_se == 1'b1)
se_addr <= addr;
else
se_addr <= se_addr;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
rdid_en <= 1'b0;
else
if (c_state == INIT_RDID && rdsr_done == 1'b1)
rdid_en <= 1'b1;
else
rdid_en <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
rd_len <= 9'd0;
else
if (c_state == IDLE && flag_rd == 1'b1)
rd_len <= len;
else
rd_len <= rd_len;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
rd_addr <= 24'd0;
else
if (c_state == IDLE && flag_rd == 1'b1)
rd_addr <= addr;
else
rd_addr <= rd_addr;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
read_en <= 1'b0;
else
if (c_state == IDLE && flag_rd == 1'b1)
read_en <= 1'b1;
else
read_en <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
mux_sel <= 3'd0;
else
case (c_state)
INIT_RDSR : mux_sel <= 3'd0;
INIT_RDID : mux_sel <= 3'd0;
INIT_ID : mux_sel <= 3'd5;
WIP : mux_sel <= 3'd0;
WIP_DONE : mux_sel <= 3'd0;
IDLE : mux_sel <= 3'd0;
RDSTATE : mux_sel <= 3'd6;
PPWREN : mux_sel <= 3'd2;
PPSTATE : mux_sel <= 3'd1;
SEWREN : mux_sel <= 3'd2;
SESTATE : mux_sel <= 3'd4;
BEWREN : mux_sel <= 3'd2;
BESTATE : mux_sel <= 3'd3;
default : mux_sel <= 3'd0;
endcase
end
endmodule
m25p16_drive設(shè)計(jì)實(shí)現(xiàn)
本模塊負(fù)責(zé)連接所有二級(jí)模塊,實(shí)現(xiàn)所有的功能。
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仿真
本次設(shè)計(jì)涉及到讀取flash的id以及狀態(tài)寄存器,所以在仿真時(shí)需要加入仿真模型。仿真模型放在msim的m25p16_sim_module中。m25p16為仿真模型的頂層文件。
由于讀寫和擦除的時(shí)間較長(zhǎng),RTL仿真中,將只仿真RDSR和RDID,其他的功能測(cè)試在板級(jí)測(cè)試時(shí)進(jìn)行。
仿真代碼如下:
`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
在設(shè)置testbench時(shí),注意將所有文件全部添加到文件中。
選擇testbench時(shí),注意選中設(shè)置的m25p16_drive_tb。
利用modelsim仿真,可以得出如下RTL仿真波形。
讀到ID,以及檢測(cè)WIP都是正確的。
板級(jí)測(cè)試
由于m25p16的時(shí)序原因,整個(gè)設(shè)計(jì)工作在10MHz(利用PLL產(chǎn)生)。
在進(jìn)行測(cè)試控制時(shí),對(duì)最后一個(gè)扇區(qū)進(jìn)行擦除;對(duì)最后一個(gè)扇區(qū)的第一頁進(jìn)行寫入數(shù)據(jù)100個(gè)(1至100);對(duì)最后一個(gè)扇區(qū)的第一個(gè)進(jìn)行讀取,驗(yàn)證數(shù)據(jù)是否為1至100。
測(cè)試的控制模塊命名為test_ctrl。
此模塊采用狀態(tài)機(jī)實(shí)現(xiàn)。WRFIFO(將1至100寫入wrfifo中)、SE(扇區(qū)擦除)、PP(寫入flash)、RD(讀出flash)、WAIT_RD(等待讀?。HECK( 檢測(cè)讀出的數(shù)據(jù)的正確性)。
設(shè)計(jì)代碼為:
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模塊設(shè)置為頂層。在test模塊中,m25p16_drive例化中,對(duì)于整片擦除不做控制,對(duì)于addr直接指向最后一個(gè)扇區(qū)的第一頁,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
由于開發(fā)板上的flash是為FPGA進(jìn)行保存配置信息的,所以管腳都連接在專用管腳上,本次實(shí)驗(yàn)需要將這專用管腳配置為普通io。
右擊器件型號(hào),選擇device。
點(diǎn)擊device and pin options。
選擇Dual-purpose pins,將其中所有的功能改為普通IO。
點(diǎn)擊ok后,即可進(jìn)行綜合分析。
連接開發(fā)板和PC,打開邏輯分析儀。
采樣時(shí)鐘選擇10MHz(PLL 的c0),采樣深度設(shè)置為2K。
觀測(cè)信號(hào)如下圖所示。
首先將wrfifo_wr的觸發(fā)條件設(shè)置為上升沿。點(diǎn)擊觸發(fā)后,按下復(fù)位按鍵。觸發(fā)后,可以看到寫入數(shù)據(jù)1至100后,然后進(jìn)行SE命令。
將rdfifo_rd的觸發(fā)條件設(shè)置為上升沿(將wrfifo_wr觸發(fā)條件修改為donot care)。點(diǎn)擊觸發(fā)后,按下復(fù)位按鍵。
通過仿真和下板實(shí)測(cè),驗(yàn)證控制器設(shè)計(jì)正確。