CPU设计实战 第五章 5.4任务与实践

1.除法器的实现

除法器是移植了chiplab中的除法器 IP/myCPU/div.v · 龙芯教育/chiplab - Gitee.com

但在移植的过程中出现了一些问题: ①run all Error: div no end in 34 clk! $finish called at time : 1745 ns : File "H:/1/div_tb.v" Line 162

出现这个问题的原因是我把reset和resetn搞混了,div.v中的端口中定义的是reset而测试文件div_tb.v中定义的是resetn,我误认为它俩是同一个变量,于是为了解决div.v和div_tb.v中的端口不一致,我把div.v中的reset全改成了resetn,导致出现这个报错。

原因是reset和resetn实际上是相反的,当reset为1/0时,resetn为0/1,因为这一点改动导致:

 always @(posedge div_clk) begin  //33位除法计算
     if (resetn || ~div || complete_delay) begin
         count <= 8'd32;     //计算33次,0010 0000
         tmp_r <= 33'b0;
     end

当div_clk到达时,上面代码中的判断语句恒为真,count的值就恒为32,无法计数,而导致测试文件div_tb.v出现Error: div no end in 34 clk!

于是我将div.v中的resetn还复原成reset,然后在div_tb.v中调用div模块的代码中改动为:

     div uut (
         .div_clk(div_clk), 
         .reset(!resetn), //原语句为 .resetn(resetn)
         .div(div), 
         .div_signed(div_signed), 
         .x(x), 
         .y(y), 
         .s(s), 
         .r(r), 
         .complete(complete)

修改完毕后成功解决Error: div no end in 34 clk!报错。

然而却又出现了下面的报错:

run all [time@ 815000]Error: x= 1206705039, y= 2033215986, signed=0, s= 0, r= 2097015289, s_ref= 0, r_ref= 1206705039, s_OK=1, r_OK=0

显示余数的结果计算错误,仔细观察波形发现上的计算结果总是比正确结果慢一个时钟出现:

 也就是当complete为1也就是除法运算结束时还没有将商的结果及时赋值过去,在下一个时钟也就是complete_delay为1时才得到商的正确结果。

发现当count为8'hf0时complete_delay才为1  

assign complete_delay = (count == 8'hf0);       //1111 0000
assign real_complete = complete_delay || complete;
always @(posedge div_clk) begin  //33位除法计算
    if (reset || ~div || complete_delay) begin
        count <= 8'd32;     //计算33次,0010 0000
        tmp_r <= 33'b0;
    end
    else if (~(count[7])) begin  //0010 0000 -> 0001 1111 -> ... ->0000 0000 ->1111 1111
        if (tmp_d[32]) begin    //tmp_d为负数
            UnsignS <= {UnsignS[31:0], 1'b0};   //tmp_d为负数,即相减结果为负,不够减,商0
            tmp_r <= result_r;
        end 
        else begin
            UnsignS <= {UnsignS[31:0], 1'b1};   //tmp_d为正数,即相减结果为正,够减,商1
            tmp_r <= tmp_d;
        end
        count <= count - 8'd1;
    end
    else begin   //计算完33位除法后,将此时的tmp_r(余数)复制给UnsignR
        UnsignR <= tmp_r;
        count   <= 8'hf0; //完成33位除法运算complete signal only maintain one clock
    end
end
assign result_r = {tmp_r[31:0], UnsignX[count]}; 

通过complete_delay发现问题出在了UnsignR的赋值上,即运算结束后tmp_r的值还没有赋给UnsignR,当count的值从0010 0000减为1111 1111时才执行UnsignR <= tmp_r,此时才得到了商的绝对值,而除法运算在count从0010 0000的值减到0000 0000时就已经完成,故慢了一个时钟周期。且发现实际上complete_delay根本没起到任何作用可以直接不用。修改代码如下:

//assign complete_delay = (count == 8'hf0);       //注释掉,用不到complete_delay
assign real_complete = complete; //删掉complete_delay
always @(posedge div_clk) begin  //33位除法计算
    if (reset || ~div ) begin //删掉complete_delay
        count <= 8'd32;     //计算33次,0010 0000
        UnsignR <= 33'b0; 	//将tmp_r改为UnsignR
    end
    else if (~(count[7])) begin  //0010 0000 -> 0001 1111 -> ... ->0000 0000 ->1111 1111
        if (tmp_d[32]) begin    //tmp_d为负数
            UnsignS <= {UnsignS[31:0], 1'b0};   //tmp_d为负数,即相减结果为负,不够减,商0
            UnsignR <= result_r; 	 //将tmp_r改为UnsignR
        end 
        else begin
            UnsignS <= {UnsignS[31:0], 1'b1};   //tmp_d为正数,即相减结果为正,够减,商1
            UnsignR <= tmp_d;		//将tmp_r改为UnsignR
        end
        count <= count - 8'd1;
    end
   /*删掉下面的代码 
   else begin   //计算完33位除法后,将此时的tmp_r(余数)复制给UnsignR
        UnsignR <= tmp_r;
        count   <= 8'hf0; //完成33位除法运算complete signal only maintain one clock
    end
    */
end
assign result_r = {UnsignR[31:0], UnsignX[count]};  //将tmp_r改为UnsignR

修改后成功解决报错!  

2.乘法器

乘法器是直接用的chiplab的乘法器…

3.添加指令

①添加ADD、ADDI、SUB指令

ADD复用ADDU的数据通路,ADDI复用ADDIU的数据通路,SUB复用SUBU的数据通路

id_stage.v

wire        inst_add;  //新增add指令 
wire        inst_addi; //新增addi指令  
wire        inst_sub;  //新增sub指令 
assign inst_add    = op_d[6'h00] & func_d[6'h20] & sa_d[5'h00];  //新增add
assign inst_addi   = op_d[6'h08];  //新增	
assign inst_sub    = op_d[6'h00] & func_d[6'h22] & sa_d[5'h00];  //新增sub
//inst_add、inst_addi,分别复用add和addiu在执行、访存、写回阶段的数据通路
assign alu_op[ 0] = inst_addu | inst_addiu | inst_lw | inst_sw | inst_jal | inst_add | inst_addi;
//sub复用subu在执行、访存、写回阶段的数据通路
assign alu_op[ 1] = inst_subu | inst_sub;  //添加inst_sub
//addi复用addiu在译码级的数据通路
assign src2_is_imm  = inst_addiu | inst_lui | inst_lw | inst_sw | inst_slti | inst_sltiu | inst_addi;
assign dst_is_rt    = inst_addiu | inst_lui | inst_lw | inst_slti | inst_sltiu | inst_andi | inst_ori | inst_xori | inst_addi;
//添加  复用译码级数据通路 inst_need_rs和inst_need_rt是和前递相关的变量
assign inst_need_rs =inst_add     |   //rd,rs,rt 新增
    				 inst_addi    |   //rt,rs,imm 新增
					 inst_sub     ;   //rd,rs,rt 新增
assign inst_need_rt =inst_add     |   //rd,rs,rt 新增
    				 inst_addi    |   //rt,rs,imm 新增
					 inst_sub     ;   //rd,rs,rt 新增

②SLTI、SLTIU指令

SLTI与SLT对于两个源操作数的比较运算是完全一致的,且SLTI与ADDIU的操作数来源和目的寄存器号来源一致。

所以SLTI复用SLT指令在执行和访存阶段的数据通路,复用ADDIU在译码和写回阶段(即写回的寄存器地址)的数据通路。

SLTIU与SLTU对于两个源操作数的比较运算是完全一致的,且SLTIU与ADDIU的操作数来源和目的寄存器号来源一致。

SLTIU复用SLTU指令在执行和访存阶段的数据通路,复用ADDIU在译码和写回阶段的数据通路。

id_stage.v

wire        inst_slti; //新增slti指令
wire        inst_sltiu; //新增sltiu指令
assign inst_slti   = op_d[6'h0a];  //新增
assign inst_sltiu  = op_d[6'h0b];  //新增
//slti复用slt在执行和访存阶段的数据通路
assign alu_op[ 2] = inst_slt  | inst_slti;  //添加inst_slti
//sltiu复用sltu在执行和访存阶段的数据通路
assign alu_op[ 3] = inst_sltu | inst_sltiu; //添加sltiu
//inst_slti、inst_sltiu复用addiu在译码和写回阶段的数据通路
assign src2_is_imm  = inst_addiu | inst_lui | inst_lw | inst_sw | inst_slti | inst_sltiu | inst_addi;
//slti和sltiu复用addiu在译码和写回阶段的数据通路
assign dst_is_rt    = inst_addiu | inst_lui | inst_lw | inst_slti | inst_sltiu | inst_andi | inst_ori | inst_xori | inst_addi;//添加slti、sltiu、inst_andi、inst_ori、inst_xori
//添加 复用译码级数据通路 inst_need_rs和inst_need_rt是和前递相关的变量
assign inst_need_rs = inst_slti    |   //rt,rs,imm 新增
                      inst_sltiu   ;   //rt,rs,imm 新增
assign inst_need_rt = inst_slti    |   //rt,rs,imm 新增
                      inst_sltiu   ;   //rt,rs,imm 新增

③ANDI、ORI、XORI指令

ANDI、ORI、XORI与AND、OR、XOR进行的逻辑运算一样,且和ADDIU一样均是将结果写入rt寄存器

所以ANDI、ORI、XORI复用AND、OR、XOR在执行和访存阶段的数据通路,以及复用ADDIU指令在写回阶段的数据通路。

不过,ANDI、ORI、XORI和ADDIU的第二个源操作数不一样,所以在译码阶段不能完全复用ADDIU的数据通路,第一个源操作数可以复用ADDIU的数据通路(即默认的数据通路,把rs寄存器的值当做第一个源操作数),而第二个源操作数是将”指令码的低16位立即数零扩展至32位“,没有可以复用的数据通路,所以第二个源操作数的产生需要新增一个数据通路,并将信号传递到执行级,最终在执行级实现第二个源操作数的产生。

id_stage.v

wire        inst_andi; //新增
wire        inst_ori;  //新增
wire        inst_xori; //新增
wire        src2_is_imm_zero; //立即数0扩展。新增
assign inst_andi   = op_d[6'h0c];  //新增
assign inst_ori    = op_d[6'h0d];  //新增
assign inst_xori   = op_d[6'h0e];  //新增
//andi、ori、xori复用and、or、xor在执行和访存阶段的数据通路
assign alu_op[ 4] = inst_and  | inst_andi;  //新增andi
assign alu_op[ 6] = inst_or  | inst_ori;   //新增ori
assign alu_op[ 7] = inst_xor | inst_xori; //新增xori
//andi、ori、xori复用addiu在写回阶段的数据通路(就是写回的是那个寄存器)
assign dst_is_rt  = inst_addiu | inst_lui | inst_lw | inst_slti | inst_sltiu | inst_andi | inst_ori | inst_xori | inst_addi;//添加slti、sltiu、inst_andi、inst_ori、inst_xori
//添加 复用译码级数据通路 inst_need_rs和inst_need_rt是和前递相关的变量
assign inst_need_rs = inst_andi    |   //rt,rs,imm 新增
                      inst_ori     |   //rt,rs,imm 新增
                      inst_xori    ;   //rt,rs,imm 新增
assign inst_need_rt = inst_andi    |   //rt,rs,imm 新增
                      inst_ori     |   //rt,rs,imm 新增
                      inst_xori    ;   //rt,rs,imm 新增
//andi、ori、xori第二个源操作数的产生
assign src2_is_imm_zero = inst_andi | inst_ori | inst_xori;//立即数0扩展。新增
//通过ds_to_es_bus将andi、ori、xori第二个源操作数的产生的信号传递到执行级
assign ds_to_es_bus = { src2_is_imm_zero} //立即数0扩展。新增

对于第二个源操作数的产生需要在执行级实现:

exe_stage.v

wire        es_src2_is_imm_zero;  //立即数0扩展。新增
assign {es_src2_is_imm_zero} = ds_to_es_bus_r; //执行级接受译码级第二个源操作数的产生信号
//产生第二个源操作数
assign es_alu_src2 = es_src2_is_imm_zero ? {16'b0, es_imm[15:0]} :  //新增 立即数零扩展
                     es_src2_is_imm ? {{16{es_imm[15]}}, es_imm[15:0]} :  //立即数符号扩展
                     es_src2_is_8   ? 32'd8 :        
                                      es_rt_value;

④SLLV、SRLV、SRAV指令

SLLV、SRLV、SRAV复用SLL、SRL、SRA在执行、访存、写回阶段的数据通路,复用ADDU在译码阶段的数据通路。

wire        inst_sllv; //新增
wire        inst_srlv; //新增
wire        inst_srav; //新增
assign inst_sllv   = op_d[6'h00] & func_d[6'h04] & sa_d[5'h00]; //新增
assign inst_srlv   = op_d[6'h00] & func_d[6'h06] & sa_d[5'h00]; //新增
assign inst_srav   = op_d[6'h00] & func_d[6'h07] & sa_d[5'h00]; //新增
//sllv复用sll在执行、访存、写回阶段的数据通路
assign alu_op[ 8] = inst_sll | inst_sllv; //新增inst_sllv
//slrv复用srl在执行、访存、写回阶段的数据通路
assign alu_op[ 9] = inst_srl | inst_srlv; //新增inst_srlv
//srav复用sra在执行、访存、写回阶段的数据通路
assign alu_op[10] = inst_sra | inst_srav; //新增inst_srav
//至于SLLV、SRLV、SRAV复用ADDU在译码阶段的数据通路。不需要采用改动,因为默认的就是rd,rs,rt
//使用默认的数据通路即可
//添加 复用译码级数据通路 inst_need_rs和inst_need_rt是和前递相关的变量
assign inst_need_rs = inst_andi    |   //rt,rs,imm 新增
                      inst_ori     |   //rt,rs,imm 新增
                      inst_xori    ;   //rt,rs,imm 新增
assign inst_need_rt = inst_andi    |   //rt,rs,imm 新增
                      inst_ori     |   //rt,rs,imm 新增
                      inst_xori    ;   //rt,rs,imm 新增

⑤MULT、MULTU、DIV、DIVU指令

乘除法指令的两个源操作数来自rs和rt寄存器,与ADDU等指令相同,故源操作数的读出可复用ADDU数据通路(即不用做改动,采用默认的通路即可)乘、除法的结果要写入HI、LO寄存器。

id_stage.v

wire        inst_mult;  //新增有符号乘法
wire        inst_multu; //新增无符号乘法
wire        inst_div;   //新增有符号除法
wire        inst_divu;  //新增无符号除法
wire [1:0]  mul_div_op; //新增乘除法信号
wire        mul_div_sign; //新增乘除法符号信号
assign inst_mult   = op_d[6'h00] & func_d[6'h18] & rd_d[5'h00] & sa_d[5'h00]; //新增
assign inst_multu  = op_d[6'h00] & func_d[6'h19] & rd_d[5'h00] & sa_d[5'h00]; //新增
assign inst_div    = op_d[6'h00] & func_d[6'h1a] & rd_d[5'h00] & sa_d[5'h00]; //新增
assign inst_divu   = op_d[6'h00] & func_d[6'h1b] & rd_d[5'h00] & sa_d[5'h00]; //新增
//乘除法
assign mul_div_op[0] = inst_mult | inst_multu; //新增  mul_div_op需传递到EXE级
assign mul_div_op[1] = inst_div  | inst_divu;  //新增
//mul_div_sign是有符号乘、除法的控制信号
assign mul_div_sign  = inst_mult  | inst_div ; //新增  并传递到EXE级
//将mul_div_op与mul_div_sign传递到exe级
assign ds_to_es_bus = {mul_div_op  ,  //143:142 新增
                       mul_div_sign,  //141:141 新增
                      }
//添加 复用译码级数据通路 inst_need_rs和inst_need_rt是和前递相关的变量
assign inst_need_rs = inst_mult    |   //rs,rt 新增
                      inst_multu   |   //rs,rt 新增
                      inst_div     |   //rs,rt 新增
                      inst_divu    ;   //rs,rt 新增
assign inst_need_rt = inst_mult    |   //rs,rt 新增
                      inst_multu   |   //rs,rt 新增
                      inst_div     |   //rs,rt 新增
                      inst_divu    ;   //rs,rt 新增

exe_stage.v

由于用的chiplab的booth两位乘+华莱士树的乘法器,一个时钟周期内就可以得到乘法结果,由于乘法没有完成信号,不知道乘法什么时候完成,如果在EXE级将乘除法结果写入HI、LO寄存器会导致写入的结果与真实值差一个时钟周期,故将乘除法结果写入HI、LO寄存器放到访存级,这样到访存级时刚好可以得到乘法的正确结果。

//将乘除法结果通过接口传到访存级
module exe_stage(
    output [63:0] mul_result, //新增
    output [31:0] div_result, //新增
    output [31:0] mod_result  //新增
);
wire [1:0]  es_mul_div_op; //新增乘除法信号
wire        es_mul_div_sign; //新增乘除法符号信号
wire        div_stall; //新增
wire        es_div_enable; //新增
wire        div_complete; //新增
wire        es_mul_enable; //新增  
//接受从译码级传来的乘除法信号
assign {
        es_mul_div_op  ,  //143:142 新增
        es_mul_div_sign   //141:141 新增
        } = ds_to_es_bus_r;
//将乘、除法信号传递到访存级用于写HI、LO寄存器
assign es_to_ms_bus = {
        				es_mul_div_op  //76:75 新增
    				   };
//如果在执行乘法指令便阻塞流水线等待乘法执行完成,与前递相关
assign dep_need_stall       = es_load_op | es_valid | es_mul_enable; 
//等待除法执行完毕
assign div_stall     = es_div_enable & ~div_complete; 
//等待除法完成
assign es_ready_go    = 1'b1 && !div_stall; //新增div_stall

assign es_div_enable = es_mul_div_op[1] & es_valid; 
assign es_mul_enable = es_mul_div_op[0]; 
//调用乘除法模块
div u_div(
    .div_clk         (clk           ),
    .reset           (reset          ),
    .div             (es_div_enable  ),
    .div_signed      (es_mul_div_sign),
    .x               (es_alu_src1    ),
    .y               (es_alu_src2   ),
    .s               (div_result     ),
    .r               (mod_result     ),
    .complete        (div_complete   )
    );

mul u_mul(
    .mul_clk         (clk           ),
    .reset           (reset          ),
    .mul_signed      (es_mul_div_sign),
    .x               (es_alu_src1    ),
    .y               (es_alu_src2    ),
    .result          (mul_result     )
    );

mem_stage.v

//接受来自执行级传递过来的乘除法结果
module mem_stage(
    input [63:0] mul_result, //新增
    input [31:0] div_result, //新增
    input [31:0] mod_result  //新增
);
wire [1:0]  es_mul_div_op; //新增乘除法信号
//接受来自执行级的乘、除法信号   
assign { es_mul_div_op }  = es_to_ms_bus_r;
//乘除法器
reg [31:0] hi;  //HI用于存放乘法结果的高32位和除法的余数
reg [31:0] lo;  //LO用于存放乘法结果的低32位和除法的商
wire        hi_write; //新增 hi写使能
wire        lo_write; //新增 lo写使能
assign hi_write = es_mul_div_op[1] | es_mul_div_op[0] ;  //当执行除法、乘法时,触发写hi寄存器
assign lo_write = es_mul_div_op[1] | es_mul_div_op[0] ;  //当执行除法、乘法时,触发写lo寄存器
//将乘、除法结果写入HI、lo寄存器
//-----{HI/LO寄存器}begin
    //要写入HI的数据
    always @(posedge clk)
    begin
        if (hi_write)    //
        begin
            if (es_mul_div_op[0] )
            begin
                hi <= mul_result[63:32];
            end
            else if (es_mul_div_op[1]) 
            begin 
                hi <= mod_result;
            end  
        end
    end
    //要写入LO的数据
    always @(posedge clk)
    begin
        if (lo_write)  //
        begin
            if (es_mul_div_op[0])
            begin
                lo <= mul_result[31:0];
            end
            else if (es_mul_div_op[1]) 
            begin
                lo <= div_result; //
            end
        end
    end
//-----{HI/LO寄存器}end
//*******************************************************************************

⑥MTHI、MTlO、MFHI、MFLO指令

MTHI、MTLO读通用寄存器rs的源操作数,写入HI、LO寄存器

MTHI、MTLO读通用寄存器rs的源操作数,这与ADDU等指令类似,只需要将rs寄存器的值写入HI、LO寄存器即可。

MFHI、MFLO读HI、LO寄存器的值,写入通用寄存器

MFHI、MFLO像其他写通用寄存器的指令一样,当读取到HI、LO寄存器的之后随着流水线逐级前进到写回级完成写通用寄存器

因为HI、LO寄存器定义在访存级所以下面要将mthi、mtlo、mfhi、mflo信号传递到访存级

id_stage.v

wire        mfhi;   //新增
wire        mflo;   //新增
wire        mthi;   //新增
wire        mtlo;   //新增                      };
assign mfhi = inst_mfhi; //新增  传递到EXE级
assign mflo = inst_mflo; //新增  传递到EXE级
assign mthi = inst_mthi; //新增  传递到EXE级
assign mtlo = inst_mtlo; //新增  传递到EXE级
//将译码级的mfhi、mflo、mthi、mtlo信号传递到EXE级
assign ds_to_es_bus = {
                       mfhi        ,  //140:140 新增
                       mflo        ,  //139:139 新增
                       mthi        ,  //138:138 新增
                       mtlo          //137:137  新增
//添加 复用译码级数据通路 inst_need_rs是和前递相关的变量
assign inst_need_rs = inst_mthi    |   //rs 新增
                      inst_mtlo    ;   //rs 新增

exe_stage.v

wire        mfhi;   //新增
wire        mflo;   //新增
wire        mthi;   //新增
wire        mtlo;   //新增
assign {
        mfhi        ,     //140:140 新增
        mflo        ,     //139:139 新增
        mthi        ,     //138:138 新增
        mtlo              //137:137  新增  
        } = ds_to_es_bus_r;
assign es_to_ms_bus = {                      
    				   es_rs_value ,     //108:77 新增 用于mthi、mtlo指令读rs寄存器的值
                       mfhi        ,     //74:74 新增
                       mflo        ,     //73:73 新增
                       mthi        ,     //72:72 新增
                       mtlo              //71:71  新增
    				  };

mem_stage.v

wire        mfhi;   //新增
wire        mflo;   //新增
wire        mthi;   //新增
wire        mtlo;   //新增
wire [31:0] ms_rs_value;
assign {
        ms_rs_value   ,  //108:77 新增 用于mthi、mtlo指令读rs寄存器的值
        mfhi        ,     //74:74 新增
        mflo        ,     //73:73 新增
        mthi        ,     //72:72 新增
        mtlo             //71:71 新增         
       } = es_to_ms_bus_r;
//修改乘除法器
reg [31:0] hi;  //HI用于存放乘法结果的高32位和除法的余数
reg [31:0] lo;  //LO用于存放乘法结果的低32位和除法的商
wire        hi_write; //新增
wire        lo_write; //新增

assign hi_write = es_mul_div_op[1] | es_mul_div_op[0] | mthi;  //当执行除法、乘法、mthi时,触发写hi寄存器
assign lo_write = es_mul_div_op[1] | es_mul_div_op[0] | mtlo;  //当执行除法、乘法、mtlo时,触发写lo寄存器
//-----{HI/LO寄存器}begin
    //要写入HI的数据
    always @(posedge clk)
    begin
        if (hi_write)    //
        begin
            if (es_mul_div_op[0] )
            begin
                hi <= mul_result[63:32];
            end
            else if (es_mul_div_op[1]) 
            begin 
                hi <= mod_result;
            end  
            else if (mthi)
            begin
                hi <= ms_rs_value;
            end
        end
    end
    //要写入LO的数据
    always @(posedge clk)
    begin
        if (lo_write)  //
        begin
            if (es_mul_div_op[0])
            begin
                lo <= mul_result[31:0];
            end
            else if (es_mul_div_op[1]) 
            begin
                lo <= div_result; //
            end
            else if (mtlo)
            begin
                lo <= ms_rs_value;
            end
        end
    end
//-----{HI/LO寄存器}end
//mfhi.mflo指令读取到hi、lo寄存器的值后通过复用ms_final_result的数据通路传递到写回级,写入通用寄存器。
assign ms_final_result = mfhi ? hi :
                         mflo ? lo : 
                         ms_res_from_mem ? mem_result
                                         : ms_alu_result;

 至此第五章 5.4任务与实践所有指令添加完成

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值