大数跨境
0
0

FPGA模块——SPI协议(读写FLASH)

FPGA模块——SPI协议(读写FLASH) 汽车以太网技术研究实验室
2025-12-19
3
导读:FPGA模块——SPI协议(读写FLASH)


点击蓝字

关注我们


(1)FLASH芯片 W25Q16BV

芯片引脚图:

在这里插入图片描述

内部结构图:

存储区域总共分成了32块,每块64KB。每块又分成了16个部分,每个部分4KB。方便进行读取和局部操作。

在这里插入图片描述

电路设计

在这里插入图片描述


(2)SPI协议

SPI的四种模式

在这里插入图片描述

这里使用这个模式:

主机和从机在时钟上升沿放入要输出的数据,在时钟下降沿读取要输入的数据。

8个时钟后交换一个字节8位数据(高位在前)。

在这里插入图片描述


(3)芯片部分命令

有个输入时序的要求 开始时CS拉低等待(tSLCH要求最小5ns)再开始, 结束时CS拉高等待(tSHSL用100ns )再进行下一次操作。

在这里插入图片描述

这个寄存器的第一位数据可以判断操作是否完成(BUSY位)

在这里插入图片描述

1.Write Enable(06h)

写使能:开始时CS拉低等待(tSLCH要求最小5ns)再开始,结束时CS拉高等待(tSHSL取100ns )再进行下一次操作。

在这里插入图片描述

2.Chip Erase (C7h / 60h)

整片擦除,要判断操作是否完成

在这里插入图片描述

3.写指令(02h)

数据写多了会把之前的数据覆盖掉,要判断操作是否完成。

在这里插入图片描述

4.读指令(03h)

要判断操作是否完成

在这里插入图片描述


(4)代码

1. FPGA做主机的SPI协议

在这里插入图片描述

对信号进行同步和提前准备:

100m时钟和clk_cnt配合进行数据的读取和输出(clk_cnt有等于1和0的时候)

spi_clk基于100m时钟输出一个相当于clk_cnt的延时半个周期的时钟,确保输入输出数据稳定。

  1. module spi_drive(


  2. input             clk_100m      ,

  3. input             sys_rst_n     ,


  4. //user interface

  5. input             spi_start     ,//spi开启使能。

  6. input [7:0 ]      spi_cmd       ,//FLAH操作指令

  7. input [23:0]      spi_addr      ,//FLASH地址

  8. input [7:0 ]      spi_data      ,//FLASH写入的数据

  9. input [3:0 ]      cmd_cnt       ,


  10. output            idel_flag_r   ,//空闲状态标志的上升沿 

  11. output reg        w_data_req    ,//FLASH写数据请求 

  12. output reg [7:0]  r_data        ,//FLASH读出的数据

  13. output reg        erro_flag     ,//读出的数据错误标志


  14. //spi interface

  15. output reg        spi_cs        ,//SPI从机的片选信号,低电平有效。

  16. output reg        spi_clk       ,//主从机之间的数据同步时钟。

  17. output reg        spi_mosi      ,//数据引脚,主机输出,从机输入。

  18. input             spi_miso       //数据引脚,主机输入,从机输出。


  19. );


  20. //状态机

  21. parameter IDLE         =4'd0;//空闲状态

  22. parameter WEL          =4'd1;//写使能状态

  23. parameter S_ERA        =4'd2;//扇区擦除状态

  24. parameter C_ERA        =4'd3;//全局擦除

  25. parameter READ         =4'd4;//读状态

  26. parameter WRITE        =4'd5;//写状态

  27. parameter R_STA_REG    =4'd6;


  28. //指令集

  29. parameter WEL_CMD      =8'h06;

  30. parameter S_ERA_CMD    =8'h20;

  31. parameter C_ERA_CMD    =8'hc7;

  32. parameter READ_CMD     =8'h03;

  33. parameter WRITE_CMD    =8'h02;

  34. parameter R_STA_REG_CMD=8'h05;


  35. //wire define

  36. wire      idel_flag;


  37. //reg define

  38. reg[3:0]  current_state  ;

  39. reg[3:0]  next_state     ;

  40. reg[7:0 ] data_buffer    ;

  41. reg[7:0 ] cmd_buffer     ;

  42. reg[7:0 ] sta_reg        ;

  43. reg[23:0] addr_buffer    ;

  44. reg[31:0] bit_cnt        ;

  45. reg       clk_cnt        ;

  46. reg       dely_cnt       ;

  47. reg[31:0] dely_state_cnt ;

  48. reg[7:0 ] rd_data_buffer ;

  49. reg       spi_clk0       ;

  50. reg       stdone         ;

  51. reg[7:0 ] data_check     ;

  52. reg       idel_flag0     ;

  53. reg       idel_flag1     ;


  54. //*****************************************************

  55. //**                    main code

  56. //*****************************************************


  57. //*抓取上升沿

  58. assign idel_flag=(current_state==IDLE)?1:0;//空闲状态标志

  59. assign idel_flag_r=idel_flag0&&(~idel_flag1);//空闲状态标志的上升沿


  60. //*抓取上升沿要用的

  61. always @(posedge clk_100m or negedge sys_rst_n )begin

  62. if(!sys_rst_n)begin

  63. idel_flag0<=1'b1;

  64. idel_flag1<=1'b1;

  65. end

  66. else begin

  67. idel_flag0<=idel_flag;

  68. idel_flag1<=idel_flag0;

  69. end

  70. end


  71. //请求数据 + 把数据放入buffer 

  72. always @(posedge clk_100m or negedge sys_rst_n )begin

  73. if(!sys_rst_n)

  74. w_data_req<=1'b0;

  75. else if((bit_cnt+2)%8==0&&bit_cnt>=30&&clk_cnt==0&&current_state==WRITE) //提前2个时钟周期

  76. w_data_req<=1'b1;

  77. else

  78. w_data_req<=1'b0;

  79. end


  80. always @(posedge clk_100m or negedge sys_rst_n )begin//读出的数据移位寄存

  81. if(!sys_rst_n)

  82. rd_data_buffer<=8'd0;

  83. else if(bit_cnt>=32&&bit_cnt<=2080&&clk_cnt==0&&current_state==READ)

  84. rd_data_buffer<={rd_data_buffer[6:0],spi_miso};

  85. else

  86. rd_data_buffer<=rd_data_buffer;

  87. end


  88. always @(posedge clk_100m or negedge sys_rst_n )begin//检查读出的数据是否正确

  89. if(!sys_rst_n)

  90. data_check<=8'd0;

  91. else if(bit_cnt%8==0&&bit_cnt>=40&&clk_cnt==1&&current_state==READ)

  92. data_check<=data_check+1'd1;

  93. else

  94. data_check<=data_check;

  95. end


  96. always @(posedge clk_100m or negedge sys_rst_n )begin//读出的数据

  97. if(!sys_rst_n)

  98. r_data<=8'd0;

  99. else if(bit_cnt%8==0&&bit_cnt>38&&clk_cnt==1&&current_state==READ)

  100. r_data<=rd_data_buffer;

  101. else

  102. r_data<=r_data;

  103. end


  104. always @(posedge clk_100m or negedge sys_rst_n )begin//读出的数据错误标志

  105. if(!sys_rst_n)

  106. erro_flag<=1'd0;

  107. else if(bit_cnt>32&&bit_cnt<=2080&&current_state==READ&&cmd_cnt==6)begin

  108. if(data_check!=r_data)

  109. erro_flag<=1'd1;

  110. else

  111. erro_flag<=erro_flag;

  112. end

  113. else

  114. erro_flag<=erro_flag;

  115. end


  116. //*把数据放入buffer 提前一个周期

  117. always @(posedge clk_100m or negedge sys_rst_n )begin

  118. if(!sys_rst_n)

  119. data_buffer<=8'd0;

  120. else if((bit_cnt+1)%8==0&&bit_cnt>30&&clk_cnt==1)//*把数据放入buffer 提前一个周期

  121. data_buffer<=spi_data;

  122. else if(clk_cnt==1&&current_state==WRITE&&bit_cnt>=32)

  123. data_buffer<={data_buffer[6:0],data_buffer[7]};

  124. else

  125. data_buffer<=data_buffer;

  126. end


  127. //*----位移cmd指令存储器 开始:cs选中且dely未生效,提前了100mhz的周期------------

  128. //使50mhz时数据提前半个周期获得

  129. always @(posedge clk_100m or negedge sys_rst_n )begin

  130. if(!sys_rst_n)

  131. cmd_buffer<=8'd0;

  132. else if(spi_cs==0&&dely_cnt==0)

  133. cmd_buffer<=spi_cmd;

  134. else if(clk_cnt==1&&(current_state==WEL||current_state==S_ERA||current_state==C_ERA

  135.       ||current_state==READ||current_state==WRITE||current_state==R_STA_REG)&&bit_cnt<8)

  136. cmd_buffer<={cmd_buffer[6:0],1'b1};

  137. else

  138. cmd_buffer<=cmd_buffer;

  139. end


  140. //取出地址每一位

  141. always @(posedge clk_100m or negedge sys_rst_n )begin

  142. if(!sys_rst_n)

  143. addr_buffer<=8'd0;

  144. else if(spi_cs==0&&dely_cnt==0)

  145. addr_buffer<=spi_addr;

  146. else if(clk_cnt==1&&(current_state==READ||current_state==WRITE)&&bit_cnt>=8&&bit_cnt<32)

  147. addr_buffer<={addr_buffer[22:0],addr_buffer[23]};

  148. else

  149. addr_buffer<=addr_buffer;

  150. end


  151. //------------使能后clk_cnt输出50M时钟用于操作信号--------------

  152. always @(posedge clk_100m or negedge sys_rst_n )begin

  153. if(!sys_rst_n)

  154. clk_cnt<=1'd0;

  155. else if(dely_cnt==1)

  156. clk_cnt<=clk_cnt+1'd1;

  157. else 

  158. clk_cnt<=1'd0;

  159. end


  160. //*---------cs选中器件后的信号输出的  dely_cnt 可以认为是使能操作------------

  161. always @(posedge clk_100m or negedge sys_rst_n )begin

  162. if(!sys_rst_n)

  163. dely_cnt<=1'd0;

  164. else if(spi_cs==0)begin

  165.    if(dely_cnt<1)

  166. dely_cnt<=dely_cnt+1'd1;

  167. else

  168. dely_cnt<=dely_cnt;

  169. end

  170. else

  171. dely_cnt<=1'd0;

  172. end


  173. //*-----------------结束的延时计时器------------------------------------

  174. always @(posedge clk_100m or negedge sys_rst_n )begin

  175. if(!sys_rst_n)

  176. dely_state_cnt<=1'd0;

  177. else if(spi_cs)begin

  178.    if(dely_state_cnt<400000000)

  179. dely_state_cnt<=dely_state_cnt+1'd1;

  180. else

  181. dely_state_cnt<=dely_state_cnt;

  182. end

  183. else

  184. dely_state_cnt<=1'd0;

  185. end


  186. //*-------------------------bit读写计数---------------------

  187. always @(posedge clk_100m or negedge sys_rst_n )begin

  188. if(!sys_rst_n)

  189. bit_cnt<=11'd0;

  190. else if(dely_cnt==1)begin

  191. if(clk_cnt==1'b1)

  192. bit_cnt<=bit_cnt+1'd1;

  193. else

  194. bit_cnt<=bit_cnt;

  195. end

  196. else

  197. bit_cnt<=11'd0;

  198. end

状态机 :每个状态该干什么,怎么转移

修改里面的命令和转态就可以移植到其他的地方了。

  1. //三段式状态机

  2. always @(posedge clk_100m or negedge sys_rst_n )begin

  3. if(!sys_rst_n)

  4. current_state<=IDLE;

  5. else

  6. current_state<=next_state;

  7. end


  8. always @(*)begin


  9. case(current_state)


  10.   IDLE: begin

  11.          if(spi_start&&spi_cmd==WEL_CMD)

  12. next_state=WEL;

  13.  else if(spi_start&&spi_cmd==C_ERA_CMD)

  14. next_state=C_ERA;

  15.  else if(spi_start&&spi_cmd==S_ERA_CMD)

  16. next_state=S_ERA;

  17.  else if(spi_start&&spi_cmd==READ_CMD)

  18. next_state=READ;

  19.  else if(spi_start&&spi_cmd==WRITE_CMD)

  20. next_state=WRITE;

  21.  else if(spi_start&&spi_cmd==R_STA_REG_CMD)

  22. next_state=R_STA_REG;

  23.  else

  24.            next_state=IDLE;

  25. end


  26. WEL: begin

  27.  if(stdone&&bit_cnt>=8)

  28.   next_state=IDLE;

  29.  else

  30.           next_state=WEL;

  31.  end


  32. S_ERA: begin

  33. if(stdone)

  34. next_state=IDLE;

  35. else

  36. next_state=S_ERA;

  37. end

  38. C_ERA: begin

  39. if(stdone)

  40. next_state=IDLE;

  41. else

  42. next_state=C_ERA;

  43. end

  44. READ: begin

  45. if(stdone&&bit_cnt>=8)

  46. next_state=IDLE;

  47. else

  48. next_state=READ;

  49. end

  50. WRITE: begin

  51. if(stdone&&bit_cnt>=8)

  52. next_state=IDLE;

  53. else

  54. next_state=WRITE;

  55. end

  56. R_STA_REG: begin

  57. if(stdone)

  58. next_state=IDLE;

  59. else

  60. next_state=R_STA_REG;

  61. end


  62. default: next_state=IDLE;

  63. endcase

  64. end


  65. always @(posedge clk_100m or negedge sys_rst_n )begin

  66. if(!sys_rst_n) begin

  67. spi_cs<=1'b1;

  68. spi_clk<=1'b0;

  69. spi_clk0<=1'b0;

  70. spi_mosi<=1'b0;

  71. stdone<=1'b0;

  72. end

  73. else begin

  74. case(current_state)

  75. IDLE: begin

  76. spi_cs<=1'b1;

  77. spi_clk<=1'b0;

  78. spi_mosi<=1'b0;

  79. end


  80. WEL: begin

  81.     stdone<=1'b0;

  82. spi_cs<=1'b0;

  83. if(dely_cnt==1&&bit_cnt<8) begin

  84. spi_clk0<=~spi_clk0;

  85. spi_clk<=spi_clk0;

  86. spi_mosi<=cmd_buffer[7];

  87. end

  88. else if(bit_cnt==8&&clk_cnt==0)begin

  89.    stdone<=1'b1;

  90. spi_clk<=1'b0;

  91. spi_mosi<=1'b0;

  92. end

  93. else if(bit_cnt==8&&clk_cnt==1)begin

  94. spi_cs<=1'b1;

  95. end

  96. end

  97. C_ERA: begin

  98. stdone<=1'b0;

  99.         if(dely_state_cnt==10)                

  100. spi_cs<=1'b0;

  101. else if(dely_cnt==1&&bit_cnt<8) begin

  102. spi_clk0<=~spi_clk0;

  103. spi_clk<=spi_clk0;

  104. spi_mosi<=cmd_buffer[7];

  105. end

  106. else if(bit_cnt==8&&clk_cnt==0)begin

  107.    stdone<=1'b1;    

  108. spi_clk<=1'b0;

  109. spi_mosi<=1'b0;

  110. end

  111.  else if(bit_cnt==8&&clk_cnt==1)begin

  112. spi_cs<=1'b1;

  113. end

  114. end

  115. S_ERA: begin

  116.       stdone<=1'b0;  

  117. if(dely_state_cnt==10)                

  118. spi_cs<=1'b0;

  119. else if(dely_cnt==1&&bit_cnt<8) begin

  120. spi_clk0<=~spi_clk0;

  121. spi_clk<=spi_clk0;

  122. spi_mosi<=cmd_buffer[7];

  123. end

  124. else if(bit_cnt>=8&&bit_cnt<32&&spi_cs==0)begin

  125.    spi_cs<=1'b0;

  126. spi_clk0<=~spi_clk0;

  127. spi_clk<=spi_clk0;

  128. spi_mosi<=addr_buffer[23];

  129. end

  130. else if(bit_cnt==32&&clk_cnt==0) begin

  131. spi_cs<=1'b1;

  132. spi_clk<=1'b0;

  133. spi_mosi<=1'b0;

  134. stdone<=1'b1;

  135. end

  136. end

  137.             READ: begin

  138.      stdone<=1'b0;

  139.  if(dely_state_cnt==10)                

  140. spi_cs<=1'b0;

  141. else if(dely_cnt==1&&bit_cnt<8) begin

  142. spi_clk0<=~spi_clk0;

  143. spi_clk<=spi_clk0;

  144. spi_mosi<=cmd_buffer[7];

  145. end

  146. else if(bit_cnt>=8&&bit_cnt<32&&spi_cs==0)begin    

  147. spi_clk0<=~spi_clk0;

  148. spi_clk<=spi_clk0;

  149. spi_mosi<=addr_buffer[23];

  150. end

  151. else if(bit_cnt>=32&&bit_cnt<2080)begin

  152. spi_clk0<=~spi_clk0;

  153. spi_clk<=spi_clk0;

  154. spi_mosi<=1'b0;

  155. end

  156. else if(bit_cnt==2080&&clk_cnt==0) begin

  157. spi_clk<=1'b0;

  158. spi_mosi<=1'b0;

  159. stdone<=1'b1;

  160. end

  161.  else if(bit_cnt==2080&&clk_cnt==1) begin

  162. spi_cs<=1'b1;

  163. end

  164. end

  165.             WRITE: begin

  166.     stdone<=1'b0;

  167.  if(dely_state_cnt==10)                

  168. spi_cs<=1'b0;

  169. else if(dely_cnt==1&&bit_cnt<8) begin

  170. spi_clk0<=~spi_clk0;

  171. spi_clk<=spi_clk0;

  172. spi_mosi<=cmd_buffer[7];

  173. end

  174. else if(bit_cnt>=8&&bit_cnt<32&&spi_cs==0)begin   

  175. spi_clk0<=~spi_clk0;

  176. spi_clk<=spi_clk0;

  177. spi_mosi<=addr_buffer[23];

  178. end

  179. else if(bit_cnt>=32&&bit_cnt<2080)begin

  180. spi_clk0<=~spi_clk0;

  181. spi_clk<=spi_clk0;

  182. spi_mosi<=data_buffer[7];

  183. end

  184. else if(bit_cnt==2080&&clk_cnt==0) begin


  185. spi_clk<=1'b0;

  186. spi_mosi<=1'b0;

  187. stdone<=1'b1;

  188. end

  189.  else if(bit_cnt==2080&&clk_cnt==1) begin

  190. spi_cs<=1'b1;

  191. end

  192.                   end

  193. R_STA_REG:begin              

  194. stdone<=1'b0;

  195.     if(dely_state_cnt==10)                

  196. spi_cs<=1'b0;

  197. else if(dely_cnt==1&&bit_cnt<8)begin

  198. spi_clk0<=~spi_clk0;

  199. spi_clk<=spi_clk0;

  200. spi_mosi<=cmd_buffer[7];

  201. end

  202. else if(bit_cnt==8)begin      

  203. spi_clk0<=~spi_clk0;

  204. spi_clk<=spi_clk0;

  205. spi_mosi<=1'b0;

  206. end                        

  207.  else if(~spi_miso&&bit_cnt%8==0)begin

  208.    spi_clk<=1'b0;

  209. spi_cs<=1'b1;

  210. stdone<=1'b1;

  211.      end

  212. else if(~spi_cs&&dely_cnt==1)begin

  213. spi_clk0<=~spi_clk0;

  214. spi_clk<=spi_clk0;

  215. end            

  216.  end 

  217.              default: begin

  218.            stdone<=1'b0;

  219.                         spi_cs<=1'b1;

  220.        spi_clk<=1'b0;

  221. spi_clk0<=1'b0;

  222.        spi_mosi<=1'b0;        

  223. end

  224.          endcase

  225. end

  226. end


  227. endmodule


2. SPI协议的使用

首先系统开始运行,来几个周期延伸。

spi_start信号只是一个周期脉冲。

idel_flag_r是进入空闲状态的标志位也就是意味着上一步操作完成。

cmd计数指令不断加来切换不同的命令。

spi_cmd 输出命令

  1. module flash_rw(


  2. input            sys_clk      ,

  3. input            sys_rst_n    ,


  4. input            idel_flag_r  ,

  5. input            w_data_req   ,

  6. output reg[3:0 ] cmd_cnt      ,

  7. output reg       spi_start    ,//spi开启使能。

  8. output reg[7:0 ] spi_cmd      ,

  9. output reg[7:0 ] spi_data      


  10. );


  11. //指令集

  12. parameter WEL_CMD      =16'h06;

  13. parameter S_ERA_CMD    =16'h20;

  14. parameter C_ERA_CMD    =16'hc7;

  15. parameter READ_CMD     =16'h03;

  16. parameter WRITE_CMD    =16'h02;

  17. parameter R_STA_REG_CMD=8'h05 ;


  18. //reg define

  19. reg[3:0] flash_start;



  20. //SPI 要写入的数据

  21. always @(posedge sys_clk or negedge sys_rst_n )begin

  22. if(!sys_rst_n)

  23. flash_start<=0;

  24. else if(flash_start<=5)

  25.    flash_start<=flash_start+1;

  26. else

  27. flash_start<=flash_start;

  28. end


  29. always @(posedge sys_clk or negedge sys_rst_n )begin

  30. if(!sys_rst_n)

  31. cmd_cnt<=0;

  32. else if(flash_start==4)

  33.    spi_start<=1'b1;

  34. else if(idel_flag_r&&cmd_cnt<10)begin

  35.    cmd_cnt<=cmd_cnt+1;

  36. spi_start<=1'b1;

  37. end

  38. else begin

  39. cmd_cnt<=cmd_cnt;

  40. spi_start<=1'b0;

  41. end

  42. end


  43. always @(posedge sys_clk or negedge sys_rst_n )begin

  44. if(!sys_rst_n)

  45. spi_data<=8'd0;

  46. else if(w_data_req)

  47. spi_data<=spi_data+1'b1;

  48. else

  49. spi_data<=spi_data;

  50. end


  51. always @(*)begin

  52. case(cmd_cnt)

  53. 0:spi_cmd=WEL_CMD;

  54. 1:spi_cmd=C_ERA_CMD;

  55. 2:spi_cmd=R_STA_REG_CMD;

  56. 3:spi_cmd=WEL_CMD;

  57. 4:spi_cmd=WRITE_CMD;

  58. 5:spi_cmd=R_STA_REG_CMD;

  59. 6:spi_cmd=READ_CMD;

  60. 7:spi_cmd=WEL_CMD;

  61. 8:spi_cmd=S_ERA_CMD;

  62. 9:spi_cmd=R_STA_REG_CMD;

  63. 10:spi_cmd=READ_CMD;


  64. default:;

  65. endcase

  66. end


  67. endmodule



*免责声明:本文由作者原创或转发。如无意中侵犯了某方的知识产权,告之即删。以上图文来源于网络,如有侵权,请及时联系我们,我们将在24小时内删除。文章内容系作者个人观点,汽车以太网技术研究实验室转载仅为了传达一种不同的观点,不代表汽车以太网技术研究实验室对该观点赞同或支持,如果有任何异议,欢迎联系汽车以太网技术研究实验室。

原文链接:

https://blog.csdn.net/Treasureljc/article/details/134564121

【声明】内容源于网络
0
0
汽车以太网技术研究实验室
以客观、严谨的态度,讲解SOME/IP、AVB、TSN和SOA等相关知识。交流汽车以太网的最新资讯,包括网络、诊断、软件等。第一时间传递最新报道和深度解读。
内容 359
粉丝 0
汽车以太网技术研究实验室 以客观、严谨的态度,讲解SOME/IP、AVB、TSN和SOA等相关知识。交流汽车以太网的最新资讯,包括网络、诊断、软件等。第一时间传递最新报道和深度解读。
总阅读826
粉丝0
内容359