【FPGA】FPGA实现SPI协议读写FLASH(二)----- SPI接口驱动模块设计

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

SPI接口驱动模块设计


写在前面
FPGA实现SPI协议读写FLASH系列相关文章
SPI通信协议
【FPGA】FPGA实现SPI协议读写FLASH一----- M25P16操作概述
在上篇文章中对FLASHM25P16读写操作及指令等做了详细介绍本文将通过SPI协议原理设计SPI通信接口实现FPGA与FLASH进行通信。
本项目中所使用的开发板型号Cyclone IV E (EP4CE6F17C8)FLASH型号M25P16。

一、功能分析

SPI接口驱动模块spi_interface主要根据SPI协议原理架起FPGA与FLASH数据传输的“桥梁”将从SPI读写控制模块接收到的指令、地址、数据字节严格按照SPI协议传输给FLASH并接收从FLASH读回的数据发送给SPI读写控制模块。

二、状态机设计

状态转移图如下
在这里插入图片描述
状态说明
IDLE空闲状态等待读写控制模块发起数据传输请求。
READY准备传输数据。
TRANS数据传输状态根据SPI协议发送或接收数据。
DONE数据传输结束。

三、信号说明

PortI/O TypeDescription
clkinput时钟信号
rst_ninput复位信号
reqinput读/写数据请求
din[7:0]input要传输给FLASH的数据
rdout[7:0]output从FLASH读回的数据
rw_doneoutput读/写一字节数据完成标志
sclkoutput同步时钟
cs_noutput片选信号
mosioutput主机输出从机输入信号
misoinput主机输入从机输出信号

四、代码实现

//  **************************************************************
//  Author: Zhang JunYi
//  Create Date: 2022.11.15                        
//  Design Name: spi_flash    
//  Module Name: spi_interface         
//  Target Device: Cyclone IV E (EP4CE6F17C8), FLASH(M25P16)                
//  Tool versions: Quartus Prime 18.1             
//  Description: SPI读写FLASH工程SPI接口模块
//  **************************************************************

`include "param.v"

module spi_interface (
    input               clk             ,
    input               rst_n           ,
    //  flash_control
    input               req             ,       //  读/写数据请求
    input   [7:0]       din             ,       //  要写入的数据
    output  [7:0]       rdout           ,       //  读回的数据
    output              rw_done         ,       //  读写一字节数据完成标志
    //  M25P16
    output              sclk            ,
    output              cs_n            ,
    output              mosi            ,
    input               miso               
);
    //  参数定义
    localparam  IDLE    =   4'b0001 ,
                READY   =   4'b0010 ,
                TRANS   =   4'b0100 ,
                DONE    =   4'b1000 ;
    
    //  信号定义
    reg     [3:0]       state_c         ;
    reg     [3:0]       state_n         ; 

    reg     [4:0]       cnt_sclk        ;       //  sclk计数器
    wire                add_cnt_sclk    ;
    wire                end_cnt_sclk    ;

    reg     [4:0]       cnt_bit         ;       //  sclk计数器
    wire                add_cnt_bit     ;
    wire                end_cnt_bit     ;

    reg                 sclk_reg        ;       //  输出sclk寄存
    reg                 tx_data         ;       //  数据输出寄存
    reg     [7:0]       rx_data         ;       //  读回数据寄存

    //  状态转移条件
    wire                idle2ready      ;  
    wire                ready2trans     ;  
    wire                trans2done      ;  
    wire                done2idle       ;  


    //  状态机
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            state_c <= IDLE ;
        end
        else begin
            state_c <= state_n ;
        end
    end  

    always @(*)begin
        case (state_c)
            IDLE: begin
                if(idle2ready)
                    state_n = READY ;
                else
                    state_n = state_c ;
            end
            READY: begin
                if(ready2trans)
                    state_n = TRANS ;
                else
                    state_n = state_c ;
            end
            TRANS: begin
                if(trans2done)
                    state_n = DONE ;
                else
                    state_n = state_c ;
            end
            DONE: begin
                if(done2idle)
                    state_n = IDLE ;
                else
                    state_n = state_c ;
            end
            default: state_n = IDLE ;
        endcase
    end 

    //  状态转移条件
    assign idle2ready   = state_c == IDLE   && req          ;
    assign ready2trans  = state_c == READY  && (1'b1)       ;
    assign trans2done   = state_c == TRANS  && end_cnt_bit  ;
    assign done2idle    = state_c == DONE   && (1'b1)       ; 

    //  cnt_sclk
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_sclk <= 0 ;
        end
        else if(add_cnt_sclk)begin
            if(end_cnt_sclk)begin
                cnt_sclk <= 0 ;
            end				
            else begin	    
                cnt_sclk <= cnt_sclk + 1 ;
            end 		    
        end
    end                  
    assign	add_cnt_sclk = state_c == TRANS ;
    assign	end_cnt_sclk = add_cnt_sclk && (cnt_sclk == `SCLK_CYCLE - 1) ;

    //  sclk_reg
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            sclk_reg <= 1'b1 ;
        end
        else if(add_cnt_sclk && cnt_sclk == `SCLK_FALL)begin
            sclk_reg <= 1'b0 ;
        end
        else if(add_cnt_sclk && cnt_sclk == `SCLK_RISE)begin
            sclk_reg <= 1'b1 ;
        end
    end

    //  cnt_bit
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_bit <= 0 ;
        end
        else if(add_cnt_bit)begin
            if(end_cnt_bit)begin
                cnt_bit <= 0 ;
            end				
            else begin	    
                cnt_bit <= cnt_bit + 1 ;
            end 		    
        end
    end                  
    assign	add_cnt_bit =  end_cnt_sclk ;
    assign	end_cnt_bit = add_cnt_bit && (cnt_bit == 7) ;

    //  tx_data
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            tx_data <= 1'b1 ;
        end
        else if(state_c == TRANS && cnt_sclk == `TRANS_S)begin
            tx_data <= din[7 - cnt_bit] ;
        end
        else if(trans2done)begin
            tx_data <= 1'b1 ;
        end
    end

    //  rx_data
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            rx_data <= 0 ;
        end
        else if(state_c == TRANS && cnt_sclk == `SAMP_S)begin
            rx_data[7 - cnt_bit] <= miso ;
        end
    end

    //  输出
    assign rdout = rx_data ;   
    assign rw_done = trans2done ; 

    assign sclk = sclk_reg ;   
    assign cs_n = ~req ;  
    assign mosi = tx_data ;    
   
    
endmodule

五、仿真测试

SPI接口模块发送数据
在这里插入图片描述
M25P16从机模型接收数据
在这里插入图片描述
通过仿真分析M25P16从机模型接收数据正确SPI接口模块功能已实现。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6