• FPGA——SPI总线控制flash(1)(含代码)


    上一篇写了SPI总线的原理,建议先看原理,链接如下: 

    FPGA——SPI总线详解(概念)_居安士的博客-CSDN博客

    这一篇分别来实现SPI总线的flash写使能,读状态

    目录

    flash写使能

    flash读状态

    flash擦除


    flash写使能

    写使能模块要实现控制模块输入start_wr(启动信号)和wren_cmd(写使能指令),写使能信号能把指令输出给spi总线,写使能框图如下:

    输入输出信号:

    名称

    输入/输出

    位宽

    解释

    clk_25m

    input

    1

    时钟信号

    reset

    input

    1

    复位信号

    start_wr

    input

    1

    开始写使能

    wren_cmd

    input

    1

    指令信号(0/1)

    mux_wren_done

    input

    1

    总线返回信号

    mux_wren

    output

    1

    向总线发送请求

    (1:开启 0:关闭)

    wren_done

    output

    1

    写使能完成

    spi_cs

    output

    1

    向总线传输的片选线

    spi_clk

    output

    1

    向总线传输的时钟线

    spi_dout

    output

    1

    向总线传输的数据线

    cnt_wren

    reg

    3

    发送指令个数寄存器(一共8个)

    wren_cmd_reg

    reg

    1

    指令缓存

    valid_cmd

    reg

    7

    指令寄存器

    写使能时序图:

    根据时序图,,当片选线拉低了,开始向总线输出DI(06/04),输出clk采集数据,特别需要注意的是,SPI先发送高位数据,且以字节(8bit)为单位去发送

    写使能状态机流程图如下:

     先把输入的start_wr,wren_cmd ,mux_wren_done打一拍

    1初始状态下:/cs=1,clk=0,dout=1,wren_cnt=0,vaild_cmd=0,如果start_wr=1则跳转

    2指令解析状态:如果指令为0,则输出04;如果指令为1,则输出06,直接跳转

    3等待总线应答:向总线发送开始请求,如果应答完成,跳转

    4发送状态1:如果cnt==8,发送完成,clk=0,cs拉低,dout=1跳转停止状态;如果没有完成,cnt+1,cs拉低,clk=0,把vaild_cmd[7]给dout,跳转发送状态2

    5发送状态2:cs拉低,clk=1把vaild_cmd进行移位,确保每一次都发送的高位

    6停止状态:向总线发送停止请求

    7等总线应答:如果应答完成,跳转

    8完成状态:wren_done=1

    代码如下:

    1. module wr_enable(
    2. input clk,
    3. input reset,
    4. input start_wr,//启动写使能
    5. input wren_cmd,//写使能命令
    6. input mux_wren_done,//总线ack
    7. output reg mux_wren , //启动总线
    8. output reg spi_cs , //
    9. output reg spi_clk , //
    10. output reg spi_dout , //
    11. output reg wren_done //写使能完成
    12. );
    13. reg wren_cmd_reg;
    14. reg [3:0]cnt_wren;//8位指令计数器
    15. reg [7:0] cmd_valid;//8位指令
    16. reg [3:0] state;
    17. always@(posedge clk)begin
    18. if(reset)begin
    19. mux_wren <=1'd0;
    20. spi_cs <=1'd1;
    21. spi_clk <=1'd0;
    22. spi_dout <=1'd1;
    23. wren_done<=1'd0;
    24. cnt_wren <=4'd0;
    25. cmd_valid<=8'd0;
    26. wren_cmd_reg<=1'd0;
    27. state <=4'd0;
    28. end
    29. else begin
    30. case(state)
    31. 4'd0:begin
    32. if(start_wr)begin
    33. state <=4'd1;
    34. wren_cmd_reg<=wren_cmd;//缓存指令:0→ 04h 1→06h
    35. end
    36. else begin
    37. state <=4'd0;
    38. end
    39. end
    40. 4'd1:begin
    41. case(wren_cmd_reg)
    42. 0:cmd_valid<=8'h04;
    43. 1:cmd_valid<=8'h06;
    44. endcase
    45. state <=4'd2;
    46. end
    47. 4'd2:begin
    48. mux_wren<=1'd1;
    49. if(mux_wren_done==1'd1)begin
    50. state <=4'd3;
    51. end
    52. else begin
    53. state <=4'd2;
    54. end
    55. end
    56. 4'd3:begin
    57. if(cnt_wren==4'd8)begin
    58. cnt_wren<=4'd0;//计数器清零
    59. spi_clk <=1'd0;
    60. spi_cs <=1'd1;//片选拉低
    61. spi_dout <=1'd1;
    62. state <=4'd5;
    63. end
    64. else begin
    65. cnt_wren<=cnt_wren+4'd1;
    66. spi_clk <=1'd0;//clk=0
    67. spi_cs <=1'd0;
    68. spi_dout <=cmd_valid[7];//把最高位给dout
    69. state <=4'd4;
    70. end
    71. end
    72. 4'd4:begin
    73. state <=4'd3;
    74. spi_clk <=1'd1;//clk=1
    75. spi_cs <=1'd0;
    76. cmd_valid<={cmd_valid[6:0],1'b0};//移位
    77. end
    78. 4'd5:begin
    79. mux_wren<=1'd0;
    80. state <=4'd6;
    81. end
    82. 4'd6:begin
    83. if(mux_wren_done==1'd1)begin
    84. state <=4'd7;
    85. end
    86. else begin
    87. state <=4'd6;
    88. end
    89. end
    90. 4'd7:begin
    91. wren_done<=1'd1;//发送完成
    92. state <=4'd0;
    93. end
    94. endcase
    95. end
    96. end
    97. endmodule

    编写仿真代码:

    1. module TB_wr_enable(
    2. );
    3. reg clk;
    4. reg reset;
    5. reg start_wr;
    6. reg wren_cmd;
    7. reg mux_wren_done;
    8. wire mux_wren ;
    9. wire spi_cs ;
    10. wire spi_clk ;
    11. wire spi_dout ;
    12. wire wren_done;
    13. wr_enable inst_wr_enable(
    14. .clk (clk),
    15. .reset (reset),
    16. .start_wr (start_wr),//启动写使能
    17. .wren_cmd (wren_cmd),//写使能命令
    18. .mux_wren_done(mux_wren_done),//总线ack
    19. .mux_wren (mux_wren), //启动总线
    20. .spi_cs (spi_cs), //
    21. .spi_clk (spi_clk), //
    22. .spi_dout (spi_dout), //
    23. .wren_done(wren_done) //写使能完成
    24. );
    25. initial begin
    26. clk=0;
    27. reset=1;
    28. #100
    29. reset=0;
    30. #1000
    31. wren_cmd=0;
    32. start_wr=1;
    33. #100
    34. start_wr=0;
    35. //#100
    36. //wren_cmd=1;
    37. //start_wr=1;
    38. //#100
    39. //start_wr=0;
    40. end
    41. always #20 clk=~clk;
    42. reg [3:0]state_tb;
    43. always @(posedge clk)begin
    44. if(reset)begin
    45. state_tb<=4'd0;
    46. mux_wren_done<=1'b0;
    47. end
    48. else begin
    49. mux_wren_done<=1'b0;
    50. case(state_tb)
    51. 4'd0:begin
    52. if(start_wr)
    53. state_tb<=4'd1;
    54. end
    55. 4'd1:begin
    56. if(mux_wren) begin //请求总线
    57. state_tb<=4'd2;
    58. mux_wren_done<=1'b1;
    59. end
    60. else begin
    61. state_tb<=4'd1;
    62. mux_wren_done<=1'b0;
    63. end
    64. end
    65. 4'd2:begin
    66. if(!mux_wren) begin //释放总线
    67. state_tb<=4'd3;
    68. mux_wren_done<=1'b1;
    69. end
    70. else begin
    71. state_tb<=4'd2;
    72. mux_wren_done<=1'b0;
    73. end
    74. end
    75. 4'd3:begin
    76. state_tb<=4'd0;
    77. mux_wren_done<=1'b0;
    78. end
    79. default:begin
    80. state_tb<=4'd0;
    81. end
    82. endcase
    83. end
    84. end
    85. endmodule

     接下来可以看一下仿真的效果,可以看出和数据手册是一致的:

    spi_dout输出0000_0100 (04)

    spi_dout输出0000_0110 (06)

    flash读状态

    通过将 /CS 驱动为低电平并将状态寄存器 1 的指令代码“05h”或状态寄存器 2 的指令代码“35h”在 CLK 的上升沿移入 DI 引脚来输入指令。 然后,状态寄存器位在 CLK 的下降沿在 DO 引脚上移出,最高有效位 (MSB) 在前,时序图如图所示:

    读状态模块图:

    和写使能不同的是,多了数据传输

    读状态输入输出:

    名称

    输入/输出

    位宽

    解释

    start_status

    input

    1

    开始读状态

    status_cmd

    input

    1

    读状态指令

    mux_status_done

    input

    1

    总线返回信号

    mux_status

    output

    1

    请求总线

    status_done

    output

    1

    读状态完成

    status_data

    output

    8

    读取数据

    spi_cs

    output

    1

    spi片选线

    spi_clk

    output

    1

    spi时钟线

    spi_dout

    output

    1

    输入spi

    spi_din

    input

    1

    spi输出

    cnt_status

    reg

    8

    指令计数器

    vaild_cmd

    reg

    8

    指令寄存器

    received_data

    reg

    8

    数据寄存器

    读状态模块流程图如下:

    读状态一共有10个状态:

    0(初始状态):把0或者1的指令信号进行寄存,防止解析时没有指令信号

    1(指令解析):根据0或者1,得到35h或者05h的指令

    2(请求总线1):请求开放总线,若应答则跳转

    3(发送指令1):得到指令35h或者05h最高位,8位都得到后跳转

    4(发送指令2):将指令35h或者05h移位

    5(接收数据1):对接收数据进行移位(低位就会移成高位),全部接收后跳转

    6(接收数据2):将数据55h(来自读状态寄存器)的最高位给接收数据最低位

    7(发送停止):请求关闭总线

    8(请求总线2):等待总线应答

    9(发送完成):发送完成指令=1

    代码如下:

    1. module read_status(
    2. input clk ,
    3. input reset ,
    4. input start_status ,//开始读状态
    5. input status_cmd ,//读状态指令
    6. input mux_status_done ,//总线返回
    7. input spi_din ,//由总线输入
    8. output reg mux_status ,//请求总线
    9. output reg status_done ,//读状态完成
    10. output reg [7:0] status_data,//读状态返回8位数据
    11. output reg spi_cs ,//片选信号
    12. output reg spi_clk ,//时钟信号
    13. output reg spi_dout //总线输入
    14. );
    15. reg [3:0] cnt_status ;//指令计数器
    16. reg status_cmd_reg ;//指令缓存
    17. reg [7:0] vaild_cmd ;//指令寄存器
    18. reg [7:0] received_data;//接收数据
    19. reg [3:0] state;
    20. always@(posedge clk)begin
    21. if(reset)begin
    22. mux_status <=1'd0;
    23. status_done <=1'd0;
    24. status_data <=8'd0;
    25. spi_cs <=1'd1;
    26. spi_clk <=1'd0;
    27. spi_dout <=1'd1;
    28. cnt_status <=4'd0;
    29. status_cmd_reg <= 1'd0;
    30. vaild_cmd <=8'd0;
    31. received_data <=8'd0;
    32. state<=4'd0;
    33. end
    34. else begin
    35. case(state)
    36. 4'd0:begin
    37. if(start_status)begin
    38. state<=4'd1;
    39. status_cmd_reg<=status_cmd;
    40. end
    41. else begin
    42. state<=4'd0;
    43. end
    44. end
    45. 4'd1:begin
    46. case(status_cmd_reg)
    47. 1'd0:vaild_cmd<=8'h05;
    48. 1'd1:vaild_cmd<=8'h35;
    49. endcase
    50. mux_status<=1'd1;
    51. state<=4'd2;
    52. end
    53. 4'd2:begin
    54. if(mux_status_done)begin
    55. state<=4'd3;
    56. end
    57. else begin
    58. state<=4'd2;
    59. end
    60. end
    61. 4'd3:begin
    62. if(cnt_status==4'd8)begin
    63. state<=4'd6;//直接跳转到数据接收2,这样的目的是spi clk可连续
    64. cnt_status<=4'd0;
    65. spi_cs <=1'd0;
    66. spi_clk <=1'd0;
    67. spi_dout <=1'd1;
    68. end
    69. else begin
    70. cnt_status<=cnt_status+4'd1;
    71. spi_cs <=1'd0;
    72. spi_clk <=1'd0;
    73. spi_dout <=vaild_cmd[7];
    74. state<=4'd4;
    75. end
    76. end
    77. 4'd4:begin
    78. state<=4'd3;
    79. spi_clk <=1'd1;
    80. spi_cs <=1'd0;
    81. vaild_cmd<={vaild_cmd[6:0],1'b0};
    82. end
    83. 4'd5:begin
    84. if(cnt_status==4'd8)begin
    85. state<=4'd7;
    86. cnt_status<=4'd0;
    87. spi_cs <=1'd1;
    88. spi_clk <=1'd0;
    89. spi_dout <=1'd1;
    90. status_data<=received_data;
    91. end
    92. else begin
    93. cnt_status<=cnt_status+4'd1;
    94. spi_cs <=1'd0;
    95. spi_clk <=1'd0;
    96. spi_dout <=1'd1;
    97. received_data<={received_data[6:0],received_data[7]};//received_data最低位移位到最高位
    98. state<=4'd6;
    99. end
    100. end
    101. 4'd6:begin
    102. state<=4'd5;
    103. spi_clk <=1'd1;
    104. spi_cs <=1'd0;
    105. received_data[0]<=spi_din;//din给received_data最低位
    106. end
    107. 4'd7:begin
    108. mux_status<=1'd0;
    109. state<=4'd8;
    110. end
    111. 4'd8:begin
    112. if(mux_status_done)begin
    113. state<=4'd9;
    114. end
    115. else begin
    116. state<=4'd8;
    117. end
    118. end
    119. 4'd9:begin
    120. status_done<=1'd1;
    121. state<=4'd0;
    122. end
    123. endcase
    124. end
    125. end
    126. endmodule

    TB仿真代码:

    1. module TB_read_status(
    2. );
    3. reg clk ;
    4. reg reset ;
    5. reg start_status ;
    6. reg status_cmd ;
    7. reg mux_status_done;
    8. reg spi_din ;
    9. wire mux_status ;
    10. wire status_done ;
    11. wire [7:0] status_data ;
    12. wire spi_cs ;
    13. wire spi_clk ;
    14. wire spi_dout ;
    15. read_status inst_read_status(
    16. .clk (clk) ,
    17. .reset (reset) ,
    18. .start_status (start_status) ,//开始读状态
    19. .status_cmd (status_cmd) ,//读状态指令
    20. .mux_status_done(mux_status_done) ,//总线返回
    21. .spi_din (spi_din) ,//由总线输入
    22. .mux_status (mux_status) ,//请求总线
    23. .status_done (status_done) ,//读状态完成
    24. .status_data (status_data) ,//读状态返回8位数据
    25. .spi_cs (spi_cs) ,//片选信号
    26. .spi_clk (spi_clk) ,//时钟信号
    27. .spi_dout (spi_dout) //总线输入
    28. );
    29. initial begin
    30. clk=0;
    31. reset=1;
    32. start_status=0;
    33. status_cmd=0;
    34. #100;
    35. reset=0;
    36. #4000
    37. start_status=1;
    38. status_cmd=1;
    39. #40
    40. start_status=0;
    41. #4000
    42. start_status=1;
    43. status_cmd=0;
    44. #40
    45. start_status=0;
    46. end
    47. always #20 clk=~clk;
    48. reg [3:0]state;
    49. reg [7:0]send_data;
    50. reg [4:0]cnt;
    51. always@(posedge clk)begin
    52. if(reset)begin
    53. state<=4'd0;
    54. mux_status_done<=1'd0;
    55. spi_din<=1'd0;
    56. send_data<=8'd0;
    57. cnt<=4'd0;
    58. end
    59. else begin
    60. case(state)
    61. 4'd0:begin
    62. spi_din <=1'd0;
    63. send_data<=8'H55;//发送数据
    64. cnt<=4'd0;
    65. if(start_status)begin
    66. state<=4'd1;
    67. end
    68. else begin
    69. state<=4'd0;
    70. end
    71. end
    72. 4'd1:begin
    73. if(mux_status==1'd1)begin//通讯开始
    74. mux_status_done<=1'd1;
    75. state<=4'd2;
    76. end
    77. else begin
    78. state<=4'd1;
    79. mux_status_done<=1'd0;
    80. end
    81. end
    82. 4'd2:begin
    83. mux_status_done<=1'd0;
    84. if(spi_clk==1'd1)begin
    85. cnt<=cnt+5'd1;
    86. end
    87. else if(cnt==5'd15) begin//16个clk(输出8指令+输入8数据)
    88. spi_din<=1'd0;
    89. cnt<=5'd0;
    90. send_data<=8'd0;
    91. state<=4'd3;
    92. end
    93. else if(cnt>=5'd7)begin//开始接收数据时
    94. spi_din<=send_data[7];
    95. send_data<={send_data[6:0],send_data[7]};
    96. end
    97. end
    98. 4'd3:begin
    99. if(!mux_status)begin//通讯结束
    100. state<=4'd4;
    101. mux_status_done<=1'd1;
    102. end
    103. else begin
    104. state<=4'd3;
    105. mux_status_done<=1'd0;
    106. end
    107. end
    108. 4'd4:begin
    109. state<=4'd0;
    110. mux_status_done<=1'd0;
    111. end
    112. endcase
    113. end
    114. end
    115. endmodule

    仿真结果如下:

    flash擦除

    擦除分为扇区擦除;32KB擦除;64KB擦除;整片擦除

    在擦除之前必须执行写使能命令,除整片擦除外,它们的时序图都是先发送8bit命令代码,再发送24bit地址:

    整片擦除,只需要发送8bit命令代码,无需发送24bit地址(因为全擦除成1了)

     根据数据手册,擦除不同大小的区域,根据不同的擦除指令,需要延时不同时间

    1 TSE(擦除扇区)       400ms

    2 TE32K(擦除32KB) 1600ms

    3 TE64K(擦除64KB)  2000ms

    4 TCE(擦除整块)       100s

    5 Tpage(页写)            3ms

    因此,需要首先编写擦除延时代码,(由于页写也需要延时,因此放在一起)产生上述5个延时完成的信号,代码如下:

    1. module erase_time(
    2. input clk,
    3. input reset,
    4. input write_en,//页写计时
    5. input erase_en,//擦除计时
    6. input [2:0]erase_cmd,//擦除模式选择
    7. output reg time_done
    8. );
    9. reg [2:0] time_mode;//计时模式(4擦除or页写,一共5种)
    10. reg [31:0]time_cnt ;//擦整片最多100s
    11. reg [31:0]time_max ;//计时最大值
    12. reg [3:0]state;
    13. parameter TSE=20 ;
    14. parameter TE32K=80 ;
    15. parameter TE64K=100;
    16. parameter TCE=1000 ;
    17. parameter Tpage=15 ;
    18. always@(posedge clk)begin
    19. if(reset)begin
    20. time_done<=1'd0;
    21. time_cnt<=32'd0;
    22. time_max<=32'd0;
    23. time_mode<=3'd0;
    24. state<=4'd0;
    25. end
    26. else begin
    27. case(state)
    28. 4'd0:begin
    29. if(write_en)begin
    30. time_mode<=3'd5;
    31. state<=4'd1;
    32. end
    33. else if(erase_en)begin
    34. time_mode<=erase_cmd;//输入的擦除模式
    35. state<=4'd1;
    36. end
    37. else begin
    38. state<=4'd0;
    39. end
    40. end
    41. 4'd1:begin
    42. case(time_mode)//根据不同模式分配计数最大值
    43. 3'd1:time_max<=TSE;
    44. 3'd2:time_max<=TE32K;
    45. 3'd3:time_max<=TE64K;
    46. 3'd4:time_max<=TCE;
    47. 3'd5:time_max<=Tpage;
    48. endcase
    49. state<=4'd2;
    50. end
    51. 4'd2:begin
    52. if(time_max==time_cnt)begin//计数值++
    53. time_cnt<=32'd0;
    54. state<=4'd3;
    55. end
    56. else begin
    57. state<=4'd2;
    58. time_cnt<=time_cnt+32'd1;
    59. end
    60. end
    61. 4'd3:begin
    62. time_done<=1'd1;
    63. state<=4'd0;
    64. end
    65. endcase
    66. end
    67. end
    68. endmodule

    接下来写擦除代码,擦除模块框图如下:

    名称

    输入输出

    位宽

    解释

    start_erase

    input

    1

    启动擦除模块

    erase_cmd

    input

    3

    擦除命令(4种擦除模式)

    erase_addr

    input

    24

    擦除地址

    mux_erase_done

    input

    1

    总线应答

    mux_erase

    output

    1

    请求总线

    erase_done

    output

    1

    擦除完成

    start_time

    output

    1

    启动擦除计时

    time_done

    input

    1

    擦除计时完成

    erase_cmd_out

    output

    3

    擦除指令确定计时器计

    spi_cs

    output

    1

    spi片选线

    spi_clk

    output

    1

    spi时钟线

    spi_dout

    output

    1

    spi输出线

     

     擦除流程图如下:

    擦除过程:

    0(初始状态):把页写突发长度和页写地址寄存

    1(指令解析):把02h给指令寄存器

    2(请求总线):请求打开总线

    3(发送指令1):把指令最高位给spi_dout,移完8位把地址最高位给spi_dout

    4(发送指令2):移位8位指令

    5(发送地址1):把地址最高位给spi_dout,移完24位把数据最高位给spi_dout

    6(发送地址2):移位24位地址

    7(计数发送数据):判断cnt_byte是否等于突发长度

    8(停止):请求总线停止

     

    1. module erase_top(
    2. input clk,
    3. input reset,
    4. input start_erase ,
    5. input [2:0] erase_cmd ,//擦除指令(1234)
    6. input [23:0]erase_addr ,//擦除地址
    7. input mux_erase_done ,//擦除总线ack
    8. input time_done ,//计时完成
    9. output reg mux_erase ,//擦除总线请求
    10. output reg spi_cs ,
    11. output reg spi_clk ,
    12. output reg spi_dout ,
    13. output reg erase_done,//擦除完成
    14. output reg erase_en
    15. );
    16. erase_time inst_erase_time(
    17. .clk (clk),
    18. .reset (reset),
    19. .write_en (),//页写计时
    20. .erase_en (erase_en),//擦除计时
    21. .erase_cmd(erase_cmd),//擦除模式选择
    22. .time_done(time_done)
    23. );
    24. reg [5:0] cnt;
    25. reg[7:0] valid_cmd ;//8位指令寄存器
    26. reg[2:0] erase_cmd_reg ; //指令寄存
    27. reg[23:0]erase_addr_reg; //地址寄存
    28. reg [3:0] state;
    29. always@(posedge clk)begin
    30. if(reset)begin
    31. mux_erase <=1'd0;
    32. spi_cs <=1'd1;
    33. spi_clk <=1'd0;
    34. spi_dout <=1'd1;
    35. erase_done<=1'd0;
    36. cnt <=6'd0;
    37. valid_cmd <=8'd0;
    38. erase_cmd_reg <=3'd0;
    39. erase_addr_reg<=24'd0;
    40. erase_en <=1'd0;
    41. state<=4'd0;
    42. end
    43. else begin
    44. case(state)
    45. 4'd0:begin
    46. if(start_erase)begin
    47. erase_cmd_reg<=erase_cmd;
    48. erase_addr_reg<=erase_addr;
    49. state<=4'd1;
    50. end
    51. else begin
    52. state<=4'd0;
    53. end
    54. end
    55. 4'd1:begin
    56. case(erase_cmd_reg)//指令1234对应的8位命令
    57. 3'D1: valid_cmd <= 8'h20;
    58. 3'D2: valid_cmd <= 8'h52;
    59. 3'D3: valid_cmd <= 8'hd8;
    60. 3'D4: valid_cmd <= 8'hC7;
    61. endcase
    62. state<=4'd2;
    63. end
    64. 4'd2:begin
    65. mux_erase<=1'd1;
    66. if(mux_erase_done)begin
    67. state<=4'd3;
    68. erase_en<=1'd1;//与总线请求通讯后开始计时
    69. end
    70. else begin
    71. state<=4'd2;
    72. end
    73. end
    74. 4'd3:begin
    75. erase_en<=1'd0;
    76. if(cnt==6'd8)begin
    77. cnt<=6'd0;
    78. spi_cs <=1'd0;
    79. spi_clk <=1'd0;
    80. spi_dout <=1'd1;
    81. state<=4'd6;
    82. end
    83. else begin
    84. cnt<=cnt+6'd1;
    85. spi_cs <=1'd0;
    86. spi_clk <=1'd0;
    87. spi_dout <=valid_cmd[7];
    88. state<=4'd4;
    89. end
    90. end
    91. 4'd4:begin
    92. spi_cs <=1'd0;
    93. spi_clk <=1'd1;
    94. valid_cmd<={valid_cmd[6:0],valid_cmd[7]};
    95. state<=4'd3;
    96. end
    97. 4'd5:begin
    98. if(cnt==6'd23)begin
    99. cnt<=6'd0;
    100. spi_cs <=1'd1;
    101. spi_clk <=1'd0;
    102. spi_dout <=erase_addr_reg[23];
    103. state<=4'd7;
    104. end
    105. else begin
    106. cnt<=cnt+6'd1;
    107. spi_cs <=1'd0;
    108. spi_clk <=1'd0;
    109. spi_dout <=erase_addr_reg[23];
    110. state<=4'd6;
    111. end
    112. end
    113. 4'd6:begin
    114. spi_cs <=1'd0;
    115. spi_clk <=1'd1;
    116. erase_addr_reg<={erase_addr_reg[22:0],1'b0};
    117. state<=4'd5;
    118. end
    119. 4'd7:begin
    120. mux_erase<=1'd1;
    121. state<=4'd8;
    122. end
    123. 4'd8:begin
    124. if(mux_erase_done)begin
    125. state<=4'd9;
    126. end
    127. else begin
    128. state<=4'd8;
    129. end
    130. end
    131. 4'd9:begin
    132. if(time_done)begin//延时的时间到了
    133. state<=4'd10;
    134. end
    135. else begin
    136. state<=4'd9;
    137. end
    138. end
    139. 4'd10:begin
    140. erase_done<=1'd0;
    141. state<=4'd0;
    142. end
    143. endcase
    144. end
    145. end
    146. endmodule

    编写仿真TB代码:

    1. module TB_erase_top(
    2. );
    3. reg clk ;
    4. reg reset ;
    5. reg start_erase ;
    6. reg [2:0] erase_cmd ;//擦除指令
    7. reg [23:0]erase_addr ;//擦除地址
    8. reg mux_erase_done ;
    9. wire time_done ;//计时完成
    10. wire mux_erase ;
    11. wire spi_cs ;
    12. wire spi_clk ;
    13. wire spi_dout ;
    14. wire erase_done;
    15. wire erase_en;
    16. erase_top erase_top(
    17. .clk (clk) ,
    18. .reset (reset) ,
    19. .start_erase (start_erase) ,
    20. .erase_cmd (erase_cmd) ,//擦除指令
    21. .erase_addr (erase_addr) ,//擦除地址
    22. .mux_erase_done (mux_erase_done) ,
    23. .time_done (time_done) ,//计时完成
    24. .mux_erase (mux_erase) ,
    25. .spi_cs (spi_cs) ,
    26. .spi_clk (spi_clk) ,
    27. .spi_dout (spi_dout) ,
    28. .erase_done (erase_done) ,
    29. .erase_en (erase_en)
    30. );
    31. initial begin
    32. clk =0;
    33. reset =1;
    34. start_erase =0; //擦除启动信号
    35. erase_cmd =0; //擦除命令数据2
    36. erase_addr =0; //擦除地址
    37. #1000;
    38. reset =0;
    39. #1000;
    40. start_erase =1; //擦除启动信号
    41. erase_cmd =1; //擦除命令数据2
    42. erase_addr =24'haaaa55; //擦除地址
    43. #40;
    44. start_erase =0; //擦除启动信号
    45. erase_cmd =0; //擦除命令数据2位
    46. erase_addr =0; //擦除地址
    47. end
    48. always #20 clk= ~clk;
    49. reg [2:0] state;
    50. always @(posedge clk)begin
    51. if(reset)begin
    52. state <= 3'd0;
    53. mux_erase_done<=1'b0;
    54. end
    55. else begin
    56. case (state)
    57. 3'd0:begin
    58. if(mux_erase)begin
    59. state <= 3'd1;
    60. mux_erase_done<=1'b1;
    61. end
    62. else begin
    63. state <= 3'd0;
    64. mux_erase_done<=1'b0;
    65. end
    66. end
    67. 3'd1:begin
    68. state <= 3'd2;
    69. mux_erase_done<=1'b0;
    70. end
    71. 3'd2:begin
    72. if(!mux_erase)begin
    73. state <= 3'd3;
    74. mux_erase_done<=1'b1;
    75. end
    76. else begin
    77. state <= 3'd2;
    78. mux_erase_done<=1'b0;
    79. end
    80. end
    81. 3'd3:begin
    82. mux_erase_done<=1'b0;
    83. state <= 3'd0;
    84. end
    85. default :state <= 3'd0;
    86. endcase
    87. end
    88. end
    89. endmodule

    仿真图如下:

     

    传擦除模式1(02h)指令

    传擦除地址

  • 相关阅读:
    基于51的单片机GPS定位系统设计
    动态内存函数笔试题
    【PAT】数据结构树和图月考复习1
    全链路自研:腾讯混元大模型释放企业全新可能性
    asp.net core 入口 验证token,但有的接口要跳过验证
    Poison Frogs! Targeted Clean-Label Poisoning Attacks on Neural Networks
    基于低代码平台的PLM系统,实现科学业务管理
    查询快递单号物流,自动识别出物流是否签收
    数据结构-栈和队列(3)
    SAP PS 第9节 合并采购申请、组合WBS之详解
  • 原文地址:https://blog.csdn.net/weixin_46188211/article/details/125884831