parent
94c4c16727
commit
a64b878058
7 changed files with 131 additions and 18 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
.vscode/
|
||||
build/
|
||||
programs/tests/
|
||||
programs/tests/
|
||||
toolchain/
|
|
@ -1,4 +1,4 @@
|
|||
RISC-V (rv32mi) emulator in C \
|
||||
RISC-V (rv32i + Zicsr) emulator in C \
|
||||
This is just for me to understand how all this works, and to learn something new. \
|
||||
So don't use it.
|
||||
|
||||
|
|
|
@ -16,6 +16,9 @@ struct AddressSpace_s {
|
|||
uint8_t *ram;
|
||||
// The size of RAM.
|
||||
uint32_t ramSize;
|
||||
|
||||
// The CSRs, size is always 4096 (12 bits)
|
||||
uint32_t *csr;
|
||||
};
|
||||
|
||||
typedef struct AddressSpace_s AddressSpace;
|
||||
|
@ -23,5 +26,7 @@ typedef struct AddressSpace_s AddressSpace;
|
|||
AddressSpace *create_address_space(const uint32_t romSize, const uint32_t ramSize);
|
||||
int read_address_space(const AddressSpace *addressSpace, const uint32_t address, const int n, void *dest);
|
||||
int write_address_space(const AddressSpace *addressSpace, const uint32_t address, const int n, void *src);
|
||||
int read_csr(const AddressSpace *addressSpace, const uint16_t address, void *dest);
|
||||
int write_csr(const AddressSpace *addressSpace, const uint16_t address, void *src);
|
||||
|
||||
#endif
|
BIN
programs/fib.elf
Executable file
BIN
programs/fib.elf
Executable file
Binary file not shown.
14
programs/src/fib.c
Normal file
14
programs/src/fib.c
Normal file
|
@ -0,0 +1,14 @@
|
|||
unsigned int main() {
|
||||
int n = 47;
|
||||
|
||||
if (n <= 1) return n;
|
||||
|
||||
unsigned int prev = 0, curr = 1, next;
|
||||
for (int i = 2; i <= n; i++) {
|
||||
next = prev + curr;
|
||||
prev = curr;
|
||||
curr = next;
|
||||
}
|
||||
|
||||
return curr;
|
||||
}
|
|
@ -23,6 +23,7 @@ AddressSpace *create_address_space(const uint32_t romSize, const uint32_t ramSiz
|
|||
addressSpace->romLocked = false;
|
||||
addressSpace->ram = ram;
|
||||
addressSpace->ramSize = ramSize;
|
||||
addressSpace->csr = calloc(4096, 1);
|
||||
|
||||
return addressSpace;
|
||||
}
|
||||
|
@ -82,5 +83,27 @@ int write_address_space(const AddressSpace *addressSpace, const uint32_t address
|
|||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int read_csr(const AddressSpace *addressSpace, const uint16_t address, void *dest) {
|
||||
if (address >= 4096) {
|
||||
fprintf(stderr, "Trying to read from CSR address 0x%X, which is higher than maximum 0xFFF\n", address);
|
||||
return 1;
|
||||
}
|
||||
|
||||
memcpy(dest, addressSpace->csr + address, 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_csr(const AddressSpace *addressSpace, const uint16_t address, void *dest) {
|
||||
if (address >= 4096) {
|
||||
fprintf(stderr, "Trying to write to CSR address 0x%X, which is higher than maximum 0xFFF\n", address);
|
||||
return 1;
|
||||
}
|
||||
|
||||
memcpy(addressSpace->csr + address, dest, 4);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -238,11 +238,11 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside
|
|||
imm |= ((instruction >> 20) & 0x1) << 11; // imm[11]
|
||||
imm |= ((instruction >> 21) & 0x3FF) << 1; // imm[10:1]
|
||||
|
||||
printf("JAL: Jumped to 0x%X + %d = 0x%X (inst %u), link x%u", cpu->programCounter, imm, cpu->programCounter, cpu->programCounter / 4, rd);
|
||||
|
||||
registers[rd] = cpu->programCounter + 4;
|
||||
cpu->programCounter += imm - 4; // program counter is incremented after this, and we have to execute the function we point to
|
||||
|
||||
printf("JAL: Jumped to 0x%X + %d = 0x%X (inst %u), link x%u", registers[rd], imm, cpu->programCounter, cpu->programCounter / 4, rd);
|
||||
|
||||
break;
|
||||
}
|
||||
case 0b0110011: { // OP for Integer Register-Register Operations (R type)
|
||||
|
@ -279,25 +279,95 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside
|
|||
}
|
||||
case 0b1110011: {// SYSTEM (I type) instructions are used to access system functionality that might require privileged access, here only ECALL and EBREAK
|
||||
uint8_t funct3 = instruction >> 12 & 0x7;
|
||||
uint32_t rs1 = instruction >> 15 & 0x1F;
|
||||
uint32_t func12 = instruction >> 20 & 0xFFF;
|
||||
uint32_t rs1 = instruction >> 15 & 0x1F; // aka uimm. zero extends automatically
|
||||
uint32_t func12 = instruction >> 20 & 0xFFF; // aka csr
|
||||
|
||||
if (funct3 != 0 || rd != 0 || rs1 != 0) {
|
||||
fprintf(stderr, "For SYSTEM instructions, funct3, rd and rs1 must be 0, but are 0x%X, 0x%X and 0x%X. Zicsr is not supported.", funct3, rd, rs1);
|
||||
return 2;
|
||||
if (funct3 == 0) {
|
||||
if (rd != 0 || rs1 != 0) {
|
||||
fprintf(stderr, "For ECALL / EBREAK instructions, rd and rs1 must be 0, but are 0x%X and 0x%X.", rd, rs1);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (func12 == 0) { // ECALL
|
||||
printf("--- ECALL ---");
|
||||
// TODO do something
|
||||
} else if (func12 == 1) { // EBREAK
|
||||
printf("--- EBREAK---");
|
||||
// TODO do something
|
||||
} else {
|
||||
fprintf(stderr, "Invalid func12 for environment instructions: 0x%X", func12);
|
||||
//return 2;
|
||||
}
|
||||
} else if (funct3 == 0b001) { // CSRRW Atomic Read/Write CSR
|
||||
printf("CSRRW: (0x%X <-> x%u) -> x%u", func12, rs1, rd);
|
||||
if (rd != 0) {
|
||||
uint32_t csrValue;
|
||||
read_csr(addressSpace, func12, &csrValue);
|
||||
|
||||
write_csr(addressSpace, func12, registers + rs1);
|
||||
registers[rd] = csrValue;
|
||||
} else {
|
||||
write_csr(addressSpace, func12, registers + rs1);
|
||||
}
|
||||
} else if (funct3 == 0b010) { // CSRRS Atomic Read and Set Bits in CSR
|
||||
printf("CSRRS: (0x%X | x%u) -> x%u", func12, rs1, rd);
|
||||
uint32_t csrValue;
|
||||
read_csr(addressSpace, func12, &csrValue);
|
||||
|
||||
if (rs1 != 0) {
|
||||
uint32_t csrValueNew = csrValue | registers[rs1];
|
||||
write_csr(addressSpace, func12, &csrValueNew);
|
||||
}
|
||||
|
||||
registers[rd] = csrValue;
|
||||
} else if (funct3 == 0b011) { // CSRRC Atomic Read and Clear Bits in CSR
|
||||
printf("CSRRC: (0x%X & ~x%u) -> x%u", func12, rs1, rd);
|
||||
uint32_t csrValue;
|
||||
read_csr(addressSpace, func12, &csrValue);
|
||||
|
||||
if (rs1 != 0) {
|
||||
uint32_t csrValueNew = csrValue & ~registers[rs1];
|
||||
write_csr(addressSpace, func12, &csrValueNew);
|
||||
}
|
||||
|
||||
registers[rd] = csrValue;
|
||||
} else if (funct3 == 0b101) { // CSRRWI Atomic Read/Write CSR Immediate
|
||||
printf("CSRRWI: (0x%X <-> 0x%X) -> x%u", func12, rs1, rd);
|
||||
// since we don't worry about order, we can simplify
|
||||
if (rd != 0) {
|
||||
read_csr(addressSpace, func12, registers + rd);
|
||||
}
|
||||
|
||||
write_csr(addressSpace, func12, &rs1); // rs1 is uimm
|
||||
} else if (funct3 == 0b110) { // CSRRSI Atomic Read and Set Bits in CSR Immediate
|
||||
printf("CSRRSI: (0x%X | 0x%X) -> x%u", func12, rs1, rd);
|
||||
read_csr(addressSpace, func12, registers + rd);
|
||||
|
||||
if (rs1 != 0) {
|
||||
uint32_t csrValueNew = registers[rd] | rs1;
|
||||
write_csr(addressSpace, func12, &csrValueNew);
|
||||
}
|
||||
} else if (funct3 == 0b111) { // CSRRCI Atomic Read and Clear Bits in CSR Immediate
|
||||
printf("CSRRCI: (0x%X & 0x%X) -> x%u", func12, rs1, rd);
|
||||
read_csr(addressSpace, func12, registers + rd);
|
||||
|
||||
if (rs1 != 0) {
|
||||
uint32_t csrValueNew = registers[rd] & ~rs1;
|
||||
write_csr(addressSpace, func12, &csrValueNew);
|
||||
}
|
||||
}
|
||||
|
||||
if (func12 == 0) { // ECALL
|
||||
printf("--- ECALL ---");
|
||||
// TODO do something
|
||||
} else if (func12 == 1) { // EBREAK
|
||||
printf("--- EBREAK---");
|
||||
// TODO do something
|
||||
} else {
|
||||
fprintf(stderr, "Invalid SYSTEM func12: 0x%X", func12);
|
||||
return 2;
|
||||
break;
|
||||
}
|
||||
case 0b0001111: { // MISC-MEM
|
||||
uint8_t funct3 = instruction >> 12 & 0x7;
|
||||
uint8_t rs1 = instruction >> 15 & 0x1F;
|
||||
|
||||
if (funct3 != 0) {
|
||||
fprintf(stderr, "funct3 in MISC-MEM must be zero, but is 0x%X", funct3);
|
||||
}
|
||||
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
|
Loading…
Reference in a new issue