diff --git a/README.txt b/README.txt index 2b13975..9560964 100644 --- a/README.txt +++ b/README.txt @@ -1,4 +1,19 @@ This is just for me to understand how all this works, and to learn something new. For example, I learned that endianness is not per bit but per byte. My whole life I've been in misinformation. -So little endian is not that first bit is the LSB, but it's MSB... and after 8 bits it becomes larger, which makes no point \ No newline at end of file +So little endian is not that first bit is the LSB, but it's MSB... and after 8 bits it becomes larger, which makes no point + +And I learned about sign extension, which is pretty cool + +And I learned that Java is bad because it doesn't have unsigned numbers + +To compile stuff: +0. Get the toolchain obviously +1. riscv32-unknown-elf-gcc -c -Oz program.c +2. riscv32-unknown-elf-objcopy -O binary program.o program.bin + program.bin is the binary file with the program +3. Encode to Base64: cat program.bin | base64 + +rv32i, ilp32 compatible toolchain for 64bit Linux: https://lfs.m724.eu/toolchain.tar.zst +Or just the stuff necessary to make a binary file: https://lfs.m724.eu/toolchainlite.tar.zst +Those were compiled with `./configure --prefix=$(pwd)/../toolchain --with-arch=rv32i --with-abi=ilp32` and `make` \ No newline at end of file diff --git a/src/main/java/eu/m724/AddressSpace.java b/src/main/java/eu/m724/AddressSpace.java index 0b1ffba..7e64d96 100644 --- a/src/main/java/eu/m724/AddressSpace.java +++ b/src/main/java/eu/m724/AddressSpace.java @@ -1,8 +1,5 @@ package eu.m724; -import java.util.Arrays; -import java.util.Base64; - /** * Represents an address space * @@ -31,40 +28,33 @@ public class AddressSpace { if (isRomInit) throw new RuntimeException("ROM already initialized"); isRomInit = true; - for (int i=0; i romSize) { + throw new RuntimeException("Program is larger than ROM"); } + + System.arraycopy(program, 0, rom, 0, program.length); } /** - * read {@code n} bytes from ROM
+ * read {@code n} bytes
* * @param address address of first byte * @param n number of bytes, max 8 * @return the unsigned bytes */ - long readRom(int address, int n) { + long read(int address, int n) { long v = 0; - for (int i=0; i> (8 * i) & 0xFF); + if (address < romSize) { + throw new RuntimeException("Cannot write to Read Only Memory"); + } else if (address < romSize + ramSize) { + for (int i=0; i> (8 * i) & 0xFF); + } + } else { + // TODO write to IO } } } diff --git a/src/main/java/eu/m724/CPU.java b/src/main/java/eu/m724/CPU.java index 1ac7495..61f2fd8 100644 --- a/src/main/java/eu/m724/CPU.java +++ b/src/main/java/eu/m724/CPU.java @@ -4,31 +4,29 @@ package eu.m724; * Represents a rv32i CPU */ public class CPU { - private final int xsize = 32; private final int[] register = new int[32]; private AddressSpace addressSpace; private int programCounter; public void init(AddressSpace addressSpace) { - this.addressSpace = addressSpace; // TODO should addressspace really be linked + this.addressSpace = addressSpace; // TODO should addressspace really be here register[2] = addressSpace.romSize + addressSpace.ramSize; programCounter = addressSpace.romBase; } - public int getNextInstruction() { + private int getCurrentInstruction() { if (programCounter > addressSpace.romSize - 1) throw new NullPointerException(); - int inst = (int) addressSpace.readRom(programCounter, 4); - programCounter += 4; - return inst; + return (int) addressSpace.read(programCounter, 4); } public boolean executeNextInstruction() { try { - executeInstruction(getNextInstruction()); + executeInstruction(getCurrentInstruction()); + programCounter += 4; } catch (NullPointerException e) { return false; } @@ -42,6 +40,7 @@ public class CPU { if (opcode != 0) { System.out.println(); + System.out.println("PC: " + programCounter); System.out.println("Instruction: " + Integer.toBinaryString(instruction)); System.out.println("Opcode: " + Integer.toBinaryString(opcode)); System.out.println("Destination register: " + Integer.toBinaryString(rd)); @@ -80,7 +79,10 @@ public class CPU { } else if (opcode == 0b0110111) { // LUI load upper immediate (U type) } else if (opcode == 0b0010111) { // AUIPC (add upper immediate to pc) (U type) + int imm = instruction & 0xFFFFF000; + register[rd] = imm + programCounter; + System.out.printf("AUIPC: pc %d + imm %d = %d -> x%d\n", programCounter, imm, register[rd], rd); } else if (opcode == 0b0110011) { // OP for Integer Register-Register Operations (R type) int funct3 = instruction >> 12 & 0x7; @@ -93,7 +95,7 @@ public class CPU { int sum = register[rs1] + register[rs2] & 0xFF; // to overflow - System.out.printf("ADD: %d #%d + %d #%d = %d #%d\n", register[rs1], rs1, register[rs2], rs2, sum, rd); + System.out.printf("ADD: %d x%d + %d x%d = %d x%d\n", register[rs1], rs1, register[rs2], rs2, sum, rd); register[rd] = sum; } else if (funct7 == 0b0100000) { // SUB @@ -121,48 +123,131 @@ public class CPU { } } else if (opcode == 0b1101111) { // JAL for unconditional jump (J type) + System.out.println("JAL"); + int imm = (instruction >> 31) << 20; // Extract imm[20] and sign-extend + imm |= (instruction >> 21) & 0x3FF; // Extract imm[10:1] + imm |= (instruction >> 20) & 0x1; // Extract imm[11] + imm |= (instruction >> 12) & 0xFF; // Extract imm[19:12] + imm <<= 1; // Left-shift by 1 to account for the implicit 0 + register[rd] = programCounter; // program counter is always incremented after executing instruction + programCounter += imm; + + System.out.printf("Jumped to %d + %d = %d (inst %d)\n", register[rd], imm, programCounter, programCounter / 4); } else if (opcode == 0b1100111) { // JALR for unconditional jump and link register (I type) + int funct3 = instruction >> 12 & 0x7; + if (funct3 != 0) { + // TODO throw an exception + } + + int rs1 = instruction >> 15 & 0x1F; + int imm = instruction >> 20; // sign extends automatically + + int result = register[rs1] + imm; + result &= ~1; // set LSB to 0 + + register[rd] = programCounter + 4; + programCounter = result - 4; // program counter is always incremented after executing instruction + + System.out.printf("JALR: %d x%d + %d = %d (inst %d) -> x%d\n", register[rs1], rs1, imm, result, result / 4, rd); } else if (opcode == 0b1100011) { // BRANCH instruction (B type) - int funct3 = instruction >> 12 & 0x1F; + int funct3 = instruction >> 12 & 0x7; + int rs1 = instruction >> 15 & 0x1F; + int rs2 = instruction >> 20 & 0x1F; + + int imm = (instruction >> 31) << 12; // imm[12] and sign extend + imm |= ((instruction >> 7) & 0x1) << 11; // imm[11] + imm |= ((instruction >> 25) & 0x3f) << 5; // imm[10:5] + imm |= ((instruction >> 8) & 0xf) << 1; // imm[4:1] + // imm[0] is implicitly 0 + + boolean branch = false; if (funct3 == 0b000) { // BEQ - + System.out.printf("BEQ: #%d %d == #%d %d?\n", rs1, register[rs1], rs2, register[rs2]); + branch = register[rs1] == register[rs2]; } else if (funct3 == 0b001) { // BNE - + System.out.printf("BNE: #%d %d != #%d %d?\n", rs1, register[rs1], rs2, register[rs2]); + branch = register[rs1] != register[rs2]; } else if (funct3 == 0b100) { // BLT - + System.out.printf("BLT: #%d %d < #%d %d?\n", rs1, register[rs1], rs2, register[rs2]); + branch = register[rs1] < register[rs2]; } else if (funct3 == 0b101) { // BGE - + System.out.printf("BGE: #%d %d > #%d %d?\n", rs1, register[rs1], rs2, register[rs2]); + branch = register[rs1] > register[rs2]; } else if (funct3 == 0b110) { // BLTU - + System.out.printf("BLTU: #%d %d < #%d %d?\n", rs1, Integer.toUnsignedLong(register[rs1]), rs2, Integer.toUnsignedLong(register[rs2])); + branch = Integer.compareUnsigned(register[rs1], register[rs2]) < 0; } else if (funct3 == 0b111) { // BGEU + System.out.printf("BGEU: #%d %d > #%d %d?\n", rs1, Integer.toUnsignedLong(register[rs1]), rs2, Integer.toUnsignedLong(register[rs2])); + branch = Integer.compareUnsigned(register[rs1], register[rs2]) > 0; + } else { + // TODO illegal instruction + } + if (branch) { + programCounter += imm - 4; // program counter is always incremented after executing instruction so -4 } } else if (opcode == 0b0000011) { // LOAD instruction (I type) int funct3 = instruction >> 12 & 0x1F; + int rs1 = instruction >> 15 & 0x1F; + int imm = instruction >> 20; // sign extends automatically + int addr = register[rs1] + imm; if (funct3 == 0b000) { // LB + // LB loads an 8-bit value from memory, then sign-extends to 32-bits. + register[rd] = (byte) addressSpace.read(addr, 1) >> 24; + System.out.printf("LB: addr: %d x%d + imm %d = %d | val: %d -> x%d\n", register[rs1], rs1, imm, addr, register[rd], rd); } else if (funct3 == 0b001) { // LH + // LH loads a 16-bit value from memory, then sign-extends to 32-bits. + register[rd] = (short) addressSpace.read(addr, 2) >> 16; + System.out.printf("LH: addr: %d x%d + imm %d = %d | val: %d -> x%d\n", register[rs1], rs1, imm, addr, register[rd], rd); } else if (funct3 == 0b010) { // LW + // LW instruction loads a 32-bit value from memory into rd. + register[rd] = (int) addressSpace.read(addr, 4); + System.out.printf("LW: addr: %d x%d + imm %d = %d | val: %d -> x%d\n", register[rs1], rs1, imm, addr, register[rd], rd); } else if (funct3 == 0b100) { // LBU + // LBU loads an 8-bit value from memory but then zero extends to 32-bits. + register[rd] = (int) addressSpace.read(addr, 1); + System.out.printf("LBU: addr: %d x%d + imm %d = %d | val: %d -> x%d\n", register[rs1], rs1, imm, addr, register[rd], rd); } else if (funct3 == 0b101) { // LHU + // LHU loads a 16-bit value from memory but then zero extends to 32-bits. + register[rd] = (int) addressSpace.read(addr, 2); + System.out.printf("LHU: addr: %d x%d + imm %d = %d | val: %d -> x%d\n", register[rs1], rs1, imm, addr, register[rd], rd); + } else { + // TODO illegal instruction } } else if (opcode == 0b0100011) { // STORE instruction (S type) - int funct3 = instruction >> 12 & 0x1F; + int funct3 = instruction >> 12 & 0x7; + int rs1 = instruction >> 15 & 0x1F; + int rs2 = instruction >> 20 & 0x1F; + int imm = instruction >> 20 & 0xffffffe0 | rd; // sign extends automatically + int addr = register[rs1] + imm; + System.out.println(addr); if (funct3 == 0b000) { // SB + // SB stores an 8-bit value from the low bits of register rs2 to memory. + addressSpace.writeRam(addr, 1, register[rs2] & 0xFF); + System.out.printf("SB: x%d %d -> %d\n", rs2, register[rs2] & 0xFF, addr); } else if (funct3 == 0b001) { // SH + // SH stores a 16-bit value from the low bits of register rs2 to memory. + addressSpace.writeRam(addr, 2, register[rs2] & 0xFFFF); + System.out.printf("SH: x%d %d -> %d\n", rs2, register[rs2] & 0xFFFF, addr); } else if (funct3 == 0b010) { // SW + // SW stores a 32-bit value from the low bits of register rs2 to memory. + addressSpace.writeRam(addr, 4, register[rs2]); + System.out.printf("SW: x%d %d -> %d\n", rs2, register[rs2], addr); + } else { + // TODO illegal instruction } } else if (opcode == 0b0001111) { // MISC-MEM for Memory Ordering Instructions (I type) diff --git a/src/main/java/eu/m724/Main.java b/src/main/java/eu/m724/Main.java index cadd202..973e83c 100644 --- a/src/main/java/eu/m724/Main.java +++ b/src/main/java/eu/m724/Main.java @@ -3,17 +3,29 @@ package eu.m724; import java.util.Base64; public class Main { - public static void main(String[] args) { - AddressSpace addressSpace = new AddressSpace(1024, 1024); // 1 KiB - byte[] program = Base64.getDecoder().decode("kw5QABMPUAKzD98B"); + public static void main(String[] args) throws InterruptedException { + AddressSpace addressSpace = new AddressSpace(1024, 1024 * 1024); // 1 KiB + //byte[] program = Base64.getDecoder().decode("kw5QABMPUAKzD98B"); // simple addition program + //byte[] program = Base64.getDecoder().decode("EwUQAGeAAAA="); // return value program + //byte[] program = Base64.getDecoder().decode("EwWgABcDAABnAAMAIyghASMmMQEjLhEAEwQFAJMEBQATCQAAkwkQAGP+mQAThfT/lwAAAOeAAACT" + + // "hOT/MwmpAG/wn/6DIMEBE3UUAAMkgQGDJEEBgynBADMFJQEDKQEBEwEBAmeAAAA="); // 10th value of fibonacci + //byte[] program = Base64.getDecoder().decode("EwXwAhcDAABnAAMAIyghASMmMQEjLhEAEwQFAJMEBQATCQAAkwkQAGP+mQAThfT/lwAAAOeAAACT" + + // "hOT/MwmpAG/wn/6DIMEBE3UUAAMkgQGDJEEBgynBADMFJQEDKQEBEwEBAmeAAAA="); // 47th value of fibonacci + //byte[] program = Base64.getDecoder().decode("EwUAAxcDAABnAAMAIyghASMmMQEjLhEAEwQFAJMEBQATCQAAkwkQAGP+mQAThfT/lwAAAOeAAACT" + + // "hOT/MwmpAG/wn/6DIMEBE3UUAAMkgQGDJEEBgynBADMFJQEDKQEBEwEBAmeAAAA="); // 48th value of fibonacci (overflow) + byte[] program = Base64.getDecoder().decode("EwEB/iMuEQAjLIEAEwQBAiMmBP5vAAABgyfE/pOHFwAjJvT+AyfE/pMHkADj1uf+gyfE/hOFBwCD" + + "IMEBAySBARMBAQJngAAA"); // returns 10 after looping addressSpace.initRom(program); CPU cpu = new CPU(); cpu.init(addressSpace); + cpu.setRegister(1, addressSpace.romSize); // make jumping to x1 end the program - while (cpu.executeNextInstruction()) { } + while (cpu.executeNextInstruction()) { + Thread.sleep(1000); + } - System.out.println("\nEnd of program"); + System.out.println("\nEnd of program. Return value (x10): " + cpu.getRegister(10)); cpu.dumpRegisters(); } } \ No newline at end of file diff --git a/src/main/java/eu/m724/Memory.java b/src/main/java/eu/m724/Memory.java deleted file mode 100644 index 4e152c4..0000000 --- a/src/main/java/eu/m724/Memory.java +++ /dev/null @@ -1,9 +0,0 @@ -package eu.m724; - -public class Memory { - private byte[] memory; - - public Memory(int sizeBytes) { - memory = new byte[sizeBytes]; - } -}