同步FIFO、异步FIFO详细介绍、verilog代码实现、FIFO最小深度计算、简答题

文章目录


前言

2023.4.6 把之前学习的内容汇总整理


一、多bit数据流跨时钟域传输——FIFO

大多数数据具有连续性,背靠背传输;需要较快的速度传输
两种解决方法:SRAM(两个时钟域通过其缓冲,这里不讲)、FIFO

1、FIFO分类

同步FIFO:读写时钟为同一时钟,时钟沿同时进行读写,内部所有逻辑为同步逻辑,常用于交互数据缓冲
异步FIFO:读写时钟不同,相互独立

2、常见参数

FIFO宽度:一次读写数据的数据位
FIFO深度:可以存储多少个N位的数据

空标志:FIFO已空或将要空时由FIFO的状态电路送出的一个信号,以阻止FIFO的读操作继续从FIFO中读出数据而造成无效数据的读出(underflow)。读写指针相等时,表示空,发生在复位或者读指针读完最后一个数追上写指针的时候。
满标志:FIFO已满或将要满时由FIFO的状态电路送出的一个信号,以阻止FIFO的写操作继续向FIFO中写数据而造成溢出(overflow)。当读写指针再次相等,表示满。

读时钟:读操作所遵循的时钟,在每个时钟沿来临时读数据。
写时钟:写操作所遵循的时钟,在每个时钟沿来临时写数据。

写指针:总是指向下一个将要被写入的单元,复位时,指向0
读指针:总是指向当前要被读出的数据,复位时,指向第 1 个单元(编号为 0)
在这里插入图片描述

3、与普通存储器的区别

  • 区别:没有外部读写地址线,使用简单

4、FIFO优缺点

  • 优点:适用性强,可以用来同步没有任何关系的时钟域间的数据交换,相比于握手协议数据传输速率更快,不同宽度的数据接口也可以用FIFO。
  • 缺点:只能顺序读写,且面积大

二、同步FIFO

1、计数器法

计数器复位时初始化为0,写操作计数器+1,读操作计数器-1,计数器为0表示空,计数器为深度时表示满。

缺点:读写不能同时操作,且计数器会占用额外的资源,当FIFO较大时,可能会降低读写的速度。

关键: 读写指针的变化、空满信号的产生

module sync_fifo
	#(parameter DEPTH = 'd16, parameter WIDTH = 'd8)(
	input clk,
	input rst_n,
	input wr_en,
	input rd_en,
	input [WIDTH-1:0] data_in,
	output [WIDTH-1:0] data_out,
	output empty,
	output full
);

	reg [$clog2(DEPTH):0] cnt;   //计数器范围为0-16
	reg [WIDTH-1:0] mem [DEPTH-1:0];
	reg [$clog2(DEPTH)-1:0] waddr, raddr;
	
	//写操作
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			waddr <= 0;
		else if(wr_en & !full)begin
			mem[waddr] <= data_in;
			waddr <= waddr+1;
		end
	end
	
	//读操作
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			raddr <= 0;
		else if(rd_en & !empty)begin
			data_out <= mem[raddr];
			raddr <= raddr+1;  //注意这里读地址也是+1
		end
	end
	
	//更新计数器
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			cnt <= 0;
		else begin
			case({wr_en, rd_en})
				2'b00: cnt <= cnt;
				2'b01: if(cnt!=0) cnt <= cnt - 1;
				2'b10: if(cnt!=DEPTH) cnt <= cnt + 1;
				2'b11: cnt <= cnt;
			endcase
		end
	end
	
	assign full = (cnt==DEPTH);
	assign empty = (cnt==0);
endmodule

2、高位扩展法

没有扩展的时候,空和满的时候,读写指针是完全相同的,无法进行判断

  • 地址位扩展一位,当写指针增加并越过最后一个FIFO地址时,就将写指针这个未用的MSB加1,其它位回零。对读指针也进行同样的操作。
  • 对于深度为2^n的FIFO,需要的读/写指针位宽为(n+1)位,如对于深度为8的FIFO,需要采用4bit的计数器,0000 ~1000、1001~1111,MSB作为折回标志位,而低3位作为地址指针
  • 如果两个指针的MSB不同,说明写指针比读指针多折回了一次;如r_addr=0000,而w_addr = 1000,为满。
  • 如果两个指针的MSB相同,则说明两个指针折回的次数相等。其余位相等,说明FIFO为空。
module sync_fifo
	#(parameter DEPTH = 'd16, parameter WIDTH = 'd8)(
	input clk,
	input rst_n,
	input wr_en,
	input rd_en,
	input [WIDTH-1:0] data_in,
	output [WIDTH-1:0] data_out,
	output empty,
	output full
);

	reg [WIDTH-1:0] mem [DEPTH-1:0];
	reg [$clog2(DEPTH):0] waddr, raddr;
	
	//写操作
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			waddr <= 0;
		else if(wr_en & !full)begin
			mem[waddr[$clog2(DEPTH)-1:0]] <= data_in;  
			waddr <= waddr+1;
		end
	end
	
	//读操作
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			raddr <= 0;
		else if(rd_en & !empty)begin
			data_out <= mem[raddr[$clog2(DEPTH)-1:0]];  //这里也可以用组合逻辑输出,实现read和data out同步输出
			raddr <= raddr+1;  //注意这里读地址也是+1
		end
	end

	assign empty = (raddr == waddr);
	assign full = (raddr == {~waddr[$clog2(DEPTH)], waddr[$clog2(DEPTH)-1:0]});   //最高位相反
endmodule

3、单端口和双端口RAM

3.1 单端口RAM

module ram_mod(
	input clk,
	input rst_n,
	
	input write_en,
	input [7:0]write_addr,
	input [3:0]write_data,
	
	input read_en,
	input [7:0]read_addr,
	output reg [3:0]read_data
);
    reg [3:0] mem [7:0];
    integer i;
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            for(i=0;i<8;i=i+1)
                mem[i]<=0;
        end
        else if(write_en)
            mem[write_addr]<=write_data;
    end
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            read_data<=0;
        end
        else if(read_en)
            read_data<=mem[read_addr];
    end
endmodule

3.2 双端口RAM

module dual_port_RAM #(parameter DEPTH = 16,
					   parameter WIDTH = 8)(
	 input wclk
	,input wenc
	,input [$clog2(DEPTH)-1:0] waddr  //深度对2取对数,得到地址的位宽。
	,input [WIDTH-1:0] wdata      	//数据写入
	,input rclk
	,input renc
	,input [$clog2(DEPTH)-1:0] raddr  //深度对2取对数,得到地址的位宽。
	,output reg [WIDTH-1:0] rdata 		//数据输出
);

	reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];
	
	always @(posedge wclk) begin
		if(wenc)
			RAM_MEM[waddr] <= wdata;
	end 
	
	always @(posedge rclk) begin
		if(renc)
			rdata <= RAM_MEM[raddr];
	end 
endmodule

4、例化双端口RAM实现同步FIFO

在这里插入图片描述

//这里用的是高位扩展法
module sfifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					clk		, 
	input 					rst_n	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output reg				wfull	,
	output reg				rempty	,
	output wire [WIDTH-1:0]	rdata
);

	wire wenc,renc;
	assign wenc = winc && !wfull;
	assign renc = rinc && !rempty;
	reg [$clog2(DEPTH):0] waddr;
	reg [$clog2(DEPTH):0] raddr;

	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			wfull<=0;
			rempty<=0;
		end
		else begin
			rempty<=(raddr==waddr);
			wfull<=(waddr==raddr+DEPTH);
		end
	end

	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			raddr<=0;
		end
		else if(renc)
			raddr<=raddr+1;
	end

	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			waddr<=0;
		end
		else if(wenc)
			waddr<=waddr+1;
	end
	
	//例化双端口RAM
	dual_port_RAM #(.DEPTH(DEPTH),.WIDTH(WIDTH))
	ram(
		.wclk(clk),
		.wenc(wenc),
		.waddr(waddr[DEPTH-1:0]),   //注意这里地址位宽,最高位不是有效地址位
		.wdata(wdata),
		.rclk(clk),
		.renc(renc),
		.raddr(raddr[DEPTH-1:0]),
		.rdata(rdata)
	);
endmodule

三、异步FIFO

在这里插入图片描述

在这里插入图片描述

1、格雷码

格雷码:循环码,相邻两个数值之间只有一位发生改变,发生亚稳态的概率大大降低

二进制码格雷码
000000
001001
010011
011010
100110
101111
110101
111100

1.1 二进制和格雷码之间的转换

在这里插入图片描述

二进制码转化为格雷码:从最右边第一位开始,依次将每一位与左邻一位异或(XOR),作为对应格雷码该位的值,最左边一位不变;

gray = bin ^ (bin>>1);

格雷码转化为二进制码:从左边第二位起,将每位与左边一位解码后的值异或(XOR),作为该位解码后的值(最左边一位依然不变)。

reg [3:0] gray, bin;
bin[3] = gray[3];
bin[2] = bin[3] ^ gray[2];
bin[1] = bin[2] ^ gray[1];
bin[0] = bin[1] ^ gray[0];

1.2 使用格雷码判断空满

  • :两者指针完全相同
  • :最高位和次高位不同,其他位完全相同
  • 7(0111)和15(1111)对应到二进制码的话就只有最高位不高。7(0100)和15(1000)对应到格雷码的话最高位和次高位相反,其他位相同

在这里插入图片描述

在这里插入图片描述

1.3 当深度不是2次幂

  • 并不是一定要用格雷码做读写指针,而是当深度为2次幂的时候,刚好格雷码满足消除亚稳态的需求;
  • 在非2次幂深度情况下,格雷码已经不再适用,此时的解决方法通常有:
    若深度为偶数,可采用最接近的2次幂的格雷码编码,在此基础上修改;
    深度为一般数值时,可自行设计一种逻辑电路,或者查找表,以实现指针每次只跳变一次的功能;
  • 以上方法通常在设计层面较为复杂,若无特定需求,可将FIFO深度设置为2次幂,浪费一些存储空间,来化简控制电路的复杂度。

在这里插入图片描述
或者在偶数个的时候,去掉头和尾的数字,中间部分也是满足要求
异步FIFO在2N个数据类就可以循环
在这里插入图片描述

1.4 异步FIFO能否消除掉亚稳态

  • 亚稳态不能从根本上消除,只能采取一定的措施使其对电路的影响降低
  • 打两拍只能大幅降低亚稳态发生的概率,但是理论上是不能完全消除的

1.5 读写判断是存在漏洞,不是真空或者真满

  • 写指针通过两级寄存器同步到读时钟域,再和读指针比较进行FIFO空状态判断,因为在两级同步寄存器需要时间,而在这个同步的时间内有可能还会写入新的数据,因此同步后的写指针一定是小于或者等于当前实际的写指针,所以此时判断 FIFO为空不一定是真空,这样更加保守,一共不会出现空读的情况,虽然会影响FIFO的性能,但是并不会出错。
  • 读指针通过两级寄存器同步到写时钟域,再和写指针比较进行FIFO满状态判断,同步后的读指针一定是小于或者等于当前的读指针,所以此时判断FIFO为满不一定是真满。

在这里插入图片描述

1.6 格雷码产生传输错误

  • 保证的是同步后的读写地址即使在出错的情形下,依然能够保证FIFO功能的正确性,不会出现空读满写的错误状态,当然同步后的读写地址出错总是存在的。
  • 地址总线bus skew一定不能超过一个周期,否则可能出现gray码多位数据跳变的情况,这个时候gray码就失去了作用,因为这时候同步后的地址已经不能保证只有1位跳变了。

写指针000 -> 001,假设同步过去变成了000 -> 000,这样会判断为空,但实际上并不为空,此时不能读操作,还是可以写。不会对FIFO的功能造成影响。

1.7 极端读写时钟域情况

  • 写时钟域慢,读时钟域快:读时钟域总是能采集到信号(慢到快),判断空不成问题
    假如读指针从1-9,同步到写时钟域的是7,写指针也在7,那么会判断“满”,会停止写入,其实没满,还差点
    在这里插入图片描述
  • 写时钟域快,读时钟域慢:会出现漏采(只能采集到135)。将读指针传到写时钟域是可以的(慢到快),判断满不成问题
    假设写指针从1-9,同步到读时钟域是7,读指针也在7,会产生“空信号”,会停止读,但实际上没有空
    在这里插入图片描述
  • 当在极端状况下,写很快,读很慢,那一开始就被写满;相反则永远是读空
    总结:写得快,会错误判断产生虚空;读得快,会错误判断产生虚满

2、例化双端口RAM实现异步FIFO

module asyn_fifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					wclk	, 
	input 					rclk	,   
	input 					wrstn	,
	input					rrstn	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output wire				wfull	,
	output wire				rempty	,
	output wire [WIDTH-1:0]	rdata
);
	//双端口RAM的读写使能信号
	wire wenc = winc && !wfull;
	wire renc = rinc && !rempty;
	
	parameter ADD_WIDTH = $clog2(DEPTH);   //地址线宽度
	
	//二进制地址加减
	reg [ADD_WIDTH:0] wptr_bin,rptr_bin;
	reg [ADD_WIDTH:0] wptr_gray,rptr_gray;
	
	//更新二进制码和格雷码
	always@(posedge wclk or negedge wrstn)begin
		if(!wrstn)
			wptr_gray <= 0;
			wptr_bin <= 0;
		else 
			wptr_gray <= wptr_bin ^ (wptr_bin>>1);
			wptr_bin <= wptr_bin + wenc;
	end

	always@(posedge rclk or negedge wrstn)begin
			if(!wrstn)
				rptr_gray <= 0;
				rptr_bin <= 0;
			else if(renc)
				rptr_gray <= rptr_gray ^ (rptr_gray>>1);
				rptr_bin <= rptr_bin + renc;
	end
	
	//同步格雷码
	reg [ADD_WIDTH:0] wptr_gray_r,wptr_gray_rr;
	always@(posedge rclk or negedge rrstn)begin   //读时钟域同步写格雷码
		if(!rrstn)
			{wptr_gray_r,wptr_gray_rr} <=0;
		else
			{wptr_gray_rr,wptr_gray_r}<={wptr_gray_r,wptr_gray};
	end
	
	reg [ADD_WIDTH:0] rptr_gray_r,rptr_gray_rr;
	always@(posedge wclk or negedge wrstn)begin   //写时钟域同步读格雷码
		if(!wrstn)
			{rptr_gray_rr,rptr_gray_r} <=0;
		else
			{rptr_gray_rr,rptr_gray_r}<={rptr_gray_r,rptr_gray};
	end
	
	//空满标志的产生,利用格雷码判断
	assign rempty = rptr_gray==wptr_gray_rr;
	assign wfull = wptr_gray=={~rptr_gray_rr[ADD_WIDTH:ADD_WIDTH-1],rptr_gray_rr[ADD_WIDTH-2:0]};
	
	//例化双端口RAM
	dual_port_RAM #(.DEPTH(DEPTH),.WIDTH(WIDTH))
	one(
		.wclk(wclk),
		.wenc(wenc),
		.waddr(wptr_bin[ADD_WIDTH-1:0]),   //注意这里地址
		.wdata(wdata),
		.rclk(rclk),
		.renc(renc),
		.raddr(rptr_bin[ADD_WIDTH-1:0]),
		.rdata(rdata)
	);
endmodule

四、计算FIFO最小深度

  • 一般来说,在不连续传输的写入情况下,才考虑FIFO深度问题,因为假设是连续写入,FIFO总是会满的

  • 在正常情况下,应该是这样的:空闲—Burst突发—空闲—Burst突发—空闲—Burst突发

  • 背靠背传输空闲—Burst突发—Burst突发—空闲—Burst突发—空闲
    如果接收方没法接收所有数据,那么剩余的数据可以被存储在FIFO内部且不会溢出,也就是我们要求的FIFO深度的意思。

  • 在SDRAM的应用中,我们通常使用的读写FIFO是突发长度的2倍,比如突发长度为256,那FIFO的深度设置为512,使得FIFO始终保持半满的状态。可以保证数据的传输。

burst_length:表示这段时间写入的数据量
burst_length/wclk:写入数据的时间
(X/Y)*rclk:每Y个时钟读出X个数据,说明读出效率不是100%,要在rclk基础上打折扣,速度没有rclk
depth:写入和读出两者之差为FIFO中残留的数据,这个也就是理论上的FIFO的最小深度。

depth = burst_length - (burst_length/wclk) * ((X/Y)*rclk)

1、FIFO写时钟100MHz,读时钟80MHz,每100个写时钟,写入80个数据;每一个读时钟读走一个数据,求最小深度不会溢出

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

2、一个8bit宽的AFIFO,输入时钟为100MHz,输出时钟为95MHz,设一个package为4Kbit,且两个package之间的发送间距足够大,问AFIFO的深度。

传输数据个数 = 时间 * 速度(频率)
深度 = (读写速率差) * (写入数据需要的时间)

传输数据个数:4Kbit/8bit =500 word
写入500个word所需时间:500/100
该时间内读取的个数:500/100*95
(1)深度 = 写个数 - 读个数 = 500 - 500/100 * 95 = 25
(2)深度 = (100-95) * (500/100) = 25

3、A/D采样率50MHz,DSP读A/D速率40MHz,要不丢失地将10万个采样数据送入DSP,在A/D在和DSP之间至少加多大容量(深度)的FIFO才行?

FIFO用于缓冲块数据流,一般用在快时钟域写慢时钟域读
FIFO深度 /(写入速率 - 读出速率)= FIFO被填满时间 > 数据包传送时间= 数据量 / 写入速率
FIFO写满的时间 > 数据包传输的时间
FIFO深度 = (写入速率 - 读出速率)* 数据包传送时间
100000 / 50MHz = 1/ 500 s = 2ms
(50MHz - 40MHz) * 1/500 = 20k

4、异步FIFO要考虑格雷码同步的时间

在这里插入图片描述

5、真题

在这里插入图片描述

五、读写频率、读写有无空闲周期下最小深度计算

1、fA>fB,读写之间无空闲周期

写速率fA=80MHz,读速率fB=50MHz,突发长度Burst Length = 120,读写之间没有空闲周期,是连续读写一个突发长度。
120-120/80*50 = 120 - 75 = 45

2、fA>fB,两次连续读写之间有一个周期的延迟

同case1,两次连续读写之间通常有延迟

3、fA>fB,读写都有空闲周期/读写使能百分比

写速率fA=80MHz,读速率fB=50MHz,突发长度Burst Length = 120

两个连续写入之间的空闲周期为=1,两个连续读取之间的空闲周期为=3
写使能占得百分比为=50%=1/2,读使能占得百分比为=25%=1/4

答: 两个连续写入之间的空闲周期为1的意思是,每写入一个数据,要等待一个周期,再写入下一个数据。这也可以理解为每两个周期,写入一个数据。同理每四个周期,读取一个数据。相当于读写频率降低了。
120-120/40*12.5=82.5=83

4、fA<fB,读写之间无空闲周期

写进去的数据总是被读走了,所以FIFO深度为1即可

5、fA<fB,读写都有空闲周期/读写使能百分比

写速率fA=30MHz,读速率fB=50MHz,突发长度Burst Length = 120
两个连续写入之间的空闲周期为=1,两个连续读取之间的空闲周期为=3
120-120/30*12.5=20(实际上这道题目写速率比读快)

6、fA=fB,读写之间无空闲周期

读写为同一时钟,可以不需要FIFO,直接写入的值给输出。如果读写时钟存在相位差,那么FIFO深度为1即可。

7、fA=fB,读写都有空闲周期/读写使能百分比

写速率fA= 50MHz,读速率fB=50MHz,突发长度Burst Length = 120
两个连续写入之间的空闲周期为=1,两个连续读取之间的空闲周期为=3
120-120/25*12.5=60(实际上这道题目写速率比读快)

8、读写速率相等每100个时钟写入80个数据每10个时钟读取8个数据

考虑背靠背情况:160-160*8/10=32

六、简答题

1、什么是异步FIFO?画出异步FIFO的结构图。

  • 读写时钟域不同。在写时钟域下将数据放入缓存区,读时钟域下将数据读取出来
  • 没有外部读写地址线,只能按照顺序读写
  • 写指针指向下一个要写入的位置,读指针指向当前要读取的位置。复位时,都指向零,是写指针下一个要写入的位置,读指针当前读取的位置,所以是“读空/empty”

2、如何判断读空或者写满信号?

直接根据地址是否相等不能判断空或者满。
对指针增加一位最高有效位MSB,n位指针,n-1位表示访问FIFO所需的地址位数。n位完全相同表示“空”;MSB不同,n-1位完全相同表示“满”

3、为什么要用格雷码?在FIFO中使用格雷码的优点

  • 二进制码在传输时可能会发生多bit位同时变化得情况,如0111->1000,这样容易发生亚稳态。格雷码是循环码,相邻两位之间只有1bit发生改变,降低亚稳态发生的概率
  • 多bit信号变化时会产生竞争冒险,消除竞争冒险
  • 多bit传输时,格雷码跳变较少,功耗降低,有利于低功耗设计
  • 格雷码出错只是一位发生错误,降低了错误对系统的影响,保证FIFO功能的正确性,只会出现假空或者假满

4、异步FIFO设计核心

  • 格雷码+指针同步

5、如何验证FIFO?

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

  • 7
    点赞
  • 154
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
以下是一个简单的同步 FIFO Verilog 代码: ```verilog module sync_fifo ( input clk, input rst, input wr, input rd, input [7:0] data_in, output [7:0] data_out, output empty, output full ); parameter depth = 16; reg [7:0] mem [0:depth-1]; reg [3:0] wr_ptr = 0; reg [3:0] rd_ptr = 0; wire [3:0] next_wr_ptr = (wr_ptr == depth-1) ? 0 : wr_ptr+1; wire [3:0] next_rd_ptr = (rd_ptr == depth-1) ? 0 : rd_ptr+1; reg [3:0] count = 0; always @(posedge clk) begin if (rst) begin wr_ptr <= 0; rd_ptr <= 0; count <= 0; end else begin if (wr && !full) begin mem[wr_ptr] <= data_in; wr_ptr <= next_wr_ptr; count <= count + 1; end if (rd && !empty) begin data_out <= mem[rd_ptr]; rd_ptr <= next_rd_ptr; count <= count - 1; end end end assign empty = (count == 0); assign full = (count == depth); endmodule ``` 该 FIFO 模块包含一个输入时钟 clk 和一个异步复位信号 rst。还有其他几个输入和输出端口,包括 wr 和 rd 信号,用于指示写入和读取数据,以及 data_in 和 data_out 端口,用于传输数据。此外,还有 empty 和 full 信号,用于指示 FIFO 是否为空或已满。 FIFO 深度由 depth 参数指定,可以根据需要进行更改。该 FIFO 使用了一个简单的循环缓冲区来存储数据。wr_ptr 和 rd_ptr 变量指示下一个写入或读取数据的位置。count 变量跟踪 FIFO 中当前存储的元素数量。当 FIFO 为空或已满时,empty 和 full 信号分别设置为 1。 在时钟上升沿时,FIFO 模块执行以下操作: - 如果复位信号 rst 为 1,则将 wr_ptr,rd_ptr 和 count 变量重置为 0。 - 如果 wr 信号为 1 并且 FIFO 不满,则将 data_in 写入 mem 数组中的 wr_ptr 位置,并将 wr_ptr 更新为下一个位置,同时将 count 增加 1。 - 如果 rd 信号为 1 并且 FIFO 不为空,则将 mem 数组中的 rd_ptr 位置的数据读入 data_out,并将 rd_ptr 更新为下一个位置,同时将 count 减少 1。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值