spi flash通用读写软IP

    xiaoxiao2022-06-30  53

    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

    最新回复(0)