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位有效
....