This commit is contained in:
Minecon724 2024-10-14 13:11:34 +02:00
parent 3850c27440
commit b21dab032d
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
5 changed files with 157 additions and 58 deletions

View file

@ -2,3 +2,18 @@ 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. 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 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`

View file

@ -1,8 +1,5 @@
package eu.m724; package eu.m724;
import java.util.Arrays;
import java.util.Base64;
/** /**
* Represents an address space * Represents an address space
* *
@ -31,41 +28,34 @@ public class AddressSpace {
if (isRomInit) throw new RuntimeException("ROM already initialized"); if (isRomInit) throw new RuntimeException("ROM already initialized");
isRomInit = true; isRomInit = true;
for (int i=0; i<program.length; i++) { if (program.length > romSize) {
rom[i] = program[i]; throw new RuntimeException("Program is larger than ROM");
} }
System.arraycopy(program, 0, rom, 0, program.length);
} }
/** /**
* read {@code n} bytes from ROM<br> * read {@code n} bytes<br>
* *
* @param address address of first byte * @param address address of first byte
* @param n number of bytes, max 8 * @param n number of bytes, max 8
* @return the unsigned bytes * @return the unsigned bytes
*/ */
long readRom(int address, int n) { long read(int address, int n) {
long v = 0; long v = 0;
if (address < romSize) {
for (int i=0; i<n; i++) { for (int i=0; i<n; i++) {
v |= (long) (rom[address + i] & 0xFF) << (8 * i); v |= (long) (rom[address + i] & 0xFF) << (8 * i);
} }
} else if (address < romSize + ramSize) {
return v;
}
/**
* read {@code n} bytes from RAM
*
* @param address address of first byte
* @param n number of bytes, max 8
* @return the unsigned bytes
*/
long readRam(int address, int n) {
long v = 0;
for (int i=0; i<n; i++) { for (int i=0; i<n; i++) {
v |= (long) (ram[address + i] & 0xFF) << (8 * i); v |= (long) (ram[address + i] & 0xFF) << (8 * i);
} }
} else {
// TODO read from IO
}
return v; return v;
} }
@ -78,8 +68,14 @@ public class AddressSpace {
* @param value the value * @param value the value
*/ */
void writeRam(int address, int n, long value) { void writeRam(int address, int n, long value) {
if (address < romSize) {
throw new RuntimeException("Cannot write to Read Only Memory");
} else if (address < romSize + ramSize) {
for (int i=0; i<n; i++) { for (int i=0; i<n; i++) {
ram[address + i] = (byte) (value >> (8 * i) & 0xFF); ram[address + i] = (byte) (value >> (8 * i) & 0xFF);
} }
} else {
// TODO write to IO
}
} }
} }

View file

@ -4,31 +4,29 @@ package eu.m724;
* Represents a rv32i CPU * Represents a rv32i CPU
*/ */
public class CPU { public class CPU {
private final int xsize = 32;
private final int[] register = new int[32]; private final int[] register = new int[32];
private AddressSpace addressSpace; private AddressSpace addressSpace;
private int programCounter; private int programCounter;
public void init(AddressSpace addressSpace) { 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; register[2] = addressSpace.romSize + addressSpace.ramSize;
programCounter = addressSpace.romBase; programCounter = addressSpace.romBase;
} }
public int getNextInstruction() { private int getCurrentInstruction() {
if (programCounter > addressSpace.romSize - 1) if (programCounter > addressSpace.romSize - 1)
throw new NullPointerException(); throw new NullPointerException();
int inst = (int) addressSpace.readRom(programCounter, 4); return (int) addressSpace.read(programCounter, 4);
programCounter += 4;
return inst;
} }
public boolean executeNextInstruction() { public boolean executeNextInstruction() {
try { try {
executeInstruction(getNextInstruction()); executeInstruction(getCurrentInstruction());
programCounter += 4;
} catch (NullPointerException e) { } catch (NullPointerException e) {
return false; return false;
} }
@ -42,6 +40,7 @@ public class CPU {
if (opcode != 0) { if (opcode != 0) {
System.out.println(); System.out.println();
System.out.println("PC: " + programCounter);
System.out.println("Instruction: " + Integer.toBinaryString(instruction)); System.out.println("Instruction: " + Integer.toBinaryString(instruction));
System.out.println("Opcode: " + Integer.toBinaryString(opcode)); System.out.println("Opcode: " + Integer.toBinaryString(opcode));
System.out.println("Destination register: " + Integer.toBinaryString(rd)); 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 == 0b0110111) { // LUI load upper immediate (U type)
} else if (opcode == 0b0010111) { // AUIPC (add upper immediate to pc) (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) } else if (opcode == 0b0110011) { // OP for Integer Register-Register Operations (R type)
int funct3 = instruction >> 12 & 0x7; int funct3 = instruction >> 12 & 0x7;
@ -93,7 +95,7 @@ public class CPU {
int sum = register[rs1] + register[rs2] & 0xFF; // to overflow 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; register[rd] = sum;
} else if (funct7 == 0b0100000) { // SUB } else if (funct7 == 0b0100000) { // SUB
@ -121,48 +123,131 @@ public class CPU {
} }
} else if (opcode == 0b1101111) { // JAL for unconditional jump (J type) } 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) } 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) } 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 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 } 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 } 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 } 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 } 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 } 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) } else if (opcode == 0b0000011) { // LOAD instruction (I type)
int funct3 = instruction >> 12 & 0x1F; 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 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 } 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 } 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 } 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 } 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) } 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 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 } 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 } 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) } else if (opcode == 0b0001111) { // MISC-MEM for Memory Ordering Instructions (I type)

View file

@ -3,17 +3,29 @@ package eu.m724;
import java.util.Base64; import java.util.Base64;
public class Main { public class Main {
public static void main(String[] args) { public static void main(String[] args) throws InterruptedException {
AddressSpace addressSpace = new AddressSpace(1024, 1024); // 1 KiB AddressSpace addressSpace = new AddressSpace(1024, 1024 * 1024); // 1 KiB
byte[] program = Base64.getDecoder().decode("kw5QABMPUAKzD98B"); //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); addressSpace.initRom(program);
CPU cpu = new CPU(); CPU cpu = new CPU();
cpu.init(addressSpace); 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(); cpu.dumpRegisters();
} }
} }

View file

@ -1,9 +0,0 @@
package eu.m724;
public class Memory {
private byte[] memory;
public Memory(int sizeBytes) {
memory = new byte[sizeBytes];
}
}