手写一个SPI FLASH 读写擦除控制器

文章目录

  • flash读写数据的特点
  • 1. 扇擦除SE(Sector Erase)
    • 1.1 flash_se 模块设计
      • 1.1.1 信号连接示意图:
      • 1.1.2 SE状态机
      • 1.1.3 波形图设计:
      • 1.1.4 代码
  • 2. 页写PP(Page Program)
    • 2.1 flash_pp模块设计
      • 2.1.1 信号连接示意图:
      • 2.1.2 PP状态机
      • 2.1.3 波形图设计(WE部分和DELAY部分和上述一致)
      • 2.1.4 代码
  • 3.读flash操作
    • 3.1 flash_rd模块设计
      • 3.1.1 信号连接示意图
      • 3.1.2 波形图设计
      • 3.1.3 代码
  • 4.模块整合
    • 4.1 state_ctrl模块设计
      • 4.1.1 信号连接示意图
      • 4.1.2 状态机转换
      • 4.1.3 波形图设计
      • 4.1.4 代码
  • 5.下板操作:

开始之前需要先了解一下SPI通信协议的特点: spi协议

本篇内容要实现FPGA芯片通过SPI总线与flash芯片进行通信,连接如下图所示:
在这里插入图片描述
信号线解释:

  1. Cs_n:片选线,为 FPGA 的输出信号,Flash 的输入信号,低电平有效;
  2. Sck:时钟线,为 FPGA 的输出信号,Flash 的输入信号,Flash 根据该信 号的上升沿锁存 sdi 发来的数据,根据该信号的下降沿通过 sdo 输出数据;
  3. sdi:mosi,数据线,为 FPGA 的输出信号,Flash 的输入信号,FPGA 可通过 sdi总线串行的传输数据到 Flash;
  4. sdo:miso,数据线,为 FPGA 的输入信号,Flash 的输出信号,Flash 可通过 sdo总线串行的传输数据到 FPGA。

flash读写数据的特点

这里以 M25p16 型号的 flash 为例进行介绍。
M25p16 最大的时钟频率为 50Mhz,在此我们进行擦除时采用 12.5Mhz 的时钟,即 sck 的时钟频率为 12.5Mhz。该款 Flash 芯片的存储空间16Mbit,每个地址内存储 1byte(8bit)的数据,共 2M 的存储深度,其地址分为 32 扇区(sector)、每个扇区包含 256 页(page)、每一页包含 256字节(byte),因此该 Flash 芯片需要用到 21 位地址线,加上扩展的 3bit 地址线
一共用到== 24bit 地址线==,具体如下图 3所示。
在这里插入图片描述

1. 扇擦除SE(Sector Erase)

通过前面的项目我们知道,ram存储器的特点是:,在同一个地址写入新的数据时,旧数据会被覆盖。而flash则不同,在对 Flash 进行写入数据时,首先需要将 Flash内部现有的数据进行擦除,Flash 的擦除分为扇区擦除(sector erase)和全擦除(bulk erase)。下面将以扇区擦除为例进行讲解.
在进行 SE 之前需要给出一个写使能指令
(WREN),然后进行扇区擦除,WREN的指令为8’h06。SE时序图如下:
在这里插入图片描述

注意:

  1. 在 WREN 结束后,cs_n 需要被拉高,为了确保 WREN 被 Flash 存储,因此 cs_n 拉高的时间至少需要 100ns(官方文档虽然没写拉高 100ns 但是为了稳定一定要 拉高 cs_n),之后再次拉低 cs_n 为发送 SE 指令做准备。
  2. 由于是扇区擦除,因此给出的地址位只有高 8bit有效,低 16bit 无论是何值都对该扇区的擦除没有影响。

1.1 flash_se 模块设计

1.1.1 信号连接示意图:

在这里插入图片描述

1.1.2 SE状态机

在这里插入图片描述

1.1.3 波形图设计:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.1.4 代码

module  flash_se(input	wire 		sclk,input	wire 		rst,input	wire 		se_flag,input	wire [23:0]	se_addr,output	wire 		sck,output	wire 		cs_n,output	wire 		sdi);parameter	IDLE 	= 4'b0001;
parameter	WE 		= 4'b0010;
parameter	DELAY	= 4'b0100;
parameter	SE 		= 4'b1000;parameter	DIV_END_NUM = 4-1;
parameter 	DELAY_END =16-1;
parameter	WRITE_EN =8'h06;
parameter   SECTOR_E =8'hD8;reg 	[3:0]	state;
reg 	[7:0]	div_cnt;
reg 	[7:0]	bit_cnt;
reg 	[7:0]	delay_cnt;
reg 			cs_n_r;
reg 			sck_r;
reg 			sdi_r;
reg 	[7:0]	we_inst_shift;
reg 	[31:0]	se_inst_shift;assign sck = sck_r;
assign cs_n = cs_n_r;
assign sdi = sdi_r;
always @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginstate <= IDLE;endelse case (state)IDLE : if(se_flag == 1'b1 )state <= WE;WE 	 : if(div_cnt == DIV_END_NUM && bit_cnt == 'd8)state <= DELAY;DELAY: if (delay_cnt == DELAY_END)state <= SE;SE   : if(div_cnt == DIV_END_NUM && bit_cnt == 'd32)state <= IDLE;default : state <= IDLE;endcase
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) begindiv_cnt <= 'd0;endelse if(div_cnt == DIV_END_NUM) begindiv_cnt <='d0;endelse if (state == WE || state == SE) begindiv_cnt <= div_cnt + 1'b1;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginbit_cnt <= 'd0;endelse if (state == WE && bit_cnt == 'd8 && div_cnt == DIV_END_NUM) beginbit_cnt <='d0;endelse if (state == WE && div_cnt == DIV_END_NUM) beginbit_cnt <= bit_cnt + 1'b1;endelse if (state == SE && bit_cnt == 'd32 && div_cnt == DIV_END_NUM) beginbit_cnt <='d0;endelse if (state == SE && div_cnt == DIV_END_NUM) beginbit_cnt <= bit_cnt + 1'b1;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) begindelay_cnt <='d0;endelse if (state == DELAY) begindelay_cnt <= delay_cnt + 1'b1;endelse begindelay_cnt <= 'd0;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) begincs_n_r <= 1'b1;endelse if(state == WE && div_cnt == DIV_END_NUM && bit_cnt == 'd8) begincs_n_r <= 1'b1;endelse if (state == IDLE && se_flag == 1'b1) begincs_n_r <= 1'b0;endelse if(state == SE && div_cnt == DIV_END_NUM && bit_cnt == 'd32) begincs_n_r <= 1'b1;endelse if(state == DELAY && delay_cnt == DELAY_END) begincs_n_r <= 1'b0;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginsck_r <= 1'b0;endelse if (state == WE && div_cnt == (DIV_END_NUM >>1) && bit_cnt != 'd8) beginsck_r <= 1'b1;endelse if (state == WE && div_cnt == (DIV_END_NUM ) && bit_cnt != 'd8) beginsck_r <= 1'b0;endelse if (state == SE && div_cnt == (DIV_END_NUM >>1) && bit_cnt != 'd32) beginsck_r <= 1'b1;endelse if (state == SE && div_cnt == (DIV_END_NUM ) && bit_cnt != 'd32) beginsck_r <= 1'b0;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginwe_inst_shift <= 'd0;endelse if (se_flag == 1'b1) beginwe_inst_shift <= WRITE_EN;endelse if(state == WE && div_cnt == DIV_END_NUM) beginwe_inst_shift <= {we_inst_shift[6:0],1'b0};end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginse_inst_shift <= 'd0;endelse if (se_flag == 1'b1 && state == IDLE) beginse_inst_shift <= {SECTOR_E,se_addr};endelse if (state == SE && div_cnt == DIV_END_NUM) beginse_inst_shift <= {se_inst_shift[30:0],1'b0};end
endalways @*  beginif (state == WE) beginsdi_r <= we_inst_shift[7];endelse if(state == SE) beginsdi_r <= se_inst_shift[31];endelse beginsdi_r <= 1'b0;end
endendmodule

2. 页写PP(Page Program)

Flash 页写功能,简称 PP,Page Program 命令 8’h02。同样与擦除指令一样,需要再发送 PP 指令之前,执行 Write EN 操作,当执行完 PP 后 Write EN 会自动复位,所以每次 PP 之前必须发送 Write EN 操作。PP时序图如下:
在这里插入图片描述

2.1 flash_pp模块设计

说明:这里:这里pp进去的256byte数据是提前存在fifo的,因此开始向flash里pp数据的时候需要产生读fifo使能(rd_en)。

Fifo的位宽设置为8,fifo一次读出8bit数据,而sdi传输1bit数据,所以需要考虑fifo读使能拉高的时机以及读出的数据移位的时机

2.1.1 信号连接示意图:

在这里插入图片描述

2.1.2 PP状态机

在这里插入图片描述

2.1.3 波形图设计(WE部分和DELAY部分和上述一致)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
rd_en和rd_data数据移位实现并转串细节:
在这里插入图片描述

2.1.4 代码

module flash_pp(	input	wire 		sclk,input	wire 		rst,input	wire 		pp_flag,input	wire [23:0]	pp_addr,input	wire 		wr_en,input	wire [7:0]	wr_data,output	wire 		sck,output	wire 		cs_n,output	wire 		sdi);parameter	IDLE 	= 4'b0001;
parameter	WE 		= 4'b0010;
parameter	DELAY	= 4'b0100;
parameter	PP 		= 4'b1000;parameter	DIV_END_NUM = 4-1;
parameter 	DELAY_END =16-1;
parameter	WRITE_EN =8'h06;
parameter   PAGE_P =8'h02;reg 	[3:0]	state;
reg 	[7:0]	div_cnt;
reg 	[11:0]	bit_cnt;
reg 	[7:0]	delay_cnt;
reg 			cs_n_r;
reg 			sck_r;
reg 			sdi_r;
reg 	[7:0]	we_inst_shift;
reg 	[31:0]	pp_inst_shift;
reg 			read_fifo_en;
reg 			pp_shift_flag;
wire 	[7:0]	read_fifo_data;assign sck = sck_r;
assign cs_n = cs_n_r;
assign sdi = sdi_r;
always @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginstate <= IDLE;endelse case (state)IDLE : if(pp_flag == 1'b1 )state <= WE;WE 	 : if(div_cnt == DIV_END_NUM && bit_cnt == 'd8)state <= DELAY;DELAY: if (delay_cnt == DELAY_END)state <= PP;PP   : if(div_cnt == DIV_END_NUM && bit_cnt == 'd2080)state <= IDLE;default : state <= IDLE;endcase
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) begindiv_cnt <= 'd0;endelse if(div_cnt == DIV_END_NUM) begindiv_cnt <='d0;endelse if (state == WE || state == PP) begindiv_cnt <= div_cnt + 1'b1;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginbit_cnt <= 'd0;endelse if (state == WE && bit_cnt == 'd8 && div_cnt == DIV_END_NUM) beginbit_cnt <='d0;endelse if (state == WE && div_cnt == DIV_END_NUM) beginbit_cnt <= bit_cnt + 1'b1;endelse if (state == PP && bit_cnt == 'd2080 && div_cnt == DIV_END_NUM) beginbit_cnt <='d0;endelse if (state == PP && div_cnt == DIV_END_NUM) beginbit_cnt <= bit_cnt + 1'b1;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) begindelay_cnt <='d0;endelse if (state == DELAY) begindelay_cnt <= delay_cnt + 1'b1;endelse begindelay_cnt <= 'd0;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) begincs_n_r <= 1'b1;endelse if(state == WE && div_cnt == DIV_END_NUM && bit_cnt == 'd8) begincs_n_r <= 1'b1;endelse if (state == IDLE && pp_flag == 1'b1) begincs_n_r <= 1'b0;endelse if(state == PP && div_cnt == DIV_END_NUM && bit_cnt == 'd2080) begincs_n_r <= 1'b1;endelse if(state == DELAY && delay_cnt == DELAY_END) begincs_n_r <= 1'b0;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginsck_r <= 1'b0;endelse if (state == WE && div_cnt == (DIV_END_NUM >>1) && bit_cnt != 'd8) beginsck_r <= 1'b1;endelse if (state == WE && div_cnt == (DIV_END_NUM ) && bit_cnt != 'd8) beginsck_r <= 1'b0;endelse if (state == PP && div_cnt == (DIV_END_NUM >>1) && bit_cnt != 'd2080) beginsck_r <= 1'b1;endelse if (state == PP && div_cnt == (DIV_END_NUM ) && bit_cnt != 'd2080) beginsck_r <= 1'b0;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginwe_inst_shift <= 'd0;endelse if (pp_flag == 1'b1) beginwe_inst_shift <= WRITE_EN;endelse if(state == WE && div_cnt == DIV_END_NUM) beginwe_inst_shift <= {we_inst_shift[6:0],1'b0};end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginpp_inst_shift <= 'd0;endelse if (pp_flag == 1'b1 && state == IDLE) beginpp_inst_shift <= {PAGE_P,pp_addr};endelse if (state == PP && div_cnt == DIV_END_NUM && bit_cnt < 'd31) beginpp_inst_shift <= {pp_inst_shift[30:0],1'b0};endelse if (read_fifo_en == 1'b1) beginpp_inst_shift <= {read_fifo_data,24'd0};endelse if (pp_shift_flag == 1'b1 ) beginpp_inst_shift <= {pp_inst_shift[30:24],25'd0};end
endalways @* beginif (bit_cnt >='d31 && bit_cnt <'d2079 && div_cnt == 'd3 && state == PP && bit_cnt[2:0]==3'b111) beginread_fifo_en <= 1'b1;endelse beginread_fifo_en <= 1'b0;end
endalways @* beginif (bit_cnt >='d31 && bit_cnt <'d2079 && div_cnt == 'd3 && state == PP && bit_cnt[2:0]!=3'b111) beginpp_shift_flag <= 1'b1;endelse beginpp_shift_flag <= 1'b0;end
endalways @*  beginif (state == WE) beginsdi_r <= we_inst_shift[7];endelse if(state == PP) beginsdi_r <= pp_inst_shift[31];endelse beginsdi_r <= 1'b0;end
endfifo_1024x8 fifo_1024x8_inst (.clk(sclk), // input clk.din(wr_data), // input [7 : 0] din.wr_en(wr_en), // input wr_en.rd_en(read_fifo_en), // input rd_en.dout(read_fifo_data), // output [7 : 0] dout.full(full), // output full.empty(empty) // output empty
);
endmodule 

3.读flash操作

Read data byte 读数据字节指令,简称 RDB 指令,指令码为 8’h03,读指令可以读取 Flash 地址空间的任何一个byte 数据,不受扇区,页的限制,而且可以连续读取出所有数据。
读数据的必要条件:给出读命令码,然后紧跟 24bit 的起始地址,之后会在 SDO 总线串行输出读出数据,直到 Cs_n 信号拉高中断读出数据操作,否则地址在 flash 内部递增读出数据。
flash_rd操作时序图:
在这里插入图片描述

3.1 flash_rd模块设计

读控制器要能满足以下功能:
给定读起始地址和需要读取的数据长度,控制器自动启动读 flash, 并把读出数据缓存到 fifo 中,以供其他模块读取调用。

3.1.1 信号连接示意图

在这里插入图片描述

3.1.2 波形图设计

sdo是1bit ,而fifo的wr_data为8bit,因此要合理设计sdo的移位标志,与wr_en的标志,实现串转并写入fifo中。
在这里插入图片描述

3.1.3 代码

module flash_rd(input	wire 		sclk,input	wire 		rst,input	wire 		rd_flag,input	wire [23:0]	rd_addr,input	wire [10:0]	rd_len,output	wire 		rd_ready,input	wire		rd_en,output	wire [7:0]	rd_data,output	wire 		sck,output	wire 		cs_n,input	wire 		sdo,output	wire 		sdi);parameter	DIV_END_NUM = 4-1;
parameter	READ_P = 8'h03;reg	[15:0]	bit_cnt_end;
reg 		cs_n_r,sdi_r,sck_r;
reg [7:0]	div_cnt;
reg [15:0]	bit_cnt;
reg [31:0]	rd_inst_shift;
reg 		shift_flag;
reg 		wr_en;
reg [7:0]	sdo_data_shift;
reg 		flash_read_end;
wire 		full,empty;assign sdi = sdi_r;
assign sck = sck_r;
assign cs_n = cs_n_r;always @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginbit_cnt_end <='d0;endelse if (rd_flag == 1'b1) beginbit_cnt_end <= 32 + (rd_len<<3);end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) begincs_n_r <= 1'b1;endelse if (div_cnt == DIV_END_NUM && bit_cnt == bit_cnt_end) begincs_n_r <= 1'b1;endelse if (rd_flag == 1'b1) begincs_n_r <= 1'b0;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) begindiv_cnt <='d0;endelse if(div_cnt == DIV_END_NUM) begindiv_cnt <= 'd0;endelse if (cs_n_r == 1'b0) begindiv_cnt <= div_cnt + 1'b1;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginbit_cnt <='d0;endelse if (cs_n_r == 1'b0 && div_cnt == DIV_END_NUM && bit_cnt == bit_cnt_end) beginbit_cnt <= 'd0;endelse if (cs_n_r == 1'b0 && div_cnt == DIV_END_NUM) beginbit_cnt <= bit_cnt + 1'b1;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginsck_r <= 1'b0;endelse if (cs_n_r == 1'b0 && div_cnt == (DIV_END_NUM>>1) && bit_cnt != bit_cnt_end) beginsck_r <= 1'b1;endelse if (cs_n_r == 1'b0 && div_cnt == DIV_END_NUM && bit_cnt != bit_cnt_end) beginsck_r <= 1'b0;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginrd_inst_shift <='d0;endelse if (rd_flag == 1'b1 && cs_n_r == 1'b1) beginrd_inst_shift <= {READ_P,rd_addr};endelse if (cs_n_r == 1'b0 && bit_cnt < 31 && div_cnt== DIV_END_NUM) beginrd_inst_shift <= {rd_inst_shift[30:0],1'b0};end
endalways @*sdi_r <= rd_inst_shift[31];always @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginshift_flag <= 1'b0;endelse if (bit_cnt >31 && bit_cnt != bit_cnt_end && div_cnt == (DIV_END_NUM>>1)) beginshift_flag <= 1'b1;endelse beginshift_flag <= 1'b0;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginwr_en <= 1'b0;endelse if (bit_cnt > 31 && bit_cnt != bit_cnt_end && div_cnt == ((DIV_END_NUM >>1)+1) && bit_cnt[2:0] == 3'b111) beginwr_en <= 1'b1;endelse beginwr_en <= 1'b0;endendalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginsdo_data_shift <= 'd0;endelse if (shift_flag == 1'b1) beginsdo_data_shift <= {sdo_data_shift[6:0],sdo};end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginflash_read_end <= 1'b0;endelse if(flash_read_end == 1'b1 && empty == 1'b1)beginflash_read_end <= 1'b0;endelse if (cs_n_r == 1'b0 && bit_cnt == bit_cnt_end && div_cnt == DIV_END_NUM) beginflash_read_end <= 1'b1;end
endassign  rd_ready = flash_read_end & (~empty);fifo_1024x8 fifo_1024x8_inst (.clk(sclk), // input clk.din(sdo_data_shift), // input [7 : 0] din.wr_en(wr_en), // input wr_en.rd_en(rd_en), // input rd_en.dout(rd_data), // output [7 : 0] dout.full(full), // output full.empty(empty) // output empty
);endmodule 

4.模块整合

到目前为止,我们分别完成了flash_se、flash_pp、flash_rd三个对flash进行操作的模块,如何将他们整合在一起,实现:
我们通过串口给 fpga 发送指令,fpga 解析指令之后,转化为对 flash_se、flash_pp、flash_rd 的控制时序(即fpga能根据接收到的指令产生控制对应模块需要的输入信号),实现对 flash 的读or写or擦除操作。

4.1 state_ctrl模块设计

既然要与上位机串口交互,我们必须制定一个协议,我们制定三种数据包类型,擦除命令包、写命令包、读命令包,当fpga接收到对应的命令包后可以跳转到对应模块。
在这里插入图片描述

4.1.1 信号连接示意图

在这里插入图片描述

4.1.2 状态机转换

在这里插入图片描述

4.1.3 波形图设计

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

4.1.4 代码

关键代码:
在这里插入图片描述

module state_ctrl(input	wire 		sclk,input	wire 		rst,output	reg  		cs_n,output	reg 		sck,output	reg 		sdi,input	wire 		sdo,/*************uart rx****************************/input	wire 		rx_flag,input	wire [7:0]	rx_data,/************uart tx ***************************/output	wire 		tx_flag,output	wire [7:0]	tx_data,/************flash se interface*******************/output	wire 		se_flag,output	wire [23:0]	se_addr,input	wire 		se_sck,input	wire 		se_cs_n,input	wire 		se_sdi,/************flash pp interface******************/output	wire 		pp_flag,output	wire [23:0]	pp_addr,output	wire 		wr_en,output	wire [7:0]	wr_data,input	wire 		pp_sck,input	wire 		pp_cs_n,input	wire 		pp_sdi,/************flash rd interface******************/output	wire 		rd_flag,output	wire [23:0]	rd_addr,output	wire [10:0]	rd_len,input	wire 		rd_ready,output	wire		rd_en,input	wire [7:0]	rd_data,input	wire 		rd_sck,input	wire 		rd_cs_n,output	wire 		rd_sdo,input	wire 		rd_sdi);
parameter 	IDLE =4'b0001;
parameter	WRITE =4'b0010;
parameter	SERASE=4'b0100;
parameter	READ  =4'b1000;parameter	DIV_CNT_END = 4340;//52082;reg [3:0]	state;
reg 		write_end,serase_end,read_end;
reg [15:0]	byte_cnt;
reg [23:0]	flash_addr_shift;
reg 		wr_fifo_en;
reg [7:0]	wr_fifo_data;
reg [15:0]	rd_len_shift;
reg 		rd_flash_req;//rd_flag
reg [15:0]	div_cnt;
reg 		rd_fifo_en;
reg [15:0]	tx_d_cnt;always @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginstate <= IDLE;endelse case(state)IDLE : if(rx_flag == 1'b1 && rx_data == 8'hcc)beginstate <= WRITE;endelse if(rx_flag == 1'b1 && rx_data == 8'hee) beginstate <= SERASE;endelse if (rx_flag == 1'b1 && rx_data == 8'hdd) beginstate <= READ;endWRITE : if (write_end == 1'b1 ) beginstate <= IDLE;endSERASE : if (serase_end == 1'b1 ) beginstate <= IDLE;endREAD : if (read_end == 1'b1) beginstate <= IDLE;enddefault : state <= IDLE;endcase
end//write state and flash pp
always @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginbyte_cnt <= 'd0;endelse if (state == IDLE) beginbyte_cnt <='d0;endelse if (state != IDLE && rx_flag == 1'b1) beginbyte_cnt <= byte_cnt + 1'b1;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginflash_addr_shift <= 'd0;endelse if (state != IDLE && byte_cnt < 3 && rx_flag == 1'b1  ) beginflash_addr_shift <= {flash_addr_shift[15:0],rx_data};end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginwr_fifo_en <= 1'b0;endelse if (state == WRITE && byte_cnt >2 && rx_flag == 1'b1) beginwr_fifo_en <= 1'b1;endelse beginwr_fifo_en <= 1'b0;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginwr_fifo_data <= 'd0;endelse if (state == WRITE) beginwr_fifo_data <= rx_data;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginwrite_end <=1'b1;endelse if (state == WRITE && byte_cnt == 'd258 && rx_flag == 1'b1) beginwrite_end <= 1'b1;endelse beginwrite_end <= 1'b0;end
endassign pp_flag = write_end;
assign pp_addr = flash_addr_shift;
assign wr_en = wr_fifo_en;
assign wr_data = wr_fifo_data;
//write state end
//serase state start
always @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginserase_end <= 1'b0;endelse if (state == SERASE && rx_flag == 1'b1 && byte_cnt == 2) beginserase_end <= 1'b1;endelse beginserase_end <= 1'b0;end
end
assign se_flag = serase_end;
assign se_addr = flash_addr_shift;
//serase state end
//read state startalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginrd_len_shift <='d0;endelse if (state == READ && (byte_cnt == 3 || byte_cnt == 4) && rx_flag == 1'b1 ) beginrd_len_shift <= {rd_len_shift[7:0],rx_data};end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginrd_flash_req<= 1'b0;endelse if (state == READ && byte_cnt == 4 && rx_flag == 1'b1 ) beginrd_flash_req <= 1'b1;endelse beginrd_flash_req <= 1'b0;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) begindiv_cnt <='d0;endelse if(state == READ && rd_ready == 1'b1 && div_cnt == DIV_CNT_END)begindiv_cnt <= 'd0;endelse if (state == READ && rd_ready == 1) begindiv_cnt <= div_cnt + 1'b1;endelse if (rd_ready == 1'b0) begindiv_cnt <='d0;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginrd_fifo_en <= 1'b0;endelse if (state == READ && rd_ready == 1'b1 && div_cnt ==0) beginrd_fifo_en <= 1'b1;endelse beginrd_fifo_en <= 1'b0;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) begintx_d_cnt <='d0;endelse if (state == READ && rd_fifo_en == 1'b1 ) begintx_d_cnt <= tx_d_cnt + 1'b1;endelse if(state != READ) begintx_d_cnt <='d0;end
endalways @(posedge sclk or posedge rst) beginif (rst == 1'b1) beginread_end <= 1'b0;endelse if (state == READ && tx_d_cnt == (rd_len_shift-1)&&rd_fifo_en == 1'b1 ) beginread_end <= 1'b1;endelse beginread_end <= 1'b0;end
endassign rd_flag = rd_flash_req;
assign rd_addr = flash_addr_shift;
assign rd_len = rd_len_shift[10:0];
assign rd_en = rd_fifo_en;assign tx_flag = rd_fifo_en;
assign tx_data = rd_data;//read state  end
//flash spi signal routealways @* beginif(state == READ) beginsdi <= rd_sdi;cs_n <= rd_cs_n;sck <= rd_sck;endelse beginsdi <= se_sdi | pp_sdi;sck <= se_sck | pp_sck;cs_n <= se_cs_n & pp_cs_n;end
endassign rd_sdo =sdo ;endmodule 

5.下板操作:

实现将一个呼吸灯的程序固化到flash中

1.先将flash_ctrl设计的bit文件通过JTAG下载线下载到fpga板子上(保证fpga有读写flash的功能,这是往flash中烧写文件的前提)

2.打开老师提供的fpga_update软件(这个软件是简化版本的,目前只能对flash进行写数据),将呼吸灯的bin文件下载到flash中
在这里插入图片描述
3.烧写完成后,板卡重新上电,发现板子开始执行呼吸灯的程序了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/837357.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript 对象入门:基础用法全解析

目录 对象 语法 属性和访问 方法和调用 this关键字 null 遍历对象 内置对象 Math 属性 方法 Date 创建日期对象 获取和设置日期 ⭐对象 对象是 JavaScript 数据类型的一种&#xff0c;数据类型也包括数值类型、字符串类型、布尔类型、undefined。对象数据类型可…

程序员之路:裁员与内卷下的生存之道

作为一名普通的程序员&#xff0c;身处这个瞬息万变的IT行业&#xff0c;面对着今年不断加剧的裁员浪潮和日益激烈的内卷竞争&#xff0c;我时常感到焦虑和不安。然而&#xff0c;正是这些挑战&#xff0c;让我们更加深入地思考了在这个行业中&#xff0c;我们该如何找到自己的…

2024统计建模中国新质生产力统计测度与时空演变及其驱动因素研究

高质量成品论文46页word版本1.5w字书写完整数据集1000行py代码一等奖论文&#xff01;这里仅展示部分内容&#xff0c;完整版在下面的链接。 【1.5w字全网最佳】2024统计建模大赛高质量成品论文39页配套完整代码运行全套数据集https://www.jdmm.cc/file/2710661/ 中国新质生产…

【2024HNCTF】密码组部分出题记录

2024H&NCTF 密码组部分出题记录 题目&#xff1a;BabyPQ、HappyDance 文章目录 2024H&NCTF 密码组部分出题记录BabyPQ | 签到HappyDance BabyPQ | 签到 本题为nc交互题&#xff0c;之所以采用这种形式&#xff0c;是因为可能有很多密码新师傅们不了解这种赛题形式&a…

AI机器人火了,探讨早就可以帮我们开拓市场的中关村科金语音机器人

近期AI机器人给我们带来了不少惊喜&#xff0c;比如国外 Figuer 联合 OpenAI 做了 Chatgpt 机器人&#xff0c;可以通过对话后推理干活&#xff0c;国内仿生机器人员工也开始量产&#xff0c;看到AI机器人不禁想到会不会替代我们的工作&#xff1f;我们了解到很多机器人厂家的…

#初阶模板

目录 1.泛型编程 2.模板 &#xff08;1&#xff09;函数模板 &#xff08;2&#xff09;举个栗子&#xff08;模板的使用&#xff09; 1.模板的声明有两种形式 2.函数模板的原理 &#xff08;3&#xff09;模板参数的匹配原则 &#xff08;4&#xff09;类模板 类模板…

AI图像生成-原理

一、图像生成流程总结 【AI绘画】深入理解Stable Diffusion&#xff01;站内首个深入教程&#xff0c;30分钟从原理到模型训练 买不到的课程_哔哩哔哩_bilibili 二、如果只是用comfy UI生成图片 1、找到下面几个文件&#xff0c;把对应模型移动到对应文件夹即可使用 2、选择对…

大学c语言基础很差,能不能学51单片机?会不会很困难?

开始前我分享下我的经历&#xff0c;我刚入行时遇到一个好公司和师父&#xff0c;给了我机会&#xff0c;一年时间从3k薪资涨到18k的&#xff0c; 我师父给了一些51单片机学习方法和资料&#xff0c;让我不断提升自己&#xff0c;感谢帮助过我的人&#xff0c; 如大家和我一样…

python turtle 升国旗

​一、导语 大家好,前段时间,我们画出了五星红旗,今天我们要用Python的Turtle库来绘制一个五星红旗,并让国旗上升,让我们一起来感受编程与艺术的完美结合吧!领略国家的强大!爱祖国,做一个遵纪守法的好公民。 二、效果展示 升国旗 三、开发过程 一、准备工作 首先我们…

OpenAI深夜震撼发布最新模型GPT-4o,送上最快速便捷教程

北京时间5月14日凌晨&#xff0c;有人说OpenAI一夜改变了历史。 在我们的深夜、太平洋时间的上午 10 点&#xff0c;OpenAI 召开春季发布会&#xff0c;公布了最新的GPT-4o模型&#xff0c;o代表Omnimodel&#xff08;全能模型&#xff09;。20多分钟的演示直播&#xff0c;展…

美国加州正测试ChatGPT等生成式AI,在4大部门应用

5月11日&#xff0c;美联社消息&#xff0c;美国加州政府正在测试ChatGPT等生成式AI&#xff0c;应用在税收和收费管理部、交通部、公共卫生部以及卫生与公众服务部4大部门。 测试时间6个月&#xff0c;为其提供技术支持的一共有5家公司&#xff0c;分别是OpenAI、Anthropic、…

成都新增数字产业园,打造生态经济新引擎

成都产业园运营方树莓集团——数字产业生态链建设者。秉持高效、友善、敢为的集团核心文化&#xff0c;有效链接政、产、企、校四个板块&#xff0c;构建了产业生态闭环系统。在成都市区内&#xff0c;已布局了多家数字文创产业园&#xff0c;其中具有代表性的参考国际数字影像…

Linux 第三十一章

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;linux &#x1f525;座右铭&#xff1a;“不要等到什么都没有了…

GRFB-UNet:一种新的多尺度注意力网络,用于铺路分割

不同场景下的带注释的触觉铺装示例: GRFB-UNet网络结构: GRFB模块的结构: 铺路在视障人士的旅行中起着至关重要的作用。因此,识别铺装的形状和位置以支持视障人士的移动性是相当有意义的,而视觉分割技术就适合这项任务。为了有效提高触觉铺装分割的精度和鲁棒性,…

httpsok-v1.11.0支持CDN证书自动部署

&#x1f525;httpsok-v1.11.0支持CDN证书自动部署 介绍 httpsok 是一个便捷的 HTTPS 证书自动续签工具&#xff0c;专为 Nginx 、OpenResty 服务器设计。已服务众多中小企业&#xff0c;稳定、安全、可靠。 一行命令&#xff0c;一分钟轻松搞定SSL证书自动续期 v1.11.0 版…

从“山寨的”MTK芯片发展历程 同类芯片比较基础常识 如何简单识别mtk机型

联发科技&#xff08;MediaTek Inc.简称“联发科” &#xff0c;联发科起家于CD-ROM芯片&#xff0c;直接将DVD内分别承担视频和数字解码功能的两颗芯片整合到了一颗芯片上&#xff0c;并提供相应的软件方案。并一度占据大陆DVD市场60%的芯片供应量&#xff01;他将图像处理、M…

证卡打印机打印头故障的主要原因以及如何预防

爱立识打印头故障的主要原因主要包括以下几点&#xff1a; Evolis 打印机清洁卡 灰尘或碎屑堆积&#xff1a;长时间不清洁打印机导致灰尘或碎屑在机器内积累&#xff0c;可能会导致打印头划伤或堵塞。 使用不洁的卡片&#xff1a;使用不洁的卡片可能会导致灰尘或污垢进入打印机…

气膜建筑为何能够建在楼顶—轻空间

近年来&#xff0c;随着体育设施建设的推进和政策支持&#xff0c;越来越多的体育从业者将目光聚焦到了楼顶&#xff0c;希望通过在楼顶加盖气膜馆来充分利用有限的土地资源。那么&#xff0c;为什么气膜建筑能够建在楼顶呢&#xff1f;轻空间将从气膜建筑的结构特点、安全性、…

Java的VO,BO,PO,DO,DTO

写在前面 本文看下VO&#xff0c;BO&#xff0c;PO&#xff0c;DO&#xff0c;DTO&#xff0c;都是啥&#xff01; 1&#xff1a;正文 先看一张图&#xff0c;看了图就能知道个大概了&#xff1a; 1.1&#xff1a;PO 全称是persistent object&#xff0c;对应数据的表&am…

Linux - make与makefile

文章目录 什么是make和makefile如何使用依赖关系 和 依赖方法伪目标 写个程序-进度条换行和回车的区别 什么是make和makefile make是一个命令 makefile是一个文件 这就是make和makefile的本质 make和 ll , pwd ,su 一样都是命令 makefile和 test &#xff0c; test.c 一样都是…