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控制模块代码如下:
<pre name="code" class="plain">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
转载请注明原文地址: https://ju.6miu.com/read-1125985.html