美文网首页ASIC DV
SPI实现(verilog)

SPI实现(verilog)

作者: sarai_c7eb | 来源:发表于2019-08-16 10:27 被阅读0次

帮朋友写了个比较简单的SPI MASTER 电路,现在公布源代码,以便初学者学习之用;

1. 源代码

module SPI_MASTER(
    input        clk     , //the FPGA input clock
    input        rst_n   , //the FPGA asynchronous reset
                 
    input        spi_sdi , //the SPI read input
    output reg   spi_sdo , //the SPI write output
    output reg   spi_sck , //the SPI clock
    output reg   spi_cs    //the SPI chip selection
);
//---------------------------------------------
// the following localparam need to configure to 
// fit defferent scenarios
//---------------------------------------------
localparam  CFG_REG0   = 32'h0123_4567; //the CFG reg0
localparam  CFG_REG1   = 32'h5555_5555; //the CFG reg0
localparam  CFG_REG2   = 32'hAAAA_AAAA; //the CFG reg0
localparam  CNT        = 16'd31       ; //the frequncy of spi_sck=clk/(CNT+1);
localparam  WR_CTRL    = 3'b111       ; //bit0:cfg_reg0 write enable
                                        //bit1:cfg_reg1 write enable
                                        //bit2:cfg_reg2 write enable
localparam  RD_CTRL    = 3'b111       ; //bit0:cfg_reg0 read enable
                                        //bit1:cfg_reg1 read enable
                                        //bit2:cfg_reg2 read enable

//---------------------------------------------
// the following localparam don't need configuration
//---------------------------------------------
localparam  CNT_DIV2   = (CNT>>1)     ; //the div clock trun around counter 
localparam  SPI_IDLE   = 3'd0         ;
localparam  SPI_TAG    = 3'd1         ;
localparam  SPI_ADDR   = 3'd3         ;
localparam  SPI_WDATA  = 3'd2         ;
localparam  SPI_TURN   = 3'd7         ;
localparam  SPI_RDATA  = 3'd6         ;

//---------------------------------------------
// REG defination
//---------------------------------------------
integer     i                ;
reg  [15:0] clock_cnt        ;
reg         spi_sck_raw      ;
reg  [5:0]  spi_fsm_ctrl     ;
reg  [2:0]  idx              ;

reg  [5:0]  spi_fsm_start_idx;
reg  [3:0]  spi_cur_st       ;
reg  [3:0]  spi_next_st      ;

reg  [4:0]  fns_cnt          ;
reg  [4:0]  fns_cnt_next     ;
reg  [31:0] spi_sdo_raw0     ;
reg  [31:0] spi_sdo_raw1     ;
reg  [31:0] rback_reg0       ;
reg  [31:0] rback_reg1       ;
reg  [31:0] rback_reg2       ;

//---------------------------------------------
// WIRE defination
//---------------------------------------------
wire        ck_rise          ;
wire        ck_fall          ;
wire        ck_jmp           ;
wire        spi_fsm_start    ;

wire        spi_fsm_start    ;
wire        addr_fns         ;
wire        wdata_fns        ;
wire        rdata_fns        ;
wire        trun_fns         ;

//---------------------------------------------
// MAIN FUNCTION
//---------------------------------------------
always@(posedge clk or negedge rst_n) begin
    if(rst_n==1'b0)
        clock_cnt<=16'b0;
    else if(spi_next_st!=SPI_IDLE) begin
        if(clock_cnt>=CNT)
            clock_cnt<=16'b0;
        else
            clock_cnt<=clock_cnt+1'b1;`
    end
    else 
       clock_cnt<=16'b0;
end

//spi clock keep 0 when there is no traction.
assign ck_rise=clock_cnt==CNT      ;
assign ck_fall=clock_cnt==CNT_DIV2 ;
assign ck_jmp =ck_rise | ck_fall   ;
always@(posedge clk or negedge rst_n) begin
    if(rst_n==1'b0)
        spi_sck<=1'b0;
    else if(spi_cur_st==SPI_IDLE && spi_next_st!=SPI_IDLE || ck_jmp)
        spi_sck<=~spi_sck;
    else if(spi_cur_st!=SPI_IDLE && spi_next_st==SPI_IDLE)
        spi_sck<=1'b0;
end

//chip selection keep 0 when there is no traction.
always@(posedge clk or negedge rst_n) begin
    if(rst_n==1'b0)
        spi_cs<=1'b0;
    else if(spi_next_st==SPI_IDLE)
        spi_cs<=1'b0;
    else
        spi_cs<=1'b1;
end

//the FSM ctrl:ctrl the SPI_FSM to run;
always@(posedge clk or negedge rst_n) begin
    if(rst_n==1'b0) begin
        spi_fsm_ctrl<={READ_CTRL,WRITE_CTRL};
        idx         <=3'b0;
    end
    else if(spi_fsm_ctrl!=6'b0) begin
        for(i=5;i>=0;i=i-1) begin
            if(spi_fsm_ctrl[i])
                idx<=i;
        end
        if(spi_fsm_done) begin
            spi_fsm_ctrl<=spi_fsm_ctrl & (~spi_fsm_start_idx);
        end
    end
end

assign  spi_fsm_start=(spi_fsm_ctrl!=6'b0) && (idx!=3'd0);
always@(*) begin
    spi_fsm_start_idx     =6'b0;
    spi_fsm_start_idx[idx]=1'b1;
end

//the FSM;
always@(posedge clk or negedge rst_n) begin
    if(rst_n==1'b0)
        spi_cur_st<=SPI_IDLE;
    else
        spi_cur_st<=spi_next_st;
end
always@(*) begin
    spi_next_st=spi_cur_st;
    case(spi_cur_st)
        SPI_IDLE: if(spi_fsm_start) begin 
                      spi_next_st=SPI_TAG;
                  end
        SPI_TAG : if(ck_rise) begin 
                      spi_next_st=SPI_ADDR;
                  end
        SPI_ADDR: if(ck_rise&&addr_fns) begin 
                      if(spi_fsm_start_idx[2:0]!=3'b0)
                          spi_next_st=SPI_WDATA;
                      else
                          spi_next_st=SPI_TURN;
                  end
        SPI_WDATA:if(ck_rise&&wdata_fns) begin 
                      spi_next_st=SPI_IDLE;
                  end
        SPI_TRUN: if(ck_rise&&turn_fns) begin
                      spi_next_st=SPI_RDATA;
                  end
        SPI_RDATA:if(ck_rise&&rdata_fns) begin
                      spi_next_st=SPI_IDLE;
                  end
    endcase
end

always@(*) begin
    fns_cnt_next=fns_cnt;
    if(spi_cur_st!=spi_next_st)
        fns_cnt_next=5'd0;
    else if(spi_next_st!=IDLE && ck_rise)
        fns_cnt_next=fns_cnt_next+1'b1;
end

always@(posedge clk or negedge rst_n) begin
    if(rst_n==1'b0)
        fns_cnt<=5'd0;
    else 
        fns_cnt<=fns_cnt_next;
end
assign addr_fns =spi_cur_st==SPI_ADDR  & fns_cnt==5'd7 ;
assign wdata_fns=spi_cur_st==SPI_WDATA & fns_cnt==5'd31;
assign rdata_fns=spi_cur_st==SPI_RDATA & fns_cnt==5'd31;
assign turn_fns =spi_cur_st==SPI_TRUN  & fns_cnt==5'd1 ;

always@(*) begin
    case(spi_fsm_start_idx[2:0])
        3'b010 : spi_sdo_raw0=CFG_REG1;
        3'b100 : spi_sdo_raw0=CFG_REG2;
        default: spi_sdo_raw0=CFG_REG0;
    endcase
    spi_sdo_raw1=spi_sdo_raw0<<fns_cnt_next;
end

always@(posedge clk or negedge rst_n) begin
    if(rst_n==1'b0)
        spi_sdo<=1'd0;
    else if(spi_next_st=SPI_WDATA)
        spi_sdo<=spi_sdo_raw1[31];
    else
        fns_sdo<=1'b0;
end
//store the read back data to 3 regs;
always@(posedge clk or negedge rst_n) begin
    if(rst_n==1'b0) begin
        rback_reg0<=32'd0;
        rback_reg0<=32'd0;
        rback_reg0<=32'd0;
    end
    else if(spi_cur_st=SPI_RDATA && ck_fall) begin
        if(spi_fsm_start_idx[5:3]==3'b001)
            rback_reg0<={rback_reg0[30:0],spi_sdi};
        if(spi_fsm_start_idx[5:3]==3'b010)
            rback_reg1<={rback_reg1[30:0],spi_sdi};
        if(spi_fsm_start_idx[5:3]==3'b100)
            rback_reg2<={rback_reg2[30:0],spi_sdi};
    end
end

endmodule

2.使用方法

使用前需要更改以下CODE:

//---------------------------------------------
// the following localparam need to configure to 
// fit defferent scenarios
//---------------------------------------------
localparam  CFG_REG0   = 32'h0123_4567; //the CFG reg0
localparam  CFG_REG1   = 32'h5555_5555; //the CFG reg0
localparam  CFG_REG2   = 32'hAAAA_AAAA; //the CFG reg0
localparam  CNT        = 16'd31       ; //the frequncy of spi_sck=clk/(CNT+1);
localparam  WR_CTRL    = 3'b111       ; //bit0:cfg_reg0 write enable
                                        //bit1:cfg_reg1 write enable
                                        //bit2:cfg_reg2 write enable
localparam  RD_CTRL    = 3'b111       ; //bit0:cfg_reg0 read enable
                                        //bit1:cfg_reg1 read enable
                                        //bit2:cfg_reg2 read enable

  • CNT为SPI传输时钟相对模块工作时钟的分频系数;
  • CFG_REG0/CFG_REG1/CFG_REG2为要写入slave中3个32bit寄存器的内容;
  • WR_CTRL/RD_CTRL控制写入哪些寄存器和读出哪些寄存器;
  • 默认的流程为先写后读。

小结

可用其他软件接口代替localparam定义;

相关文章

  • SPI实现(verilog)

    帮朋友写了个比较简单的SPI MASTER 电路,现在公布源代码,以便初学者学习之用; 1. 源代码 2.使用方法...

  • Verilog实现SPI协议

    关于SPI的教程有很多,这里写下自己学习SPI协议后的总结。 什么是SPI? SPI是Serial Periphe...

  • SPI框架学习

    SPI框架实现之旅 SPI框架实现之旅一:背景介绍 SPI框架实现之旅二:整体设计 SPI框架实现之旅三:实现说明...

  • java SPI入门详解

    Java SPI机制详解 1、什么是SPI? ​ 如上图所示,接口对应的抽象SPI接口;实现方实现SP...

  • Java - SPI

    SPI简介 如何使用SPI 应用举例1. 组织方制定接口2. 实现方根据SPI规范实现接口3. 组织方加载实现类 ...

  • SPI原理解析一

    SPI(service provider Interfaces),对于Java中的实现SPI类ServiceLoa...

  • Java SPI 机制解析

    本文以JDBC为例深入讲解 java spi 机制,将帮助你理解:什么是SPI,SPI实现原理,SPI的使用和SP...

  • Java中的SPI机制及接口多实现调用

    Java中的SPI机制及接口多实现调用 0x00 SPI机制 SPI 全称为 (Service Provider ...

  • 第2章 JDK SPI 的设计与实现

    SPI 机制是实现可扩展性的一种方式。Dubbo SPI 是从 JDK SPI(Service Provider ...

  • Dubbo之SPI

    SPI SPI全称Serice Provider Interface,是一种服务发现的实现机制。SPI的本质是将接...

网友评论

    本文标题:SPI实现(verilog)

    本文链接:https://www.haomeiwen.com/subject/vqtfsctx.html