본문 바로가기

Logic Design

SPI Interface.

 

 

SPI Interface.

`timescale 1 ns / 1 ps module task_spi ( // SPI master in out ports input i_spi_miso , output o_spi_clk , output o_spi_csn , output o_spi_wex , output o_spi_mosi ); localparam SPI_PER = 300; reg spi_clk; reg spi_csn; reg spi_wex; reg spi_mosi; reg [7:0] sp

onlyiknowabout.tistory.com

 

write protocol, address 8'h01

 

write protocol, address 8'h07

 

load last bit at posedge sclk

 

 

`timescale 1 ns / 1 ps

module task_spi   (
   // SPI master in out ports 
   input       i_spi_miso  ,
   output      o_spi_clk   ,
   output      o_spi_csn   ,
   output      o_spi_wex   ,
   output      o_spi_mosi  
);
   localparam  SPI_PER = 300;
   
   reg         spi_clk;
   reg         spi_csn;
   reg         spi_wex;
   reg         spi_mosi;
   reg   [7:0] spi_data;
   reg   [7:0] spi_rdata;
   
   integer  count;

////////////////////////////////////SPI_TASK////////////////////////////////////
   initial begin                                                              
      spi_clk = 1'b1; spi_csn = 1'b1; spi_wex = 1'bx; // spi initial status
                                                                                    
      repeat(3) #(SPI_PER); // waveform delay : 900ns                                     
      SPI_WR_ADDR(8'h07);                                                                 
      SPI_WR_DATA(8'hff);                                                                 
      SPI_WR_DATA(8'hff);
      SPI_WR_DATA(8'hff);
      SPI_WR_DATA(8'hff);
      SPI_WR_DATA(8'h09);
      SPI_WR_DATA(8'hff);
      SPI_WR_DATA(8'hff);
      SPI_WR_DATA(8'hff);
      SPI_WR_DATA(8'hff);
      SPI_WR_DATA(8'd122);
      SPI_WR_DEND(8'd200);                                                                      
      repeat(4) #(SPI_PER);
      SPI_RD_ADDR(8'h07);
      SPI_RD_DUMM;
      SPI_RD_DATA;
      SPI_RD_DATA;
      SPI_RD_DATA;
      SPI_RD_DATA;
      SPI_RD_DATA;
      SPI_RD_DATA;
      SPI_RD_DATA;
      SPI_RD_DATA;
      SPI_RD_DATA;
      SPI_RD_DATA;
      SPI_RD_DEND;
      repeat(4) #(SPI_PER); // waveform end delay                                      
   end                                                                        
////////////////////////////////////////////////////////////////////////////////
   
   task automatic SPI_WR_ADDR;
      input [7:0] spi_addr;
      begin
         spi_clk = 1'b1; spi_csn = 1'b1; spi_wex = 1'bx;
         #(SPI_PER);
         spi_clk = 1'b0; spi_csn = 1'b0; spi_wex = 1'b0;
         for (count=7; count>=0; count=count-1) begin
            spi_mosi = spi_addr[7-count];
            #(SPI_PER/2) spi_clk = 1'b1;
            #(SPI_PER/2) spi_clk = 1'b0;
         end
         spi_data = spi_addr;
      end
   endtask  
   
   task automatic SPI_WR_DATA;
      input [7:0] spi_wdata;
      begin
         for (count=7; count>=0; count=count-1) begin
            spi_mosi = spi_wdata[7-count];
            #(SPI_PER/2) spi_clk = 1'b1;
            #(SPI_PER/2) spi_clk = 1'b0;
         end
         spi_data = spi_wdata;
      end
   endtask        
   
   task automatic SPI_WR_DEND;
      input [7:0] spi_wdata;
      begin
         for (count=7; count>=0; count=count-1) begin
            spi_mosi = spi_wdata[7-count];
            #(SPI_PER/2) spi_clk = 1'b1;
            #(SPI_PER/2) spi_clk = 1'b0;
         end
         spi_data = spi_wdata;
         spi_clk = 1'b1; spi_csn = 1'b1; spi_wex = 1'bx; count = 'bx; spi_mosi = 1'bx;
      end
   endtask
   
   task automatic SPI_RD_ADDR;
      input [7:0] spi_addr;
      begin
         spi_clk = 1'b1; spi_csn = 1'b1; spi_wex = 1'bx;
         #(SPI_PER);
         spi_clk = 1'b0; spi_csn = 1'b0; spi_wex = 1'b1;
         for (count=7; count>=0; count=count-1) begin
            spi_mosi = spi_addr[7-count];
            #(SPI_PER/2) spi_clk = 1'b1;
            #(SPI_PER/2) spi_clk = 1'b0;
         end
         spi_data = spi_addr; spi_mosi = 1'bx;
      end
   endtask
   
   task automatic SPI_RD_DUMM;
      begin
         for (count=7; count>=0; count=count-1) begin
            spi_rdata[7-count] = i_spi_miso;
            #(SPI_PER/2) spi_clk = 1'b1;
//          spi_rdata[7-count] = i_spi_miso;
            #(SPI_PER/2) spi_clk = 1'b0;
         end
         spi_data = spi_rdata;   
      end
   endtask
   
   task automatic SPI_RD_DATA;
      begin
         for (count=7; count>=0; count=count-1) begin
            spi_rdata[7-count] = i_spi_miso;
            #(SPI_PER/2) spi_clk = 1'b1;
//          spi_rdata[7-count] = i_spi_miso;
            #(SPI_PER/2) spi_clk = 1'b0;
         end
         spi_data = spi_rdata;
      end
   endtask
   
   task automatic SPI_RD_DEND;
      begin
         for (count=7; count>=0; count=count-1) begin
            spi_rdata[7-count] = i_spi_miso;
            #(SPI_PER/2) spi_clk = 1'b1;
//          spi_rdata[7-count] = i_spi_miso;
            #(SPI_PER/2) spi_clk = 1'b0;
         end
         spi_data = spi_rdata;
         spi_clk = 1'b1; spi_csn = 1'b1; spi_wex = 1'bx; count = 3'bx;
      end
   endtask  
               
   assign o_spi_clk  = spi_clk ;
   assign o_spi_csn  = spi_csn ;
   assign o_spi_wex  = spi_wex ;
   assign o_spi_mosi = spi_mosi;
   
endmodule

 

 

module spi_slave_fsm    (
    // system reset
    input         i_rstb      ,

    // spi slave clock & reset
    input         i_spi_clk   ,
    input         i_spi_csn   ,     
    input         i_spi_wex   ,  
    input         i_spi_mosi  ,  
    output reg    o_spi_miso     

); 
    // define parameter
    localparam IDLE  =  3'd0;
    localparam WR_ADDR  =  3'd1;
    localparam WR_DATA  =  3'd2;
    localparam RD_ADDR  =  3'd3;
    localparam RD_DUMM  =  3'd4;
    localparam RD_DATA  =  3'd5;

    // internal register
    reg     [2:0]   cst, nst, count;
    reg             w_wdata_ena, w_wr_ena;
    reg             w_rdata_ena, w_rd_ena;
    reg     [7:0]   w_spi_waddr;
    reg     [7:0]   w_spi_raddr;

    reg     [7:0]   spi_mem[0:255];
    reg     [7:0]   adr_count;
    reg     [7:0]   r_spi_rdata;

    // internal wire
    wire w_spi_rst = i_spi_csn | ~i_rstb  ;
    wire w_enable  = w_wr_ena  | w_rd_ena ;

    // state transition
    always@(posedge i_spi_clk or posedge w_spi_rst)
        if(w_spi_rst)
            cst <= IDLE;
        else
            cst <= nst;

    // bit counter logic
    always@(posedge i_spi_clk or posedge w_spi_rst)
        if(w_spi_rst)
            count <= 3'd0;
        else if(count == 3'd7)
            count <= 3'd0;
        else    
            count <= count + 3'd1;
    
    // nst logic
    always@* begin
      nst = cst;
      case(cst)
            IDLE    : nst = (~i_spi_csn) ? (~i_spi_wex) ? WR_ADDR : RD_ADDR : IDLE    ;
            WR_ADDR : nst = (count == 3'd0 & w_enable ) ?           WR_DATA : WR_ADDR ;
            WR_DATA : nst = (i_spi_wex ) ?                          IDLE    : WR_DATA ;
            RD_ADDR : nst = (count == 3'd0 & w_enable ) ?           RD_DUMM : RD_ADDR ;
            RD_DUMM : nst = (count == 3'd0 & w_enable ) ?           RD_DATA : RD_DUMM ;
            RD_DATA : nst = (~i_spi_wex) ?                          IDLE    : RD_DATA ;
            default : nst = IDLE ;
        endcase
    end

    // write enable signal
    always@(*) begin
        case(cst)
            WR_ADDR : begin w_wr_ena = 1'b1; w_wdata_ena = 1'b0; end
            WR_DATA : begin w_wr_ena = 1'b1; w_wdata_ena = 1'b1; end
            default : begin w_wr_ena = 1'b0; w_wdata_ena = 1'b0; end
        endcase
    end

    // read enable signal
    always@(*) begin
        case(cst)
            RD_ADDR : begin w_rd_ena = 1'b1; w_rdata_ena = 1'b0; end
            RD_DUMM : begin w_rd_ena = 1'b1; w_rdata_ena = 1'b0; end
            RD_DATA : begin w_rd_ena = 1'b1; w_rdata_ena = 1'b1; end
            default : begin w_rd_ena = 1'b0; w_rdata_ena = 1'b0; end
        endcase
    end

    // write address
    always@ (posedge i_spi_clk or posedge w_spi_rst)
       if(w_spi_rst)
          w_spi_waddr <= 8'hx;
       else
          case(nst)
             WR_ADDR : w_spi_waddr[count] <= i_spi_mosi ;   
             default : w_spi_waddr        <= w_spi_waddr;
          endcase
          
    // read address
    always@ (posedge i_spi_clk or posedge w_spi_rst)
       if(w_spi_rst)
          w_spi_raddr <= 8'hx;
       else
          case(nst)
             RD_ADDR : w_spi_raddr[count] <= i_spi_mosi ;   
             default : w_spi_raddr        <= w_spi_raddr;
          endcase

//////////////////////////REGISTER TABLE//////////////////////////
          
   // address counter
   always@(posedge i_spi_clk or posedge w_spi_rst)
      if(w_spi_rst)
         adr_count <= 8'd0;
      else if(count == 3'd7 & (w_wdata_ena | w_rdata_ena))
         adr_count <= adr_count + 8'd1;

   // mem write 
   always@(posedge i_spi_clk or posedge w_spi_rst)
      if(w_spi_rst)
         spi_mem[adr_count + w_spi_waddr][count] <= 1'bx;
      else if(~i_spi_wex)
         spi_mem[adr_count + w_spi_waddr][count] <= i_spi_mosi;
         
   // mem read
   always@(posedge i_spi_clk or posedge w_spi_rst)
      if(w_spi_rst)
         o_spi_miso <= 1'bx;
      else if(i_spi_wex)
         o_spi_miso <= spi_mem[adr_count + w_spi_raddr][count];
         
//////////////////////////////////////////////////////////////////
         
endmodule

 

`timescale 1 ns / 1 ps

module tb_spi_slave_fsm ();

   wire           w_spi_clk      ;
   wire           w_spi_csn      ;
   wire           w_spi_wex      ;
   wire           w_spi_mosi     ;
   
   reg            r_rstb         ;  
   wire           w_spi_miso     ;
   
   task_spi u_task_spi  (
      .i_spi_miso    (w_spi_miso    ),
      .o_spi_clk     (w_spi_clk     ),
      .o_spi_csn     (w_spi_csn     ),
      .o_spi_wex     (w_spi_wex     ),
      .o_spi_mosi    (w_spi_mosi    )
   );
   
   spi_slave_fsm u_spi_slave_fsm (
      .i_rstb        (r_rstb        ),    
      .i_spi_clk     (w_spi_clk     ),
      .i_spi_csn     (w_spi_csn     ),
      .i_spi_wex     (w_spi_wex     ),
      .i_spi_mosi    (w_spi_mosi    ),
      .o_spi_miso    (w_spi_miso    )
   );
   
   initial begin
      r_rstb = 1'b1;
      #100 r_rstb = 1'b0;
      #200 r_rstb = 1'b1;
   end
   
   localparam  IDLE     =  3'd0;
   localparam  WR_ADDR  =  3'd1;
   localparam  WR_DATA  =  3'd2;
   localparam  RD_ADDR  =  3'd3;
   localparam  RD_DUMM  =  3'd4;
   localparam  RD_DATA  =  3'd5;
   
   reg [7*8-1:0] cst;
   always@(u_spi_slave_fsm.cst)begin
      case(u_spi_slave_fsm.cst)
         IDLE     : cst = "IDLE   ";
         WR_ADDR  : cst = "WR_ADDR";
         WR_DATA  : cst = "WR_DATA";
         RD_ADDR  : cst = "RD_ADDR";
         RD_DUMM  : cst = "RD_DUMM";
         RD_DATA  : cst = "RD_DATA";
         default  : cst = "IDLE   ";
      endcase
   end
   
   reg [7*8-1:0] nst;
   always@(u_spi_slave_fsm.nst)begin
      case(u_spi_slave_fsm.nst)
         IDLE     : nst = "IDLE   ";
         WR_ADDR  : nst = "WR_ADDR";
         WR_DATA  : nst = "WR_DATA";
         RD_ADDR  : nst = "RD_ADDR";
         RD_DUMM  : nst = "RD_DUMM";
         RD_DATA  : nst = "RD_DATA";
         default  : nst = "IDLE   ";
      endcase
   end
   
endmodule

 

vlib work
vlog tb_spi_slave_fsm.v spi_slave_fsm.v register_table.v task_spi.v
vsim work.tb_spi_slave_fsm
view wave

add wave -divider TASK_SPI

add wave -radix uns   /w_spi_clk 
add wave -radix uns   /w_spi_csn 
add wave -radix uns   /w_spi_wex 
add wave -radix uns   /w_spi_mosi
add wave -radix uns   /w_spi_miso   
add wave -radix dec   /u_task_spi/count
add wave -radix hex   /u_task_spi/spi_data

add wave -divider SPI_SLAVE_FSM

add wave -radix uns   /u_spi_slave_fsm/w_spi_rst
add wave -radix uns   /u_spi_slave_fsm/i_spi_clk
add wave -radix uns   /u_spi_slave_fsm/i_spi_csn
add wave -radix uns   /u_spi_slave_fsm/i_spi_wex
add wave -radix uns   /u_spi_slave_fsm/cst
add wave -radix ASCII /cst
add wave -radix uns   /u_spi_slave_fsm/count
add wave -radix uns   /u_spi_slave_fsm/w_spi_waddr
add wave -radix uns   /u_spi_slave_fsm/w_spi_raddr

add wave -group enable -radix uns /u_spi_slave_fsm/w_wr_ena
add wave -group enable -radix uns /u_spi_slave_fsm/w_wdata_ena
add wave -group enable -radix uns /u_spi_slave_fsm/w_rd_ena
add wave -group enable -radix uns /u_spi_slave_fsm/w_rdata_ena

add wave -radix uns   /u_spi_slave_fsm/r_spi_rdata

add wave -divider REGISTER_TABLE

add wave -radix uns   /u_spi_slave_fsm/count
add wave -radix uns   /u_spi_slave_fsm/adr_count
add wave -radix uns   /u_spi_slave_fsm/spi_mem


run 66000ns

 

spi write / read protocol

'Logic Design' 카테고리의 다른 글

Pulse Generator.  (0) 2024.03.24
sys_sync_gen  (0) 2024.03.01
Image processing filter design.  (0) 2024.02.09
MIPI specification.  (0) 2024.02.01
Linux.  (0) 2024.01.24