MIPS多周期CPU设计实现(40条指令)

无异常指令(共40条)

1.算术指令

  1. ADDIU
  2. ADDU
  3. SUBU
  4. MULU
  5. DIVU
  6. MUL
  7. DIV

2.逻辑指令

  1. AND
  2. ANDI
  3. NOR
  4. OR
  5. ORI
  6. XOR
  7. XORI
  8. LUI

3.移位指令

  1. SLLV
  2. SLL
  3. SRAV
  4. SRA
  5. SLT
  6. SLTI
  7. SLTIU
  8. SLTU
  9. SRL
  10. SRLV

4.分支跳转指令

  1. J
  2. JAL
  3. JR
  4. BEQ
  5. BNE
  6. BGEZ
  7. BGTZ
  8. BLEZ
  9. BLTZ

5.读写指令

  1. LW
  2. SW

6.数据移动

1.MFLO
2.MFHI
3.MTLO
4.MTHI

包括的模块

  1. mips模块是一个CPU,只含有复位信号rst和时钟信号clk,内部由PC、NPC、DM、IM、EXT、ALU、IR、Ctrl等模块以及一些多路选择器和缓冲器组成。
  2. PC(程序计数器):PC是指令计数器,主要功能是完成输出当前指令地址并保存下一条指令地址。复位后,PC指向0x0000_3000,此处为第一条指令的地址。
  3. NPC(NextPC计算单元):NPC是下条指令计数器,主要功能是计算下一条指令地址,NPCOp[1:0]决定如何计算NPC。
  4. Regfile(通用寄存器组,也称为寄存器文件、寄存器堆):RF主要功能是保存寄存器文件,并支持对通用寄存器的访问。
  5. ALU(算术逻辑单元):ALU主要功能是完成对输入数据的算数逻辑计算,包括加法、减法、按位或运算以及判断两个操作数是否相等。
  6. EXT(扩展单元):EXT主要功能是将16位的数据扩展为32位数据。
  7. IMEM(指令存储器):IM是指令存储器,主要功能是根据读控制信号DMWr,读写对应addr地址的32位数据。
  8. IR(指令寄存器):IR主要功能是完成对来自IM的指令的缓冲。
  9. DMEM(数据存储器):DM是数据存储器,主要功能是根据读写控制信号DMWr,读写对应addr地址的32位数据。
  10. MUX(数据选择器):主要功能是多路选择器。mux.v文件包含二选一、四选一、八选一、十六选一4中多路选择器。实例化多路选择器时,可使用#(XXX),实例化位宽为XXX的多路选择器。
  11. ICUT:主要功能是将32位指令分割为几个部分,传到不同的地方。
  12. MDU:进行乘除运算的单元
  13. define:对所有的名字进行宏定义。

模块

1.PC

端口说明
信号名方向描述
ClkI时钟信号
RstI复位信号
1:将PC置为初始值
0:无效
PCwrIPC读写使能端信号
1:可以写
0:不可写
PCin[31:0]I下一条指令的地址
PCout[31:0]O当前指令的地址(映射)
功能定义
序号功能描述
1复位将PC置为初始值
2计数当PCWR有效并且时钟上升沿,PC就更新为NPC的输出

2.IMEM

端口说明
信号名方向描述
AddrI当前指令的地址
IoutO当前指令
功能定义
序号功能描述
1取指令输出Addr地址所代表的当前指令

3.IR

端口说明
信号名方向描述
clkI时钟信号
IRinI来自IMEM的指令
IRwrI控制指令寄存器的读写
1:可以写
0:不可写
IRoutO当前指令
功能定义
序号功能描述
1缓冲对来自IMEM的指令进行缓冲

4.Regfile

端口说明
信号名方向描述
ClkI时钟信号
rstI重置信号
Ra[4:0]I5位地址输入信号,将寄存器中的内容读出到Raout
Rb[4:0]I5位地址输入信号,将寄存器中的内容读出到Rbout
Rw[4:0]I5位地址输入信号,将寄存器作为目标寄存器
Wd[31:0]I32位数据输入信号
IRwrI控制IR读写
1:可写
0:不可写
RaoutO输出Raout所指定的寄存器中的32位数据
RboutO输出Rbout所指定的寄存器中的32位数据
功能定义
序号功能描述
1读数据读出Ra,Rb地址所对应寄存器中1的数据到Raout,Rbout
2写数据当RFwr有效且时钟上升沿来临,将Wd中的数据写入Rw所对应的寄存器中

5.ALU

端口说明
信号名方向描述
A[31:0]I操作数A
B[31:0]I操作数B
ALUop[3:0]I详见功能表
Out[31:0]OALU输出结果为32位数据
功能定义
序号功能描述
1加运算0000:Out = A + B
2减运算0001:Out = A - B
3与运算0010:Out = A & B
4或非运算0011:Out = ~(A | B)
5异或运算0100:Out = A ^ B
6或运算0101:Out = A | B
7逻辑左移0110:Out = B << A[10:6]
8逻辑右移0111:Out = B >> A[10:6]
9算术右移1000:Out = Signed(B) >>> A[10:6]
10小于置1(有符号)1001:Out = Signed(A) < Signed(B) ? 1 : 0
11小于置1(无符号)1010:Out = A < B ? 1 : 0
12将A操作数左移16位(LUI)1011:Out = A << 16

6.DMEM

端口说明
信号名方向描述
DMwrI数据存储器写使能信号
1:可写
0:无效
Addr[31:0]I32位地址输入,指定读出或写入地址数据
Imm[31:0]I经过拓展的32位立即数,用于计算地址
Din[31:0]I32为数据输入
Dout[31:0]O32为数据输出,由计算后的地址指定
功能定义
序号功能描述
1读数据读出Addr所指定的数据到Dout
2计算地址计算出由Addr和立即数相加的地址
3写数据当DMwr有效且时钟上升到来时,将输入的数据Din写到计算结果所指定的地址

7.EXT

端口说明
信号名方向描述
EXTinI16位输入数据
EXTopI扩展方式选择信号
0:符号扩展
1:零扩展
EXToutO扩展到32位后的输出数据
功能定义
序号功能描述
1符号扩展将16位的输入数据进行符号扩展,输出32位数据
2零扩展将16位的输入数据进行零扩展,输出32位数据

8.NPC

端口说明
信号名方向描述
Immj[25:0]I跳转指令目的地址中间26位
ImmB[31:0]I分支指令的偏移00量(分支选择时加上)
PC[31:0]I分支指令基地址
CmpAI分支指令第一个比较数,指令为Jump时当做地址使用
CmpBI分支指令第二个比较数
is_JRI单独判断是不是JR指令
NPCop[2:0]I详见功能表
JORBI对Jump和Branch选择
0:Jump
1:Branch
NPCout[31:0]O从Jump和Branch里选出来的下一条指令地址
NextPCO常规情况下PC + 4
功能定义
序号功能描述
1计算分支地址先拓展立即数,根据比较结果来计算分支地址:
000:BEQ
001:BNE
010:BLEZ
011:BGTZ
100:BLTZ
101:BGEZ
2计算跳转地址通过位拼接来计算得到跳转地址

9.MDU

端口说明
信号名方向描述
clkI时钟信号
rstI重置信号
MDUresselI读寄存器选择信号
0:读LO寄存器
1:读HI寄存器
A[31:0]I参与计算的第一个值
B[31:0]I参与计算的第二个值
MDUcoacIMDU使能端
MDUop[2:0]IMDU功能选择信号
000:{HI, LO} = $signed(A) * $signed(B)
001:{HI, LO} = A * B
010:LO = A / B, HI = A % B
011:LO = $signed(A) / $signed(B, HI = $signed(A) % $signed(B)
100:LO = A
101:HI = A
MDUout[31:0]O将计算结果输出
功能定义
序号功能描述
1无符号乘{HI, LO} = A * B
2有符号乘{HI, LO} = $signed(A) * $signed(B)
3无符号除LO = A / B, HI = A % B
4有符号除LO = $signed(A) / $signed(B, HI = $signed(A) % $signed(B)
5写LO寄存器LO = A
6读LO寄存器MDUout = LO
7写HI寄存器HI = A
8读HI寄存器MDUout = HI

10.ICUT

端口说明
信号名方向描述
Ins[31:0]I传入的32位指令
op[5:0]Ors寄存器
rt[4:0]Ort寄存器
rd[4:0]Ord寄存器
rs[4:0]O操作码
sa[4:0]O位移量
Imm[15:0]O立即数
Addr[25:0]OJ指令地址片段
Insout[31:0]O传给control的32位指令
功能定义
序号功能描述
1分割将32位指令分割成片段,传到各个的端口中去

11.Control

端口说明
信号名方向描述
RstI重置
ClkI时钟信号
InsI32位指令,用来译码
PCwrO控制PC是否可写
0:不可写
1:可写
IRwrO控制IR是否可写
0:不可写
1:可写
Regin[1:0]O选择写入Regfile的地址
00:写到rt寄存器
01:写到rd寄存器
10:写到1F寄存器
RFwrO控制是否可以写入Regfile
0:不可写
1:可写
SEL_AO选择A操作数
0:从Regfile中来
1:从EXT来
SEL_BO选择B操作数
0:从Regfile中来
1:从EXT来
ALUop[3:0]O控制ALU选择哪种运算,详见ALU功能描述
JORBO控制NPCout选择Jump还是Branch
0:Jump
1:Branch
NPCop[2:0]O选择Branch的种类, 详见NPC功能描述
MDUresselO读寄存器选择信号
0:读LO寄存器
1:读HI寄存器
MDUop[2:0]O选择MDU的功能,详见功能描述
DMwrO控制DM是否可写
0:不可写
1:可写
EXTop[1:0]O控制扩展方式,详见功能表
WBsel[1:0]O选择写回寄存器的数据从哪里来
NPCOselO选择正常执行下一条还是跳转分支
is_JRO判断是不是将CMPA作为JR的地址使用
MDUcoacOMDU使能信号
1:可以进行计算
0:不可以进行计算

CPU数据数据通路图

image

状态对应信号

image
image
image
image
image
image

模块代码实现

1.ALU

module ALU(
    input [31:0]A,          // 操作数A
    input [31:0]B,          // 操作数B
    input [3:0]ALUop,       // 选择功能
    output reg [31:0]Out        // 输出结果
);

    always @(*) begin
        case(ALUop)
            4'b0000: Out = A + B;
            4'b0001: Out = A - B;
            4'b0010: Out = A & B;
            4'b0011: Out = A | B;
            4'b0100: Out = A ^ B;                               // XOR
            4'b0101: Out = ~(A | B);                            // NOR
            4'b0110: Out = B << A[10:6];                        // SLL
            4'b0111: Out = B >> A[10:6];                        // SRL
            4'b1000: Out = $signed(B) >>> A[10:6];              // SRA
            4'b1001: Out = ($signed(A) < $signed(B)) ? 1 : 0;   // SLT和SLTI
            4'b1010: Out = (A < B) ? 1 : 0;                     // SLTU和SLTIU
            4'b1011: Out = A << 16;                             // LUI
        endcase 
    end

endmodule

2.Control

`include "define.v"

module Control(
    input clk,                  // 时钟信号
    input rst,                  // 重置信号
    input [31:0]Ins,            // 传入的32位指令
    output reg PCwr,                // 控制PC是否能写入
    output reg IRwr,                // 控制IR是否能写入
    output reg[1:0]Regin,       // 进入Regfile的选择
    output reg RFwr,                // 控制Regfile是否能写入
    output reg SEL_A,               // 选入ALU的A操作数
    output reg SEL_B,               // 选入ALU的B操作数
    output reg[3:0]ALUop,          // ALU的操作
    output reg JORB,                // 判断是Jump还是Branch
    output reg[2:0]NPCop,          // 判断是哪种Branch
    output reg MDUressel,           // 选择是HI还是LO
    output reg[2:0]MDUop,          // MDU的操作
    output reg DMwr,                // DM使能信号
    output reg[1:0]EXTop,          // 是哪一种扩展
    output reg[1:0]WBsel,          // 写回的数据从哪里来
    output reg NPCOsel,             // 对NPC出来的结果进行选择(跳或者常态)
    output reg is_JR,               // 判断是不是JR, 需要将CmpA作为地址
    output reg Dtype,               // 判断DM是读还是写
    output reg MDUcoac              // MDU使能信号
);

    parameter  IF = 3'b000;    // 取指
    parameter  ID = 3'b001;    // 译码
    parameter  EXE = 3'b010;   // 执行
    parameter  MEM = 3'b011;   // 读取
    parameter  WB = 3'b100;    // 写回


    reg[2:0] CurrentState;     // 当前状态
    reg[2:0] NextState;        // 下一状态

    reg[5:0] Op;
    /*时序逻辑*/
    always @(posedge clk,posedge rst) begin
        if(rst) 
            CurrentState <= IF;
        else
            CurrentState <= NextState;
    end
    
    always @(*) begin
        case(CurrentState)
            IF: begin
                NextState = ID;
                PCwr = 1'b0;
                IRwr = 1'b1;
                RFwr = 1'b0;
                DMwr = 1'b0;
                NPCOsel = 1'b0;
            end

            ID: begin
                JORB = ~(Ins[`op] == `OP_J || Ins[`op] == `OP_JAL || (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_JR));
                NPCOsel = (Ins[`op] == `OP_J || Ins[`op] == `OP_JAL || (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_JR));
                //$display("NPCOsel = %8X", NPCOsel);
                is_JR = (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_JR) ? 1'b1 : 1'b0;

                case(Ins[`op])
                    `OP_J: begin
                        NextState = IF;
                        PCwr = 1'b1;
                        IRwr = 1'b0;
                        RFwr = 1'b0;
                        DMwr = 1'b0;
                    end 

                    `OP_BEQ,`OP_BGEZ,`OP_BGTZ,
                    `OP_BLEZ,`OP_BLTZ,`OP_BNE: begin
                        NextState = EXE;
                        PCwr = 1'b0;
                        RFwr = 1'b0;
                        DMwr = 1'b0;
                        IRwr = 1'b0;
                    end

                    `OP_JAL: begin
                        NextState = IF;
                        PCwr = 1'b1;
                        IRwr = 1'b0;
                        RFwr = 1'b1;
                        DMwr = 1'b0;
                        Regin = 2'b10;
                        WBsel = 2'b11;
                    end

                    `OP_R:
                        case(Ins[`funct])
                            `FUNCT_JR: begin
                                NextState = IF;
                                PCwr = 1'b1;
                                IRwr = 1'b0;
                                RFwr = 1'b0;
                                DMwr = 1'b0;
                            end
                            default: begin
                                NextState = EXE;
                                PCwr = 1'b1;       // 为了每次让PC + 4
                                RFwr = 1'b0;
                                DMwr = 1'b0;
                                IRwr = 1'b0;
                                MDUcoac = 1'b0; 
                            end
                        endcase
                    default: begin
                        NextState = EXE;
                        PCwr = 1'b1;
                        RFwr = 1'b0;
                        DMwr = 1'b0;
                        IRwr = 1'b0;
                    end
                endcase
            end

            EXE: begin
                is_JR = 1'b0;

                Regin = 
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_DIV) ? 2'b00 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_DIVU) ? 2'b00 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_MULT) ? 2'b00 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_MULTU) ? 2'b00 :
                2'b01;

                SEL_A = 
                (Ins[`op] == `OP_LUI) ? 1'b1 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SLL) ? 1'b1 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SRL) ? 1'b1 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SRA) ? 1'b1 :
                1'b0;

                SEL_B = 
                (Ins[`op] == `OP_ADDIU || Ins[`op] == `OP_ANDI || Ins[`op] == `OP_ORI
                || Ins[`op] == `OP_XORI || Ins[`op] == `OP_SLTI || Ins[`op] == `OP_SLTIU);

                ALUop = 
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_ADDU) ? 4'b0000 :
                (Ins[`op] == `OP_ADDIU) ? 4'b0000 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_AND) ? 4'b0010 :
                (Ins[`op] == `OP_ANDI) ? 4'b0010 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_OR) ? 4'b0011 :
                (Ins[`op] == `OP_ORI) ? 4'b0011 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_XOR) ? 4'b0100 :
                (Ins[`op] == `OP_XORI) ? 4'b0100 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SLL) ? 4'b0110 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SLLV) ? 4'b0110 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SRA) ? 4'b1000 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SRAV) ? 4'b1000 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SLT) ? 4'b1001 :
                (Ins[`op] == `OP_SLTI) ? 4'b1001 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SLTU) ? 4'b1010 :
                (Ins[`op] == `OP_SLTIU) ? 4'b1010 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SRL) ? 4'b0111 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SRLV) ? 4'b0111 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SUBU) ? 4'b0001 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_NOR) ? 4'b0101 :
                (Ins[`op] == `OP_LUI) ? 4'b1011 :
                4'b1111;

                JORB = 
                (Ins[`op] == `OP_BEQ) ? 1'b1 :
                (Ins[`op] == `OP_BNE) ? 1'b1 :
                (Ins[`op] == `OP_BLEZ && Ins[`rt] == `RT_BLEZ) ? 1'b1 :
                (Ins[`op] == `OP_BGTZ && Ins[`rt] == `RT_BGTZ) ? 1'b1 :
                (Ins[`op] == `OP_BLTZ && Ins[`rt] == `RT_BLTZ) ? 1'b1 :
                (Ins[`op] == `OP_BGEZ && Ins[`rt] == `RT_BGEZ) ? 1'b1 :
                1'b0;

                NPCop = 
                (Ins[`op] == `OP_BEQ) ? 3'b000 :
                (Ins[`op] == `OP_BNE) ? 3'b001 :
                (Ins[`op] == `OP_BLEZ && Ins[`rt] == `RT_BLEZ) ? 3'b010 :
                (Ins[`op] == `OP_BGTZ && Ins[`rt] == `RT_BGTZ) ? 3'b011 :
                (Ins[`op] == `OP_BLTZ && Ins[`rt] == `RT_BLTZ) ? 3'b100 :
                (Ins[`op] == `OP_BGEZ && Ins[`rt] == `RT_BGEZ) ? 3'b101 :
                3'b111;

                MDUop = 
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_MULT) ? 3'b000 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_MULTU) ? 3'b001 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_DIV) ? 3'b011 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_DIVU) ? 3'b010 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_MTLO) ? 3'b100 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_MTHI) ? 3'b101 :
                3'b111;

                EXTop = 
                (Ins[`op] == `OP_ADDIU) ? 2'b00 :
                (Ins[`op] == `OP_ANDI) ? 2'b01 :
                (Ins[`op] == `OP_ORI) ? 2'b01 :
                (Ins[`op] == `OP_XORI) ? 2'b01 :
                (Ins[`op] == `OP_LUI) ? 2'b01 :
                2'b00;

                NPCOsel = 
                (Ins[`op] == `OP_BEQ) ? 1'b1 :
                (Ins[`op] == `OP_BNE) ? 1'b1 :
                (Ins[`op] == `OP_BLEZ && Ins[`rt] == `RT_BLEZ) ? 1'b1 :
                (Ins[`op] == `OP_BGTZ && Ins[`rt] == `RT_BGTZ) ? 1'b1 :
                (Ins[`op] == `OP_BLTZ && Ins[`rt] == `RT_BLTZ) ? 1'b1 :
                (Ins[`op] == `OP_BGEZ && Ins[`rt] == `RT_BGEZ) ? 1'b1 :
                1'b0;

                MDUcoac = 
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_DIV) ? 1'b1 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_DIVU) ? 1'b1 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_MULT) ? 1'b1 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_MULTU) ? 1'b1 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_MTLO) ? 1'b1 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_MTHI) ? 1'b1 :
                1'b0;

                case(Ins[`op])
                    `OP_R,`OP_ADDI,`OP_ADDIU,
                    `OP_ANDI,`OP_ORI,`OP_XORI,`OP_SLTI,`OP_SLTIU,`OP_LUI: begin
                        
                        if (Ins[`funct] == `FUNCT_DIV || Ins[`funct] == `FUNCT_DIVU || 
                            Ins[`funct] == `FUNCT_MULT || Ins[`funct] == `FUNCT_MULTU ||
                            Ins[`funct] == `FUNCT_MTLO || Ins[`funct] == `FUNCT_MTHI) begin
                            NextState = IF;
                            PCwr = 1'b0;
                            RFwr = 1'b0;
                            DMwr = 1'b0;
                            IRwr = 1'b0;
                        end
                        else begin
                            NextState = WB;
                            PCwr = 1'b0;
                            RFwr = 1'b0;
                            DMwr = 1'b0;
                            IRwr = 1'b0;
                        end
                    end

                    `OP_BEQ,`OP_BGEZ,`OP_BGTZ,
                    `OP_BLEZ,`OP_BLTZ,`OP_BNE: begin
                        NextState = IF;
                        PCwr = 1'b1;
                        RFwr = 1'b0;
                        DMwr = 1'b0;
                        IRwr = 1'b0;
                    end

                    default: begin
                        NextState = MEM;
                        PCwr = 1'b0;
                        RFwr = 1'b0;
                        DMwr = 1'b0;
                        IRwr = 1'b0;
                    end
                endcase
            end

            MEM: begin
                case(Ins[`op])
                    `OP_LW: begin
                        NextState = WB;
                        PCwr = 1'b0;
                        RFwr = 1'b1;
                        DMwr = 1'b0;
                        IRwr = 1'b0;
                        //Dtype = 1'b0;
                    end

                    default: begin
                        NextState = IF;
                        PCwr = 1'b0;
                        RFwr = 1'b0;
                        DMwr = 1'b1;
                        IRwr = 1'b0;
                        //Dtype = 1'b1;
                    end
                endcase
            end

            WB: begin
                Regin = 
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_ADDU) ? 2'b01 : 
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SUBU) ? 2'b01 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_AND) ? 2'b01 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_OR) ? 2'b01 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_XOR) ? 2'b01 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_NOR) ? 2'b01 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SLL) ? 2'b01 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SLLV) ? 2'b01 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SRL) ? 2'b01 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SRLV) ? 2'b01 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SRA) ? 2'b01 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SRAV) ? 2'b01 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SLT) ? 2'b01 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_SLTU) ? 2'b01 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_MFLO) ? 2'b01 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_MFHI) ? 2'b01 :
                (Ins[`op] == `OP_ADDIU) ? 2'b00 :
                (Ins[`op] == `OP_ANDI) ? 2'b00 :
                (Ins[`op] == `OP_ORI) ? 2'b00 :
                (Ins[`op] == `OP_XORI) ? 2'b00 :
                (Ins[`op] == `OP_LUI) ? 2'b00 :
                (Ins[`op] == `OP_SLTI) ? 2'b00 :
                (Ins[`op] == `OP_SLTIU) ? 2'b00 :
                (Ins[`op] == `OP_LW) ? 2'b00 :
                0;


                MDUressel = (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_MFHI) ? 1'b1 : 1'b0;

                WBsel = 
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_MFLO) ? 2'b01 :
                (Ins[`op] == `OP_R && Ins[`funct] == `FUNCT_MFHI) ? 2'b01 :
                (Ins[`op] == `OP_LW) ? 2'b10 :
                2'b00;

                NextState = IF;
                PCwr = 1'b0;
                RFwr = 1'b1;
                DMwr = 1'b0;
                IRwr = 1'b0;
            end
        endcase
    end

endmodule

3.define

/*指令分段*/
`define op 31:26
`define funct 5:0
`define imm26 25:0
`define imm16 15:0
`define rs 25:21
`define rt 20:16
`define rd 15:11
`define s 10:6

/*ALU操作*/
`define ALUop_ADD     4'b0000
`define ALUop_SUB     4'b0001
`define ALUop_AND     4'b0010
`define ALUop_OR      4'b0011
`define ALUop_XOR     4'b0100
`define ALUop_NOR     4'b0101
`define ALUop_SLL     4'b0110
`define ALUop_SRL     4'b0111
`define ALUop_SRA     4'b1000
`define ALUop_SLT     4'b1001
`define ALUop_SLTU    4'b1010
`define ALUop_LUI     4'b1011

/*EXTop*/
`define EXT_SIGN      2'b00
`define EXT_ZERO      2'b01

/******* OP 操作码 *******/
`define OP_R    6'b000000
/******* 存储读取指令 *******/
`define OP_LW   6'b100011
`define OP_LUI  6'b001111
`define OP_SW   6'b101011
/*******     I TYPE      *******/
`define OP_ADDI 6'b001000
`define OP_ADDIU 6'b001001
`define OP_ANDI 6'b001100
`define OP_ORI  6'b001101
`define OP_SLTI 6'b001010
`define OP_SLTIU 6'b001011
`define OP_XORI 6'b001110
/*******   BRANCH TYPE   *******/
`define OP_BEQ  6'b000100
`define OP_BGEZ 6'b000001
`define OP_BGTZ 6'b000111
`define OP_BLEZ 6'b000110
`define OP_BLTZ 6'b000001
`define OP_BNE  6'b000101
/*******     J TYPE      *******/
`define OP_J    6'b000010
`define OP_JAL  6'b000011


//功能码
`define FUNCT_ADD     6'b100000
`define FUNCT_SUB     6'b100010
`define FUNCT_ADDU    6'b100001
`define FUNCT_SUBU    6'b100011
`define FUNCT_AND     6'b100100
`define FUNCT_OR      6'b100101
`define FUNCT_NOR     6'b100111
`define FUNCT_XOR     6'b100110
`define FUNCT_SLL     6'b000000
`define FUNCT_SLLV    6'b000100
`define FUNCT_SRL     6'b000010
`define FUNCT_SRLV    6'b000110
`define FUNCT_SRA     6'b000011
`define FUNCT_SRAV    6'b000111
`define FUNCT_SLT     6'b101010
`define FUNCT_SLTU    6'b101011
`define FUNCT_JALR    6'b001001
`define FUNCT_JR      6'b001000
`define FUNCT_MULT    6'b011000
`define FUNCT_MULTU   6'b011001
`define FUNCT_DIV     6'b011010
`define FUNCT_DIVU    6'b011011
`define FUNCT_MFHI    6'b010000
`define FUNCT_MFLO    6'b010010
`define FUNCT_MTHI    6'b010001
`define FUNCT_MTLO    6'b010011
//B_type_opcode
`define RT_BGEZ    5'b00001
`define RT_BGTZ    5'b00000
`define RT_BLEZ    5'b00000
`define RT_BLTZ    5'b00000

4.DMEM

module DMEM(
    input clk,                  // 时钟信号
    input DMwr,                 // DM写使能信号
    input [31:0]Din,            // 要写入DM的数据
    input [31:0]Imm,            // 用来计算地址的,扩展过的立即数
    input [31:0]Addr,           // 用来计算地址的rs中的内容
    output reg[31:0]Dout        // 从DM中输出的数据
);

    reg [31:0] RAM[1023:0];

    integer i;

    initial begin  // 初始时将所有内存单元初始化
        for (i = 0; i < 1023; i = i + 1) RAM[i] = 32'b0;
    end

    always @(posedge clk) begin
        if (DMwr) begin
            RAM[Imm + Addr[11:2]] = Din;  // 利用映射地址写入
            //$display("%H",RAM[Addr[11:2]]);
        end
        else begin
            Dout = RAM[Imm + Addr[11:2]];  // 利用映射地址读出
            //$display("%H",RAM[Addr[11:2]]);
        end
    end

endmodule

5.EXT

module EXT(
    input [15:0] EXTin,    // 需要扩展的16位立即数
    input EXTop,           // 操作
    output reg[31:0]EXTout    // 扩展后的32位输出
);

    always @(*) begin
        if (EXTop) begin       // 零扩展
            EXTout = EXTin;
        end
        else EXTout = $signed(EXTin);    // 符号扩展
    end

endmodule

6.ICUT

module ICUT(
    input [31:0]Ins,
    output reg[4:0]rs,          // 传入rs端口
    output reg[4:0]rt,          // 传入rt端口
    output reg[4:0]rd,          // 传入rd端口
    output reg[15:0]Imm,        // Imm传入EXT中拓展
    output reg[25:0]Addr,       // 地址片段传入NPC中
    output reg[31:0]Insout      // 不分割,直接送入Control进行译码
);

    initial begin
        rs = 5'b00000;
        rt = 5'b00000;
        rd = 5'b00000;
    end

    always @(Ins) begin
        rs = Ins[25:21];
        rt = Ins[20:16];
        rd = Ins[15:11];
        Imm = Ins[15:0];
        Addr = Ins[25:0];
        Insout = Ins;
    end

endmodule

7.IMEM

module IMEM(
    input [31:0]Addr,
    output reg [31:0]Iout
);

    reg [31:0] IM [1023:0];

    initial
        $readmemh("F:/MIPS_MULTI_CYCLE_CPU/test_app/test_12.txt", IM);

    always@(*) begin
        Iout <= IM[Addr[11:2]];
    end

endmodule

8.IR

module IR(
    input clk,
    input IRwr,
    input [31:0]IRin,
    output reg [31:0]IRout
);

    initial
        IRout = 0;

    always @(posedge clk) begin
        if (IRwr) begin
            IRout <= IRin;
        end
    end

endmodule

9.MDU

module MDU(
    input clk,              // 时钟信号
    input rst,              // 重置信号
    input [31:0]A,          // 操作数A
    input [31:0]B,          // 操作数B
    input MDUressel,        // 结果选择(HI/LO)
    input MDUcoac,          // MUD使能端
    input [2:0]MDUop,       // 操作选择
    output [31:0]MDUout     // 输出结果
);

    reg [31:0]HI;
    reg [31:0]LO;

    assign MDUout = (MDUressel == 0) ? LO : HI;

    always @(posedge clk) begin
        if (rst) begin
            HI = 0;
            LO = 0;
        end
        else begin
            if (MDUcoac) begin
                case(MDUop)
                    3'b000:  {HI,LO} = $signed(A) * $signed(B);
                    3'b001:  {HI,LO} = A * B;
                    3'b010:  if(B != 0) begin
                        LO = $signed(A) / $signed(B);
                        HI = $signed(A) % $signed(B);
                    end
                    3'b011:  if(B != 0) begin
                        LO = A / B;
                        HI = A % B;
                    end
                    3'b100: begin 
                        LO = A;
                    end
                    3'b101: begin
                        HI = A;
                    end 
                endcase
            end
        end
    end

endmodule

10.mips

`timescale 1ns / 1ps
`include "define.v"
//
// Company: 
// Engineer: 
// 
// Create Date: 2021/07/02 14:26:18
// Design Name: 
// Module Name: mips
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module mips(
    input clk,
    input rst
);
    //线网部分
    // PC模块
    wire PCwr;
    wire [31:0]PCin;
    wire [31:0]PCout;

    // IMEM模块
    wire [31:0]Iout;

    // 拓展器模块
    wire EXTop;

    // IR模块
    wire [31:0]IRout;
    wire IRwr;

    // ICUT模块
    wire [4:0]rs;
    wire [4:0]rt;
    wire [4:0]rd;
    wire [15:0]Imm;
    wire [25:0]OutAddr;
    wire [31:0]Insout;

    // ICUT和RF之间的数据选择器
    wire [1:0]Regin;
    wire [4:0]CUT_MUX_RF_out;

    // Regfile模块
    wire [31:0]Wd;
    wire RFwr;
    wire [31:0]Raout;
    wire [31:0]Rbout;

    // NPC模块
    wire JORB;
    wire is_JR;
    wire [2:0]NPCop;
    wire [31:0]NextPC;
    wire [31:0]NPCout;

    // NPC和PC之间的数据选择器
    wire NPCOsel;

    // 操作数A选择器
    wire [31:0]SEL_A_out;
    wire [31:0]EXTout;
    wire Sel_A;

    // 操作数B选择器
    wire [31:0]SEL_B_out;
    wire Sel_B;

    // ALU模块
    wire [3:0]ALUop;
    wire [31:0]ALUout;

    //  数据存储器模块
    wire DMwr;
    wire Dtype;
    wire [31:0]Dout;

    // 乘除器单元
    wire MDUressel;
    wire MDUcoac;
    wire [2:0]MDUop;
    wire [31:0]MDUout;

    // 写回寄存器的选择器
    wire [1:0]WBsel;

    

    // 模块实例化
    PC U_PC(.clk(clk), .rst(rst), .PCwr(PCwr), .PCin(PCin), .PCout(PCout));
    IMEM U_IMEM(.Addr(PCout), .Iout(Iout));
    EXT U_EXT(.EXTin(Imm), .EXTop(EXTop), .EXTout(EXTout));
    IR U_IR(.clk(clk), .IRwr(IRwr), .IRin(Iout), .IRout(IRout));
    ICUT U_ICUT(.Ins(IRout), .rs(rs), .rt(rt), .rd(rd), .Imm(Imm), .Addr(OutAddr), .Insout(Insout));
    MUX4#(5) CUT_MUX_RF(.In0(rt), .In1(rd), .In2(31), .Op(Regin), .Out(CUT_MUX_RF_out));
    Regfile U_Regfile(.clk(clk), .rst(rst), .Ra(rs), .Rb(rt), .Rw(CUT_MUX_RF_out), .Wd(Wd), .RFwr(RFwr), .Raout(Raout), .Rbout(Rbout));
    NPC U_NPC(.Immj(OutAddr), .ImmB(EXTout), .PC(PCout), .CmpA(Raout), .CmpB(Rbout), .is_JR(is_JR), .NPCop(NPCop), .JORB(JORB), .NPCout(NPCout), .NextPC(NextPC));
    MUX2 NPC_TO_PC(.In0(NextPC), .In1(NPCout), .Op(NPCOsel), .Out(PCin));
    MUX2 SEL_A(.In0(Raout), .In1(EXTout), .Op(Sel_A), .Out(SEL_A_out));
    MUX2 SEL_B(.In0(Rbout), .In1(EXTout), .Op(Sel_B), .Out(SEL_B_out));
    ALU U_ALU(.A(SEL_A_out), .B(SEL_B_out), .ALUop(ALUop), .Out(ALUout));
    DMEM U_DMEM(.clk(clk), .DMwr(DMwr), .Din(Rbout), .Imm(EXTout), .Addr(Raout), .Dout(Dout));
    MDU U_MDU(.clk(clk), .rst(rst), .A(Raout), .B(Rbout), .MDUressel(MDUressel), .MDUcoac(MDUcoac), .MDUop(MDUop), .MDUout(MDUout));
    MUX4 WB_MUX(.In0(ALUout), .In1(MDUout), .In2(Dout), .In3(NextPC), .Op(WBsel), .Out(Wd));
    Control U_Control(.clk(clk), .rst(rst), .Ins(Insout), .PCwr(PCwr), .IRwr(IRwr), .Regin(Regin), .RFwr(RFwr),
                      .SEL_A(Sel_A), .SEL_B(Sel_B), .ALUop(ALUop), .JORB(JORB), .NPCop(NPCop), .MDUressel(MDUressel), 
                      .MDUop(MDUop), .DMwr(DMwr), .EXTop(EXTop), .WBsel(WBsel), .NPCOsel(NPCOsel), .is_JR(is_JR), 
                      .Dtype(Dtype), .MDUcoac(MDUcoac));
endmodule

11.MUX

module MUX2
#(parameter width = 32)(
    input [width-1:0] In0,
    input [width-1:0] In1,
    input Op,
    output reg [width-1:0] Out
    );
    
    

    always @(*) begin
        case (Op)
            1'b0: Out = In0;
            1'b1: Out = In1;
        endcase
    end

endmodule


module MUX4
#(parameter width = 32)(
    input [width-1:0] In0,
    input [width-1:0] In1,
    input [width-1:0] In2,
    input [width-1:0] In3,
    input [1:0] Op,
    output reg [width-1:0] Out
    );
    
    

    always @(*) begin
        case (Op)
            2'b00: Out = In0;
            2'b01: Out = In1;
            2'b10: Out = In2;
            2'b11: Out = In3;
        endcase
    end

endmodule


module MUX8
#(parameter width = 32)(
    input [width-1:0] In0,
    input [width-1:0] In1,
    input [width-1:0] In2,
    input [width-1:0] In3,
    input [width-1:0] In4,
    input [width-1:0] In5,
    input [width-1:0] In6,
    input [width-1:0] In7,
    input [2:0] Op,
    output reg [width-1:0] Out
    );
    

    always @(*) begin
        case (Op)
            3'b000: Out = In0;
            3'b001: Out = In1;
            3'b010: Out = In2;
            3'b011: Out = In3;
            3'b100: Out = In4;
            3'b101: Out = In5;
            3'b110: Out = In6;
            3'b111: Out = In7;
        endcase
    end

endmodule

12.NPC

module NPC(
    input [25:0]Immj,           // 分支指令立即数
    input [31:0]ImmB,           // 跳转指令立即数(偏移量)
    input [31:0]PC,             // 基地址
    input [31:0]CmpA,           // 分支比较数A
    input [31:0]CmpB,           // 分支比较数B
    input [2:0]NPCop,           // 选择分支
    input is_JR,                // 单独判断是不是JR
    input JORB,                 // 选择输出是Jump还是Branch
    output reg [31:0]NPCout,    // Jump和Branch选出的下一条指令地址
    output [31:0]NextPC         // 常态下PC + 4
);

    assign NextPC = PC + 4;

    always @(*) begin
        if (JORB) begin  // JORB是1执行Branch
            case(NPCop)
                3'b000: NPCout = ($signed(CmpA) == $signed(CmpB)) ? (PC + (ImmB << 2) + 4) : (PC + 4);       // BEQ
                3'b001: NPCout = ($signed(CmpA) != $signed(CmpB)) ? (PC + (ImmB << 2) + 4) : (PC + 4);       // BNE
                3'b010: NPCout = ($signed(CmpA) <= 0) ? (PC + (ImmB << 2) + 4) : (PC + 4);                   // BLEZ
                3'b011: NPCout = ($signed(CmpA) > 0) ? (PC + (ImmB << 2) + 4) : (PC + 4);                    // BGTZ
                3'b100: NPCout = ($signed(CmpA) < 0) ? (PC + (ImmB << 2) + 4) : (PC + 4);                    // BLTZ
                3'b101: NPCout = ($signed(CmpA) >= 0) ? (PC + (ImmB << 2) + 4) : (PC + 4);                   // BGEZ
            endcase
        end
        else begin  // JORB是0执行Jump
            if (is_JR) begin
                NPCout = CmpA;
            end
            else NPCout = {PC[31:28], Immj, 2'b00};
        end
    end

endmodule

13.PC

module PC(
	input clk,					// 时钟信号
	input rst,					// 重置信号
	input PCwr,					// PC写使能信号
	input [31:0]PCin,			// PC输入端口
	output reg [31:0]PCout		// PC 输出端口
);

	always @(posedge clk or rst) begin
		if (rst) begin
			PCout <= 32'h0000_0000;
		end
		else if (PCwr) begin
			PCout <= PCin;
		end
	end

endmodule

14.Regfile

module Regfile(
    input clk,                  // 时钟信号
    input rst,                  // 重置信号
    input [4:0]Ra,              // rs寄存器读入
    input [4:0]Rb,              // rt寄存器读入
    input [4:0]Rw,              // rd/rt寄存器读入
    input [31:0]Wd,             // 写入寄存器的数据
    input RFwr,                 // 写寄存器使能信号
    output reg [31:0]Raout,         // 从rs中读出来的内容
    output reg [31:0]Rbout          // 从rt中读出来的内容
);

    initial begin
        Raout = 0;
        Rbout = 0;
    end

    // 寄存器堆
    reg [31:0] RF[31:0];
    integer i;
    initial begin
        for (i = 0; i < 32; i = i + 1) RF[i] = 32'b0;
    end

    always@(Ra or Rb) begin
        Raout <= RF[Ra];
        Rbout <= RF[Rb];
    end

    always @(posedge clk) begin
        if (rst) begin
            for (i = 0; i < 32; i = i + 1) RF[i] = 32'b0;
        end

        else if (RFwr && Rw != 0) begin
            RF[Rw] <= Wd;
            //$display("Wd = %8X", Wd);
            //$display("R[00-07]= %8X, %8X, %8X, %8X, %8X, %8X, %8X, %8X", 0, RF[1], RF[2], RF[3], RF[4], RF[5], RF[6], RF[7]);
            //$display("R[08-15]= %8X, %8X, %8X, %8X, %8X, %8X, %8X, %8X", RF[8], RF[9], RF[10], RF[11], RF[12], RF[13], RF[14], RF[15]);
            //$display("R[16-23]= %8X, %8X, %8X, %8X, %8X, %8X, %8X, %8X", RF[16], RF[17], RF[18], RF[19], RF[20], RF[21], RF[22], RF[23]);
            //$display("R[24-31]= %8X, %8X, %8X, %8X, %8X, %8X, %8X, %8X", RF[24], RF[25], RF[26], RF[27], RF[28], RF[29], RF[30], RF[31]);
        end
    end

endmodule
  • 12
    点赞
  • 203
    收藏
    觉得还不错? 一键收藏
  • 23
    评论
### 回答1: MIPS周期CPU设计需要考虑以下几个方面: 1. 指令集的设计MIPS指令集包含24指令,需要根据指令集的特点设计CPU指令集。 2. 数据通路的设计CPU的数据通路需要包含寄存器、ALU、控制单元等模块,以实现指令的执行。 3. 控制单元的设计:控制单元需要根据指令的不同,控制数据通路中各个模块的工作,以实现指令的执行。 4. 存储器的设计CPU需要包含指令存储器和数据存储器,以存储指令和数据。 5. 中断处理的设计CPU需要支持中断处理,以响应外部事件。 6. 性能优化的设计CPU的性能需要优化,可以采用流水线等技术提高CPU的执行效率。 以上是MIPS周期CPU设计需要考虑的几个方面,需要综合考虑各个方面的因素,才能设计出高效、稳定的CPU。 ### 回答2: MIPS是一种基于RISC (Reduced Instruction Set Computing)的指令集架构,具有精简指令集、快速响应、高效率等特点,被广泛用于微控制器和嵌入式系统中。在MIPS体系结构中,CPU通常采用单周期设计,其指令集包括24指令,这些指令可以被划分为6类,分别是:算术运算指令、逻辑运算指令、转移指令、访存指令、加载指令和存储指令。 在单周期CPU设计中,每一指令在执行过程中都需要占用一个时钟周期,因此这种CPU的时钟周期时间需要较长。在MIPS周期CPU设计中,CPU需要包括五个模块:指令存储器、指令译码器、执行单元、数据存储器和控制单元。 指令存储器模块主要是用于存储指令代码,执行译码器模块会对指令进行解码,并从其中提取出指令码、操作码、立即数、寄存器编号等信息。执行单元则包括算术逻辑单元(ALU)和移位单元,它们主要用于进行各种数值计算。数据存储器是用于存储数据的,包括 CPU 中的寄存器和主存储器,控制单元则是用于控制CPU中各个功能单元的工作状态。 在设计MIPS周期CPU时,需要根据指令的不同来分配不同的控制信号,使CPU能够正确地执行指令。在指令序列中,访存指令和转移指令的执行过程较为复杂,需要特别考虑其执行过程中所需的控制信号,以确保CPU能够正确地执行指令。 为了进一步优化MIPS周期CPU的性能,通常还需要引入流水线技术。在流水线的设计中,CPU的各个模块可以并行工作,从而实现指令的并行执行,大大提高了CPU的工作效率。不过在引入流水线的同时,也需要考虑各个模块之间的数据冲突和控制冲突等问题,以确保CPU的工作正常、稳定。 ### 回答3: MIPS周期 CPU 设计是一种基于 MIPS 体系结构的 CPU 设计,它实现了 24 指令的功能,包括数据传输、逻辑运算、跳转等基本操作。它的架构非常简单,由五个主要部件构成,分别是取指单元、执行单元、数据存储器、寄存器堆以及控制单元。 首先,取指单元主要负责从程序存储器中读取指令,并将其送到执行单元。执行单元则将指令进行解码、分析和执行。数据存储器负责存储数据,供指令读取和写入。寄存器堆用于存储 CPU 内部的寄存器,包括通用寄存器和特殊寄存器。最后,控制单元用于控制整个 CPU 的运行流程和时序。 在 MIPS周期 CPU 设计中,指令流程非常简单,取指、解码和执行都在同一个周期内完成。每指令需要多个时钟周期才能完成,所以指令周期较长。但是由于 CPU 周期固定,逻辑电路简单,因此响应速度比较快。 该设计包括以下 24 指令: 1. 加载字(LW) 2. 存储字(SW) 3. 加(ADD) 4. 加立即数(ADDI) 5. 减(SUB) 6. 位与(AND) 7. 位或(OR) 8. 移位(SLL) 9. 移位立即数(SLLI) 10. 移位右(SRL) 11. 移位右立即数(SRLI) 12. 逻辑运算左移(SLT) 13. 逻辑运算左移立即数(SLTI) 14. 分支等于零(BEQZ) 15. 分支不等于零(BNEZ) 16. 跳转(J) 17. 跳转和链接(JAL) 18. 加载地址(LA) 19. 加载字节(LB) 20. 存储字节(SB) 21. 移位右算术(SRA) 22. 移位右算术立即数(SRAI) 23. 乘(MUL) 24. 除(DIV) 总的来说,MIPS周期 CPU 设计是一种较为简单,响应速度较快的 CPU 设计,但由于指令周期固定,每指令都需要多个时钟周期才能完成,因此效率相对较低。
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值