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’ 的流程。

(完)

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

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

相关文章

Leetcode199. 二叉树的右视图

Every day a Leetcode 题目来源&#xff1a;199. 二叉树的右视图 解法1&#xff1a;层序遍历 给定一个二叉树的 根节点 root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 按层序遍历&#xff0c;将每层的…

如何判断一个题目用“贪心/动态规划“还是用“BFS/DFS”方法解决

1 总结 1.1 贪心、动态规划和BFS/DFS题解的关系 一般能使用贪心、动态规划解决一个问题时&#xff0c;使用BFS&#xff0c;DFS也能解决这个题&#xff0c;但是反之不能成立。 1.2 2 贪心 -> BFS/DFS 2.1 跳跃游戏1和3的异同 这两道题&#xff0c;“跳跃游戏”&#xf…

html实现各种瀑布流(附源码)

文章目录 1.设计来源1.1 动态响应瀑布流1.2 分页瀑布流1.3 响应瀑布流 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/134613121 html实现各种瀑布流(附源码)&#xff0c;…

100元预算,轻松涨粉1000!腾讯运营面试秘籍大揭秘!

大家好啊&#xff01;小米在这里&#xff5e; 很高兴又有机会和大家见面啦&#xff01;最近小米参加了一场腾讯的运营面试&#xff0c;遇到了一个超有趣的问题&#xff1a;如果让你运营一个公众号&#xff0c;近期需要增加1000个关注&#xff0c;预算100元&#xff0c;怎么完成…

【阿里云】图像识别 智能分类识别 项目开发(一)

语音模块和阿里云图像识别结合 环境准备 代码实现 编译运行 写个shell脚本用于杀死运行的进程 语音模块和阿里云图像识别结合 使用语音模块和摄像头在香橙派上做垃圾智能分类识别 语音控制摄像下载上传阿里云解析功能点实现 环境准备 将语音模块接在UART5的位置 在orange…

数据结构总复习

文章目录 线性表动态分配的顺序存储结构链式存储 线性表 动态分配的顺序存储结构 通过分析代码&#xff0c;我们发现&#xff0c;要注意什么&#xff1a; 要分清你的下标Insert 函数是可以用来没有元素的时候&#xff0c;增加元素的Init(或者Create )函数一般只用来分配空间…

go atexit源码分析

文章目录 atexit源码解析UML类图样例一: 程序退出之前执行注册函数1.1 流程图1.2 代码分析 样例二&#xff1a;使用cancel取消注册函数2.1 cancel流程图2.2 代码分析 样例三&#xff1a;使用Fatal/Fatalln/Fatal执行注册函数3.1 Fatal/Fatalln/Fatal流程图3.2 代码分析 atexit源…

Android平台GB28181设备接入模块开发填坑指南

技术背景 为什么要开发Android平台GB28181设备接入模块&#xff1f;这个问题不再赘述&#xff0c;在做Android平台GB28181客户端的时候&#xff0c;媒体数据这块&#xff0c;我们已经有了很好的积累&#xff0c;因为在此之前&#xff0c;我们就开发了非常成熟的RTMP推送、轻量…

Scannet v2 数据集介绍以及子集下载展示

Scannet v2 数据集介绍以及子集下载展示 文章目录 Scannet v2 数据集介绍以及子集下载展示参考数据集简介子集scannet_frames_25kscannet_frames_test 下载脚本 download_scannetv2.py 参考 scannet数据集简介和下载-CSDN博客 scannet v2 数据集下载_scannetv2数据集_蓝羽飞鸟的…

BeanUtil的正确使用方式

shigen日更文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 在实际的开发中&#xff0c;我们常常会用到工具类去拷贝对象的属性&#xff0c;将一个对象的属性转换成另外一个…

球面的表面积

此推导需要用到重积分的知识&#xff0c;另外关于曲面的面积公式可以看我之前的博客

百度AI布局:从财报看百度的核心竞争力和未来发展方向

百度是中国最大的搜索引擎&#xff0c;也是全球领先的人工智能&#xff08;AI&#xff09;公司。百度在2023年第三季度业绩中&#xff0c;展示了其在AI领域的强劲表现和广阔前景。 百度财报透露了关于AI业务的哪些重要信息&#xff1f; 百度在2023年第三季度的财报中&#xf…

机器学习:攻击方法FGSM系列

任务 FGSM I-FGSM MI-FGSM Ensemble Attack 攻击评价指标 准确率越低表明攻击越好 数据 预训练模型 BaseLine 实践

***Linux常用命令及解释

1、查看Linux的版本信息 1.1、uname -a 1.2、cat /etc/issue 1.3、cat /proc/version 1.4、hostnamectl 通过使用hostnamectl命令&#xff0c;可以查询和更改系统主机名&#xff0c;并且还可以查看Linux的发行版和内核版本。 2、删除文件 3、修改目录权限 4、解压文件 5、…

探索 Linux vim/vi 编辑器:介绍、模式以及基本操作演示

&#x1f490;作者&#xff1a;insist-- &#x1f490;个人主页&#xff1a;insist-- 的个人主页 理想主义的花&#xff0c;最终会盛开在浪漫主义的土壤里&#xff0c;我们的热情永远不会熄灭&#xff0c;在现实平凡中&#xff0c;我们终将上岸&#xff0c;阳光万里 ❤️欢迎点…

Spring的依赖注入,依赖注入的基本原则,依赖注入的优势

文章目录 Spring的依赖注入依赖注入的基本原则依赖注入有什么优势查找定位操作与应用代码完全无关。有哪些不同类型的依赖注入实现方式&#xff1f;构造器依赖注入和 Setter方法注入的区别 Spring的依赖注入 控制反转IoC是一个很大的概念&#xff0c;可以用不同的方式来实现。…

android系统新特性——用户界面以及系统界面改进

用户界面改进 Android用户界面改进最明显的就是MD了。MD是Google于2014年推出的设计语言&#xff0c;它是一套完整的设计系统&#xff0c;包含了动画、样式、布局、组件等一系列与设计有关的元素。通过对这些行为的描述&#xff0c;让开发者设计出更符合目标的软件&#xff0c…

vue3+elementPlus之侧边菜单栏功能

选择默认的颜色&#xff0c;将代码拷贝至<el-aside>模块中 稍微把不需要的修改一下。 <template><div class"common-layout"><el-container><el-header class"homeHeader"><div class"headerTitle">Devops…

vue怎么实现国际化? vue-i18n 插件实现国际化,支持切换不同语言

依赖的文档开始 | Vue I18n 一、安装 npm install vue-i18n 如果在一个模块系统中使用它&#xff0c;你必须通过 Vue.use() 明确地安装 vue-i18n&#xff1a; import Vue from vue import VueI18n from vue-i18nVue.use(VueI18n)二、使用 在 src 下创建 lang 文件夹 1.准…

实现【Linux--NTP 时间同步服务搭建】

实现【Linux--NTP 时间同步服务搭建】 &#x1f53b; 前言&#x1f53b; 一、NTP 校时&#x1f530; 1.1 NTP 服务校时与 ntpdate 校时的区别&#x1f530; 1.2 NTP 校时服务搭建&#x1f530; 1.2.1 确认 ntp 的安装&#x1f530; 1.2.2 配置 ntp 服务&#x1f530; 1.2.3 启动…