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