当前位置:   article > 正文

SPI控制Flash读写_spi flash

spi flash

一、SPI协议简介

1.SPI简介

1.SPI(Serial Peripheral Interface)是一种高速、全双工、同步串行通信总线,由摩托摩拉公司推出。

2.优缺点:全双工通信,通信方式简单,相对数据传输速度较快;SPI没有应答机制确认数据是否接收,数据可靠性上有一定缺陷。(相对IIC协议)。

2.SPI原理

2.1 主要端口:

SCK :时钟信号线,用于同步通讯数据。

MOSI:主设备输出/从设备输入引脚。

MISO:主设备输入/从设备输出引脚。

CS :片选信号。也称CS_N(片选信号低电平有效)。片选信号线独立,有多少个从机就有多少个CS。

2.2 通信模式(四种)

CPOL(时钟极性) : 片选信号处于空闲状态时,时钟的电平

CPHA(时钟相位):采样的时钟边沿(奇数沿/偶数沿);CPHA=0,采样的时钟边沿为奇数沿;CPHA=1,采样的时钟边沿为偶数沿;采样沿保持数据稳定,另一沿改变数据。

2.3数据传输时序

二、Flash简介(M25P16)

1.基本属性

1.1 储存属性:16Mbit,一共32个扇区,每个扇区256页,每页256字节,Flash断电后数据不会擦除

1.2 SPI模式支持:M25P16支持SPI模式0和模式3

1.3 引脚

2.关键时间参数

3.指令

3.1指令集

指令传输时是MSB传输

3.2 使用到的各个指令详解
3.2.1写使能指令(WREN)

WREN( Write Enable)指令通过驱动芯片选择(S)低,发送指令代码,然后驱动芯片选择(S)高。必须在每个页程序(PP)、扇区擦除(SE)、批量擦除(BE)和写入状态寄存器(WRSR)指令之前,输入WREN指令以设置写入启用锁存器(WEL)位。

3.2.2 扇区擦除指令(SE)

扇区擦除(SE)指令将所选扇区内的所有位设置为1 (FFh)。扇区内的任何地址都是扇区擦除(SE)指令的有效地址。指令序列如下所示。芯片选择(S)必须在最后一个地址字节的第八位被锁存后驱动为高电平,否则扇区擦除(SE)指令不执行。一旦芯片选择(S)驱动高,自定时扇区擦除周期(其持续时间为tse)被启动

地址例子:

当扇区擦除周期正在进行时,可以读取状态寄存器以检查正在进行的写入(WIP)位的值。在自定时扇区擦除周期中,WIP位为1,擦除完成后为0。在周期完成之前的某个未指定时间,写使能锁存(WEL)位被重置。一个扇区擦除(SE)指令,应用于由块保护(BP2, BP1, BPO)位保护的页(见表2和表3)不执行。

3.2.3 全擦除指令 BE

全擦除指令将flash内的所有位都置1

3.2.4 读状态寄存器 RDSR

WIP位:Write In Progress (WIP)位表示内存是否正忙于写状态寄存器、程序或擦除周期。当设置为1时,这样的周期正在进行,当重置为0时,没有这样的周期在进行。

WEL位:写使能锁存(WEL)位表示内部写使能锁存的状态。当设置为1时,内部写使能锁存设置,当设置为0时,内部写使能锁存复位,不接受写状态寄存器,程序或擦除指令。

BP2、BP1、BPO位:块保护(BP2、BP1、BPO)位是非易失性的。它们定义了被软件保护以防止程序和擦除指令的区域的大小。这些位是用WRSR指令写入的。当块保护位(BP2、BP1、BPO)中的一个或两个位设置为1时,相关的内存区域(如下图中定义的)将受到保护,不受页程序(PP)和扇区擦除(SE)指令的影响。未设置“硬件保护”模式时,可以写入块保护位(BP2、BP1、BO)。当且仅当块保护(BP2, BP1, BPO)位都为0时,执行Bulk Erase (BE)指令。

SRWD位:状态寄存器写禁止(SRWD)位与写保护(W)信号一起工作。状态寄存器写禁止(SRWD)位和写保护(W)信号允许设备进入硬件保护模式(当状态寄存器写禁止(SRWD)位被置为1,写保护(W)被置为Low)。在这种模式下,状态寄存器(SRWD, BP2, BP1, BPO)的非易失位变成只读位,写状态寄存器(WRSR)指令不再被执行。

3.2.5 页编程指令 PP

如果数据超过256字节,那么前面的数据会被覆盖,只有最后的256字节数据会被保留。如果小于256字节,则正常写入,且不会对本页其他字节数据造成影响

如果页编程的字节地址不是全零的话,就会在本页内循环写入数据(到了本页最后一字节,下一字节会回到本页起始地址编程,即字节地址全零的地方开始

3.2.6 读取指令 READ

可以任意地址读,读完该地址后,地址自加1;

写指令和擦除指令执行时,读取指令不会执行,且不会影响正在执行的指令。

3.2.7 读ID RDID

S拉低 + 读ID指令 + 3字节ID(设备ID、存储类型、存储容量)+ S拉高

读取识别(RDID)指令在数据输出期间的任何时间通过驱动芯片选择(S)高来终止。

当擦除或程序周期正在进行时,任何读标识(RDID)指令都不会被解码,并且对正在进行的周期没有影响。

四、工程实践

1.系统框图

2.程序设计

2.1SPI接口设计
  1. /*SPI主机接口模块,采用模式3*/
  2. module spi_master(
  3. input clk ,
  4. input rst_n ,
  5. //user
  6. input [7:0] din ,
  7. input req ,//主机传输数据请求 加uart 改为din_vld
  8. output [7:0] dout ,//读出的数据
  9. output done ,
  10. //spi_slave
  11. output sclk ,//串行时钟信号
  12. output cs_n ,//片选信号
  13. output mosi ,//主机输出从机输入信号
  14. input miso //主机输入从机输入信号
  15. );
  16. //参数定义
  17. parameter SCLK_PERIOD = 16, //16分频 3.125M 320ns
  18. SCLK_LOW = 4 ,
  19. SCLK_HIGH = 12,
  20. BIT_NUM = 8 ; //bit数参数
  21. parameter IDLE = 4'b0001, //空闲状态
  22. READY = 4'b0010, //准备传输状态
  23. DATA = 4'b0100, //传输过程状态
  24. DONE = 4'b1000; //传输完成状态
  25. //中间信号
  26. reg [3:0] state_c ; //状态机
  27. reg [3:0] state_n ;
  28. reg [3:0] cnt_sclk ;//串行时钟计数器
  29. wire add_cnt_sclk;
  30. wire end_cnt_sclk;
  31. reg [2:0] cnt_bit ; //比特计数器
  32. wire add_cnt_bit ;
  33. wire end_cnt_bit ;
  34. reg spi_sclk ;
  35. reg spi_mosi ;
  36. reg [7:0] rd_data ; //读取的数据
  37. wire idle2ready ; //状态机跳转条件
  38. wire ready2data ;
  39. wire data2done ;
  40. wire done2idle ;
  41. //状态机
  42. always @(posedge clk or negedge rst_n)begin
  43. if(!rst_n)begin
  44. state_c <= IDLE;
  45. end
  46. else begin
  47. state_c <= state_n;
  48. end
  49. end
  50. always @(*)begin
  51. case (state_c)
  52. IDLE :begin
  53. if(idle2ready)begin
  54. state_n = READY;
  55. end
  56. else
  57. state_n = state_c;
  58. end
  59. READY :begin
  60. if(ready2data )begin
  61. state_n = DATA ;
  62. end
  63. else
  64. state_n = state_c;
  65. end
  66. DATA :begin
  67. if(data2done )begin
  68. state_n = DONE;
  69. end
  70. else
  71. state_n = state_c;
  72. end
  73. DONE :begin
  74. if(done2idle)begin
  75. state_n = IDLE;
  76. end
  77. else
  78. state_n = state_c;
  79. end
  80. default: state_n = IDLE;
  81. endcase
  82. end
  83. assign idle2ready = (state_c == IDLE ) && (req );
  84. assign ready2data = (state_c == READY) && (1'b1 );
  85. assign data2done = (state_c == DATA ) && (end_cnt_bit);
  86. assign done2idle = (state_c == DONE ) && (1'b1 );
  87. //cnt_sclk
  88. always @(posedge clk or negedge rst_n) begin
  89. if (!rst_n) begin
  90. cnt_sclk <= 'd0;
  91. end
  92. else if(add_cnt_sclk)begin
  93. if (end_cnt_sclk) begin
  94. cnt_sclk <= 'd0;
  95. end
  96. else begin
  97. cnt_sclk <= cnt_sclk + 1'b1;
  98. end
  99. end
  100. end
  101. assign add_cnt_sclk = (req);
  102. assign end_cnt_sclk = add_cnt_sclk && cnt_sclk == (SCLK_PERIOD - 1);
  103. //比特计数器cnt_bit
  104. always @(posedge clk or negedge rst_n) begin
  105. if (!rst_n) begin
  106. cnt_bit <= 'd0;
  107. end
  108. else if(add_cnt_bit)begin
  109. if (end_cnt_bit) begin
  110. cnt_bit <= 'd0;
  111. end
  112. else begin
  113. cnt_bit <= cnt_bit + 1'b1;
  114. end
  115. end
  116. end
  117. assign add_cnt_bit = (state_c == DATA ) && end_cnt_sclk;
  118. assign end_cnt_bit = add_cnt_bit && cnt_bit == BIT_NUM - 1;
  119. //spi_sclk 生成一个时钟,低电平开始,可画图
  120. always @(posedge clk or negedge rst_n)begin
  121. if(!rst_n)begin
  122. spi_sclk <= 1'b1;
  123. end
  124. else if(cnt_sclk == SCLK_LOW - 1)begin
  125. spi_sclk <= 1'b0;
  126. end
  127. else if(cnt_sclk == SCLK_HIGH - 1)begin
  128. spi_sclk <= 1'b1;
  129. end
  130. end
  131. //spi_csn
  132. //spi_mosi 主机输出 模式3 前沿(下降沿)输出 高位传输
  133. always @(posedge clk or negedge rst_n)begin
  134. if(!rst_n)begin
  135. spi_mosi <= 1'b0;
  136. end
  137. else if(cnt_sclk == SCLK_LOW - 1)begin
  138. spi_mosi <= din[7-cnt_bit];
  139. end
  140. end
  141. // rd_data
  142. always @(posedge clk or negedge rst_n)begin
  143. if(!rst_n)begin
  144. rd_data <= 'd0;
  145. end
  146. else if(cnt_sclk == SCLK_HIGH - 1)begin
  147. rd_data[7-cnt_bit] <= miso;
  148. end
  149. end
  150. //输出描述
  151. assign sclk = spi_sclk ;
  152. assign cs_n = ~req ;
  153. assign mosi = spi_mosi ;
  154. assign dout = rd_data ;
  155. assign done = end_cnt_bit;
  156. endmodule
2.2 SPI读写Flash控制模块设计
2.2.1 控制模块状态跳转

2.2.2 控制模块代码
  1. module spi_contrl #(parameter READ_MAX = 5,WRITE_MAX=5,WAIT_MAX=150000000,DONE_MAX=250000)(
  2. input clk ,
  3. input rst_n ,
  4. input wr_en ,
  5. input rd_en ,
  6. input done ,
  7. input busy ,
  8. input [7:0] rx_data ,//页写输入数据
  9. input rx_data_vld ,//输入数据寄存信号
  10. input [7:0] din_data ,//读取数据
  11. output [7:0] tx_data ,
  12. output tx_data_vld ,//读数据发送到tx模块
  13. output reg [7:0] dout_data ,//输出给接口模块的数据
  14. output reg req
  15. );
  16. /**************************************************************
  17. 信号定义
  18. **************************************************************/
  19. reg [30:0] cnt_delay ;
  20. reg delay_flag ;
  21. wire add_cnt_delay ;
  22. wire end_cnt_delay ;
  23. reg [9:0] cnt_byte ;
  24. reg [9:0] byte_max ;
  25. wire add_cnt_byte ;
  26. wire end_cnt_byte ;
  27. wire idle2wren1 ;
  28. wire idle2read ;
  29. wire wren12se ;
  30. wire se2wait ;
  31. wire wait2wren2 ;
  32. wire wren22pp ;
  33. wire pp2done ;
  34. wire done2idle ;
  35. wire read2done ;
  36. reg [2:0] state ;
  37. wire wfifo_rd ;
  38. wire wfifo_wr ;
  39. wire wfifo_empty ;
  40. wire wfifo_full ;
  41. wire [7:0] wfifo_qout ;
  42. wire [5:0] wfifo_usedw ;
  43. wire rfifo_rd ;
  44. wire rfifo_wr ;
  45. wire rfifo_empty ;
  46. wire rfifo_full ;
  47. wire [7:0] rfifo_qout ;
  48. wire [5:0] rfifo_usedw ;
  49. reg [7:0] dout_data_r ;
  50. reg dout_data_vld_r ;
  51. reg [7:0] din_data_r ;
  52. reg [7:0] tx_data_r ;
  53. parameter IDLE = 0 ,
  54. WREN_1 = 1 ,
  55. SE = 2 ,
  56. WAIT = 3 ,
  57. PP = 5 ,
  58. WREN_2 = 4 ,
  59. DONE = 7 ,
  60. READ = 6 ;
  61. /**************************************************************
  62. 命令码定义
  63. **************************************************************/
  64. parameter READ_ADDR_SECTOR = 8'b0000_0000,//地址
  65. READ_ADDR_PAGE = 8'b0000_0000,
  66. READ_ADDR_WORD = 8'b0000_0000,
  67. WRITE_ADDR_SECTOR = 8'b0000_0000,
  68. WRITE_ADDR_PAGE = 8'b0000_0000,
  69. WRITE_ADDR_WORD = 8'b0000_0000;
  70. parameter READ_CMD = 8'b0000_0011,//指令
  71. READ_ID_CMD = 8'b1001_1111,
  72. WREN_CMD = 8'b0000_0110,
  73. SE_CMD = 8'b1101_1000,
  74. PP_CMD = 8'b0000_0010;
  75. /**************************************************************
  76. 状态机
  77. **************************************************************/
  78. always@(posedge clk or negedge rst_n)
  79. if(!rst_n)
  80. state <= IDLE;
  81. else case(state)
  82. IDLE : if(idle2wren1)
  83. state <=WREN_1 ;
  84. else if(idle2read)
  85. state <=READ ;
  86. WREN_1 : if(wren12se )
  87. state <=SE ;
  88. SE : if(se2wait )
  89. state <=WAIT ;
  90. WAIT : if(wait2wren2 )
  91. state <=WREN_2 ;
  92. WREN_2 : if(wren22pp )
  93. state <=PP ;
  94. PP : if(pp2done )
  95. state <=DONE ;
  96. DONE : if(done2idle )
  97. state <=IDLE ;
  98. READ : if(read2done )
  99. state <=DONE ;
  100. default : state <=IDLE ;
  101. endcase
  102. assign idle2wren1 = state ==IDLE && wr_en ;
  103. assign idle2read = state ==IDLE && rd_en ;
  104. assign wren12se = state ==WREN_1 && end_cnt_delay ;
  105. assign se2wait = state ==SE && end_cnt_delay ;
  106. assign wait2wren2 = state ==WAIT && end_cnt_delay ;
  107. assign wren22pp = state ==WREN_2 && end_cnt_delay ;
  108. assign pp2done = state ==PP && end_cnt_byte ;
  109. assign done2idle = state ==DONE && end_cnt_delay ;
  110. assign read2done = state ==READ && end_cnt_byte ;
  111. /**************************************************************
  112. 延迟计数器
  113. **************************************************************/
  114. //延迟计数器开启信号在数据传输完成后打开
  115. always@(posedge clk or negedge rst_n)
  116. if(!rst_n)
  117. delay_flag<='d0;
  118. else if (wren12se || wren22pp || se2wait || wait2wren2 || pp2done || done2idle)
  119. delay_flag<='d0;
  120. else if ((state == WREN_1 || state == WREN_2 || state == SE ) && end_cnt_byte)
  121. delay_flag<='d1;
  122. else if (state == WAIT || state == DONE)
  123. delay_flag<='d1;
  124. //计数器 wait-3s done-5ms other-120ns
  125. always@(posedge clk or negedge rst_n)
  126. if(!rst_n)
  127. cnt_delay <= 'd0;
  128. else if(add_cnt_delay) begin
  129. if(end_cnt_delay)
  130. cnt_delay <= 'd0;
  131. else
  132. cnt_delay <= cnt_delay + 1'b1;
  133. end
  134. assign add_cnt_delay = delay_flag ;
  135. assign end_cnt_delay = add_cnt_delay && cnt_delay == ((state==WAIT)?(WAIT_MAX-1):(state==DONE?(DONE_MAX-1):5)) ;
  136. /**************************************************************
  137. 字节计数器
  138. **************************************************************/
  139. //字节计数最大值控制
  140. always@(posedge clk or negedge rst_n)
  141. if(!rst_n)
  142. byte_max<=0;
  143. else if (state == READ)
  144. byte_max<=READ_MAX-1;
  145. else if (state == SE)
  146. byte_max<=3;
  147. else if (state == WREN_1||state == WREN_2)
  148. byte_max<=0;
  149. else if (state == PP)
  150. byte_max<=WRITE_MAX-1;
  151. //字节计数器 //接口模块传输一字节后返回done信号开启
  152. always@(posedge clk or negedge rst_n)
  153. if(!rst_n)
  154. cnt_byte <= 'd0;
  155. else if (wren12se || wren22pp || se2wait || wait2wren2 || pp2done || done2idle)
  156. cnt_byte <= 'd0;
  157. else if(add_cnt_byte) begin
  158. if(end_cnt_byte)
  159. cnt_byte <= 'd0;
  160. else
  161. cnt_byte <= cnt_byte + 1'b1;
  162. end
  163. assign add_cnt_byte =( (state == READ) || (state == SE) ||(state == PP)||(state == WREN_1)||(state == WREN_2) )&& done;
  164. assign end_cnt_byte = add_cnt_byte && cnt_byte == byte_max ;
  165. /**************************************************************
  166. 数据控制逻辑
  167. **************************************************************/
  168. always@(posedge clk or negedge rst_n)
  169. if(!rst_n)
  170. dout_data<='d0;
  171. else if (state == READ) begin
  172. case(cnt_byte)
  173. 0 : dout_data = READ_CMD ;
  174. 1 : dout_data = READ_ADDR_SECTOR ;
  175. 2 : dout_data = READ_ADDR_PAGE ;
  176. 3 : dout_data = READ_ADDR_WORD ;
  177. READ_MAX-1 : tx_data_r = din_data ;
  178. default : tx_data_r = din_data ;
  179. endcase
  180. end
  181. else if (state == SE) begin
  182. case(cnt_byte)
  183. 0 : dout_data = SE_CMD ;
  184. 1 : dout_data = WRITE_ADDR_SECTOR;
  185. 2 : dout_data = WRITE_ADDR_PAGE ;
  186. 3 : dout_data = WRITE_ADDR_WORD ;
  187. default : dout_data = 8'h0 ;
  188. endcase
  189. end
  190. else if (state == PP) begin
  191. case(cnt_byte)
  192. 0 : dout_data = PP_CMD ;
  193. 1 : dout_data = WRITE_ADDR_SECTOR;
  194. 2 : dout_data = WRITE_ADDR_PAGE ;
  195. 3 : dout_data = WRITE_ADDR_WORD ;
  196. WRITE_MAX-1 : dout_data = wfifo_qout ;
  197. default : dout_data = wfifo_qout ;
  198. endcase
  199. end
  200. else if (state == WREN_1||state == WREN_2) begin
  201. case(cnt_byte)
  202. 0 : dout_data = WREN_CMD ;
  203. default : dout_data = 8'h0 ;
  204. endcase
  205. end
  206. /**************************************************************
  207. req控制逻辑
  208. **************************************************************/
  209. always @(posedge clk or negedge rst_n)
  210. if(!rst_n)
  211. req <= 1'd0;
  212. else if(end_cnt_byte || delay_flag)
  213. req <= 1'b0;
  214. else if((state == READ) || (state == SE) || (state == PP) || (state == WREN_1) || (state == WREN_2))
  215. req <= 1'b1;
  216. /**************************************************************
  217. fifo控制逻辑
  218. **************************************************************/
  219. fifo_8x256 fifo_wr (
  220. .aclr ( ~rst_n ),
  221. .clock ( clk ),
  222. .data ( rx_data ),
  223. .rdreq ( wfifo_rd ),
  224. .wrreq ( wfifo_wr ),
  225. .empty ( wfifo_empty ),
  226. .full ( wfifo_full ),
  227. .q ( wfifo_qout ),
  228. .usedw ( wfifo_usedw )
  229. );
  230. assign wfifo_rd = (state == PP) && done && (cnt_byte > 3) ;
  231. assign wfifo_wr = ~wfifo_full && rx_data_vld ;
  232. fifo_8x256 fifo_rd (
  233. .aclr ( ~rst_n ),
  234. .clock ( clk ),
  235. .data ( din_data_r ),
  236. .rdreq ( rfifo_rd ),
  237. .wrreq ( rfifo_wr ),
  238. .empty ( rfifo_empty ),
  239. .full ( rfifo_full ),
  240. .q ( rfifo_qout ),
  241. .usedw ( rfifo_usedw )
  242. );
  243. assign rfifo_wr = ~rfifo_full && (state == READ) && (cnt_byte > 3) && done ;
  244. assign rfifo_rd = (~rfifo_empty && busy) || read2done ;
  245. /**************************************************************
  246. 返回数据寄存
  247. **************************************************************/
  248. always@(posedge clk or negedge rst_n)
  249. if(!rst_n)
  250. din_data_r<='d0;
  251. else
  252. din_data_r<=din_data;
  253. /**************************************************************
  254. 读取数据寄存
  255. **************************************************************/
  256. always@(posedge clk or negedge rst_n)
  257. if(!rst_n)begin
  258. dout_data_r <=8'b0 ;
  259. dout_data_vld_r <=1'b0 ;
  260. end
  261. else begin
  262. dout_data_r <=rfifo_qout ;
  263. dout_data_vld_r <=rfifo_rd ;
  264. end
  265. //输出端口
  266. assign tx_data = dout_data_r ;
  267. assign tx_data_vld = dout_data_vld_r;
  268. endmodule
2.3 顶层模块设计
  1. module top(
  2. input clk ,
  3. input rst_n ,
  4. input rx ,
  5. output tx ,
  6. input [1:0] key_in ,
  7. output sclk ,
  8. output cs_n ,
  9. output mosi ,
  10. input miso
  11. );
  12. wire [1:0] key_out ;
  13. wire [7:0] rx_dout ;
  14. wire rx_dout_vld;
  15. wire [7:0] tx_din ;
  16. wire tx_din_vld ;
  17. wire [7:0] dout_data ;
  18. wire req ;
  19. wire [7:0] din_data ;
  20. wire done ;
  21. wire tx_done ;
  22. spi_contrl #(.READ_MAX (20),.WRITE_MAX(20),.WAIT_MAX(150000000),.DONE_MAX(250000)) u_spi_contrl(
  23. .clk (clk ),
  24. .rst_n (rst_n ),
  25. .wr_en (key_out[0] ),
  26. .rd_en (key_out[1] ),
  27. .done (done ),
  28. .busy (tx_done ),
  29. .rx_data (rx_dout ),
  30. .rx_data_vld (rx_dout_vld ),
  31. .din_data (din_data ),
  32. .tx_data (tx_din ),
  33. .tx_data_vld (tx_din_vld ),
  34. .dout_data (dout_data ),
  35. .req (req )
  36. );
  37. spi_master u_spi_master(
  38. .clk (clk ),
  39. .rst_n (rst_n ),
  40. .din (dout_data ),
  41. .req (req ),
  42. .dout (din_data ),
  43. .done (done ),
  44. .sclk (sclk ),
  45. .cs_n (cs_n ),
  46. .mosi (mosi ),
  47. .miso (miso )
  48. );
  49. key_filter #(.KEY_W (2 ) ,.CNT_MAX_20MS(5 ) ) u_key_filter(
  50. .clk (clk ),
  51. .rst_n (rst_n ),
  52. .key_in (key_in ),
  53. .key_out (key_out )
  54. );
  55. uart_rx u_uart_rx(
  56. .clk (clk ),
  57. .rst_n (rst_n ),
  58. .rx_din (rx ),
  59. .rx_dout (rx_dout ),
  60. .rx_dout_vld (rx_dout_vld )
  61. );
  62. uart_tx u_uart_tx(
  63. .clk (clk ),
  64. .rst_n (rst_n ),
  65. .tx_din (tx_din ),
  66. .tx_din_vld (tx_din_vld ),
  67. .tx_dout (tx ),
  68. .tx_done (tx_done )
  69. );
  70. endmodule
2.4 其他模块设计

UART模块、按键消抖模块;

3.仿真测试

3.1 仿真测试代码
  1. `timescale 1ns/1ps
  2. module top_tb();
  3. parameter CLK_CYCLE = 20;
  4. defparam u_top.u_spi_contrl.READ_MAX = 4 + 5; //1字节指令,3字节ID,加上传输的数据量(此处设为5
  5. defparam u_top.u_spi_contrl.WRITE_MAX= 4 + 5;
  6. defparam u_top.u_spi_contrl.WAIT_MAX=1500;
  7. defparam u_top.u_spi_contrl.DONE_MAX=250;
  8. reg clk,rst_n;
  9. reg rx;
  10. reg [1:0] key_in;
  11. always #(CLK_CYCLE/2) clk = ~clk;
  12. wire mosi;
  13. wire miso;
  14. top u_top(
  15. .clk (clk ),
  16. .rst_n (rst_n ),
  17. .rx (rx ),
  18. .tx (tx ),
  19. .key_in (key_in ),
  20. .sclk (sclk ),
  21. .cs_n (cs_n ),
  22. .mosi (mosi ),
  23. .miso (miso )
  24. );
  25. m25p16 m25p16(sclk,mosi,cs_n,w,hold,miso);
  26. integer i;
  27. task my_uart_rx;
  28. input [7:0]data_in;
  29. begin
  30. rx=0;
  31. #(CLK_CYCLE*434);
  32. for(i=0;i<=7;i=i+1)begin
  33. rx = data_in[i];
  34. #(CLK_CYCLE*434);
  35. end
  36. rx = 1;
  37. #(CLK_CYCLE*434);
  38. end
  39. endtask
  40. initial begin
  41. clk = 1'b1;
  42. rst_n = 1'b0;
  43. rx=1;
  44. key_in=2'b11;
  45. #(CLK_CYCLE*2);
  46. rst_n = 1'b1;
  47. #(CLK_CYCLE*2);
  48. my_uart_rx(8'h33);
  49. #(CLK_CYCLE*2);
  50. my_uart_rx(8'h22);
  51. #(CLK_CYCLE*2);
  52. my_uart_rx(8'h44);
  53. #(CLK_CYCLE*2);
  54. my_uart_rx(8'h55);
  55. #(CLK_CYCLE*2);
  56. my_uart_rx(8'h66);
  57. #(CLK_CYCLE*200);
  58. key_in=2'b10;
  59. #(CLK_CYCLE*21);
  60. key_in=2'b11;
  61. wait(u_top.u_spi_contrl.done2idle);
  62. #(CLK_CYCLE*200);
  63. key_in=2'b01;
  64. #(CLK_CYCLE*21);
  65. key_in=2'b11;
  66. wait(u_top.u_spi_contrl.done2idle);
  67. #(CLK_CYCLE*20000);
  68. $stop;
  69. end
  70. endmodule
3.2 仿真测试图

4.上板测试

本次测试采用JCOM上位机,观察下图可见,输入Flash的数据与从中读取到的输出数据一致,至此SPI控制Flash读写代码设计完成。

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

闽ICP备14008679号