From 58b4bdb1e6c07599fbec7bc9fbb669415d597395 Mon Sep 17 00:00:00 2001 From: vhaudiquet Date: Wed, 11 Oct 2023 21:30:46 +0200 Subject: [PATCH] Memory interface is now MMIO-capable --- src/cpu/rv32cpu.c | 62 +++++++------ src/gdbstub/gdbstub.c | 17 +--- src/memory/memory.c | 205 +++++++++++++++++++++++++++++++++++++++++- src/memory/memory.h | 8 +- src/memory/mmu/mmu.h | 6 -- 5 files changed, 242 insertions(+), 56 deletions(-) delete mode 100644 src/memory/mmu/mmu.h diff --git a/src/cpu/rv32cpu.c b/src/cpu/rv32cpu.c index 3c2e8ba..2183b45 100644 --- a/src/cpu/rv32cpu.c +++ b/src/cpu/rv32cpu.c @@ -4,7 +4,6 @@ #include "devices/sbi/sbi.h" #include "memory/memory.h" -#include "memory/mmu/mmu.h" #include "vriscv.h" #include @@ -210,27 +209,27 @@ static void cpu_execute(rv32_cpu_t* cpu, instruction_t* instruction) { case FUNC3_LB: // Load Byte (8-bits) - cpu->regs.x[instruction->rd] = memory[mmu_translate(address)]; + cpu->regs.x[instruction->rd] = mem_read8(address); // Sign extend from 8 bits to 32 bits cpu->regs.x[instruction->rd] |= (cpu->regs.x[instruction->rd] & 0x80 ? 0xFFFFFF00 : 0); break; case FUNC3_LH: // Load Halfword (16-bits) - cpu->regs.x[instruction->rd] = *((uint16_t*) &memory[mmu_translate(address)]); + cpu->regs.x[instruction->rd] = mem_read16(address); // Sign extend from 16 bits to 32 bits cpu->regs.x[instruction->rd] |= (cpu->regs.x[instruction->rd] & 0x8000 ? 0xFFFF0000 : 0); break; case FUNC3_LW: // Load Word (32-bits) - cpu->regs.x[instruction->rd] = *((uint32_t*) &memory[mmu_translate(address)]); + cpu->regs.x[instruction->rd] = mem_read32(address); break; case FUNC3_LBU: // Load Byte Unsigned (8-bits) - cpu->regs.x[instruction->rd] = memory[mmu_translate(address)]; + cpu->regs.x[instruction->rd] = mem_read8(address); break; case FUNC3_LHU: // Load Halfword Unsigned (16-bits) - cpu->regs.x[instruction->rd] = *((uint16_t*) &memory[mmu_translate(address)]); + cpu->regs.x[instruction->rd] = mem_read16(address); break; default: fprintf(stderr, "FATAL: Unknown func3 0x%x for load instruction, could not execute\n", instruction->func3); @@ -250,15 +249,15 @@ static void cpu_execute(rv32_cpu_t* cpu, instruction_t* instruction) { case FUNC3_SB: // Store Byte (8-bits) - memory[mmu_translate(address)] = cpu->regs.x[instruction->rs2] & 0xFF; + mem_write8(address, cpu->regs.x[instruction->rs2] & 0xFF); break; case FUNC3_SH: // Store Halfword (16-bits) - *((uint16_t*) &memory[mmu_translate(address)]) = cpu->regs.x[instruction->rs2] & 0xFFFF; + mem_write16(address, cpu->regs.x[instruction->rs2] & 0xFFFF); break; case FUNC3_SW: // Store Word (32-bits) - *((uint32_t*) &memory[mmu_translate(address)]) = cpu->regs.x[instruction->rs2]; + mem_write32(address, cpu->regs.x[instruction->rs2]); break; default: fprintf(stderr, "FATAL: Unknown func3 0x%x for store instruction, could not execute\n", instruction->func3); @@ -588,77 +587,76 @@ static void cpu_execute(rv32_cpu_t* cpu, instruction_t* instruction) // FUNC7 contains 2 flag bits in lower part ; ignore them, we look for func7_5 uint32_t address = cpu->regs.x[instruction->rs1]; - uint32_t* ptr = ((uint32_t*) &memory[mmu_translate(address)]); switch(instruction->func7 >> 2) { case FUNC75_LRW: // Load-Reserved Word - cpu->regs.x[instruction->rd] = *ptr; + cpu->regs.x[instruction->rd] = mem_read32(address); // TODO register reservation set that subsumes the bytes in word fprintf(stderr, "LR.W\n"); break; case FUNC75_SCW: // Store-Conditional Word // TODO succeed only if the reservation is still valid and the reservation set contains the bytes written - *ptr = cpu->regs.x[instruction->rs2]; + mem_write32(address, cpu->regs.x[instruction->rs2]); cpu->regs.x[instruction->rd] = 0; // TODO write 1 in rd on failure fprintf(stderr, "SC.W\n"); break; case FUNC75_AMOSWAPW: // Atomic Memory Operation SWAP Word - cpu->regs.x[instruction->rd] = *ptr; + cpu->regs.x[instruction->rd] = mem_read32(address); // Put in RS1 addr the value of RS2 - *ptr = cpu->regs.x[instruction->rs2]; + mem_write32(address, cpu->regs.x[instruction->rs2]); // Put in RS2 the value of RS1 addr (which is in RD) cpu->regs.x[instruction->rs2] = cpu->regs.x[instruction->rd]; break; case FUNC75_AMOADDW: // Atomic Memory Operation ADD Word - cpu->regs.x[instruction->rd] = *ptr; + cpu->regs.x[instruction->rd] = mem_read32(address); // Add rs1 addr and value of rs2 - *ptr = cpu->regs.x[instruction->rd] + cpu->regs.x[instruction->rs2]; + mem_write32(address, cpu->regs.x[instruction->rd] + cpu->regs.x[instruction->rs2]); break; case FUNC75_AMOXORW: // Atomic Memory Operation XOR Word - cpu->regs.x[instruction->rd] = *ptr; + cpu->regs.x[instruction->rd] = mem_read32(address); // Xor rs1 addr and value of rs2 - *ptr = cpu->regs.x[instruction->rd] ^ cpu->regs.x[instruction->rs2]; + mem_write32(address, cpu->regs.x[instruction->rd] ^ cpu->regs.x[instruction->rs2]); break; case FUNC75_AMOANDW: // Atomic Memory Operation AND Word - cpu->regs.x[instruction->rd] = *ptr; + cpu->regs.x[instruction->rd] = mem_read32(address); // AND rs1 addr and value of rs2 - *ptr = cpu->regs.x[instruction->rd] & cpu->regs.x[instruction->rs2]; + mem_write32(address, cpu->regs.x[instruction->rd] & cpu->regs.x[instruction->rs2]); break; case FUNC75_AMOORW: // Atomic Memory Operation OR Word - cpu->regs.x[instruction->rd] = *ptr; + cpu->regs.x[instruction->rd] = mem_read32(address); // Or rs1 addr and value of rs2 - *ptr = cpu->regs.x[instruction->rd] | cpu->regs.x[instruction->rs2]; + mem_write32(address, cpu->regs.x[instruction->rd] | cpu->regs.x[instruction->rs2]); break; case FUNC75_AMOMINW: // Atomic Memory Operation MIN Word - cpu->regs.x[instruction->rd] = *ptr; + cpu->regs.x[instruction->rd] = mem_read32(address); // Min rs1 addr and value of rs2 - *ptr = ((int32_t) cpu->regs.x[instruction->rd]) < ((int32_t) cpu->regs.x[instruction->rs2]) ? cpu->regs.x[instruction->rd] : cpu->regs.x[instruction->rs2]; + mem_write32(address, ((int32_t) cpu->regs.x[instruction->rd]) < ((int32_t) cpu->regs.x[instruction->rs2]) ? cpu->regs.x[instruction->rd] : cpu->regs.x[instruction->rs2]); break; case FUNC75_AMOMAXW: // Atomic Memory Operation MAX Word - cpu->regs.x[instruction->rd] = *ptr; + cpu->regs.x[instruction->rd] = mem_read32(address); // Max rs1 addr and value of rs2 - *ptr = ((int32_t) cpu->regs.x[instruction->rd]) > ((int32_t) cpu->regs.x[instruction->rs2]) ? cpu->regs.x[instruction->rd] : cpu->regs.x[instruction->rs2]; + mem_write32(address, ((int32_t) cpu->regs.x[instruction->rd]) > ((int32_t) cpu->regs.x[instruction->rs2]) ? cpu->regs.x[instruction->rd] : cpu->regs.x[instruction->rs2]); break; case FUNC75_AMOMINUW: // Atomic Memory Operation MIN Unsigned Word - cpu->regs.x[instruction->rd] = *ptr; + cpu->regs.x[instruction->rd] = mem_read32(address); // Min rs1 addr and value of rs2 - *ptr = cpu->regs.x[instruction->rd] < cpu->regs.x[instruction->rs2] ? cpu->regs.x[instruction->rd] : cpu->regs.x[instruction->rs2]; + mem_write32(address, cpu->regs.x[instruction->rd] < cpu->regs.x[instruction->rs2] ? cpu->regs.x[instruction->rd] : cpu->regs.x[instruction->rs2]); break; case FUNC75_AMOMAXUW: // Atomic Memory Operation MAX Unsigned Word - cpu->regs.x[instruction->rd] = *ptr; + cpu->regs.x[instruction->rd] = mem_read32(address); // Max rs1 addr and value of rs2 - *ptr = cpu->regs.x[instruction->rd] > cpu->regs.x[instruction->rs2] ? cpu->regs.x[instruction->rd] : cpu->regs.x[instruction->rs2]; + mem_write32(address, cpu->regs.x[instruction->rd] > cpu->regs.x[instruction->rs2] ? cpu->regs.x[instruction->rd] : cpu->regs.x[instruction->rs2]); break; default: fprintf(stderr, "FATAL: Unknown func7 0x%x for ATOMIC/0x2 instruction, could not execute\n", instruction->func7); @@ -688,7 +686,7 @@ void cpu_loop(rv32_cpu_t* cpu) while(!cpu->sim_ticks_left) pthread_cond_wait(&cpu0->sim_condition, &cpu0_mutex); - pthread_mutex_lock(&memory_mutex); + // pthread_mutex_lock(&memory_mutex); // Fetch raw_instruction_t raw_instruction; @@ -723,7 +721,7 @@ void cpu_loop(rv32_cpu_t* cpu) cpu->sim_ticks_left--; // Let go of cpu and memory mutex - pthread_mutex_unlock(&memory_mutex); + // pthread_mutex_unlock(&memory_mutex); pthread_mutex_unlock(&cpu0_mutex); } } diff --git a/src/gdbstub/gdbstub.c b/src/gdbstub/gdbstub.c index eb6aec4..a5ac990 100644 --- a/src/gdbstub/gdbstub.c +++ b/src/gdbstub/gdbstub.c @@ -2,7 +2,6 @@ #include "cpu/rv32cpu.h" #include "memory/memory.h" -#include "memory/mmu/mmu.h" #include #include @@ -259,19 +258,13 @@ void gdbstub_thread_gdb() uint32_t length; sscanf(packet + 1, "%x,%x", &address, &length); - // Aquire memory mutex - pthread_mutex_lock(&memory_mutex); - char data[length * 2 + 1]; for(size_t i = 0; i < length; i++) { - uint32_t value = memory[mmu_translate(address + i)]; + uint32_t value = mem_read32(address + i); snprintf(data + i * 2, 3, "%02x", value); } - // Let go of memory mutex - pthread_mutex_unlock(&memory_mutex); - gdbstub_send_packet(data, length * 2); } else if(packet[0] == 'M') @@ -286,19 +279,13 @@ void gdbstub_thread_gdb() data_start++; data_start++; - // Aquire memory mutex - pthread_mutex_lock(&memory_mutex); - for(size_t i = 0; i < length; i++) { uint32_t value; sscanf(packet + data_start + i * 2, "%02x", &value); - memory[mmu_translate(address + i)] = value; + mem_write32(address + i, value); } - // Let go of memory mutex - pthread_mutex_unlock(&memory_mutex); - gdbstub_send_packet("OK", 2); } else if(packet[0] == 's') diff --git a/src/memory/memory.c b/src/memory/memory.c index 1156f34..5c93bf2 100644 --- a/src/memory/memory.c +++ b/src/memory/memory.c @@ -4,8 +4,209 @@ uint8_t* memory; pthread_mutex_t memory_mutex; +#define MMIO_INSIDE(io, addr) (addr >= io->address && addr < io->address + (io->reg_size * io->reg_count)) +struct MMIO_ENTRY +{ + uint32_t address; + uint32_t reg_size; + uint32_t reg_count; + void* fn_write; + void* fn_read; + struct MMIO_ENTRY* next; +}; +struct MMIO_ENTRY* mmio = 0; + void mem_init() { - memory = malloc(memory_size); - pthread_mutex_init(&memory_mutex, 0); + memory = malloc(memory_size); + pthread_mutex_init(&memory_mutex, 0); +} + +void mem_register_mmio(uint32_t address, uint32_t reg_size, uint32_t reg_count, void* fn_write, void* fn_read) +{ + struct MMIO_ENTRY** current = &mmio; + while(*current) + current = &(*current)->next; + + *current = malloc(sizeof(struct MMIO_ENTRY)); + (*current)->address = address; + (*current)->reg_count = reg_count; + (*current)->reg_size = reg_size; + (*current)->fn_write = fn_write; + (*current)->fn_read = fn_read; + (*current)->next = 0; +} + +void mem_write8(uint32_t address, uint8_t value) +{ + // Look wether we are on an MMIO region + struct MMIO_ENTRY* io = mmio; + while(io) + { + if(MMIO_INSIDE(io, address)) + { + if(io->reg_size == 1) + { + void (*fn_write)(uint32_t, uint8_t) = io->fn_write; + fn_write(address, value); + return; + } + else + { + fprintf(stderr, "MEMORY: Invalid MMIO access of size 1 in a mapping of %u-sized registers\n", io->reg_size); + exit(EXIT_FAILURE); + } + } + io = io->next; + } + + // Proceed with memory write + pthread_mutex_lock(&memory_mutex); + memory[address] = value; + pthread_mutex_unlock(&memory_mutex); +} + +void mem_write16(uint32_t address, uint16_t value) +{ + // Look wether we are on an MMIO region + struct MMIO_ENTRY* io = mmio; + while(io) + { + if(MMIO_INSIDE(io, address)) + { + if(io->reg_size == 2) + { + void (*fn_write)(uint32_t, uint16_t) = io->fn_write; + fn_write(address, value); + return; + } + else + { + fprintf(stderr, "MEMORY: Invalid MMIO access of size 2 in a mapping of %u-sized registers\n", io->reg_size); + exit(EXIT_FAILURE); + } + } + io = io->next; + } + + // Proceed with memory write + pthread_mutex_lock(&memory_mutex); + *((uint16_t*) &memory[address]) = value; + pthread_mutex_unlock(&memory_mutex); +} + +void mem_write32(uint32_t address, uint32_t value) +{ + // Look wether we are on an MMIO region + struct MMIO_ENTRY* io = mmio; + while(io) + { + if(MMIO_INSIDE(io, address)) + { + if(io->reg_size == 4) + { + void (*fn_write)(uint32_t, uint32_t) = io->fn_write; + fn_write(address, value); + return; + } + else + { + fprintf(stderr, "MEMORY: Invalid MMIO access of size 4 in a mapping of %u-sized registers\n", io->reg_size); + exit(EXIT_FAILURE); + } + } + io = io->next; + } + + // Proceed with memory write + pthread_mutex_lock(&memory_mutex); + *((uint32_t*) &memory[address]) = value; + pthread_mutex_unlock(&memory_mutex); +} + +uint8_t mem_read8(uint32_t address) +{ + // Look wether we are on an MMIO region + struct MMIO_ENTRY* io = mmio; + while(io) + { + if(MMIO_INSIDE(io, address)) + { + if(io->reg_size == 1) + { + uint8_t (*fn_read)(uint32_t) = io->fn_read; + return fn_read(address); + } + else + { + fprintf(stderr, "MEMORY: Invalid MMIO access of size 1 in a mapping of %u-sized registers\n", io->reg_size); + exit(EXIT_FAILURE); + } + } + io = io->next; + } + + // Proceed with memory read + pthread_mutex_lock(&memory_mutex); + uint8_t tr = memory[address]; + pthread_mutex_unlock(&memory_mutex); + return tr; +} + +uint16_t mem_read16(uint32_t address) +{ + // Look wether we are on an MMIO region + struct MMIO_ENTRY* io = mmio; + while(io) + { + if(MMIO_INSIDE(io, address)) + { + if(io->reg_size == 2) + { + uint16_t (*fn_read)(uint32_t) = io->fn_read; + return fn_read(address); + } + else + { + fprintf(stderr, "MEMORY: Invalid MMIO access of size 2 in a mapping of %u-sized registers\n", io->reg_size); + exit(EXIT_FAILURE); + } + } + io = io->next; + } + + // Proceed with memory read + pthread_mutex_lock(&memory_mutex); + uint16_t tr = *((uint16_t*) &memory[address]); + pthread_mutex_unlock(&memory_mutex); + return tr; +} + +uint32_t mem_read32(uint32_t address) +{ + // Look wether we are on an MMIO region + struct MMIO_ENTRY* io = mmio; + while(io) + { + if(MMIO_INSIDE(io, address)) + { + if(io->reg_size == 4) + { + uint32_t (*fn_read)(uint32_t) = io->fn_read; + return fn_read(address); + } + else + { + fprintf(stderr, "MEMORY: Invalid MMIO access of size 4 in a mapping of %u-sized registers\n", io->reg_size); + exit(EXIT_FAILURE); + } + } + io = io->next; + } + + // Proceed with memory read + pthread_mutex_lock(&memory_mutex); + uint32_t tr = *((uint32_t*) &memory[address]); + pthread_mutex_unlock(&memory_mutex); + return tr; } diff --git a/src/memory/memory.h b/src/memory/memory.h index d49ddef..1a29d19 100644 --- a/src/memory/memory.h +++ b/src/memory/memory.h @@ -5,8 +5,14 @@ #include extern uint8_t* memory; -extern pthread_mutex_t memory_mutex; void mem_init(); +void mem_register_mmio(uint32_t address, uint32_t size, uint32_t reg_size, void* fn_write, void* fn_read); +void mem_write8(uint32_t address, uint8_t value); +void mem_write16(uint32_t address, uint16_t value); +void mem_write32(uint32_t address, uint32_t value); +uint8_t mem_read8(uint32_t address); +uint16_t mem_read16(uint32_t address); +uint32_t mem_read32(uint32_t address); #endif diff --git a/src/memory/mmu/mmu.h b/src/memory/mmu/mmu.h deleted file mode 100644 index 8bdb923..0000000 --- a/src/memory/mmu/mmu.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef MMU_H -#define MMU_H - -#define mmu_translate(vaddr) (vaddr) - -#endif