当前位置:   article > 正文

SPI协议(二):SPI_Flash(M25P16)擦除操作_tshsl

tshsl

       通过全擦除和扇区擦除可将固化到Flash芯片中的程序擦除。

一 SPI_Flash全擦除

       全擦除就是将Flash芯片所有的存储空间都进行擦除,使各个存储空间的内存数据都恢复到初始值,Flash全擦除有两种方式:

       (1)利用FPGA编译软件,通过Quartus软件中的“programmer”可将烧录到Flash中的*.jic文件擦除,如下图所示。

       (2)通过编写全擦除程序,将Flash芯片进行全擦除。

1.1实验内容

       编写全擦除程序,将Flash芯片事先烧录的程序擦除,使FPGA重新上电后,程序无法自启动。

       全擦除指令和时序如下图所示:

       由手册可知,全擦除指令是将Flash芯片中的所有存储单元置1,在写入全擦除指令之前,需要写入写使能(WREN)指令,将芯片置为写使能锁存(WEL)状态。拉低片选信号,写入全擦除指令,在指令写入过程中片选信号保持低电平,当指令被芯片所存后,将片选信号拉高;全擦除指令被锁存并执行后,需要等待一个完整的全擦除周期(tBE),才能完成Flash芯片的全擦除操作。

       写使能指令和时序如下图所示:

        写使能指令、全擦除指令以及其它操作指令写入Flash之前需要遵循芯片的串行输入时序,如下图所示:

       需要注意tSLCH、tCHSH、tSHSL这三个参数值:

       tSLCH:片选信号拉低到第一个有效数据写入,需要等待的时间,必须大于等于5ns。

       tCHSH:最后一个有效数据写入到片选信号拉高,需要等待的时间,必须大于等于5ns。

       tSHSL:片选信号拉高到再次拉低,需要等待的时间,必须大于等于100ns。

       因此,完整的全擦除操作时序图如下所示,为了方便起见,每个等待时间统一为32个时钟周期,即640ns。

1.2工程代码 

       全擦除工程主要包含顶层例化flash_be_top、按键消抖key_filter、擦除控制spi_flash_be三个子模块。

        spi_flash_be模块代码如下: 

  1. `timescale 1ns/1ps
  2. module spi_flash_be(
  3. input clk,
  4. input rst_n,
  5. input key_in,
  6. output reg cs_n,
  7. output reg sck,
  8. output reg mosi
  9. );
  10. parameter WR_EN_INST = 8'h06; //写使能指令
  11. parameter BE_INST = 8'hc7; //全擦除指令
  12. parameter IDLE = 4'b0001; //空闲状态
  13. parameter WR_EN = 4'b0010; //写使能状态
  14. parameter DELAY = 4'b0100; //片选拉高等待状态
  15. parameter BE = 4'b1000; //全擦除状态
  16. reg [3:0] curr_state;
  17. reg [3:0] next_state;
  18. reg [4:0] clk_cnt; //系统时钟计数器
  19. reg [2:0] byte_cnt; //字节计数器
  20. reg [1:0] sck_cnt; //sck时钟计数器
  21. reg [2:0] bit_cnt; //数据位计数器
  22. always @(posedge clk or negedge rst_n)
  23. begin
  24. if(!rst_n)
  25. curr_state <= IDLE;
  26. else
  27. curr_state <= next_state;
  28. end
  29. always @(*)
  30. begin
  31. next_state <= IDLE;
  32. case(curr_state)
  33. IDLE:begin
  34. if(key_in)
  35. next_state <= WR_EN;
  36. else
  37. next_state <= IDLE;
  38. end
  39. WR_EN:begin
  40. if(byte_cnt == 3'd1 && clk_cnt == 5'd31)
  41. next_state <= DELAY;
  42. else
  43. next_state <= WR_EN;
  44. end
  45. DELAY:begin
  46. if(byte_cnt == 3'd2 && clk_cnt == 5'd31)
  47. next_state <= BE;
  48. else
  49. next_state <= DELAY;
  50. end
  51. BE:begin
  52. if(byte_cnt == 3'd6 && clk_cnt == 5'd31)
  53. next_state <= IDLE;
  54. else
  55. next_state <= BE;
  56. end
  57. default: next_state <= IDLE;
  58. endcase
  59. end
  60. //clk_Cnt:系统时钟计数器
  61. always @(posedge clk or negedge rst_n)
  62. begin
  63. if(!rst_n)
  64. clk_cnt <= 5'd0;
  65. else if(curr_state != IDLE)
  66. clk_cnt <= clk_cnt + 1'b1;
  67. else
  68. clk_cnt <= 5'd0;
  69. end
  70. //byte_cnt:字节计数器
  71. always @(posedge clk or negedge rst_n)
  72. begin
  73. if(!rst_n)
  74. byte_cnt <= 3'd0;
  75. else if(byte_cnt == 3'd6 && clk_cnt == 5'd31)
  76. byte_cnt <= 3'd0;
  77. else if(clk_cnt == 5'd31)
  78. byte_cnt <= byte_cnt + 1'b1;
  79. else
  80. byte_cnt <= byte_cnt;
  81. end
  82. //sck_cnt:sck时钟计数器
  83. always @(posedge clk or negedge rst_n)
  84. begin
  85. if(!rst_n)
  86. sck_cnt <= 2'd0;
  87. else if(curr_state == WR_EN && byte_cnt == 3'd1)
  88. sck_cnt <= sck_cnt + 1'b1;
  89. else if(curr_state == BE && byte_cnt == 3'd5)
  90. sck_cnt <= sck_cnt + 1'b1;
  91. else
  92. sck_cnt <= 2'd0;
  93. end
  94. //bit_cnt:数据位计数器
  95. always @(posedge clk or negedge rst_n)
  96. begin
  97. if(!rst_n)
  98. bit_cnt <= 3'd0;
  99. else if(sck_cnt == 2'd1)
  100. bit_cnt <= bit_cnt + 1'b1;
  101. else
  102. bit_cnt <= bit_cnt;
  103. end
  104. //cs_n:片选信号
  105. always @(posedge clk or negedge rst_n)
  106. begin
  107. if(!rst_n)
  108. cs_n <= 1'b1;
  109. else if(key_in)
  110. cs_n <= 1'b0;
  111. else if(byte_cnt == 3'd2 && clk_cnt == 5'd31)
  112. cs_n <= 1'b1;
  113. else if(byte_cnt == 3'd3 && clk_cnt == 5'd31)
  114. cs_n <= 1'b0;
  115. else if(byte_cnt == 3'd6 && clk_cnt == 5'd31)
  116. cs_n <= 1'b1;
  117. else
  118. cs_n <= cs_n;
  119. end
  120. //sck:sck时钟生成
  121. always @(posedge clk or negedge rst_n)
  122. begin
  123. if(!rst_n)
  124. sck <= 1'b0;
  125. else if(sck_cnt == 2'd1)
  126. sck <= 1'b1;
  127. else if(sck_cnt == 2'd3)
  128. sck <= 1'b0;
  129. else
  130. sck <= sck;
  131. end
  132. //mosi:
  133. always @(posedge clk or negedge rst_n)
  134. begin
  135. if(!rst_n)
  136. mosi <= 1'b0;
  137. else if(byte_cnt == 3'd1 && clk_cnt == 5'd31)
  138. mosi <= 1'b0;
  139. else if(byte_cnt == 3'd5 && clk_cnt == 5'd31)
  140. mosi <= 1'b0;
  141. else if(byte_cnt == 3'd1 && sck_cnt == 2'd0) //写入写使能指令
  142. mosi <= WR_EN_INST[7 - bit_cnt];
  143. else if(byte_cnt == 3'd5 && sck_cnt == 2'd0) //写入全擦除指令
  144. mosi <= BE_INST[7 - bit_cnt];
  145. else
  146. mosi <= mosi;
  147. end
  148. endmodule

       顶层模块flash_be_top代码如下:

  1. `timescale 1ns/1ps
  2. module flash_be_top(
  3. input sys_clk,
  4. input rst_n,
  5. input key_in,
  6. output cs_n,
  7. output sck,
  8. output mosi
  9. );
  10. wire key_flag;
  11. spi_flash_be u_spi_flash_be(
  12. .clk(sys_clk),
  13. .rst_n(rst_n),
  14. .key_in(key_flag),
  15. .cs_n(cs_n),
  16. .sck(sck),
  17. .mosi(mosi)
  18. );
  19. key_filter u_key_filter(
  20. .clk(sys_clk),
  21. .rst_n(rst_n),
  22. .key_in(key_in), //按键输入
  23. .key_flag(key_flag) //检测到按键按下标志信号
  24. );
  25. endmodule

       仿真代码flsh_be_top_tb如下:

  1. `timescale 1ns/1ps
  2. module flsh_be_top_tb();
  3. reg sys_clk;
  4. reg rst_n;
  5. reg key_in;
  6. wire cs_n;
  7. wire sck;
  8. wire mosi;
  9. initial
  10. begin
  11. sys_clk = 1'b1;
  12. rst_n = 1'b0;
  13. key_in = 1'b1;
  14. #200
  15. rst_n = 1'b1;
  16. key_in = 1'b0;
  17. #21_000_000
  18. key_in = 1'b1;
  19. end
  20. always #10 sys_clk <= ~sys_clk;
  21. defparam memory.mem_access.initfile = "initmemory.txt";
  22. flash_be_top u_flash_be_top(
  23. .sys_clk(sys_clk),
  24. .rst_n(rst_n),
  25. .key_in(key_in),
  26. .cs_n(cs_n),
  27. .sck(sck),
  28. .mosi(mosi)
  29. );
  30. m25p16 memory
  31. (
  32. .c (sck ), //输入串行时钟,频率12.5Mhz,1bit
  33. .data_in (mosi ), //输入串行指令或数据,1bit
  34. .s (cs_n ), //输入片选信号,1bit
  35. .w (1'b1 ), //输入写保护信号,低有效,1bit
  36. .hold (1'b1 ), //输入hold信号,低有效,1bit
  37. .data_out ( ) //输出串行数据
  38. );
  39. endmodule

        仿真结果如下:

       仿真结果显示,当byte_cnt 为3’d1时完成写使能指令8’h06的写入,当字节计数器byte_cnt为3’d5时完成全擦除指令的写入。之后等待40s后,仿真结果提示全擦除操作完成。

1.3 板级验证

        首先将流水灯程序烧录到Flash芯片中,断电后重新上电检测Flash芯片中的流水灯程序可以自启动,然后将全擦除程序下载到FPGA开发板中,之后断电后重新上电,发现流水灯程序无法自启动,可以说明全擦除程序板级验证成功。

二 SPI_flash扇区擦除

       M25P16 Flash芯片总存储容量为16Mbit,分为32个扇区,每个扇区容量为512Kbit,扇区地址如下图所示:

2.1实验内容 

       通过扇区擦除工程将实现烧录到Flash芯片中的流水灯程序所在的某个扇区进行擦除,使流水灯程序上电之后无法自启动,本次选择擦除第0个扇区,扇区地址为24’h00_05_20。   

        扇区擦除指令和时序如下所示:

        从数据手册可以看出,扇区擦除指令是将Flash芯片中的被选中扇区的所有存储单元置为1,在Flash芯片写入扇区擦除指令之前,需要先写入写使能(WREN)指令,将芯片设置为写使能锁存(WEL)状态。然后拉低片选信号,写入扇区擦除指令、扇区地址、页地址和字节地址,在写入过程中,片选信号一直保持低电平状态,当指令和地址被芯片锁存后,将片选信号拉高。扇区擦除指令、地址被锁存并执行后,需要等待一个完整的扇区擦除周期(tSE),才能完成Flash芯片的扇区擦除操作。

       完整的扇区擦除操作时序如下图所示,为了方便起见,每个等待时间统一为32个时钟周期,即640ns。

2.2工程代码

      扇区擦除工程主要包括顶层例化flash_se_top、按键消抖key_filter、扇区擦除控制spi_flash_se三个子模块。

       扇区擦除控制spi_flash_se模块如下:

  1. `timescale 1ns/1ps
  2. module spi_flash_se
  3. #(
  4. parameter SE_ADDR = 8'h00, //扇区地址
  5. parameter PP_ADDR = 8'h05, //页地址
  6. parameter BYTE_ADDR = 8'h20 //字节地址
  7. )
  8. (
  9. input clk,
  10. input rst_n,
  11. input key_in,
  12. output reg cs_n,
  13. output reg sck,
  14. output reg mosi
  15. );
  16. localparam WR_EN_INST = 8'h06; //写使能指令
  17. localparam SE_INST = 8'hd8; //扇区擦除指令
  18. localparam IDLE = 4'b0001;
  19. localparam WR_EN = 4'b0010;
  20. localparam DELAY = 4'b0100;
  21. localparam SE = 4'b0000;
  22. reg [3:0] curr_state;
  23. reg [3:0] next_state;
  24. reg [4:0] clk_cnt;
  25. reg [3:0] byte_cnt;
  26. reg [2:0] bit_cnt;
  27. reg [1:0] sck_cnt;
  28. always @(posedge clk or negedge rst_n)
  29. begin
  30. if(!rst_n)
  31. curr_state <= IDLE;
  32. else
  33. curr_state <= next_state;
  34. end
  35. always @(*)
  36. begin
  37. next_state <= IDLE;
  38. case(curr_state)
  39. IDLE:begin
  40. if(key_in)
  41. next_state <= WR_EN;
  42. else
  43. next_state <= IDLE;
  44. end
  45. WR_EN:begin
  46. if(byte_cnt == 4'd2 && clk_cnt == 5'd31)
  47. next_state <= DELAY;
  48. else
  49. next_state <= WR_EN;
  50. end
  51. DELAY:begin
  52. if(byte_cnt == 4'd3 && clk_cnt == 5'd31)
  53. next_state <= SE;
  54. else
  55. next_state <= DELAY;
  56. end
  57. SE:begin
  58. if(byte_cnt == 4'd9 && clk_cnt == 5'd31)
  59. next_state <= IDLE;
  60. else
  61. next_state <= SE;
  62. end
  63. default: next_state <= IDLE;
  64. endcase
  65. end
  66. //clk_cnt:系统时钟计数器
  67. always @(posedge clk or negedge rst_n)
  68. begin
  69. if(!rst_n)
  70. clk_cnt <= 5'd0;
  71. else if(curr_state != IDLE)
  72. clk_cnt <= clk_cnt + 1'b1;
  73. else
  74. clk_cnt <= 5'd0;
  75. end
  76. //byte_cnt:字节计数器
  77. always @(posedge clk or negedge rst_n)
  78. begin
  79. if(!rst_n)
  80. byte_cnt <= 4'd0;
  81. else if(byte_cnt == 4'd9 && clk_cnt == 5'd31)
  82. byte_cnt <= 4'd0;
  83. else if(clk_cnt == 5'd31)
  84. byte_cnt <= byte_cnt + 1'b1;
  85. else
  86. byte_cnt <= byte_cnt;
  87. end
  88. //sck_cnt:sck时钟计数器
  89. always @(posedge clk or negedge rst_n)
  90. begin
  91. if(!rst_n)
  92. sck_cnt <= 2'd0;
  93. else if(curr_state == WR_EN && byte_cnt == 4'd1)
  94. sck_cnt <= sck_cnt + 1'b1;
  95. else if(byte_cnt >= 4'd5 && byte_cnt <= 4'd8)
  96. sck_cnt <= sck_cnt + 1'b1;
  97. else
  98. sck_cnt <= 2'd0;
  99. end
  100. //bit_cnt:数据位计数器
  101. always @(posedge clk or negedge rst_n)
  102. begin
  103. if(!rst_n)
  104. bit_cnt <= 1'b0;
  105. else if(sck_cnt == 2'd1)
  106. bit_cnt <= bit_cnt + 1'b1;
  107. else
  108. bit_cnt <= bit_cnt;
  109. end
  110. //sck:sck时钟生成
  111. always @(posedge clk or negedge rst_n)
  112. begin
  113. if(!rst_n)
  114. sck <= 1'b0;
  115. else if(sck_cnt == 2'd1)
  116. sck <= 1'b1;
  117. else if(sck_cnt == 2'd3)
  118. sck <= 1'b0;
  119. else
  120. sck <= sck;
  121. end
  122. //cs_n:片选信号
  123. always @(posedge clk or negedge rst_n)
  124. begin
  125. if(!rst_n)
  126. cs_n <= 1'b1;
  127. else if(key_in)
  128. cs_n <= 1'b0;
  129. else if(curr_state == WR_EN && byte_cnt == 4'd2 && clk_cnt == 5'd31)
  130. cs_n <= 1'b1;
  131. else if(curr_state == DELAY && byte_cnt == 4'd3 && clk_cnt == 5'd31)
  132. cs_n <= 1'b0;
  133. else if(curr_state == SE && byte_cnt == 4'd9 && clk_cnt == 5'd31)
  134. cs_n <= 1'b1;
  135. else
  136. cs_n <= cs_n;
  137. end
  138. //mosi:数据输出
  139. always @(posedge clk or negedge rst_n)
  140. begin
  141. if(!rst_n)
  142. mosi <= 1'b0;
  143. else if(byte_cnt == 4'd1 && clk_cnt == 5'd31)
  144. mosi <= 1'b0;
  145. else if(byte_cnt == 4'd8 && clk_cnt == 5'd31)
  146. mosi <= 1'b0;
  147. else if(byte_cnt == 4'd1 && sck_cnt == 2'd0)
  148. mosi <= WR_EN_INST[7 - bit_cnt];
  149. else if(byte_cnt == 4'd5 && sck_cnt == 2'd0)
  150. mosi <= SE_INST[7 - bit_cnt];
  151. else if(byte_cnt == 4'd6 && sck_cnt == 2'd0)
  152. mosi <= SE_ADDR[7 - bit_cnt];
  153. else if(byte_cnt == 4'd7 && sck_cnt == 2'd0)
  154. mosi <= PP_ADDR[7 - bit_cnt];
  155. else if(byte_cnt == 4'd8 && sck_cnt == 2'd0)
  156. mosi <= BYTE_ADDR[7 - bit_cnt];
  157. else
  158. mosi <= mosi;
  159. end
  160. endmodule

        顶层例化flash_se_top模块如下:

  1. `timescale 1ns/1ps
  2. module flash_se_top(
  3. input sys_clk,
  4. input rst_n,
  5. input key_in,
  6. output cs_n,
  7. output sck,
  8. output mosi
  9. );
  10. parameter SE_ADDR = 8'h00; //扇区擦除地址
  11. parameter PP_ADDR = 8'h05; //页地址
  12. parameter BYTE_ADDR = 8'h20; //字节地址
  13. wire key_flag;
  14. //spi_flash_se模块例化
  15. spi_flash_se
  16. #(
  17. .SE_ADDR (SE_ADDR),
  18. .PP_ADDR (PP_ADDR),
  19. .BYTE_ADDR (BYTE_ADDR)
  20. )
  21. u_spi_flash_se(
  22. .clk(sys_clk),
  23. .rst_n(rst_n),
  24. .key_in(key_flag),
  25. .cs_n(cs_n),
  26. .sck(sck),
  27. .mosi(mosi)
  28. );
  29. //key_filter 模块例化
  30. key_filter u_key_filter(
  31. .clk(sys_clk),
  32. .rst_n(rst_n),
  33. .key_in(key_in), //按键输入
  34. .key_flag(key_flag) //检测到按键按下标志信号
  35. );
  36. endmodule

仿真代码flash_se_top_tb如下:

  1. `timescale 1ns/1ps
  2. module flash_se_top_tb();
  3. reg sys_clk;
  4. reg rst_n;
  5. reg key_in;
  6. wire cs_n;
  7. wire sck;
  8. wire mosi;
  9. initial
  10. begin
  11. sys_clk = 1'b1;
  12. rst_n = 1'b0;
  13. key_in = 1'b1;
  14. #200
  15. rst_n = 1'b1;
  16. key_in = 1'b0;
  17. #21_000_000
  18. key_in = 1'b1;
  19. end
  20. always #10 sys_clk <= ~sys_clk;
  21. defparam memory.mem_access.initfile = "initmemory.txt";
  22. flash_se_top u_flash_se_top(
  23. .sys_clk(sys_clk),
  24. .rst_n(rst_n),
  25. .key_in(key_in),
  26. .cs_n(cs_n),
  27. .sck(sck),
  28. .mosi(mosi)
  29. );
  30. m25p16 memory
  31. (
  32. .c (sck ), //输入串行时钟,频率12.5Mhz,1bit
  33. .data_in (mosi ), //输入串行指令或数据,1bit
  34. .s (cs_n ), //输入片选信号,1bit
  35. .w (1'b1 ), //输入写保护信号,低有效,1bit
  36. .hold (1'b1 ), //输入hold信号,低有效,1bit
  37. .data_out ( ) //输出串行数据
  38. );
  39. endmodule

仿真结果如下:

       仿真结果显示,在byte_cnt为4’d1时,完成写使能指令写入,当byte_cnt为4’d5时,完成扇区擦除指令写入,随后发送需要擦除的扇区地址、页地址和字节地址三个字节数据,经过3s后,仿真结果提示扇区擦除操作完成。

2.3 板级验证

       将流水灯程固化到Flash芯片中,断电后上电验证流水灯程序可以自启动。然后下载扇区擦除工程到Cyclone IV 开发板中,按下按键,执行扇区擦除操作。最后,重新断电后上电,发现流水灯程序无法自启动,证明扇区擦除程序板级验证成功。

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

闽ICP备14008679号