赞
踩
芯片引脚图:
内部结构图:
存储区域总共分成了32块,每块64KB。每块又分成了16个部分,每个部分4KB。方便进行读取和局部操作。
电路设计
SPI的四种模式
这里使用这个模式:
主机和从机在时钟上升沿放入要输出的数据,在时钟下降沿读取要输入的数据。
8个时钟后交换一个字节8位数据(高位在前)。
有个输入时序的要求 开始时CS拉低等待(tSLCH要求最小5ns)再开始, 结束时CS拉高等待(tSHSL用100ns )再进行下一次操作。
这个寄存器的第一位数据可以判断操作是否完成(BUSY位)
写使能:开始时CS拉低等待(tSLCH要求最小5ns)再开始,结束时CS拉高等待(tSHSL取100ns )再进行下一次操作。
整片擦除,要判断操作是否完成
数据写多了会把之前的数据覆盖掉,要判断操作是否完成。
要判断操作是否完成
对信号进行同步和提前准备:
100m时钟和clk_cnt配合进行数据的读取和输出(clk_cnt有等于1和0的时候)
spi_clk基于100m时钟输出一个相当于clk_cnt的延时半个周期的时钟,确保输入输出数据稳定。
module spi_drive( input clk_100m , input sys_rst_n , //user interface input spi_start ,//spi开启使能。 input [7:0 ] spi_cmd ,//FLAH操作指令 input [23:0] spi_addr ,//FLASH地址 input [7:0 ] spi_data ,//FLASH写入的数据 input [3:0 ] cmd_cnt , output idel_flag_r ,//空闲状态标志的上升沿 output reg w_data_req ,//FLASH写数据请求 output reg [7:0] r_data ,//FLASH读出的数据 output reg erro_flag ,//读出的数据错误标志 //spi interface output reg spi_cs ,//SPI从机的片选信号,低电平有效。 output reg spi_clk ,//主从机之间的数据同步时钟。 output reg spi_mosi ,//数据引脚,主机输出,从机输入。 input spi_miso //数据引脚,主机输入,从机输出。 ); //状态机 parameter IDLE =4'd0;//空闲状态 parameter WEL =4'd1;//写使能状态 parameter S_ERA =4'd2;//扇区擦除状态 parameter C_ERA =4'd3;//全局擦除 parameter READ =4'd4;//读状态 parameter WRITE =4'd5;//写状态 parameter R_STA_REG =4'd6; //指令集 parameter WEL_CMD =8'h06; parameter S_ERA_CMD =8'h20; parameter C_ERA_CMD =8'hc7; parameter READ_CMD =8'h03; parameter WRITE_CMD =8'h02; parameter R_STA_REG_CMD=8'h05; //wire define wire idel_flag; //reg define reg[3:0] current_state ; reg[3:0] next_state ; reg[7:0 ] data_buffer ; reg[7:0 ] cmd_buffer ; reg[7:0 ] sta_reg ; reg[23:0] addr_buffer ; reg[31:0] bit_cnt ; reg clk_cnt ; reg dely_cnt ; reg[31:0] dely_state_cnt ; reg[7:0 ] rd_data_buffer ; reg spi_clk0 ; reg stdone ; reg[7:0 ] data_check ; reg idel_flag0 ; reg idel_flag1 ; //***************************************************** //** main code //***************************************************** //*抓取上升沿 assign idel_flag=(current_state==IDLE)?1:0;//空闲状态标志 assign idel_flag_r=idel_flag0&&(~idel_flag1);//空闲状态标志的上升沿 //*抓取上升沿要用的 always @(posedge clk_100m or negedge sys_rst_n )begin if(!sys_rst_n)begin idel_flag0<=1'b1; idel_flag1<=1'b1; end else begin idel_flag0<=idel_flag; idel_flag1<=idel_flag0; end end //请求数据 + 把数据放入buffer always @(posedge clk_100m or negedge sys_rst_n )begin if(!sys_rst_n) w_data_req<=1'b0; else if((bit_cnt+2)%8==0&&bit_cnt>=30&&clk_cnt==0&¤t_state==WRITE) //提前2个时钟周期 w_data_req<=1'b1; else w_data_req<=1'b0; end always @(posedge clk_100m or negedge sys_rst_n )begin//读出的数据移位寄存 if(!sys_rst_n) rd_data_buffer<=8'd0; else if(bit_cnt>=32&&bit_cnt<=2080&&clk_cnt==0&¤t_state==READ) rd_data_buffer<={rd_data_buffer[6:0],spi_miso}; else rd_data_buffer<=rd_data_buffer; end always @(posedge clk_100m or negedge sys_rst_n )begin//检查读出的数据是否正确 if(!sys_rst_n) data_check<=8'd0; else if(bit_cnt%8==0&&bit_cnt>=40&&clk_cnt==1&¤t_state==READ) data_check<=data_check+1'd1; else data_check<=data_check; end always @(posedge clk_100m or negedge sys_rst_n )begin//读出的数据 if(!sys_rst_n) r_data<=8'd0; else if(bit_cnt%8==0&&bit_cnt>38&&clk_cnt==1&¤t_state==READ) r_data<=rd_data_buffer; else r_data<=r_data; end always @(posedge clk_100m or negedge sys_rst_n )begin//读出的数据错误标志 if(!sys_rst_n) erro_flag<=1'd0; else if(bit_cnt>32&&bit_cnt<=2080&¤t_state==READ&&cmd_cnt==6)begin if(data_check!=r_data) erro_flag<=1'd1; else erro_flag<=erro_flag; end else erro_flag<=erro_flag; end //*把数据放入buffer 提前一个周期 always @(posedge clk_100m or negedge sys_rst_n )begin if(!sys_rst_n) data_buffer<=8'd0; else if((bit_cnt+1)%8==0&&bit_cnt>30&&clk_cnt==1)//*把数据放入buffer 提前一个周期 data_buffer<=spi_data; else if(clk_cnt==1&¤t_state==WRITE&&bit_cnt>=32) data_buffer<={data_buffer[6:0],data_buffer[7]}; else data_buffer<=data_buffer; end //*----位移cmd指令存储器 开始:cs选中且dely未生效,提前了100mhz的周期------------ //使50mhz时数据提前半个周期获得 always @(posedge clk_100m or negedge sys_rst_n )begin if(!sys_rst_n) cmd_buffer<=8'd0; else if(spi_cs==0&&dely_cnt==0) cmd_buffer<=spi_cmd; else if(clk_cnt==1&&(current_state==WEL||current_state==S_ERA||current_state==C_ERA ||current_state==READ||current_state==WRITE||current_state==R_STA_REG)&&bit_cnt<8) cmd_buffer<={cmd_buffer[6:0],1'b1}; else cmd_buffer<=cmd_buffer; end //取出地址每一位 always @(posedge clk_100m or negedge sys_rst_n )begin if(!sys_rst_n) addr_buffer<=8'd0; else if(spi_cs==0&&dely_cnt==0) addr_buffer<=spi_addr; else if(clk_cnt==1&&(current_state==READ||current_state==WRITE)&&bit_cnt>=8&&bit_cnt<32) addr_buffer<={addr_buffer[22:0],addr_buffer[23]}; else addr_buffer<=addr_buffer; end //------------使能后clk_cnt输出50M时钟用于操作信号-------------- always @(posedge clk_100m or negedge sys_rst_n )begin if(!sys_rst_n) clk_cnt<=1'd0; else if(dely_cnt==1) clk_cnt<=clk_cnt+1'd1; else clk_cnt<=1'd0; end //*---------cs选中器件后的信号输出的 dely_cnt 可以认为是使能操作------------ always @(posedge clk_100m or negedge sys_rst_n )begin if(!sys_rst_n) dely_cnt<=1'd0; else if(spi_cs==0)begin if(dely_cnt<1) dely_cnt<=dely_cnt+1'd1; else dely_cnt<=dely_cnt; end else dely_cnt<=1'd0; end //*-----------------结束的延时计时器------------------------------------ always @(posedge clk_100m or negedge sys_rst_n )begin if(!sys_rst_n) dely_state_cnt<=1'd0; else if(spi_cs)begin if(dely_state_cnt<400000000) dely_state_cnt<=dely_state_cnt+1'd1; else dely_state_cnt<=dely_state_cnt; end else dely_state_cnt<=1'd0; end //*-------------------------bit读写计数--------------------- always @(posedge clk_100m or negedge sys_rst_n )begin if(!sys_rst_n) bit_cnt<=11'd0; else if(dely_cnt==1)begin if(clk_cnt==1'b1) bit_cnt<=bit_cnt+1'd1; else bit_cnt<=bit_cnt; end else bit_cnt<=11'd0; end
状态机 :每个状态该干什么,怎么转移
修改里面的命令和转态就可以移植到其他的地方了。
//三段式状态机 always @(posedge clk_100m or negedge sys_rst_n )begin if(!sys_rst_n) current_state<=IDLE; else current_state<=next_state; end always @(*)begin case(current_state) IDLE: begin if(spi_start&&spi_cmd==WEL_CMD) next_state=WEL; else if(spi_start&&spi_cmd==C_ERA_CMD) next_state=C_ERA; else if(spi_start&&spi_cmd==S_ERA_CMD) next_state=S_ERA; else if(spi_start&&spi_cmd==READ_CMD) next_state=READ; else if(spi_start&&spi_cmd==WRITE_CMD) next_state=WRITE; else if(spi_start&&spi_cmd==R_STA_REG_CMD) next_state=R_STA_REG; else next_state=IDLE; end WEL: begin if(stdone&&bit_cnt>=8) next_state=IDLE; else next_state=WEL; end S_ERA: begin if(stdone) next_state=IDLE; else next_state=S_ERA; end C_ERA: begin if(stdone) next_state=IDLE; else next_state=C_ERA; end READ: begin if(stdone&&bit_cnt>=8) next_state=IDLE; else next_state=READ; end WRITE: begin if(stdone&&bit_cnt>=8) next_state=IDLE; else next_state=WRITE; end R_STA_REG: begin if(stdone) next_state=IDLE; else next_state=R_STA_REG; end default: next_state=IDLE; endcase end always @(posedge clk_100m or negedge sys_rst_n )begin if(!sys_rst_n) begin spi_cs<=1'b1; spi_clk<=1'b0; spi_clk0<=1'b0; spi_mosi<=1'b0; stdone<=1'b0; end else begin case(current_state) IDLE: begin spi_cs<=1'b1; spi_clk<=1'b0; spi_mosi<=1'b0; end WEL: begin stdone<=1'b0; spi_cs<=1'b0; if(dely_cnt==1&&bit_cnt<8) begin spi_clk0<=~spi_clk0; spi_clk<=spi_clk0; spi_mosi<=cmd_buffer[7]; end else if(bit_cnt==8&&clk_cnt==0)begin stdone<=1'b1; spi_clk<=1'b0; spi_mosi<=1'b0; end else if(bit_cnt==8&&clk_cnt==1)begin spi_cs<=1'b1; end end C_ERA: begin stdone<=1'b0; if(dely_state_cnt==10) spi_cs<=1'b0; else if(dely_cnt==1&&bit_cnt<8) begin spi_clk0<=~spi_clk0; spi_clk<=spi_clk0; spi_mosi<=cmd_buffer[7]; end else if(bit_cnt==8&&clk_cnt==0)begin stdone<=1'b1; spi_clk<=1'b0; spi_mosi<=1'b0; end else if(bit_cnt==8&&clk_cnt==1)begin spi_cs<=1'b1; end end S_ERA: begin stdone<=1'b0; if(dely_state_cnt==10) spi_cs<=1'b0; else if(dely_cnt==1&&bit_cnt<8) begin spi_clk0<=~spi_clk0; spi_clk<=spi_clk0; spi_mosi<=cmd_buffer[7]; end else if(bit_cnt>=8&&bit_cnt<32&&spi_cs==0)begin spi_cs<=1'b0; spi_clk0<=~spi_clk0; spi_clk<=spi_clk0; spi_mosi<=addr_buffer[23]; end else if(bit_cnt==32&&clk_cnt==0) begin spi_cs<=1'b1; spi_clk<=1'b0; spi_mosi<=1'b0; stdone<=1'b1; end end READ: begin stdone<=1'b0; if(dely_state_cnt==10) spi_cs<=1'b0; else if(dely_cnt==1&&bit_cnt<8) begin spi_clk0<=~spi_clk0; spi_clk<=spi_clk0; spi_mosi<=cmd_buffer[7]; end else if(bit_cnt>=8&&bit_cnt<32&&spi_cs==0)begin spi_clk0<=~spi_clk0; spi_clk<=spi_clk0; spi_mosi<=addr_buffer[23]; end else if(bit_cnt>=32&&bit_cnt<2080)begin spi_clk0<=~spi_clk0; spi_clk<=spi_clk0; spi_mosi<=1'b0; end else if(bit_cnt==2080&&clk_cnt==0) begin spi_clk<=1'b0; spi_mosi<=1'b0; stdone<=1'b1; end else if(bit_cnt==2080&&clk_cnt==1) begin spi_cs<=1'b1; end end WRITE: begin stdone<=1'b0; if(dely_state_cnt==10) spi_cs<=1'b0; else if(dely_cnt==1&&bit_cnt<8) begin spi_clk0<=~spi_clk0; spi_clk<=spi_clk0; spi_mosi<=cmd_buffer[7]; end else if(bit_cnt>=8&&bit_cnt<32&&spi_cs==0)begin spi_clk0<=~spi_clk0; spi_clk<=spi_clk0; spi_mosi<=addr_buffer[23]; end else if(bit_cnt>=32&&bit_cnt<2080)begin spi_clk0<=~spi_clk0; spi_clk<=spi_clk0; spi_mosi<=data_buffer[7]; end else if(bit_cnt==2080&&clk_cnt==0) begin spi_clk<=1'b0; spi_mosi<=1'b0; stdone<=1'b1; end else if(bit_cnt==2080&&clk_cnt==1) begin spi_cs<=1'b1; end end R_STA_REG:begin stdone<=1'b0; if(dely_state_cnt==10) spi_cs<=1'b0; else if(dely_cnt==1&&bit_cnt<8)begin spi_clk0<=~spi_clk0; spi_clk<=spi_clk0; spi_mosi<=cmd_buffer[7]; end else if(bit_cnt==8)begin spi_clk0<=~spi_clk0; spi_clk<=spi_clk0; spi_mosi<=1'b0; end else if(~spi_miso&&bit_cnt%8==0)begin spi_clk<=1'b0; spi_cs<=1'b1; stdone<=1'b1; end else if(~spi_cs&&dely_cnt==1)begin spi_clk0<=~spi_clk0; spi_clk<=spi_clk0; end end default: begin stdone<=1'b0; spi_cs<=1'b1; spi_clk<=1'b0; spi_clk0<=1'b0; spi_mosi<=1'b0; end endcase end end endmodule
首先系统开始运行,来几个周期延伸。
spi_start信号只是一个周期脉冲。
idel_flag_r是进入空闲状态的标志位也就是意味着上一步操作完成。
cmd计数指令不断加来切换不同的命令。
spi_cmd 输出命令
module flash_rw( input sys_clk , input sys_rst_n , input idel_flag_r , input w_data_req , output reg[3:0 ] cmd_cnt , output reg spi_start ,//spi开启使能。 output reg[7:0 ] spi_cmd , output reg[7:0 ] spi_data ); //指令集 parameter WEL_CMD =16'h06; parameter S_ERA_CMD =16'h20; parameter C_ERA_CMD =16'hc7; parameter READ_CMD =16'h03; parameter WRITE_CMD =16'h02; parameter R_STA_REG_CMD=8'h05 ; //reg define reg[3:0] flash_start; //SPI 要写入的数据 always @(posedge sys_clk or negedge sys_rst_n )begin if(!sys_rst_n) flash_start<=0; else if(flash_start<=5) flash_start<=flash_start+1; else flash_start<=flash_start; end always @(posedge sys_clk or negedge sys_rst_n )begin if(!sys_rst_n) cmd_cnt<=0; else if(flash_start==4) spi_start<=1'b1; else if(idel_flag_r&&cmd_cnt<10)begin cmd_cnt<=cmd_cnt+1; spi_start<=1'b1; end else begin cmd_cnt<=cmd_cnt; spi_start<=1'b0; end end always @(posedge sys_clk or negedge sys_rst_n )begin if(!sys_rst_n) spi_data<=8'd0; else if(w_data_req) spi_data<=spi_data+1'b1; else spi_data<=spi_data; end always @(*)begin case(cmd_cnt) 0:spi_cmd=WEL_CMD; 1:spi_cmd=C_ERA_CMD; 2:spi_cmd=R_STA_REG_CMD; 3:spi_cmd=WEL_CMD; 4:spi_cmd=WRITE_CMD; 5:spi_cmd=R_STA_REG_CMD; 6:spi_cmd=READ_CMD; 7:spi_cmd=WEL_CMD; 8:spi_cmd=S_ERA_CMD; 9:spi_cmd=R_STA_REG_CMD; 10:spi_cmd=READ_CMD; default:; endcase end endmodule
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。