当前位置:   article > 正文

SPI协议(三):SPI_Flash(M25P16)读写操作_spi flash 读写

spi flash 读写

一 SPI_Flash读写操作时序

1.1 SPI_Flash写操作

        通过页写操作指令,可以实现向Flash芯片中连续写入数据,主要有两种实现方式:

        1.每次只写入单字节数据,连续写入N次,实现N个数据写入。

        2.写入数据前,判断数据可以写满多少页,将数据写满整页,剩下不满一页的数据再通过页写指令一次性写入。

        SPI_Flash页写指令和时序如下图所示:

        在写入页写指令之前,需要先写入写使能(WREN)指令,将芯片设置为写使能锁存(WEL)状态;随后要拉低片选信号,写入页写指令、扇区地址、页地址、字节地址,紧跟地址写入要存储在 Flash 的字节数据,在指令、地址以及数据写入过程中,片选信号始终保持低电平,待指令、地址、数据被芯片锁存后,将片选信号拉高;片选信号拉高后,等待一个完整的页写周期(tPP),才能完成 Flash 芯片的页写操作。

        根据写使能指令、页写指令和操作时序,可绘制完整的页写操作时序如下所示:

 1.2 SPI_Flash读操作

        SPI_Flash读操作指令和数据如下图所示:        数据读操作指令写入之前不需要写入写使能指令,且执行数据读操作过程中,片选信号拉低后和拉高前不需要做时间等待。

 二  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代码如下:

  1. `timescale 1ns/1ps
  2. module flash_wr
  3. #(
  4. parameter WR_RD_ADDR = 24'h00_00_00 //数据写入地址
  5. )
  6. (
  7. input clk,
  8. input rst_n,
  9. input key_in, //全擦除标志信号
  10. input rx_valid,
  11. input [7:0] rx_data,
  12. output reg cs_n,
  13. output reg sck,
  14. output reg mosi,
  15. output reg[8:0] rx_data_num //uart接收数据总数
  16. );
  17. localparam WR_EN_INST = 8'h06; //写使能指令
  18. localparam BE_INST = 8'hc7; //全擦除指令
  19. localparam PP_INST = 8'h02; //页写指令
  20. localparam IDLE = 3'd0;
  21. localparam WR_EN0 = 3'd1;
  22. localparam DELAY0 = 3'd2;
  23. localparam BE = 3'd3;
  24. localparam WR_EN1 = 3'd4;
  25. localparam DELAY1 = 3'd5;
  26. localparam PP = 3'd6;
  27. reg [3:0] curr_state;
  28. reg [3:0] next_state;
  29. reg [4:0] clk_cnt; //系统时钟计数器
  30. reg [3:0] byte_cnt; //字节计数器
  31. reg [2:0] bit_cnt; //数据位计数器
  32. reg [1:0] sck_cnt; //sck时钟计数器
  33. reg [23:0] addr_r; //数据写入地址寄存器
  34. reg [23:0] addr; //数据写入地址
  35. always @(posedge clk or negedge rst_n)
  36. begin
  37. if(!rst_n)
  38. curr_state <= IDLE;
  39. else
  40. curr_state <= next_state;
  41. end
  42. always @(*)
  43. begin
  44. next_state <= IDLE;
  45. case(curr_state)
  46. IDLE:begin
  47. if(key_in)
  48. next_state <= WR_EN0;
  49. else if(rx_valid)
  50. next_state <= WR_EN1;
  51. else
  52. next_state <= IDLE;
  53. end
  54. WR_EN0:begin
  55. if(byte_cnt == 4'd2 && clk_cnt == 5'd31)
  56. next_state <= DELAY0;
  57. else
  58. next_state <= WR_EN0;
  59. end
  60. DELAY0:begin
  61. if(byte_cnt == 4'd3 && clk_cnt == 5'd31)
  62. next_state <= BE;
  63. else
  64. next_state <= DELAY0;
  65. end
  66. BE:begin
  67. if(byte_cnt == 4'd6 && clk_cnt == 5'd31)
  68. next_state <= IDLE;
  69. else
  70. next_state <= BE;
  71. end
  72. WR_EN1:begin
  73. if(byte_cnt == 4'd2 && clk_cnt == 5'd31)
  74. next_state <= DELAY1;
  75. else
  76. next_state <= WR_EN1;
  77. end
  78. DELAY1:begin
  79. if(byte_cnt == 4'd3 && clk_cnt == 5'd31)
  80. next_state <= PP;
  81. else
  82. next_state <= DELAY1;
  83. end
  84. PP:begin
  85. if(byte_cnt == 4'd10 && clk_cnt == 5'd31)
  86. next_state <= IDLE;
  87. else
  88. next_state <= PP;
  89. end
  90. default: next_state <= IDLE;
  91. endcase
  92. end
  93. //addr_r:数据写入地址寄存器
  94. always @(posedge clk or negedge rst_n)
  95. begin
  96. if(!rst_n)
  97. addr_r <= WR_RD_ADDR;
  98. else if(rx_valid)
  99. addr_r <= addr_r + 1'b1;
  100. end
  101. //addr:数据写入地址
  102. always @(posedge clk or negedge rst_n)
  103. begin
  104. if(!rst_n)
  105. addr <= 24'd0;
  106. else if(rx_valid)
  107. addr <= addr_r;
  108. end
  109. //rx_data_num:接收数据计数器
  110. always @(posedge clk or negedge rst_n)
  111. begin
  112. if(!rst_n)
  113. rx_data_num <= 8'd0;
  114. else if(rx_valid)
  115. rx_data_num <= rx_data_num + 1'b1;
  116. else
  117. rx_data_num <= rx_data_num;
  118. end
  119. //clk_cnt:系统时钟计数器
  120. always @(posedge clk or negedge rst_n)
  121. begin
  122. if(!rst_n)
  123. clk_cnt <= 5'd0;
  124. else if(curr_state != IDLE)
  125. clk_cnt <= clk_cnt + 1'b1;
  126. else
  127. clk_cnt <= 5'd0;
  128. end
  129. //byte_cnt:字节计数器
  130. always @(posedge clk or negedge rst_n)
  131. begin
  132. if(!rst_n)
  133. byte_cnt <= 4'd0;
  134. else if(curr_state == BE && byte_cnt == 4'd6 && clk_cnt == 5'd31)
  135. byte_cnt <= 4'd0;
  136. else if(curr_state == PP && byte_cnt == 4'd10 && clk_cnt == 5'd31)
  137. byte_cnt <= 4'd0;
  138. else if(clk_cnt == 5'd31)
  139. byte_cnt <= byte_cnt + 1'b1;
  140. else
  141. byte_cnt <= byte_cnt;
  142. end
  143. //sck_cnt:sck时钟计数器
  144. always @(posedge clk or negedge rst_n)
  145. begin
  146. if(!rst_n)
  147. sck_cnt <= 2'd0;
  148. else if(curr_state == WR_EN0 && byte_cnt == 4'd1)
  149. sck_cnt <= sck_cnt + 1'b1;
  150. else if(curr_state == BE && byte_cnt == 4'd5)
  151. sck_cnt <= sck_cnt + 1'b1;
  152. else if(curr_state == WR_EN1 && byte_cnt == 4'd1)
  153. sck_cnt <= sck_cnt + 1'b1;
  154. else if(curr_state == PP && byte_cnt >= 4'd5 && byte_cnt <= 4'd9)
  155. sck_cnt <= sck_cnt + 1'b1;
  156. else
  157. sck_cnt <= 2'd0;
  158. end
  159. //bit_cnt:数据位计数器
  160. always @(posedge clk or negedge rst_n)
  161. begin
  162. if(!rst_n)
  163. bit_cnt <= 3'd0;
  164. else if(sck_cnt == 2'd1)
  165. bit_cnt <= bit_cnt + 1'b1;
  166. else
  167. bit_cnt <= bit_cnt;
  168. end
  169. //cs_n:片选信号
  170. always @(posedge clk or negedge rst_n)
  171. begin
  172. if(!rst_n)
  173. cs_n <= 1'b1;
  174. else if(key_in)
  175. cs_n <= 1'b0;
  176. else if(curr_state == WR_EN0 && byte_cnt == 4'd2 && clk_cnt == 5'd31)
  177. cs_n <= 1'b1;
  178. else if(curr_state == DELAY0 && byte_cnt == 4'd3 && clk_cnt == 5'd31)
  179. cs_n <= 1'b0;
  180. else if(curr_state == BE && byte_cnt === 4'd6 && clk_cnt == 5'd31)
  181. cs_n <= 1'b1;
  182. else if(rx_valid)
  183. cs_n <= 1'b0;
  184. else if(curr_state == WR_EN1 && byte_cnt == 4'd2 && clk_cnt == 5'd31)
  185. cs_n <= 1'b1;
  186. else if(curr_state == DELAY1 && byte_cnt == 4'd3 && clk_cnt == 5'd31)
  187. cs_n <= 1'b0;
  188. else if(curr_state == PP && byte_cnt == 4'd10 && clk_cnt == 5'd31)
  189. cs_n <= 1'b1;
  190. else
  191. cs_n <= cs_n;
  192. end
  193. //sck:sck时钟生成
  194. always @(posedge clk or negedge rst_n)
  195. begin
  196. if(!rst_n)
  197. sck <= 1'b0;
  198. else if(sck_cnt == 2'd1)
  199. sck <= 1'b1;
  200. else if(sck_cnt == 2'd3)
  201. sck <= 1'b0;
  202. else
  203. sck <= sck;
  204. end
  205. //mosi:数据输出
  206. always @(posedge clk or negedge rst_n)
  207. begin
  208. if(!rst_n)
  209. mosi <= 1'b0;
  210. else if(curr_state == WR_EN0 && byte_cnt == 4'd2)
  211. mosi <= 1'b0;
  212. else if(curr_state == BE && byte_cnt == 4'd6)
  213. mosi <= 1'b0;
  214. else if(curr_state == WR_EN1 && byte_cnt == 4'd2)
  215. mosi <= 1'b0;
  216. else if(curr_state == PP && byte_cnt == 4'd10)
  217. mosi <= 1'b0;
  218. else if(curr_state == WR_EN0 && byte_cnt == 4'd1 && sck_cnt == 2'd0)
  219. mosi <= WR_EN_INST[7 - bit_cnt]; //输出WR_EN_INST指令
  220. else if(curr_state == BE && byte_cnt == 3'd5 && sck_cnt == 2'd0)
  221. mosi <= BE_INST[7 - bit_cnt]; //输出BE_INST指令
  222. else if(curr_state == WR_EN1 && byte_cnt == 4'd1 && sck_cnt == 2'd0)
  223. mosi <= WR_EN_INST[7 - bit_cnt]; //输出WR_EN_INST指令
  224. else if(curr_state == PP && byte_cnt == 4'd5 && sck_cnt == 2'd0)
  225. mosi <= PP_INST[7 - bit_cnt]; //输出PP_INST指令
  226. else if(curr_state == PP && byte_cnt == 4'd6 && sck_cnt == 2'd0)
  227. mosi <= addr[23 - bit_cnt]; //输出扇区地址
  228. else if(curr_state == PP && byte_cnt == 4'd7 && sck_cnt == 2'd0)
  229. mosi <= addr[15 - bit_cnt]; //输出页地址
  230. else if(curr_state == PP && byte_cnt == 4'd8 && sck_cnt == 2'd0)
  231. mosi <= addr[7- bit_cnt]; //输出字节地址
  232. else if(curr_state == PP && byte_cnt == 4'd9 && sck_cnt == 2'd0)
  233. mosi <= rx_data[7- bit_cnt]; //输出uart串口接收到的数据
  234. else
  235. mosi <= mosi;
  236. end
  237. endmodule

         数据读模块flash_rd代码如下:

  1. `timescale 1ns/1ps
  2. module flash_rd
  3. #(
  4. parameter WR_RD_ADDR = 24'h00_00_00 //数据读出地址
  5. )
  6. (
  7. input clk,
  8. input rst_n,
  9. input key_in, //按键按下
  10. input miso, //读出Flash数据
  11. input [8:0] data_num, //待读出数据总数
  12. output reg flash_rd_busy, //flash读取数据忙碌状态
  13. output reg cs_n, //片选信号
  14. output reg sck, //sck串行时钟
  15. output reg mosi, //主输出从输入数据
  16. output reg tx_en, //发送使能信号
  17. output [7:0] tx_data //发送数据
  18. );
  19. localparam IDLE = 3'b001; //空闲状态
  20. localparam READ = 3'b010; //数据读状态
  21. localparam SEND = 3'b100; //数据发送状态
  22. localparam READ_INST = 8'h03; //数据读指令
  23. //等待计数最大值,波特率9600时,发送1bit数据需要5208个时钟周期,这里计数值需要大于5208*10
  24. localparam WAIT_MAX_CNT= 16'd59_999;
  25. wire [8:0] fifo_data_num; //fifo中数据个数
  26. reg [2:0] curr_state;
  27. reg [2:0] next_state;
  28. reg [4:0] clk_cnt; //系统时钟计数
  29. reg [15:0]byte_cnt; //字节计数器
  30. reg [1:0] sck_cnt; //sck时钟计数器
  31. reg [2:0] bit_cnt; //数据位计数器
  32. reg miso_flag; //miso数据提取标志信号
  33. reg [7:0] miso_r; //对输入的miso数据进行移位拼接
  34. reg wr_req_r; //读请求信号
  35. reg wr_req; //写请求信号
  36. reg [7:0] wr_data; //写数据
  37. reg fifo_rd_valid; //fifo读有效信号
  38. reg [15:0]wait_cnt; //等待时钟计数器
  39. reg rd_req; //读请求信号
  40. reg [8:0] rd_data_num; //读出fifo数据个数
  41. always @(posedge clk or negedge rst_n)
  42. begin
  43. if(!rst_n)
  44. curr_state <= IDLE;
  45. else
  46. curr_state <= next_state;
  47. end
  48. always @(*)
  49. begin
  50. next_state <= IDLE;
  51. case(curr_state)
  52. IDLE:begin
  53. if(key_in)
  54. next_state <= READ;
  55. else
  56. next_state <= IDLE;
  57. end
  58. READ:begin
  59. if(byte_cnt == data_num + 2'd3 && clk_cnt == 5'd31)
  60. next_state <= SEND;
  61. else
  62. next_state <= READ;
  63. end
  64. SEND:begin
  65. if(rd_data_num == data_num && wait_cnt == WAIT_MAX_CNT)
  66. next_state <= IDLE;
  67. else
  68. next_state <= SEND;
  69. end
  70. default: next_state <= IDLE;
  71. endcase
  72. end
  73. //clk_cnt:系统时钟计数器
  74. always @(posedge clk or negedge rst_n)
  75. begin
  76. if(!rst_n)
  77. clk_cnt <= 5'd0;
  78. else if(curr_state == READ)
  79. clk_cnt <= clk_cnt + 1'b1;
  80. else
  81. clk_cnt <= 5'd0;
  82. end
  83. //byte_cnt:字节计数器
  84. always @(posedge clk or negedge rst_n)
  85. begin
  86. if(!rst_n)
  87. byte_cnt <= 16'd0;
  88. else if(byte_cnt == data_num + 2'd3 && clk_cnt == 5'd31)
  89. byte_cnt <= 16'd0;
  90. else if(clk_cnt == 5'd31)
  91. byte_cnt <= byte_cnt + 1'b1;
  92. else
  93. byte_cnt <= byte_cnt;
  94. end
  95. //sck_cnt:sck时钟计数器
  96. always @(posedge clk or negedge rst_n)
  97. begin
  98. if(!rst_n)
  99. sck_cnt <= 2'd0;
  100. else if(curr_state == READ)
  101. sck_cnt <= sck_cnt + 1'b1;
  102. else
  103. sck_cnt <= 2'd0;
  104. end
  105. //bit_cnt:数据位计数器
  106. always @(posedge clk or negedge rst_n)
  107. begin
  108. if(!rst_n)
  109. bit_cnt <= 3'd0;
  110. else if(sck_cnt == 2'd2)
  111. bit_cnt <= bit_cnt + 1'b1;
  112. else
  113. bit_cnt <= bit_cnt;
  114. end
  115. //cs_n:片选信号
  116. always @(posedge clk or negedge rst_n)
  117. begin
  118. if(!rst_n)
  119. cs_n <= 1'b1;
  120. else if(key_in)
  121. cs_n <= 1'b0;
  122. else if(curr_state == READ && byte_cnt == data_num + 2'd3 && clk_cnt == 5'd31)
  123. cs_n <= 1'b1;
  124. else
  125. cs_n <= cs_n;
  126. end
  127. //sck:sck时钟生成
  128. always @(posedge clk or negedge rst_n)
  129. begin
  130. if(!rst_n)
  131. sck <= 1'b0;
  132. else if(sck_cnt == 2'd1)
  133. sck <= 1'b1;
  134. else if(sck_cnt == 2'd3)
  135. sck <= 1'b0;
  136. else
  137. sck <= sck;
  138. end
  139. //mosi:数据输出
  140. always @(posedge clk or negedge rst_n)
  141. begin
  142. if(!rst_n)
  143. mosi <= 1'b0;
  144. else if(curr_state == READ && byte_cnt >= 16'd4)
  145. mosi <= 1'b0;
  146. else if(byte_cnt == 16'd0 && sck_cnt == 2'd0)
  147. mosi <= READ_INST[7 - bit_cnt];
  148. else if(byte_cnt == 16'd1 && sck_cnt == 2'd0)
  149. mosi <= WR_RD_ADDR[23 - bit_cnt];
  150. else if(byte_cnt == 16'd2 && sck_cnt == 2'd0)
  151. mosi <= WR_RD_ADDR[15 - bit_cnt];
  152. else if(byte_cnt == 16'd3 && sck_cnt == 2'd0)
  153. mosi <= WR_RD_ADDR[7 - bit_cnt];
  154. else
  155. mosi <= mosi;
  156. end
  157. //miso_flag:miso数据提取标志信号
  158. always @(posedge clk or negedge rst_n)
  159. begin
  160. if(!rst_n)
  161. miso_flag <= 1'b0;
  162. else if(byte_cnt >= 16'd4 && sck_cnt == 2'd1)
  163. miso_flag <= 1'b1;
  164. else
  165. miso_flag <= 1'b0;
  166. end
  167. //miso_r:将miso输入数据进行移位寄存,高位在前低位在后
  168. always @(posedge clk or negedge rst_n)
  169. begin
  170. if(!rst_n)
  171. miso_r <= 8'd0;
  172. else if(miso_flag == 1'b1)
  173. miso_r <= {miso_r[6:0],miso};
  174. else
  175. miso_r <= miso_r;
  176. end
  177. //wr_req_r:写请求数据标志位
  178. always @(posedge clk or negedge rst_n)
  179. begin
  180. if(!rst_n)
  181. wr_req_r <= 1'b0;
  182. else if(bit_cnt == 3'd7 && miso_flag == 1'b1)
  183. wr_req_r <= 1'b1;
  184. else
  185. wr_req_r <= 1'b0;
  186. end
  187. //wr_req:对wr_req_r打一拍,与写入数据同步
  188. always @(posedge clk or negedge rst_n)
  189. begin
  190. if(!rst_n)
  191. wr_req <= 1'b0;
  192. else
  193. wr_req <= wr_req_r;
  194. end
  195. //wr_data:写入数据
  196. always @(posedge clk or negedge rst_n)
  197. begin
  198. if(!rst_n)
  199. wr_data <= 8'd0;
  200. else if(wr_req_r == 1'b1)
  201. wr_data <= miso_r;
  202. else
  203. wr_data <= wr_data;
  204. end
  205. //fifo_rd_valid:fifo读有效信号
  206. always @(posedge clk or negedge rst_n)
  207. begin
  208. if(!rst_n)
  209. fifo_rd_valid <= 1'b0;
  210. else if(curr_state == SEND && rd_data_num == data_num && wait_cnt == WAIT_MAX_CNT)
  211. fifo_rd_valid <= 1'b0;
  212. else if(curr_state == SEND && fifo_data_num <= data_num)
  213. fifo_rd_valid <= 1'b1;
  214. else
  215. fifo_rd_valid <= fifo_rd_valid;
  216. end
  217. //wait_cnt:数据读取间隔
  218. always @(posedge clk or negedge rst_n)
  219. begin
  220. if(!rst_n)
  221. wait_cnt <= 16'd0;
  222. else if(fifo_rd_valid == 1'b0)
  223. wait_cnt <= 16'd0;
  224. else if(wait_cnt == WAIT_MAX_CNT)
  225. wait_cnt <= 16'd0;
  226. else if(fifo_rd_valid == 1'b1)
  227. wait_cnt <= wait_cnt + 1'b1;
  228. else
  229. wait_cnt <= 16'd0;
  230. end
  231. //rd_req_r:fifo读使能标志信号
  232. always @(posedge clk or negedge rst_n)
  233. begin
  234. if(!rst_n)
  235. rd_req <= 1'b0;
  236. else if(rd_data_num < data_num && wait_cnt == WAIT_MAX_CNT)
  237. rd_req <= 1'b1;
  238. else
  239. rd_req <= 1'b0;
  240. end
  241. //rd_data_num:从fifo中读出数据计数器
  242. always @(posedge clk or negedge rst_n)
  243. begin
  244. if(!rst_n)
  245. rd_data_num <= 9'd0;
  246. else if(fifo_rd_valid == 1'b0)
  247. rd_data_num <= 9'd0;
  248. else if(rd_req == 1'b1)
  249. rd_data_num <= rd_data_num + 1'b1;
  250. else
  251. rd_data_num <= rd_data_num;
  252. end
  253. //tx_en:uart串口发送使能
  254. always @(posedge clk or negedge rst_n)
  255. begin
  256. if(!rst_n)
  257. tx_en <= 1'b0;
  258. else
  259. tx_en <= rd_req;
  260. end
  261. //flash_rd_busy:flash读忙碌标志信号
  262. always @(posedge clk or negedge rst_n)
  263. begin
  264. if(!rst_n)
  265. flash_rd_busy <= 1'b0;
  266. else if(key_in == 1'b1)
  267. flash_rd_busy <= 1'b1;
  268. else if(fifo_data_num == 9'd0 && wait_cnt == WAIT_MAX_CNT)
  269. flash_rd_busy <= 1'b0;
  270. else
  271. flash_rd_busy <= flash_rd_busy;
  272. end
  273. my_fifo u_my_fifo(
  274. .clock (clk ), //时钟信号
  275. .data (wr_data ), //写如数据,8bit
  276. .rdreq (rd_req), //读请求
  277. .wrreq (wr_req ), //写请求
  278. .q (tx_data ), //数据读出,8bit
  279. .usedw (fifo_data_num) //fifo内数据个数 [8:0]
  280. );
  281. endmodule

         读写控制模块flash_wr_rd代码如下:

  1. `timescale 1ns/1ps
  2. module flash_wr_rd(
  3. //输入信号
  4. input clk,
  5. input rst_n,
  6. input flash_rd_busy,
  7. //输入flash_wr模块的spi控制信号
  8. input cs_n_wr,
  9. input sck_wr,
  10. input mosi_wr,
  11. //输入flash_rd模块的spi控制信号
  12. input cs_n_rd,
  13. input sck_rd,
  14. input mosi_rd,
  15. //输出FPAG给spi_flash的控制信号
  16. output cs_n,
  17. output sck,
  18. output mosi
  19. );
  20. //如果检测到读按键按下,进入读忙碌状态,待写入Flash中的数据全部读出,退出读忙碌状态
  21. //在读忙碌状态下,FPGA选择flash_rd模块的spi控制信号输出,否则,输出flash_wr模块的spi控制信号
  22. assign cs_n = flash_rd_busy ? cs_n_rd : cs_n_wr;
  23. assign sck = flash_rd_busy ? sck_rd : sck_wr;
  24. assign mosi = flash_rd_busy ? mosi_rd : mosi_wr;
  25. endmodule

读写顶层模块flash_wr_rd代码如下:

  1. `timescale 1ns/1ps
  2. module flash_wr_rd_top(
  3. //系统输入端口
  4. input sys_clk,
  5. input rst_n,
  6. //按键控制端口
  7. input key_be,
  8. input key_rd,
  9. //spi_flash
  10. input miso,
  11. output cs_n,
  12. output sck,
  13. output mosi,
  14. //uart串口
  15. input uart_rx,
  16. output uart_tx
  17. );
  18. parameter WR_RD_ADDR = 24'h00_00_00;
  19. parameter CLK_FREQ = 26'd50_000_000; //系统时钟
  20. parameter UART_BPS = 14'd9600; //波特率
  21. wire be_flag; //擦除按键标志信号
  22. wire rd_flag; //读操作标志信号
  23. wire cs_n_wr;
  24. wire sck_wr;
  25. wire mosi_wr;
  26. wire cs_n_rd;
  27. wire sck_rd;
  28. wire mosi_rd;
  29. wire tx_en; //uart数据发送使能
  30. wire [7:0] tx_data; //uart发送数据
  31. wire rx_valid; //uart接收有效标志信号
  32. wire [7:0] rx_data; //uart接收数据
  33. wire [8:0] data_num; //uart接收数据总数
  34. wire flash_rd_busy; //flash读忙碌状态
  35. //flash_wr:flash写操作模块例化
  36. flash_wr
  37. #(
  38. .WR_RD_ADDR(WR_RD_ADDR) //数据写入地址
  39. )
  40. u_flash_wr(
  41. .clk(sys_clk),
  42. .rst_n(rst_n),
  43. .key_in(be_flag),
  44. .rx_valid(rx_valid),
  45. .rx_data(rx_data),
  46. .cs_n(cs_n_wr),
  47. .sck(sck_wr),
  48. .mosi(mosi_wr),
  49. .rx_data_num(data_num)
  50. );
  51. //flash_rd:flash读操作模块例化
  52. flash_rd
  53. #(
  54. .WR_RD_ADDR(WR_RD_ADDR) //数据写入地址
  55. )
  56. u_flash_rd(
  57. .clk(sys_clk),
  58. .rst_n(rst_n),
  59. .key_in(rd_flag), //读按键按下
  60. .miso(miso), //读出Flash数据
  61. .data_num(data_num),
  62. .flash_rd_busy(flash_rd_busy), //flash读取数据忙碌状态
  63. .cs_n(cs_n_rd), //片选信号
  64. .sck(sck_rd), //sck串行时钟
  65. .mosi(mosi_rd), //主输出从输入数据
  66. .tx_en(tx_en), //发送使能信号
  67. .tx_data(tx_data) //发送数据
  68. );
  69. //flash_wr_rd:flash读写控制模块例化
  70. flash_wr_rd u_flash_wr_rd(
  71. .clk(sys_clk),
  72. .rst_n(rst_n),
  73. .flash_rd_busy(flash_rd_busy), //flash读操作忙碌标志信号
  74. .cs_n_wr(cs_n_wr),
  75. .sck_wr(sck_wr),
  76. .mosi_wr(mosi_wr),
  77. .cs_n_rd(cs_n_rd),
  78. .sck_rd(sck_rd),
  79. .mosi_rd(mosi_rd),
  80. .cs_n(cs_n),
  81. .sck(sck),
  82. .mosi(mosi)
  83. );
  84. //uart_rx:串口接收模块例化
  85. uart_rx
  86. #(
  87. .CLK_FREQ(CLK_FREQ), //系统时钟频率
  88. .UART_BPS(UART_BPS) //串口波特率
  89. )
  90. u_uart_rx(
  91. .clk(sys_clk),
  92. .rst_n(rst_n),
  93. .uart_rxd(uart_rx),
  94. .uart_rx_busy(),
  95. .rx_done(rx_valid),
  96. .rx_data(rx_data)
  97. );
  98. //uart_tx:串口发送模块例化
  99. uart_tx
  100. #(
  101. .CLK_FREQ(CLK_FREQ), //系统时钟频率
  102. .UART_BPS(UART_BPS) //串口波特率
  103. )
  104. u_uart_tx(
  105. .clk(sys_clk),
  106. .rst_n(rst_n),
  107. .tx_data(tx_data), //待发送数据
  108. .tx_en(tx_en), //发送使能信号
  109. .uart_tx_busy(), //发送忙碌标志信号
  110. .uart_txd(uart_tx) //UART数据发送端口
  111. );
  112. //key_filter:全擦除按键消抖模块例化
  113. key_filter u1_key_filter(
  114. .clk(sys_clk),
  115. .rst_n(rst_n),
  116. .key_in(key_be), //按键输入
  117. .key_flag(be_flag) //检测到按键按下标志信号
  118. );
  119. //key_filter:读操作按键消抖模块例化
  120. key_filter u2_key_filter(
  121. .clk(sys_clk),
  122. .rst_n(rst_n),
  123. .key_in(key_rd), //按键输入
  124. .key_flag(rd_flag) //检测到按键按下标志信号
  125. );
  126. endmodule

读写工程仿真代码如下:

  1. `timescale 1ns/1ps
  2. module flash_wr_rd_top_tb();
  3. //系统输入
  4. reg sys_clk;
  5. reg rst_n;
  6. //按键控制端口
  7. reg key_be;
  8. reg key_rd;
  9. //spi_flash
  10. wire miso;
  11. wire cs_n;
  12. wire sck;
  13. wire mosi;
  14. //uart串口
  15. reg uart_rx;
  16. wire uart_tx;
  17. reg [7:0] data_mem [255:0];
  18. //spi_flash中数据为00-FF 256个数据
  19. initial
  20. $readmemh("E:/FPGA/demo/programe/spi_flash/spi_wr_rd/sim/spi_flash.txt",data_mem);
  21. initial
  22. begin
  23. sys_clk = 1'b1;
  24. rst_n = 1'b0;
  25. key_be = 1'b1;
  26. key_rd = 1'b1;
  27. uart_rx = 1'b1;
  28. #200
  29. rst_n = 1'b1;
  30. key_be = 1'b0; //按下全擦除按键
  31. #21_000_000
  32. key_be = 1'b1;
  33. #1_000_000_000
  34. rx_byte(); //接收spi_flash中数据
  35. #50_000_000
  36. key_rd = 1'b0; //按下读操作按键
  37. #21_000_000;
  38. key_rd = 1'b1;
  39. end
  40. always #10 sys_clk = ~sys_clk;
  41. task rx_byte();
  42. integer j;
  43. for(j=0;j<256;j=j+1)
  44. rx_bit(data_mem[j]);
  45. endtask
  46. task rx_bit(input[7:0] data); //datadata_mem[j]的值。
  47. integer i;
  48. for(i=0;i<10;i=i+1)
  49. begin
  50. case(i)
  51. 0: uart_rx <= 1'b0 ; //起始位
  52. 1: uart_rx <= data[0];
  53. 2: uart_rx <= data[1];
  54. 3: uart_rx <= data[2];
  55. 4: uart_rx <= data[3];
  56. 5: uart_rx <= data[4];
  57. 6: uart_rx <= data[5];
  58. 7: uart_rx <= data[6];
  59. 8: uart_rx <= data[7]; //上面8个发送的是数据位
  60. 9: uart_rx <= 1'b1 ; //停止位
  61. endcase
  62. #1040; //一个波特时间=sclk周期*波特计数器
  63. end
  64. endtask
  65. defparam u_flash_wr_rd_top.u_uart_rx.CLK_FREQ = 500_000;
  66. defparam u_flash_wr_rd_top.u_uart_tx.CLK_FREQ = 500_000;
  67. defparam memory.mem_access.initfile = "initmemory.txt";
  68. flash_wr_rd_top u_flash_wr_rd_top(
  69. //系统输入端口
  70. .sys_clk(sys_clk),
  71. .rst_n(rst_n),
  72. //按键控制端口
  73. .key_be(key_be),
  74. .key_rd(key_rd),
  75. //spi_flash
  76. .miso(miso),
  77. .cs_n(cs_n),
  78. .sck(sck),
  79. .mosi(mosi),
  80. //uart串口
  81. .uart_rx(uart_rx),
  82. .uart_tx(uart_tx)
  83. );
  84. m25p16 memory (
  85. .c (sck ),
  86. .data_in (mosi ),
  87. .s (cs_n ),
  88. .w (1'b1 ),
  89. .hold (1'b1 ),
  90. .data_out (miso )
  91. );
  92. endmodule

四 工程仿真及板级验证

4.1工程仿真

     在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数据读操作。

4.2板级验证

        将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读写操作成功。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/704020
推荐阅读
相关标签
  

闽ICP备14008679号