当前位置: 首页 > news >正文

S25FL系列FLASH读写的FPGA实现

文章目录

  • 实现思路
  • 具体实现
    • 子模块实现
    • top模块
  • 测试
  • Something

实现思路

  建议读者先对 S25FL-S 系列 FLASH 进行了解,我之前的博文中有详细介绍。

  笔者的芯片具体型号为 S25FL256SAGNFI00,存储容量 256Mb,增强高性能 EHPLC,4KB 与 64KB 混合 Sector 的存储阵列,256 Byte 的 Page Programming Buffer 大小,最高支持 133MHz,无硬复位 RESET# 引脚。

  为简单起见,采用 SDR 时钟模式;为了兼顾读写速度,采用 Quad mode;同时考虑到 Quad Page Programming 地址只能通过 SI 单线传输,因此读、写 FLASH 分别采用 Quad Output Read、Quad Page Programming,以实现时序格式的统一,简化编程。

  由于 S25FL-S 在 SCK 上升沿锁存数据,在 SCK 下降沿转换数据,因此主控端应在 SCK 下降沿转换数据,在 SCK 上升沿锁存数据

  由于写 FLASH 需要先进行写使能以及擦除操作,而擦除操作需要检查 WIP bit(SR1[0]);要使用 Quad 读写模式,需要置位 Quad bit(CR1[1]);要判断地址映射类型和四元读模式下的 Dummy 长度,需要实现读写寄存器。因此需要实现以下功能:写使能 WREN、写失能 WRDI、写寄存器 WRR、清除状态寄存器 CLSR、读状态寄存器 RDSR1/RDSR2、读配置寄存器 RDCR、擦除操作(扇区擦除 4SE、批量擦除 BE)、四元编程操作 4QPP、Quad Output Read 操作 4QOR 等。

  为每一种功能单独写一个模块当然也是可行的思路,但过于繁杂;观察到在时序层面上述指令可以归类为简单的 5 种:单 8bit 指令(如 WREN、WRDI、CLSR、BE 等)、写寄存器(8bit 指令后跟随 1~4Byte 数据,SI 单线传输,如 WRR、ABWR、BRWR 等,甚至 8bit 指令 + 4Byte 地址的 4SE 也可归于此类)、读寄存器(8bit 指令(SI)后跟随 1~4Byte 输出(SO),如 RDSR1、RDSR2、RDCR1、ABRD、BRRD 等)、四元写 FLASH (8bit 指令(SI)+ 32bit 地址(SI)+ 1~256Byte 数据(IO0~IO3写),如 4QPP)、四元读 FLASH (8bit 指令(SI)+ 32bit 地址(SI)+ xbit Dummy + xByte 数据(IO0~IO3读回),如 4QOR)。

  因此可以首先实现以上几个基础模块,然后根据需要在上层模块中用状态机控制几个基础模块的运行。

具体实现

  由于本示例实现中每个子模块都涉及 FLASH_IO 这组 inout 线的操作,因此有注意事项如下:

  每个 FPGA 管脚上都要有 IBUF、OBUF 或 IOBUF,input/output 管脚上 IBUF/OBUF 会自动生成,而 inout 管脚需要用户编写,要么用 IOBUF,要么直接用 link? xx_OBUF : 1’bz 这种形式(其实后者也是生成了一个 OBUF 和一个 IBUF)。

  对于每个 FPGA 管脚,只能由一个 OBUF 驱动,因此如果多个子模块要用 inout 操作同一根线,会出问题(这种情况下 vivado 会自动生成 IBUF,导致模块大部分逻辑无效化,进而在综合后整个模块被优化掉,即使强制关闭 IBUF/OBUF 自动插入功能,也会因为多个 OBUF 驱动同一管脚而综合失败)。

 因此子模块不能再保有 inout,而是通过操作顶层模块的 IOBUF 实现数据读写,具体实现方式为:子模块关于 FLASH_IO 的接口设计为两个单向接口(FLASH_IO_IBUF、FLASH_IO_OBUF),并给出何时使能 O_BUF 的 link 信号;顶层模块根据状态仲裁接通哪路子模块,并根据对应的 link 决定驱动方向。

子模块实现

  • 单条指令
/* * file			: flash_instruction.v* author		: 今朝无言* Lab			: WHU-EIS-LMSWE* date			: 2023-11-15* version		: v2.0* description	: 单条 8bit 指令,从而支持诸如 WREN、WRDI、Bulk Erase 等指令* Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.*/
`default_nettype none
module flash_instruction(
input	wire			clk,
input	wire			rst_n,output	wire			FLASH_SCK,
output	reg				FLASH_nCS,output	reg		[3:0]	link,
output	reg		[3:0]	FLASH_IO_OBUF,
input	wire	[3:0]	FLASH_IO_IBUF,//usr interface
input	wire			send_en,		//上升沿有效
input	wire	[7:0]	instruction,
output	reg				busy
);reg		FLASH_nCS	= 1'b1;
assign	FLASH_SCK	= FLASH_nCS? 1'b1 : clk;	//SPI mode 3reg		[3:0]	link			= 4'h0;
reg		[3:0]	FLASH_IO_OBUF	= 4'hf;//--------------------------------------------------
wire	send_en_pe;
reg		send_en_d0;
reg		send_en_d1;always @(posedge clk) beginsend_en_d0	<= send_en;send_en_d1	<= send_en_d0;
endassign	send_en_pe	= send_en_d0 & (~send_en_d1);//--------------------FSM---------------------------
localparam	S_IDLE		= 8'h01;
localparam	S_COMMAND	= 8'h02;
localparam	S_STOP		= 8'h04;reg		[7:0]	state	= S_IDLE;
reg		[7:0]	next_state;reg		[2:0]	cnt		= 3'd0;always @(posedge clk or negedge rst_n) beginif(~rst_n) beginstate	<= S_IDLE;endelse beginstate	<= next_state;end
endalways @(*) begincase(state)S_IDLE: beginif(send_en_pe) beginnext_state	<= S_COMMAND;endelse beginnext_state	<= S_IDLE;endendS_COMMAND: beginif(cnt >= 3'd7) beginnext_state	<= S_STOP;endelse beginnext_state	<= S_COMMAND;endendS_STOP: beginnext_state	<= S_IDLE;enddefault: beginnext_state	<= S_IDLE;endendcase
end//FLASH_nCS
always @(negedge clk) begincase(state)S_COMMAND: beginFLASH_nCS	<= 1'b0;enddefault: beginFLASH_nCS	<= 1'b1;endendcase
end//cnt
always @(posedge clk) begincase(state)S_IDLE: begincnt		<= 3'd0;endS_COMMAND: beginif(~FLASH_nCS) begincnt		<= cnt + 1'b1;endelse begincnt		<= 3'd0;endendS_STOP: begincnt		<= 3'd0;enddefault: begincnt		<= cnt;endendcase
end//FLASH_IO_OBUF
always @(negedge clk) begin		//在SCK下降沿转换数据case(state)S_COMMAND: beginFLASH_IO_OBUF[0]	<= instruction[3'd7-cnt];	//首先移出MSBFLASH_IO_OBUF[3:1]	<= 3'b111;enddefault: beginFLASH_IO_OBUF	<= 4'hf;endendcase
end//link
always @(negedge clk) begincase(state)S_COMMAND: beginlink	<= 4'b1101;//指令阶段,SO应维持高阻,WP#、HOLD#应拉高;//而WP#、HOLD#内部有上拉电阻,因此IO1~IO3可以直接释放掉//不过为保险起见,这里还是强制拉高IO2/IO3,而IO1可以释放掉enddefault: beginlink	<= 4'h0;endendcase
end//busy
always @(*) begincase(state)S_IDLE: beginbusy	<= 1'b0;endS_COMMAND, S_STOP: beginbusy	<= 1'b1;enddefault: beginbusy	<= 1'b0;endendcase
endendmodule
  • 读寄存器
/* * file			: flash_RDR.v* author		: 今朝无言* Lab			: WHU-EIS-LMSWE* date			: 2023-11-15* version		: v2.0* description	: 读寄存器,支持1~4Byte读取,从而支持对SR1、SR2、CR1、ABR、BAR等寄存器的读取* Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.*/
`default_nettype none
module flash_RDR(
input	wire			clk,
input	wire			rst_n,output	wire			FLASH_SCK,
output	reg				FLASH_nCS,output	reg		[3:0]	link,
output	reg		[3:0]	FLASH_IO_OBUF,
input	wire	[3:0]	FLASH_IO_IBUF,//usr interface
input	wire			read_en,		//上升沿有效
input	wire	[7:0]	instruction,input	wire	[3:0]	Register_Len,	//寄存器长度,1/2/4 Byte
output	reg 	[31:0]	Reg,			//低位对齐。即1Byte的寄存器占用Reg[7:0],4Byte的寄存器占用Reg[31:0]output	reg				busy
);reg		FLASH_nCS	= 1'b1;
assign	FLASH_SCK	= FLASH_nCS? 1'b1 : clk;	//SPI mode 3reg		[3:0]	link			= 4'h0;
reg		[3:0]	FLASH_IO_OBUF	= 4'hf;wire	read_en_pe;
reg		read_en_d0;
reg		read_en_d1;always @(posedge clk) beginread_en_d0	<= read_en;read_en_d1	<= read_en_d0;
endassign	read_en_pe	= read_en_d0 & (~read_en_d1);//--------------------FSM---------------------------
localparam	S_IDLE		= 8'h01;
localparam	S_COMMAND	= 8'h02;
localparam	S_RDR		= 8'h04;
localparam	S_STOP		= 8'h08;reg		[7:0]	state	= S_IDLE;
reg		[7:0]	next_state;reg		[2:0]	cnt			= 3'd0;		//Byte内bit计数
reg		[3:0]	cnt_Byte	= 4'd0;always @(posedge clk or negedge rst_n) beginif(~rst_n) beginstate	<= S_IDLE;endelse beginstate	<= next_state;end
endalways @(*) begincase(state)S_IDLE: beginif(read_en_pe) beginnext_state	<= S_COMMAND;endelse beginnext_state	<= S_IDLE;endendS_COMMAND: beginif(cnt >= 3'd7) beginnext_state	<= S_RDR;endelse beginnext_state	<= S_COMMAND;endendS_RDR: beginif(cnt >= 3'd7 && cnt_Byte >= Register_Len - 1'b1) beginnext_state	<= S_STOP;endelse beginnext_state	<= S_RDR;endendS_STOP: beginnext_state	<= S_IDLE;enddefault: beginnext_state	<= S_IDLE;endendcase
end//FLASH_nCS
always @(negedge clk) begincase(state)S_COMMAND, S_RDR: beginFLASH_nCS	<= 1'b0;enddefault: beginFLASH_nCS	<= 1'b1;endendcase
end//cnt
always @(posedge clk) begincase(state)S_IDLE: begincnt		<= 3'd0;endS_COMMAND, S_RDR: begin		//将cnt设计为3bit位宽,可实现模8加if(~FLASH_nCS) begincnt		<= cnt + 1'b1;endelse begincnt		<= 3'd0;endendS_STOP: begincnt		<= 3'd0;enddefault: begincnt		<= cnt;endendcase
end//cnt_Byte
always @(posedge clk) begincase(state)S_RDR: beginif(cnt==3'd7) begincnt_Byte	<= cnt_Byte + 1'b1;endelse begincnt_Byte	<= cnt_Byte;endenddefault: begincnt_Byte	<= 4'd0;endendcase
end//FLASH_IO_OBUF
always @(negedge clk) begin		//在SCK下降沿转换数据case(state)S_COMMAND: beginFLASH_IO_OBUF[0]	<= instruction[3'd7-cnt];	//首先移出MSBFLASH_IO_OBUF[3:1]	<= 3'b111;enddefault: beginFLASH_IO_OBUF	<= 4'hf;endendcase
end//link
always @(negedge clk) begincase(state)S_COMMAND: beginlink	<= 4'b1101;endS_RDR: beginlink	<= 4'h0;enddefault: beginlink	<= 4'h0;endendcase
end//read reg
wire	SO	= FLASH_IO_IBUF[1];
always @(posedge clk or negedge rst_n) begin	//须在SCK上升沿锁存数据if(~rst_n) beginReg		<= 32'd0;endelse begincase(state)S_RDR: beginReg		<= {Reg[30:0], SO};		//移位寄存来自SO的值enddefault: beginReg		<= Reg;endendcaseend
end//busy
always @(*) begincase(state)S_IDLE: beginbusy	<= 1'b0;enddefault: beginbusy	<= 1'b1;endendcase
endendmodule
  • 写寄存器
/* * file			: flash_WRR.v* author		: 今朝无言* Lab			: WHU-EIS-LMSWE* date			: 2023-11-15* version		: v2.0* description	: 写寄存器,支持 1Byte ~ 4Byte 的写入,* 				  从而支持对 SR1、CR1、ABR、BAR 等寄存器的写入操作,* 				  以及Sector Erase擦除命令* Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.*/
`default_nettype none
module flash_WRR(
input	wire			clk,
input	wire			rst_n,output	wire			FLASH_SCK,
output	reg				FLASH_nCS,output	reg		[3:0]	link,
output	reg		[3:0]	FLASH_IO_OBUF,
input	wire	[3:0]	FLASH_IO_IBUF,//usr interface
input	wire			send_en,		//上升沿有效
input	wire	[7:0]	instruction,input	wire	[3:0]	Register_Len,	//寄存器长度,1/2/4 Byte
input	wire	[7:0]	Byte1,
input	wire	[7:0]	Byte2,
input	wire	[7:0]	Byte3,
input	wire	[7:0]	Byte4,output	reg				busy
);
//使用示例:对于单写SR1寄存器,令Reg_Len=1,并在Byte1给出要写入SR1的值;
//对于写CR1,需要用到2Byte的形式,令Reg_Len=2,Byte1=SR1,Byte2=CR1;
//对于Autiboot Reister,Len=4,Byte1~4分别为ABR[31:24]、ABR[23:16]、ABR[15:8]、ABR[7:0];
//其余写寄存器指令依此类推
//甚至对于4SE擦除操作,Byte1~4可直接用作Sector地址使用reg		FLASH_nCS	= 1'b1;
assign	FLASH_SCK	= FLASH_nCS? 1'b1 : clk;	//SPI mode 3reg		[3:0]	link			= 4'h0;
reg		[3:0]	FLASH_IO_OBUF	= 4'hf;wire	send_en_pe;
reg		send_en_d0;
reg		send_en_d1;always @(posedge clk) beginsend_en_d0	<= send_en;send_en_d1	<= send_en_d0;
endassign	send_en_pe	= send_en_d0 & (~send_en_d1);//--------------------FSM---------------------------
localparam	S_IDLE		= 8'h01;
localparam	S_COMMAND	= 8'h02;
localparam	S_WRR		= 8'h04;
localparam	S_STOP		= 8'h08;reg		[7:0]	state	= S_IDLE;
reg		[7:0]	next_state;reg		[2:0]	cnt			= 3'd0;		//Byte内bit计数
reg		[3:0]	cnt_Byte	= 4'd0;always @(posedge clk or negedge rst_n) beginif(~rst_n) beginstate	<= S_IDLE;endelse beginstate	<= next_state;end
endalways @(*) begincase(state)S_IDLE: beginif(send_en_pe) beginnext_state	<= S_COMMAND;endelse beginnext_state	<= S_IDLE;endendS_COMMAND: beginif(cnt >= 3'd7) beginnext_state	<= S_WRR;endelse beginnext_state	<= S_COMMAND;endendS_WRR: beginif(cnt >= 3'd7 && cnt_Byte >= Register_Len - 1'b1) beginnext_state	<= S_STOP;endelse beginnext_state	<= S_WRR;endendS_STOP: beginnext_state	<= S_IDLE;enddefault: beginnext_state	<= S_IDLE;endendcase
end//FLASH_nCS
always @(negedge clk) begincase(state)S_COMMAND, S_WRR: beginFLASH_nCS	<= 1'b0;enddefault: beginFLASH_nCS	<= 1'b1;endendcase
end//cnt
always @(posedge clk) begincase(state)S_IDLE: begincnt		<= 3'd0;endS_COMMAND, S_WRR: begin		//将cnt设计为3bit位宽,可实现模8加if(~FLASH_nCS) begincnt		<= cnt + 1'b1;endelse begincnt		<= 3'd0;endendS_STOP: begincnt		<= 3'd0;enddefault: begincnt		<= cnt;endendcase
end//cnt_Byte
always @(posedge clk) begincase(state)S_WRR: beginif(cnt==3'd7) begincnt_Byte	<= cnt_Byte + 1'b1;endelse begincnt_Byte	<= cnt_Byte;endenddefault: begincnt_Byte	<= 4'd0;endendcase
end//FLASH_IO_OBUF
always @(negedge clk) begin		//在SCK下降沿转换数据case(state)S_COMMAND: beginFLASH_IO_OBUF[0]	<= instruction[3'd7-cnt];	//首先移出MSBFLASH_IO_OBUF[3:1]	<= 3'b111;endS_WRR: begincase(cnt_Byte)4'd0:		FLASH_IO_OBUF[0]	<= Byte1[3'd7-cnt];4'd1:		FLASH_IO_OBUF[0]	<= Byte2[3'd7-cnt];4'd2:		FLASH_IO_OBUF[0]	<= Byte3[3'd7-cnt];4'd3:		FLASH_IO_OBUF[0]	<= Byte4[3'd7-cnt];default:	FLASH_IO_OBUF[0]	<= 1'b1;endcaseFLASH_IO_OBUF[3:1]	<= 3'b111;enddefault: beginFLASH_IO_OBUF	<= 4'hf;endendcase
end//link
always @(negedge clk) begincase(state)S_COMMAND, S_WRR: beginlink	<= 4'b1101;enddefault: beginlink	<= 4'h0;endendcase
end//busy
always @(*) begincase(state)S_IDLE: beginbusy	<= 1'b0;enddefault: beginbusy	<= 1'b1;endendcase
endendmodule
  • Page Programming
/* * file			: flash_4QPP.v* author		: 今朝无言* Lab			: WHU-EIS-LMSWE* date			: 2023-11-16* version		: v2.0* description	: 实现 4QPP 指令,32bit Addr,Quad Page Programming* Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.*/
`default_nettype none
module flash_4QPP(
input	wire			clk,			//S25FL256SAGNFI00 在 4QPP 下最大支持 80M
input	wire			rst_n,output	wire			FLASH_SCK,
output	reg				FLASH_nCS,output	reg		[3:0]	link,
output	reg		[3:0]	FLASH_IO_OBUF,
input	wire	[3:0]	FLASH_IO_IBUF,//usr interface
input	wire			program_start,	//上升沿有效input	wire	[31:0]	addr,			//起始地址,可以是任意字节地址,但建议是 Page 起始地址,S25FL256SAGNFI00 的 Page 大小为 256Byte
input	wire	[9:0]	Byte_Len,		//一次写多少字节数据,Page Programming 只能在当前 Page 内进行写入,超出的将被忽略,建议一次写一整个 Pageoutput	wire			data_rd_clk,	//读数据的驱动时钟,若使用FIFO请用这个时钟,是clk的二分频时钟
output	reg				data_rden,		//读数据请求,可用作 FIFO 的 rden,FIFO 应采用 First Word Fall Through
input	wire	[7:0]	data,			//字节数据output	reg				busy
);localparam	instruction		= 8'h34;	//4QPP的指令码为 0x34reg		FLASH_nCS	= 1'b1;
assign	FLASH_SCK	= FLASH_nCS? 1'b1 : clk;	//SPI mode 3reg		[3:0]	link			= 4'h0;
reg		[3:0]	FLASH_IO_OBUF	= 4'hf;wire	program_start_pe;
reg		program_start_d0;
reg		program_start_d1;always @(posedge clk) beginprogram_start_d0	<= program_start;program_start_d1	<= program_start_d0;
endassign	program_start_pe	= program_start_d0 & (~program_start_d1);clkdiv #(.N(2))
clkdiv_2(.clk_in		(clk),.clk_out	(data_rd_clk)
);//--------------------FSM---------------------------
localparam	S_IDLE		= 8'h01;
localparam	S_COMMAND	= 8'h02;
localparam	S_ADDR		= 8'h04;
localparam	S_QUAD_WR	= 8'h08;
localparam	S_STOP		= 8'h10;reg		[7:0]	state	= S_IDLE;
reg		[7:0]	next_state;reg		[2:0]	cnt			= 3'd0;		//Byte内bit计数
reg		[9:0]	cnt_Byte	= 10'd0;always @(posedge clk or negedge rst_n) beginif(~rst_n) beginstate	<= S_IDLE;endelse beginstate	<= next_state;end
endalways @(*) begincase(state)S_IDLE: beginif(program_start_pe) beginnext_state	<= S_COMMAND;endelse beginnext_state	<= S_IDLE;endendS_COMMAND: beginif(cnt >= 3'd7) beginnext_state	<= S_ADDR;endelse beginnext_state	<= S_COMMAND;endendS_ADDR: beginif(cnt >= 3'd7 && cnt_Byte >= 4'd3) beginnext_state	<= S_QUAD_WR;endelse beginnext_state	<= S_ADDR;endendS_QUAD_WR: beginif(cnt >= 3'd4 && (Byte_Len == 10'd0 || cnt_Byte >= Byte_Len - 1'b1)) begin	//Len=0时视作Len=1next_state	<= S_STOP;endelse beginnext_state	<= S_QUAD_WR;endendS_STOP: beginnext_state	<= S_IDLE;enddefault: beginnext_state	<= S_IDLE;endendcase
end//FLASH_nCS
always @(negedge clk) begincase(state)S_COMMAND, S_ADDR, S_QUAD_WR: beginFLASH_nCS	<= 1'b0;enddefault: beginFLASH_nCS	<= 1'b1;endendcase
end//cnt
always @(posedge clk) begincase(state)S_IDLE: begincnt		<= 3'd0;endS_COMMAND, S_ADDR: beginif(~FLASH_nCS) begincnt		<= cnt + 1'b1;endelse begincnt		<= 3'd0;endendS_QUAD_WR: beginif(~FLASH_nCS) begincnt		<= cnt + 3'd4;	//Quad WR 阶段一次传送4bitendelse begincnt		<= 3'd0;endendS_STOP: begincnt		<= 3'd0;enddefault: begincnt		<= cnt;endendcase
end//cnt_Byte
always @(posedge clk) begincase(state)S_ADDR: beginif(cnt==3'd7) beginif(cnt_Byte >= 16'd3) begincnt_Byte	<= 10'd0;endelse begincnt_Byte	<= cnt_Byte + 1'b1;endendelse begincnt_Byte	<= cnt_Byte;endendS_QUAD_WR: beginif(cnt==3'd4) begincnt_Byte	<= cnt_Byte + 1'b1;endelse begincnt_Byte	<= cnt_Byte;endenddefault: begincnt_Byte	<= 10'd0;endendcase
end//link
always @(negedge clk) begincase(state)S_COMMAND, S_ADDR: beginlink	<= 4'b1101;endS_QUAD_WR: beginlink	<= 4'b1111;enddefault: beginlink	<= 4'h0;endendcase
end//FLASH_IO_OBUF
always @(negedge clk) begin		//在SCK下降沿转换数据case(state)S_COMMAND: beginFLASH_IO_OBUF[0]	<= instruction[3'd7-cnt];	//首先移出MSBFLASH_IO_OBUF[3:1]	<= 3'b111;endS_ADDR: begincase(cnt_Byte[3:0])4'd0:		FLASH_IO_OBUF[0]	<= addr[5'd31-cnt];4'd1:		FLASH_IO_OBUF[0]	<= addr[5'd23-cnt];4'd2:		FLASH_IO_OBUF[0]	<= addr[5'd15-cnt];4'd3:		FLASH_IO_OBUF[0]	<= addr[5'd7-cnt];default:	FLASH_IO_OBUF[0]	<= 1'b1;endcaseFLASH_IO_OBUF[3:1]	<= 3'b111;endS_QUAD_WR: begincase(cnt)4'd0:		FLASH_IO_OBUF[3:0]	<= data[7:4];4'd4:		FLASH_IO_OBUF[3:0]	<= data[3:0];default:	FLASH_IO_OBUF[3:0]	<= 4'hf;endcaseenddefault: beginFLASH_IO_OBUF	<= 4'hf;endendcase
end//data_rden
always @(posedge clk) begincase(state)S_QUAD_WR: begindata_rden	<= 1'b1;enddefault: begindata_rden	<= 1'b0;endendcase
end//busy
always @(*) begincase(state)S_IDLE: beginbusy	<= 1'b0;enddefault: beginbusy	<= 1'b1;endendcase
endendmodule
  • 读 FLASH 主存储器
/* * file			: flash_4QOR.v* author		: 今朝无言* Lab			: WHU-EIS-LMSWE* date			: 2023-11-17* version		: v2.0* description	: 4QOR读flash,32bit Addr,Quad Output Read* Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.*/
`default_nettype none
module flash_4QOR(
input	wire			clk,
input	wire			rst_n,output	wire			FLASH_SCK,
output	reg				FLASH_nCS,output	reg		[3:0]	link,
output	reg		[3:0]	FLASH_IO_OBUF,
input	wire	[3:0]	FLASH_IO_IBUF,//usr interface
input	wire			read_start,		//上升沿有效input	wire	[31:0]	addr,			//起始地址,可以是任意字节地址
input	wire	[31:0]	Byte_Len,		//一次读多少字节数据,读取过程中flash会自动地址+1,达到最大地址后将从0x00地址继续读取output	wire			data_wr_clk,	//写数据的驱动时钟,若使用FIFO请用这个时钟,是clk的二分频时钟
output	reg				data_wren,		//wren,可用作 FIFO 的 wren
output	reg		[7:0]	data,			//读到的字节数据output	reg				busy,//LC
input	wire	[1:0]	LC				//LC bit(CR1[7:6])
);
//LC确定Dummy的长度,对于HPLC和PLC,在Quad Output Read下表现一致,
//都没有mode字段(mode len=0),除LC=11对应dummy len=0外(最大支持50MHz),其余都是dummy len=8localparam	instruction		= 8'h6C;	//4QOR的指令码为 0x6Creg		FLASH_nCS	= 1'b1;
assign	FLASH_SCK	= FLASH_nCS? 1'b1 : clk;	//SPI mode 3reg		[3:0]	link			= 4'h0;
reg		[3:0]	FLASH_IO_OBUF	= 4'hf;wire	read_start_pe;
reg		read_start_d0;
reg		read_start_d1;always @(posedge clk) beginread_start_d0	<= read_start;read_start_d1	<= read_start_d0;
endassign	read_start_pe	= read_start_d0 & (~read_start_d1);clkdiv #(.N(2))
clkdiv_2(.clk_in		(clk),.clk_out	(data_wr_clk)
);//--------------------FSM---------------------------
localparam	S_IDLE		= 8'h01;
localparam	S_COMMAND	= 8'h02;
localparam	S_ADDR		= 8'h04;
localparam	S_DUMMY		= 8'h08;
localparam	S_QUAD_RD	= 8'h10;
localparam	S_STOP		= 8'h20;reg		[7:0]	state	= S_IDLE;
reg		[7:0]	next_state;reg		[2:0]	cnt			= 3'd0;		//Byte内bit计数
reg		[31:0]	cnt_Byte	= 32'd0;always @(posedge clk or negedge rst_n) beginif(~rst_n) beginstate	<= S_IDLE;endelse beginstate	<= next_state;end
endalways @(*) begincase(state)S_IDLE: beginif(read_start_pe) beginnext_state	<= S_COMMAND;endelse beginnext_state	<= S_IDLE;endendS_COMMAND: beginif(cnt >= 3'd7) beginnext_state	<= S_ADDR;endelse beginnext_state	<= S_COMMAND;endendS_ADDR: beginif(cnt >= 3'd7 && cnt_Byte >= 4'd3) begincase(LC)		//根据LC判断Dummy的长度2'b11: beginnext_state	<= S_QUAD_RD;end2'b00, 2'b01, 2'b10: beginnext_state	<= S_DUMMY;enddefault: ;endcaseendelse beginnext_state	<= S_ADDR;endendS_DUMMY: beginif(cnt >= 3'd7) beginnext_state	<= S_QUAD_RD;endelse beginnext_state	<= S_DUMMY;endendS_QUAD_RD: beginif(cnt >= 3'd4 && (Byte_Len == 32'd0 || cnt_Byte >= Byte_Len - 1'b1)) begin	//Len=0时视作Len=1next_state	<= S_STOP;endelse beginnext_state	<= S_QUAD_RD;endendS_STOP: beginif(cnt>=1) begin	//维持在STOP两个clk,以保持data和wren保持一个wr_clknext_state	<= S_IDLE;endelse beginnext_state	<= S_STOP;endenddefault: beginnext_state	<= S_IDLE;endendcase
end//FLASH_nCS
always @(negedge clk) begincase(state)S_COMMAND, S_ADDR, S_DUMMY, S_QUAD_RD: beginFLASH_nCS	<= 1'b0;enddefault: beginFLASH_nCS	<= 1'b1;endendcase
end//cnt
always @(posedge clk) begincase(state)S_IDLE: begincnt		<= 3'd0;endS_COMMAND, S_ADDR: beginif(~FLASH_nCS) begincnt		<= cnt + 1'b1;endelse begincnt		<= 3'd0;endendS_DUMMY: beginif(cnt >= 3'd7) begin	//这里设置Bummy长度;由于4QOR只有0/8的Dummy长度,因该case实际可以和上面合并cnt		<= 3'd0;endelse begincnt		<= cnt + 1'b1;endendS_QUAD_RD: begincnt		<= cnt + 3'd4;	//Quad RD 阶段一次读回4bitendS_STOP: begincnt		<= 3'd1;enddefault: begincnt		<= cnt;endendcase
end//cnt_Byte
always @(posedge clk) begincase(state)S_ADDR: beginif(cnt==3'd7) beginif(cnt_Byte >= 32'd3) begincnt_Byte	<= 32'd0;endelse begincnt_Byte	<= cnt_Byte + 1'b1;endendelse begincnt_Byte	<= cnt_Byte;endendS_DUMMY: begincnt_Byte		<= 32'd0;endS_QUAD_RD: beginif(cnt==3'd4) begincnt_Byte	<= cnt_Byte + 1'b1;endelse begincnt_Byte	<= cnt_Byte;endenddefault: begincnt_Byte		<= 32'd0;endendcase
end//link
always @(negedge clk) begincase(state)S_COMMAND, S_ADDR: beginlink	<= 4'b1101;endS_DUMMY, S_QUAD_RD: begin	//为防止主控端与flash端的驱动器冲突,Dummy期间主控端应释放总线link	<= 4'b0000;enddefault: beginlink	<= 4'h0;endendcase
end//FLASH_IO_OBUF
always @(negedge clk) begin		//在SCK下降沿转换数据case(state)S_COMMAND: beginFLASH_IO_OBUF[0]	<= instruction[3'd7-cnt];	//首先移出MSBFLASH_IO_OBUF[3:1]	<= 3'b111;endS_ADDR: begincase(cnt_Byte[3:0])4'd0:		FLASH_IO_OBUF[0]	<= addr[5'd31-cnt];4'd1:		FLASH_IO_OBUF[0]	<= addr[5'd23-cnt];4'd2:		FLASH_IO_OBUF[0]	<= addr[5'd15-cnt];4'd3:		FLASH_IO_OBUF[0]	<= addr[5'd7-cnt];default:	FLASH_IO_OBUF[0]	<= 1'b1;endcaseFLASH_IO_OBUF[3:1]	<= 3'b111;enddefault: beginFLASH_IO_OBUF	<= 4'hf;endendcase
end//data_tmp
reg		[7:0]	data_tmp;
always @(posedge clk) begin	//须在SCK上升沿锁存数据case(state)S_QUAD_RD: begincase(cnt)3'd0: begindata_tmp[7:4]	<= FLASH_IO_IBUF;end3'd4: begindata_tmp[3:0]	<= FLASH_IO_IBUF;enddefault: begindata_tmp	<= data_tmp;endendcaseenddefault: begindata_tmp	<= data_tmp;endendcase
end//data_wren & data
reg				data_wren_buf;
reg		[7:0]	data_buf;
always @(posedge clk) begincase(state)S_QUAD_RD: beginif(cnt==0 && cnt_Byte>=1) begindata_wren_buf	<= 1'b1;data_buf		<= data_tmp;endelse begindata_wren_buf	<= data_wren_buf;data_buf		<= data_buf;endendS_STOP: begin		//S_STOP时锁存输出最后一个数据if(cnt==0) begindata_wren_buf	<= 1'b1;data_buf		<= data_tmp;endelse begindata_wren_buf	<= data_wren_buf;data_buf		<= data_buf;endenddefault: begindata_wren_buf	<= 1'b0;data_buf		<= 8'd0;endendcase
endalways @(posedge data_wr_clk) begin		//同步到data_wr_clk时钟域data_wren	<= data_wren_buf;data		<= data_buf;
end//busy
always @(*) begincase(state)S_IDLE: beginbusy	<= 1'b0;enddefault: beginbusy	<= 1'b1;endendcase
endendmodule

top模块

  • FLASH_top.v
/* * file			: FLASH_top.v* author		: 今朝无言* Lab			: WHU-EIS-LMSWE* date			: 2023-11-18* version		: v2.0* description	: S25FL256SAGNFI00 的读写控制,实现 SDR 时钟模式下的 Quad 读写模式* Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.*/
`default_nettype none
module FLASH_top(
input	wire			clk,
input	wire			rst_n,output	reg				FLASH_SCK,
output	reg				FLASH_nCS,
inout	wire	[3:0]	FLASH_IO,//----------------user interface---------------------
//wr FLASH
input	wire			WR_req,				//Page Programminginput	wire	[31:0]	WR_addr,			//起始编程地址,对于S25FL256S,可用地址为0~1FFFFFF(25bit)
input	wire	[9:0]	WR_Byte_Len,		//编程字节数,单次只能在一个Page里进行写入(256Byte Programming Buffer Size)
// 最好一次写一个完整的Page(低8位地址为0,Len=256)output	wire			data_rd_clk,		//读wFIFO的时钟
output	wire			data_rden,			//读wFIFO的使能信号
input	wire	[7:0]	data_PP,			//从wFIFO读到的数据,将写入FLASH//rd FLASH
input	wire			RD_req,
input	wire	[1:0]	LC,					//LC bits, CR1[7:6]input	wire	[31:0]	RD_addr,			//起始读取地址
input	wire	[31:0]	RD_Byte_Len,		//读取字节数output	wire			data_wr_clk,		//写rFIFO的clk
output	wire			data_wren,			//写rFIFO的使能信号
output	wire	[7:0]	data_4QOR,			//从FLASH读到的数据//WREN/WRDI/CLSR/RESET
input	wire			WREN_req,			//置位WEL bit
input	wire			WRDI_req,			//复位WEL bit
input	wire			CLSR_req,			//清空SR1,只复位P_ERR、E_ERR这两个bit
input	wire			RESET_req,			//软复位//erase
input	wire			bulk_erase_req,		//批量擦除input	wire			sector_erase_req,	//Sector擦除,一次擦除一个标准Sector(64KB)
input	wire	[31:0]	sector_erase_addr,	//低16位直接置零即可//RD SR1/CR1/SR2/BAR/ABR
input	wire			rd_SR1_req,			//Status Register 1
output	reg		[7:0]	SR1_rd,input	wire			rd_CR1_req,			//Configuration Register
output	reg		[7:0]	CR1_rd,input	wire			rd_SR2_req,			//Status Register 2
output	reg		[7:0]	SR2_rd,input	wire			rd_BAR_req,			//Bank Address Register
output	reg		[7:0]	BAR_rd,input	wire			rd_ABR_req,			//Autoboot Register
output	reg		[31:0]	ABR_rd,//WR SR1/CR1/BAR/ABR
input	wire			wr_SR1_req,			//发起WR_SR1只需要给入SR1
input	wire			wr_CR1_req,			//发起WR_CR1请求时,要同时给入SR1、CR1两个值
input	wire	[7:0]	SR1_wr,
input	wire	[7:0]	CR1_wr,input	wire			wr_BAR_req,
input	wire	[7:0]	BAR_wr,input	wire			wr_ABR_req,
input	wire	[31:0]	ABR_wr,output	reg				busy,//debug
output	reg		[3:0]	link,
output	reg		[3:0]	FLASH_IO_OBUF,
output	wire	[3:0]	FLASH_IO_IBUF,
output	reg		[23:0]	state
);
//注意,为避免操作冲突,所有req信号请最多同时启用一个(本模块已经做了优先编码)
//所有req高电平有效,请发起req后检测busy,若busy=H,则置低req,避免重复读写
//所有req均应在busy=L时才可发起//---------------------------------COMMAND----------------------------------------
localparam	I_WREN	= 8'h06;		//置位WEL
localparam	I_WRDI	= 8'h04;		//复位WEL
localparam	I_CLSR	= 8'h30;		//复位P_ERR、E_ERR
localparam	I_RESET	= 8'hF0;		//软复位localparam	I_WRR	= 8'h01;		//写SR1、CR1
localparam	I_RDSR1	= 8'h05;		//读SR1
localparam	I_RDSR2	= 8'h07;		//读SR2
localparam	I_RDCR1	= 8'h35;		//读CR1localparam	I_RDABR	= 8'h14;		//读Autoboot Register
localparam	I_WRABR	= 8'h15;		//写ABRlocalparam	I_RDBAR	= 8'h16;		//读Bank Address Register
localparam	I_WRBAR	= 8'h17;		//写BARlocalparam	I_BE	= 8'h60;		//bulk erase
localparam	I_SE	= 8'hDC;		//4SE,Erase 64KB Sector (4-byte address)localparam	I_4QPP	= 8'h34;		//Quad Page Programming (4-byte address)
localparam	I_4QOR	= 8'h6C;		//Quad Output Read (4-byte address)
//4QPP、4QOR的指令码在子模块里写好了,这里只是罗列一下,除此之外的指令码都在本模块内用到//----------------------------------SPI x4----------------------------------------
reg		[3:0]	link			= 4'h0;
reg		[3:0]	FLASH_IO_OBUF	= 4'hf;
wire	[3:0]	FLASH_IO_IBUF;genvar i;
generatefor(i=0; i<4; i=i+1) beginIOBUF IOBUF_FLASH_IO(				//IOBUF由一个IBUF和一个OBUF组成,.O		(FLASH_IO_IBUF[i]),		//O为IBUF的输出.IO		(FLASH_IO[i]),			//IO为OBUF的输出、IBUF的输入.I		(FLASH_IO_OBUF[i]),		//I为OBUF的输入.T		(~link[i])				//T为OBUF的三态门使能,低电平有效);end
endgenerateassign	FLASH_IO_IBUF1	= FLASH_IO_IBUF;
assign	FLASH_IO_IBUF2	= FLASH_IO_IBUF;
assign	FLASH_IO_IBUF3	= FLASH_IO_IBUF;
assign	FLASH_IO_IBUF4	= FLASH_IO_IBUF;
assign	FLASH_IO_IBUF5	= FLASH_IO_IBUF;//********重要**********
//注意,每个FPGA管脚上都要有IBUF、OBUF或IOBUF,input/output管脚上IBUF/OBUF会自动生成,
//而inout管脚需要用户编写,要么用IOBUF,要么直接用 link? xx_OBUF : 1'bz 这种形式(其实后者也是生成了一个OBUF和一个IBUF)
//对于每个FPGA管脚,只能由一个OBUF驱动,因此如果多个子模块要用inout操作同一根线,会出问题
//(这种情况下vivado会自动生成IBUF,导致模块大部分逻辑无效化,进而在综合后整个模块被优化掉,
//  即使强制关闭IBUF/OBUF自动插入功能,也会因为多个OBUF驱动同一管脚而综合失败)
//因此子模块不能再保有inout,而是通过操作顶层模块的IOBUF实现数据读写
//**********************//--------------------------------几个子模块--------------------------------------
//---------------单条8bit指令发送模块---------------
wire			FLASH_SCK_1;
wire			FLASH_nCS_1;wire	[3:0]	link1;
wire	[3:0]	FLASH_IO_OBUF1;
wire	[3:0]	FLASH_IO_IBUF1;reg				start_1;
reg		[7:0]	instruction_1;
wire			busy_1;flash_instruction flash_instruction_inst(.clk			(clk),.rst_n			(rst_n),.FLASH_SCK		(FLASH_SCK_1),.FLASH_nCS		(FLASH_nCS_1),.link			(link1),.FLASH_IO_OBUF	(FLASH_IO_OBUF1),.FLASH_IO_IBUF	(FLASH_IO_IBUF1),//usr interface.send_en		(start_1),.instruction	(instruction_1),.busy			(busy_1)
);//-------------写寄存器指令,支持1~4Byte-------------
wire			FLASH_SCK_2;
wire			FLASH_nCS_2;wire	[3:0]	link2;
wire	[3:0]	FLASH_IO_OBUF2;
wire	[3:0]	FLASH_IO_IBUF2;reg				start_2;
reg		[7:0]	instruction_2;
wire			busy_2;reg		[3:0]	Register_Len_WRR;
reg		[7:0]	WRR_Byte1, WRR_Byte2, WRR_Byte3, WRR_Byte4;flash_WRR flash_WRR_inst(.clk			(clk),.rst_n			(rst_n),.FLASH_SCK		(FLASH_SCK_2),.FLASH_nCS		(FLASH_nCS_2),.link			(link2),.FLASH_IO_OBUF	(FLASH_IO_OBUF2),.FLASH_IO_IBUF	(FLASH_IO_IBUF2),//usr interface.send_en		(start_2),.instruction	(instruction_2),.Register_Len	(Register_Len_WRR),.Byte1			(WRR_Byte1),.Byte2			(WRR_Byte2),.Byte3			(WRR_Byte3),.Byte4			(WRR_Byte4),.busy			(busy_2)
);//------------------读寄存器------------------
wire			FLASH_SCK_3;
wire			FLASH_nCS_3;wire	[3:0]	link3;
wire	[3:0]	FLASH_IO_OBUF3;
wire	[3:0]	FLASH_IO_IBUF3;reg				start_3;
reg		[7:0]	instruction_3;
wire			busy_3;reg		[3:0]	Register_Len_RDR;
wire	[31:0]	RDR_Reg;flash_RDR flash_RDR_inst(.clk			(clk),.rst_n			(rst_n),.FLASH_SCK		(FLASH_SCK_3),.FLASH_nCS		(FLASH_nCS_3),.link			(link3),.FLASH_IO_OBUF	(FLASH_IO_OBUF3),.FLASH_IO_IBUF	(FLASH_IO_IBUF3),//usr interface.read_en		(start_3),.instruction	(instruction_3),.Register_Len	(Register_Len_RDR),.Reg			(RDR_Reg),.busy			(busy_3)
);//---------------Page Programming---------------
wire			FLASH_SCK_4;
wire			FLASH_nCS_4;wire	[3:0]	link4;
wire	[3:0]	FLASH_IO_OBUF4;
wire	[3:0]	FLASH_IO_IBUF4;reg				start_4;
wire			busy_4;reg		[31:0]	addr_PP;
reg		[9:0]	Byte_Len_PP;wire			data_rd_clk;
wire			data_rden;
wire	[7:0]	data_PP;flash_4QPP flash_4QPP_inst(.clk			(clk),.rst_n			(rst_n),.FLASH_SCK		(FLASH_SCK_4),.FLASH_nCS		(FLASH_nCS_4),.link			(link4),.FLASH_IO_OBUF	(FLASH_IO_OBUF4),.FLASH_IO_IBUF	(FLASH_IO_IBUF4),//usr interface.program_start	(start_4),.addr			(addr_PP),.Byte_Len		(Byte_Len_PP),.data_rd_clk	(data_rd_clk),	//读wFIFO,将数据写入FLASH.data_rden		(data_rden),.data			(data_PP),		//从wFIFO读到的数据.busy			(busy_4)
);//-------------------read flash-------------------
wire			FLASH_SCK_5;
wire			FLASH_nCS_5;wire	[3:0]	link5;
wire	[3:0]	FLASH_IO_OBUF5;
wire	[3:0]	FLASH_IO_IBUF5;reg				start_5;
wire			busy_5;reg		[31:0]	addr_4QOR;
reg		[31:0]	Byte_Len_4QOR;wire			data_wr_clk;
wire			data_wren;
wire	[7:0]	data_4QOR;wire	[1:0]	LC;flash_4QOR flash_4QOR_inst(.clk			(clk),.rst_n			(rst_n),.FLASH_SCK		(FLASH_SCK_5),.FLASH_nCS		(FLASH_nCS_5),.link			(link5),.FLASH_IO_OBUF	(FLASH_IO_OBUF5),.FLASH_IO_IBUF	(FLASH_IO_IBUF5),//usr interface.read_start		(start_5),.addr			(addr_4QOR),.Byte_Len		(Byte_Len_4QOR),.data_wr_clk	(data_wr_clk),	//读FLASH并将数据写入rFIFO.data_wren		(data_wren),.data			(data_4QOR),	//写到rFIFO的数据.busy			(busy_5),//LC.LC				(LC)			//LC bit(CR1[7:6])
);//--------------------------------通道仲裁--------------------------------------
localparam	M_NONE			= 8'h01;
localparam	M_instruction	= 8'h02;
localparam	M_WRR			= 8'h04;
localparam	M_RDR			= 8'h08;
localparam	M_PP			= 8'h10;
localparam	M_4QOR			= 8'h20;reg		[7:0]	module_arb	= M_NONE;
reg				submodule_busy;always @(*) begincase(module_arb)M_NONE: beginsubmodule_busy	<= 1'b0;FLASH_SCK		<= 1'b1;FLASH_nCS		<= 1'b1;link			<= 4'h0;FLASH_IO_OBUF	<= 4'hf;endM_instruction: beginsubmodule_busy	<= busy_1;FLASH_SCK		<= FLASH_SCK_1;FLASH_nCS		<= FLASH_nCS_1;link			<= link1;FLASH_IO_OBUF	<= FLASH_IO_OBUF1;endM_WRR: beginsubmodule_busy	<= busy_2;FLASH_SCK		<= FLASH_SCK_2;FLASH_nCS		<= FLASH_nCS_2;link			<= link2;FLASH_IO_OBUF	<= FLASH_IO_OBUF2;endM_RDR: beginsubmodule_busy	<= busy_3;FLASH_SCK		<= FLASH_SCK_3;FLASH_nCS		<= FLASH_nCS_3;link			<= link3;FLASH_IO_OBUF	<= FLASH_IO_OBUF3;endM_PP: beginsubmodule_busy	<= busy_4;FLASH_SCK		<= FLASH_SCK_4;FLASH_nCS		<= FLASH_nCS_4;link			<= link4;FLASH_IO_OBUF	<= FLASH_IO_OBUF4;endM_4QOR: beginsubmodule_busy	<= busy_5;FLASH_SCK		<= FLASH_SCK_5;FLASH_nCS		<= FLASH_nCS_5;link			<= link5;FLASH_IO_OBUF	<= FLASH_IO_OBUF5;enddefault: beginsubmodule_busy	<= 1'b0;FLASH_SCK		<= 1'b1;FLASH_nCS		<= 1'b1;link			<= 4'h0;FLASH_IO_OBUF	<= 4'hf;endendcase
end//----------------------------------FSM----------------------------------------
localparam	S_IDLE		= 24'h000001;
localparam	S_ARB		= 24'h000002;		//仲裁对哪一个req进行响应
localparam	S_WAIT		= 24'h000004;		//等待子模块工作完成
localparam	S_STOP		= 24'h000008;localparam	S_WREN		= 24'h000010;		//执行WREN指令,置位WEL bit
localparam	S_WRDI		= 24'h000020;		//执行WRDI指令,复位WEL bit
localparam	S_CLSR		= 24'h000040;		//执行CLSR,复位P_ERR、E_ERR bit
localparam	S_BE		= 24'h000080;		//Bulk Eraselocalparam	S_WRSR1		= 24'h000100;		//写Status Register 1
localparam	S_WRCR1		= 24'h000200;		//写Configurate Register 1
localparam	S_WRBAR		= 24'h000400;		//写Bank Address Register
localparam	S_WRABR		= 24'h000800;		//写Autoboot Register
localparam	S_SE		= 24'h001000;		//Sector Eraselocalparam	S_RDSR1		= 24'h002000;		//读SR1
localparam	S_RDSR2		= 24'h004000;		//读SR2
localparam	S_RDCR1		= 24'h008000;		//读CR1
localparam	S_RDBAR		= 24'h010000;		//读Bank Address Register
localparam	S_RDABR		= 24'h020000;		//读Autoboot Registerlocalparam	S_4QPP		= 24'h040000;		//Page Programming
localparam	S_4QOR		= 24'h080000;		//Quad Output Readlocalparam	S_RESET		= 24'h100000;		//flash software resetreg		[23:0]	state	= S_IDLE;
reg		[23:0]	next_state;always @(posedge clk or negedge rst_n) beginif(~rst_n) beginstate	<= S_IDLE;endelse beginstate	<= next_state;end
endwire	[16:0]	all_req;
reg		[16:0]	all_req_buf;
assign	all_req	= {RESET_req, WREN_req, WRDI_req, CLSR_req, bulk_erase_req, sector_erase_req,rd_SR1_req, rd_CR1_req, rd_SR2_req, rd_BAR_req, rd_ABR_req,wr_SR1_req, wr_CR1_req, wr_BAR_req, wr_ABR_req,WR_req, RD_req};always @(posedge clk) beginall_req_buf		<= all_req;
endalways @(*) begincase(state)S_IDLE: beginnext_state	<= S_ARB;endS_ARB: begincasex(all_req_buf)17'b1_xxxx_xxxx_xxxx_xxxx: next_state	<= S_RESET;17'b0_1xxx_xxxx_xxxx_xxxx: next_state	<= S_WREN;17'b0_01xx_xxxx_xxxx_xxxx: next_state	<= S_WRDI;17'b0_001x_xxxx_xxxx_xxxx: next_state	<= S_CLSR;17'b0_0001_xxxx_xxxx_xxxx: next_state	<= S_BE;17'b0_0000_1xxx_xxxx_xxxx: next_state	<= S_SE;17'b0_0000_01xx_xxxx_xxxx: next_state	<= S_RDSR1;17'b0_0000_001x_xxxx_xxxx: next_state	<= S_RDCR1;17'b0_0000_0001_xxxx_xxxx: next_state	<= S_RDSR2;17'b0_0000_0000_1xxx_xxxx: next_state	<= S_RDBAR;17'b0_0000_0000_01xx_xxxx: next_state	<= S_RDABR;17'b0_0000_0000_001x_xxxx: next_state	<= S_WRSR1;17'b0_0000_0000_0001_xxxx: next_state	<= S_WRCR1;17'b0_0000_0000_0000_1xxx: next_state	<= S_WRBAR;17'b0_0000_0000_0000_01xx: next_state	<= S_WRABR;17'b0_0000_0000_0000_001x: next_state	<= S_4QPP;17'b0_0000_0000_0000_0001: next_state	<= S_4QOR;default: next_state	<= S_ARB;endcaseendS_RESET, S_WREN, S_WRDI, S_CLSR, S_BE, S_SE,S_RDSR1, S_RDCR1, S_RDSR2, S_RDBAR, S_RDABR,S_WRSR1, S_WRCR1, S_WRBAR, S_WRABR,S_4QPP, S_4QOR: beginif(submodule_busy) beginnext_state	<= S_WAIT;endelse beginnext_state	<= state;endendS_WAIT: beginif(~submodule_busy) beginnext_state	<= S_STOP;endelse beginnext_state	<= S_WAIT;endendS_STOP: beginnext_state	<= S_IDLE;enddefault: beginnext_state	<= S_IDLE;endendcase
endreg		[3:0]	update_register	= 4'd0;		//在RD REG操作中判断要更新哪一个Reg
//1:SR1, 2:CR1, 3:SR2, 4:BAR, 5:ABRalways @(posedge clk) begincase(state)S_IDLE: beginmodule_arb			<= M_NONE;start_1				<= 1'b0;start_2				<= 1'b0;start_3				<= 1'b0;start_4				<= 1'b0;start_5				<= 1'b0;update_register		<= 4'd0;endS_ARB: beginmodule_arb			<= M_NONE;start_1				<= 1'b0;start_2				<= 1'b0;start_3				<= 1'b0;start_4				<= 1'b0;start_5				<= 1'b0;endS_RESET: beginmodule_arb			<= M_instruction;start_1				<= 1'b1;instruction_1		<= I_RESET;endS_WREN: beginmodule_arb			<= M_instruction;start_1				<= 1'b1;instruction_1		<= I_WREN;endS_WRDI: beginmodule_arb			<= M_instruction;start_1				<= 1'b1;instruction_1		<= I_WRDI;endS_CLSR: beginmodule_arb			<= M_instruction;start_1				<= 1'b1;instruction_1		<= I_CLSR;endS_BE: beginmodule_arb			<= M_instruction;start_1				<= 1'b1;instruction_1		<= I_BE;endS_SE: beginmodule_arb			<= M_WRR;start_2				<= 1'b1;instruction_2		<= I_SE;Register_Len_WRR	<= 4'd4;WRR_Byte1			<= sector_erase_addr[31:24];WRR_Byte2			<= sector_erase_addr[23:16];WRR_Byte3			<= sector_erase_addr[15:8];WRR_Byte4			<= sector_erase_addr[7:0];endS_RDSR1: beginmodule_arb			<= M_RDR;start_3				<= 1'b1;instruction_3		<= I_RDSR1;Register_Len_RDR	<= 4'd1;update_register		<= 4'd1;endS_RDCR1: beginmodule_arb			<= M_RDR;start_3				<= 1'b1;instruction_3		<= I_RDCR1;Register_Len_RDR	<= 4'd1;update_register		<= 4'd2;endS_RDSR2: beginmodule_arb			<= M_RDR;start_3				<= 1'b1;instruction_3		<= I_RDSR2;Register_Len_RDR	<= 4'd1;update_register		<= 4'd3;endS_RDBAR: beginmodule_arb			<= M_RDR;start_3				<= 1'b1;instruction_3		<= I_RDBAR;Register_Len_RDR	<= 4'd1;update_register		<= 4'd4;endS_RDABR: beginmodule_arb			<= M_RDR;start_3				<= 1'b1;instruction_3		<= I_RDABR;Register_Len_RDR	<= 4'd4;update_register		<= 4'd5;endS_WRSR1: beginmodule_arb			<= M_WRR;start_2				<= 1'b1;instruction_2		<= I_WRR;Register_Len_WRR	<= 4'd1;WRR_Byte1			<= SR1_wr;WRR_Byte2			<= 8'd0;WRR_Byte3			<= 8'd0;WRR_Byte4			<= 8'd0;endS_WRCR1: beginmodule_arb			<= M_WRR;start_2				<= 1'b1;instruction_2		<= I_WRR;Register_Len_WRR	<= 4'd2;WRR_Byte1			<= SR1_wr;WRR_Byte2			<= CR1_wr;WRR_Byte3			<= 8'd0;WRR_Byte4			<= 8'd0;endS_WRBAR: beginmodule_arb			<= M_WRR;start_2				<= 1'b1;instruction_2		<= I_WRBAR;Register_Len_WRR	<= 4'd1;WRR_Byte1			<= BAR_wr;WRR_Byte2			<= 8'd0;WRR_Byte3			<= 8'd0;WRR_Byte4			<= 8'd0;endS_WRABR: beginmodule_arb			<= M_WRR;start_2				<= 1'b1;instruction_2		<= I_WRABR;Register_Len_WRR	<= 4'd4;WRR_Byte1			<= ABR_wr[31:24];WRR_Byte2			<= ABR_wr[23:16];WRR_Byte3			<= ABR_wr[15:8];WRR_Byte4			<= ABR_wr[7:0];endS_4QPP: beginmodule_arb		<= M_PP;start_4			<= 1'b1;addr_PP			<= WR_addr;Byte_Len_PP		<= WR_Byte_Len;endS_4QOR: beginmodule_arb		<= M_4QOR;start_5			<= 1'b1;addr_4QOR		<= RD_addr;Byte_Len_4QOR	<= RD_Byte_Len;endS_WAIT: beginstart_1			<= 1'b0;start_2			<= 1'b0;start_3			<= 1'b0;start_4			<= 1'b0;start_5			<= 1'b0;endS_STOP: beginmodule_arb		<= M_NONE;case(update_register)4'd1: SR1_rd	<= RDR_Reg[7:0];4'd2: CR1_rd	<= RDR_Reg[7:0];4'd3: SR2_rd	<= RDR_Reg[7:0];4'd4: BAR_rd	<= RDR_Reg[7:0];4'd5: ABR_rd	<= RDR_Reg;default: ;endcaseenddefault: beginmodule_arb		<= M_NONE;start_1			<= 1'b0;start_2			<= 1'b0;start_3			<= 1'b0;start_4			<= 1'b0;start_5			<= 1'b0;endendcase
endalways @(*) begincase(state)S_IDLE, S_ARB: beginbusy	<= 1'b0;enddefault: beginbusy	<= 1'b1;endendcase
endendmodule

测试

  编写测试代码如下,并下载到板子进行测试(注意,我的板子上的 FLASH 的 QUAD bit(CR1[1])已经被置位了,所以这里只执行了擦除、写入、读取流程,如果你的不是,需要多加一个 WRR 步骤)

// FLASH 测试(主存读写测试)
`default_nettype none
module test_flash_mainMemory(
input	wire			clk_sys,	//OXCO_10Moutput	wire			FLASH_nCS,
inout	wire	[3:0]	FLASH_IO,input	wire	[3:0]	Key,
output	wire	[3:0]	LED
);wire 	clk_100M;
wire 	clk_flash;
wire	clk_1k;
wire	clk_1Hz;reg		rst_n	= 1'b1;clk_wiz_0 clk_wiz(.clk_in1   (clk_sys),.clk_out1  (clk_100M),    .reset     (1'b0), .locked    ()
);clkdiv #(.N(3))
clkdiv_flash(.clk_in		(clk_100M),.clk_out	(clk_flash)		//测试发现50M下寄存器写操作可能出现错误,因此降为33M
);clkdiv #(.N(1000_00))
clkdiv_1k(.clk_in		(clk_100M),.clk_out	(clk_1k)
);clkdiv #(.N(100_000_000))
clkdiv_1Hz(.clk_in		(clk_100M),.clk_out	(clk_1Hz)
);wire	usrdone;
set_CCLK set_CCLK_inst(.usrcclk	(FLASH_SCK),.usrdone	(usrdone),.cfgclk		(),.cfgmclk	(),.eos		()
);assign	usrdone	= clk_1Hz;//-------------------------------------FLASH------------------------------------------------------
wire			FLASH_SCK;
wire			FLASH_nCS;
wire	[3:0]	FLASH_IO;//wr FLASH
reg				WR_req	= 1'b0;				//Page Programmingreg		[31:0]	WR_addr	= 32'd0;			//起始编程地址,对于S25FL256S,可用地址为0~1FFFFFF(25bit)
reg		[9:0]	WR_Byte_Len	= 10'd1;		//编程字节数wire			data_rd_clk;				//读wFIFO的时钟
wire			data_rden;					//读wFIFO的使能信号
reg		[7:0]	data_PP		= 8'd0;			//从wFIFO读到的数据,将写入FLASH//rd FLASH
reg				RD_req	= 1'b0;
reg		[1:0]	LC		= 2'b00;			//LC bits, CR1[7:6]reg		[31:0]	RD_addr	= 32'd0;			//起始读取地址
reg		[31:0]	RD_Byte_Len	= 32'd1;		//读取字节数wire			data_wr_clk;				//写rFIFO的clk
wire			data_wren;					//写rFIFO的使能信号
wire	[7:0]	data_4QOR;					//从FLASH读到的数据//WREN/WRDI/CLSR/RESET
reg				WREN_req	= 1'b0;			//置位WEL bit
reg				WRDI_req	= 1'b0;			//复位WEL bit
reg				CLSR_req	= 1'b0;			//清空SR1,只复位P_ERR、E_ERR这两个bit
reg				RESET_req	= 1'b0;			//软复位//erase
reg				bulk_erase_req	= 1'b0;		//批量擦除reg				sector_erase_req	= 1'b0;		//Sector擦除,一次擦除一个标准Sector(64KB)
reg		[31:0]	sector_erase_addr	= 32'd0;	//低16位直接置零即可//RD SR1/CR1/SR2/BAR/ABR
reg				rd_SR1_req	= 1'b0;			//Status Register 1
wire	[7:0]	SR1_rd;reg				rd_CR1_req	= 1'b0;			//Configuration Register
wire	[7:0]	CR1_rd;reg				rd_SR2_req	= 1'b0;			//Status Register 2
wire	[7:0]	SR2_rd;reg				rd_BAR_req	= 1'b0;			//Bank Address Register
wire	[7:0]	BAR_rd;reg				rd_ABR_req	= 1'b0;			//Autoboot Register
wire	[31:0]	ABR_rd;//WR SR1/CR1/BAR/ABR
reg				wr_SR1_req	= 1'b0;			//发起WR_SR1只需要给入SR1
reg				wr_CR1_req	= 1'b0;			//发起WR_CR1请求时,要同时给入SR1、CR1两个值
reg		[7:0]	SR1_wr		= 8'd0;
reg		[7:0]	CR1_wr;reg				wr_BAR_req	= 1'b0;
reg		[7:0]	BAR_wr;reg				wr_ABR_req	= 1'b0;
reg		[31:0]	ABR_wr;wire			busy;FLASH_top FLASH_top_inst(.clk				(clk_flash),.rst_n				(rst_n),.FLASH_SCK			(FLASH_SCK),.FLASH_nCS			(FLASH_nCS),.FLASH_IO			(FLASH_IO),//----------------user interface---------------------//wr FLASH.WR_req				(WR_req),.WR_addr			(WR_addr),.WR_Byte_Len		(WR_Byte_Len),.data_rd_clk		(data_rd_clk),.data_rden			(data_rden),.data_PP			(data_PP),//rd FLASH.RD_req				(RD_req),.LC					(LC),.RD_addr			(RD_addr),.RD_Byte_Len		(RD_Byte_Len),.data_wr_clk		(data_wr_clk),.data_wren			(data_wren),.data_4QOR			(data_4QOR),//WREN/WRDI/CLSR/RESET.WREN_req			(WREN_req),.WRDI_req			(WRDI_req),.CLSR_req			(CLSR_req),.RESET_req			(RESET_req),//erase.bulk_erase_req		(bulk_erase_req),.sector_erase_req	(sector_erase_req),.sector_erase_addr	(sector_erase_addr),//RD SR1/CR1/SR2/BAR/ABR.rd_SR1_req			(rd_SR1_req),.SR1_rd				(SR1_rd),.rd_CR1_req			(rd_CR1_req),.CR1_rd				(CR1_rd),.rd_SR2_req			(rd_SR2_req),.SR2_rd				(SR2_rd),.rd_BAR_req			(rd_BAR_req),.BAR_rd				(BAR_rd),.rd_ABR_req			(rd_ABR_req),.ABR_rd				(ABR_rd),//WR SR1/CR1/BAR/ABR.wr_SR1_req			(wr_SR1_req),.wr_CR1_req			(wr_CR1_req),.SR1_wr				(SR1_wr),.CR1_wr				(CR1_wr),.wr_BAR_req			(wr_BAR_req),.BAR_wr				(BAR_wr),.wr_ABR_req			(wr_ABR_req),.ABR_wr				(ABR_wr),.busy				(busy),//debug.link				(link),.FLASH_IO_OBUF		(FLASH_IO_OBUF),.FLASH_IO_IBUF		(FLASH_IO_IBUF),.state				(state)
);//debug
wire	[3:0]	link;
wire	[3:0]	FLASH_IO_OBUF;
wire	[3:0]	FLASH_IO_IBUF;
wire	[23:0]	state;//-----------------------------test------------------------------------
wire	PPS_pe;
reg		PPS_d0;
reg		PPS_d1;reg		PPS_pe_d1;
reg		PPS_pe_d2;assign	PPS_pe	= PPS_d0 & (~PPS_d1);reg		[7:0]	cnt	= 8'd0;always @(posedge clk_flash) beginPPS_d0		<= clk_1k;PPS_d1		<= PPS_d0;if(PPS_pe) beginif(cnt==1 || cnt==11) beginif(SR1_rd[1]) begin		//检查WELcnt		<= cnt + 1'b1;endelse begincnt		<= cnt;endendelse if(cnt==3 || cnt==13) beginif(~SR1_rd[0]) begin	//检查WIPcnt		<= cnt + 1'b1;endelse begincnt		<= cnt;endendelse begincnt		<= cnt + 1'b1;endendPPS_pe_d1	<= PPS_pe;PPS_pe_d2	<= PPS_pe_d1;
endlocalparam	WR_RD_ADDR	= 32'h0100_0000;reg		[7:0]	data_PP_tmp	= 8'd0;
always @(posedge data_rd_clk) beginif(data_rden) begindata_PP_tmp		<= data_PP_tmp + 1'b1;endelse begindata_PP_tmp		<= data_PP_tmp;end
endalways @(posedge clk_100M) begincase(cnt)//---------------erase-------------------------8'd0: WREN_req		<= PPS_pe_d2;8'd1: rd_SR1_req	<= PPS_pe_d2;8'd2: beginsector_erase_req	<= PPS_pe_d2;sector_erase_addr	<= WR_RD_ADDR;end8'd3: rd_SR1_req	<= PPS_pe_d2;8'd4: rd_CR1_req	<= PPS_pe_d2;//------------wr main mem----------------------8'd10: WREN_req		<= PPS_pe_d2;8'd11: rd_SR1_req	<= PPS_pe_d2;8'd12: beginWR_req			<= PPS_pe_d2;WR_addr			<= WR_RD_ADDR;WR_Byte_Len		<= 10'd16;data_PP			<= data_PP_tmp;end8'd13: rd_SR1_req	<= PPS_pe_d2;//--------------get LC--------------------------8'd20: rd_CR1_req	<= PPS_pe_d2;8'd21: LC			<= CR1_rd[7:6];//------------rd main mem----------------------8'd30: beginRD_req			<= PPS_pe_d2;RD_addr			<= WR_RD_ADDR;RD_Byte_Len		<= 10'd16;enddefault: ;endcase
end//-----------------------------ILA------------------------------------
ila_test ila(.clk		(clk_100M),.probe0		(cnt),.probe1		(busy),.probe2		(FLASH_SCK),.probe3		(FLASH_nCS),.probe4		(link),.probe5		(FLASH_IO_IBUF),.probe6		(SR1_rd),.probe7		(CR1_rd),.probe8		(data_rd_clk),.probe9		(data_rden),.probe10	(data_PP),.probe11	(data_wr_clk),.probe12	(data_wren),.probe13	(data_4QOR)
);endmodule

  用户控制 CCLK 主要用到 STARTUPE2 原语,我这里封装为了一个代码模块,具体可看这篇博文

/* * file			: set_CCLK.v* author		: 今朝无言* Lab			: WHU-EIS-LMSWE* date			: 2023-11-02* version		: v1.0* description	: 使用原语设置CCLK* Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.*/
`default_nettype none
module set_CCLK(
input	wire	usrcclk,
input	wire	usrdone,output	wire	cfgclk,
output	wire	cfgmclk,
output	wire	eos
);//-------------------STARTUPE2---------------------
STARTUPE2 #(.PROG_USR		("FALSE"),.SIM_CCLK_FREQ	(0.0)
)
STARTUPE2_inst(.CFGCLK			(cfgclk),.CFGMCLK		(cfgmclk),.EOS			(eos),.PREQ			(),.CLK			(0),.GSR			(0),.GTS			(0),.KEYCLEARB		(1),.PACK			(1),.USRCCLKO		(usrcclk),.USRCCLKTS		(0),.USRDONEO		(usrdone),.USRDONETS		(0)
);endmodule

  在该测试代码中,循环向 FLASH 写入自增 1 的数据,然后观察从 FLASH 读取到的数据,如下

在这里插入图片描述

可以看到读取到正确的数据。

Something

  在测试 FLASH 读写中踩到了好多坑,主要是写入/擦除操作方面的(写寄存器、写主存、擦除等),记录如下:

  • WREN 操作后,WEL bit 不是立即置位的,如果执行 WREN 后立即执行写寄存器、擦除、写主存等操作,都会失败(这些操作都需要写使能位 WEL 为高才能执行)。精细测量发现在执行 WREN 后约 800us ,WEL 才被置位,且这个时间不是很固定,因此强烈建议在执行 WREN 后,周期检查 WEL bit,待 WEL=1 后再执行擦除、写入操作。

  • WRR 命令执行后,若只存在把某位(某些位)从 0 置 1 的操作,则执行非常快(小于 1ms);而如果存在把某些位从 1 置 0 的操作时,设备会陷入长时间的忙碌状态(WIP=1),测试表明约 383ms。若在 WIP=1 的状态执行新的写入、擦除操作时,这些指令都会被忽略。因此在执行 WRR 后也需要检查 WIP,待 WIP=0 后才能退回空闲状态。即写寄存器应当遵循 ‘WREN -> check WEL -> WR Reg -> check WIP -> return IDLE’ 的流程。

  • Erase、Page Program 等操作执行后时间也很长,也应当遵循 ‘WREN -> check WEL -> Erase/PP -> check WIP -> return IDLE’ 的流程。

(完)

相关文章:

S25FL系列FLASH读写的FPGA实现

文章目录 实现思路具体实现子模块实现top模块 测试Something 实现思路 建议读者先对 S25FL-S 系列 FLASH 进行了解&#xff0c;我之前的博文中有详细介绍。 笔者的芯片具体型号为 S25FL256SAGNFI00&#xff0c;存储容量 256Mb&#xff0c;增强高性能 EHPLC&#xff0c;4KB 与 6…...

一次【自定义编辑器功能脚本】【调用时内存爆仓】事故排查

一 、事故描述 我有一个需求&#xff1a;在工程文件中找得到所有的图片&#xff08;Texture 2D&#xff09;&#xff0c;然后把WebGL发布打包时的图片压缩规则进行修改。 项目中有图片2千多张&#xff0c;其中2k分辨率的图片上百张&#xff0c;当我右键进行批量处理的时候&…...

【STM32单片机】简易计算器设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用STM32F103C8T6单片机控制器&#xff0c;使用动态数码管模块、矩阵按键、蜂鸣器模块等。 主要功能&#xff1a; 系统运行后&#xff0c;数码管默认显示0&#xff0c;输入对应的操作数进行四则运…...

【详解二叉树】

&#x1f320;作者&#xff1a;TheMythWS. &#x1f387;座右铭&#xff1a;不走心的努力都是在敷衍自己&#xff0c;让自己所做的选择&#xff0c;熠熠发光。 目录 树形结构 概念 树的示意图 树的基本术语 树的表示 树的应用 二叉树(重点) 二叉树的定义 二叉树的五…...

【Amazon】在Amazon EKS集群中安装部署最小化KubeSphere容器平台

文章目录 一、准备工作二、部署 KubeSphere三、访问 KubeSphere 控制台四、安装Amazon EBS CSI 驱动程序4.1 集群IAM角色建立并赋予权限4.2 安装 Helm Kubernetes 包管理器4.3 安装Amazon EBS CSI 驱动程序 五、常见问题六、参考链接 一、准备工作 Kubernetes 版本必须为&…...

ubuntu20.04下安装标注工具CVAT

1 安装docker sudo apt-get update sudo apt-get --no-install-recommends install -y apt-transport-https ca-certificates \curl \gnupg-agent \software-properties-commoncurl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo add-apt-r…...

pytest-pytest-html测试报告这样做,学完能涨薪3k

在 pytest 中提供了生成html格式测试报告的插件 pytest-html 安装 安装命令如下&#xff1a; pip install pytest-html使用 我们已经知道执行用例的两种方式&#xff0c;pytest.main()执行和命令行执行&#xff0c;而要使用pytest-html生成报告&#xff0c;只需要在执行时加…...

本地运行“李开复”的零一万物 34B 大模型

这篇文章&#xff0c;我们来聊聊如何本地运行最近争议颇多的&#xff0c;李开复带队的国产大模型&#xff1a;零一万物 34B。 写在前面 零一万物的模型争议有很多&#xff0c;不论是在海外的社交媒体平台&#xff0c;还是在国内的知乎和一种科技媒体上&#xff0c;不论是针对…...

Redis-Redis缓存高可用集群

1、Redis集群方案比较 哨兵模式 在redis3.0以前的版本要实现集群一般是借助哨兵sentinel工具来监控master节点的状态&#xff0c;如果master节点异常&#xff0c;则会做主从切换&#xff0c;将某一台slave作为master&#xff0c;哨兵的配置略微复杂&#xff0c;并且性能和高可…...

Django之admin页面样式定制(Simpleui)

好久不见&#xff0c;各位it朋友们&#xff01; 本篇文章我将向各位介绍Django框架中admin后台页面样式定制的一个插件库&#xff0c;名为Simpleui。 一&#xff09;简介 SimpleUI是一款简单易用的用户界面&#xff08;UI&#xff09;库&#xff0c;旨在帮助开发人员快速构建…...

TypeScript 中的type与interface

TypeScript 中的type与interface 对于 TypeScript&#xff0c;有两种定义类型的方法&#xff1a;type与interface。 人们常常想知道该使用哪一种&#xff0c;而答案并不是一刀切的。这确实取决于具体情况。有时&#xff0c;其中一种比另一种更好&#xff0c;但在许多情况下&a…...

【uniapp】uniapp开发小程序定制uni-collapse(折叠面板)

需求 最近在做小程序&#xff0c;有一个类似折叠面板的ui控件&#xff0c;效果大概是这样 代码 因为项目使用的是uniapp&#xff0c;所以打算去找uniapp的扩展组件&#xff0c;果然给我找到了这个叫uni-collapse的组件&#xff08;链接&#xff1a;uni-collapse&#xff09…...

单片机学习7——定时器/计数器编程

#include<reg52.h>unsigned char a, num; sbit LED1 P1^0;void main() {num0;EA1;ET01;//IT00;//设置TMOD的工作模式TMOD0x01;//给定时器装初值&#xff0c;50000,50ms中断20次&#xff0c;就得到1sTH0(65536-50000)/256;TL0(65536-50000)%256;TR01; // 定时器/计数器启…...

OpenWrt Lan口上网设置

LAN口上网设置 连接上openwrt&#xff0c;我用的 倍控N5105&#xff0c;eth0&#xff0c;看到Openwrt的IP是10.0.0.1 在 网络 -> 网口配置 -> 设置好 WAN 口和 LAN 口 初次使用经常重置 openwrt 所以我设置的是 静态IP模式 - 网络 -> 防火墙 -> 常规设置 ->…...

监控同一局域网内其它主机上网访问信息

1.先取得网关IP 2.安装IPTABLES路由表 sudo apt-get install iptables 3.启用IP转发 sudo sysctl -p 查看配置是否生效 4.配置路由 iptables -t nat -A POSTROUTING -j MASQUERADE 配置成功后,使用sudo iptables-save查看...

DC cut 滤直流滤波器实现

在音频处理中&#xff0c;会无意中产生直流偏置&#xff0c;这个偏置如果通过功放去推喇叭&#xff0c;会对喇叭造成不可逆转的损坏&#xff0c;所以在实际应用中&#xff0c;会通过硬件(添加直流检测模块&#xff0c;如果有 使用继电器切断输出) 、软件(软件直流滤波算法)&…...

uni-app,nvue中text标签文本超出宽度不换行问题解决

复现&#xff1a;思路&#xff1a; 将text标签换为rich-text&#xff0c;并给rich-text增加换行的样式class类名解决&#xff1a;...

和鲸ModelWhale平台与海光人工智能加速卡系列完成适配认证,夯实 AI 应用核心底座

AIGC 浪潮席卷&#xff0c;以大模型为代表的人工智能发展呈现出技术创新快、应用渗透强、国际竞争激烈等特点。创新为本&#xff0c;落地为王&#xff0c;技术的快速发展与大规模训练需求的背后&#xff0c;是对平台化基础设施与 AI 算力的更高要求。在此全球 AI 产业竞争的风口…...

Flutter学习(四)如何取消listview的越界效果

背景 在flutter的开发过程中&#xff0c;ListView是很常见的一个组件&#xff0c;但是&#xff0c;由于ListView的某些自带的体验&#xff0c;导致不太好的用户体验。例如ListView中&#xff0c;滑动到顶部或者底部的时候&#xff0c;再次滑动&#xff0c;会有越界的效果&…...

system.setProperty导致的https血案

system.setProperty导致的https血案 现象排查思考建议 现象 系统外调签名服务突然无法使用&#xff0c;排查发起请求的服务正常&#xff0c;查看日志报recieve fatal alert: protocal_version, 当时大家没有深入研究代码&#xff0c;印象里最近没有动过服务&#xff0c;就网络…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

mongodb源码分析session执行handleRequest命令find过程

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令&#xff0c;把数据流转换成Message&#xff0c;状态转变流程是&#xff1a;State::Created 》 St…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...