minirvEMU的实现

📅 2026/7/12 1:12:11
minirvEMU的实现
minirvEMU的实现本次实验通过C语言模拟了minirv的功能,并通过AM框架模拟外设进行测试.下面进行详细介绍实现了lui jalr addi add sw lw sb lbu八条riscv指令实现了取指、译码、执行、更新PC等功能通过AM模拟外设进行验证主要代码介绍取值加译码模块void instruction_fetch(){ int inst M[PC2]; int opcode inst 0b00000000000000000111000001111111; if(inst 0x00100073){ ebreak(inst); } switch(opcode){ case 0b00000000000000000000000000010011: addi(inst); break; case 0b00000000000000000000000001100111: jalr(inst); break; case 0b00000000000000000010000000000011: lw(inst); break; case 0b00000000000000000100000000000011: lbu(inst); break; case 0b00000000000000000010000000100011: sw(inst); break; case 0b00000000000000000000000000100011: sb(inst); break; default: break; } opcode inst 0b11111110000000000111000001111111; switch(opcode){ case 0b00000000000000000000000000110011: add(inst); break; default: break; } opcode inst 0b00000000000000000000000001111111; switch(opcode){ case 0b00000000000000000000000000110111: lui(inst); break; default: break; } }指令执行模块void reg_write(int addr, int val){ if(addr 0) return; else R[addr] val; } void addi(int inst){ int rd (inst 0b00000000000000000000111110000000) 7; int rs (inst 0b00000000000011111000000000000000) 15; int imm (inst 0b11111111111100000000000000000000) 20; imm imm 20 20; reg_write(rd, R[rs] imm); } void add(int inst){ int rd (inst 0b00000000000000000000111110000000) 7; int rs1 (inst 0b00000000000011111000000000000000) 15; int rs2 (inst 0b00000001111100000000000000000000) 20; reg_write(rd, R[rs1] R[rs2]); } void lui(int inst){ int rd (inst 0b00000000000000000000111110000000) 7; int imm (inst 0b11111111111111111111000000000000) 12; imm imm 12; reg_write(rd, imm); } void lw(int inst){ int rd (inst 0b00000000000000000000111110000000) 7; int rs1 (inst 0b00000000000011111000000000000000) 15; int imm (inst 0b11111111111100000000000000000000) 20; imm imm 20 20; int addr (R[rs1] imm) 2; reg_write(rd, ROM[addr]); } void lbu(int inst){ int rd (inst 0b00000000000000000000111110000000) 7; int rs1 (inst 0b00000000000011111000000000000000) 15; int imm (inst 0b11111111111100000000000000000000) 20; imm imm 20 20; uint32_t sel (R[rs1] (int)imm) 0b11; uint32_t addr (R[rs1] (int)imm) 2; unsigned int val (unsigned int)ROM[addr]; if(sel 0) val val % 256; else if(sel 1){ val val 8; val val % 256; }else if(sel 2){ val val 16; val val % 256; }else{ val val 24; } reg_write(rd, val); } void sw(int inst){ int rs1 (inst 0b00000000000011111000000000000000) 15; int rs2 (inst 0b00000001111100000000000000000000) 20; int imm1 (inst 0b00000000000000000000111110000000) 7; int imm2 (inst 0b11111110000000000000000000000000) 25; int imm (imm2 5) imm1; imm imm 20 20; uint32_t addr (imm R[rs1]); if(addr 0x20000000 addr 0x20040000){ rgb[(addr-0x20000000) 2] R[rs2]; } else{ addr addr 2; ROM[addr] R[rs2]; } } void sb(int inst){ int rs1 (inst 0b00000000000011111000000000000000) 15; int rs2 (inst 0b00000001111100000000000000000000) 20; int imm1 (inst 0b00000000000000000000111110000000) 7; int imm2 (inst 0b11111110000000000000000000000000) 25; int imm (imm2 5) imm1; imm imm 20 20; uint32_t sel (R[rs1] imm) 0b11; uint32_t addr (R[rs1] imm) 2; uint32_t temp (uint32_t)ROM[addr]; uint32_t temp1 temp 0xff; uint32_t temp2 (temp 8) 0xff; uint32_t temp3 (temp 16) 0xff; uint32_t temp4 temp 24; uint32_t val; if(sel 0){ val (temp4 24) (temp3 16) (temp2 8) ((uint32_t)R[rs2] 0xff); }else if(sel 1){ val (temp4 24) (temp3 16) (((uint32_t)R[rs2] 0xff) 8) temp1; }else if(sel 2){ val (temp4 24) (((uint32_t)R[rs2] 0xff) 16) (temp2 8) temp1; }else if(sel 3){ val (((uint32_t)R[rs2] 0xff) 24) (temp3 16) (temp2 8) temp1; } ROM[addr] val; } void jalr(int inst){ int rd (inst 0b00000000000000000000111110000000) 7; int rs (inst 0b00000000000011111000000000000000) 15; uint32_t imm (inst 0b11111111111100000000000000000000) 20; imm imm 20 20; int temp PC; PC imm R[rs]; reg_write(rd, temp 4); jalr_en 1; } void output(int inst){ int rs (inst 0b00001100) 2; printf(R[%d]: %d\n, rs, R[rs]); } void ebreak(int inst){ if(R[10] 0){ printf(HIT GOOD TRAP\n); flag 0; }else{ printf(HIT BAD TRAP\n); flag 0; } }小技巧imm imm 20 20;通过int特性进行符号拓展寄存器写模块void reg_write(uint_t addr, uint_t val){ if(addr 0) return; else R[addr] val; }由于R[0]寄存器只能为0,故用此函数进行写操作对寄存器进行操作多用uint32_t数据类型,uint32_t用于模仿寄存器如uint32_t temp (uint32_t)ROM[addr]; uint32_t temp1 temp 0xff; uint32_t temp2 (temp 8) 0xff; uint32_t temp3 (temp 16) 0xff; uint32_t temp4 temp 24; uint32_t val;感觉很重要不同intuint32_t位宽位宽不固定由编译器/平台决定,可能16/32/64位标准强制32位对负数右移是算数右移,符号拓展高位补0溢出行为C标准是未定义行为,不同编译器结果随机模2³²自动环绕与硬件行为相同输出printf(“%d”, val)printf(“inst %“PRIx32”\n”, inst);