FPGA学习笔记——SPI读写FLASH
目录
一、任务
二、需求分析
三、Visio图
四、具体分析
五、IP核配置
六、代码
七、实验现象
一、任务
实验任务:
1.按下按键key1,开启读ID操作,将读出来的ID,通过串口发送至PC端显示,显示格式为“读ID:XX-XX-XX”;
2.按下按键key2,开启读数据操作,连续读3个字节,将读出来的数据,通过串口发送至PC端显示,显示格式为“读数据:XX XX XX”;
3.PC端通过串口助手向FPGA发送数据,每传输3字节数据,开启1次页编程操作;
按键按键key4,执行1次扇区擦除操作。
需要用到的指令。
WREN:0x06
RDID:0x9F
RDSR:0x05 和 状态寄存器
READ:0x03
PP:0x02
SE:0xD8
以上就是指令的时序。
二、需求分析
根据任务,可以知道要用哪些模块:按键消抖模块、串口收发模块和Flash控制模块,在Flash控制模块,我们可以细分成读控制模块、写控制模块和SPI接口模块。
数据流向:
读Flash时,按键按下,发送读操作指令,从Flash接收3个字节数据,然后将这个三个数据缓存到读FIFO中(FIFO IP核),当读FIFO存有3个数据时,开启串口发送,将这三个数据发送到PC端的串口助手进行显示。(读ID与之类似)
写Flash时,从PC端的串口助手发送3个或者3个以上的数据,串口接收模块,将接收到的数据缓存到写FIFO中(FIFO IP核),当写FIFO中存有三个数据时,开始将三个数据写入Flash中,如果发送完三个数据,写FIFO中,还有3个数据及以上,又开启一次将三个数据写入Flash中,直到写FIFO中,数据少于3个。
擦除操作:当按键按下,进行扇区擦除操作。
三、Visio图
以下就是,根据各个模块来化的Visio图。(系统框架)
四、具体分析
串口接收和发送模块:串口接收发送模块介绍
按键消抖模块:这里不做介绍;
读操作模块:根据Flash器件数据手册(M25P16数据手册——Flash)可以知道,要从Flash中读出数据,首先,要将读指令发送给Flash,然后接收Flash发送过来的数据(MSB),在把数据缓存到读FIFO中,然后通过串口发送模块,发送给串口助手显示。
因此状态可以划成:
IDLE
RD_ID
RD_DATA
DONE
当按键1按下,状态从IDLE跳到RD_ID状态(Flash的ID有三个字节),当Flash中的三个字节读完,跳到DONE状态,随后跳到IDLE状态等待下一次按键触发;当按键2按下,状态从IDLE跳到RD_DATA状态,将Flash的三个字节读出来后,跳到DONE状态,随后跳到IDLE状态。
写操作模块:从PC端的串口助手发送3个或者3个以上的数据,串口接收模块,将接收到的数据缓存到写FIFO中(FIFO IP核),当写FIFO中存有三个数据时,发送WREN指令,然后发PP(页编程)指令,将三个数据写入到Flash中,然后进行轮询操作,等待Flash内部的写操作完成,如果FIFO中剩下数据超过三个,继续发送PP指令,进行数据的写入;当按键按下,发送WREN指令,然后在发送SE(扇区擦除)指令,将对应的扇区全部置为1。然后进行轮询操作,等待Flash内部的擦除操作完成。
因此状态可以划分成:
IDLE
WREN
DELAY1
SE
PP
DELAY2
RDSR
从PC端的串口助手发送3个或者3个以上的数据,串口接收模块,将接收到的数据缓存到写FIFO中(FIFO IP核),当写FIFO中存有三个数据时,状态由IDLE跳到WREN状态,然后延时1us(这里要将CS_N拉高,进行指令的切换),状态由DELAY1跳到PP状态,发送PP指令,然后延时1us(这里要将CS_N拉高,进行指令的切换),状态由DELAY2跳到RDSR状态,进行轮询,等待Flash中的状态寄存器的最低为为0。
当按键按下,进行擦除操作,这里只有指令的不同,其他都是一样的。
SPI接口模块:这里要了解SPI协议的通讯流程SPI协议简介
这里的SPI接口就不做状态机,写一个分频器,自己产生一个串行时钟周期,比特计数器用来计数8个时钟周期(SPI是以8bit或者16bit传输),在对应的模式下,在对应得奇数沿和偶数沿进行输出和采样。
五、IP核配置
这里需要用到FIFO来缓存数据。(可以新建一个FIFO或者两个FIFO),我这里是新建了两个FIFO,一个读FIFO和一个写FIFO。
读FIFO IP核配置
写FIFO IP核配置
六、代码
sys_top.v
module sys_top( input sys_clk ,input sys_rst_n,input uart_rxd ,output uart_txd ,input [2:0] key_in ,output spi_sclk ,output spi_mosi ,input spi_miso ,output spi_cs_n
);wire [2:0] key_down ;
wire [7:0] rx_data ;
wire rx_data_vld;
wire ready ;
wire [7:0] tx_data ;
wire tx_data_vld; key_filter u_key_filter_1(.clk (sys_clk ),.rst_n (sys_rst_n ),.key_in (key_in[0] ),.key_down (key_down[0] )
);key_filter u_key_filter_2(.clk (sys_clk ),.rst_n (sys_rst_n ),.key_in (key_in[1] ),.key_down (key_down[1] )
); key_filter u_key_filter_4(.clk (sys_clk ),.rst_n (sys_rst_n ),.key_in (key_in[2] ),.key_down (key_down[2] )
);uart_rx #( .CLOCK_FRQ(50_000_000),.BAUD(115200),.DATA_LENTH(8) ,.CHECKBIT_SELECT(0),.CHECK_TYPE(0) //偶校验:0 奇校验:1
)uart_rx_inst( /*input */.clk (sys_clk ),/*input */.rst_n (sys_rst_n ),/*input */.uart_rxd (uart_rxd ),/*output reg [7:0] */.dout (rx_data ),/*output */.dout_vld (rx_data_vld)
);flash_ctrl u_flash_ctrl(.clk (sys_clk ),.rst_n (sys_rst_n ),.rx_data (rx_data ),.rx_data_vld (rx_data_vld ),.ready (ready ),.tx_data (tx_data ),.tx_data_vld (tx_data_vld ),.key_down (key_down ),.sclk (spi_sclk ),.mosi (spi_mosi ),.miso (spi_miso ),.cs_n (spi_cs_n )
);uart_tx #( .CLOCK_FRQ(50_000_000),.BAUD(115200),.DATA_LENTH(8) ,.CHECKBIT_SELECT(0),.CHECK_TYPE(0) //偶校验:0 奇校验:1)uart_tx_inst( /*input */.clk (sys_clk ),/*input */.rst_n (sys_rst_n ),/*input [DATA_LENTH-1:0] */.tx_din (tx_data ),/*input */.tx_enable(tx_data_vld),/*output reg */.uart_txd (uart_txd ),/*output reg */.ready (ready ) //忙闲指示信号
);endmodule
uart_tx.v
module uart_tx #(parameter CLOCK_FRQ = 50_000_000,BAUD = 9600 ,DATA_LENTH = 8 ,CHECKBIT_SELECT = 0 ,CHECK_TYPE = 0 //偶校验:0 奇校验:1
)( input clk ,input rst_n ,input [DATA_LENTH-1:0] tx_din ,input tx_enable,output reg uart_txd ,output reg ready //忙闲指示信号
);
//---------<参数定义>------------------------------------------------//状态机参数定义localparam IDLE = 5'b00001,START = 5'b00010,DATA = 5'b00100,CHECK = 5'b01000,STOP = 5'b10000;localparam BUAD_MAX = CLOCK_FRQ/BAUD;//---------<内部信号定义>--------------------------------------------reg [4:0] state_c ;reg [4:0] state_n ;reg [15:0] cnt_baud ;//波特率计数器wire add_cnt_baud;wire end_cnt_baud;reg [2:0] cnt_bit ;//数据传输的比特计数器wire add_cnt_bit ;wire end_cnt_bit ;reg [7:0] tx_din_r ;//状态转移条件定义wire idle2start ;wire start2data ;wire data2check ;wire data2stop ;wire check2stop ;wire stop2idle ;//*******************************************************************
//--tx_din_r
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begintx_din_r <= 'd0;end else if(tx_enable)begin tx_din_r <= tx_din;end end//*******************************************************************
//--状态机
//*******************************************************************//第一段:同步时序描述状态转移always @(posedge clk or negedge rst_n)begin if(!rst_n)beginstate_c <= IDLE;end else begin state_c <= state_n;end end//第二段:组合逻辑判断状态转移条件,描述状态转移规律always @(*) begincase(state_c)IDLE : beginif(idle2start)state_n = START; else state_n = state_c;endSTART : beginif(start2data)state_n = DATA;else state_n = state_c;endDATA : beginif(data2check)state_n = CHECK;else if(data2stop)state_n = STOP;else state_n = state_c;endCHECK : beginif(check2stop)state_n = STOP;else state_n = state_c;endSTOP : beginif(stop2idle)state_n = IDLE; else state_n = state_c;enddefault : state_n = IDLE;endcaseendassign idle2start = (state_c == IDLE ) && tx_enable; assign start2data = (state_c == START) && end_cnt_baud;assign data2check = (state_c == DATA ) && end_cnt_bit && CHECKBIT_SELECT;assign data2stop = (state_c == DATA ) && end_cnt_bit && !CHECKBIT_SELECT;assign check2stop = (state_c == CHECK) && end_cnt_baud;assign stop2idle = (state_c == STOP ) && end_cnt_baud;//*******************************************************************
//--cnt_baud
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_baud <= 'd0;end else if(add_cnt_baud)begin if(end_cnt_baud)begin cnt_baud <= 'd0;endelse begin cnt_baud <= cnt_baud + 1'b1;end endend assign add_cnt_baud = state_c != IDLE;assign end_cnt_baud = add_cnt_baud && cnt_baud == ((state_c == STOP) ? ((BUAD_MAX>>1)+(BUAD_MAX>>2)) : (BUAD_MAX - 1));//*******************************************************************
//--cnt_bit
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_bit <= 'd0;end else if(add_cnt_bit)begin if(end_cnt_bit)begin cnt_bit <= 'd0;endelse begin cnt_bit <= cnt_bit + 1'b1;end endend assign add_cnt_bit = state_c == DATA && end_cnt_baud;assign end_cnt_bit = add_cnt_bit && cnt_bit == DATA_LENTH - 1;//*******************************************************************
//--uart_txd
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)beginuart_txd <= 1'b1;end else begin case (state_c)IDLE : uart_txd <= 1'b1;START : uart_txd <= 1'b0;DATA : uart_txd <= tx_din_r[cnt_bit];//并转串,LSB// CHECK : uart_txd <= ^tx_din_r + CHECK_TYPE;CHECK : uart_txd <= CHECK_TYPE ? (^tx_din_r + 1'b1) : (^tx_din_r); STOP : uart_txd <= 1'b1;default: uart_txd <= 1'b1;endcaseend end//*******************************************************************
//--ready
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)beginready <= 'd0;end else begin ready <= state_n == IDLE;end endendmodule
uart_rx.v
module uart_rx #(parameter CLOCK_FRQ = 50_000_000,BAUD = 9600 ,DATA_LENTH = 8 ,CHECKBIT_SELECT = 0 ,CHECK_TYPE = 0 //偶校验:0 奇校验:1
)( input clk ,input rst_n ,input uart_rxd ,output reg [7:0] dout ,output dout_vld
);
//---------<参数定义>------------------------------------------------//状态机参数定义localparam IDLE = 5'b00001,START = 5'b00010,DATA = 5'b00100,CHECK = 5'b01000,STOP = 5'b10000;localparam BUAD_MAX = CLOCK_FRQ/BAUD;//---------<内部信号定义>--------------------------------------------reg [4:0] state_c ;reg [4:0] state_n ;reg [15:0] cnt_baud ;//波特率计数器wire add_cnt_baud;wire end_cnt_baud;reg [2:0] cnt_bit ;//接收bit数量的计数器wire add_cnt_bit;wire end_cnt_bit;reg [2:0] uart_rxd_r ;wire rxd_n_edge ;//状态转移条件定义wire idle2start ;wire start2data ;wire data2check ;wire data2stop ;wire check2stop ;wire stop2idle ;//*******************************************************************
//--打三拍(前两拍同步,后一拍获得边沿)
//*******************************************************************//uart_rxd_ralways @(posedge clk or negedge rst_n)begin if(!rst_n)beginuart_rxd_r <= 3'b111;end else begin uart_rxd_r <= {uart_rxd_r[1:0],uart_rxd};end end//rxd_n_edgeassign rxd_n_edge = ~uart_rxd_r[1] & uart_rxd_r[2];//*******************************************************************
//--状态机
//*******************************************************************//第一段:同步时序描述状态转移always @(posedge clk or negedge rst_n)begin if(!rst_n)beginstate_c <= IDLE;end else begin state_c <= state_n;end end//第二段:组合逻辑判断状态转移条件,描述状态转移规律always @(*) begincase(state_c)IDLE : beginif(idle2start)state_n = START; else state_n = state_c;endSTART : beginif(start2data)state_n = DATA;else state_n = state_c;endDATA : beginif(data2check)state_n = CHECK;else if(data2stop)state_n = STOP;else state_n = state_c;endCHECK : beginif(check2stop)state_n = STOP;else state_n = state_c;endSTOP : beginif(stop2idle)state_n = IDLE; else state_n = state_c;enddefault : state_n = IDLE;endcaseendassign idle2start = (state_c == IDLE ) && rxd_n_edge;assign start2data = (state_c == START) && end_cnt_baud;assign data2check = (state_c == DATA ) && end_cnt_bit && CHECKBIT_SELECT;assign data2stop = (state_c == DATA ) && end_cnt_bit && !CHECKBIT_SELECT;assign check2stop = (state_c == CHECK) && end_cnt_baud;assign stop2idle = (state_c == STOP ) && end_cnt_baud;//*******************************************************************
//--cnt_baud
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_baud <= 'd0;end else if(add_cnt_baud)begin if(end_cnt_baud)begin cnt_baud <= 'd0;endelse begin cnt_baud <= cnt_baud + 1'b1;end endend assign add_cnt_baud = state_c != IDLE;assign end_cnt_baud = add_cnt_baud && cnt_baud == ((state_c == STOP) ? ((BUAD_MAX>>1)+(BUAD_MAX>>2)) : (BUAD_MAX - 1));//*******************************************************************
//--cnt_bit
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_bit <= 'd0;end else if(add_cnt_bit)begin if(end_cnt_bit)begin cnt_bit <= 'd0;endelse begin cnt_bit <= cnt_bit + 1'b1;end endend assign add_cnt_bit = state_c == DATA && end_cnt_baud;assign end_cnt_bit = add_cnt_bit && cnt_bit == DATA_LENTH - 1;//*******************************************************************
//--dout
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begindout <= 'd0;end else if(state_c == DATA && cnt_baud == (BUAD_MAX>>1) - 1)begin dout[cnt_bit] <= uart_rxd_r[2];// dout <= {uart_rxd_r[2],dout[7:1]};end end//*******************************************************************
//--dout_vld
//*******************************************************************assign dout_vld = stop2idle;endmodule
flash_ctrl.v
module flash_ctrl( input clk ,input rst_n ,//uart_rxinput [7:0] rx_data ,input rx_data_vld,//uart_txinput ready ,output [7:0] tx_data ,output tx_data_vld,//key_filterinput [2:0] key_down ,//M25P16 output sclk ,output mosi ,input miso ,output cs_n
);wire start_en;
wire [7:0] wr_data ;
wire [7:0] rd_data ;
wire trans_done;rw_ctrl u_rw_ctrl(.clk (clk ),.rst_n (rst_n ),.rx_data (rx_data ),.rx_data_vld (rx_data_vld ),.ready (ready ),.tx_data (tx_data ),.tx_data_vld (tx_data_vld ),.key_down (key_down ),.start_en (start_en ),.wr_data (wr_data ),.rd_data (rd_data ),.trans_done (trans_done )
);spi_intf u_spi_intf(.clk (clk ),.rst_n (rst_n ),.start_en (start_en ),.wr_data (wr_data ),.rd_data (rd_data ),.trans_done (trans_done ),.sclk (sclk ),.mosi (mosi ),.miso (miso ),.cs_n (cs_n )
);endmodule
rw_ctrl.v
module rw_ctrl( input clk ,input rst_n ,//uart_rxinput [7:0] rx_data ,input rx_data_vld,//uart_txinput ready ,output [7:0] tx_data ,output tx_data_vld,//key_filterinput [2:0] key_down ,//spi_intfoutput start_en ,output [7:0] wr_data ,input [7:0] rd_data ,input trans_done
);wire wr_start_en ;
wire [7:0] wr_wr_data ;wire rd_start_en ;
wire [7:0] rd_wr_data ;write_op u_write_op(.clk (clk ),.rst_n (rst_n ),.key_down (key_down[2] ),.rx_data (rx_data ),.rx_data_vld (rx_data_vld ),.start_en (wr_start_en ),.wr_data (wr_wr_data ),.rd_data (rd_data ),.trans_done (trans_done )
);read_op u_read_op(.clk (clk ),.rst_n (rst_n ),.key_down (key_down[1:0]),.ready (ready ),.tx_data (tx_data ),.tx_data_vld (tx_data_vld ),.start_en (rd_start_en ),.wr_data (rd_wr_data ),.rd_data (rd_data ),.trans_done (trans_done )
);assign start_en = wr_start_en | rd_start_en;
assign wr_data = ({8{wr_start_en}} & wr_wr_data) | ({8{rd_start_en}} & rd_wr_data);endmodule
write_op.v
`include "param.vh"module write_op#(parameter WR_LENTH = 2'd3)( //Userinput clk ,input rst_n ,input key_down ,input [7:0] rx_data ,input rx_data_vld,//spi_intfoutput reg start_en ,output reg [7:0] wr_data ,input [7:0] rd_data ,input trans_done
);localparam IDLE = 7'b000_0001,WREN = 7'b000_0010,DELAY1 = 7'b000_0100,SE = 7'b000_1000,PP = 7'b001_0000,DELAY2 = 7'b010_0000,RDSR = 7'b100_0000;reg [6:0] state_c ;
reg [6:0] state_n ;
reg [5:0] cnt_delay ;//指令时序间隔(片选取消选择时间)延时计数器,t>=100ns ,取1us
wire add_cnt_delay;
wire end_cnt_delay;
reg [3:0] cnt_byte ;//传输字节计数器
wire add_cnt_byte ;
wire end_cnt_byte ;
reg [23:0] wr_addr ;//写地址计数器
wire add_wr_addr ;
wire end_wr_addr ;reg op_flag ;//操作的标志信号 0:SE 1:PP
reg rdsr_wr_flag ;wire idle2wren ;
wire wren2delay1 ;
wire delay1se ;
wire delay1pp ;
wire se2delay2 ;
wire pp2delay2 ;
wire delay22rdsr ;
wire rdsr2idle ;wire full ;
wire rdreq ;
wire empty ;
wire [7:0] fifo_q;
wire [7:0] usedw ;//---------<FIFO例化:缓存>-------------------------------------------------
wr_fifo u_wr_fifo(.aclr (~rst_n ),.clock (clk ),.data (rx_data ),.rdreq (rdreq ),.wrreq (~full && rx_data_vld),.empty (empty ),.full (full ),.q (fifo_q ),.usedw (usedw )
);assign rdreq = ~empty && state_c == PP && cnt_byte >= 4 && trans_done;//---------<State Machine>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginstate_c <= IDLE;end else begin state_c <= state_n;end
endalways @(*) begincase(state_c)IDLE : beginif(idle2wren)state_n = WREN;else state_n = state_c;endWREN : beginif(wren2delay1)state_n = DELAY1;else state_n = state_c;endDELAY1 : beginif(delay1se)state_n = SE;else if(delay1pp)state_n = PP;else state_n = state_c;endSE : beginif(se2delay2)state_n = DELAY2;else state_n = state_c;endPP : beginif(pp2delay2)state_n = DELAY2;else state_n = state_c;endDELAY2 : beginif(delay22rdsr)state_n = RDSR;else state_n = state_c;endRDSR : beginif(rdsr2idle)state_n = IDLE;else state_n = state_c;enddefault : ;endcase
endassign idle2wren = (state_c == IDLE) && (key_down | usedw >= WR_LENTH);
assign wren2delay1 = (state_c == WREN) && trans_done;
assign delay1se = (state_c == DELAY1) && end_cnt_delay && ~op_flag;
assign delay1pp = (state_c == DELAY1) && end_cnt_delay && op_flag;
assign se2delay2 = (state_c == SE) && end_cnt_byte;
assign pp2delay2 = (state_c == PP) && end_cnt_byte;
assign delay22rdsr = (state_c == DELAY2) && end_cnt_delay;
assign rdsr2idle = (state_c == RDSR) && rd_data[0] == 1'b0;//检测WIP位//---------<cnt_delay>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_delay <= 'd0;end else if(add_cnt_delay)begin if(end_cnt_delay)begin cnt_delay <= 'd0;endelse begin cnt_delay <= cnt_delay + 1'b1;end end
end assign add_cnt_delay = state_c == DELAY1 || state_c == DELAY2;
assign end_cnt_delay = add_cnt_delay && cnt_delay == 50 - 1;//---------<op_flag>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginop_flag <= 'd0;end else if(key_down)begin //SEop_flag <= 1'b0;end else if(usedw >= WR_LENTH)begin //PPop_flag <= 1'b1;end
end//---------<cnt_byte>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_byte <= 'd0;end else if(add_cnt_byte)begin if(end_cnt_byte)begin cnt_byte <= 'd0;endelse begin cnt_byte <= cnt_byte + 1'b1;end end
end assign add_cnt_byte = trans_done && (state_c == SE || state_c == PP);
assign end_cnt_byte = add_cnt_byte && cnt_byte == (state_c == SE ? 4-1 : 4+WR_LENTH-1);//---------<wr_addr>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginwr_addr <= 'd0;end else if(add_wr_addr)begin if(end_wr_addr)begin wr_addr <= 'd0;endelse begin wr_addr <= wr_addr + WR_LENTH;end end
end assign add_wr_addr = pp2delay2;
assign end_wr_addr = add_wr_addr && wr_addr == 24'h1f_ff_ff;//---------<rdsr_wr_flag>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginrdsr_wr_flag <= 'd0;end else if(delay22rdsr)begin rdsr_wr_flag <= 1'b1;end else if(state_c == RDSR && trans_done) begin rdsr_wr_flag <= 1'b0;end
end//---------<输出>-------------------------------------------------
always @(*)begin case (state_c)WREN : begin start_en = 1'b1;wr_data = `CMD_WREN;endSE : begincase (cnt_byte)0 : begin start_en = 1'b1;wr_data = `CMD_SE;end1 : begin start_en = 1'b1;wr_data = wr_addr[23:16];end2 : begin start_en = 1'b1;wr_data = wr_addr[15:8];end3 : begin start_en = 1'b1;wr_data = wr_addr[7:0];enddefault: begin start_en = 1'b0;wr_data = 8'd0;endendcaseendPP : begincase (cnt_byte)0 : begin start_en = 1'b1;wr_data = `CMD_PP;end1 : begin start_en = 1'b1;wr_data = wr_addr[23:16];end2 : begin start_en = 1'b1;wr_data = wr_addr[15:8];end3 : begin start_en = 1'b1;wr_data = wr_addr[7:0];enddefault: begin start_en = 1'b1;wr_data = fifo_q;endendcaseendRDSR : beginstart_en = 1'b1;wr_data = rdsr_wr_flag ? `CMD_RDSR : 8'd0;enddefault: begin start_en = 1'b0;wr_data = 8'd0;endendcase
endendmodule
read_op.v
`include "param.vh"module read_op#(parameter RDDA_LENTH = 2'd3)( //USERinput clk ,input rst_n ,input [1:0] key_down ,input ready ,output [7:0] tx_data ,output tx_data_vld,//spi_intfoutput reg start_en ,output reg [7:0] wr_data ,input [7:0] rd_data ,input trans_done
);localparam IDLE = 4'h1,RD_ID = 4'h2, RD_DATA = 4'h4,DONE = 4'h8;reg [3:0] state_c ;
reg [3:0] state_n ;
reg [7:0] cnt_byte ;
wire add_cnt_byte;
wire end_cnt_byte;
reg [23:0] rd_addr ;
reg [23:0] temp_data ;
reg tx_id_flag ;//读ID操作时,向rd_fifo中写数据标志信号
reg tx_data_flag;//读数据操作时,向rd_fifo中写数据标志信号
reg [3:0] cnt_tx ;//需要传输的字节数量计数器
wire add_cnt_tx;
wire end_cnt_tx;
reg [3:0] max_cnt_tx; wire wrreq;
reg [7:0] wr_fifo_data;
wire rdreq;
wire empty;
wire full ;
wire [7:0] fifo_q;//---------<fifo例化>-------------------------------------------------
rd_fifo u_rd_fifo(.aclr (~rst_n ),.clock (clk ),.data (wr_fifo_data),.rdreq (rdreq ),.wrreq (wrreq ),.empty (empty ),.full (full ),.q (fifo_q )
);assign wrreq = ~full && (tx_id_flag | tx_data_flag);
assign rdreq = ~empty && ready;//---------<State Machine>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginstate_c <= IDLE;end else begin state_c <= state_n;end
endalways @(*) begincase(state_c)IDLE : state_n = key_down[0] ? RD_ID : (key_down[1] ? RD_DATA : state_c);RD_ID : state_n = end_cnt_byte ? DONE : state_c;RD_DATA : state_n = end_cnt_byte ? DONE : state_c;DONE : state_n = IDLE;default : state_n = IDLE;endcase
end//---------<cnt_byte>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_byte <= 'd0;end else if(add_cnt_byte)begin if(end_cnt_byte)begin cnt_byte <= 'd0;endelse begin cnt_byte <= cnt_byte + 1'b1;end end
end assign add_cnt_byte = (state_c == RD_ID || state_c == RD_DATA) && trans_done;
assign end_cnt_byte = add_cnt_byte && cnt_byte == (state_c == RD_ID ? 4 - 1 : RDDA_LENTH + 4 - 1);//---------<rd_addr>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginrd_addr <= 'd0;end else if(rd_addr >= 24'h1f_ff_ff - RDDA_LENTH && end_cnt_byte)beginrd_addr <= 'd0;endelse if(state_c == RD_DATA && end_cnt_byte)begin rd_addr <= rd_addr + RDDA_LENTH;end
end//---------<start_en wr_data>-------------------------------------------------
always @(*)begin case (state_c)RD_ID : begincase (cnt_byte)0 : begin start_en = 1'b1;wr_data = `CMD_RDID;end default: begin start_en = 1'b1;wr_data = 8'd0;end endcaseendRD_DATA : begincase (cnt_byte)0 : begin start_en = 1'b1;wr_data = `CMD_RDDA;end 1 : begin start_en = 1'b1;wr_data = rd_addr[23:16];end 2 : begin start_en = 1'b1;wr_data = rd_addr[15:8];end 3 : begin start_en = 1'b1;wr_data = rd_addr[7:0];end default: begin start_en = 1'b1;wr_data = 8'd0;end endcaseenddefault: begin start_en = 1'b0;wr_data = 8'd0;end endcase
end//---------<temp_data>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin if(!rst_n)begintemp_data <= 'd0;end else if((state_c == RD_ID && trans_done && cnt_byte >= 1) ||state_c == RD_DATA && trans_done && cnt_byte >= 4 )begin temp_data <= {temp_data[15:0],rd_data};end
end//---------<串口显示>-------------------------------------------------
//读ID: --> B6 C1 49 44 A3 BA X X X X X X -->12bytes
//读数据:--> B6 C1 CA FD BE D8 A3 BA X X X X X X -->14bytes//tx_id_flag
always @(posedge clk or negedge rst_n)begin if(!rst_n)begintx_id_flag <= 'd0;end else if(state_c == RD_ID && end_cnt_byte)begin tx_id_flag <= 1'b1;end else if(end_cnt_tx)begin tx_id_flag <= 1'b0;end
end//tx_data_flag
always @(posedge clk or negedge rst_n)begin if(!rst_n)begintx_data_flag <= 'd0;end else if(state_c == RD_DATA && end_cnt_byte)begin tx_data_flag <= 1'b1;end else if(end_cnt_tx)begin tx_data_flag <= 1'b0;end
end//cnt_tx
always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_tx <= 'd0;end else if(add_cnt_tx)begin if(end_cnt_tx)begin cnt_tx <= 'd0;endelse begin cnt_tx <= cnt_tx + 1'b1;end end
end assign add_cnt_tx = tx_id_flag || tx_data_flag;
assign end_cnt_tx = add_cnt_tx && cnt_tx == max_cnt_tx - 1;//max_cnt_tx
always @(*)begin if(tx_id_flag)max_cnt_tx = 12;else if(tx_data_flag)max_cnt_tx = 14;else max_cnt_tx = 0;
end//wr_fifo_data
always @(*)begin if(tx_id_flag)begincase (cnt_tx)0 : wr_fifo_data = 8'hB6;//读1 : wr_fifo_data = 8'hC1;2 : wr_fifo_data = "I";3 : wr_fifo_data = "D";4 : wr_fifo_data = 8'ha3;//中文冒号5 : wr_fifo_data = 8'hba;6 : wr_fifo_data = (temp_data[23:20] >= 10) ? (temp_data[23:20] + "A" - 8'd10) : (temp_data[23:20] + "0");7 : wr_fifo_data = (temp_data[19:16] >= 10) ? (temp_data[19:16] + "A" - 8'd10) : (temp_data[19:16] + "0");8 : wr_fifo_data = (temp_data[15:12] >= 10) ? (temp_data[15:12] + "A" - 8'd10) : (temp_data[15:12] + "0");9 : wr_fifo_data = (temp_data[11:8] >= 10) ? (temp_data[11:8] + "A" - 8'd10) : (temp_data[11:8] + "0");10 : wr_fifo_data = (temp_data[7:4] >= 10) ? (temp_data[7:4] + "A" - 8'd10) : (temp_data[7:4] + "0");11 : wr_fifo_data = (temp_data[3:0] >= 10) ? (temp_data[3:0] + "A" - 8'd10) : (temp_data[3:0] + "0");default: wr_fifo_data = 0;endcaseendelse if(tx_data_flag)begincase (cnt_tx)0 : wr_fifo_data = 8'hB6;//读1 : wr_fifo_data = 8'hC1;2 : wr_fifo_data = 8'hca;//数3 : wr_fifo_data = 8'hfd;4 : wr_fifo_data = 8'hbe;//据5 : wr_fifo_data = 8'hd8;6 : wr_fifo_data = 8'ha3;//中文冒号7 : wr_fifo_data = 8'hba;8 : wr_fifo_data = (temp_data[23:20] >= 10) ? (temp_data[23:20] + "A" - 4'd10) : (temp_data[23:20] + "0"); 9 : wr_fifo_data = (temp_data[19:16] >= 10) ? (temp_data[19:16] + "A" - 4'd10) : (temp_data[19:16] + "0"); 10 : wr_fifo_data = (temp_data[15:12] >= 10) ? (temp_data[15:12] + "A" - 4'd10) : (temp_data[15:12] + "0");11 : wr_fifo_data = (temp_data[11:8] >= 10) ? (temp_data[11:8] + "A" - 4'd10) : (temp_data[11:8] + "0");12 : wr_fifo_data = (temp_data[7:4] >= 10) ? (temp_data[7:4] + "A" - 4'd10) : (temp_data[7:4] + "0");13 : wr_fifo_data = (temp_data[3:0] >= 10) ? (temp_data[3:0] + "A" - 4'd10) : (temp_data[3:0] + "0");default: wr_fifo_data = 8'd0;endcaseendelse beginwr_fifo_data = 8'd0;end
end//---------<tx_data tx_data_vld>-------------------------------------------------
assign tx_data = fifo_q;
assign tx_data_vld = rdreq;endmodule
param.vh
//FLASH指令参数
`define CMD_RDID 8'h9f
`define CMD_RDDA 8'h03
`define CMD_WREN 8'h06
`define CMD_SE 8'hD8
`define CMD_PP 8'h02
`define CMD_RDSR 8'h05
spi_intf.v
module spi_intf#(parameter SYS_CLOCK = 50_000_000,SPI_CLOCK = 12_500_000,DATA_WIDTH = 8 ,CPOL = 1'b0,CPHA = 1'b0
)( //USERinput clk ,input rst_n ,input start_en ,//读/写使能信号input [DATA_WIDTH - 1:0] wr_data ,//输出的数据output [DATA_WIDTH - 1:0] rd_data ,//读出的数据output trans_done,//SPI INTERFACEoutput sclk ,output mosi ,input miso ,output cs_n );//定义一个函数:实现取对数的功能
function integer log2b;input integer data;begin for(log2b=0;data>0;log2b=log2b+1) data = data>>1;log2b = log2b - 1;end
endfunctionlocalparam DIV_NUM = SYS_CLOCK/SPI_CLOCK;reg sclk_r ;
reg [DATA_WIDTH-1:0] rd_data_r ;
reg mosi_r ;
reg [log2b(DIV_NUM)-1:0] cnt_div ;//时钟分频计数器
wire add_cnt_div;
wire end_cnt_div;
reg [log2b(DATA_WIDTH)-1:0] cnt_bit ;
wire add_cnt_bit;
wire end_cnt_bit;//---------<cnt_div>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_div <= 'd0;end else if(add_cnt_div)begin if(end_cnt_div)begin cnt_div <= 'd0;endelse begin cnt_div <= cnt_div + 1'b1;end end
end assign add_cnt_div = start_en;
assign end_cnt_div = add_cnt_div && cnt_div == DIV_NUM - 1;//---------<sclk>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginsclk_r <= CPOL;end else if(start_en)begin // if(cnt_div < (DIV_NUM>>1))// sclk_r <= CPOL^CPHA;// else // sclk_r <= CPOL^~CPHA;if(CPHA == 1)sclk_r <= cnt_div < (DIV_NUM>>1) ? ~CPOL : CPOL;else sclk_r <= cnt_div < (DIV_NUM>>1) ? CPOL : ~CPOL;end else begin sclk_r <= CPOL;end
end//---------<cnt_bit>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_bit <= 'd0;end else if(add_cnt_bit)begin if(end_cnt_bit)begin cnt_bit <= 'd0;endelse begin cnt_bit <= cnt_bit + 1'b1;end end
end assign add_cnt_bit = end_cnt_div;
assign end_cnt_bit = add_cnt_bit && cnt_bit == DATA_WIDTH - 1;//---------<rd_data_r>-------------------------------------------
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginrd_data_r <= 'd0;end else if(cnt_div == (DIV_NUM>>1))begin rd_data_r[7-cnt_bit] <= miso;//串转并end
end
// always @(posedge sclk_r or negedge rst_n)begin
// if(!rst_n)begin
// rd_data_r <= 'd0;
// end
// else if(start_en)begin
// rd_data_r[7-cnt_bit] <= miso;
// end
// end//---------<mosi_r>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginmosi_r <= 'd0;end else if(start_en)begin mosi_r <= wr_data[7-cnt_bit];//并转串end
end//---------<输出>-------------------------------------------------
assign rd_data = rd_data_r;
assign trans_done = end_cnt_bit;
assign sclk = sclk_r;
assign mosi = mosi_r;
assign cs_n = ~start_en;endmodule
key_filter.v
module key_filter#(parameter WIDTH = 1,TIME_20MS = 1000_000)( input clk ,input rst_n ,input [WIDTH-1:0] key_in ,output reg [WIDTH-1:0] key_down
);
//---------<参数定义>--------------------------------------------------------- //状态机参数 独热码编码localparam IDLE = 4'b0001,//空闲状态FILTER_DOWN = 4'b0010,//按键按下抖动状态HOLD = 4'b0100,//按键稳定按下状态FILTER_UP = 4'b1000;//按键释放抖动状态//---------<内部信号定义>-----------------------------------------------------reg [3:0] state_c ;//现态reg [3:0] state_n ;//次态reg [WIDTH-1:0] key_r0 ;//同步打拍reg [WIDTH-1:0] key_r1 ;reg [WIDTH-1:0] key_r2 ;wire [WIDTH-1:0] n_edge ;//下降沿wire [WIDTH-1:0] p_edge ;//上升沿 reg [19:0] cnt_20ms ;//延时计数器(20ms)wire add_cnt_20ms ;wire end_cnt_20ms ; //状态转移条件信号wire idle2filter_down ;wire filter_down2idle ;wire filter_down2hold ;wire hold2filter_up ;wire filter_up2hold ;wire filter_up2idle ; //****************************************************************
//--状态机
//****************************************************************//第一段:时序逻辑描述状态转移always @(posedge clk or negedge rst_n)begin if(!rst_n)beginstate_c <= IDLE;end else begin state_c <= state_n;end end//第二段:组合逻辑描述状态转移规律和状态转移条件always @(*)begin case (state_c)IDLE : begin if(idle2filter_down)begin state_n = FILTER_DOWN;endelse begin // state_n = IDLE;state_n = state_c;endendFILTER_DOWN : begin if(filter_down2idle)begin state_n = IDLE;endelse if(filter_down2hold)begin state_n = HOLD;endelse begin state_n = state_c;endendHOLD : begin if(hold2filter_up)begin state_n = FILTER_UP;endelse begin state_n = state_c;endendFILTER_UP : begin if(filter_up2hold)begin state_n = HOLD;endelse if(filter_up2idle)begin state_n = IDLE;endelse begin state_n = state_c;endenddefault: state_n = IDLE;endcaseendassign idle2filter_down = (state_c == IDLE) && n_edge;assign filter_down2idle = (state_c == FILTER_DOWN) && p_edge;assign filter_down2hold = (state_c == FILTER_DOWN) && end_cnt_20ms && !p_edge;assign hold2filter_up = (state_c == HOLD) && p_edge;assign filter_up2hold = (state_c == FILTER_UP) && n_edge;assign filter_up2idle = (state_c == FILTER_UP) && end_cnt_20ms;//****************************************************************
//--n_edge、p_edge
//**************************************************************** always @(posedge clk or negedge rst_n)begin if(!rst_n)beginkey_r0 <= {WIDTH{1'b1}};key_r1 <= {WIDTH{1'b1}};key_r2 <= {WIDTH{1'b1}};end else begin key_r0 <= key_in;key_r1 <= key_r0;key_r2 <= key_r1; end endassign n_edge = ~key_r1 & key_r2;//下降沿assign p_edge = ~key_r2 & key_r1;//上升沿//****************************************************************
//--cnt_20ms
//****************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_20ms <= 'd0;end else if(add_cnt_20ms)begin if(end_cnt_20ms || filter_down2idle || filter_up2hold)begin cnt_20ms <= 'd0;endelse begin cnt_20ms <= cnt_20ms + 1'b1;end endend assign add_cnt_20ms = (state_c == FILTER_DOWN) || (state_c == FILTER_UP);assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == TIME_20MS - 1;//****************************************************************
//--key_down
//****************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)beginkey_down <= 'd0;end else begin key_down <= filter_down2hold ? ~key_r2 : 1'b0;endendendmodule
七、实验现象
以上就是SPI读写FLASH。(如果有错误的地方,还请大家指出来,谢谢!)