热度 10| ||
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位有效
....
PicoRV32 笔记 04 - jjinl的个人页面 - OSCHINA https://my.oschina.net/jjinl/blog/5290920