SPI的通信试验 --verilog (从机-全双工)

 

    SPI的 有关知识参考FPGA作为主机的通信实验。

    本实验中FPGA作为从机通过SPI与MCU等通信的试验,可以在时钟上升沿接收数据并且在时钟下降沿发送数据,模仿全双工模式。接收的

数据作为地址,通过读取ROM中地址的数据然后发送出去。注意 发送完成以及接收完成之后的数据处理的关系。

 

   程序:

    顶层文件:       

/********************************Copyright**************************************                           
**----------------------------File information--------------------------
** File name  :spi_slave_send.v  
** CreateDate :2015.04
** Funtions   :FPGA作为从机在接收主机发来的地址信号时,输出数据信号,同时还接收主机发来的地址数据(全双工),一直到CS线拉高,没有信号再从主机中发出。
               数据地址都为8bit,spi的时钟在发送完8位之后最好能够间隔一下再发生另外8个时钟   
** Operate on :M5C06N3L114C7
** Copyright  :All rights reserved. 
** Version    :V1.0
**---------------------------Modify the file information----------------
** Modified by   :
** Modified data :        
** Modify Content:
*******************************************************************************/
 
 module  spi_slave (
           clk,
           rst_n,
           
                     spi_cs,
                     spi_sck,
                     spi_miso,
                     spi_mosi,
                     
                     spi_over
             );
 input          clk;
 input          rst_n;
 
 input           spi_cs;
 input           spi_sck;
 output          spi_miso;
 input           spi_mosi;
 
 output          spi_over;

//-----------------------------
 wire   [7:0]    rdata;
 wire            rover;
 wire            txd_en;
 wire   [7:0]    txd_data;
 wire            txd_over;
  spi_slave_rxd  spi_slave_rxd_1(
                                 .clk(clk),
                                 .rst_n(rst_n),
                       
                                 .spi_cs(spi_cs),
                                 .spi_sck(spi_sck),
                                 .spi_miso(),
                                 .spi_mosi(spi_mosi),
                                 
                                 .rdata_out(rdata),
                                 .rover(rover)
                                     );

  data_read  data_read_1(
                   .clk(clk),
                                 .rst_n(rst_n),
                                        
                                 .rover(rover),
                                 .rdata(rdata),
                                         
                                  .txd_en(txd_en),
                                 .txd_data(txd_data)
                                        
                                        );
  spi_slave_txd  spi_slave_txd_1(
               .clk(clk),
               .rst_n(rst_n),
               
                         .txd_en(txd_en),
                         .txd_data(txd_data),
                         
                         .spi_cs(spi_cs),
                         .spi_sck(spi_sck),
                         .spi_mosi(spi_mosi),
                         .spi_miso(spi_miso),
                         
                         .spi_over(spi_over),
                         .txd_over(txd_over)
                 );



endmodule
View Code

    接收程序:

/********************************Copyright**************************************                           
**----------------------------File information--------------------------
** File name  :SPI_slave_rd.v  
** CreateDate :2015.04
** Funtions   :SPI的通信实验,FPGA作为从机,接收主机数据以及向主机发送数据
** Operate on :M5C06N3L114C7
** Copyright  :All rights reserved. 
** Version    :V1.0
**---------------------------Modify the file information----------------
** Modified by   :
** Modified data :        
** Modify Content:
*******************************************************************************/
 

  module  spi_slave_rxd  (
                                 clk,
                                 rst_n,
                       
                                 spi_cs,
                                 spi_sck,
                                 spi_miso,
                                 spi_mosi,
                                 
                                 rdata_out,
                                 rover
                                     );
     input          clk;
     input          rst_n;
     
     input         spi_cs;
     input         spi_sck;
     input         spi_mosi;
     output        spi_miso;
     
     output reg [7:0] rdata_out;
     output reg       rover;
 //---------------------------------------
   reg           spi_cs_0,spi_cs_1;  /* 延时两个时钟,配合检测时钟边沿 */
     reg           spi_sck_0,spi_sck_1;
     reg           spi_mosi_0,spi_mosi_1;
     wire          spi_sck_pos;
//     wire          spi_sck_neg;
     wire          spi_cs_pos;
     wire          spi_cs_neg;
     wire          spi_cs_flag;
     wire          spi_miso_flag;
     always @(posedge clk or negedge rst_n)
     begin
      if(!rst_n)
       begin
          {spi_cs_1,spi_cs_0} <= 2'b11;
                {spi_sck_1,spi_sck_0} <= 2'b00;
                {spi_mosi_1,spi_mosi_0} <= 2'b00;
        end
      else 
        begin
          {spi_cs_1,spi_cs_0} <= {spi_cs_0,spi_cs}; 
                {spi_sck_1,spi_sck_0} <= {spi_sck_0,spi_sck};
                {spi_mosi_1,spi_mosi_0} <= {spi_mosi_0,spi_mosi};
        end
      end
        assign  spi_sck_pos = ~spi_sck_1 &spi_sck_0;  /* 取上升沿 */
//        assign  spi_sck_neg = ~spi_sck_0 &spi_sck_1;  /* 取下降沿 */
      assign  spi_cs_pos  = ~spi_cs_1&spi_cs_0;      /* 取spi_cs上升沿,作为结束信号 */
        assign  spi_cs_neg  = ~spi_cs_0&spi_cs_1;    /* 取spi_cs下降沿,作为开始信号 */
      assign  spi_cs_flag = spi_cs_1;
        assign  spi_miso_flag = spi_mosi_1;
    //----------------------------------------------------------
    localparam  idel = 3'd0;
    localparam  rxd_sta = 3'd1;
    localparam  rxd_over = 3'd2;    
      reg  [3:0]   cnt;
      reg  [7:0]   rdata;
        reg  [2:0]   state;
        always @(posedge clk or negedge rst_n)
         begin
          if(!rst_n)
           begin
             cnt <= 0;
                 rdata <= 0;
                 state <= idel;
                 rover <= 0;
                 rdata_out <= 0;
            end
            else 
                begin
                    case(state)    
                     idel:
                       begin
                            cnt <= 0;
                      rdata <= 0;
                            rover <= 0;
                            rdata_out <= 0;
                            if(spi_cs_neg) 
                               state <= rxd_sta;
                             else 
                                 state <= idel;
                            end
                   rxd_sta:
                       begin
                                if(spi_cs_flag) 
                                  state <= idel;
                                 else 
                                     begin
//                                         if((cnt == 8)&&(spi_sck_neg))     /* 更严谨 */
                                         if((cnt == 8))      
                                           begin
                                                    cnt <= 0;
                                                    state <= rxd_over;
                                                    rdata_out <= rdata;
                                                    rover <= 1;
                                                end
                                          else 
                                              begin
                                                 if(spi_sck_pos)
                                                        begin
                                                         cnt <= cnt + 1;  /* 最后cnt=8 */    
                                                         rdata[4'd7 - cnt] <= spi_miso_flag;  /* 从最高位到最低位逐渐接收数据 */
                                                        end
                                                 else
                                                        begin
                                                            cnt <= cnt ;  
                                                            rdata <= rdata;                      
                                                        end     
                                            end                                         
                                        end
                            end
                     rxd_over:
                        begin
                                rover<= 0;    
                                state <= rxd_sta;    
                             end
                default :    state <= idel;         
             endcase
      end    
  end
    
//    assign rdata_out = rdata;
//          else if(!psi_cs_flag)
//            begin
//             if(spi_sck_pos)
//                  begin
//                         cnt <= cnt + 1;  /* 最后cnt=8 */    
//                         rdata[4'd7 - cnt] <= spi_miso_flag;  /* 从最高位到最低位逐渐接收数据 */
//                        end
//                 else
//                        begin
//                            cnt <= cnt ;  
//                          rdata <= rdata;                      
//                        end
//            end
//             else 
//                 begin
//                            cnt <= 0 ;  
//                          rdata <= rdata;          /* 注意:保持,不清除 */              
//                 end    
//          end
//            
//      //------------------------
//     always @(posedge clk or negedge rst_n)
//     begin
//      if(!rst_n)
//       begin
//          rdata_out <= 8'd0;
//                rover <= 0;
//        end
//      else if(spi_cs_pos)
//        begin
//              rdata_out <= rdata; /* 赋值 */
//                rover <= 1;         /* 置位 */
//        end
//        else 
//            begin
//              rdata_out <= rdata_out;  /* 注意:保持,不清除 */   
//                rover <= 0;        /* 清除 */
//        end
//      end
 
    endmodule
    
    
View Code

   数据转换程序:

/********************************Copyright**************************************                           
**----------------------------File information--------------------------
** File name  :data_read.v  
** CreateDate :2015.04
** Funtions   : 因为接收完成标志只有一个时钟周期,所以需要马上寄存地址数据,并且地址读取待发送的数据,然后置位发送是能信号。
** Operate on :M5C06N3L114C7
** Copyright  :All rights reserved. 
** Version    :V1.0
**---------------------------Modify the file information----------------
** Modified by   :
** Modified data :        
** Modify Content:
*******************************************************************************/
 
   module data_read(
                      clk,
                                        rst_n,
                                        
                                        rover,
                                        rdata,
                                        
                                        txd_en,
                                        txd_data
                                        
                                        ); 

  input         clk;
    input         rst_n;
    
    input         rover;
    input  [7:0]  rdata;
    
    output        txd_en;
    output  [7:0] txd_data;
    
 //------------------------------//
 reg            r_over_1;
 reg   [7:0]    r_addr;
 always @(posedge clk or negedge rst_n)
 begin
  if(!rst_n)
   begin
     r_addr <= 0;
         r_over_1 <= 0;
    end
  else if(rover)
    begin
     r_addr <= rdata; 
         r_over_1 <= 1;
    end
    else if(txd_en)
       r_over_1 <= 0;
  end
    
 reg      r_over_1_1;
 reg    r_over_1_2;
 reg    r_over_1_3;
 always @(posedge clk or negedge rst_n)
 begin
  if(!rst_n)
   begin
      {r_over_1_3,r_over_1_2,r_over_1_1} <= 3'b000;
    end
  else 
    begin
      {r_over_1_3,r_over_1_2,r_over_1_1} <= {r_over_1_2,r_over_1_1,r_over_1}; 
    end
  end
 assign  txd_en =     ~r_over_1_3&r_over_1_2;
     
    
    data_rom data_rom_1(
                         .addr(r_addr),
                                             .data(txd_data)
                                             );
endmodule
View Code

   rom程序: 

/********************************Copyright**************************************                           
**----------------------------File information--------------------------
** File name  :data_rom.v  
** CreateDate :2015.04
** Funtions   : 简单 rom 文件
** Operate on :M5C06N3L114C7
** Copyright  :All rights reserved. 
** Version    :V1.0
**---------------------------Modify the file information----------------
** Modified by   :
** Modified data :        
** Modify Content:
*******************************************************************************/
 

 module  data_rom  (
              addr,
                            
                            data

             );
 input   [7:0]       addr;
 output  [7:0]       data;
 
// always @(*)
//    begin
//         case()
//         end
 assign data = addr + 1;

endmodule


 
View Code

发送程序:

    /********************************Copyright**************************************                           
    **----------------------------File information--------------------------
    ** File name  :spi_slave_txd.v  
    ** CreateDate :2015.04
    ** Funtions   :FPGA作为从机的发送数据程序
    ** Operate on :M5C06N3L114C7
    ** Copyright  :All rights reserved. 
    ** Version    :V1.0
    **---------------------------Modify the file information----------------
    ** Modified by   :
    ** Modified data :        
    ** Modify Content:
    *******************************************************************************/
 
  module  spi_slave_txd  (
               clk,
               rst_n,
               
                         txd_en,
                         txd_data,
                         
                         spi_cs,
                         spi_sck,
                         spi_mosi,
                         spi_miso,
                         
                         spi_over,
                         txd_over
                 );
     input          clk;
     input          rst_n;
     
     input          txd_en;
     input  [7:0]   txd_data;
     input          spi_cs;
     input          spi_sck;
     input          spi_mosi;
     output   reg   spi_miso;
     
     output         spi_over;
     output   reg   txd_over;
     
     //----------------------------------
      reg        spi_cs_0,spi_cs_1;
        reg        spi_sck_0,spi_sck_1;
        wire       spi_sck_neg;
        wire       spi_over;
        wire       spi_cs_flag;
        always @(posedge clk or negedge rst_n)
         begin
          if(!rst_n)
           begin
             {spi_cs_1,spi_cs_0} <= 2'b11;
                 {spi_sck_1,spi_sck_0} <= 2'b00;
            end
          else 
            begin
             {spi_cs_1,spi_cs_0} <=  {spi_cs_0,spi_cs};
                 {spi_sck_1,spi_sck_0} <= {spi_sck_0,spi_sck};
            end
          end
        
        assign spi_cs_flag = spi_cs_1;
        assign spi_sck_neg = (spi_sck_1&(~spi_sck_0));
        assign spi_over  =     ~spi_cs_1&spi_cs_0;
     //---------------------------------------------//
     localparam   idel = 2'd0;
     localparam   txd_sta= 2'd2;
     localparam   txd_data_sta = 2'd1;
     reg     [1:0]  state;
     reg     [3:0]  cnt;
     reg     [7:0]  txdata;
     
    always @(posedge clk or negedge rst_n)
     begin
      if(!rst_n)
       begin
         state <= idel;
             cnt <= 0;
         txdata <= 0;
             spi_miso <= 1;   /* 拉高 */
      end
      else 
        begin
          case(state)
                 idel:
                   begin
                         cnt <= 0;
               txdata <= 0;
                         spi_miso <= 1;   /* 拉高 */
                         if(txd_en)     
                          begin
                                 state <= txd_data_sta;
                                 
                                end
                            else
                                begin
                                    state <= idel;    
                                end                                    
                      end
                    txd_data_sta:
                      begin
                                txdata <= txd_data;
                                state <= txd_sta;    
                                txd_over <=0;
                            end
                 txd_sta:
                     begin
                        if(spi_cs_flag )     
                             state <= idel;
                        else if(cnt == 8)
                          begin
                                cnt <= 0;    
                                txd_over <= 1;
                                state <= txd_data_sta;
                                end
                         else
                                begin
                                     if(spi_sck_neg)
                                         begin
                                             spi_miso <= txdata[7-cnt[2:0]] ; /* 先高位再低位传输 */
                                             cnt <= cnt +1;    /* 范围:0-8 */
                                            end
                                        else 
                                            begin
                                                spi_miso <= spi_miso; /* 保持 */
                                              cnt <= cnt;
                                            end
                                    end                                                     
                          end
                default:state <= idel;
             endcase    
        end
      end        
    
    endmodule
    
View Code

  仿真程序:

 
    /********************************Copyright**************************************                           
    **----------------------------File information--------------------------
    ** File name  :spi_slave_tb.v  
    ** CreateDate :2015.04
    ** Funtions   :测试文件
    ** Operate on :M5C06N3L114C7
    ** Copyright  :All rights reserved. 
    ** Version    :V1.0
    **---------------------------Modify the file information----------------
    ** Modified by   :
    ** Modified data :        
    ** Modify Content:
    *******************************************************************************/
  
    `timescale 1 ns/1 ns
    
    module  spi_slave_tb ;
       reg          clk;
         reg          rst_n;
         
         reg           spi_cs;
         reg           spi_sck;
         wire          spi_miso;
         reg           spi_mosi;
         
         wire          spi_over;
    
    spi_slave spi_slave_1(
                                 .clk,
                                 .rst_n,
                                 
                                 .spi_cs,
                                 .spi_sck,
                                 .spi_miso,
                                 .spi_mosi,
                                 
                                 .spi_over
                                     );

     parameter tck = 24;
     parameter t = 1000/tck;
     
     always 
       #(t/2) clk = ~clk;
    
     
     //-------------------------------
  /* 模仿spi主机的发送程序,这个task很好,仿顺序操作,可以直观的显示过程 */
    task  spi_sd;
    input [7:0]  data_in;
    begin
    #(5*t);
        
        spi_sck = 0; spi_mosi= data_in[7]; #(5*t);    spi_sck = 1; #(5*t);    //send bit[7]
        spi_sck = 0; spi_mosi= data_in[6]; #(5*t);    spi_sck = 1; #(5*t);    //send bit[6]
        spi_sck = 0; spi_mosi= data_in[5]; #(5*t);    spi_sck = 1; #(5*t);    //send bit[5]
        spi_sck = 0; spi_mosi= data_in[4]; #(5*t);    spi_sck = 1; #(5*t);    //send bit[4]
        spi_sck = 0; spi_mosi= data_in[3]; #(5*t);    spi_sck = 1; #(5*t);    //send bit[3]
        spi_sck = 0; spi_mosi= data_in[2]; #(5*t);    spi_sck = 1; #(5*t);    //send bit[2]
        spi_sck = 0; spi_mosi= data_in[1]; #(5*t);    spi_sck = 1; #(5*t);    //send bit[1]
        spi_sck = 0; spi_mosi= data_in[0]; #(5*t);    spi_sck = 1; #(5*t);    //send bit[0]
      spi_sck = 0;
             
        end
    endtask
    
    
    initial 
      begin
       clk = 0;
         rst_n = 0;
         spi_cs = 1;
         spi_sck = 0;
       spi_mosi = 0;
         
         #(20*t) rst_n = 1; 
         #(10*t);
          spi_cs = 0;
          spi_sd(8'h01);
          #(50*t);
          spi_sd(8'h04);
            #(50*t);
          spi_sd(8'h00);
          #(50*t);
          spi_cs = 1;
      end
    
    
    
    
    endmodule
    
    
  
View Code

  仿真图:

    

  

posted @ 2015-04-15 14:02  远航路上ing  阅读(6976)  评论(2编辑  收藏  举报