qspi驱动优化 - minichao9901/personal-writes GitHub Wiki

特点

  • 大位宽
  • 硬件管理cs(目前看不太好,cs还是应该要交给上层来管理比较合适)
  • 建议改进一下,放弃cs引脚的硬件管理,简化底层驱动设计。

代码

`timescale 1ns / 1ps

module qspi
#(
    parameter BURST_LENGTH=192
)
(
    input clk,
    input rst_n,
    
    (*mark_debug="true"*)input xfer_start,
    (*mark_debug="true"*)output xfer_end,
    (*mark_debug="true"*)output reg xfer_final,
    (*mark_debug="true"*)output xfer_rdy,           
    
    (*mark_debug="true"*)input [7:0] cmd,
    (*mark_debug="true"*)input [23:0] addr,
    (*mark_debug="true"*)input [0: BURST_LENGTH-1] din, 
    (*mark_debug="true"*)input [6:0]  current_vadid_length,  /* Max suppport 127*8=1016 */   
    (*mark_debug="true"*)input [17:0] total_length, 
    (*mark_debug="true"*)input [1:0] data_mode,  /*00: cmd, 01: cmd+addr, 10: cmd+addr+1wire data, 11: cmd+addr+4wire data*/ 
    
    (*mark_debug="true"*)output reg csx,
    (*mark_debug="true"*)output reg sck,
    (*mark_debug="true"*)output reg [3:0] dout
    );
    
parameter IDLE=2'b00;
parameter SEND_CMD=2'b01;
parameter SEND_DATA=2'b10;
parameter SEND_WAIT=2'b11;

reg [1:0] state;
reg [6:0] lsm_cnt;
reg [17:0] xfer_total_cnt;
reg [6:0] xfer_valid_cnt;
reg xfer_end_full;

always @(posedge clk or negedge rst_n)
if(rst_n==0)
    state<=IDLE;
else case(state)
    IDLE: if(xfer_start) 
            state<=SEND_CMD;
    SEND_CMD: if(xfer_end_full && xfer_final)
                state<=IDLE;
              else if(xfer_end_full && ~xfer_final)
                state<=SEND_DATA;
    SEND_DATA: if(xfer_end_full && xfer_final)
                state<=IDLE;
               else if(xfer_end_full && ~xfer_final)
                state<=SEND_WAIT;
    SEND_WAIT: if(xfer_start)
                state<=SEND_DATA;
    default: state<=IDLE;
endcase

always @(posedge clk or negedge rst_n)
if(rst_n==0)
    xfer_total_cnt<=18'd0;
else if(state==IDLE && xfer_start)
    xfer_total_cnt<=total_length;
else if(state==SEND_DATA && xfer_end_full)    
//else if(state==SEND_DATA && xfer_end_full && ~xfer_final)
    xfer_total_cnt<=xfer_total_cnt-current_vadid_length;
    
always @(posedge clk or negedge rst_n)
if(rst_n==0)
    xfer_valid_cnt<=7'd0;
else if(state==IDLE && xfer_start                       /* first enter send_data mode from send_cmd mode */
        ||state==SEND_WAIT && xfer_start)               /* then require new data in wait mode */
    xfer_valid_cnt<=current_vadid_length;    
//else if(state==SEND_DATA && lsm_cnt==4-1)
else if(state==SEND_DATA) begin
    if((data_mode==2'b10) && lsm_cnt==16-1       /* 1wire */
        || (data_mode==2'b11) && lsm_cnt==4-1)   /* 4wire */
        xfer_valid_cnt<=xfer_valid_cnt-1;
end
    
always @(posedge clk or negedge rst_n)
if(rst_n==0)
    lsm_cnt<=0;
else if(state==IDLE || state==SEND_WAIT)
    lsm_cnt<=0;  
else if(state==SEND_CMD) begin
    if(xfer_end_full)
        lsm_cnt<=0;
    else
        lsm_cnt<=lsm_cnt+1;
end
else if(state==SEND_DATA) begin
    if((data_mode==2'b10) && lsm_cnt==16-1       /* 1wire */
        || (data_mode==2'b11) && lsm_cnt==4-1)   /* 4wire */
        lsm_cnt<=0;
    else
        lsm_cnt<=lsm_cnt+1;
end

reg [0:7] din_tmp;
always @(*) begin
if(current_vadid_length>=current_vadid_length)
    din_tmp=din[(current_vadid_length-current_vadid_length)*8 +:8];
else
    din_tmp=8'h00;
end
 
always @(posedge clk or negedge rst_n)
if(rst_n==0) begin
    csx<=1;
    sck<=0;
    dout<=4'h0;
end
else case(state)
    IDLE: begin csx<=1; sck=0; end  //begin and final state
    SEND_CMD: begin
        dout[3:1]<=3'b000;            
         if(data_mode==2'b00)
             case(lsm_cnt)
                0:  begin csx<=0;end
                1:  begin dout[0]<=cmd[7];end
                2:  begin sck<=1;end     
                3:  begin dout[0]<=cmd[6]; sck<=0; end
                4:  begin sck<=1;end      
                5:  begin dout[0]<=cmd[5]; sck<=0; end
                6:  begin sck<=1;end     
                7:  begin dout[0]<=cmd[4]; sck<=0; end
                8:  begin sck<=1;end    
                9:  begin dout[0]<=cmd[3]; sck<=0; end
                10: begin sck<=1;end     
                11: begin dout[0]<=cmd[2]; sck<=0; end
                12: begin sck<=1;end      
                13: begin dout[0]<=cmd[1]; sck<=0; end
                14: begin sck<=1;end     
                15: begin dout[0]<=cmd[0]; sck<=0; end
                16: begin sck<=1;end                                                                               
            endcase 
        else if(data_mode!=2'b00)
             case(lsm_cnt)
                0:  begin csx<=0;end
                1:  begin dout[0]<=cmd[7];end
                2:  begin sck<=1;end     
                3:  begin dout[0]<=cmd[6]; sck<=0; end
                4:  begin sck<=1;end      
                5:  begin dout[0]<=cmd[5]; sck<=0; end
                6:  begin sck<=1;end     
                7:  begin dout[0]<=cmd[4]; sck<=0; end
                8:  begin sck<=1;end    
                9:  begin dout[0]<=cmd[3]; sck<=0; end
                10: begin sck<=1;end     
                11: begin dout[0]<=cmd[2]; sck<=0; end
                12: begin sck<=1;end      
                13: begin dout[0]<=cmd[1]; sck<=0; end
                14: begin sck<=1;end     
                15: begin dout[0]<=cmd[0]; sck<=0; end
                16: begin sck<=1;end   
                                  
                17: begin dout[0]<=addr[23]; sck<=0; end
                18: begin sck<=1;end      
                19: begin dout[0]<=addr[22]; sck<=0; end
                20: begin sck<=1;end     
                21: begin dout[0]<=addr[21]; sck<=0; end
                22: begin sck<=1;end    
                23: begin dout[0]<=addr[20]; sck<=0; end
                24: begin sck<=1;end     
                25: begin dout[0]<=addr[19]; sck<=0; end
                26: begin sck<=1;end      
                27: begin dout[0]<=addr[18]; sck<=0; end
                28: begin sck<=1;end     
                29: begin dout[0]<=addr[17]; sck<=0; end    
                30: begin sck<=1;end     
                31: begin dout[0]<=addr[16]; sck<=0; end
                32: begin sck<=1;end      
                33: begin dout[0]<=addr[15]; sck<=0; end
                34: begin sck<=1;end     
                35: begin dout[0]<=addr[14]; sck<=0; end
                36: begin sck<=1;end    
                37: begin dout[0]<=addr[13]; sck<=0; end
                38: begin sck<=1;end     
                39: begin dout[0]<=addr[12]; sck<=0; end
                40: begin sck<=1;end      
                41: begin dout[0]<=addr[11]; sck<=0; end
                42: begin sck<=1;end     
                43: begin dout[0]<=addr[10]; sck<=0; end    
                44: begin sck<=1;end   
                
                45: begin dout[0]<=addr[9]; sck<=0; end
                46: begin sck<=1;end      
                47: begin dout[0]<=addr[8]; sck<=0; end
                48: begin sck<=1;end     
                49: begin dout[0]<=addr[7]; sck<=0; end
                50: begin sck<=1;end    
                51: begin dout[0]<=addr[6]; sck<=0; end
                52: begin sck<=1;end     
                53: begin dout[0]<=addr[5]; sck<=0; end
                54: begin sck<=1;end      
                55: begin dout[0]<=addr[4]; sck<=0; end
                56: begin sck<=1;end     
                57: begin dout[0]<=addr[3]; sck<=0; end    
                58: begin sck<=1;end     
                59: begin dout[0]<=addr[2]; sck<=0; end
                60: begin sck<=1;end      
                61: begin dout[0]<=addr[1]; sck<=0; end
                62: begin sck<=1;end     
                63: begin dout[0]<=addr[0]; sck<=0; end
                64: begin sck<=1;end                                                                                                                    
            endcase  
     end
     
     SEND_DATA: begin
            if(data_mode==2'b10) begin
                 dout[3:1]<=3'b000;             
                 case(lsm_cnt)
                    0:  begin dout[0]<=din_tmp[0]; sck<=0; end
                    1:  begin sck<=1;end     
                    2:  begin dout[0]<=din_tmp[1]; sck<=0; end
                    3:  begin sck<=1;end      
                    4:  begin dout[0]<=din_tmp[2]; sck<=0; end
                    5:  begin sck<=1;end     
                    6:  begin dout[0]<=din_tmp[3]; sck<=0; end
                    7:  begin sck<=1;end    
                    8:  begin dout[0]<=din_tmp[4]; sck<=0; end
                    9:  begin sck<=1;end     
                    10: begin dout[0]<=din_tmp[5]; sck<=0; end
                    11: begin sck<=1;end      
                    12: begin dout[0]<=din_tmp[6]; sck<=0; end
                    13: begin sck<=1;end     
                    14: begin dout[0]<=din_tmp[7]; sck<=0; end
                    15: begin sck<=1;end  
                endcase  
           end  
           else if(data_mode==2'b11)
                 case(lsm_cnt)
                    0:  begin dout[3:0]<=din_tmp[0:3]; sck<=0; end
                    1:  begin sck<=1;end     
                    2:  begin dout[3:0]<=din_tmp[4:7]; sck<=0; end
                    3:  begin sck<=1;end       
                endcase                                                      
     end   
     SEND_WAIT: sck<=0;
        
endcase

//xfer_end_full要单独产生,因为它只占一个aclk脉宽,与其它信号不一样
always @(posedge clk or negedge rst_n)
if(rst_n==0)
    xfer_end_full<=0;
else if(state==SEND_CMD && (data_mode==2'b00) && lsm_cnt==17)
    xfer_end_full<=1;
else if(state==SEND_CMD && (data_mode!=2'b00) && lsm_cnt==65)
    xfer_end_full<=1; 
else if(state==SEND_DATA &&  (data_mode==2'b10) && lsm_cnt==16-1 && xfer_valid_cnt==1)
    xfer_end_full<=1;
else if(state==SEND_DATA &&  (data_mode==2'b11) && lsm_cnt==4-1 && xfer_valid_cnt==1)
    xfer_end_full<=1;  
else
    xfer_end_full<=0; 

assign xfer_end=(state==SEND_DATA) && xfer_end_full;    
    
always @(posedge clk or negedge rst_n)
if(rst_n==0)
    xfer_final<=0;
else if(state==SEND_CMD && (data_mode==2'b00) && lsm_cnt==17)   //cmd only 
    xfer_final<=1;
else if(state==SEND_CMD && (data_mode==2'b01) && lsm_cnt==65)   //cmd+addr
    xfer_final<=1;    
else if(state==SEND_DATA &&  (data_mode==2'b10) && lsm_cnt==16-1 && xfer_valid_cnt==1 && xfer_total_cnt<=current_vadid_length)  //data use 1wire
    xfer_final<=1;
else if(state==SEND_DATA &&  (data_mode==2'b11) && lsm_cnt==4-1 && xfer_valid_cnt==1 && xfer_total_cnt<=current_vadid_length)   //data use 4wire
    xfer_final<=1;    
else
    xfer_final<=0; 
    
//always @(posedge clk or negedge rst_n)
//if(rst_n==0)
//    xfer_rdy<=1;
//else if(state==IDLE && xfer_start)
//    xfer_rdy<=0;
//else if(state==SEND_CMD && xfer_end_full && xfer_final)
//    xfer_rdy<=1; 
//else if(state==SEND_DATA && xfer_end_full && xfer_final)
//    xfer_rdy<=1;   

assign xfer_rdy=(state==IDLE)? 1:0;         
    
endmodule

testbench

module tb;
    reg clk, rst_n;
    initial clk=0;
    always #10 clk=~clk;
    initial begin
        rst_n=0;
        #1000;
        rst_n=1;
    end 

    reg xfer_start;
    reg [7:0] cmd;
    reg [23:0] addr;
    reg [191:0] din; 
    reg [6:0]  current_vadid_length;
    reg [17:0] total_length;
    reg [1:0] data_mode;
    wire [3:0] dout;    
    
    initial begin
        xfer_start=0;
        cmd=8'h00;
        addr=24'hf0000f;
        din=8'h00;
        current_vadid_length=0;
        total_length=0;
        data_mode=2'b00;
        
        wait(rst_n==1);
        #1000;
        
        //send command only
        current_vadid_length=0;
        total_length=0;
        data_mode=2'b00;     
        cmd=8'hf0;          
        @(posedge clk) #1 xfer_start=1;
        @(posedge clk) #1 xfer_start=0; 
        wait(xfer_final==1);
        #100;
  
        //send command+addr
        current_vadid_length=3;
        total_length=3;
        data_mode=2'b01; 
        cmd=8'hf0; 
        @(posedge clk) #1 xfer_start=1;
        @(posedge clk) #1 xfer_start=0;          
        wait(xfer_final==1);
        #100;
        
        //send command+addr+data, 1wire mode             
        current_vadid_length=3;
        total_length=3;
        data_mode=2'b10; 
        cmd=8'hf0; 
        din={24'hf0f0f0,168'b0}; 
        @(posedge clk) #1 xfer_start=1;
        @(posedge clk) #1 xfer_start=0;          
        wait(xfer_final==1);
        #100;
  
         //send command+addr+data, 4wire mode          
        current_vadid_length=24;
        total_length=40;
        data_mode=2'b10; 
        cmd=8'h23; 
        din={12{16'h1234}}; 
        @(posedge clk) #1 xfer_start=1;
        @(posedge clk) #1 xfer_start=0;          
        wait(xfer_end==1); 
        #100;
        
        current_vadid_length=16;
        din={12{16'h5678}}; 
        @(posedge clk) #1 xfer_start=1;
        @(posedge clk) #1 xfer_start=0;          
        wait(xfer_final==1);  
        #1;                            
    
    end
    
    qspi qspi_inst(
        .clk(clk),
        .rst_n(rst_n),
        
        .xfer_start(xfer_start),
        .xfer_end(xfer_end),
        .xfer_final(xfer_final),
        .xfer_rdy(xfer_rdy),        
        
        .cmd(cmd),
        .addr(addr),
        .din(din),
        .current_vadid_length(current_vadid_length),
        .total_length(total_length),
        .data_mode(data_mode),
        
        .csx(csx),
        .sck(sck),
        .dout(dout)
        );


endmodule