SPI通信实验---verilog(FPGA作为从机,使用可读可写)

   本实验讲究实用性,故设计思想为:主机先向从机发送地址,若是向从机写入数据,则向从机发送数据,若是读取从机数据,则向从机发送时钟,然后在时钟下降沿读取数据即可。cs信号上升沿作为SPI通信的结束信号。rom程序只是做测试使用。

 每次发送16个时钟信号,前八个是地址和命令,后八个是数据。其中:前8个时钟接受的数据的最高位决定着这次通信是读取数据还是写入数据,最高位为1,则是读取数据,为0则是写入数据。

 

  程序:  

 
/********************************Copyright**************************************                           
**----------------------------File information--------------------------
** File name  :spi_slave_2.v  
** CreateDate :2015.004
** 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_2  (
                                 clk,
                                 rst_n,
                       
                                 spi_cs,
                                 spi_sck,
                                 spi_miso,
                                 spi_mosi,
                                 
                                 spi_over
                                 
                                 
                                 
                  );
     input          clk;
     input          rst_n;
     
     input          spi_cs;
     input          spi_sck;
     input          spi_mosi;
     
     output   reg      spi_miso;
     output         spi_over;
     
     //-----------------------------//
     reg        spi_cs_2,spi_cs_1;
     reg        spi_sck_2,spi_sck_1;
     reg        spi_mosi_2,spi_mosi_1;
     wire       spi_cs_pos;
     wire       spi_cs_flag;
     wire       spi_sck_neg;
     wire       spi_sck_pos;
     wire       spi_mosi_flag;
     always @(posedge clk or negedge rst_n)
     begin
      if(!rst_n)
       begin
        {spi_cs_2,spi_cs_1} <= 2'b11;
        {spi_sck_2,spi_sck_1} <= 2'b00;
            {spi_mosi_2,spi_mosi_1} <= 2'b00;
         end
      else 
        begin
         {spi_cs_2,spi_cs_1} <= {spi_cs_1,spi_cs};
         {spi_sck_2,spi_sck_1} <= {spi_sck_1,spi_sck};
             {spi_mosi_2,spi_mosi_1} <= {spi_mosi_1,spi_mosi}; 
        end
      end
        
        assign spi_cs_pos = ~spi_cs_2 &spi_cs_1;
        assign spi_cs_flag = spi_cs_2;
        assign spi_sck_neg = ~spi_sck_1&spi_sck_2;
        assign spi_sck_pos = ~spi_sck_2&spi_sck_1; 
        assign spi_mosi_flag = spi_mosi_2;
         
        assign spi_over = spi_cs_pos;
    //----------------------------------------//
     localparam idel       = 4'd0;
     localparam rxd_addr   = 4'd1;
     localparam jude_wr_rd = 4'd2;
     localparam rxd_data   = 4'd3;
     localparam rxd_over   = 4'd4;
     localparam txd_data   = 4'd5;
     localparam txd_over   = 4'd6;
     localparam end_sta    = 4'd7;
     
     reg    [3:0]     state;
     reg    [3:0]     cnt;
     reg    [7:0]     raddr;
     reg    [7:0]     rdata;
     reg    [7:0]     tdata;
     reg              rover_flag;
     reg              wover_flag;
     reg              rd_flag;
     wire  [7:0]      data_out;
     always @(posedge clk or negedge rst_n)
     begin
      if(!rst_n)
       begin
          state <= 4'd0;
                cnt <= 0;
                raddr <= 8'd0;
                rdata <= 8'd0;
                tdata <= 8'd0;
                rover_flag <= 0;
                wover_flag <= 0;
                rd_flag <= 0;
                spi_miso <= 1;
        end
      else if(!spi_cs_flag)
        begin
          case(state)
                 idel:
                   begin
                            state <= rxd_addr;
                            cnt <= 0;
                            raddr <= 8'd0;
                            rdata <= 8'd0;
                            tdata <= 8'd0; 
                            rover_flag <= 0;
                      wover_flag <= 0;
                            rd_flag <= 0;
                            spi_miso <= 1;
                         end
                    rxd_addr:
                      begin
                            if(cnt == 8)
                             begin
                               cnt <= 0;
                               state <= jude_wr_rd;
                                end
                            else if(spi_sck_pos)
                                begin
                                    cnt <= cnt + 1;
                                    raddr[7 - cnt[2:0]] <= spi_mosi_flag;
                                end
                         end
                jude_wr_rd:
                   begin
                            if(raddr[7] == 1)
                               state <= rxd_data;
                            else
                                 begin
                                   state <= txd_data;
                                     rd_flag <= 1;
                                    end
                         end
            rxd_data:
                begin
                        if(cnt == 8)
                             begin
                               cnt <= 0;
                               state <= rxd_over;
                                end
                         else if(spi_sck_pos)
                                begin
                                    cnt <= cnt + 1;
                                    rdata[7 - cnt[2:0]] <= spi_mosi_flag;
                                end                                
                     end
            rxd_over:
              begin
                     rover_flag <= 1;    
                     state <= end_sta;
                    end
            txd_data:
              begin
                    tdata <= data_out;
                    if(cnt == 8)
                         begin
                              cnt <= 0;
                              state <= txd_over;
                            end
                    else if(spi_sck_pos)
                        begin
                                cnt <= cnt + 1;
                                spi_miso <= tdata[7 - cnt[2:0]];
                            end                        
                 end
            txd_over:
              begin
                    wover_flag <= 1;    
                     state <= end_sta;    
                    end
            end_sta:
               begin
                     rover_flag <= 0;    
                     wover_flag <= 0;    
                     state <= end_sta;                              
                 end
            default:state <= 4'd0;
         endcase      
     end
    else 
            begin
            state <= 4'd0;
                cnt <= 0;
                raddr <= 8'd0;
                rdata <= 8'd0;
                tdata <= 8'd0;
                rover_flag <= 0;
                wover_flag <= 0;
                rd_flag <= 0;
                spi_miso <= 1;    
            end
 end
    
    
    
    data_rom  data_rom_1 (
                                 .clk(clk),
                                 .rst_n(rst_n),
                                 
                                 .wr(rover_flag),
                                 .rd(rd_flag),
                                 
                                 .addr(raddr[6:0]),
                                 .data_in(rdata),
                                 .data_out(data_out)
                                     );
    endmodule
    
View Code

 ROM:

  

 
/********************************Copyright**************************************                           
**----------------------------File information--------------------------
** File name  :data_rom.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_rom  (
               clk,
               rst_n,
               
                         wr,
                         rd,
                         
                         addr,
                         data_in,
                         data_out
                 );
     input          clk;
     input          rst_n;
     
     input          wr;
     input          rd;
     input  [6:0]   addr;
     input  [7:0]   data_in;
     
     output reg [7:0]   data_out;
     
     reg  [7:0]  table_1   [7:0];
     wire [7:0]  table_2   [7:0];
     always @(posedge clk or negedge rst_n)
     begin
      if(!rst_n)
       begin
          table_1[7] <= 0;
                table_1[6] <= 0;
                table_1[5] <= 0;
                table_1[4] <= 0;
                table_1[3] <= 0;
                table_1[2] <= 0;
                table_1[1] <= 0;
                table_1[0] <= 0;
                data_out <= 0;
        end
      else if(wr)
        begin
          table_1[addr] <= data_in;
        end
        else if(rd)
           data_out <= table_1[addr];
        else 
            begin
          table_1[7] <= table_1[7];
                table_1[6] <= table_1[6];
                table_1[5] <= table_1[5];
                table_1[4] <= table_1[4];
                table_1[3] <= table_1[3];
                table_1[2] <= table_1[2];
                table_1[1] <= table_1[1];
                table_1[0] <= table_1[0];
                data_out <= data_out;
        end 
      end
        
    assign table_2[7] = table_1[7];    
    assign table_2[6] = table_1[6];    
    assign table_2[5] = table_1[5];    
    assign table_2[4] = table_1[4];    
    assign table_2[3] = table_1[3];    
    assign table_2[2] = table_1[2];    
    assign table_2[1] = table_1[1];    
    assign table_2[0] = table_1[0];    
    
    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_2  spi_slave_2_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 = 1;
         
         #(20*t) rst_n = 1; 
         #(10*t);
          spi_cs = 0;
          spi_sd(8'h81);
          #(50*t);
          spi_sd(8'h04);
            #(50*t);
             #(50*t);
          spi_cs = 1;
            
            #(20*t);
          spi_cs = 0;                
           spi_sd(8'h01);
            #(50*t);
           spi_sd(8'h00);
          #(50*t);
          spi_cs = 1;
      end
    
    
    
    
    endmodule
    
    
  
View Code

 

 仿真图:

 图中可以看出,第一次输入8‘h81,意味着向01的地址写入数据。第二个数8’h04,则是要写入的数据。然后写入数据8‘h01,则意味着要读取01地址的数据,然后发送8个时钟则是再读取数据。

     

posted @ 2015-04-15 21:11  远航路上ing  阅读(14439)  评论(13编辑  收藏  举报