PicoRV32 筆記 04

PicoRV32 是一款RISC-V指令的實現的軟核CPU。

PicoRV32實現指令rdcycle,rdcycleh,用於讀取時鐘計數,當使能ENABLE_COUNTERS和ENABLE_COUNTERS64,此指令便有效。

PicoRV32內部實現64bit的計數器count_cycle,定義在源碼234行

在源碼1486行-1492行對cout_cycle操作

由上源碼可知,當使能ENABLE_COUNTERS,每個時鐘的上升沿對count_cycle操作,

如果復位則count_cycle取值爲0,反之自增1,如果沒有使能ENABLE_COUNTERS64則把count_cycle的64bit-32bit設置爲0,如果沒有使能ENABLE_COUNTERS則count_cycle值爲任意,與此同時對應的指令也會被取消,此時使用rdcycle,rdcycleh會導致硬件異常。

涉及到源碼中對count_cycle操作的另一處,就是讀取計數器的內容

在源碼的1686行   “ENABLE_COUNTERS && is_rdcycle_rdcycleh_rdinstr_rdinstrh”判斷指令是否是 rdcycle, rdcycleh, rdinstr, rdinstrh這四條指令,如果是rdcycle,則把count_cycle[31:0]寫到目標寄存器,如果是rdcycleh則把count_cycle[63:32]寫到目標寄存器中。

is_rdcycle_rdcycleh_rdinstr_rdinstrh:是一個單bit信號,PicoRV32中有幾個此類信號,用於對一類操作指令進行歸類。

例如

assign is_rdcycle_rdcycleh_rdinstr_rdinstrh = |{instr_rdcycle, instr_rdcycleh, instr_rdinstr, instr_rdinstrh};

is_rdcycle_rdcycleh_rdinstr_rdinstrh:判斷指令是否是  instr_rdcycle, instr_rdcycleh, instr_rdinstr, instr_rdinstrh其中的一個。注意 |{......} 中的|是個單目運算符,表示把組合後的信號的所有bit進行or運算。

is_lui_auipc_jal <= |{instr_lui, instr_auipc, instr_jal};判斷指令是否是instr_lui, instr_auipc, instr_jal

is_lui_auipc_jal_jalr_addi_add_sub <= |{instr_lui, instr_auipc, instr_jal, instr_jalr, instr_addi, instr_add, instr_sub};判斷指令是否是instr_lui, instr_auipc, instr_jal, instr_jalr, instr_addi, instr_add, instr_sub

is_slti_blt_slt <= |{instr_slti, instr_blt, instr_slt};判斷指令是否是instr_slti, instr_blt, instr_slt

is_lbu_lhu_lw <= |{instr_lbu, instr_lhu, instr_lw};判斷指令是否是instr_lbu, instr_lhu, instr_lw

is_compare <= |{is_beq_bne_blt_bge_bltu_bgeu, instr_slti, instr_slt, instr_sltiu, instr_sltu};判斷指令是is_beq_bne_blt_bge_bltu_bgeu, instr_slti, instr_slt, instr_sltiu, instr_sltu

is_beq_bne_blt_bge_bltu_bgeu <= mem_rdata_latched[6:0] == 7'b1100011;判斷指令是否是beq bne blt bge bltu bgeu

is_lb_lh_lw_lbu_lhu          <= mem_rdata_latched[6:0] == 7'b0000011;判斷指令是否是lb lh lw lbu lhu

is_sb_sh_sw                  <= mem_rdata_latched[6:0] == 7'b0100011;判斷指令是否是 sb sh sw

is_alu_reg_imm               <= mem_rdata_latched[6:0] == 7'b0010011;判斷指令的原操作數是否是寄存器和立即數

is_alu_reg_reg               <= mem_rdata_latched[6:0] == 7'b0110011;判斷指令的原操作數是否是寄存器和寄存器

is_slli_srli_srai <= is_alu_reg_imm && |{
                mem_rdata_q[14:12] == 3'b001 && mem_rdata_q[31:25] == 7'b0000000,
                mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0000000,
                mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0100000
            };

判斷指令是否是slli srli srai

is_jalr_addi_slti_sltiu_xori_ori_andi <= instr_jalr || is_alu_reg_imm && |{
                mem_rdata_q[14:12] == 3'b000,
                mem_rdata_q[14:12] == 3'b010,
                mem_rdata_q[14:12] == 3'b011,
                mem_rdata_q[14:12] == 3'b100,
                mem_rdata_q[14:12] == 3'b110,
                mem_rdata_q[14:12] == 3'b111
            };

判斷指令是否是jalr addi slti sltiu xori ori andi

is_sll_srl_sra <= is_alu_reg_reg && |{
                mem_rdata_q[14:12] == 3'b001 && mem_rdata_q[31:25] == 7'b0000000,
                mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0000000,
                mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0100000
            };

判斷指令是否是sll srl sra

根據RISC-V的基礎指令集RV32I表,其中47條指令,有11個opcode

7'b011_0111:  lui

7'b001_0111: auipc

7'b110_1111: jal

7'b110_0111:jalr

7'b110_0011: beq, bne, blt, bge, bltu, bgeu

7'b000_0011: lb, lh, lw, lbu, lhu

7'b010_0011: sb, sh, sw

7'b001_0011:addi, slti, sltiu, xori, ori, andi, slli, srli, srai

7'b011_0011:add, sub, sll, slt, sltu, xor, srl, sra, or, and

7'b000_1111: fence, fence.i

7'b111_0011: ecall, ebreak,  csrrw, csrrs, csrrc, csrrwi, csrrsi, csrrci

綜上所述,PicoRV32首先通過opcode對指令先對指令進行分類

信號名 操作嗎 對應指令  
instr_lui 7'b011_0111 lui  
instr_auipc 7'b001_0111 auipc  
instr_jal 7'b110_1111 jal  
instr_jalr 7'b110_0111, fun3=3'b000 jalr  
is_beq_bne_blt_bge_bltu_bgeu 7'b110_0011 beq,bne,blt,bge,bltu,bgeu  
is_lb_lh_lw_lbu_lhu 7'b000_0011 lb, lh, lw, lbu, lhu  
is_sb_sh_sw 7'b010_0011 sb, sh, sw  
is_alu_reg_imm 7'b001_0011 addi,slti,sltiu, xori,ori,andi,slli,srli,srai  
is_alu_reg_reg 7'b011_0011 add, sub, sll, slt, sltu,xor, srl,sra,or,and  
instr_getq 7'b000_1011,fun7=7'b000_0000 getq  
instr_setq 7'b000_1011,fun7=7'b000_0001 setq  
instr_retirq 7'b000_1011,fun7=7'b000_0010 retirq  
instr_maskirq 7'b000_1011,fun7=7'b000_0011 maskirq  
instr_waitirq 7'b000_1011,fun7=7'b000_0100 waitirq  
instr_timer 7'b000_1011,fun7=7'b000_0101 timer  

接下來在第一分類的基礎上進行細分

跳轉類的指令,6條

instr_beq :is_beq_bne_blt_bge_bltu_bgeu && fun3 =  3'b000;

instr_bne:is_beq_bne_blt_bge_bltu_bgeu && fun3 = 3'b001;
 instr_blt   : is_beq_bne_blt_bge_bltu_bgeu && fun3 =  3'b100;
 instr_bge   : is_beq_bne_blt_bge_bltu_bgeu && fun3 =  3'b101;
 instr_bltu  : is_beq_bne_blt_bge_bltu_bgeu && fun3 =  3'b110;
 instr_bgeu  : is_beq_bne_blt_bge_bltu_bgeu && fun3 =  3'b111;

加載指令,5條

 instr_lb    : is_lb_lh_lw_lbu_lhu && fun3 = 3'b000;
 instr_lh    : is_lb_lh_lw_lbu_lhu && fun3 = 3'b001;
 instr_lw    : is_lb_lh_lw_lbu_lhu && fun3 = 3'b010;
 instr_lbu   : is_lb_lh_lw_lbu_lhu && fun3 = 3'b100;
 instr_lhu   : is_lb_lh_lw_lbu_lhu && fun3 = 3'b101;

存儲類指令,3條

 instr_sb    : is_sb_sh_sw && fun3 = 3'b000;
 instr_sh    : is_sb_sh_sw && fun3 = 3'b001;
  instr_sw    : is_sb_sh_sw && fun3 = 3'b010;

寄存器和立即數指令,9條

  instr_addi  : is_alu_reg_imm && fun3 =3'b000;
 instr_slti  : is_alu_reg_imm && fun3 = 3'b010;
  instr_sltiu : is_alu_reg_imm && fun3 = 3'b011;
  instr_xori  : is_alu_reg_imm && fun3 =3'b100;
  instr_ori   : is_alu_reg_imm && fun3 = 3'b110;
  instr_andi  : is_alu_reg_imm && fun3 = 3'b111;

  instr_slli  : is_alu_reg_imm && fun3 = 3'b001 && fun7 = 7'b0000000;
  instr_srli  : is_alu_reg_imm && fun3 =3'b101 && fun7 = 7'b0000000;
 instr_srai  : is_alu_reg_imm && fun3 = 3'b101 && fun7 = 7'b0100000;

寄存器和寄存器指令,10條

instr_add   : is_alu_reg_reg &&fun3 = 3'b000 && fun7 = 7'b0000000;
 instr_sub   : is_alu_reg_reg && fun3 = 3'b000 && fun7 = 7'b0100000;
  instr_sll   : is_alu_reg_reg && fun3 = 3'b001 && fun7 = 7'b0000000;
  instr_slt   : is_alu_reg_reg && fun3 = 3'b010 && fun7 = 7'b0000000;
  instr_sltu  : is_alu_reg_reg && fun3 = 3'b011 && fun7 = 7'b0000000;
 instr_xor   : is_alu_reg_reg && fun3 = 3'b100 && fun7 = 7'b0000000;
 instr_srl   : is_alu_reg_reg && fun3 = 3'b101 && fun7 = 7'b0000000;
 instr_sra   : is_alu_reg_reg && fun3 = 3'b101 && fun7 = 7'b0100000;
 instr_or    : is_alu_reg_reg && fun3 = 3'b110 && fun7 = 7'b0000000;
 instr_and   : is_alu_reg_reg && fun3 = 3'b111 && fun7 = 7'b0000000;

經過上述細分,支持的指令都可以識別到,接下來把相近功能的指令進行合併

is_lui_auipc_jal:判斷指令是否是instr_lui, instr_auipc, instr_jal,

            這三條指令整合到一個信號,是因爲這三個指令格式基本相同

            20bit立即數   |    5bit rd |    7bit操作碼

        唯一的差別是jal指令的20位立即數排列方式不一樣

        對於lui指令,把指令跟隨的立即數低位補上12'b0加上高位一同放到目標寄存器,

                                rd = (imm<<12)

        對於auipc指令,把pc寄存器的值加上{立即數,12'b0}放到目標寄存器

                        rd = pc + (imm<<12)

        對於jal指令,跳轉到地址,並把跳轉前的下一條指令地址存儲到rd中

                    rd = pc + 4

                    pc = pc + imm

is_slti_blt_slt:判斷指令是否是slti, blt, slt.

        slti: 小於置位,當rs1 < 立即數[11:0] 時候設置rd爲1,反之設置爲0。 slt =set less then

        slt:小於置位,當rs1 < rs2時候,rd爲1,反之爲0

        blt: 小於跳轉,當rs1 < rs2時候

is_lb_lh_lw_lbu_lhu:判斷指令是否爲lb, lh, lw, lbu, lhu

                lb: rs1+12位立即數符號擴展後作爲地址,讀取一個字節並作符號擴展後存放到rd中

                lh: rs1+12位立即數符號擴展後作爲地址,讀取一個16bit並作符號擴展後存放到rd中

                lw: rs1+12位立即數符號擴展後作爲地址,讀取一個32bit後存放到rd中

                lbu: rs1+12位立即數符號擴展後作爲地址,讀取一個8bit並作0擴展後存放到rd中

                lhu: rs1+12位立即數符號擴展後作爲地址,讀取一個16bit並作0擴展後存放到rd中

            公共的操作是提取地址

is_slli_srli_srai:判斷指令是否是slli, srli, srai.這三條指令格式相同,除了操作碼不一樣

            slli: 把rs1左移n位,低位補0,結果寫入rd中

            srli:把rs1右移n位,高位補0,結果寫入rd中

            srai:把rs1右移n位,高位位補符號位,結果寫入rd中

is_sb_sh_sw:判斷指令是否爲sb, sh,sw

                sb:  rs1+12位立即數符號擴展後作爲地址,把rs2最低8bit作爲數據寫到總線上

                sh:  rs1+12位立即數符號擴展後作爲地址,把rs2最低16bit作爲數據寫到總線上

                sw:  rs1+12位立即數符號擴展後作爲地址,把rs2爲數據寫到總線上

            公共部分提取地址

is_sll_srl_sra:判斷指令是否爲sll, srl, sra,這三條指令格式相同,除了操作碼不同

                sll :把寄存器rs1 左移 rs2位, rs2只有低5位有效

                srl :把寄存器rs1 右移 rs2位, rs2只有低5位有效

                sra :把寄存器rs1 右移 rs2位, 高位填充符號位,rs2只有低5位有效

....

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章