FPGA project : flash_erasure

SPI是什么:

SPI(Serial Peripheral Interface,串行外围设备接口)通讯协议,是Motorola公司提出的一种同步串行接口技术,是一种高速、全双工、同步通信总线,在芯片中只占用四根管脚用来控制及数据传输。

应用:EEPROM、Flash、RTC、ADC、DSP等。

优缺点:全双工通信,通讯方式较为简单,相对数据传输速率较快;没有应答机制确认数据是否接收,在数据可靠性上有一定缺陷(与I2C相比)。

物理层:

SCK (Serial Clock):时钟信号线,用于同步通讯数据;

MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚;

MISO (Master Input,Slave Output):主设备输入/从设备输出引脚;

𝐶𝑆(CS) ̅ (Chip Select):片选信号线,也称为CS_N。

协议层:

spi通讯协议有四种模式:模式0和模式3,从设备在sck上升沿采样。

模式1和模式2在时钟下降沿采样。

模式0和模式1,在cs_n==1时,sck==0。

模式2和模式3,在cs_n==1时,sck==1。

比较常用的就是模式0和模式3。

flash相关资料:

芯片手册是必须要看的。

模块框图:

状态机:

时序图:

代码:

复制代码
module spi (
    input       wire            sys_clk     ,
    input       wire            sys_rst_n   , 
    input       wire            key_start   ,

    output      wire            mosi        ,
    output      wire            miso        ,
    output      reg             cs_n        ,
    output      reg             sck 
);
    // parameter 
    parameter   COMD_W  = 8'h06   ,
                COMD_B  = 8'hc7   ;
    parameter   IDLE    = 4'b0001 ,
                WREN    = 4'b0010 ,
                WEL     = 4'b0100 ,
                BE      = 4'b1000 ;
    // wire signal degine
    wire                IDLEtoWREN; 
    wire                WRENtoWEL ;  
    wire                WRENtoBE  ;   
    wire                BEtoIDLE  ;   
    // reg signal define
    reg     [3:0]       state_c   ;
    reg     [3:0]       state_n   ;
    reg     [3:0]       cnt_20ns  ;
    reg     [3:0]       cnt_bit   ;
    reg                 flag_bit  ;
    reg                 f_b_reg   ; // flag_bit_reg的缩写
    reg     [7:0]       comd      ;
/****************************************************************************/
    // 三段式状态机
    // 现态与次态描述
    // state_c
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            state_c <= IDLE ;
        else
            state_c <= state_n ;
    end
    // state_n
    always @(*) begin
        case (state_c)
        IDLE   :if(IDLEtoWREN)
                    state_n <= WREN ;
                else 
                    state_n <= IDLE ;
        WREN   :if(WRENtoWEL)
                    state_n <= WEL  ;
                else 
                    state_n <= WREN ;
        WEL    :if(WRENtoBE)
                    state_n <= BE   ;
                else 
                    state_n <= WEL  ;
        BE     :if(BEtoIDLE)
                    state_n <= IDLE ;
                else 
                    state_n <= BE   ;
        default:    state_n <= IDLE ;
        endcase
    end
    // 状态转移描述
    assign  IDLEtoWREN  = ( state_c == IDLE) && ( key_start     ) ;
    assign  WRENtoWEL   = ( state_c == WREN) && ( f_b_reg       ) ;
    assign  WRENtoBE    = ( state_c == WEL ) && ( cnt_20ns == 6 ) ;
    assign  BEtoIDLE    = ( state_c == BE  ) && ( f_b_reg       ) ;
    // 相关信号描述
    // reg     [3:0]       cnt_20ns  ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            cnt_20ns <= 4'd0 ;
        else 
        case (state_c)
        IDLE :  cnt_20ns <= 4'd0 ;
        WREN :  if(cnt_20ns || f_b_reg)
                    cnt_20ns <= 4'd0 ;
                else 
                    cnt_20ns <= cnt_20ns + 1'b1 ;
        WEL  :  if(cnt_20ns == 6) // 60x20ns==120ns
                    cnt_20ns <= 4'd0 ;
                else
                    cnt_20ns <= cnt_20ns + 1'b1 ;
        BE   :    if(cnt_20ns || f_b_reg)
                    cnt_20ns <= 4'd0 ;
                else 
                    cnt_20ns <= cnt_20ns + 1'b1 ;
        default:    cnt_20ns <= 4'd0 ;
        endcase
    end
    // reg     [3:0]       cnt_bit   ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n)
            cnt_bit <= 4'd0 ;
        else 
        case (state_c)
        IDLE :  cnt_bit <= 4'd0 ;
        WREN :  if(!cnt_20ns && sck && cnt_bit == 7)
                    cnt_bit <= 4'd0 ;
                else if(!cnt_20ns && sck)
                    cnt_bit <= cnt_bit + 1'b1 ;
        WEL  :  cnt_bit <= 4'd0 ;
        BE   :  if(!cnt_20ns && sck && cnt_bit == 7)
                    cnt_bit <= 4'd0 ;
                else if(!cnt_20ns && sck)
                    cnt_bit <= cnt_bit + 1'b1 ;
        default:    cnt_bit <= 4'd0 ;
        endcase
    end
    // reg                 flag_bit  ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            flag_bit <= 1'b0 ;
        else
        case (state_c)
        IDLE :  flag_bit <= 1'b0 ;
        WREN :  if(cnt_bit == 7 && sck && !cnt_20ns)
                    flag_bit <= 1'b1 ;
                else 
                    flag_bit <= flag_bit ;
        WEL  :  flag_bit <= 1'b0 ;
        BE   :  if(cnt_bit == 7 && sck && !cnt_20ns)
                    flag_bit <= 1'b1 ;
                else 
                    flag_bit <= flag_bit ;
        default: flag_bit <= 1'b0 ;
        endcase
    end
    // reg                 f_b_reg   ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            f_b_reg <= 1'b0 ;
        end else begin
            f_b_reg <= flag_bit ;
        end
    end
    // reg     [7:0]       comd      ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n)
            comd <= 8'd0 ;
        else
        case (state_c)
        IDLE :  comd <= 8'd0 ;
        WREN :  if(cnt_20ns && sck)
                    comd <= (COMD_W << cnt_bit) ;
                else if(!cnt_bit)
                    comd <= COMD_W ;
                else 
                    comd <= comd ;
        WEL  :  comd <= 8'd0 ;
        BE   :  if(cnt_20ns && sck)
                    comd <= (COMD_B << cnt_bit) ;
                else if(!cnt_bit)
                    comd <= COMD_B ;
                else 
                    comd <= comd ;
        default :   comd <= 8'd0 ;
        endcase 
    end
    // output signal
    // wire            mosi        ,
    assign mosi = comd[7] ;
    // wire            miso        ,
    assign miso = 1'bz ;
    // reg             cs_n        ,
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            cs_n <= 1'b1 ;
        end else begin
            case (state_c)
            IDLE :  if(key_start)
                        cs_n <= 1'b0 ;
                    else 
                        cs_n <= 1'b1 ;
            WREN :  if(f_b_reg)
                        cs_n <= 1'b1 ;
                    else 
                        cs_n <= cs_n ;
            WEL  :  if(cnt_20ns == 6) 
                        cs_n <= 1'b0 ;
                    else 
                        cs_n <= cs_n ;
            BE   :  if(f_b_reg)
                        cs_n <= 1'b1 ;
                    else 
                        cs_n <= cs_n ;
            default:    cs_n <= 1'b1 ;
            endcase
        end
    end
    // reg             sck 
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n)
            sck <= 1'b0 ;
        else 
        case (state_c)
        IDLE :  sck <= 1'b0 ;
        WREN :  if(cnt_20ns)
                    sck <= ~sck ;
                else 
                    sck <= sck  ;
        WEL  :  sck <= 1'b0 ;
        BE   :  if(cnt_20ns)
                    sck <= ~sck ;
                else 
                    sck <= sck  ;
        default:    sck <= 1'b0 ;
        endcase
    end

endmodule

module key_filter
#(
    parameter MAX_CNT_20MS = 20'd100_0000 
)(
    input           wire    sys_clk     ,
    input           wire    sys_rst_n   ,
    input           wire    key_in      ,

    output          wire    key_out     
);

    reg     key_r_0 ;
    reg     key_r_1 ;
    wire    nege    ;
    wire    pose    ;

    reg     [19:00]     cnt_20ms     ;
    wire                add_cnt_20ms ;
    wire                end_cnt_20ms ;
    reg                 add_cnt_flag ;

    // key_r_0 key_r_1 
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            key_r_0 <= 1'b1 ;
        end else begin
            key_r_0 <= key_in ;
        end
    end
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            key_r_1 <= 1'b1 ;
        end else begin
            key_r_1 <= key_r_0 ;
        end
    end

    // nege pose
    assign nege = ~key_r_0 &&  key_r_1 ;
    assign pose =  key_r_0 && ~key_r_1 ;

    // add_cnt_flag
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            add_cnt_flag <= 1'b0 ;
        end else begin
            if(nege) begin
                add_cnt_flag <= 1'b1 ;
            end else begin
                if( pose || end_cnt_20ms ) begin
                    add_cnt_flag <= 1'b0 ;
                end else begin
                    add_cnt_flag <= add_cnt_flag ;
                end
            end 
        end
    end

    // cnt_20ms add_cnt_20ms end_cnt_20ms
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            cnt_20ms <= 20'd0 ;
        end else begin
            if(add_cnt_20ms) begin
                if(end_cnt_20ms) begin
                    cnt_20ms <= 20'd0 ;
                end else begin
                    cnt_20ms <= cnt_20ms + 20'd1 ;
                end
            end else begin
                cnt_20ms <= 20'd0 ;
            end
        end
    end
    assign add_cnt_20ms = add_cnt_flag ;
    assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == ( MAX_CNT_20MS - 1'b1 ) ;

    // key_out
    // always @(posedge sys_clk or negedge sys_rst_n) begin
    // // always @(*) begin // 这样的话 会综合成 数据选择器
    //     if(~sys_rst_n) begin
    //         key_out <= 1'b0 ;
    //     end else begin
    //         if(end_cnt_20ms) begin
    //             key_out <= 1'b1 ;
    //         end else begin
    //             key_out <= 1'b0 ;
    //         end
    //     end
    // end
    assign key_out = end_cnt_20ms ;
endmodule

module top(
    input       wire            sys_clk     ,
    input       wire            sys_rst_n   ,
    input       wire            key_in      ,

    output      wire            cs_n        ,
    output      wire            sck         ,
    output      wire            mosi        
);
    // 例化间连线
    wire            key_flag ;

key_filter key_filter_inst(
    .sys_clk                    ( sys_clk   ) ,
    .sys_rst_n                  ( sys_rst_n ) ,
    .key_in                     ( key_in    ) ,

    .key_out                    ( key_flag  )     
);

spi spi_inst(
    .sys_clk                    ( sys_clk   ) ,
    .sys_rst_n                  ( sys_rst_n ) ,
    .key_start                  ( key_flag  ) ,

    .mosi                       ( mosi      ) ,
    .miso                       (           ) ,
    .cs_n                       ( cs_n      ) ,
    .sck                        ( sck       )
);endmodule

`timescale 1ns/1ns
module test_top();
    reg             sys_clk     ;
    reg             sys_rst_n   ;
    reg             key_in      ;

    wire            cs_n        ;
    wire            sck         ;
    wire            mosi        ;
    wire            miso        ;

top top_inst(
    .sys_clk                ( sys_clk   ) ,
    .sys_rst_n              ( sys_rst_n ) ,
    .key_in                 ( key_in    ) ,

    .cs_n                   ( cs_n      ) ,
    .sck                    ( sck       ) ,
    .mosi                   ( mosi      ) ,
    .miso                   ( miso      )
);

    parameter CYCLE = 20 ;
    defparam  top_inst.key_filter_inst.MAX_CNT_20MS = 20'd100 ;

    initial begin
        sys_clk = 1'b1 ;
        sys_rst_n <= 1'b0 ;
        key_in    <= 1'b1 ;
        #(CYCLE) ;
        sys_rst_n <= 1'b1 ;
        #(CYCLE * 30) ;
        key_in <= 1'b0 ;
        #(CYCLE * 130) ;
        key_in <= 1'b1 ;
        #(CYCLE * 100) ;
        $stop;
    end
    always #(CYCLE/2) sys_clk = ~sys_clk ;

endmodule

仿真波形:

上板验证:

先往板子上固化一个流水灯程序,也就是把生成的.jic文件,写进flash。

重启开发板,流水灯正常工作,说明程序写进了flash。

然后把全擦除程序(.sof),写进开发板。

重启开发板,流水灯效果消失。

说明上板成功。

相关推荐
仲南音2 天前
Xilinx FPGA——时钟IP核MMCM、PLL
fpga开发
I'm a winner3 天前
基于Xilinx FPGA的LVDS高速串行通信系统(三)--数据训练及握手机制【文末源码】
fpga开发
upper20203 天前
coremark移植坑之-nostartfiles -nostdlib参数
学习·fpga开发·coremark移植·riscv设计
Eloudy3 天前
Vivado 纯命令行构建 FPGA 项目教程
fpga开发
Rambo.xia3 天前
AXI跨时钟域与数据一致性——CDC FIFO深度不够、写后读冒险、多Master仲裁、乱序返回,出了Bug根本查不出来
fpga开发·bug
upper20203 天前
FPGA部署卷积神经网络识别MINST图片
fpga开发·卷积神经网络·minst·minst识别·部署神经网络·神经网络识别图片
你是我的解忧王子4 天前
嵌入式 ADC采样 原始数据分析上层(平均值 波动率 正态分布 线性补偿 系统误差)
fpga开发·数据挖掘·数据分析
9527华安4 天前
FPGA纯verilog代码ISP图像处理培训教程,基于IMX214,提供工程源码+视频教程+FPGA开发板
图像处理·fpga开发·isp·imx214·mipi
千寻xun5 天前
二、实战篇-NVME SSD控制之ZYNQ实现(六)-读写NVME SSD硬盘数据
fpga开发·nvme·nvme ssd
千寻xun5 天前
二、实战篇-NVME SSD控制之ZYNQ实现(三)
fpga开发·nvme ssd·nvme协议