spi flash通用读写软IP


spi flash通用读写模块,有两个模块,分别为spiflash控制模块和spi控制模块

spiflash控制模块RTL代码如下:

//功能描述
//这是一个spiflash的控制程序
//写选择(wr)和读选择(rd)一样时为空操作
//写选择(wr)为1并且读选择(rd)为0时使用写模式,写模式下有数据命令的选择
//写选择(wr)为0并且读选择(rd)为1时使用读模式
//命令和数据的输入都是使用data_in
//地址的输入是使用addr
//目前能使用的只有写入8位的命令(通过data_in),写入数据(通过addr和data_in),读出8位数据(通过addr和data_out),时钟上升沿
//使用时不用检测忙位,模块会自动进行检测
//当完成读或者写时信号spifl_over会出现上升沿 
//DO、Dio、cs、spi_clk_out对应spiflash的端口
module spiflash_common(flash_clk,sys_rst,DO,wr,rs,rd,addr,data_in,data_out,cs,Dio,spiflash_over,spi_clk_out);
input  			flash_clk;//flash的时钟
input  			sys_rst;//复位
input  			DO;//flash数据的串行输出口
input  			wr;//写选择,为1时有效,为0时无效
input  			rs;//读选择,为1时有效,为0时无效
input  			rd;//数据命令选择,为1时为数据,为0时为命令
input   [23:0]  addr;
input   [7:0]   data_in;
output  [7:0]   data_out;
output			cs;
output 			Dio;
output 			spiflash_over;//操作完成信号(上升沿)
output 			spi_clk_out;

reg [7:0] data_out_r;
reg 	  cs_r;
reg		  spiflash_over_r;   

assign data_out = data_out_r;

parameter disenable			= 8'b00000001;//状态机编码,使用独热玛,这是不使能状态
parameter open_wel 	 		= 8'b00000010;//打开写使能状态
parameter write_com 	 	= 8'b00000100;//写命令状态
parameter chip_program		= 8'b00001000;//芯片编程状态,就是写数据
parameter done       		= 8'b00010000;//完成状态
parameter close_wel 	 	= 8'b00100000;//关闭写使能状态
parameter read_data			= 8'b01000000;//读数据状态
parameter busy_check		= 8'b10000000;//检测忙位状态

reg [7:0] current_state/*synthesis noprune*/;
reg [7:0] next_state;

reg [1:0] check_busy_flag;
wire 	  busy;

initial 
begin
	current_state = 8'd0;
	next_state = 8'd0;
	check_busy_flag = 2'd0;
end

wire input_check;

assign input_check = wr ^ rd;//检测是否会读写冲突

always@(posedge flash_clk or negedge sys_rst)//状态转移
begin
	if(~sys_rst)
		current_state <= disenable;
	else if(input_check)
		current_state <= next_state;
	else
		current_state <= disenable;
end

reg [39:0] spi_data_out_r;
reg [1:0]  mode_r;

always@(negedge flash_clk)					//状态机
begin
	case(current_state)
		disenable:
			begin
				cs_r <= 1; 					//关闭片选
				spiflash_over_r <= 0;		
				if(input_check)				//检测是否读写冲突
				begin
					if(wr)								
					begin
						next_state <= busy_check;
						check_busy_flag <= 2'b00;//传到忙检测状态的标识码,以便检测到不忙时跳转到相应的状态
					end
					else
						next_state <= read_data;
				end
				else
					next_state <= disenable;
			end
		open_wel:
			begin
				mode_r <= 2'b01;				//传到spi控制器的模式选择,具体看spi控制器模块
				spi_data_out_r <= 40'h0000000006;//传到spi控制器的数据
				if(spi_over)					//如果spi返回操作已经完成
				begin
					cs_r <= 1;
					if(rs)
						next_state <= chip_program;
					else
						next_state <= write_com;
				end
				else
				begin
					cs_r <= 0;
					next_state <= open_wel;
				end
			end
		write_com: 
			begin
				mode_r <= 2'b01;
				spi_data_out_r <= {32'd0,data_in};
				check_busy_flag <= 2'b01;			//传到忙检测状态的标识码,以便检测到不忙时跳转到相应的状态
				if(spi_over)
				begin
					cs_r <= 1;
					next_state <= busy_check;
				end
				else
				begin
					cs_r <= 0;
					next_state <= write_com;
				end
			end
		chip_program:
			begin
				mode_r <= 2'b11;
				spi_data_out_r <= {2'h02,addr,data_in};
				check_busy_flag <= 2'b10;
				if(spi_over)
				begin
					cs_r <= 1;
					next_state <= busy_check;
			end
				else
				begin
					cs_r <= 0;
					next_state <= chip_program;
				end
			end
		close_wel:
			begin
				spi_data_out_r <= 40'h0000000004;
				mode_r <= 2'b01;
				if(spi_over)
				begin
					cs_r <= 1;
					next_state <= done;
					spiflash_over_r <= 1;				//操作已经完成,将spiflash_over拉高
				end
				else
				begin
					cs_r <= 0;
					next_state <= close_wel;
				end
			end
		read_data:
			begin
				spi_data_out_r <= {8'h03,addr,8'h00};
				mode_r <= 2'b00;
				if(spi_over)
				begin
					cs_r <= 1;
					data_out_r <= state_reg;
					next_state <= done;
					spiflash_over_r <= 1;				//操作已经完成,将spiflash_over拉高
				end
				else
				begin
					cs_r <= 0;
					next_state <= read_data;
				end
			end
		busy_check:
			begin
				if(spi_over)
				begin
					cs_r <= 1;
					if(busy)
						next_state <= busy_check;
					else begin
						if(check_busy_flag == 2'b00)			//根据不同的标识码跳转的不同的状态
							next_state <= open_wel;
						else if(check_busy_flag == 2'b10)			//根据不同的标识码跳转的不同的状态
							next_state <= close_wel;
						else 
						begin
							next_state <= done;
							spiflash_over_r <= 1;				//操作已经完成,将spiflash_over拉高
						end
					end
				end
				else
				begin
					cs_r <= 0;
					mode_r <= 2'b10; 
					spi_data_out_r <= 40'h0000000500; 
					next_state <= busy_check;
				end
			end
		done:next_state <= disenable;						//跳转到非使能状态,在这个状态中保持spiflash_over的高电平,否则如果直接跳转到非使能状态会立刻拉低spiflash_over,造成错误
		default: next_state <= disenable;
	endcase
end

wire [1:0] 	mode;
wire [39:0] spi_data_out;
wire 		spi_over;
wire [7:0]	state_reg;

spi_controller u1
(
	.spi_clk_in		(flash_clk),
	.mode  			(mode),
	.data			(spi_data_out),	
	.cs				(cs),
	.miso			(DO),
	.state_reg		(state_reg),
	.mosi			(mosi),
	.spi_over		(spi_over),
	.spi_clk_out 	(spi_clk_out)
);

assign Dio 			= mosi;
assign cs   		= cs_r;
assign spi_data_out = spi_data_out_r;
assign mode 		= mode_r; 
assign spiflash_over= spiflash_over_r;
assign busy 		= state_reg[0];

endmodule

spi控制模块代码如下:

module spi_controller(spi_clk_in,mode,data,cs,miso,state_reg,mosi,spi_over,spi_clk_out);
input		 spi_clk_in;//spi时钟的输入
input [1:0]	 mode;//模式选择,有四个模式,00位读数据模式{8位读指令,24位地址,8位数据输出},01为输入8位命令,10为{8位读寄存器命令,8位寄存器数据输出},11为写数据模式{8位写命令,24位地址,8位数据输入}}
input [39:0] data;//由上面的模式选择可以的到输入最多为40位的数据,输入数据用40位的位宽,如果用不到40位则只需用低位,如16位只用到40位的低16位
input		 cs;//输入的片选端
input 		 miso;//flash的数据串行输出口
output [7:0] state_reg;//状态寄存器的值
output 		 mosi;//flash的数据串行输入端
output		 spi_over;//执行完成时标识信号,为0时为完成为1 时表示已经完成
output 		 spi_clk_out;//经过处理的spi时钟输出

reg [5:0] spi_bit_num;
reg       spi_clk_out_flag;

reg [5:0] spi_bit_counter;
reg spi_over_r;

always@(posedge spi_clk_in)
begin 
	case(mode)																				//对spi_bit_counter进行判断,如果已经操作完成,则给一个信号用于控制spi_clk_out,
		2'b00:begin spi_bit_num <= 41; spi_clk_out_flag <= (spi_bit_counter >= 40)?1:0; end//00模式,在40位的基础上加上一个周期给cs拉低到开始操作,和一个周期给停止操作到cs拉高
		2'b01:begin spi_bit_num <= 9;  spi_clk_out_flag <= (spi_bit_counter >= 8)?1:0; end//01模式,同上
		2'b10:begin spi_bit_num <= 17; spi_clk_out_flag <= (spi_bit_counter >= 16)?1:0; end//10模式,同上
		2'b11:begin spi_bit_num <= 41; spi_clk_out_flag <= (spi_bit_counter >= 40)?1:0; end//11模式,同上
		default:begin spi_bit_num <= 0; spi_clk_out_flag <= 0; end
	endcase
end

always@(posedge spi_clk_in)
begin
	if(cs) 
	begin						//如果没有被选中则清零
		spi_bit_counter <= 0;
		spi_over_r <= 0;
	end
	else if(spi_over)			//如果操作结束对进度计数器清零
	begin
		spi_bit_counter <= 0;
	end
	else if(spi_bit_counter == spi_bit_num)//用于判断操作是否结束
	begin
		spi_bit_counter <= spi_bit_num;
		spi_over_r <= 1;
	end
	else
	begin
		spi_bit_counter <= spi_bit_counter + 1;//操作未结束时进度计数器自加
		spi_over_r <= 0;
	end
end

assign spi_clk_out = ((spi_bit_counter == 0) || (spi_clk_out_flag == 1)) ? 1 : spi_clk_in;//如果进度计数器是在0或者最大值,就是最小值和最大值,则时钟一直为1,否则就是spi时钟

reg [39:0] data_r;
reg mosi_r;

always@(negedge spi_clk_out)				//数据串行输出
begin
	if(spi_over)
		mosi_r <= 0;
	else
	case(mode)								//不同模式只取40位数据相应的位宽
		2'b00:mosi_r 	<= data_r[39];
		2'b01:mosi_r 	<= data_r[7];
		2'b10:mosi_r 	<= data_r[15];
		2'b11:mosi_r 	<= data_r[39];
		default:mosi_r <= 0;
	endcase
end

always@(posedge spi_clk_in)
begin
	if(spi_bit_counter == 0)
		data_r <= data;			//在进度为0时置数,将数据锁存进来
	else 
		data_r <= data_r << 1;//随后开始右移,以满足总是取最高位给flash(上头的数据串行输出),简化设计
end

reg [7:0] state_reg_r;

always@(posedge spi_clk_out)
begin
	if((mode == 2'b10 && spi_bit_counter >= 9 && ~spi_over)||(mode == 2'b00 && spi_bit_counter >=33 && ~spi_over))//如果进度到了数据输出时开始更新寄存器数据
		state_reg_r <= {state_reg_r[6:0],miso};
end

assign state_reg = (spi_over)?state_reg_r:0;//操作完成时将数据输出
assign mosi = mosi_r;
assign spi_over = spi_over_r;

endmodule


 
  


你可能感兴趣的:(spi flash通用读写软IP)