通用读写仲裁模块(FPGA实现)

这篇具有很好参考价值的文章主要介绍了通用读写仲裁模块(FPGA实现)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  当涉及多个模块向同一个模块进行读写操作、向一个半双工模块请求读写,甚至综合一下,多个模块向一个半双工模块发起读写请求,那就要涉及读写仲裁。因为最近做的项目中涉及的读写仲裁太多了,所以就想还是要写一个通用的读写仲裁模块,最好还是具备“凡请求,必执行”的功能的(因为一般简单实现的仲裁在发生冲突时,会选择执行一个,而直接忽视其他请求,这就对发起读写请求的模块的控制逻辑造成了不必要的麻烦),于是就有了这篇文章。

  由于每个人实现的模块控制信号不尽相同,因此本文档中的代码仅作为一种实现思路的参考。下面以写仲裁作为例子介绍实现思路,读仲裁逻辑与之相同。

  首先看怎么实现“凡请求,必执行”功能的,我们知道,若想将某些位进行置位,可以使用或运算,因此

SW_ARB: begin
    wr_busy		<= wr_busy | wr_req;		//将出现请求的通道标记
end

就可以将出现了写请求的通道标记,之后轮询时如果发现通道具有标记,则进入写功能流程。发现通道具有标记的代码如下

if(|(wr_busy & W_CH_SEL)) begin		//检测到该通道存在请求,启动写进程
    next_state_W	<= SW_WR;
end
else begin
    next_state_W	<= SW_POLL;		//否则轮询下个通道
end

首先将 wr_busy 和 W_CH_SEL 做与运算,则若当前通道存在标记,对应位将为 1,而若不存在标记,对应位为 0,其他非当前检查通道的位都为 0,之后再对与运算后的结果做一个或运算,就得到当前通道是否存在标记的判断值了。

  在进行完写操作后,需要将对应通道的标记清除,而我们也知道想要实现复位操作,可以使用与运算,因此

SW_END: begin
	wr_busy		<= wr_busy & (~W_CH_SEL);	//清除该通道的标记
end

就可以把对应通道的标记清除,以示写操作完成,而对应通道就可以决定是否再发起写请求了。

  轮询的实现,使用 CHANNEL_NUM 宽度的信号 W_CH_SEL 进行通道轮询,初始值置为 0b1,若检测到当前通道不存在请求标记,则轮询下一通道,实现方式为循环移位

SW_POLL: begin
	W_CH_SEL	<= {W_CH_SEL[CHANNEL_NUM-2:0], W_CH_SEL[CHANNEL_NUM-1]};	//检查下一个通道
end

  为了实现全双工时,读写过程的并发,因此读仲裁、写仲裁的状态机应该是独立的,分别维护自身的状态 state_W/state_R,而在半双工模式下,读写通道不能同时进入执行状态,因此需要判断对方的状态,且为了避免读写通道同时处于裁决状态时一起进入读写流程的问题,state_W 应当在检测到 state_R 处在轮询状态时才能进行裁决,对应的状态控制如下

SW_ARB: begin
    if(is_FULL_DUPLEX | (state_R == SR_POLL)) begin	//全双工,或者半双工且读通道正在通道轮询
        if(|(wr_busy & W_CH_SEL)) begin					//判断是否存在标记
            next_state_W	<= SW_WR;						//有标记,进入写进程
        end
        else begin
            next_state_W	<= SW_POLL;						//否则轮询
        end
    end
    else begin
        next_state_W	<= SW_ARB;					//半双工,且读通道正在裁决或执行读操作,则等待
    end
end

代码

/* 
 * file			: arb.v
 * author		: 今朝无言
 * date			: 2023-05-31
 * version		: v1.0
 * description	: 通用读写仲裁模块
 */
module arb(
input										clk,
input										rst_n,

//写通道仲裁
input		[CHANNEL_NUM-1:0]				wr_req,		//CHANNEL_NUM路写通道请求,高电平有效
input		[CHANNEL_NUM*DATA_WIDTH-1:0]	wrdata,		//对应的写数据
input		[CHANNEL_NUM*ADDR_WIDTH-1:0]	wr_addr,	//对应的写地址
output	reg	[CHANNEL_NUM-1:0]				wr_busy,	//回应各个通道是否写忙碌

output	reg									wr_req_o,	//输出到实际写入模块的写请求信号
output	reg	[DATA_WIDTH-1:0]				wrdata_o,	//对应的写数据
output	reg	[ADDR_WIDTH-1:0]				wr_addr_o,	//对应的写地址
input										wr_busy_i,	//模块是否忙碌

//读通道仲裁
input		[CHANNEL_NUM-1:0]				rd_req,		//多路读请求,高电平有效
output	reg	[CHANNEL_NUM*DATA_WIDTH-1:0]	rddata,		//对应的多路读数据
input		[CHANNEL_NUM*ADDR_WIDTH-1:0]	rd_addr,	//对应的读地址
output	reg	[CHANNEL_NUM-1:0]				rd_busy,	//回应各个通道是否读忙碌

output	reg									rd_req_o,	//输出到实际读模块的读请求信号
input		[DATA_WIDTH-1:0]				rddata_i,	//获取的数据
output	reg	[ADDR_WIDTH-1:0]				rd_addr_o,	//对应的读地址
input										rd_busy_i	//模块是否忙碌
);

parameter	is_FULL_DUPLEX 	= 1;	//是否全双工
parameter	CHANNEL_NUM		= 2;	//仲裁通道数
parameter	DATA_WIDTH		= 8;	//数据位宽
parameter	ADDR_WIDTH		= 8;	//地址位宽

//------------------------Write Arbitrate--------------------------------
parameter	SW_IDLE		= 8'h01;
parameter	SW_ARB		= 8'h02;	//仲裁
parameter	SW_POLL		= 8'h04;	//轮询
parameter	SW_WR		= 8'h08;
parameter	SW_WITE		= 8'h10;
parameter	SW_END		= 8'h20;

reg		[7:0]	state_W			= SW_IDLE;
reg		[7:0]	next_state_W;

reg		[CHANNEL_NUM-1:0]	W_CH_SEL	= 1'b1;	//选择哪个通道

always @(posedge clk or negedge rst_n) begin
	if(~rst_n) begin
		state_W		<= SW_IDLE;
	end
	else begin
		state_W		<= next_state_W;
	end
end

always @(*) begin
	case(state_W)
	SW_IDLE: begin
		next_state_W		<= SW_ARB;
	end
	SW_ARB: begin
		if(is_FULL_DUPLEX | (state_R == SR_POLL)) begin
			if(|(wr_busy & W_CH_SEL)) begin		//检测到该通道存在请求,启动写进程(wrbusy中记录了哪些通道出现了写请求,但还没有处理)
				next_state_W	<= SW_WR;
			end
			else begin
				next_state_W	<= SW_POLL;		//否则轮询下个通道
			end
		end
		else begin
			next_state_W	<= SW_ARB;
		end
	end
	SW_POLL: begin
		next_state_W		<= SW_ARB;
	end
	SW_WR: begin
		if(wr_busy_i) begin
			next_state_W	<= SW_WITE;
		end
		else begin
			next_state_W	<= SW_WR;
		end
	end
	SW_WITE: begin
		if(~wr_busy_i) begin
			next_state_W	<= SW_END;
		end
		else begin
			next_state_W	<= SW_WITE;
		end
	end
	SW_END: begin
		next_state_W		<= SW_IDLE;
	end
	default: begin
		next_state_W		<= SW_IDLE;
	end
	endcase
end

integer		ch_index_W;
integer		i;
always @(posedge clk or negedge rst_n) begin
	if(~rst_n) begin
		wr_req_o	<= 1'b0;
		wrdata_o	<= 0;
		wr_addr_o	<= 0;
		wr_busy		<= 0;
		W_CH_SEL	<= 1'b1;
	end
	else begin
		ch_index_W	= 0;
		for(i=0; i<CHANNEL_NUM; i=i+1) begin
			if(W_CH_SEL[i]) begin
				ch_index_W	= i;
			end
		end

		case(state_W)
		SW_IDLE: begin
			wr_req_o	<= 1'b0;
			wrdata_o	<= 0;
			wr_addr_o	<= 0;
			wr_busy		<= wr_busy;
			W_CH_SEL	<= W_CH_SEL;
		end
		SW_ARB: begin
			wr_busy		<= wr_busy | wr_req;		//将出现请求的通道标记
		end
		SW_POLL: begin
			W_CH_SEL	<= {W_CH_SEL[CHANNEL_NUM-2:0], W_CH_SEL[CHANNEL_NUM-1]};	//检查下一个通道
		end
		SW_WR: begin
			wr_req_o	<= 1'b1;
			wrdata_o	<= wrdata[ch_index_W*DATA_WIDTH +: DATA_WIDTH];
			wr_addr_o	<= wr_addr[ch_index_W*ADDR_WIDTH +: ADDR_WIDTH];
		end
		SW_WITE: begin
			wr_req_o	<= 1'b0;
		end
		SW_END: begin
			wr_busy		<= wr_busy & (~W_CH_SEL);	//清除该通道的标记
		end
		default: begin
			wr_req_o	<= 1'b0;
			wrdata_o	<= 0;
			wr_addr_o	<= 0;
			wr_busy		<= wr_busy;
			W_CH_SEL	<= W_CH_SEL;
		end
		endcase
	end
end

//------------------------Read Arbitrate---------------------------------
parameter	SR_IDLE		= 8'h01;
parameter	SR_ARB		= 8'h02;	//仲裁
parameter	SR_POLL		= 8'h04;	//轮询
parameter	SR_RD		= 8'h08;
parameter	SR_WITE		= 8'h10;
parameter	SR_END		= 8'h20;

reg		[7:0]	state_R			= SR_IDLE;
reg		[7:0]	next_state_R;

reg		[CHANNEL_NUM-1:0]	R_CH_SEL	= 1'b1;	//选择哪个通道

always @(posedge clk or negedge rst_n) begin
	if(~rst_n) begin
		state_R		<= SR_IDLE;
	end
	else begin
		state_R		<= next_state_R;
	end
end

always @(*) begin
	case(state_R)
	SR_IDLE: begin
		next_state_R		<= SR_ARB;
	end
	SR_ARB: begin
		if(is_FULL_DUPLEX | (state_W == SW_POLL)) begin
			if(|(rd_busy & R_CH_SEL)) begin		//检测到该通道存在请求,启动读进程(rd_busy中记录了哪些通道出现了读请求,但还没有处理)
				next_state_R	<= SR_RD;
			end
			else begin
				next_state_R	<= SR_POLL;		//否则轮询下个通道
			end
		end
		else begin
			next_state_R	<= SR_POLL;
		end
	end
	SR_POLL: begin
		next_state_R		<= SR_ARB;
	end
	SR_RD: begin
		if(rd_busy_i) begin
			next_state_R	<= SR_WITE;
		end
		else begin
			next_state_R	<= SR_RD;
		end
	end
	SR_WITE: begin
		if(~rd_busy_i) begin
			next_state_R	<= SR_END;
		end
		else begin
			next_state_R	<= SR_WITE;
		end
	end
	SR_END: begin
		next_state_R		<= SR_IDLE;
	end
	default: begin
		next_state_R		<= SR_IDLE;
	end
	endcase
end

integer		ch_index_R;
integer		j;
always @(posedge clk or negedge rst_n) begin
	if(~rst_n) begin
		rd_req_o	<= 1'b0;
		rd_addr_o	<= 0;
		rddata		<= 0;
		rd_busy		<= 0;
		R_CH_SEL	<= 1'b1;
	end
	else begin
		ch_index_R	= 0;
		for(j=0; j<CHANNEL_NUM; j=j+1) begin
			if(R_CH_SEL[j]) begin
				ch_index_R	= j;
			end
		end

		case(state_R)
		SR_IDLE: begin
			rd_req_o	<= 1'b0;
			rd_addr_o	<= 0;
			rddata		<= rddata;
			rd_busy		<= rd_busy;
			R_CH_SEL	<= R_CH_SEL;
		end
		SR_ARB: begin
			rd_busy		<= rd_busy | rd_req;		//将出现请求的通道标记
		end
		SR_POLL: begin
			R_CH_SEL	<= {R_CH_SEL[CHANNEL_NUM-2:0], R_CH_SEL[CHANNEL_NUM-1]};	//检查下一个通道
		end
		SR_RD: begin
			rd_req_o	<= 1'b1;
			rd_addr_o	<= rd_addr[ch_index_R*ADDR_WIDTH +: ADDR_WIDTH];
		end
		SR_WITE: begin
			rd_req_o	<= 1'b0;
		end
		SR_END: begin
			rddata[ch_index_R*DATA_WIDTH +: DATA_WIDTH]		<= rddata_i;
			rd_busy		<= rd_busy & (~R_CH_SEL);	//清除该通道的标记
		end
		default: begin
			rd_req_o	<= 1'b0;
			rd_addr_o	<= 0;
			rddata		<= rddata;
			rd_busy		<= rd_busy;
			R_CH_SEL	<= R_CH_SEL;
		end
		endcase
	end
end

endmodule

测试代码 & 测试结果

`timescale 1ns/100ps

module arb_tb();

reg		clk_100M	= 1'b1;
always #5 begin
	clk_100M	<= ~clk_100M;
end

reg		rst_n;

reg				wr_req1, wr_req2, wr_req3;
reg		[7:0]	wrdata1, wrdata2, wrdata3;
reg		[7:0]	wr_addr1, wr_addr2, wr_addr3;
wire			wr_busy1, wr_busy2, wr_busy3;

wire			wr_req_o;
wire	[7:0]	wrdata_o;
wire	[7:0]	wr_addr_o;
reg				wr_busy_i;

reg				rd_req1, rd_req2, rd_req3;
wire	[7:0]	rddata1, rddata2, rddata3;
reg		[7:0]	rd_addr1, rd_addr2, rd_addr3;
wire			rd_busy1, rd_busy2, rd_busy3;

wire			rd_req_o;
reg		[7:0]	rddata_i;
wire	[7:0]	rd_addr_o;
reg				rd_busy_i;

//----------------------仲裁------------------------------------
arb #(
	.is_FULL_DUPLEX	(0),	//是否全双工
	.CHANNEL_NUM	(3),
	.DATA_WIDTH		(8),
	.ADDR_WIDTH 	(8)
)
arb_inst(
	.clk			(clk_100M),
	.rst_n			(rst_n),

	//写通道仲裁
	.wr_req			({wr_req1, wr_req2, wr_req3}),		//CHANNEL_NUM路写通道请求,高电平有效
	.wrdata			({wrdata1, wrdata2, wrdata3}),		//对应的写数据
	.wr_addr		({wr_addr1, wr_addr2, wr_addr3}),	//对应的写地址
	.wr_busy		({wr_busy1, wr_busy2, wr_busy3}),	//回应各个通道是否写忙碌

	.wr_req_o		(wr_req_o),							//输出到实际写入模块的写请求信号
	.wrdata_o		(wrdata_o),							//对应的写数据
	.wr_addr_o		(wr_addr_o),						//对应的写地址
	.wr_busy_i		(wr_busy_i),						//模块是否忙碌

	//读通道仲裁
	.rd_req			({rd_req1, rd_req2, rd_req3}),		//多路读请求,高电平有效
	.rddata			({rddata1, rddata2, rddata3}),		//对应的多路读数据
	.rd_addr		({rd_addr1, rd_addr2, rd_addr3}),	//对应的读地址
	.rd_busy		({rd_busy1, rd_busy2, rd_busy3}),	//回应各个通道是否读忙碌

	.rd_req_o		(rd_req_o),							//输出到实际读模块的读请求信号
	.rddata_i		(rddata_i),							//获取的数据
	.rd_addr_o		(rd_addr_o),						//对应的读地址
	.rd_busy_i		(rd_busy_i)							//模块是否忙碌
);

//-----------------------tb----------------------------------
localparam	WAIT_N	= 20;

wire	wr_req_o_pe;
reg		wr_req_o_d0;
reg		wr_req_o_d1;

wire	rd_req_o_pe;
reg		rd_req_o_d0;
reg		rd_req_o_d1;

always @(posedge clk_100M) begin
	wr_req_o_d0		<= wr_req_o;
	wr_req_o_d1		<= wr_req_o_d0;

	rd_req_o_d0		<= rd_req_o;
	rd_req_o_d1		<= rd_req_o_d0;
end

assign	wr_req_o_pe	= wr_req_o_d0 & (~wr_req_o_d1);
assign	rd_req_o_pe	= rd_req_o_d0 & (~rd_req_o_d1);

reg		[7:0]	wr_busy_cnt		= WAIT_N;
always @(posedge clk_100M) begin
	if(wr_req_o_pe) begin
		wr_busy_cnt		<= 8'd0;
	end
	else begin
		if(wr_busy_cnt <= WAIT_N) begin
			wr_busy_cnt		<= wr_busy_cnt + 1'b1;
		end
		else begin
			wr_busy_cnt		<= wr_busy_cnt;
		end
	end

	wr_busy_i		<= (wr_busy_cnt != 0 && wr_busy_cnt < WAIT_N)? 1'b1 : 1'b0;
end

reg		[7:0]	rd_busy_cnt		= WAIT_N;
always @(posedge clk_100M) begin
	if(rd_req_o_pe) begin
		rd_busy_cnt		<= 8'd0;
	end
	else begin
		if(rd_busy_cnt <= WAIT_N) begin
			rd_busy_cnt		<= rd_busy_cnt + 1'b1;
		end
		else begin
			rd_busy_cnt		<= rd_busy_cnt;
		end
	end

	rd_busy_i		<= (rd_busy_cnt != 0 && rd_busy_cnt < WAIT_N)? 1'b1 : 1'b0;

	rddata_i		<= rd_addr_o;
end

initial begin
	rst_n	<= 1'b0;
	{wr_req1, wr_req2, wr_req3}		<= 3'b000;
	{rd_req1, rd_req2, rd_req3}		<= 3'b000;
	#100;
	rst_n	<= 1'b1;
	#100;

	fork
		begin
			{wr_req1, wr_req2, wr_req3}		<= 3'b101;
			{wrdata1, wrdata2, wrdata3}		<= {8'd12, 8'd34, 8'd56};
			{wr_addr1, wr_addr2, wr_addr3}	<= {8'd1, 8'd2, 8'd3};
			#20;
			{wr_req1, wr_req2, wr_req3}		<= 3'b000;
			wait(wr_busy_i); wait(~wr_busy_i);
			wait(wr_busy_i); wait(~wr_busy_i);
			#100;
		end
		
		begin
			{rd_req1, rd_req2, rd_req3}		<= 3'b011;
			{rd_addr1, rd_addr2, rd_addr3}	<= {8'd1, 8'd2, 8'd3};
			#20;
			{rd_req1, rd_req2, rd_req3}		<= 3'b000;
			wait(rd_busy_i); wait(~rd_busy_i);
			wait(rd_busy_i); wait(~rd_busy_i);
			#100;
		end
	join

	$stop;
end

endmodule

全双工时的结果如下:

通用读写仲裁模块(FPGA实现)

半双工时的结果如下:

通用读写仲裁模块(FPGA实现)文章来源地址https://www.toymoban.com/news/detail-467585.html

到了这里,关于通用读写仲裁模块(FPGA实现)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FPGA纯verilog实现UDP通信,三速网自协商仲裁,动态ARP和Ping功能,提供工程源码和技术支持

    目前网上的fpga实现udp基本生态如下: 1:verilog编写的udp收发器,但不带ping功能,这样的代码功能正常也能用,但不带ping功能基本就是废物,在实际项目中不会用这样的代码,试想,多机互联,出现了问题,你的网卡都不带ping功能,连基本的问题排查机制都不具备,这样的代

    2023年04月11日
    浏览(25)
  • STM32F407单片机通用24CXXX读写程序(KEIL),兼容24C系列存储器(24C01到24C512),支持存储器任意地址跨页连续读写多个页

    原文链接:https://blog.csdn.net/ba_wang_mao/article/details/108318633 AT24C01,AT24C02,AT24C04,AT24C08,AT24C16,AT24C32,AT24C64,AT24C128,AT24C256…不同的xxx代表不同的容量。 总容量(Byte容量) = 页数 × 页内字节单元数。 对AT24CXXX进行读写操作时,都得先访问存储地址、比如AT24C01写一个字节的I

    2024年04月11日
    浏览(50)
  • FPGA实现高带宽NVMeSSD读写——纯逻辑实现

    在两年之前的帖子《FPGA实现高带宽NVMe SSD读写》中,我们实现了一个基于PS和PL端的高速NVMe SSD IP。这个IP在我们自己的系统中使用正常,但是由于该IP使用了PS端资源,在与其他应用系统集成时遇到了麻烦。为了保护自有的设计,需要独占PS端的一个ARM处理器,这使得该IP在与其

    2024年02月05日
    浏览(49)
  • 【最通用版FPGA 实现 SPI 驱动】

    最近研究了一下SPI协议的FPGA实现,发现网上很多大佬分享的方法都是针对某一特定的flash芯片或者某一传感器芯片来设计电路结构的。所以想根据SPI(Serial Peripheral Interface)的基本通讯协议实现一个通用版的SPI Master驱动。SPI在嵌入式领域是一个很成熟且应用非常广泛的通信协

    2024年02月05日
    浏览(34)
  • 基于fpga的ddr3读写控制,纯verilog实现,能实现多通道图像数据读写控制

    基于fpga的ddr3读写控制,纯verilog实现,能实现多通道图像数据读写控制,模块接口清晰,可移植性高. 基于FPGA的DDR3读写控制是一项重要的技术,它为多通道图像数据的读写提供了高效的解决方案。本文将介绍一种纯Verilog实现的DDR3读写控制模块,旨在实现模块接口清晰、可移

    2024年04月12日
    浏览(48)
  • 【FPGA图像处理】——DDR仲裁、多输入源拼接、旋转任意角度、突发长度修改、任意地址读取。

    前言:做FPGA大赛期间遇到的问题,自己coding过程。 包含:hdmi、摄像头等多输入源的拼接;了解DDR以及多种DMA传输方式,修改底层突发长度以及存储位宽;单输入源任意角度旋转(无需降低帧率)。 写这篇文章的原因呢,是因为之前参加FPGA大赛的时候遇到很多问题找不到系

    2024年02月05日
    浏览(34)
  • 【FPGA】Verilog编程实现SDRAM读写(一) ----- 初识SDRAM

    SDRAM(Synchronous Dynamic Random Access Memory),同步动态随机存储器。同步、动态、随机是其性能特点的外在说明: 同步(Synchronous )是指内存工作需要同步时钟,内部的命令的发送与数据的传输都以它为基准 动态(Dynamic )是指存储阵列 需要不断的刷新来保证数据不丢失 随机(

    2023年04月08日
    浏览(32)
  • FPGA 20个例程篇:12.千兆网口实现MDIO接口读写

           千兆网口是我们日常生活中经常见到的外设接口,在后面三个例程中,我们将会一起去动手实现千兆网口实现MDIO接口读写、ARP通信协议、ICMP和UDP通信协议等,这三个例程有一定的难度,通过实际分析、动手编码、模块划分、上板调试大家可以学到很多内容,涵盖了

    2024年02月01日
    浏览(79)
  • 基于Java的两个通用安全模块的设计与实现

    摘  要 本文详细介绍了基于口令的身份认证与文件安全传输两个通用安全模块的设计原理和实现过程,分析了当前口令保存的安全性,提出了运用MD5算法等对口令进行处理,并将处理结果保存在数据库中的方法。同时为了进一步增强认证系统的灵活度,设计了用户注册时的口

    2024年02月03日
    浏览(30)
  • 深入详解使用 RabbitMQ 过程中涉及到的多个细节问题(面试可用)

    目录 1、基础类问题 2、cluster 相关问题 3、综合性问题 4、参考资料 C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...) https://blog.csdn.net/chenlycly/article/details/125529931 C/C++基础与进阶(专栏文章,持续更新中...) https://blog.csdn.net/chenlycly/category_119

    2024年01月20日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包