BUAACO-PIPELINE-CPU

流水线CPU

一、流水线CPU概述

1、流水线CPU的原理

流水线CPU是为提高吞吐量而创造的,五段式流水线CPU的吞吐量是单周期CPU的五倍,同一时间CPU上最多有五条指令在运行。如何达到同一CPU上五条指令呢?答案就在于把每条指令都拆分成五个阶段,按照CPU硬件执行流来拆成五段:IF(instruction fetch)、DEC(decode)、EXE(execute)、ME(memory)、WB(write back),CPU上五段部分分别执行一条指令的一个阶段。

因为CPU上五段分别进行不同指令的不同阶段,每段都需要自己当前执行指令的数据:IF段所用数据为指令地址,可有pc寄存器提供;DE、EXE、ME、WB段所用数据包含但不限于指令内容,一定需要对应流水线寄存器保存。

2、流水线CPU设计的难点

流水线CPU同时有多条指令运行,一个很重要的设计因素是解决冲突。冲突包含硬件冲突、控制冲突和数据冲突。

  • 硬件冲突

    硬件冲突是说同一时刻需要对同一互斥硬件(一次只允许一次读或写)进行访问,举例来说,D段需要从存储器中取出指令,M段需要对存储器写入数据,这两个操作同时进行就会带来冲突,在这里IM和DM是独立的两个存储器,因此不必考虑。

  • 控制冲突

    控制冲突是分支指令和跳转指令带来的冲突。分支指令的最终分支地址晚于下一周期到来,导致下一条指令的地址不能及时算出或者决定;跳转指令的跳转地址晚于下一周期到来,导致上述同样冲突。解决这样的冲突主要是通过假设不跳转或者延迟槽。假设不跳转是说先假设不跳转和分支,正常执行下一条指令,当计算出要跳转时清楚已执行的结果。延迟槽是说跳转指令后面加空指令nop,即等待跳转地址计算出来再决定是继续下一条指令还是跳转。

  • 数据冲突

    数据冲突是关于数据“新鲜性”的冲突。指令的执行离不开寄存器,有的指令会利用寄存器数据,有的指令会写回寄存器,有的指令两者皆有,当前序执行的指令的目的寄存器(将要写回的寄存器)和后序执行的指令的源寄存器(利用其数据的寄存器)相同时,就存在数据关联。当前序指令数据还未写入寄存器,后序指令就要用到该数据时,就会产生错误(冲突)。解决这种冲突需要暂停或者转发:当前序指令的“新”将要写回寄存器的数据(计算)赶不及后序指令使用其数据时就需要暂停后序指令,直到前序指令的“新”数据准备好;当前序指令的“新”数据能赶上后序指令的使用,当前序指令的“新”数据准备好后,转发给后序指令当前所处阶段的流水线寄存器,以达到更新数据的效果。

注意:以下是笔者设计流水线的流程(吃百家饭得来

二、流水线CPU的功能设计

1、支持指令

strldcal_rcal_iluib_typejjrjaljalrshamtmod
swlwadduoribeqsllmult
sblbaddxoribnesramultu
shlhsubuandiblezsrldiv
lbusubaddiubgtzdivu
lhusllvaddibltz
sravsltiubgez
srlvslti
and
or
xor
nor
slt
sltu

2、流水线寄存器

流水线寄存器记录上一阶段的数据并保持,供所在阶段使用,因此每阶段寄存器所需数据如下:

DEMW
IRIRIRIR
RD
AOAO
RD1
EXTDEXTDEXTD
RD2RD2
PC4
PC8PC8PC8PC8
PCPCPCPC
TNEW_ETNEW_M
HIHI
LOLO

3、所需硬件

先不考虑所有冲突而只考虑执行指令,和单周期CPU的功能硬件设计一样,列出所需硬件,不一样的是指令分五段执行,硬件也可分阶段列出。

本表不包含多选器,多选器在整合数据通路时给出。

下面列出的硬件中有三个值得说明:

  • BEEXT和RDEXT

    支持指令中有sh,sb,lh,lhu,lb,lbu这些对半字、字节操作的指令,但是存储器是按照字读或者写的,因此对于存储指令需要BEEXT给出字节选择信号,对于加载指令需要RDEXT处理读出的字。

    BEEXT接受的输入为AO_M1_0和三个布尔信号(分别代表指令是否为sw,sh,sb)。AO_M1_0意义是ALU输出在M段结果的低两位,也就是写入地址对4取模的结果(一个字4字节)。BEEXT产生的输出是四位信号,分别表示将要存储的数据的四个字节是否存储。

    RDEXT接受的输入为AO_W1_0、RD_W和五个布尔信号(分别表示指令是否是lw,lh,lhu,lb,lbu)。AO_W1_0是ALU输出在W段结果的低两位,也就是读出地址对4取模的结果。RD_W是DM中读出的字。按照五条指令的要求分别对RD_W处理,最后根据五个布尔信号选择一个输出。

  • MDU

    乘除运算单元和hi,lo寄存器所在。whi和wlo是hi,lo寄存器的写入信号。start是乘除运算的启动信号,busy是乘除运算进行中的信号。这里乘法模拟用5个时钟周期完成(start后的第一个上升沿开始,busy高亮五个周期),除法模拟用10个时钟周期完成。

阶段moduleinputoutput功能描述
IFPCDQ
ADD4PCPC4
ADD8PCPC8
IMIAIR
DERFA1RD1从寄存器堆读出寄存器数据
A2RD2
EXTI16EXTD选择输出SIMM,LIMM,UIMM
EXTOP
CMP0FRSDLESS输出和0比较的结果,独热输出
GREAT
LE_EQ
GR_EQ
CMPD1RES输出比较结果
D2
NPCPC4NEXTPC选择输出BPC,JPC
I26
NPCOP
EXMDUNUM1BUSY乘除模块,WHI,WLO为写使能,WDHI,WDLO为写入数据
NUM2HI
MDUOPLO
START
WHI
WLO
ALUAAO执行不同操作
B
SHAMT
ALUOP
MEBEEXTAO_M1_0BE得到字节写入使能信号,为1的那一位代表对应dm中的字相应部分写入字节
SW_M
SH_M
SB_M
DMDA[11:2]RD支持写入字节、半字、字和读出字
WD
WM
BE[3:0]
WBRDEXTRD_WRDEXTD将dm中取出的字按照指令做相应处理得到将写入rf的字
AO_W1_0
LW_W
LHU_W
LH_W
LBU_W
LB_W
RFA3
WD3
WR

4、数据通路

将流水线寄存器和功能硬件针对每一条指令连接起来。

模块INPUTNOPLDSTRCAL_RCAL_ISHAMTLUIJJRJALJALRB_TYPEMDU
PCD
ADD4PCQQQQQQQQQQQ
ADD8PCQQ
IMIAQQQQQQQQQQQQQ
PCDPC4PC4PC4PC4PC4PC4PC4PC4PC4
REG_DIR_DIRIRIRIRIRIRIRIRIRIRIRIRIR
PC4_DPC4PC4PC4
PC8_DPC8PC8
RFA1IR_D[RS]IR_D[RS]IR_D[RS]IR_D[RS]IR_D[RS]IR_D[RS]IR_D[RS]IR_D[RS]
A2IR_D[RT]IR_D[RT]IR_D[RT]IR_D[RT]
EXTI16IR_D[I16]IR_D[I16]IR_D[I16]IR_D[I16]
CMPD1RD1
D2RD2
CMP0FRSDFRSD
NPCPC4PC4_DPC4_DPC4_D
I26IR_D[I26]IR_D[I26]IR_D[I16]
PCDNEXTPCRD1NEXTPCRD1NEXTPC/PC4
REG_EIR_EIR_DIR_DIR_DIR_DIR_DIR_DIR_DIR_DIR_DIR_DIR_DIR_DIR_D
RS_ERD1RD1RD1RD1RD1
EXT_EEXTDEXTDEXTDEXTD
RT_ERD2RD2RD2RD2
PC8_EPC8_DPC8_D
MDUNUM1RS_E
NUM2RT_E
ALUARS_ERS_ERS_ERS_E
BEXT_EEXT_ERT_EEXT_ERT_E
SHAMTIR_E[SH]
REG_MIR_MIR_EIR_EIR_EIR_EIR_EIR_EIR_EIR_EIR_EIR_EIR_EIR_EIR_E
AO_MAOAOAOAOAO
RT_MRT_E
PC8_MPC8_EPC8_E
EXT_MEXT_E
HI_M
LO_M
BEEXTAO_M1_0AO_M[1:0]
DMDAAO_MAO_M
WDRT_M
BEBE
REG_WIR_WIR_MIR_MIR_MIR_MIR_MIR_MIR_MIR_MIR_MIR_MIR_MIR_MIR_M
RD_WRD
AO_WAO_MAO_MAO_MAO_M
PC8_WPC8_MPC8_M
EXT_WEXT_M
HI_W
LO_W
RDEXTRD_WRD_W
AO_W1_0AO_W[1:0]
RFA3IR_W[RT]IR_W[RD]IR_W[RT]IR_M[RD]IR_W[RT]$31IR_W[RD]
WD3RDEXTDAO_WAO_WAO_WEXT_WPC8_WPC8_W

5、整合数据通路

将上面的数据通路整合起来,得到硬件间的连接情况。当某一硬件的某一端口前输入来源不唯一时,需添加多选器来选择一个作为输入。

外模块内模块输入复用器选择信号
IFUPCQPC4NEXTPCRD1MFPCPCSEL
ADD4PCQ
ADD8PCQ
IMIAQ
nowstate
REG_DIR_DIR
PC4_DPC4
PC8_DPC8
RFURFA1IR_D[RS]
A2IR_D[RT]
A3IR_W[RT]IR_W[RD]31MFA3A3SEL
WD3RDEXTDAO_WEXT_WPC8_WHI_WLO_WMFWD3WD3SEL
IDUEXTI16IR_D[I16]
CMP0FRSDRD1
CMPD1RD1
D2RD2
NPCPC4PC4_D
I26IR_D[I26]
REG_EIR_EIR_D
RS_ERD1
EXT_EEXTD
RT_ERD2
PC8_EPC8_D
EXUMDUNUM1RS_E
NUM2RT_ERS_EMFNUM2NUM2SEL
ALUARS_E
BEXT_ERT_EMFBBSEL
SHAMTIR_E[SH]
REG_MIR_MIR_E
AO_MAO
RT_MRT_E
PC8_MPC8_E
EXT_MEXT_E
HI_MHI
LO_MLO
MEUBEEXTAO_M1_0AO_M[1:0]
DMDAAO_M
WDRT_M
BEBE
REG_WIR_WIR_M
RD_WRD
AO_WAO_M
PC8_WPC8_M
EXT_WEXT_M
HI_WHI_M
LO_WLO_M
RFURDEXTRD_WRD_W
AO_W1_0AO_W[1:0]

6、控制信号

列出所有指令的控制信号表。

TYPE指令INPUTOUTPUT
OPFUNCTRTPCSELEXTOPNPCOPALUCTRLALUOPBSELSTARTMDUOPWHIWLONUM2SELWMA3SELWD3SELWR
LDLW100011XX00X00(+)00X00X0(不写)001
LHU100101XX00X00(+)00X00X0001
LH100001XX00X00(+)00X00X0001
LBU100100XX00X00(+)00X00X0001
LB100000XX00X00(+)00X00X0001
STRSW101011XX00X00(+)00X00X1XX0
SH101001XX00X00(+)00X00X1XX0
SB101000XX00X00(+)00X00X1XX0
CAL_RADD000000100000X0XX15010X00X0111
ADDU000000100001X0XX15010X00X0111
SUB000000100010X0XX15110X00X0111
SUBU000000100011X0XX15110X00X0111
SLLV000000000100X0XX15410X00X0111
SRAV000000000111X0XX15510X00X0111
SRLV000000000110X0XX15610X00X0111
AND000000100100X0XX151010X00X0111
OR000000100101X0XX15210X00X0111
XOR000000100110X0XX151110X00X0111
NOR000000100111X0XX151210X00X0111
SLT000000101010X0XX15310X00X0111
SLTU000000101011X0XX151310X00X0111
SHAMTSLL000000000000X0XX15710X00X0111
SRL000000000010X0XX15910X00X0111
SRA000000000011X0XX15810X00X0111
CAL_IORI001101XX02X2200X00X0011
XORI001110XX02X111100X00X0011
ANDI001100XX02X101000X00X0011
SLTIU001011XX00X131300X00X0011
SLTI001010XX00X3300X00X0011
ADDI001000XX00X0000X00X0011
ADDIU001001XX00X0000X00X0011
LUI001111XX01XXXX0X00X0021
B_TYPEBEQ000100XXRESX0XXX0X00X0XX0
BNE000101XX~RESX0XXX0X00X0XX0
BLEZ000110XX~GREATX0XXX0X00X0XX0
BGTZ000111XXGREATX0XXX0X00X0XX0
BLTZ000001X00000LESSX0XXX0X00X0XX0
BGEZ000001X00001~LESSX0XXX0X00X0XX0
J000010XX1X1XXX0X00X0XX0
JR000000001000X2XXXXX0X00X0XX0
JAL000011XX1X1XXX0X00X0231
JALR000000001001X2XXXXX0X00X0131
NOP000000000000X0XXXXX0X00X0XX0
MODMULT000000011000X0XXXXX100000XX0
MULTU000000011001X0XXXXX110000XX0
DIV000000011010X0XXXXX120000XX0
DIVU000000011011X0XXXXX130000XX0
MFRMFHI000000010000X0XXXXX0X00X0141
MFLO000000010010X0XXXXX0X00X0151
MTOMTHI000000010001X0XXXXX0X10X0XX0
MTLO000000010011X0XXXXX0X0110XX0

三、流水线CPU的冲突解决

0、特别说明

我们现在要考虑流水线CPU的冲突问题,前面说过的三类冲突问题:硬件冲突在这里没有,因为IM和DM的独立;控制冲突通过延迟槽解决,因此硬件上也不需要多余设计(如果用假设不跳转的思路,需要增加流水线寄存器的同步清零信号,以清除错误执行指令的结果,且添加同步清零的控制逻辑);数据冲突是我们着重要考虑的问题,关键在于什么时候暂停,什么时候转发,并且逻辑不能太复杂,经过大佬方法点拨和个人揣摩,我采取如下方法。

1、AT法

AT法是大佬的叫法,按我下面的表格,更适合叫做是SDT法,即源头、目的时间法。

对任意一条标准指令(只涉及通用寄存器的指令),有源头寄存器和目的寄存器:当一条通用指令i需要寄存器s1、寄存器s2的数据时,s1和s2就是i的源头寄存器(不需要用到数据时,s1和s2为0);当一条通用指令i需要写入寄存器des时,des就是i的目的寄存器(不需要写寄存器时,des为0)。

对于源寄存器s有时间tuse_***D(/E/M)***,意思是在从***D(/E/M)***段开始,过几个时钟周期需要使用s里的数据。对于目的寄存器des有数据tnew_***D(/E/M)***,意思是从***D(/E/M)***段开始,过几个时钟周期des的数据被更新。

  • 什么时候要暂停

    暂停的时刻放在D段,每条指令i在D段时,要和它前面的指令j对照判断,看是否需要暂停:当i的源寄存器和j的目的寄存器相同时(设为寄存器k),i和j存在数据关联,这时如果i的tuse大于j的tnew,代表k中数据还没被j准备好(甚至不能转发过来),这时需要暂停D段指令i,否则i会使用k中旧数据里错误运行。

    暂停需要做的工作很简单,将pc和D段流水线寄存器禁止使能,即一直维持当前数据直到D段指令和前序指令没有数据关联或者tuse不大于tnew。

  • 何时转发?转发去哪?转发什么?

    当前序指令的tnew为0时代表新数据已经准备好,需要立刻转发到前面以备可能的数据冲突。

    这里是转发到前面阶段(比如D段)的最开始,紧接着对应阶段的寄存器数据源头(比如RD1、RD2、RS_E、RT_E),和原本流水线寄存器的输出一起被选择(比如多选器MFRSD、MFRTD),当数据关联时就选择转发的数据(比如FRSD、FRTD),否则选择原来数据(比如RD1、RD2)。

    转发的是前序指令已经准备好的数据,在每一阶段可以根据指令类别写出待转发数据,得到真值表。

    特别的对于W段指令到D段指令的转发,由于W段写入和D段读出都是对于寄存器堆,我们采用内部转发:通过寄存器堆的结构使得,在同一个时钟上升沿,若有数据写入寄存器r,也有从寄存器r读出数据的请求,将将写入数据直接读出。这样一来,W段到D段的数据不再需要外部转发。

对于非通用指令,比如mult、div、mfhi、mflo、mthi、mtlo,通过下表分析可知:它们内部不存在数据冲突,因为写回hi/lo和计算出结果在同一周期内完成;它们和通用指令之间的数据冲突处理方式和前面一样。

S1_D/E/MTUSE1_S1DMUXS2_D/E/MTUSE2_S2DMUXDES_TNEW_DESD_
DSTRIR_D[RS]1RD1MFRSDIR_D[RT]2RD2MFRTDXXX
LDIR_D[RS]1RD1MFRSD$0XXXX
CAL_RIR_D[RS]1RD1MFRSDIR_D[RT]1RD2MFRTDXXX
SHAMT$0XIR_D[RT]1RD2MFRTDXXX
CAL_IIR_D[RS]1RD1MFRSD$0XXXX
LUI$0X$0XXXX
B_TYPEIR_D[RS]0RD1MFRSDIR_D[RT]0RD2MFRTDXXX
J$0X$0XXXX
JRIR_D[RS]0RD1MFRSD$0XXXX
JAL$0X$0XXXX
JALRIR_D[RS]0RD1MFRSD$0XXXX
MODIR_D[RS]1RD1MFRSDIR_D[RT]1RD2MFRTD
MFHIHI/$03$0X
MFLOLO/$03$0X
MTHIIR_D[RS]1RD1MFRSD$0X
MTLOIR_D[RS]1RD1MFRSD$0X
ESTRIR_E[RS]0RS_EMFRSEIR_E[RT]1RT_EMFRTE$0XX
LDIR_E[RS]0RS_EMFRSE$0XIR_E[RT]2X
CAL_RIR_E[RS]0RS_EMFRSEIR_E[RT]0RT_EMFRTEIR_E[RD]1X
SHAMT$0XIR_E[RT]0RT_EMFRTEIR_E[RD]1X
CAL_IIR_E[RS]0RS_EMFRSE$0XIR_E[RT]1X
LUI$0X$0XIR_E[RT]0EXT_E
B_TYPE$0X$0X$0XX
J$0X$0X$0XX
JR$0X$0X$0XX
JAL$0X$0X$310PC8_E
JALR$0X$0XIR_E[RD]0PC8_E
MODIR_E[RS]0RS_EMFRSEIR_E[RT]0RT_EMFRTEHI,LO/$05,10/XX
MFHIHI/$02HI/X$0XIR_E[RD]1X
MFLOLO/$02LO/X$0XIR_E[RD]1X
MTHIIR_E[RS]0RS_EMFRSE$0XHI/$00X
MTLOIR_E[RS]0RS_EMFRSE$0XLO/$00X
MSTR$0XIR_M[RT]0RT_MMFRTM$0XX
LD$0X$0XIR_M[RT]1X
CAL_R$0X$0XIR_M[RD]0AO_M
SHAMT$0X$0XIR_M[RD]0AO_M
CAL_I$0X$0XIR_M[RT]0AO_M
LUI$0X$0XIR_M[RT]0EXT_M
B_TYPE$0X$0X$0XX
J$0X$0X$0XX
JR$0X$0X$0XX
JAL$0X$0X$310PC8_M
JALR$0X$0XIR_M[RD]0PC8_M
MOD$0X$0X$0XX
MFHIHI/$01HI_M/X$0XIR_M[RD]0HI_M
MFLOLO/$01LO_M/X$0XIR_M[RD]0LO_M
MTHI$0X$0X$0XX
MTLO$0X$0X$0XX
WSTRXXXX$0XX

2、补充所需硬件

经过AT法的分析,每一阶段寄存器数据的源头不再唯一,还包含从后面阶段(前序指令)转发过来的“最新”数据,需要添加相应多选器,这些多选器mux在上表中已然列出。

阶段moduleinputoutput功能描述
IFMFPCPCSELD选择下一个pc值
PC4
NEXTPC
RD1/FRSD
PCDQ
ADD4PCPC4
ADD8PCPC8
IMIAIR
DERFA1RD1从寄存器堆读出寄存器数据
A2RD2
MFRSDFRSDSELFRSD选择转发到D级rs寄存器的值
RD1
ETODE段转发到D段的数据,下同
MTOD
MFRTDFRTDSELFRTD选择转发到D级rt寄存器的值
RD2
ETOD
MTOD
EXTI16EXTD选择输出SIMM,LIMM,UIMM
EXTOP
CMP0FRSDLESS输出和0比较的结果,独热输出
GREAT
LE_EQ
GR_EQ
CMPD1RES输出比较结果
D2
NPCPC4NEXTPC选择输出BPC,JPC
I26
NPCOP
EXMFRSEFRSESELFRSE选择转发到E级rs寄存器的值
RS_E
MTOE
WTOE
MFRTEFRTESELFRTE选择转发到E级rt寄存器的值
RT_E
MTOE
WTOE
MFNUM2FRTENUM2选择输入num2
FRSE
MDUNUM1BUSY乘除模块,WHI,WLO为写使能,WDHI,WDLO为写入数据
NUM2HI
MDUOPLO
START
WHI
WLO
MFBBSELB选择alu的b输入
EXT_E
FRTE
ALUAAO执行不同操作
B
SHAMT
ALUOP
MEMFRTMFRTMSELFRTM选择转发到M级rt寄存器的值
RT_M
WTOM
BEEXTAO_M1_0BE得到字节写入使能信号,为1的那一位代表对应dm中的字相应部分写入字节
SW_M
SH_M
SB_M
DMDA[11:2]RD支持写入字节、半字、字和读出字
WD
WM
BE[3:0]
WBRDEXTRD_WRDEXTD将dm中取出的字按照指令做相应处理得到将写入rf的字
AO_W1_0
LW_W
LHU_W
LH_W
LBU_W
LB_W
MFA3A3SELA3选择a3
IR_W[RT]
IR_W[RD]
31
MFWD3WD3SELWD3选择wd3
RDEXTD
AO_W
EXT_W
PC8_W
RFA3
WD3
WR

3、修改数据通路

经过转发数据mux选择后的数据作为新的所在阶段寄存器数据的源头。

外模块内模块端口复用器选择信号
IFUPCQPC4NEXTPCRD1**/FRSD**MFPCPCSEL
ADD4PCQ
ADD8PCQ
IMIAQ
nowstate
REG_DIR_DIR
PC4_DPC4
PC8_DPC8
RFURFA1IR_D[RS]
A2IR_D[RT]
A3IR_W[RT]IR_W[RD]$31MFA3A3SEL
WD3RDEXTDAO_WEXT_WPC8_WHI_WLO_WMFWD3WD3SEL
MFRSDFRSDSEL
RD1
ETOD
MTOD
MFRTDFRTDSEL
RD2
ETOD
MTOD
IDUEXTI16IR_D[I16]
CMP0FRSDRD1**/FRSD**
CMPD1RD1**/FRSD**
D2RD2**/FRTD**
NPCPC4PC4_D
I26IR_D[I26]
REG_EIR_EIR_D
RS_ERD1**/FRSD**
EXT_EEXTD
RT_ERD2**/FRTD**
PC8_EPC8_D
EXUMFRSEFRSESEL
RS_E
MTOE
WTOE
MFRTEFRTESEL
RT_E
MTOE
WTOE
MDUNUM1RS_E**/FRSE**
NUM2RT_E**/FRTE**RS_E**/FRSE**MFNUM2NUM2SEL
ALUARS_E**/FRSE**
BEXT_ERT_E**/FRTE**MFBBSEL
SHAMTIR_E[SH]
REG_MIR_MIR_E
AO_MAO
RT_MRT_E**/FRTE**
PC8_MPC8_E
EXT_MEXT_E
HI_MHI
LO_MLO
MEUMFRTMFRTMSEL
RT_M
WTOM
BEEXTAO_M1_0AO_M[1:0]
DMDAAO_M
WDRT_M**/FRTM**
BEBE
REG_WIR_WIR_M
RD_WRD
AO_WAO_M
PC8_WPC8_M
EXT_WEXT_M
HI_WHI_M
LO_WLO_M
RFURDEXTRD_WRD_W
AO_W1_0AO_W[1:0]

四、结束

到此,简化五段式流水线CPU就已经设计完毕,Verilog代码就不放出来了~~,代码量庞大~~。

说明一下,文中写的通用指令和非通用指令是自创的,没有严格定义,只是区分一下。

本文是对笔者学习思考CPU的一点总结,供复习所用,若能帮到读者就最好不过了。

感谢阅读,若有错误,请评论给出,万分感谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值