From f30e8b22c64690b439fea150cae03b06ead2532c Mon Sep 17 00:00:00 2001 From: ponyma Date: Thu, 30 Mar 2023 15:50:33 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=BA=86RV32C=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=EF=BC=8C=E5=B9=B6=E9=80=9A=E8=BF=87=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/ponyvm/soc/board/RV32.java | 20 ++ src/main/java/com/ponyvm/soc/board/Soc.java | 11 + src/main/java/com/ponyvm/soc/core/CPU.java | 224 +++++-------- .../java/com/ponyvm/soc/core/Instruction.java | 314 +----------------- .../com/ponyvm/soc/core/Instruction16.java | 160 +++++++++ .../com/ponyvm/soc/core/Instruction32.java | 293 ++++++++++++++++ src/test/java/RVCInstrTest.java | 291 ++++++++++++++++ src/test/java/VMTest.java | 2 +- src/test/resources/loop-imc.bin | Bin 0 -> 5980 bytes src/test/resources/pi-im.bin | Bin 0 -> 13944 bytes src/test/resources/pi-imc.bin | Bin 0 -> 11864 bytes src/test/resources/pi.bin | Bin 11864 -> 11832 bytes src/test/resources/zs-imc.bin | Bin 0 -> 6040 bytes 13 files changed, 863 insertions(+), 452 deletions(-) create mode 100644 src/main/java/com/ponyvm/soc/core/Instruction16.java create mode 100644 src/main/java/com/ponyvm/soc/core/Instruction32.java create mode 100644 src/test/java/RVCInstrTest.java create mode 100755 src/test/resources/loop-imc.bin create mode 100755 src/test/resources/pi-im.bin create mode 100755 src/test/resources/pi-imc.bin create mode 100755 src/test/resources/zs-imc.bin diff --git a/src/main/java/com/ponyvm/soc/board/RV32.java b/src/main/java/com/ponyvm/soc/board/RV32.java index 6433207..cbd9737 100644 --- a/src/main/java/com/ponyvm/soc/board/RV32.java +++ b/src/main/java/com/ponyvm/soc/board/RV32.java @@ -19,6 +19,26 @@ public class RV32 implements Soc { public CPU CORE; + @Override + public String getNAME() { + return this.NAME; + } + + @Override + public String getVERSION() { + return this.VERSION; + } + + @Override + public SysBus getSYS_BUS() { + return this.SYS_BUS; + } + + @Override + public CPU getCORE() { + return this.CORE; + } + @Override public int launchROM(File rom) throws IOException { ELFFile elfFile = loadELFFile(rom); diff --git a/src/main/java/com/ponyvm/soc/board/Soc.java b/src/main/java/com/ponyvm/soc/board/Soc.java index c5f3fc2..9957a1b 100644 --- a/src/main/java/com/ponyvm/soc/board/Soc.java +++ b/src/main/java/com/ponyvm/soc/board/Soc.java @@ -1,8 +1,19 @@ package com.ponyvm.soc.board; +import com.ponyvm.soc.core.CPU; +import com.ponyvm.soc.internal.sysbus.SysBus; + import java.io.File; import java.io.IOException; public interface Soc { + String getNAME(); + + String getVERSION(); + + SysBus getSYS_BUS(); + + CPU getCORE(); + int launchROM(File rom) throws IOException; } diff --git a/src/main/java/com/ponyvm/soc/core/CPU.java b/src/main/java/com/ponyvm/soc/core/CPU.java index 14bf6f8..3c371f9 100644 --- a/src/main/java/com/ponyvm/soc/core/CPU.java +++ b/src/main/java/com/ponyvm/soc/core/CPU.java @@ -40,7 +40,7 @@ public int launch(int EntryAddr) { pc = EntryAddr; stop = false; while (isRunning()) { - int instruction = SYS_BUS.getWord(pc); + int instruction = instructionDecode(pc); if ((instruction & 0x03) == 3) { executeInstruction32(instruction); } else { @@ -50,208 +50,156 @@ public int launch(int EntryAddr) { return reg[10]; } + private int instructionDecode(int pc) { + Integer instr = I_CACHE.get(pc); + if (instr == null) { + instr = SYS_BUS.getWord(pc); + I_CACHE.put(pc, instr); + } + return instr; + } + public boolean executeInstruction16(int instruction) { - System.out.print(Integer.toUnsignedString(pc, 16) + ":" + Integer.toUnsignedString(instruction, 16) + ":"); - short inst_opcode = (short) ((instruction >> 0) & 0x3); - short inst_rd = (short) ((instruction >> 7) & 0x1f); - short inst_rs1 = (short) ((instruction >> 7) & 0x1f); - short inst_rs2 = (short) ((instruction >> 2) & 0x1f); - short inst_rs1s = (short) ((instruction >> 7) & 0x7); - short inst_rs2s = (short) ((instruction >> 2) & 0x7); - short inst_rds = (short) ((instruction >> 2) & 0x7); - short inst_imm6 = (short) (((instruction >> 2) & 0x1f) | ((instruction >> 7) & (1 << 5))); - short inst_imm7 = (short) (((instruction >> 4) & (1 << 2)) | ((instruction >> 7) & (0x7 << 3)) | ((instruction << 1) & (1 << 6))); - short inst_imm8 = (short) (((instruction >> 7) & (0x7 << 3)) | ((instruction << 1) & (0x3 << 6))); - short inst_imm9 = (short) (((instruction >> 2) & (0x3 << 3)) | ((instruction >> 7) & (1 << 5)) | ((instruction << 4) & (0x7 << 6))); - short inst_imm10 = (short) (((instruction >> 4) & (1 << 2)) | ((instruction >> 2) & (1 << 3)) | ((instruction >> 7) & (0x3 << 4)) | ((instruction >> 1) & (0x7 << 6))); - short inst_imm12 = (short) (((instruction >> 2) & (0x7 << 1)) | ((instruction >> 7) & (1 << 4)) | ((instruction << 3) & (1 << 5)) | ((instruction >> 1) & (0x2d << 6)) | ((instruction << 1) & (1 << 7)) | ((instruction << 2) & (1 << 10))); - short inst_imm18 = (short) (((instruction << 5) & (1 << 17)) | ((instruction << 10) & (0x1f << 12))); - short inst_funct2 = (short) ((instruction >> 10) & 0x3); - short inst_funct3 = (short) ((instruction >> 13) & 0x7); + Instruction16 instr16 = new Instruction16(instruction); + String instAddr = Integer.toUnsignedString(pc, 16); +// System.out.println(instAddr + ":" + Integer.toUnsignedString(instruction, 16) + ":" + instr16.toAssemblyString()); int bflag = 0, temp; - - switch (inst_opcode) { + int rd, rs1, rs2, rds, rs1s, rs2s, imm; + switch (instr16.opcode) { case 0b00: - switch (inst_funct3) { + switch (instr16.funct3) { case 0b000: - System.out.println("c.addi4spn " + inst_rds + " x2 " + inst_imm10); - reg[8 + inst_rds] = reg[2] + inst_imm10; - break; // c.addi4spn -// case 1: // c.fld -// fdreg[8 + inst_rds] = SYS_BUS.getWord(reg[8 + inst_rs1s] + inst_imm8 + 0) << 0; -// fdreg[8 + inst_rds] |= SYS_BUS.getWord(reg[8 + inst_rs1s] + inst_imm8 + 4) << 32; -// break; + // c.addi4spn Expansion:addi rd’,x2,nzuimm + rds = (instruction >> 2) & 0b111; + //nzuimm[5:4|9:6|2|3] + imm = instr16.imm10; + reg[8 + rds] = reg[2] + imm; + break; case 0b010: - System.out.println("c.lw"); - reg[8 + inst_rds] = SYS_BUS.getWord(reg[8 + inst_rs1s] + inst_imm7); - break; // -// case 0b011: -// fdreg[8 + inst_rds] = SYS_BUS.getWord(reg[8 + inst_rs1s] + inst_imm7); -// break; // c.flw -// case 5: // c.fsd -// SYS_BUS.storeWord(reg[8 + inst_rs1s] + inst_imm8 + 0, (int) (fdreg[8 + inst_rs2s] >> 0f)); -// SYS_BUS.storeWord(reg[8 + inst_rs1s] + inst_imm8 + 4, (int) (fdreg[8 + inst_rs2s] >> 32f)); -// break; + //c.lw Expansion:lw rd’,offset[6:2](rs1’) + imm = instr16.imm7; + reg[8 + instr16.rds] = SYS_BUS.getWord(reg[8 + instr16.rs1s] + imm); + break; case 0b110: - System.out.println("c.sw x" + (8 + inst_rs2s) + " " + inst_imm7 + "(x" + (8 + inst_rs1s) + ") (sw x" + (8 + inst_rs2s) + " " + inst_imm7 + "(x" + (8 + inst_rs1s) + "))"); - SYS_BUS.storeWord(reg[8 + inst_rs1s] + inst_imm7, reg[8 + inst_rs2s]); - break; //c.sw Expansion:sw rs2’,offset[6:2](rs1’) -// case 7: -// SYS_BUS.storeWord(reg[8 + inst_rs1s] + inst_imm7, (int) fdreg[8 + inst_rs2s]); -// break; // c.fsw + //c.sw Expansion:sw rs2’,offset[6:2](rs1’) + imm = instr16.imm7; + SYS_BUS.storeWord(reg[8 + instr16.rs1s] + imm, reg[8 + instr16.rs2s]); + break; default: - System.out.println("未知指令"); break; } break; case 0b01: - switch (inst_funct3) { + switch (instr16.funct3) { case 0b000: - int sext_imm6 = signed_extend(inst_imm6, 6); - System.out.println("c.addi x" + inst_rd + " " + sext_imm6 + " (addi x" + inst_rd + " x" + inst_rd + " " + sext_imm6 + ")"); - reg[inst_rd] += sext_imm6; - break; //c.addi Expansion:addi rd, rd, nzimm[5:0] + //c.addi Expansion:addi rd, rd, nzimm[5:0] + int sext_imm6 = signed_extend(instr16.imm6, 6); + reg[instr16.rd] += sext_imm6; + break; case 0b001: - int sext_imm12 = signed_extend(inst_imm12, 12); - System.out.println("c.jal " + sext_imm12 + " (jal x1 " + sext_imm12 + "(x0))"); + // c.jal Expansion:jal x1, offset[11:1] + int sext_imm12 = signed_extend(instr16.imm12, 12); reg[1] = pc + 2; pc += sext_imm12; bflag = 1; - break;// c.jal Expansion:jal x1, offset[11:1] + break; case 0b010: - sext_imm6 = signed_extend(inst_imm6, 6); - System.out.println("c.li x" + inst_rd + " " + sext_imm6 + " (addi x" + inst_rd + " x0 " + sext_imm6 + ")"); - reg[inst_rd] = sext_imm6; + sext_imm6 = signed_extend(instr16.imm6, 6); + reg[instr16.rd] = sext_imm6; break; // c.li Expansion:addi rd,x0,imm[5:0] case 0b011: - if (inst_rd == 2) { // c.addi16sp - System.out.println("c.addi16sp"); + if (instr16.rd == 2) { // c.addi16sp Expansion:addi x2,x2, nzimm[9:4] temp = ((instruction >> 2) & (1 << 4)) | ((instruction << 3) & (1 << 5)) | ((instruction << 1) & (1 << 6)) | ((instruction << 4) & (0x3 << 7)) | ((instruction >> 3) & (1 << 9)); - reg[inst_rd] += signed_extend(temp, 10); - } else { //c.lui - System.out.println("c.lui"); - reg[inst_rd] = signed_extend(inst_imm18, 18); + int sext_imm10 = signed_extend(temp, 10); + reg[2] += sext_imm10; + } else { //c.lui Expansion: lui rd,nzuimm[17:12] + int sext_imm18 = signed_extend(instr16.imm18, 18); + reg[instr16.rd] = sext_imm18; } break; case 0b100: - switch (inst_funct2) { + switch (instr16.funct2) { case 0b00: - System.out.println("c.srli"); - reg[8 + inst_rs1s] = (int) reg[8 + inst_rs1s] >> inst_imm6; - break; // c.srli + // c.srli Expansion:srli rd’,rd’,shamt[5:0] + int ShiftAmt = instr16.imm6 & 0x1F; + reg[8 + instr16.rs1s] = reg[8 + instr16.rs1s] >>> ShiftAmt; + break; case 0b01: - System.out.println("c.srai"); - reg[8 + inst_rs1s] = (int) reg[8 + inst_rs1s] >> inst_imm6; - break; // c.srai + // c.srai + ShiftAmt = instr16.imm6 & 0x1F; + reg[8 + instr16.rs1s] = reg[8 + instr16.rs1s] >>> ShiftAmt; + break; case 0b10: - sext_imm6 = signed_extend(inst_imm6, 6); - System.out.println("c.andi x" + (8 + inst_rs1s) + " " + sext_imm6 + " (andi x" + (8 + inst_rs1s) + " x" + (8 + inst_rs1s) + " " + sext_imm6 + ")"); - reg[8 + inst_rs1s] &= sext_imm6; + imm = signed_extend(instr16.imm6, 6); + reg[8 + instr16.rs1s] &= imm; break; // c.andi Expansion:andi rd’,rd’,imm[5:0] case 0b11: switch ((instruction >> 5) & 3) { case 0b00: - System.out.println("c.sub x" + (8 + inst_rs1s) + " x" + (8 + inst_rs1s) + " x" + (8 + inst_rs2s)); - reg[8 + inst_rs1s] -= reg[8 + inst_rs2s]; + reg[8 + instr16.rs1s] -= reg[8 + instr16.rs2s]; break; // c.sub case 0b01: - System.out.println("c.xor"); - reg[8 + inst_rs1s] ^= reg[8 + inst_rs2s]; + reg[8 + instr16.rs1s] ^= reg[8 + instr16.rs2s]; break; // c.xor case 0b10: - System.out.println("c.or"); - reg[8 + inst_rs1s] |= reg[8 + inst_rs2s]; + reg[8 + instr16.rs1s] |= reg[8 + instr16.rs2s]; break; // c.or case 0b11: - System.out.println("c.and"); - reg[8 + inst_rs1s] &= reg[8 + inst_rs2s]; + reg[8 + instr16.rs1s] &= reg[8 + instr16.rs2s]; break; // c.and } break; } break; case 0b101:// c.j Expansion:jal x0,offset[11:1] - sext_imm12 = signed_extend(inst_imm12, 12); - System.out.println("c.j " + sext_imm12 + " (jal x0 " + sext_imm12 + ")"); - pc += sext_imm12; + imm = signed_extend(instr16.imm12, 12); + pc += imm; bflag = 1; break; case 0b110: // c.beqz Expansion: beq rs1’,x0,offset[8:1] case 0b111: // c.bnez Expansion: bne rs1’,x0,offset[8:1] - temp = ((instruction >> 2) & (0x3 << 1)) | ((instruction >> 7) & (0x3 << 3)) | ((instruction << 3) & (1 << 5)) | ((instruction << 1) & (0x3 << 6)) | ((instruction >> 4) & (1 << 8)); - int sext_temp9 = signed_extend(temp, 9); - System.out.println("c." + (inst_funct3 == 6 ? "beqz" : "bnez") + " x" + (8 + inst_rs1s) + " " + sext_temp9 + " (" + (inst_funct3 == 6 ? "beq" : "bne") + " x" + (8 + inst_rs1s) + " x0 " + sext_temp9 + ")"); - if (inst_funct3 == 6 && reg[8 + inst_rs1s] == 0 || inst_funct3 == 7 && reg[8 + inst_rs1s] != 0) { - pc += sext_temp9; + imm = signed_extend(instr16.imm9, 9); + if (instr16.funct3 == 6 && reg[8 + instr16.rs1s] == 0 || instr16.funct3 == 7 && reg[8 + instr16.rs1s] != 0) { + pc += imm; bflag = 1; } break; default: - System.out.println("未知指令"); break; } break; case 0b10: - switch (inst_funct3) { + switch (instr16.funct3) { case 0b000: - System.out.println("c.slli"); - reg[inst_rd] <<= inst_imm6; + reg[instr16.rd] <<= instr16.imm6; break; // c.slli -// case 1: // c.fldsp -// fdreg[inst_rd] = SYS_BUS.getWord(reg[2] + inst_imm9 + 0) << 0; -// fdreg[inst_rd] |= SYS_BUS.getWord(reg[2] + inst_imm9 + 4) << 32; -// break; case 0b010: // c.lwsp - System.out.println("c.lwsp"); - temp = ((instruction >> 2) & (0x7 << 2)) | ((instruction >> 7) & (1 << 5)) | ((instruction << 4) & (0x3 << 6)); - reg[inst_rd] = SYS_BUS.getWord(reg[2] + temp); // c.lwsp - break; -// case 3: // c.flwsp -// temp = ((instruction >> 2) & (0x7 << 2)) | ((instruction >> 7) & (1 << 5)) | ((instruction << 4) & (0x3 << 6)); -// if (inst_funct3 == 2) reg[inst_rd] = SYS_BUS.getWord(reg[2] + temp); // c.lwsp -// else fdreg[inst_rd] = SYS_BUS.getWord(reg[2] + temp); // c.flwsp -// break; + reg[instr16.rd] = SYS_BUS.getWord(reg[2] + instr16.lwspimm8); // c.lwsp + break; case 0b100: if ((instruction & (1 << 12)) == 0) { - if (inst_rs2 == 0) { // c.jr Expansion:jalr x0,rs1,0 - System.out.println("c.jr x" + inst_rs1 + " (jalr x0 x" + inst_rs1 + " 0)"); - pc = reg[inst_rs1]; + if (instr16.rs2 == 0) { // c.jr Expansion:jalr x0,rs1,0 + pc = reg[instr16.rs1]; bflag = 1; } else { // c.mv Expansion:add rd, x0, rs2 - System.out.println("c.mv x" + inst_rd + " x" + inst_rs2 + " (add x" + inst_rd + " x0 x" + inst_rs2 + ")"); - reg[inst_rd] = reg[inst_rs2]; + reg[instr16.rd] = reg[instr16.rs2]; } } else { - if (inst_rs1 == 0 && inst_rs2 == 0) { // c.ebreak; - System.out.println("c.ebreak"); - } else if (inst_rs2 == 0) { // c.jalr - System.out.println("c.jalr"); + if (instr16.rs1 == 0 && instr16.rs2 == 0) { // c.ebreak; + } else if (instr16.rs2 == 0) { // c.jalr temp = pc + 2; - pc = reg[inst_rs1]; + pc = reg[instr16.rs1]; reg[1] = temp; bflag = 1; } else { // c.add Expansion:add rd,rd,rs2 - System.out.println("c.add x" + inst_rd + " x" + inst_rs2 + " (add x" + inst_rd + " x" + inst_rd + " x" + inst_rs2 + ")"); - reg[inst_rd] += reg[inst_rs2]; + reg[instr16.rd] += reg[instr16.rs2]; } } break; -// case 5: // c.fsdsp -// temp = ((instruction >> 7) & (0x7 << 3)) | ((instruction >> 1) & (0x7 << 6)); -// SYS_BUS.storeWord(reg[2] + temp + 0, (int) (fdreg[inst_rs2] >> 0)); -// SYS_BUS.storeWord(reg[2] + temp + 4, (int) (fdreg[inst_rs2] >> 32)); -// break; case 0b110: // c.swsp Expansion: sw rs2,offset[7:2](x2) - temp = ((instruction >> 7) & (0xf << 2)) | ((instruction >> 1) & (0x3 << 6)); - System.out.println("c.swsp x" + inst_rs2 + " " + temp + "(x2) (sw x" + inst_rs2 + " " + temp + "(x2))"); - SYS_BUS.storeWord(reg[2] + temp, inst_funct3 == 6 ? reg[inst_rs2] : (int) fdreg[inst_rs2]); - break; -// case 0b111: // c.fswsp -// temp = ((instruction >> 7) & (0xf << 2)) | ((instruction >> 1) & (0x3 << 6)); -// SYS_BUS.storeWord(reg[2] + temp, inst_funct3 == 6 ? reg[inst_rs2] : (int) fdreg[inst_rs2]); -// break; + SYS_BUS.storeWord(reg[2] + instr16.swspimm8, reg[instr16.rs2]); + break; default: - System.out.println("未知指令"); break; } break; @@ -267,10 +215,10 @@ private int signed_extend(int a, int size) { public boolean executeInstruction32(int instruction) { prevPc = pc; - Instruction inst = new Instruction(instruction); + Instruction32 inst = new Instruction32(instruction); String instAddr = Integer.toUnsignedString(pc, 16); // 打印指令操作码 - System.out.println(Integer.toUnsignedString(pc, 16) + ":" + Integer.toUnsignedString(instruction, 16) + ":" + inst.toAssemblyString()); +// System.out.println(instAddr + ":" + Integer.toUnsignedString(instruction, 16) + ":" + inst.toAssemblyString()); switch (inst.opcode) { // R-type instructions 包含 RVI RVM case 0b0110011: // ADD / SUB / SLL / SLT / SLTU / XOR / SRL / SRA / OR / AND /MUL /MULH /MULHSU /MULHU /DIV /DIVU /REM /REMU @@ -326,7 +274,7 @@ public boolean executeInstruction32(int instruction) { * r-Type instructions: * ADD / SUB / SLL / SLT / SLTU / XOR / SRL / SRA / OR / AND */ - private void rType(Instruction inst) { + private void rType(Instruction32 inst) { switch (inst.funct3) { case 0b000: // ADD / SUB /MUL switch (inst.funct7) { @@ -425,7 +373,7 @@ private void rType(Instruction inst) { * i-Type load instructions: * LB / LH / LW / LBU / LHU */ - private void iTypeLoad(Instruction inst) { + private void iTypeLoad(Instruction32 inst) { int addr = reg[inst.rs1] + inst.imm; // Byte address switch (inst.funct3) { @@ -454,7 +402,7 @@ private void iTypeLoad(Instruction inst) { * I-type integer instructions: * ADDI / SLTI / SLTIU / XORI / ORI / ANDI / SLLI / SRLI / SRAI */ - private void iTypeInteger(Instruction inst) { + private void iTypeInteger(Instruction32 inst) { switch (inst.funct3) { case 0b000: // ADDI reg[inst.rd] = reg[inst.rs1] + inst.imm; @@ -536,7 +484,7 @@ private void iTypeEcall() { * S-type instructions: * SB / SH / SW */ - private void sType(Instruction inst) { + private void sType(Instruction32 inst) { int addr = reg[inst.rs1] + inst.imm; switch (inst.funct3) { case 0b000: // SB @@ -556,7 +504,7 @@ private void sType(Instruction inst) { * B-type instructions: * BEQ / BNE / BLT / BGE / BLTU / BGEU */ - private void bType(Instruction inst) { + private void bType(Instruction32 inst) { int Imm = inst.imm; switch (inst.funct3) { case 0b000: // BEQ diff --git a/src/main/java/com/ponyvm/soc/core/Instruction.java b/src/main/java/com/ponyvm/soc/core/Instruction.java index 0df0590..22f2425 100644 --- a/src/main/java/com/ponyvm/soc/core/Instruction.java +++ b/src/main/java/com/ponyvm/soc/core/Instruction.java @@ -1,316 +1,4 @@ package com.ponyvm.soc.core; public class Instruction { - int instruction, opcode, rd, rs1, rs2, funct3, funct7, imm; - boolean noRd = false; - boolean sType = false; - boolean ecall = false; - boolean compress = false; - - public Instruction(int instruction) { - this.instruction = instruction; - this.opcode = instruction & 0x03; - - //非压缩指令 - if (this.opcode == 3) { - this.opcode = instruction & 0x7F; // First 7 bits - this.rd = (instruction >> 7) & 0x1F; // bits 11 to 7 - this.funct3 = (instruction >> 12) & 0x7; // bits 14 to 12 - this.rs1 = (instruction >> 15) & 0x1F; // bits 19 to 15 - this.rs2 = (instruction >> 20) & 0x1F; // bits 24 to 20 - } else { - this.compress = true; - this.rd = (instruction >> 7) & 0x1F; // bits 11 to 7 - this.funct3 = (instruction >> 12) & 0x7; // bits 14 to 12 - this.rs1 = (instruction >> 15) & 0x1F; // bits 19 to 15 - this.rs2 = (instruction >> 20) & 0x1F; // bits 24 to 20 - } - - switch (opcode) { - //16位压缩指令 - case 0b00:// - - break; - case 0b01: - - break; - case 0b10: - - break; - //32位指令 - case 0b1101111: // J-type - this.imm = getImmJ(instruction); - break; - case 0b1100111: // I-type - case 0b0000011: - case 0b0010011: - this.imm = (instruction >> 20); // bits 31 to 20 - this.funct7 = (instruction >> 25) & 0x7F;// bits 31 to 25 - break; - case 0b0110011: // R-type - this.funct7 = (instruction >> 25) & 0x7F; // bits 31 to 25 - break; - case 0b0100011: // S-type - imm = (((instruction >> 20) & 0xFFFFFFE0) | - ((instruction >>> 7) & 0x0000001F)); // Returns bits 31 to 25 and 11 to 7 - break; - case 0b1100011: // B-type - this.imm = getImmB(instruction); - break; - case 0b0110111:// U-type - case 0b0010111: - this.imm = instruction & 0xFFFFF000; - break; - default: - // R-type 和 ECALL 没有立即数 - break; - } - } - - - /** - * 获取 B-type 立即数 - * imm[12|10:5|4:1|11] - */ - private int getImmB(int instruction) { - return ((((((instruction >>> 7) & 0x0000001F) | (instruction >> 20) & 0xFFFFFFE0)) & 0xFFFFF7FE) - | (((((instruction >> 20) & 0xFFFFFFE0) | ((instruction >>> 7) & 0x0000001F)) & 0x00000001) << 11)); - } - - /** - * 获取 J-type 立即数 - * Decoded like this: imm[20|10:1|11|19:12] - */ - private int getImmJ(int instruction) { - int b12to19 = (instruction >> 12) & 0xFF; // Bits 12 to 19 of immediate (12 to 19 of instruction) - int b11 = (instruction >> 20) & 0x1; // Bit 11 of immediate (20th bit of instruction) - int b1to10 = (instruction >> 21) & 0x3FF; // Bit 1 to 10 of immediate (21 to 30 of instruction) - int b20 = (instruction >> 31); // Bit 20 of immediate (MSB of instruction) - return (b20 << 20 | b12to19 << 12 | b11 << 11 | b1to10 << 1); - } - - public String toAssemblyString() { - String instr = "", arg1 = "", arg2 = "", arg3 = ""; - switch (opcode) { - // R-type instructions - case 0b0110011: // ADD / SUB / SLL / SLT / SLTU / XOR / SRL / SRA / OR / AND - arg1 = String.format("x%d", rd); - arg2 = String.format("x%d", rs1); - arg3 = String.format("x%d", rs2); - switch (funct3) { - case 0b000: // ADD / SUB - switch (funct7) { - case 0b0000000: // ADD - instr = "add"; - break; - case 0b0100000: // SUB - instr = "sub"; - } - break; - case 0b001: // SLL - instr = "sll"; - break; - case 0b010: // SLT - instr = "slt"; - break; - case 0b011: // SLTU - instr = "sltu"; - break; - case 0b100: // XOR - instr = "xor"; - break; - case 0b101: // SRL / SRA - switch (funct7) { - case 0b0000000: // SRL - instr = "srl"; - break; - case 0b0100000: // SRA - instr = "sra"; - break; - } - break; - case 0b110: // OR - instr = "or"; - break; - case 0b111: // AND - instr = "and"; - } - break; - case 0b1101111: //JAL - arg1 = String.format("x%d", rd); - arg2 = String.format("%d(x%d)", imm, rs1); - instr = "jal"; - break; - case 0b1100111: // JALR - arg1 = String.format("x%d", rd); - arg2 = String.format("%d(x%d)", imm, rs1); - instr = "jalr"; - break; - case 0b0000011: // LB / LH / LW / LBU / LHU - arg1 = String.format("x%d", rd); - arg2 = String.format("%d(x%d)", imm, rs1); - switch (funct3) { - case 0b000: // LB - instr = "lb"; - break; - case 0b001: // LH - instr = "lh"; - break; - case 0b010: // LW - instr = "lw"; - break; - case 0b100: // LBU - instr = "lbu"; - break; - case 0b101: // LHU - instr = "lhu"; - break; - } - break; - case 0b0010011: // ADDI / SLTI / SLTIU / XORI / ORI / ANDI / SLLI / SRLI / SRAI - arg1 = String.format("x%d", rd); - arg2 = String.format("x%d", rs1); - arg3 = String.format("%d", imm); - switch (funct3) { - case 0b000: // ADDI - instr = "addi"; - break; - case 0b010: // SLTI - instr = "slti"; - break; - case 0b011: // SLTIU - instr = "sltiu"; - break; - case 0b100: // XORI - instr = "xori"; - break; - case 0b110: // ORI - instr = "ori"; - break; - case 0b111: // ANDI - instr = "andi"; - break; - case 0b001: // SLLI - instr = "slli"; - break; - case 0b101: // SRLI / SRAI - switch (funct7) { - case 0b0000000: // SRLI - instr = "srli"; - break; - case 0b0100000: // SRAI - instr = "srai"; - break; - } - break; - } - break; - case 0b0001111: // FENCE / FENCE.I - switch (funct3) { - case 0b000: // FENCE - instr = "fence"; - break; - case 0b001: // FENCE.I - instr = "fence.i"; - break; - } - break; - case 0b1110011: // ECALL / EBREAK / CSRRW / CSRRS / CSRRC / CSRRWI / CSRRSI / CSRRCI - switch (funct3) { - case 0b000: // ECALL / EBREAK - switch (imm) { - case 0b000000000000: // ECALL - instr = "ecall"; - ecall = true; - break; - case 0b000000000001: // EBREAK - instr = "ebreak"; - break; - } - break; - case 0b001: // CSRRW - instr = "csrrw"; - break; - case 0b010: // CSRRS - instr = "csrrs"; - break; - case 0b011: // CSRRC - instr = "csrrc"; - break; - case 0b101: // CSRRWI - instr = "csrrwi"; - break; - case 0b110: // CSRRSI - instr = "csrrsi"; - break; - case 0b111: // CSRRCI - instr = "csrrci"; - break; - } - noRd = true; - break; - - //S-type instructions - case 0b0100011: //SB / SH / SW - arg1 = String.format("x%d", rs2); - arg2 = String.format("%d(x%d)", imm, rs1); - switch (funct3) { - case 0b000: // SB - instr = "sb"; - break; - case 0b001: // SH - instr = "sh"; - break; - case 0b010: // SW - instr = "sw"; - break; - } - noRd = true; - sType = true; - break; - - //B-type instructions - case 0b1100011: // BEQ / BNE / BLT / BGE / BLTU / BGEU - arg1 = String.format("x%d", rs1); - arg2 = String.format("x%d", rs2); - arg3 = String.format("%d", imm); - switch (funct3) { - case 0b000: // BEQ - instr = "beq"; - break; - case 0b001: // BNE - instr = "bne"; - break; - case 0b100: // BLT - instr = "blt"; - break; - case 0b101: // BGE - instr = "bge"; - break; - case 0b110: //BLTU - instr = "bltu"; - break; - case 0b111: //BLGEU - instr = "blgeu"; - break; - } - noRd = true; - break; - - //U-type instructions - case 0b0110111: //LUI - arg1 = String.format("x%d", rd); - arg2 = String.format("%d", imm >>> 12); - instr = "lui"; - break; - case 0b0010111: //AUIPC - arg1 = String.format("x%d", rd); - arg2 = String.format("%d", imm >>> 12); - instr = "auipc"; - break; - default: - return String.format("Unrecognized: 0x%08x", instruction); - } - return String.format("%s %s %s %s", instr, arg1, arg2, arg3); - } -} \ No newline at end of file +} diff --git a/src/main/java/com/ponyvm/soc/core/Instruction16.java b/src/main/java/com/ponyvm/soc/core/Instruction16.java new file mode 100644 index 0000000..2459470 --- /dev/null +++ b/src/main/java/com/ponyvm/soc/core/Instruction16.java @@ -0,0 +1,160 @@ +package com.ponyvm.soc.core; + +public class Instruction16 extends Instruction { + int instruction, opcode, rd, rs1, rs2, rs1s, rs2s, rds, imm6, imm7, imm8, lwspimm8, swspimm8, imm9, imm10, imm12, imm18, funct2, funct3; + + public Instruction16(int instruction) { + this.instruction = instruction; + this.opcode = (instruction >> 0) & 0x3; + this.rd = (instruction >> 7) & 0x1f; + this.rs1 = (instruction >> 7) & 0x1f; + this.rs2 = (instruction >> 2) & 0x1f; + this.rs1s = (instruction >> 7) & 0x7; + this.rs2s = (instruction >> 2) & 0x7; + this.rds = (instruction >> 2) & 0x7; + //12 nzimm[5] 6-2 nzimm[4:0] c.addi,c.li c.srli c.andi + this.imm6 = ((instruction >> 2) & 0x1f) | ((instruction >> 7) & (1 << 5));//pass + this.imm7 = ((instruction >> 4) & (1 << 2)) | ((instruction >> 7) & (0x7 << 3)) | ((instruction << 1) & (1 << 6));//pass + this.imm8 = ((instruction >> 7) & (0x7 << 3)) | ((instruction << 1) & (0x3 << 6));//pass + this.lwspimm8 = ((instruction >> 2) & (0x7 << 2)) | ((instruction >> 7) & (1 << 5)) | ((instruction << 4) & (0x3 << 6));//pass + this.swspimm8 = ((instruction >> 7) & (0xf << 2)) | ((instruction >> 1) & (0x3 << 6));//pass + this.imm9 = ((instruction >> 2) & (0x3 << 1)) | ((instruction >> 7) & (0x3 << 3)) | ((instruction << 3) & (1 << 5)) | ((instruction << 1) & (0x3 << 6)) | ((instruction >> 4) & (1 << 8));//pass + this.imm10 = ((instruction >> 4) & (1 << 2)) | ((instruction >> 2) & (1 << 3)) | ((instruction >> 7) & (0b11 << 4)) | ((instruction >> 1) & (0b1111 << 6));//pass + this.imm12 = ((instruction >> 2) & (0x7 << 1)) | ((instruction >> 7) & (1 << 4)) | ((instruction << 3) & (1 << 5)) | ((instruction >> 1) & (0x2d << 6)) | ((instruction << 1) & (1 << 7)) | ((instruction << 2) & (1 << 10));//pass + this.imm18 = ((instruction << 5) & (1 << 17)) | ((instruction << 10) & (0x1f << 12));//pass + this.funct2 = (instruction >> 10) & 0x3; + this.funct3 = (instruction >> 13) & 0x7; + } + + public String toAssemblyString() { + String as = ""; + switch (opcode) { + case 0b00: + switch (funct3) { + case 0b000: + as = "c.addi4spn " + rds + " x2 " + imm10 + " (addi x" + (8 + rds) + " x2 " + imm10 + ")"; + break; // c.addi4spn Expansion:addi rd’,x2,nzuimm + case 0b010: + as = "c.lw"; + break; // + case 0b110: + as = "c.sw x" + (8 + rs2s) + " " + imm7 + "(x" + (8 + rs1s) + ") (sw x" + (8 + rs2s) + " " + imm7 + "(x" + (8 + rs1s) + "))"; + break; //c.sw Expansion:sw rs2’,offset[6:2](rs1’) + default: + as = "未知指令"; + break; + } + break; + case 0b01: + switch (funct3) { + case 0b000: + int sext_imm6 = signed_extend(imm6, 6); + as = "c.addi x" + rd + " " + sext_imm6 + " (addi x" + rd + " x" + rd + " " + sext_imm6 + ")"; + break; //c.addi Expansion:addi rd, rd, nzimm[5:0] + case 0b001: + int sext_imm12 = signed_extend(imm12, 12); + as = "c.jal " + sext_imm12 + " (jal x1 " + sext_imm12 + "(x0))"; + break;// c.jal Expansion:jal x1, offset[11:1] + case 0b010: + sext_imm6 = signed_extend(imm6, 6); + as = "c.li x" + rd + " " + sext_imm6 + " (addi x" + rd + " x0 " + sext_imm6 + ")"; + break; // c.li Expansion:addi rd,x0,imm[5:0] + case 0b011: + if (rd == 2) { // c.addi16sp Expansion:addi x2,x2, nzimm[9:4] + int temp = ((instruction >> 2) & (1 << 4)) | ((instruction << 3) & (1 << 5)) | ((instruction << 1) & (1 << 6)) | ((instruction << 4) & (0x3 << 7)) | ((instruction >> 3) & (1 << 9)); + int sext_imm10 = signed_extend(temp, 10); + as = "c.addi16sp " + sext_imm10 + " (addi x2 x2 " + sext_imm10 + ")"; + } else { //c.lui Expansion: lui rd,nzuimm[17:12] + int sext_imm18 = signed_extend(imm18, 18); + as = "c.lui x" + rd + " " + sext_imm18; + } + break; + case 0b100: + switch (funct2) { + case 0b00: + as = "c.srli x" + (8 + rs1s) + " " + imm6 + " (srli x" + (8 + rs1s) + " x" + (8 + rs1s) + " " + imm6 + ")"; + break; // c.srli Expansion:srli rd’,rd’,shamt[5:0] + case 0b01: + as = "c.srai"; + break; // c.srai + case 0b10: + sext_imm6 = signed_extend(imm6, 6); + as = "c.andi x" + (8 + rs1s) + " " + sext_imm6 + " (andi x" + (8 + rs1s) + " x" + (8 + rs1s) + " " + sext_imm6 + ")"; + break; // c.andi Expansion:andi rd’,rd’,imm[5:0] + case 0b11: + switch ((instruction >> 5) & 3) { + case 0b00: + as = "c.sub x" + (8 + rs1s) + " x" + (8 + rs1s) + " x" + (8 + rs2s); + break; // c.sub + case 0b01: + as = "c.xor"; + break; // c.xor + case 0b10: + as = "c.or"; + break; // c.or + case 0b11: + as = "c.and"; + break; // c.and + } + break; + } + break; + case 0b101:// c.j Expansion:jal x0,offset[11:1] + sext_imm12 = signed_extend(imm12, 12); + as = "c.j " + sext_imm12 + " (jal x0 " + sext_imm12 + ")"; + break; + case 0b110: // c.beqz Expansion: beq rs1’,x0,offset[8:1] + case 0b111: // c.bnez Expansion: bne rs1’,x0,offset[8:1] + int temp = ((instruction >> 2) & (0x3 << 1)) | ((instruction >> 7) & (0x3 << 3)) | ((instruction << 3) & (1 << 5)) | ((instruction << 1) & (0x3 << 6)) | ((instruction >> 4) & (1 << 8)); + int sext_temp9 = signed_extend(temp, 9); + as = "c." + (funct3 == 6 ? "beqz" : "bnez") + " x" + (8 + rs1s) + " " + sext_temp9 + " (" + (funct3 == 6 ? "beq" : "bne") + " x" + (8 + rs1s) + " x0 " + sext_temp9 + ")"; + break; + default: + as = "未知指令"; + break; + } + break; + case 0b10: + switch (funct3) { + case 0b000: + as = "c.slli"; + break; // c.slli + case 0b010: // c.lwsp + as = "c.lwsp"; + int temp = ((instruction >> 2) & (0x7 << 2)) | ((instruction >> 7) & (1 << 5)) | ((instruction << 4) & (0x3 << 6)); + break; + case 0b100: + if ((instruction & (1 << 12)) == 0) { + if (rs2 == 0) { // c.jr Expansion:jalr x0,rs1,0 + as = "c.jr x" + rs1 + " (jalr x0 x" + rs1 + " 0)"; + } else { // c.mv Expansion:add rd, x0, rs2 + as = "c.mv x" + rd + " x" + rs2 + " (add x" + rd + " x0 x" + rs2 + ")"; + } + } else { + if (rs1 == 0 && rs2 == 0) { // c.ebreak; + as = "c.ebreak"; + } else if (rs2 == 0) { // c.jalr + as = "c.jalr"; + } else { // c.add Expansion:add rd,rd,rs2 + as = "c.add x" + rd + " x" + rs2 + " (add x" + rd + " x" + rd + " x" + rs2 + ")"; + } + } + break; + case 0b110: // c.swsp Expansion: sw rs2,offset[7:2](x2) + temp = ((instruction >> 7) & (0xf << 2)) | ((instruction >> 1) & (0x3 << 6)); + as = "c.swsp x" + rs2 + " " + temp + "(x2) (sw x" + rs2 + " " + temp + "(x2))"; + break; + default: + as = "未知指令"; + break; + } + break; + } + return as; + } + + private int signed_extend(int a, int size) { +// return (a & (1 << (size - 1))) == 1 ? (a | ~((1 << size) - 1)) : a; + return (a >> (size - 1) & 1) == 1 ? (a | ~((1 << size) - 1)) : a; + } +} diff --git a/src/main/java/com/ponyvm/soc/core/Instruction32.java b/src/main/java/com/ponyvm/soc/core/Instruction32.java new file mode 100644 index 0000000..82c83a5 --- /dev/null +++ b/src/main/java/com/ponyvm/soc/core/Instruction32.java @@ -0,0 +1,293 @@ +package com.ponyvm.soc.core; + +public class Instruction32 extends Instruction { + int instruction, opcode, rd, rs1, rs2, funct3, funct7, imm; + boolean noRd = false; + boolean sType = false; + boolean ecall = false; + boolean compress = false; + + public Instruction32(int instruction) { + this.instruction = instruction; + this.opcode = instruction & 0x7F; // First 7 bits + this.rd = (instruction >> 7) & 0x1F; // bits 11 to 7 + this.funct3 = (instruction >> 12) & 0x7; // bits 14 to 12 + this.rs1 = (instruction >> 15) & 0x1F; // bits 19 to 15 + this.rs2 = (instruction >> 20) & 0x1F; // bits 24 to 20 + + switch (opcode) { + case 0b1101111: // J-type + this.imm = getImmJ(instruction); + break; + case 0b1100111: // I-type + case 0b0000011: + case 0b0010011: + this.imm = (instruction >> 20); // bits 31 to 20 + this.funct7 = (instruction >> 25) & 0x7F;// bits 31 to 25 + break; + case 0b0110011: // R-type + this.funct7 = (instruction >> 25) & 0x7F; // bits 31 to 25 + break; + case 0b0100011: // S-type + imm = (((instruction >> 20) & 0xFFFFFFE0) | + ((instruction >>> 7) & 0x0000001F)); // Returns bits 31 to 25 and 11 to 7 + break; + case 0b1100011: // B-type + this.imm = getImmB(instruction); + break; + case 0b0110111:// U-type + case 0b0010111: + this.imm = instruction & 0xFFFFF000; + break; + default: + // R-type 和 ECALL 没有立即数 + break; + } + } + + /** + * 获取 B-type imm + * imm[12|10:5|4:1|11] + */ + private int getImmB(int instruction) { + return ((((((instruction >>> 7) & 0x0000001F) | (instruction >> 20) & 0xFFFFFFE0)) & 0xFFFFF7FE) + | (((((instruction >> 20) & 0xFFFFFFE0) | ((instruction >>> 7) & 0x0000001F)) & 0x00000001) << 11)); + } + + /** + * 获取 J-type imm + * imm[20|10:1|11|19:12] + */ + private int getImmJ(int instruction) { + int b12to19 = (instruction >> 12) & 0xFF; // Bits 12 to 19 of immediate (12 to 19 of instruction) + int b11 = (instruction >> 20) & 0x1; // Bit 11 of immediate (20th bit of instruction) + int b1to10 = (instruction >> 21) & 0x3FF; // Bit 1 to 10 of immediate (21 to 30 of instruction) + int b20 = (instruction >> 31); // Bit 20 of immediate (MSB of instruction) + return (b20 << 20 | b12to19 << 12 | b11 << 11 | b1to10 << 1); + } + + public String toAssemblyString() { + String instr = "", arg1 = "", arg2 = "", arg3 = ""; + switch (opcode) { + // R-type instructions + case 0b0110011: // ADD / SUB / SLL / SLT / SLTU / XOR / SRL / SRA / OR / AND + arg1 = String.format("x%d", rd); + arg2 = String.format("x%d", rs1); + arg3 = String.format("x%d", rs2); + switch (funct3) { + case 0b000: // ADD / SUB + switch (funct7) { + case 0b0000000: // ADD + instr = "add"; + break; + case 0b0100000: // SUB + instr = "sub"; + } + break; + case 0b001: // SLL + instr = "sll"; + break; + case 0b010: // SLT + instr = "slt"; + break; + case 0b011: // SLTU + instr = "sltu"; + break; + case 0b100: // XOR + instr = "xor"; + break; + case 0b101: // SRL / SRA + switch (funct7) { + case 0b0000000: // SRL + instr = "srl"; + break; + case 0b0100000: // SRA + instr = "sra"; + break; + } + break; + case 0b110: // OR + instr = "or"; + break; + case 0b111: // AND + instr = "and"; + } + break; + case 0b1101111: //JAL + arg1 = String.format("x%d", rd); + arg2 = String.format("%d(x%d)", imm, rs1); + instr = "jal"; + break; + case 0b1100111: // JALR + arg1 = String.format("x%d", rd); + arg2 = String.format("%d(x%d)", imm, rs1); + instr = "jalr"; + break; + case 0b0000011: // LB / LH / LW / LBU / LHU + arg1 = String.format("x%d", rd); + arg2 = String.format("%d(x%d)", imm, rs1); + switch (funct3) { + case 0b000: // LB + instr = "lb"; + break; + case 0b001: // LH + instr = "lh"; + break; + case 0b010: // LW + instr = "lw"; + break; + case 0b100: // LBU + instr = "lbu"; + break; + case 0b101: // LHU + instr = "lhu"; + break; + } + break; + case 0b0010011: // ADDI / SLTI / SLTIU / XORI / ORI / ANDI / SLLI / SRLI / SRAI + arg1 = String.format("x%d", rd); + arg2 = String.format("x%d", rs1); + arg3 = String.format("%d", imm); + switch (funct3) { + case 0b000: // ADDI + instr = "addi"; + break; + case 0b010: // SLTI + instr = "slti"; + break; + case 0b011: // SLTIU + instr = "sltiu"; + break; + case 0b100: // XORI + instr = "xori"; + break; + case 0b110: // ORI + instr = "ori"; + break; + case 0b111: // ANDI + instr = "andi"; + break; + case 0b001: // SLLI + instr = "slli"; + break; + case 0b101: // SRLI / SRAI + switch (funct7) { + case 0b0000000: // SRLI + instr = "srli"; + break; + case 0b0100000: // SRAI + instr = "srai"; + break; + } + break; + } + break; + case 0b0001111: // FENCE / FENCE.I + switch (funct3) { + case 0b000: // FENCE + instr = "fence"; + break; + case 0b001: // FENCE.I + instr = "fence.i"; + break; + } + break; + case 0b1110011: // ECALL / EBREAK / CSRRW / CSRRS / CSRRC / CSRRWI / CSRRSI / CSRRCI + switch (funct3) { + case 0b000: // ECALL / EBREAK + switch (imm) { + case 0b000000000000: // ECALL + instr = "ecall"; + ecall = true; + break; + case 0b000000000001: // EBREAK + instr = "ebreak"; + break; + } + break; + case 0b001: // CSRRW + instr = "csrrw"; + break; + case 0b010: // CSRRS + instr = "csrrs"; + break; + case 0b011: // CSRRC + instr = "csrrc"; + break; + case 0b101: // CSRRWI + instr = "csrrwi"; + break; + case 0b110: // CSRRSI + instr = "csrrsi"; + break; + case 0b111: // CSRRCI + instr = "csrrci"; + break; + } + noRd = true; + break; + + //S-type instructions + case 0b0100011: //SB / SH / SW + arg1 = String.format("x%d", rs2); + arg2 = String.format("%d(x%d)", imm, rs1); + switch (funct3) { + case 0b000: // SB + instr = "sb"; + break; + case 0b001: // SH + instr = "sh"; + break; + case 0b010: // SW + instr = "sw"; + break; + } + noRd = true; + sType = true; + break; + + //B-type instructions + case 0b1100011: // BEQ / BNE / BLT / BGE / BLTU / BGEU + arg1 = String.format("x%d", rs1); + arg2 = String.format("x%d", rs2); + arg3 = String.format("%d", imm); + switch (funct3) { + case 0b000: // BEQ + instr = "beq"; + break; + case 0b001: // BNE + instr = "bne"; + break; + case 0b100: // BLT + instr = "blt"; + break; + case 0b101: // BGE + instr = "bge"; + break; + case 0b110: //BLTU + instr = "bltu"; + break; + case 0b111: //BLGEU + instr = "blgeu"; + break; + } + noRd = true; + break; + + //U-type instructions + case 0b0110111: //LUI + arg1 = String.format("x%d", rd); + arg2 = String.format("%d", imm >>> 12); + instr = "lui"; + break; + case 0b0010111: //AUIPC + arg1 = String.format("x%d", rd); + arg2 = String.format("%d", imm >>> 12); + instr = "auipc"; + break; + default: + return String.format("Unrecognized: 0x%08x", instruction); + } + return String.format("%s %s %s %s", instr, arg1, arg2, arg3); + } +} \ No newline at end of file diff --git a/src/test/java/RVCInstrTest.java b/src/test/java/RVCInstrTest.java new file mode 100644 index 0000000..b9e3b75 --- /dev/null +++ b/src/test/java/RVCInstrTest.java @@ -0,0 +1,291 @@ +public class RVCInstrTest { + public static void main(String[] args) { + RVCInstrTest t = new RVCInstrTest(); + System.out.println("test_nzuimm_10: " + (t.test_nzuimm_10() ? "pass" : "fail")); + System.out.println("test_uimm_7: " + (t.test_uimm_7() ? "pass" : "fail")); + System.out.println("test_nzimm_6: " + (t.test_nzimm_6() ? "pass" : "fail")); + System.out.println("test_sign_extend: " + (t.test_sign_extend() ? "pass" : "fail")); + System.out.println("test_uimm12: " + (t.test_uimm12() ? "pass" : "fail")); + System.out.println("test_imm18: " + (t.test_imm18() ? "pass" : "fail")); + System.out.println("test_imm8: " + (t.test_imm8() ? "pass" : "fail")); + System.out.println("test_lwspimm8: " + (t.test_lwspimm8() ? "pass" : "fail")); + System.out.println("test_swspimm8: " + (t.test_swspimm8() ? "pass" : "fail")); + System.out.println("test_imm9: " + (t.test_imm9() ? "pass" : "fail")); + } + + /** + * c.beqz c.bnez + * 15-13 12-10 9-7 6-2 1-0 + * 110 offset[8|4:3] rs1’ offset[7:6|2:1|5] 01 + * @return + */ + private boolean test_imm9() { + boolean pass = true; + int instruction = 0b000_1_11_000_11_11_1_00; + int imm9 = imm9(instruction); + pass = pass & (imm9 == 0b111111110); + instruction = 0b000_1_00_000_00_00_0_00; + imm9 = imm9(instruction); + pass = pass & (imm9 == 0b100000000); + instruction = 0b000_0_11_000_00_00_0_00; + imm9 = imm9(instruction); + pass = pass & (imm9 == 0b000011000); + instruction = 0b000_0_00_000_11_00_0_00; + imm9 = imm9(instruction); + pass = pass & (imm9 == 0b011000000); + instruction = 0b000_0_00_000_00_11_0_00; + imm9 = imm9(instruction); + pass = pass & (imm9 == 0b000000110); + instruction = 0b000_0_00_000_00_00_1_00; + imm9 = imm9(instruction); + pass = pass & (imm9 == 0b000100000); + return pass; + } + + private int imm9(int instruction) { + return ((instruction >> 2) & (0x3 << 1)) | ((instruction >> 7) & (0x3 << 3)) | ((instruction << 3) & (1 << 5)) | ((instruction << 1) & (0x3 << 6)) | ((instruction >> 4) & (1 << 8)); + } + + /** + * c.ld + * 15-13 12-10 9-7 6-5 4-2 1-0 + * 011 uimm[5:3] rs1’ uimm[7:6] rd’ 00 + * + * @return + */ + private boolean test_imm8() { + boolean pass = true; + int instruction = 0b000_111_000_11_000_00; + int imm8 = imm8(instruction); + pass = pass & (imm8 == 0b11111000); + instruction = 0b000_111_000_00_000_00; + imm8 = imm8(instruction); + pass = pass & (imm8 == 0b00111000); + instruction = 0b000_000_000_11_000_00; + imm8 = imm8(instruction); + pass = pass & (imm8 == 0b11000000); + return pass; + } + + /** + * c.lwsp + * 15-13 12 11-7 6-2 1-0 + * 010 uimm[5] rd uimm[4:2|7:6] 10 + * + * @return + */ + private boolean test_lwspimm8() { + boolean pass = true; + int instruction = 0b000_1_00000_111_11_00; + int imm8 = lwspimm8(instruction); + pass = pass & (imm8 == 0b11111100); + instruction = 0b000_1_00000_000_00_00; + imm8 = lwspimm8(instruction); + pass = pass & (imm8 == 0b00100000); + instruction = 0b000_0_00000_111_00_00; + imm8 = lwspimm8(instruction); + pass = pass & (imm8 == 0b00011100); + instruction = 0b000_0_00000_000_11_00; + imm8 = lwspimm8(instruction); + pass = pass & (imm8 == 0b11000000); + return pass; + } + + /** + * c.swsp + * 15-13 12-7 6-2 1-0 + * 110 uimm[5:2|7:6] rs2 10 + * + * @return + */ + private boolean test_swspimm8() { + boolean pass = true; + int instruction = 0b000_1111_11_00000_00; + int imm8 = swspimm8(instruction); + pass = pass & (imm8 == 0b11111100); + instruction = 0b000_1111_00_00000_00; + imm8 = swspimm8(instruction); + pass = pass & (imm8 == 0b00111100); + instruction = 0b000_0000_11_00000_00; + imm8 = swspimm8(instruction); + pass = pass & (imm8 == 0b11000000); + return pass; + } + + private int imm8(int instruction) { + return ((instruction >> 7) & (0x7 << 3)) | ((instruction << 1) & (0x3 << 6)); + } + + private int lwspimm8(int instruction) { + return ((instruction >> 2) & (0x7 << 2)) | ((instruction >> 7) & (1 << 5)) | ((instruction << 4) & (0x3 << 6)); + } + + private int swspimm8(int instruction) { + return ((instruction >> 7) & (0xf << 2)) | ((instruction >> 1) & (0x3 << 6)); + } + + + /** + * 15-13 12 11-7 6-2 1-0 + * 011 imm[17] rd imm[16:12] 01 + * + * @return + */ + private boolean test_imm18() { + boolean pass = true; + int instruction = 0b000_1_00000_11111_00; + int imm18 = imm18(instruction); + pass = pass & (imm18 == 0b11_1111_0000_0000_0000); + instruction = 0b000_1_00000_00000_00; + imm18 = imm18(instruction); + pass = pass & (imm18 == 0b10_0000_0000_0000_0000); + instruction = 0b000_0_00000_11111_00; + imm18 = imm18(instruction); + pass = pass & (imm18 == 0b01_1111_0000_0000_0000); + return pass; + } + + private int imm18(int instruction) { + return ((instruction << 5) & (1 << 17)) | ((instruction << 10) & (0x1f << 12)); + } + + /** + * 12-2 + * imm[11|4|9:8|10|6|7|3:1|5] + * + * @return + */ + private boolean test_uimm12() { + boolean pass = true; + int instruction = 0b000_11111111111_00; + int uimm_12 = uimm_12(instruction); + pass = pass & (uimm_12 == 0b111111111110); + + instruction = 0b000_1_0_00_0_0_0_000_0_00; + uimm_12 = uimm_12(instruction); + pass = pass & (uimm_12 == 0b1000_0000_0000); + instruction = 0b000_0_1_00_0_0_0_000_0_00; + uimm_12 = uimm_12(instruction); + pass = pass & (uimm_12 == 0b0000_0001_0000); + instruction = 0b000_0_0_11_0_0_0_000_0_00; + uimm_12 = uimm_12(instruction); + pass = pass & (uimm_12 == 0b0011_0000_0000); + instruction = 0b000_0_0_00_1_0_0_000_0_00; + uimm_12 = uimm_12(instruction); + pass = pass & (uimm_12 == 0b0100_0000_0000); + instruction = 0b000_0_0_00_0_1_0_000_0_00; + uimm_12 = uimm_12(instruction); + pass = pass & (uimm_12 == 0b0000_0100_0000); + instruction = 0b000_0_0_00_0_0_1_000_0_00; + uimm_12 = uimm_12(instruction); + pass = pass & (uimm_12 == 0b0000_1000_0000); + instruction = 0b000_0_0_00_0_0_0_111_0_00; + uimm_12 = uimm_12(instruction); + pass = pass & (uimm_12 == 0b0000_0000_1110); + instruction = 0b000_0_0_00_0_0_0_000_1_00; + uimm_12 = uimm_12(instruction); + pass = pass & (uimm_12 == 0b0000_0010_0000); + return pass; + } + + private int uimm_12(int instruction) { + return ((instruction >> 2) & (0x7 << 1)) | ((instruction >> 7) & (1 << 4)) | ((instruction << 3) & (1 << 5)) | ((instruction >> 1) & (0x2d << 6)) | ((instruction << 1) & (1 << 7)) | ((instruction << 2) & (1 << 10)); + } + + private boolean test_sign_extend() { + boolean pass = true; + short imm = -4034; + int imm1 = imm & 0xFFFF; + int sext_nzimm_6 = signed_extend(imm1 & 0xFFFF, 16); + pass = pass & (sext_nzimm_6 == -4034); + return pass; + } + + + /** + * 有符号扩展 + * 15-13 12 11-7 6-2 1-0 + * 000 nzimm[5] rs1/rd!=0 nzimm[4:0] 01 + * + * @return + */ + private boolean test_nzimm_6() { + boolean pass = true; + int instruction = 0b000_1_00000_11111_00; + int nzimm_6 = nzimm_6(instruction); + pass = pass & (nzimm_6 == 0b111111); + + instruction = 0b000_1_00000_00000_00; + nzimm_6 = nzimm_6(instruction); + pass = pass & (nzimm_6 == 0b100000); + instruction = 0b000_0_00000_11111_00; + nzimm_6 = nzimm_6(instruction); + pass = pass & (nzimm_6 == 0b011111); + + + return pass; + } + + private int nzimm_6(int instruction) { + return ((instruction >> 2) & 0x1f) | ((instruction >> 7) & (1 << 5)); + } + + + /** + * 15-13 12-10 9-7 6-5 4-2 1-0 + * 010 uimm[5:3] rs1’ uimm[2|6] rd’ 00 + */ + + private boolean test_uimm_7() { + boolean pass = true; + int instruction = 0b000_11100011_000_00; + int uimm_7 = uimm_7(instruction); + pass = pass & (uimm_7 == 0b1111100); + + instruction = 0b000_11100000_000_00; + uimm_7 = uimm_7(instruction); + pass = pass & (uimm_7 == 0b0111000); + + instruction = 0b000_00000011_000_00; + uimm_7 = uimm_7(instruction); + pass = pass & (uimm_7 == 0b1000100); + return pass; + } + + /** + * nzuimm[5:4|9:6|2|3] + */ + private boolean test_nzuimm_10() { + boolean pass = true; + int instruction = 0b000_11111111_000_00; + + int nzuimm_10 = nzuimm_10(instruction); + pass = pass & (nzuimm_10 == 0b1111111100); + instruction = 0b000_11000000_000_00; + nzuimm_10 = nzuimm_10(instruction); + pass = pass & (nzuimm_10 == 0b110000); + instruction = 0b000_00111100_000_00; + nzuimm_10 = nzuimm_10(instruction); + pass = pass & (nzuimm_10 == 0b1111000000); + instruction = 0b000_00000010_000_00; + nzuimm_10 = nzuimm_10(instruction); + pass = pass & (nzuimm_10 == 0b0000000100); + instruction = 0b000_00000001_000_00; + nzuimm_10 = nzuimm_10(instruction); + pass = pass & (nzuimm_10 == 0b0000001000); + return pass; + } + + private int uimm_7(int instruction) { + return ((instruction >> 4) & (1 << 2)) | ((instruction >> 7) & (0x7 << 3)) | ((instruction << 1) & (1 << 6)); + } + + private int nzuimm_10(int instruction) { + return ((instruction >> 4) & (1 << 2)) | ((instruction >> 2) & (1 << 3)) | ((instruction >> 7) & (0b11 << 4)) | ((instruction >> 1) & (0b1111 << 6)); + } + + private int signed_extend(int a, int size) { +// return (a & (1 << (size - 1))) == 1 ? (a | ~((1 << size) - 1)) : a; + return (a >> (size - 1) & 1) == 1 ? (a | ~((1 << size) - 1)) : a; + } +} diff --git a/src/test/java/VMTest.java b/src/test/java/VMTest.java index c314a18..2ef2606 100644 --- a/src/test/java/VMTest.java +++ b/src/test/java/VMTest.java @@ -7,6 +7,6 @@ public class VMTest { public static void main(String[] args) throws IOException { Soc vm = new PonySoc(); - vm.launchROM(new File(ClassLoader.getSystemResource("loop.bin").getFile())); + vm.launchROM(new File(ClassLoader.getSystemResource("pi-imc.bin").getFile())); } } diff --git a/src/test/resources/loop-imc.bin b/src/test/resources/loop-imc.bin new file mode 100755 index 0000000000000000000000000000000000000000..82588fbab9196e246b1ba3312b1e70bd2e250ea0 GIT binary patch literal 5980 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

dBt5Rjzs^f3+3U+mx98R*5xcKF|awGfKnA_eyAY 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 literal 0 HcmV?d00001 diff --git a/src/test/resources/pi.bin b/src/test/resources/pi.bin index be7a369a953fd5ee6622aff718221be990012942..b9b4d0c417b58d6d63b9701a449144dadcf243e4 100755 GIT binary patch delta 1846 zcmZ9Me@I(b6vxkfiN+Y_o=ehLhrSqpr0GmsVU!_if2`KJKg3e@kGMa&LA!R+Wn2HK zL&&d1kv<4URGP&*Hu}c~73Xym#OE zoOABE_r7!PUG7iyf90)Sd4ot0(VOHY3+gdO6q%#(|(n7@1Nry8Lz3&{3 zrtO!aomus2Pr=;F zIx)iz_v^~)e%|Hw*-v+RT-dX|KWnw8$6TjpeHy1$x$=yyL#0@cydm_9uUB zZjNtkZl1&23t4`znONH7D|OO7Z+fpKFIIVkW1v}325(zX@X6|lG3w6ODF$i>-Mc+TwiL^O2;%LZx~8Y|w~@RHLP~xGc{rTWpui zzt?^u6zItuacFou;6@z;^EeLEP=tl(jNG>#Y@+dDN?k z49}r&<0bU-{2YB1+v_yp;{f^yccbs)Gw2t26#W3t)oI~hJcUKe8m>V2OcDQ9#LGqe zsE8jI@slEcx}BZ$=XM3HdAQG4tE_wYb6>3}=Lw&N|8v`?g^di#WA_7AZRA7p(l)lz zBe0P_A}hTtVu6&K?H+KX1x{!xZEOa>8RSc9Dv^(Fg~D{lq}T_C@4&J&D#=D4fXh)( zvJ_s!u#dp<-{F(zCr3Rlh8qTwtoZYM z3z9rLL;MFMx%LQ)V10?Ko>>k+l0P)X$05nvC&hz7zew?9(679x;h%y{!o?dp##_FZ-9Cx~RLfCV{AmPJ`e zP&yM+d$g05i?Of!Ud~(_z7R;PIkWGwooHgg>mhPgB)U&IPaGZ|x)34a*wl7cB6iI+ z8e4FjjU8Nb4jn65H5^)V68Khk=3bL{k9UYBk>C#DjcV3HFo4!*EqpZ-Z~CT?czIEnE>eCbuov~46leQ2aTF`rnY))#jrFDKhm^C>xR)Sj?+V0kB= zE=ot|M~^11CsKP1vNTu=@rDD1sb(n&YO9EwFNBIA?ZL);!2&0#eop>#`nR8NZy@Y+ z62TTm^ohA&#H?Mh|B)MWbXJMm{H?oQEbw3MCl&j%yv-AEBoBJsI1%dse#R4V^mmnL zl}~vZw%PZU$l8(n*fXfG+xOJ-H`g~e&)?tNd;_fqWBJXRSn6bxGO3?2wQTN9pI20U z&>|?0))yAsRXdSut{t*$&mwX1k>5ODt&2xneI<#7l%giBTUcZn;5oO&)*?@Caj__h zo}|J;wxWJ9Z1_VV;91_{$|BQlL+_BxpZnhy8i&+gg}#?((J%56`n`M?eJ4BWRgvWo`awQ|euW3n zk8!GAjsEQ|ElO5+HQcY4alVW<%J@MU|5wHj%b4^-*W@X90xC&b}$9j`1npWDsygFU59jRhoJNN ztQD}VA;YZ#Lz_d;9PX6dXBF6|Ulb=w-V44`Eoi^4*TAyIjQRV)#>H_WQ9V{9Y6bh8 z(F48&gKi<{N1c1Yvd9ewXTb6j7GL;Wc*AtgpFnR^rW5JE zYz>-MWT#Kz$h@PgG9XVhs4vL7V}$)3Qh5~&R%Wg<_Jde j@3YLu!%bqDW8o&H%FlD*M$yLKL)6Cqq95k^-QNEJ(;IYW diff --git a/src/test/resources/zs-imc.bin b/src/test/resources/zs-imc.bin new file mode 100755 index 0000000000000000000000000000000000000000..dd043a76d86abbc89b77ed3142d9de94b539150f GIT binary patch literal 6040 zcmeHLeQZQCrlk}iApu<~S_Wai<`Xj(cY(lEEpuP*mqUmd`VV7gfGG5ucsLVJV zKK{g$dyheLpufW*L9!67?2;yATl;n3w!u5Tl<*i8Yq6X?D2A zQC}-WLS*yO+liPkuG$T+EJXNiu#1EQtK%fgPvl)*P-35+i8q#%P0#E(RN^pEy>xo! zj{U~+v6*Oj@679MR}Jy==-Dwnv-l86VtHg?L@f%Nsg|qPqviE8d(NvZOf$bok{nj( zlVpj!(5k5q&R+{Ru8Jx3W_Kj?YAoC!GzhNWy-aZp79tOQefJgG5RIw+zPomn9k+}p zHk8(cCpX(x?JKUiVcTqr44%7Ak?l>zM(vtM)s}5{t1UmRw2YS>uZs88NYC}Lf{jaS z!u7>9F_YC48JxOUdQ-K|TdG={D#((oq;UWv^y>q6-o#kxNMRVTA$=3#ZCzezSuFq>)>rdCOZaUoLXr{TU+6j+J ze>gXKu&>D6Q1z|P*!wS(95`E4Qo7O_Zo=X)C(WH_h`v&vFU9(d+ih2DQ$^J`9UG{>!)y0$ zFl_w1skQlZ^Xi8WKkR7P+v0M^uuhxUnVK7gQl!sR^0J9t_$XQ)FRB{sXVH6_*jKyb zmm&j3*hI_wZIW$Jn2MGE>yxvq;u}Yz<$o@U4%g4wzI)O6$77L!3&%q^|S`Bm@!s(PXCvx|Q-v)jL}&VJ+9HyQXp%fKY=97M#67YVGf zuI>SQ#g?^h>1li8nn&#Gn%39d`>5UJYn447*<**^zGdb8;{DZ2?aNmc5l+yGAYgjG`U0b>S#Pty?N{g3MGAI#Ac|L$| z=lVBw`oGSiAJgeC-tM3|uH6_RIj>zxlfPSw4$C zm>yJ}&z>B-B?oWI!9UNz&*b1x4(`ptJ9BWq4zHTU-(5O=(=7U5>-3N1=zj{VUvHUg zsq@(h{g}2MNb)Q&UtfgxW?>`lqnZ6oJ8UEepUA;~2c9g(1C)>b^*O3^Mv=sd^}G;A zgh_|Lg5o;%Kb;I#?Eib9r}*eQY9U%x1JnG_$wIgp6@Fgi!|HM1Y3=;58Z4|0IA7O) zFL0?24{7cBct?S0{XM1iKM73hd8LN`0<@zXnY62bS`u7)KnP@Y6>*cd)5|0!xD3`{t&n%3DvBPz*w*21 zb$5uKpkE1y?(Qv2^830vGVT^)X1G4R7A01UG0xtqH9gPC{iHsChfc@TM!7k zl^{e}0g_FZBHtp@33$6~GcMI;rOWT_!~~Y2J91v+A&H7Cbt?gHk1YDSG2J@7B2_Ey zPLJQ0*2okdEEE~8w#h-YE2+aDkRe@PU%R1JT>BCC)OS6oc9fcxy$nJogu|p5p7FeI6TiV?1 zRkE#8TZNWEm*STK|E*a{r|l%(;1>H-I1n>($6fT&Ksx{8_4?d`l} zllrH4gIC@KNEdW&>M^1;dIvT1OG10WlnUq6D>v|J9>qs{zrw4 z&MaNO8+y8zlO26qya6NqmjJb)@_m#AC}c->no$Tz&WYRALBPkQciAKqbU)?9`)<~sn|c@GvxL5{IB~m2A>ekYJ38!&xDhzF`!VX|-!iU)T@|lu zI?{VkFlHB5(f`?H9DtJz5pkY#!cV|%6q7R#b;<>eOL0N0#sE%K4B-ddDPZ0oeM6;U RB_q%!{9oInR#Q)I_itFiPUQdq literal 0 HcmV?d00001