Xilinx FPGA:vivado SPI实现FLASH通信

一、实验要求

        要求使用SPI协议实现对flash芯片的页编程、读操作、页擦除等功能。

二、模块划分

大概的时序图:

三、程序设计
(1)接收端模块
`timescale 1ns / 1ps
module uart_rx(
   input                    sys_clk   ,
   input                    rst_n     ,
   (* MARK_DEBUG="true" *)input                    rx_data   ,
   (* MARK_DEBUG="true" *)output    reg[7:0]       uart_data ,
   (* MARK_DEBUG="true" *)output    reg            rx_done  
    );
    parameter               SYSCLK = 50_000_000 ;
    parameter               Baud   = 115200     ; 
    parameter               COUNT  = SYSCLK/Baud;
    parameter               MID    = COUNT/2    ;
    
    ///start_flag
    reg             rx_reg1 ;
    reg             rx_reg2 ;
    (* MARK_DEBUG="true" *)wire            start_flag ;
    
    always@(posedge sys_clk )
         if(!rst_n)begin
            rx_reg1 <= 1 ;
            rx_reg2 <= 1 ;
         end
         else
             begin
                  rx_reg1 <= rx_data  ;
                  rx_reg2 <= rx_reg1  ;
             end
    assign  start_flag = ~rx_reg1 & rx_reg2 ;
    
    /rx_flag
    (* MARK_DEBUG="true" *)reg               rx_flag  ;
    (* MARK_DEBUG="true" *)reg[4:0]          cnt_bit  ;
    (* MARK_DEBUG="true" *)reg[9:0]          cnt      ;
    
    always@(posedge sys_clk )
         if(!rst_n)
            rx_flag <= 0 ;
         else if ( start_flag == 1 )
            rx_flag <= 1 ;
         else if ( cnt_bit == 10 && cnt == MID - 1 )
            rx_flag <= 0 ;
         else
            rx_flag <= rx_flag ;
    
    //cnt
    always@(posedge sys_clk )
         if(!rst_n)
            cnt <= 0 ;
         else if ( rx_flag == 1 )begin
              if ( cnt == COUNT - 1 )
                   cnt <= 0 ;
              else
                   cnt <= cnt +1 ;
         end
         else
         cnt <= 0 ;
  
  /cnt_bit
    always@(posedge sys_clk )
         if(!rst_n)
            cnt_bit <= 0 ;
         else if ( rx_flag == 1 )begin
              if ( cnt == COUNT - 1 )begin
                  if( cnt_bit == 10 )
                      cnt_bit <= 0 ;
                  else
                      cnt_bit <= cnt_bit +1 ;
              end
              else
              cnt_bit <= cnt_bit ;
         end
         else
         cnt_bit <= 0 ;
         
         
            
  
  ///data_reg                         
     (* MARK_DEBUG="true" *)reg[8:0]         data_reg  ;  //data_reg:01234567 [8]
     always@(posedge sys_clk )     //cnt_bit:[0]12345678[9][10]
          if(!rst_n)
             data_reg <= 0 ;
          else if ( rx_flag == 1 )begin
               if ( cnt_bit > 0 && cnt_bit < 10 && cnt == MID - 1)
                    data_reg[cnt_bit - 1 ] <= rx_data  ;
               else
                    data_reg <= data_reg  ;
          end
          else
          data_reg <= 0 ;
 
 check
   (* MARK_DEBUG="true" *)reg                  check   ;
   always@(posedge sys_clk )
        if(!rst_n)
           check <= 0 ;
        else if ( rx_flag == 1 )begin
             if ( cnt_bit == 10 )
                 check <= ^data_reg  ;
             else
                 check <= 0 ;
        end
        else
        check <= 0 ;
 
 uart_data
 parameter            MODE_CHECK = 0 ;
 
    always@(posedge sys_clk )
         if(!rst_n)
            uart_data <= 0 ;
         else if ( rx_flag == 1 )begin
              if ( cnt_bit == 10 && cnt == 10 && check == MODE_CHECK)
                   uart_data <= data_reg[7:0] ;
              else
                   uart_data <= uart_data  ;
         end
         else
         uart_data <= uart_data  ;
 
 rx_done
 
     always@(posedge sys_clk )
          if(!rst_n)
             rx_done <= 0 ;
          else if ( rx_flag == 1 )begin
               if ( cnt_bit == 10 && cnt == MID/2 - 1 )
                    rx_done <= 1 ;
               else
                    rx_done <= 0 ;
          end
          else
          rx_done <= 0 ;
        
 
 

    
endmodule
(2)cmd模块
`timescale 1ns / 1ps
长度 指令 地址 数据
/* 这个模块的功能是把接收到的数据放在寄存器里,这个模块的功能也可以由fifo和ram完成

*/
module cmd_data(
    input                      clk      ,
    input                      rst_n    ,
    (* MARK_DEBUG="true" *)input       [7:0]          data_in  ,
    (* MARK_DEBUG="true" *)input                      rx_done  ,
    (* MARK_DEBUG="true" *)output     reg             spi_start,
    (* MARK_DEBUG="true" *)output     reg [7:0]       cmd      ,
    (* MARK_DEBUG="true" *)output     reg [7:0]       length   ,//长度
    (* MARK_DEBUG="true" *)output     reg [23:0]      addr     ,//地址
    (* MARK_DEBUG="true" *)output     reg [7:0]       data_out 
    );
    parameter               idle  =   1  ;
    parameter               s0    =   2  ;//长度
    parameter               s1    =   3  ;//指令
    parameter               s2    =   4  ;//地址
    parameter               s3    =   5  ;//地址
    parameter               s4    =   6  ;//地址
    parameter               s5    =   7  ;//数据
    
    (* MARK_DEBUG="true" *)reg[3:0]                 cur_state    ;
    (* MARK_DEBUG="true" *)reg[3:0]                 next_state   ;
    
    state1
    always@(posedge clk)
         if(!rst_n)
            cur_state <= idle ;
         else
            cur_state <= next_state ;
    
    ///state2
    always@(*)
           case(cur_state)
                idle :
                      begin
                           if ( rx_done )//长度
                                next_state = s0 ;
                           else 
                                next_state = cur_state ;
                      end
                s0   :
                      begin
                           if ( rx_done )//指令
                                next_state = s1 ;
                           else
                                next_state = cur_state ;
                      end
                s1   :
                      begin
                           if ( rx_done )//地址
                                next_state = s2 ;
                           else
                                next_state = cur_state ;
                      end
                s2   :
                      begin
                           if ( rx_done )//地址
                                next_state = s3 ;
                           else
                                next_state = cur_state ;
                      end
                s3   :
                      begin
                           if ( rx_done )//地址
                                next_state = s4 ;
                           else
                                next_state = cur_state ;
                      end
                s4   :
                      begin
                           if ( rx_done )//数据
                                next_state = s5 ;
                           else
                                next_state = cur_state ;
                      end
                s5   :          next_state = idle ;
   
           default:next_state = idle ;
           endcase

    //state3
    always@(posedge clk )
         if(!rst_n)begin
            spi_start <= 0 ;
            cmd       <= 0 ;
            length    <= 0 ;
            addr      <= 0 ;
            data_out  <= 0 ;
         end
         else
             case(cur_state)
                  idle :
                        begin
                             spi_start <= 0 ; 
                             cmd       <= cmd ;
                             addr      <= addr ;
                             data_out  <= data_out ;
                             if ( rx_done )
                                  length <= data_in ;
                             else
                                  length <= length ;
                        end
                  s0   :
                        begin
                             spi_start <= 0        ;
                             length    <= length   ;
                             addr      <= addr     ;///其他保持不变避免自动清零
                             data_out  <= data_out ;
                             if ( rx_done )
                                  cmd <= data_in ;
                             else
                                  cmd <= cmd ;
                        end
                  s1   :
                        begin
                             spi_start <= 0        ;
                             length    <= length   ;
                             data_out  <= data_out ; 
                             cmd       <= cmd      ;
                             if ( rx_done )
                                  addr[23:16] <= data_in ;
                             else
                                  addr <= addr ;
                        end
                  s2   :
                        begin
                             spi_start <= 0        ;
                             length    <= length   ;
                             data_out  <= data_out ; 
                             cmd       <= cmd      ;
                             if ( rx_done )
                                  addr[15:8] <= data_in ;
                             else
                                  addr <= addr ;
                        end
                  s3   :
                        begin
                             spi_start <= 0        ;
                             length    <= length   ;
                             data_out  <= data_out ; 
                             cmd       <= cmd      ;
                             if ( rx_done )
                                  addr[7:0] <= data_in ;
                             else
                                  addr <= addr ;
                        end
                  s4   :
                        begin
                             spi_start <= 0        ; 
                             length    <= length   ; 
                             cmd       <= cmd      ;
                             addr      <= addr     ;
                             if ( rx_done )
                                  data_out <= data_in ;
                             else
                                  data_out <= data_out ;
                         end 
                  s5   :
                        begin
                             spi_start <= 1        ;
                             cmd       <= cmd      ;
                             length    <= length   ;
                             addr      <= addr     ;
                             data_out  <= data_out ;
                        end
             default:   begin
                             spi_start <= 0 ;
                             cmd       <= cmd      ;
                             length    <= length   ;
                             addr      <= addr     ;
                             data_out  <= data_out ;
                        end
             endcase
            
    
    
    
    
endmodule
(3)send_data模块
`timescale 1ns / 1ps
/*
将数据发送给主机
指令 地址 数据是不能直接给主机模块的
长度可以直接给主机
*/
module send_data(
    input                   clk      ,
    input                   rst_n    ,
    (* MARK_DEBUG="true" *)input       [7:0]       cmd      ,
    (* MARK_DEBUG="true" *)input       [23:0]      addr     ,
    (* MARK_DEBUG="true" *)input       [7:0]       data_in  ,
    (* MARK_DEBUG="true" *)input                   done_bit ,///传输完一个字节的结束信号
    (* MARK_DEBUG="true" *)input                   done     ,SPI传输完一次数据的结束信号
    (* MARK_DEBUG="true" *)output      reg[7:0]    data_spi 
    );
    parameter              idle = 1 ;
    parameter              s0   = 2 ;
    parameter              s1   = 3 ;
    parameter              s2   = 4 ;
    parameter              s3   = 5 ;
    parameter              s4   = 6 ;
    
    (* MARK_DEBUG="true" *)reg[3:0]                cur_state  ;
    (* MARK_DEBUG="true" *)reg[3:0]                next_state ;
    
    ///state1
    always@(posedge clk )
         if(!rst_n)
            cur_state <= idle ;
         else
            cur_state <= next_state ;
    
    state2
    always@(*)
          case(cur_state)
               idle :   next_state = s0 ;
               s0   :
                     begin
                          if ( done_bit )
                               next_state = s1 ;
                          else if ( done )
                               next_state = idle ;   ///不加这个信号的话后面发送数据会锁在s1状态
                          else
                               next_state = cur_state ;
                     end
               s1   :
                     begin
                          if ( done_bit )
                               next_state = s2 ;
                          else if ( done )
                               next_state = idle ;
                          else
                               next_state = cur_state ;
                     end
               s2   :
                     begin
                          if ( done_bit )
                               next_state = s3 ;
                          else if ( done )
                               next_state = idle ;
                          else
                               next_state = cur_state ;
                     end
               s3   :
                     begin
                          if ( done_bit )
                               next_state = s4 ;
                          else if ( done )
                               next_state = idle ;
                          else
                               next_state = cur_state ;
                     end
               s4   :
                     begin
                          if ( done_bit )
                               next_state = idle ;
                          else if ( done )
                               next_state = idle ;
                          else
                               next_state = cur_state ;
                     end
          default:next_state = idle ;
          endcase

    //state3
    always@(posedge clk )
         if(!rst_n)
            data_spi <= 0 ;
         else
             case(cur_state)
                  idle :  data_spi <= 0           ;  
                  s0   :  data_spi <= cmd         ;
                  s1   :  data_spi <= addr[23:16] ;
                  s2   :  data_spi <= addr[15:8]  ;
                  s3   :  data_spi <= addr[7:0]   ;
                  s4   :  data_spi <= data_in     ;
             default:data_spi <= 0 ;
             endcase
    
    
    
    

endmodule
(4)SPI_Master模块
`timescale 1ns / 1ps
module 
SPI_Master #(
//    parameter               all_bit  = 16  ,
    parameter               data_LEN = 8   ,
    parameter               data_bit = 8   ,
    parameter               delay    = 8     
)
(
    (* MARK_DEBUG="true" *)input                           clk            ,
    (* MARK_DEBUG="true" *)input                           rst_n          ,
    (* MARK_DEBUG="true" *)input                           spi_start      ,
    (* MARK_DEBUG="true" *)input                           miso           ,
    (* MARK_DEBUG="true" *)input       [7:0]               data_in        ,
    (* MARK_DEBUG="true" *)input       [data_LEN-1:0]      data_len       ,
    (* MARK_DEBUG="true" *)output      reg                 mosi           ,
    (* MARK_DEBUG="true" *)output      reg                 done           ,整体的结束信号
    (* MARK_DEBUG="true" *)output      reg[data_bit -1:0]  data_out_tx    ,
    (* MARK_DEBUG="true" *)output      reg                 sck            ,
    (* MARK_DEBUG="true" *)output      reg                 cs             ,
    (* MARK_DEBUG="true" *)output                          done_bit     字节的结束信号
);
//    localparam               data_in = 16'b0011_1101_0000_0000     ;
//    localparam               data_len = 2 ; 
    
//    localparam               data_in = 16'b0011_1101    ;
//    localparam               data_len = 1 ; 


    ///状态机/
    (* MARK_DEBUG="true" *)reg[31:0]           cnt_sck     ;
    (* MARK_DEBUG="true" *)reg[31:0]           cnt_bit     ;
    (* MARK_DEBUG="true" *)reg[2:0]            cur_state   ;
    (* MARK_DEBUG="true" *)reg[2:0]            next_state  ;
    
    localparam          IDLE  =  0       ;
    localparam          s0    =  1       ;
    localparam          s1    =  2       ;
    localparam          s2    =  3       ;   
    
    /state1
    always@(posedge clk )
         if(!rst_n)
            cur_state <= IDLE ;
         else
            cur_state <= next_state ;
    
    ///state2
    always@(*)
            case(cur_state)
                 IDLE :
                        begin
                             if ( spi_start == 1 )
                                  next_state <= s0 ;
                             else
                                  next_state <= cur_state ;
                        end
                 s0   :
                        begin
                             if ( cnt_sck == 1 )
                                  next_state = s1 ;
                             else
                                  next_state = cur_state ;
                        end
                 s1   :
                        begin
                             if ( (cnt_bit+1)/8 == data_len && cnt_sck == delay -1 )
                                   next_state = s2 ;
                             else
                                   next_state = cur_state ;
                        end
                 s2   :
                        begin
                            if ( cnt_sck == delay -1 )
                                 next_state = IDLE ;
                            else
                                 next_state = cur_state ;
                        end
            default:next_state = IDLE;
            endcase

    state3
    always@(posedge clk )
         if(!rst_n)begin
            cnt_bit <= 0 ;
            cnt_sck <= 0 ;
            mosi    <= 0 ;
            sck     <= 0 ;
            cs      <= 1 ;
            data_out_tx<= 0 ;
            done    <= 0 ;
         end
         else begin
               case (cur_state)
                     IDLE  :
                            begin
                                cnt_bit <= 0 ; 
                                cnt_sck <= 0 ; 
                                mosi    <= 0 ; 
                                sck     <= 0 ; 
                                cs      <= 1 ; 
                                data_out_tx<= data_out_tx ; 
                                done    <= 0 ; 
                            end
                     s0    :
                            begin
                                cnt_bit <= 0 ;  
                                mosi    <= 0 ; 
                                sck     <= 0 ; 
                                cs      <= 0 ; 
                                data_out_tx<= 0 ; 
                                done    <= 0 ; 
                                cnt_sck
                                if ( cnt_sck == delay -1 )  
                                     cnt_sck <= 0 ;         
                                else                        
                                     cnt_sck <= cnt_sck +1 ;
                                
                            end
                     s1    :
                            begin
                                cs      <= 0 ; 
                                done    <= 0 ; 
                                /cnt_sck
                                if ( cnt_sck == delay -1 )
                                     cnt_sck <= 0 ;
                                else
                                     cnt_sck <= cnt_sck +1 ;
                                /sck 
                                if ( cnt_sck == delay/2 -1 || cnt_sck == delay -1 )
                                     sck <= ~sck ;
                                else
                                     sck <= sck ;
                                /cnt_bit  
                                if ( cnt_sck == delay -1 )begin
                                     if ( cnt_bit == data_len*data_bit -1 )
                                          cnt_bit <= 0 ;
                                     else
                                          cnt_bit <= cnt_bit +1 ;
                                end
                                else
                                cnt_bit <= cnt_bit ;
                                //mosi
                                if ( cnt_sck == 2 )
//                                     mosi <= data_in [ (data_len*data_bit - 1) - cnt_bit ] ;
                                     mosi <= data_in [ 7 -(cnt_bit)%8 ] ;根据data_in的不同进行调整
                                else
                                     mosi <= mosi ;    
                                /data_out
                                if ( cnt_sck == delay -2 )/上升沿采集数据  
                                     data_out_tx <= { data_out_tx [data_bit-2:0] , miso }   ;
                                else
                                     data_out_tx <= data_out_tx ;              
                            end
                     s2    :
                            begin
                                cnt_bit <= 0 ; 
                                mosi    <= 0 ;  
                                cs      <= 1 ; 
                                sck     <= 0 ;
                                data_out_tx<= data_out_tx ; 
                                /cnt_sck
                                if ( cnt_sck == delay -1 )
                                     cnt_sck <= 0 ;
                                else
                                     cnt_sck <= cnt_sck +1 ;
                                ///sck
//                                if ( cnt_sck == delay/2 -1 || cnt_sck == delay -1 )
//                                     sck <= ~sck ;
//                                else
//                                     sck <= sck ;
                                done
                                if ( cnt_sck == delay -1 )
                                     done <= 1 ;
                                else
                                     done <= 0 ;
                            end
               default:    begin
                                cnt_bit <= 0 ; 
                                cnt_sck <= 0 ; 
                                mosi    <= 0 ; 
                                sck     <= sck ; 
                                cs      <= 1 ; 
                                data_out_tx<= data_out_tx ; 
                                done    <= 0 ; 
                            end
               endcase
         end
            
   assign   done_bit = ( cnt_bit %8 == 7 && cnt_sck == delay -1 )?1:0 ;
    
    
endmodule
(5)发送端模块
`timescale 1ns / 1ps
module uart_tx(
    input                 sys_clk   ,
    input                 rst_n     ,
    input                 tx_start  ,
    input     [7:0]       rd_data   ,
    output    reg         tx_data   ,
    output    reg         tx_done
    );
    parameter            SYSCLK = 50_000_000 ;
    parameter            Baud   = 115200     ;
    parameter            COUNT  = SYSCLK/Baud;
    parameter            MID    = COUNT/2    ;
    
 
    
    //start_flag
    reg              tx_reg1 ;
    reg              tx_reg2 ;
    wire             start_flag ;
    
    always@(posedge sys_clk )
         if(!rst_n)begin
            tx_reg1 <= 0 ;
            tx_reg2 <= 0 ;
         end
         else
             begin
                  tx_reg1 <= tx_start ;
                  tx_reg2 <= tx_reg1  ;
             end
   assign start_flag = tx_reg1 & ~tx_reg2 ;
   
   //tx_flag
   reg              tx_flag  ;
   reg [9:0]        cnt      ;
   reg [4:0]        cnt_bit  ; //0 12345678 9 10 
   
   always@(posedge sys_clk )
        if(!rst_n)
           tx_flag <= 0 ;
        else if ( start_flag )
           tx_flag <= 1 ;
        else if ( cnt == COUNT -1 && cnt_bit == 10 )
           tx_flag <= 0 ;
        else
           tx_flag <= tx_flag ;
   
   //cnt
   always@(posedge sys_clk )
        if(!rst_n)
           cnt <= 0 ;
        else if ( tx_flag )begin
             if ( cnt == COUNT -1 )
                  cnt <= 0 ;
             else
                  cnt <= cnt +1 ;
        end
        else
        cnt <= 0 ;
   
   //cnt_bit 
     always@(posedge sys_clk )
          if(!rst_n)
             cnt_bit <= 0 ;
          else if ( tx_flag )begin
               if ( cnt == COUNT -1 )begin
                   if ( cnt_bit == 10 )
                        cnt_bit <= 0 ;
                   else
                        cnt_bit <= cnt_bit +1 ;
               end
               else
               cnt_bit <= cnt_bit ;
          end
          else
          cnt_bit <= 0 ;
   
   
       //寄存rd_data     rd_data随着cur_state变为STOP后清零,在uart_tx模块
    //中,cnt_bit == 0 的时候可以捕捉到数据
    reg[7:0]              data_reg  ;
    always@(posedge sys_clk )
         if(!rst_n)
            data_reg <= 0 ;
         else if ( tx_flag )begin
              if ( cnt_bit == 0 && cnt == MID -1 )
                  data_reg <= rd_data ;
              else
                  data_reg <= data_reg ;
         end
         else
         data_reg <= data_reg ;
   
     
   
   //tx_data
   parameter                  MODE_CHECK = 0 ;
   always@(posedge sys_clk )
        if(!rst_n)                  //cnt_bit: 0 12345678 9 10
            tx_data <= 0 ;          //rd_data: 01234567
        else if ( tx_flag )begin
             if ( cnt_bit > 0 && cnt_bit <9 )
                  tx_data <= data_reg [ cnt_bit -1 ] ;
             else if ( cnt_bit == 0 )
                  tx_data <= 0 ;
             else if ( cnt_bit == 10 )
                  tx_data <= 1 ;
             else if ( cnt_bit == 9 )
                  tx_data <= (MODE_CHECK == 0 )? ^rd_data : ~^rd_data ;
             else
                  tx_data <= tx_data ;                
        end
        else
        tx_data <= 1 ;
    
    //tx_done 
    always@(posedge sys_clk )
         if(!rst_n)
            tx_done <= 0 ;
         else if ( tx_flag )begin
              if ( cnt == COUNT -1 && cnt_bit == 10 )
                   tx_done <= 1 ;
              else
                   tx_done <= 0 ;
         end
         else
         tx_done <= 0 ;
            
   
   
   
            
    
    
    
    
endmodule
(6)引脚分配
set_property PACKAGE_PIN K17 [get_ports clk]
set_property PACKAGE_PIN P20 [get_ports cs]
set_property PACKAGE_PIN T20 [get_ports miso]
set_property PACKAGE_PIN V20 [get_ports mosi]
set_property PACKAGE_PIN M20 [get_ports rst_n]
set_property PACKAGE_PIN U15 [get_ports rx_data]
set_property PACKAGE_PIN U20 [get_ports sck]
set_property PACKAGE_PIN W15 [get_ports tx_data]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports cs]
set_property IOSTANDARD LVCMOS33 [get_ports miso]
set_property IOSTANDARD LVCMOS33 [get_ports mosi]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports rx_data]
set_property IOSTANDARD LVCMOS33 [get_ports sck]
set_property IOSTANDARD LVCMOS33 [get_ports tx_data]
四、实验结果

FLASH指令集(用到的已经用红框标出来了):

(1)打开写使能

返回FF,捕捉到波形

(2)打开读状态寄存器

返回值02表示打开成功

(3)写入数据

  向01这个地址里面写入22    返回FF

(4)读出

返回刚才写入的数字22

(5)擦除

(6)读出

经过刚才的擦除操作,从000001这个地址里看能否读出来数,读不出来就表示擦除成功

返回FF,擦除成功

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值