赞
踩
通过页写操作指令,可以实现向Flash芯片中连续写入数据,主要有两种实现方式:
1.每次只写入单字节数据,连续写入N次,实现N个数据写入。
2.写入数据前,判断数据可以写满多少页,将数据写满整页,剩下不满一页的数据再通过页写指令一次性写入。
SPI_Flash页写指令和时序如下图所示:
在写入页写指令之前,需要先写入写使能(WREN)指令,将芯片设置为写使能锁存(WEL)状态;随后要拉低片选信号,写入页写指令、扇区地址、页地址、字节地址,紧跟地址写入要存储在 Flash 的字节数据,在指令、地址以及数据写入过程中,片选信号始终保持低电平,待指令、地址、数据被芯片锁存后,将片选信号拉高;片选信号拉高后,等待一个完整的页写周期(tPP),才能完成 Flash 芯片的页写操作。
根据写使能指令、页写指令和操作时序,可绘制完整的页写操作时序如下所示:
SPI_Flash读操作指令和数据如下图所示: 数据读操作指令写入之前不需要写入写使能指令,且执行数据读操作过程中,片选信号拉低后和拉高前不需要做时间等待。
通过uart串口使用页写操作指令向flash中连续写入256个以内的任意数据,然后按下读操作按键, 执行flash读操作读取写入的数据,将读出的数据通过uart串口发送到PC机,观察写入数据和读出数据是否一致。在控制串口调试工具往flash芯片写入数据之前,需要对芯片进行全擦除操作,通过按下全擦除按键来执行全擦除操作。本次数据写入地址为24’h00_00_00。
整个读写工程包含7个模块:
模块名称 | 功能描述 |
flash_wr_rd_top | 读写顶层例化模块 |
flash_wr_rd | 数据读写控制模块 |
flash_wr | 数据写模块 |
flash_rd | 数据读模块 |
uart_rx | 串口数据接收模块 |
uart_tx | 串口数据发送模块 |
key_filter | 按键消抖模块 |
读写工程的RTL示图如下所示:
key_filter模块、uart_rx模块、uart_tx模块已经给出,可以参照前面工程。数据写模块flash_wr代码如下:
- `timescale 1ns/1ps
- module flash_wr
- #(
- parameter WR_RD_ADDR = 24'h00_00_00 //数据写入地址
- )
- (
- input clk,
- input rst_n,
- input key_in, //全擦除标志信号
- input rx_valid,
- input [7:0] rx_data,
-
- output reg cs_n,
- output reg sck,
- output reg mosi,
- output reg[8:0] rx_data_num //uart接收数据总数
- );
- localparam WR_EN_INST = 8'h06; //写使能指令
- localparam BE_INST = 8'hc7; //全擦除指令
- localparam PP_INST = 8'h02; //页写指令
-
- localparam IDLE = 3'd0;
- localparam WR_EN0 = 3'd1;
- localparam DELAY0 = 3'd2;
- localparam BE = 3'd3;
- localparam WR_EN1 = 3'd4;
- localparam DELAY1 = 3'd5;
- localparam PP = 3'd6;
- reg [3:0] curr_state;
- reg [3:0] next_state;
- reg [4:0] clk_cnt; //系统时钟计数器
- reg [3:0] byte_cnt; //字节计数器
- reg [2:0] bit_cnt; //数据位计数器
- reg [1:0] sck_cnt; //sck时钟计数器
- reg [23:0] addr_r; //数据写入地址寄存器
- reg [23:0] addr; //数据写入地址
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- curr_state <= IDLE;
- else
- curr_state <= next_state;
- end
- always @(*)
- begin
- next_state <= IDLE;
- case(curr_state)
-
- IDLE:begin
- if(key_in)
- next_state <= WR_EN0;
- else if(rx_valid)
- next_state <= WR_EN1;
- else
- next_state <= IDLE;
- end
-
- WR_EN0:begin
- if(byte_cnt == 4'd2 && clk_cnt == 5'd31)
- next_state <= DELAY0;
- else
- next_state <= WR_EN0;
- end
-
- DELAY0:begin
- if(byte_cnt == 4'd3 && clk_cnt == 5'd31)
- next_state <= BE;
- else
- next_state <= DELAY0;
- end
-
- BE:begin
- if(byte_cnt == 4'd6 && clk_cnt == 5'd31)
- next_state <= IDLE;
- else
- next_state <= BE;
- end
-
- WR_EN1:begin
- if(byte_cnt == 4'd2 && clk_cnt == 5'd31)
- next_state <= DELAY1;
- else
- next_state <= WR_EN1;
- end
- DELAY1:begin
- if(byte_cnt == 4'd3 && clk_cnt == 5'd31)
- next_state <= PP;
- else
- next_state <= DELAY1;
- end
-
- PP:begin
- if(byte_cnt == 4'd10 && clk_cnt == 5'd31)
- next_state <= IDLE;
- else
- next_state <= PP;
- end
-
- default: next_state <= IDLE;
- endcase
- end
- //addr_r:数据写入地址寄存器
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- addr_r <= WR_RD_ADDR;
- else if(rx_valid)
- addr_r <= addr_r + 1'b1;
- end
-
- //addr:数据写入地址
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- addr <= 24'd0;
- else if(rx_valid)
- addr <= addr_r;
- end
- //rx_data_num:接收数据计数器
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- rx_data_num <= 8'd0;
- else if(rx_valid)
- rx_data_num <= rx_data_num + 1'b1;
- else
- rx_data_num <= rx_data_num;
- end
- //clk_cnt:系统时钟计数器
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- clk_cnt <= 5'd0;
- else if(curr_state != IDLE)
- clk_cnt <= clk_cnt + 1'b1;
- else
- clk_cnt <= 5'd0;
- end
-
- //byte_cnt:字节计数器
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- byte_cnt <= 4'd0;
- else if(curr_state == BE && byte_cnt == 4'd6 && clk_cnt == 5'd31)
- byte_cnt <= 4'd0;
- else if(curr_state == PP && byte_cnt == 4'd10 && clk_cnt == 5'd31)
- byte_cnt <= 4'd0;
- else if(clk_cnt == 5'd31)
- byte_cnt <= byte_cnt + 1'b1;
- else
- byte_cnt <= byte_cnt;
- end
- //sck_cnt:sck时钟计数器
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- sck_cnt <= 2'd0;
- else if(curr_state == WR_EN0 && byte_cnt == 4'd1)
- sck_cnt <= sck_cnt + 1'b1;
- else if(curr_state == BE && byte_cnt == 4'd5)
- sck_cnt <= sck_cnt + 1'b1;
- else if(curr_state == WR_EN1 && byte_cnt == 4'd1)
- sck_cnt <= sck_cnt + 1'b1;
- else if(curr_state == PP && byte_cnt >= 4'd5 && byte_cnt <= 4'd9)
- sck_cnt <= sck_cnt + 1'b1;
- else
- sck_cnt <= 2'd0;
- end
-
- //bit_cnt:数据位计数器
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- bit_cnt <= 3'd0;
- else if(sck_cnt == 2'd1)
- bit_cnt <= bit_cnt + 1'b1;
- else
- bit_cnt <= bit_cnt;
- end
- //cs_n:片选信号
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- cs_n <= 1'b1;
- else if(key_in)
- cs_n <= 1'b0;
- else if(curr_state == WR_EN0 && byte_cnt == 4'd2 && clk_cnt == 5'd31)
- cs_n <= 1'b1;
- else if(curr_state == DELAY0 && byte_cnt == 4'd3 && clk_cnt == 5'd31)
- cs_n <= 1'b0;
- else if(curr_state == BE && byte_cnt === 4'd6 && clk_cnt == 5'd31)
- cs_n <= 1'b1;
- else if(rx_valid)
- cs_n <= 1'b0;
- else if(curr_state == WR_EN1 && byte_cnt == 4'd2 && clk_cnt == 5'd31)
- cs_n <= 1'b1;
- else if(curr_state == DELAY1 && byte_cnt == 4'd3 && clk_cnt == 5'd31)
- cs_n <= 1'b0;
- else if(curr_state == PP && byte_cnt == 4'd10 && clk_cnt == 5'd31)
- cs_n <= 1'b1;
- else
- cs_n <= cs_n;
- end
-
- //sck:sck时钟生成
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- sck <= 1'b0;
- else if(sck_cnt == 2'd1)
- sck <= 1'b1;
- else if(sck_cnt == 2'd3)
- sck <= 1'b0;
- else
- sck <= sck;
- end
- //mosi:数据输出
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- mosi <= 1'b0;
- else if(curr_state == WR_EN0 && byte_cnt == 4'd2)
- mosi <= 1'b0;
- else if(curr_state == BE && byte_cnt == 4'd6)
- mosi <= 1'b0;
- else if(curr_state == WR_EN1 && byte_cnt == 4'd2)
- mosi <= 1'b0;
- else if(curr_state == PP && byte_cnt == 4'd10)
- mosi <= 1'b0;
- else if(curr_state == WR_EN0 && byte_cnt == 4'd1 && sck_cnt == 2'd0)
- mosi <= WR_EN_INST[7 - bit_cnt]; //输出WR_EN_INST指令
- else if(curr_state == BE && byte_cnt == 3'd5 && sck_cnt == 2'd0)
- mosi <= BE_INST[7 - bit_cnt]; //输出BE_INST指令
- else if(curr_state == WR_EN1 && byte_cnt == 4'd1 && sck_cnt == 2'd0)
- mosi <= WR_EN_INST[7 - bit_cnt]; //输出WR_EN_INST指令
- else if(curr_state == PP && byte_cnt == 4'd5 && sck_cnt == 2'd0)
- mosi <= PP_INST[7 - bit_cnt]; //输出PP_INST指令
- else if(curr_state == PP && byte_cnt == 4'd6 && sck_cnt == 2'd0)
- mosi <= addr[23 - bit_cnt]; //输出扇区地址
- else if(curr_state == PP && byte_cnt == 4'd7 && sck_cnt == 2'd0)
- mosi <= addr[15 - bit_cnt]; //输出页地址
- else if(curr_state == PP && byte_cnt == 4'd8 && sck_cnt == 2'd0)
- mosi <= addr[7- bit_cnt]; //输出字节地址
- else if(curr_state == PP && byte_cnt == 4'd9 && sck_cnt == 2'd0)
- mosi <= rx_data[7- bit_cnt]; //输出uart串口接收到的数据
- else
- mosi <= mosi;
- end
-
- endmodule
数据读模块flash_rd代码如下:
- `timescale 1ns/1ps
- module flash_rd
- #(
- parameter WR_RD_ADDR = 24'h00_00_00 //数据读出地址
- )
- (
- input clk,
- input rst_n,
- input key_in, //按键按下
- input miso, //读出Flash数据
- input [8:0] data_num, //待读出数据总数
-
- output reg flash_rd_busy, //flash读取数据忙碌状态
- output reg cs_n, //片选信号
- output reg sck, //sck串行时钟
- output reg mosi, //主输出从输入数据
- output reg tx_en, //发送使能信号
- output [7:0] tx_data //发送数据
- );
- localparam IDLE = 3'b001; //空闲状态
- localparam READ = 3'b010; //数据读状态
- localparam SEND = 3'b100; //数据发送状态
-
- localparam READ_INST = 8'h03; //数据读指令
- //等待计数最大值,波特率9600时,发送1bit数据需要5208个时钟周期,这里计数值需要大于5208*10
- localparam WAIT_MAX_CNT= 16'd59_999;
-
- wire [8:0] fifo_data_num; //fifo中数据个数
-
- reg [2:0] curr_state;
- reg [2:0] next_state;
- reg [4:0] clk_cnt; //系统时钟计数
- reg [15:0]byte_cnt; //字节计数器
- reg [1:0] sck_cnt; //sck时钟计数器
- reg [2:0] bit_cnt; //数据位计数器
-
- reg miso_flag; //miso数据提取标志信号
- reg [7:0] miso_r; //对输入的miso数据进行移位拼接
- reg wr_req_r; //读请求信号
- reg wr_req; //写请求信号
- reg [7:0] wr_data; //写数据
- reg fifo_rd_valid; //fifo读有效信号
- reg [15:0]wait_cnt; //等待时钟计数器
- reg rd_req; //读请求信号
- reg [8:0] rd_data_num; //读出fifo数据个数
-
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- curr_state <= IDLE;
- else
- curr_state <= next_state;
- end
-
- always @(*)
- begin
- next_state <= IDLE;
- case(curr_state)
-
- IDLE:begin
- if(key_in)
- next_state <= READ;
- else
- next_state <= IDLE;
- end
-
- READ:begin
- if(byte_cnt == data_num + 2'd3 && clk_cnt == 5'd31)
- next_state <= SEND;
- else
- next_state <= READ;
- end
-
- SEND:begin
- if(rd_data_num == data_num && wait_cnt == WAIT_MAX_CNT)
- next_state <= IDLE;
- else
- next_state <= SEND;
- end
-
- default: next_state <= IDLE;
- endcase
- end
-
- //clk_cnt:系统时钟计数器
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- clk_cnt <= 5'd0;
- else if(curr_state == READ)
- clk_cnt <= clk_cnt + 1'b1;
- else
- clk_cnt <= 5'd0;
- end
- //byte_cnt:字节计数器
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- byte_cnt <= 16'd0;
- else if(byte_cnt == data_num + 2'd3 && clk_cnt == 5'd31)
- byte_cnt <= 16'd0;
- else if(clk_cnt == 5'd31)
- byte_cnt <= byte_cnt + 1'b1;
- else
- byte_cnt <= byte_cnt;
- end
- //sck_cnt:sck时钟计数器
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- sck_cnt <= 2'd0;
- else if(curr_state == READ)
- sck_cnt <= sck_cnt + 1'b1;
- else
- sck_cnt <= 2'd0;
- end
-
- //bit_cnt:数据位计数器
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- bit_cnt <= 3'd0;
- else if(sck_cnt == 2'd2)
- bit_cnt <= bit_cnt + 1'b1;
- else
- bit_cnt <= bit_cnt;
- end
- //cs_n:片选信号
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- cs_n <= 1'b1;
- else if(key_in)
- cs_n <= 1'b0;
- else if(curr_state == READ && byte_cnt == data_num + 2'd3 && clk_cnt == 5'd31)
- cs_n <= 1'b1;
- else
- cs_n <= cs_n;
- end
-
- //sck:sck时钟生成
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- sck <= 1'b0;
- else if(sck_cnt == 2'd1)
- sck <= 1'b1;
- else if(sck_cnt == 2'd3)
- sck <= 1'b0;
- else
- sck <= sck;
- end
- //mosi:数据输出
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- mosi <= 1'b0;
- else if(curr_state == READ && byte_cnt >= 16'd4)
- mosi <= 1'b0;
- else if(byte_cnt == 16'd0 && sck_cnt == 2'd0)
- mosi <= READ_INST[7 - bit_cnt];
- else if(byte_cnt == 16'd1 && sck_cnt == 2'd0)
- mosi <= WR_RD_ADDR[23 - bit_cnt];
- else if(byte_cnt == 16'd2 && sck_cnt == 2'd0)
- mosi <= WR_RD_ADDR[15 - bit_cnt];
- else if(byte_cnt == 16'd3 && sck_cnt == 2'd0)
- mosi <= WR_RD_ADDR[7 - bit_cnt];
- else
- mosi <= mosi;
- end
-
- //miso_flag:miso数据提取标志信号
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- miso_flag <= 1'b0;
- else if(byte_cnt >= 16'd4 && sck_cnt == 2'd1)
- miso_flag <= 1'b1;
- else
- miso_flag <= 1'b0;
- end
- //miso_r:将miso输入数据进行移位寄存,高位在前低位在后
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- miso_r <= 8'd0;
- else if(miso_flag == 1'b1)
- miso_r <= {miso_r[6:0],miso};
- else
- miso_r <= miso_r;
- end
- //wr_req_r:写请求数据标志位
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- wr_req_r <= 1'b0;
- else if(bit_cnt == 3'd7 && miso_flag == 1'b1)
- wr_req_r <= 1'b1;
- else
- wr_req_r <= 1'b0;
- end
-
- //wr_req:对wr_req_r打一拍,与写入数据同步
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- wr_req <= 1'b0;
- else
- wr_req <= wr_req_r;
- end
- //wr_data:写入数据
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- wr_data <= 8'd0;
- else if(wr_req_r == 1'b1)
- wr_data <= miso_r;
- else
- wr_data <= wr_data;
- end
- //fifo_rd_valid:fifo读有效信号
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- fifo_rd_valid <= 1'b0;
- else if(curr_state == SEND && rd_data_num == data_num && wait_cnt == WAIT_MAX_CNT)
- fifo_rd_valid <= 1'b0;
- else if(curr_state == SEND && fifo_data_num <= data_num)
- fifo_rd_valid <= 1'b1;
- else
- fifo_rd_valid <= fifo_rd_valid;
- end
-
- //wait_cnt:数据读取间隔
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- wait_cnt <= 16'd0;
- else if(fifo_rd_valid == 1'b0)
- wait_cnt <= 16'd0;
- else if(wait_cnt == WAIT_MAX_CNT)
- wait_cnt <= 16'd0;
- else if(fifo_rd_valid == 1'b1)
- wait_cnt <= wait_cnt + 1'b1;
- else
- wait_cnt <= 16'd0;
- end
- //rd_req_r:fifo读使能标志信号
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- rd_req <= 1'b0;
- else if(rd_data_num < data_num && wait_cnt == WAIT_MAX_CNT)
- rd_req <= 1'b1;
- else
- rd_req <= 1'b0;
- end
-
-
- //rd_data_num:从fifo中读出数据计数器
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- rd_data_num <= 9'd0;
- else if(fifo_rd_valid == 1'b0)
- rd_data_num <= 9'd0;
- else if(rd_req == 1'b1)
- rd_data_num <= rd_data_num + 1'b1;
- else
- rd_data_num <= rd_data_num;
- end
- //tx_en:uart串口发送使能
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- tx_en <= 1'b0;
- else
- tx_en <= rd_req;
- end
-
- //flash_rd_busy:flash读忙碌标志信号
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- flash_rd_busy <= 1'b0;
- else if(key_in == 1'b1)
- flash_rd_busy <= 1'b1;
- else if(fifo_data_num == 9'd0 && wait_cnt == WAIT_MAX_CNT)
- flash_rd_busy <= 1'b0;
- else
- flash_rd_busy <= flash_rd_busy;
- end
- my_fifo u_my_fifo(
- .clock (clk ), //时钟信号
- .data (wr_data ), //写如数据,8bit
- .rdreq (rd_req), //读请求
- .wrreq (wr_req ), //写请求
-
- .q (tx_data ), //数据读出,8bit
- .usedw (fifo_data_num) //fifo内数据个数 [8:0]
- );
- endmodule
读写控制模块flash_wr_rd代码如下:
- `timescale 1ns/1ps
- module flash_wr_rd(
- //输入信号
- input clk,
- input rst_n,
- input flash_rd_busy,
- //输入flash_wr模块的spi控制信号
- input cs_n_wr,
- input sck_wr,
- input mosi_wr,
- //输入flash_rd模块的spi控制信号
- input cs_n_rd,
- input sck_rd,
- input mosi_rd,
- //输出FPAG给spi_flash的控制信号
- output cs_n,
- output sck,
- output mosi
- );
- //如果检测到读按键按下,进入读忙碌状态,待写入Flash中的数据全部读出,退出读忙碌状态
- //在读忙碌状态下,FPGA选择flash_rd模块的spi控制信号输出,否则,输出flash_wr模块的spi控制信号
- assign cs_n = flash_rd_busy ? cs_n_rd : cs_n_wr;
- assign sck = flash_rd_busy ? sck_rd : sck_wr;
- assign mosi = flash_rd_busy ? mosi_rd : mosi_wr;
-
- endmodule
读写顶层模块flash_wr_rd代码如下:
- `timescale 1ns/1ps
- module flash_wr_rd_top(
- //系统输入端口
- input sys_clk,
- input rst_n,
- //按键控制端口
- input key_be,
- input key_rd,
- //spi_flash
- input miso,
- output cs_n,
- output sck,
- output mosi,
- //uart串口
- input uart_rx,
- output uart_tx
- );
-
- parameter WR_RD_ADDR = 24'h00_00_00;
- parameter CLK_FREQ = 26'd50_000_000; //系统时钟
- parameter UART_BPS = 14'd9600; //波特率
-
- wire be_flag; //擦除按键标志信号
- wire rd_flag; //读操作标志信号
-
- wire cs_n_wr;
- wire sck_wr;
- wire mosi_wr;
- wire cs_n_rd;
- wire sck_rd;
- wire mosi_rd;
-
- wire tx_en; //uart数据发送使能
- wire [7:0] tx_data; //uart发送数据
-
- wire rx_valid; //uart接收有效标志信号
- wire [7:0] rx_data; //uart接收数据
- wire [8:0] data_num; //uart接收数据总数
- wire flash_rd_busy; //flash读忙碌状态
-
- //flash_wr:flash写操作模块例化
- flash_wr
- #(
- .WR_RD_ADDR(WR_RD_ADDR) //数据写入地址
- )
- u_flash_wr(
- .clk(sys_clk),
- .rst_n(rst_n),
- .key_in(be_flag),
- .rx_valid(rx_valid),
- .rx_data(rx_data),
-
- .cs_n(cs_n_wr),
- .sck(sck_wr),
- .mosi(mosi_wr),
- .rx_data_num(data_num)
- );
-
- //flash_rd:flash读操作模块例化
- flash_rd
- #(
- .WR_RD_ADDR(WR_RD_ADDR) //数据写入地址
- )
- u_flash_rd(
- .clk(sys_clk),
- .rst_n(rst_n),
- .key_in(rd_flag), //读按键按下
- .miso(miso), //读出Flash数据
- .data_num(data_num),
-
- .flash_rd_busy(flash_rd_busy), //flash读取数据忙碌状态
- .cs_n(cs_n_rd), //片选信号
- .sck(sck_rd), //sck串行时钟
- .mosi(mosi_rd), //主输出从输入数据
- .tx_en(tx_en), //发送使能信号
- .tx_data(tx_data) //发送数据
- );
-
- //flash_wr_rd:flash读写控制模块例化
- flash_wr_rd u_flash_wr_rd(
- .clk(sys_clk),
- .rst_n(rst_n),
- .flash_rd_busy(flash_rd_busy), //flash读操作忙碌标志信号
- .cs_n_wr(cs_n_wr),
- .sck_wr(sck_wr),
- .mosi_wr(mosi_wr),
-
- .cs_n_rd(cs_n_rd),
- .sck_rd(sck_rd),
- .mosi_rd(mosi_rd),
-
- .cs_n(cs_n),
- .sck(sck),
- .mosi(mosi)
- );
-
- //uart_rx:串口接收模块例化
- uart_rx
- #(
- .CLK_FREQ(CLK_FREQ), //系统时钟频率
- .UART_BPS(UART_BPS) //串口波特率
- )
- u_uart_rx(
- .clk(sys_clk),
- .rst_n(rst_n),
-
- .uart_rxd(uart_rx),
- .uart_rx_busy(),
- .rx_done(rx_valid),
- .rx_data(rx_data)
- );
-
- //uart_tx:串口发送模块例化
- uart_tx
- #(
- .CLK_FREQ(CLK_FREQ), //系统时钟频率
- .UART_BPS(UART_BPS) //串口波特率
- )
- u_uart_tx(
- .clk(sys_clk),
- .rst_n(rst_n),
-
- .tx_data(tx_data), //待发送数据
- .tx_en(tx_en), //发送使能信号
- .uart_tx_busy(), //发送忙碌标志信号
- .uart_txd(uart_tx) //UART数据发送端口
- );
-
- //key_filter:全擦除按键消抖模块例化
- key_filter u1_key_filter(
- .clk(sys_clk),
- .rst_n(rst_n),
- .key_in(key_be), //按键输入
-
- .key_flag(be_flag) //检测到按键按下标志信号
- );
-
- //key_filter:读操作按键消抖模块例化
- key_filter u2_key_filter(
- .clk(sys_clk),
- .rst_n(rst_n),
- .key_in(key_rd), //按键输入
-
- .key_flag(rd_flag) //检测到按键按下标志信号
- );
-
- endmodule
读写工程仿真代码如下:
- `timescale 1ns/1ps
- module flash_wr_rd_top_tb();
- //系统输入
- reg sys_clk;
- reg rst_n;
- //按键控制端口
- reg key_be;
- reg key_rd;
- //spi_flash
- wire miso;
- wire cs_n;
- wire sck;
- wire mosi;
- //uart串口
- reg uart_rx;
- wire uart_tx;
-
- reg [7:0] data_mem [255:0];
-
- //spi_flash中数据为00-FF 256个数据
- initial
- $readmemh("E:/FPGA/demo/programe/spi_flash/spi_wr_rd/sim/spi_flash.txt",data_mem);
-
- initial
- begin
- sys_clk = 1'b1;
- rst_n = 1'b0;
- key_be = 1'b1;
- key_rd = 1'b1;
- uart_rx = 1'b1;
- #200
- rst_n = 1'b1;
- key_be = 1'b0; //按下全擦除按键
- #21_000_000
- key_be = 1'b1;
- #1_000_000_000
- rx_byte(); //接收spi_flash中数据
- #50_000_000
- key_rd = 1'b0; //按下读操作按键
- #21_000_000;
- key_rd = 1'b1;
- end
-
- always #10 sys_clk = ~sys_clk;
-
- task rx_byte();
- integer j;
- for(j=0;j<256;j=j+1)
- rx_bit(data_mem[j]);
- endtask
-
- task rx_bit(input[7:0] data); //data是data_mem[j]的值。
- integer i;
- for(i=0;i<10;i=i+1)
- begin
- case(i)
- 0: uart_rx <= 1'b0 ; //起始位
- 1: uart_rx <= data[0];
- 2: uart_rx <= data[1];
- 3: uart_rx <= data[2];
- 4: uart_rx <= data[3];
- 5: uart_rx <= data[4];
- 6: uart_rx <= data[5];
- 7: uart_rx <= data[6];
- 8: uart_rx <= data[7]; //上面8个发送的是数据位
- 9: uart_rx <= 1'b1 ; //停止位
- endcase
- #1040; //一个波特时间=sclk周期*波特计数器
- end
- endtask
-
- defparam u_flash_wr_rd_top.u_uart_rx.CLK_FREQ = 500_000;
- defparam u_flash_wr_rd_top.u_uart_tx.CLK_FREQ = 500_000;
- defparam memory.mem_access.initfile = "initmemory.txt";
-
- flash_wr_rd_top u_flash_wr_rd_top(
- //系统输入端口
- .sys_clk(sys_clk),
- .rst_n(rst_n),
- //按键控制端口
- .key_be(key_be),
- .key_rd(key_rd),
- //spi_flash
- .miso(miso),
- .cs_n(cs_n),
- .sck(sck),
- .mosi(mosi),
- //uart串口
- .uart_rx(uart_rx),
- .uart_tx(uart_tx)
- );
-
- m25p16 memory (
- .c (sck ),
- .data_in (mosi ),
- .s (cs_n ),
- .w (1'b1 ),
- .hold (1'b1 ),
- .data_out (miso )
- );
-
- endmodule
在flash读写操作之前,首先需要进行全擦除操作,在按全擦除按键key_be后,完成写使能指令写入和全擦除指令写入,然后等待1秒之后完成Flash全擦除操作,这里为了减少仿真时间,将全擦除时间由40s改为1s。全擦除仿真结果如下:
完成全擦除操作之后,工程一直处于串口数据接收等待状态,当检测到接收有效标志信号rx_valid,将执行flash_wr写入操作,将接收到的数据写入falsh中,数据写入地址从24’h00_00_00开始,数据写入flash仿真结果如下:
以写入数据5为例:
当byte_cnt== 4’d0时,执行tSLCH等待时间>5ns,本实验为32时钟周期640ns;
当 byte_cnt== 4’d1时,写入写使能指令WR_INST == 8’h06;
当byte_cnt== 4’d2时,执行tCHSH等待时间>5ns,本实验为32时钟周期640ns;
当 byte_cnt== 4’d3时,执行tSHSL等待时间>100ns,本实验为等待640ns;
当byte_cnt==4’d4时,执行tSLCH等待时间>5ns,本实验为32时钟周期640ns;
当byte_cnt==4’d5时,写入页写操作指令PP_INST == 8’h02;
当byte_cnt>=4’d6&& byte_cnt<=4’d8,写入页写地址24’h00_00_05;
当byte_cnt==4’d9时,写入串口接收的数据rx_data == 8’d5;
当byte_cnt==4’d10时,执行tCHSH等待时间>5ns,本实验为等待640ns;
执行flash写操作时,将spi_flash.txt中的00-FF 256个数据写入到flash中,仿真结果如下所示,此时写入数据地址寄存器addr == 24’h00_00_FF,写入最终数据rx_data == 8’h255,写入数据总数rx_data_num == 9’d256。
页写操作写入数据总数为0~256个,在未检测到读操作按键key_rd按下时,数据一直处于串口数据接收等待状态。当按键按下时候,flash读操作开始,将写入的数据全部读出,读出数据总数为data_num == rx_data_num。读出的数据通过串口发送到PC机,由于数据读出速率和数据通过uart串口发送到PC机速率不同,因此首先将读出的数据全部存到FIFO中,然后按照uart串口波特率发送到PC机,以9600波特率为例,发送一位数据需要5208*10个时钟周期,因此,本实验时钟计数器每计数60_000产生一个发送使能信号。
当读操作按键按下有效时,进入flash数据读操作,首先写入读操作指令RD_ISNT==8’h03和读数据地址WR_RD_ADDR == 24’h00,然后将flash中的数据全部读出并写入到FIFO进行暂存,curr_state == 3’b010的仿真结果如下:
最后将FIFO中的数据通过串口发送到PC机,当进入到数据发送状态时,即curr_state == 3’b100,wait_cnt开始计数,每计数60_000次,产生一个串口发送使能信号tx_en。当FIFO中数据全部读出时,拉低flash_rd_busy信号,此时回到flash写操作,等待串口接收有效信号rx_valid状态。从FIFO中读出数据发送到PC机仿真结果如下:
仿真结果成功验证了flash全擦除、flash页写指令执行数据连续写操作、flash数据读操作。
将flash读写工程下载到cyclone IV 开发板中,按下key_be按键,完成flash全擦除操作,使用SignalTap II,抓取按键按下后波形图如下:
使用串口发送数据2 A,对应的ASCII码为50 65,抓取rx_done信号上升沿,抓取信号波形为:
按下按键key_rd执行读操作,抓取tx_en上升沿,抓取信号波形为:
打开串口调试工具,设置波特率为9600。首先按下按键key_be执行全擦除,然后写入数据 Wish each lover been a couple at last!,按下key_rd后,读出数据为Wish each lover been a couple at last!。证明本次flash读写操作成功。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。