详解Xilinx Native FIFO的使用以及RST复位的注意事项


一、FIFO的简介

  FIFO( First Input First Output)指的是一种数据结构,数据以先进先出的顺序进行处理。FIFO分为同步FIFO和异步FIFO,应用场景非常广泛,例如:跨时钟域传输、数据缓存、数据位宽转换等等。

  Xilinx FIFO支持自定义宽度、深度、状态标志、内存类型和写入 (读取)端口宽高比。FIFO 还可以进行定制,以利用某些 FPGA 系列中提供的块 RAM、分布式 RAM 或内置 FIFO 资源来创建高性能、面积优化等 FPGA 设计。Xilinx支持 Native、AXI4-Stream、AXI4、AXI3 和 AXI4-Lite 接口 本文主要讲解Native接口的FIFO,AXI接口的后续再讲解。其中Native接口异步FIFO结构如下:
在这里插入图片描述

二、Native FIFO 特点

  1. FIFO 数据宽度为 1 至 1024 位
  2. 对称或非对称宽高比(读写端口比范围为 1:8 至 8:1)
  3. 同步或异步复位选项
  4. 可选存储器类型(块 RAM、分布式 RAM) 、移位寄存器或内置 FIFO
  5. 选择在标准或第一个字下降模式 (FWFT) 下运行
  6. 满和空状态标志,以及用于指示左字的几乎满和几乎空标志
  7. 可编程满和空状态标志,由用户定义的常量或专用输入端口设置
  8. 可配置的握手信号
  9. 支持 Block RAM 和内置 FIFO 配置的汉明错误注入和纠正检查 (ECC)

三、同步FIFO的不同配置以及仿真测试

3.1 打开 FIFO Generrator

在这里插入图片描述

  1. 用户给FIFO的命名,这里测试同步FIFO,因此命名为scfifo_test
  2. 接口种类:正如上文所述,Xilinx支持Native和AXI接口,本文主要讲解Native接口
  3. 组成FIFO的方式,同步还是异步,由块 RAM、分布式 RAM 还是内置 FIFO 资源构成
  4. 各种FIFO支持的特性列表
  5. 每种特性的是什么意思:
    (1) Non-symmetric aspect ratios:非对称纵横比(读写的数据端口位宽可以不一致,范围从 1:8 到 8:1)
    (2) First-Word Fall-Through:第一个字读出功能,当 FIFO 中存在数据时,第一个字将通过 FIFO自动出现在输出总线 dout上
    (3) Uses Built-in FlFO primitives:使用内置的FIFO单元,从而可以通过在宽度和深度上级联内置 FIFO 来创建大型 FIFO
    (4) ECC support:纠正检查支持
    (5) Dynamic Error Injection:汉明错误注入

我们就选择Native接口,由块Block RAM组成的同步FIFO

3.2 端口设置

在这里插入图片描述

  1. 读FIFO模式,标准的FIFO是读使能来临后,延迟一个时钟周期输出数据。这里选择标准,后面会测试FWFT。
  2. 读写数据位宽以及深度,默认都是同位宽,后面会测试不同读写位宽。
  3. 输出寄存器,如果打开,输出的数据会延迟一拍出来
  4. 复位管脚
  5. 复位模式,分为同步复位和异步复位
  6. 复位时,dout数据显示什么数值
  7. 读数据延迟的时钟周期

3.3 标志信号设置

在这里插入图片描述

  1. 可以操作的标准信号,几乎满标志和几乎空标志
  2. 写确认确认信号和读有效信号,和读写溢出信号。可以打开看是否数据正确的读写进FIFO

3.3 数据数量信号设置

在这里插入图片描述
  可以显示FIFO内还剩多少数量,因为这里选择的是同步FIFO,因此只有一个data_count。如果是异步FIFO,将会有读写数量。

3.4 标准读模式,读写位宽一致的FIFO仿真测试以及信号分析

3.4.1 控制代码编写

`timescale 1ns / 1ps

module my_scfifo_test(
    input                                               sys_clk ,         //输入时钟
    input                                               rst_n             //系统复位
);


    wire            [17:0]                              dout    ;         //FIFO读数据
    wire                                                full    ;         //FIFO满信号
    wire                                                empty   ;         //FIFO空信号
    wire                                                almost_full ;     //FIFO几乎满信号
    wire                                                almost_empty    ; //FIFO几乎空信号
    wire            [9:0]                               data_count  ;     //FIFO数据数量
    reg                                                 srst    ;         //FIFO复位
    reg             [17:0]                              din ;             //FIFO写数据
    reg                                                 wr_en   ;         //FIFO 写使能
    reg                                                 rd_en   ;         //FIFO读使能
    reg             [6:0]                               wait_cnt    ;     //等待复位完成计数器
    reg             [2:0]                               state   ;         

always @(posedge sys_clk or negedge rst_n) begin
  if(rst_n == 1'b0)begin
    wait_cnt <= 'd0;
    state <= 'd0;
    srst <= 1'b1;
    din <= 'd0;
    wr_en <= 1'b0;
    rd_en <= 1'b0;
  end
  else case (state)
    0:  if(wait_cnt[6] == 1'b1) begin   //复位完后,等待一段时间再进行读写操作
          state <= 'd1;
          wait_cnt <= 'd0;
        end
        else begin
          state <= 'd0;
          wait_cnt <= wait_cnt +1'b1;
          srst <= 1'b0;
        end

    1:  if(full == 1'b0)begin     //如果数据没满,则一直写累加的数据
          wr_en <= 1'b1;
          din <= din +1'b1;
          state <= 'd1;
        end
        else begin
          wr_en <= 1'b0;
          din <= din;
          state <= 'd2;
        end
    2:  if(empty == 1'b0)begin  //如果数据没空,就一直读数据
          rd_en <= 1'b1;
          state <= 'd2;
        end
        else begin
          rd_en <= 1'b0;
          state <= 'd3;
        end
    3:  state <= 'd3;
    default: state <= 'd0;
  endcase
end

scfifo_test u_scfifo_test(
  .clk          (sys_clk        ),                   
  .srst         (srst           ),                 
  .din          (din            ),                   
  .wr_en        (wr_en          ),               
  .rd_en        (rd_en          ),               
  .dout         (dout           ),                 
  .full         (full           ),                 
  .almost_full  (almost_full    ),   
  .empty        (empty          ),               
  .almost_empty (almost_empty   ), 
  .data_count   (data_count     )      
);
endmodule

3.4.2 FIFO写数据端仿真结果分析

在这里插入图片描述

  由仿真可以看出,在1510ns处FIFO写入第一个数据,在下一拍1530ns处data_count累加,同时拉低empty。再过一拍1550ns处almost_empty拉低(一旦 FIFO 中出现两个或更多字,almost_empty 标志就会失效)。data_count相对于FIFO内实际数量有一拍的延迟。
  但是为什么在1530ns和1550ns处empty和almost_empty拉低但是我们采集到的数据依然是高电平。我们将1530ns处的上升沿不断地放大,我们将看到如下:
在这里插入图片描述
  我们局部放大后可以看到,在1530ns处时钟上升沿时,过了0.1ns的延迟才将empty信号拉低,这是仿真工具根据实际信号变化的延迟预设的0.1ns 具体为什么请看关于Verilog中判断语句执行时序和modelsim时标取值的问题。结论就是:凡是在功能仿真中,左侧取样的信号一定是在时钟沿右侧翻转,仿真工具以0.1ns的延迟填充。 同理,almost_empty信号在1550ns处的上升沿处也有0.1ns的延迟翻转。

在这里插入图片描述
  在21970ns处实际写入1024个数据后,由于data_count和空满信号会延迟一拍,因此在21990ns处的数据1025并没有写进去。由于data_count位宽为10,所以最大只能显示1023。当内部数据有1024个时,因为数据1024是11位宽,低10位都为0,所以data_count显示为0

3.4.3 FIFO读数据端仿真结果分析

在这里插入图片描述
  在22030ns处拉高rd_en,dout在下一拍才出来,同时full拉低,data_count从1024减1等于1023。如果放大局部来看,依然也延迟了0.1ns 。实际上,在22030ns拉高的读使能,信号变化不是瞬时的,也会有一定延时。因此实际上,在22030ns拉高的读使能,在22050ns才能采集到读使能为高电平,同时经过0.1ns延迟后,再22050.1ns出数据,和拉低full信号以及data_count减1。具体为什么依然请看关于Verilog中判断语句执行时序和modelsim时标取值的问题。结论就是:凡是在功能仿真中,右侧取样的信号一定是在时钟沿时刻翻转,仿真工具以0延迟填充。
在这里插入图片描述
  由于rd_en比data_count早一拍,因此在42490ns处的rd_en是读取的最后一个数据1024。1024在42510ns处出来,在42510ns的读使能将读不出任何数据,所以没有1025 出来。因此在写使能端,1025没有被写入到FIFO里。

3.5 标准读模式,打开output Register的FIFO仿真测试

在这里插入图片描述

  打开了输出寄存器,我们用同样的程序进行仿真观看结果,写数据端和上面仿真一模一样,不一样的是读数据端,结果如下:

在这里插入图片描述

   在22030ns时拉高rd_en,由于打开了输出寄存器,因此读出来的数据还会延迟有一拍。但是为什么data_count和full信号还是只延迟了一拍才变化呢?因为这个输出寄存器存在FIFO内部外侧,数据读出FIFO后再经过的寄存器,因此data_count和full属于FIFO内部的信号,所以只延迟了一拍。

3.6 FWFT模式下,FIFO仿真测试以及信号解析

在这里插入图片描述

   上面我们可以看到 Read Latency 变成了0 ,data_count变成了11位。实际上的FIFO深度为1026个。用同样的程序,打开仿真:

在这里插入图片描述
  从仿真中我们可以看到,在1510ns时写入的第一个数据,延迟3拍后,输出到dout上。同时拉低了empty和almost_empty。

在这里插入图片描述
  由于实际FIFO深度为1026个,所以在22030ns处的1027数据没有被写进去。在后面的22070ns处rd_en拉高,延迟一拍后,dout输出FIFO内部的第二个数据。符合预期。

3.7 标准读模式,读写位宽不一致的FIFO仿真测试以及信号分析

  根据PG057手册,对称纵横比允许 FIFO 的输入和输出深度不同。支持以下写入读取宽高比:1:8、1:4、1:2、1:1、2:1、4:1、8:1,并且 FIFO 的输出深度根据输入深度和写入和读取宽度自动计算。

  仅当可以写入或读取一个完整字时,满标志和空标志才有效。 FIFO 不允许访问部分字。例如,假设 FIFO 已满,如果写入宽度为 8 位且读取宽度为 2 位,则必须先完成四个有效的读取操作,然后才能完全置低并接受写入操作。写入数据计数根据写入端口比率显示 FIFO 字数,读取数据计数根据读取端口比率显示 FIFO 字数。

3.7.1 写读宽度1:4的FIFO测试

在这里插入图片描述
  上图是长宽比为 1:4 的 FIFO (写入宽度 = 2,读取宽度 = 8)。由图可以看出在执行读操作之前执行了四个连续的写操作。第一个写入操作是 01,然后是 00、11,最后是 10。存储器从左到右(MSB 到 LSB)填满。执行读操作时,接收到的数据为01_00_11_10。时序图如下:

在这里插入图片描述

  修改FIFO为写宽度为2,读宽度为8。
在这里插入图片描述

  由上图可以看出,设置写读宽度为1:4后,写深度为1024,读深度变为为256,因为每写入4个数据后,才能读一个数出来。
在这里插入图片描述
  在1510ns时开始写入01、10、11、00;在1570ns时刻写入第四个数据后,后一拍rd_count开始累加。后续写数据端每写入四个,rd_count累加1,符合预期。接下来我们看读出端:
在这里插入图片描述
  在22030ns时开始拉高读使能,后一拍出的数据为01101100,符合预期。

3.7.2 写读宽度4:1的FIFO测试

在这里插入图片描述

  上图显示写读宽度为 4:1 的 FIFO(写入宽度为 8,读取宽度为 2)。写操作是11_00_01_11。执行读操作时,数据从左到右(MSB 到 LSB)接收。如上图所示,第一次读取结果为数据 11,随后是 00、01,最后是 11,时序图如下:

在这里插入图片描述

  修改FIFO为写宽度为8,读宽度为2。

在这里插入图片描述
  由上图可以看出,设置写读宽度为4:1后,写深度为256,读深度变为为1024,因为每写入1个数据后,可以读4个数出来。

在这里插入图片描述

  在1510ns时开始写入00000001后,后一拍rd_count开始累加4。后续写数据端每写入1个,rd_count就累加4,符合预期。接下来我们看读出端:

在这里插入图片描述
  在6670ns拉高rd_en,后面读出来的数据是00、00、00、01 符合预期。

四、异步FIFO的不同配置以及仿真测试

4.1 打开 FIFO Generrator

在这里插入图片描述
   各选项的解释与同步FIFO一致,这里不再赘述。

4.2 端口设置

在这里插入图片描述

4.3 标志信号设置

在这里插入图片描述

4.4 读写数据数量信号设置

在这里插入图片描述

4.5 关于FIFO异步复位需要注意的问题

  根据《PG057》数据手册,在复位时,读写时钟必须都存在。如果出于任何原因,读或者写时钟在复位时丢失,则必须在读写时钟都存在时再次复位。否则可能会导致FIFO的意外行为。有时rst_busy信号可能会被卡住,可能需要重新配置 FPGA。

  如果异步复位是一个最慢时钟宽度,并且断言发生在非常接近最慢时钟的上升沿时,则复位检测可能无法正确发生,从而导致意外行为。为了避免这种情况,始终建议将异步复位置位至少 3 个最慢的时钟周期。以下是官方给出的推荐复位时序图:
RS
   No Access Zone期间的所有 FIFO 输出均应被视为无效。

由以上时序图可以看出:

  • 在拉高RST后延迟了7个WR_CLK周期,FIFO将WR_RST_BUSY信号拉高;
  • 再延迟1个WR_CLK周期将FULL信号拉高;
  • 从WR_RST_BUSY拉高开始直到FULL信号拉低后一个周期内,wr_en信号应该为低电平。
  • 在拉高RST后延迟了7个RD_CLK周期,FIFO将RD_RST_BUSY信号拉高;
  • 再延迟1个RD_CLK周期将EMPTY信号拉高;
  • 从RD_RST_BUSY拉高开始直到FULL信号拉低后一个RD_CLK周期内rd_en信号应该为低电平;
  • DOUT信号在RD_RST_BUSY拉高开始直到EMPTY信号拉低后一个RD_CLK内都无效。
  1. 在复位周期加上60个慢时钟周期后,FIFO可以正常读写

4.5.1 FIFO复位时,读或者写时钟丢失的情况仿真

   按照上面的步骤配置好异步FIFO后,例化,写入控制程序如下:

`timescale 1ns / 1ps

module my_dcfifo_test(
    input                                               wr_clk  ,         //输入写时钟
    input                                               rd_clk  ,         //输入写时钟
    input                                               rst_n             //系统复位
);


    wire            [17:0]                              dout    ;         //FIFO读数据
    wire                                                wr_rst_busy ;     //写复位安全信号
    wire                                                rd_rst_busy ;     //读复位安全信号
    wire                                                full    ;         //FIFO满信号
    wire                                                empty   ;         //FIFO空信号
    wire                                                almost_full ;     //FIFO几乎满信号
    wire                                                almost_empty    ; //FIFO几乎空信号
    wire            [9:0]                               rd_data_count   ; //FIFO读数据数量
    wire            [9:0]                               wr_data_count   ; //FIFO写数据数量
    reg                                                 srst    ;         //写时钟端给的FIFO复位
    reg                                                 drst    ;         //读时钟端给的FIFO复位
    wire                                                fifo_rst    ;
    reg             [17:0]                              din ;             //FIFO写数据
    reg                                                 wr_en   ;         //FIFO写使能
    reg                                                 rd_en   ;         //FIFO读使能
    reg             [2:0]                               wr_state    ;     //写FIFO状态机
    reg             [2:0]                               rd_state    ;     //读FIFO状态机
    reg             [7:0]                               wr_cnt  ;         //写数据的计数器
    reg             [7:0]                               wait_cnt    ;

always @(posedge wr_clk or negedge rst_n) begin
  if(rst_n == 1'b0)begin
    wr_state <= 'd0;
    srst <= 1'b1;
    din <= 'd0;
    wr_en <= 1'b0;
    wr_cnt <= 'd0;
  end
  else case (wr_state)
    0:  if(wr_rst_busy == 1'b0) begin   //等待复位完成
          wr_state <= 'd1;
        end
        else begin
          wr_state <= 'd0;
          srst <= 1'b0;
        end

    1: 
        if(wr_cnt == 'd1033)begin// 如果写到某个数后,突然给个复位信号
            srst <= 1'b1;
            wr_state <= 'd0;
            wr_en <= 1'b0;
            wr_cnt <= wr_cnt + 1'b1;
        end
        else if(full == 1'b0)begin     //如果数据没满,则一直写累加的数据
          wr_en <= 1'b1;
          din <= din +1'b1;
          wr_cnt <= wr_cnt+ 1'b1;
          wr_state <= 'd1;
        end

        else begin
          wr_en <= 1'b0;
          din <= din;
          wr_state <= 'd2;
          srst <= 1'b0;
        end
    2:  wr_state <= 'd2;
    default: wr_state <= 'd0;
  endcase
end

always @(posedge rd_clk or negedge rst_n) begin
  if(rst_n == 1'b0)begin
    rd_state <= 'd0;
    rd_en <= 1'b0;
    drst <= 1'b0;
    wait_cnt <= 'd0;
  end
  else case (rd_state)

    0:  if(wait_cnt == 'd10)  begin  //如果读时钟存在,则复位FIFO 10个周期
          rd_state <= 'd1;
          wait_cnt <= 'd0;
          drst <= 1'b0;
        end
        else begin 
            drst <= 1'b1;
            wait_cnt <= wait_cnt +1'b1;
            rd_state <= 'd0;
        end
    1:  if(rd_rst_busy == 1'b0) begin   //等待复位完成
          rd_state <= 'd2;
        end
        else begin
          rd_state <= 'd1;
        end

    2:  if(wait_cnt[7] == 1'b1) begin //等待一段时间,写数据
          rd_state <= 'd3;
          wait_cnt <= 'd0;
       end
       else begin
          wait_cnt <= wait_cnt + 1'b1;
          rd_state <= 'd2;
       end

    3:  if(empty == 1'b0 )begin     //如果数据没空,则一直读
          rd_en <= 1'b1;
          rd_state <= 'd3;
        end
        else begin
          rd_en <= 1'b0;
          rd_state <= 'd3;
        end
    
    default: rd_state <= 'd0;
  endcase
end

assign fifo_rst = srst | drst;

dc_fifo_test your_instance_name (
  .rst                  (fifo_rst),                      
  .wr_clk               (wr_clk),                
  .rd_clk               (rd_clk),                
  .din                  (din),                      
  .wr_en                (wr_en),                  
  .rd_en                (rd_en),                  
  .dout                 (dout),                    
  .full                 (full),                    
  .almost_full          (almost_full),      
  .empty                (empty),                  
  .almost_empty         (almost_empty),    
  .rd_data_count        (rd_data_count),  
  .wr_data_count        (wr_data_count),  
  .wr_rst_busy          (wr_rst_busy),      
  .rd_rst_busy          (rd_rst_busy)       
);
endmodule

   仿真结果如下:

在这里插入图片描述
   由上图仿真结果可知,在FIFO复位期间,如果读写时钟没有同时存在的话,导致FIFO一直处于异常状态,此时的FIFO无法进行读写操作。根据上述官方手册所说,后续在读写时钟都存在时候,再给FIFO进行复位,FIFO就能恢复正常了,结果如下:

在这里插入图片描述
   由上图仿真结果可知,在415ns以内,没有读时钟导致FIFO复位异常;在415ns后,读写时钟都有时再次复位FIFO,在1170ns以后就能正确的读写FIFO了。

4.5.2 FIFO复位时,RST复位周期小于3个慢时钟周期的情况仿真

   我们修改一下写状态机代码,使其读写时钟都有,然后快时钟只给一个周期的复位:

always @(posedge wr_clk or negedge rst_n) begin
  if(rst_n == 1'b0)begin
    wr_state <= 'd0;
    srst <= 1'b0;
    din <= 'd0;
    wr_en <= 1'b0;
    wr_cnt <= 'd0;
  end
  else case (wr_state)
    0:  if(wr_rst_busy == 1'b0) begin   //等待复位完成
          wr_state <= 'd1;
          srst <= 1'b0;
        end
        else begin
          wr_state <= 'd0;
          srst <= 1'b0;
        end

    1: 
        if(wr_cnt == 'd133)begin// 如果写到某个数后,突然给个复位信号
            srst <= 1'b1;
            wr_state <= 'd0;
            wr_en <= 1'b0;
            wr_cnt <= wr_cnt + 1'b1;
        end
        else if(full == 1'b0)begin     //如果数据没满,则一直写累加的数据
          wr_en <= 1'b1;
          din <= din +1'b1;
          wr_cnt <= wr_cnt+ 1'b1;
          wr_state <= 'd1;
        end

        else begin
          wr_en <= 1'b0;
          din <= din;
          wr_state <= 'd2;
          srst <= 1'b0;
        end
    2:  wr_state <= 'd2;
    default: wr_state <= 'd0;
  endcase
end

   仿真结果如下:

在这里插入图片描述
   在4690ns时,写入端以及写了133个数据,突然给了一个快时钟周期的复位,后续FIFO也出现了异常。

4.6 标准读模式,读写位宽一致的FIFO仿真测试以及读写域信号分析

   按照上述的配置,可控制代码。我们打开仿真观察:
在这里插入图片描述
  从仿真可以看出,当FIFO复位完成后,状态机开始写FIFO操作,为什么读写count不一致呢?我们一步步分析:

  1. 在①时刻2450ns,写入了第一个数据1,然后在延迟wr_clk两个周期后,wr_count开始累加,后面以此类推。

  2. 在②时刻2505ns,rd_clk上升沿采集到wr_data_count等于1,然后延迟rd_clk三个周期后,rd_count输出1。

  3. 在③时刻2535ns,rd_clk上升沿采集到wr_data_count等于3,然后延迟rd_clk三个周期后,rd_count输出3。

  4. 在④时刻2565ns,rd_clk上升沿采集到wr_data_count等于4,然后延迟rd_clk三个周期后,rd_count输出4。

  5. 在2595ns,rd_clk上升沿采集到wr_data_count等于6,然后延迟rd_clk三个周期后,rd_count输出6。

后续以此类推,因此单靠rd_data_count和wr_data_count来判断FIFO内还剩多少数是不准确的,只能估计一个大概。如果想要使用更准确的计数来作为逻辑判断,我们可以打开可编程的空满信号来作为判断的标准,如下:

4.6.1 programmable flag仿真测试

在这里插入图片描述
  打开可编程满信号设置后,我们打开仿真:

在这里插入图片描述
  例如我想要在FIFO内有512个数据时开始读操作,如果用rd_clk去判断rd_data_count=512,由图可以看出,rd_data_count并没有=512,因此该条件就不会触发。所以,在自定义空满标准信号后,在wr_data_count=512时,拉高prog_full,rd_clk只需要判断此信号是否为高,就能够操作后续逻辑了。

在这里插入图片描述
  我们设置的当FIFO内部wr_data_dount小于300时候,拉低prog_full信号,因此用户可以通过自定义prog_full和prog_empty信号的范围,就能很好的更准确的判断。

五、使用FIFO的注意事项总结

  1. 在使用小容量FIFO时,可以选择分布式ram构成的FIFO,这样比较节省block ram的资源。大容量的FIFO优先使用block ram;
  2. 读写宽度不一致时,注意先出高位,再出低位;
  3. FIFO异步复位时,读写时钟必须都存在;
  4. 异步复位至少复位三个慢时钟周期;
  5. 复位期间不能进行读写操作;
  6. 复位拉低后,等待60个周期或者读写安全信号拉低后,才进行读写操作;
  7. FIFO的读写数量不准,建议自己对写入的数据进行计数,或者使用可编程的空满信号加以判断;

参考

《PG057》

  • 40
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱奔跑的虎子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值