Memory interface is now MMIO-capable

master
vhaudiquet 1 year ago
parent d7e684ad91
commit 58b4bdb1e6
  1. 62
      src/cpu/rv32cpu.c
  2. 17
      src/gdbstub/gdbstub.c
  3. 205
      src/memory/memory.c
  4. 8
      src/memory/memory.h
  5. 6
      src/memory/mmu/mmu.h

@ -4,7 +4,6 @@
#include "devices/sbi/sbi.h" #include "devices/sbi/sbi.h"
#include "memory/memory.h" #include "memory/memory.h"
#include "memory/mmu/mmu.h"
#include "vriscv.h" #include "vriscv.h"
#include <stdlib.h> #include <stdlib.h>
@ -210,27 +209,27 @@ static void cpu_execute(rv32_cpu_t* cpu, instruction_t* instruction)
{ {
case FUNC3_LB: case FUNC3_LB:
// Load Byte (8-bits) // 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 // Sign extend from 8 bits to 32 bits
cpu->regs.x[instruction->rd] |= (cpu->regs.x[instruction->rd] & 0x80 ? 0xFFFFFF00 : 0); cpu->regs.x[instruction->rd] |= (cpu->regs.x[instruction->rd] & 0x80 ? 0xFFFFFF00 : 0);
break; break;
case FUNC3_LH: case FUNC3_LH:
// Load Halfword (16-bits) // 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 // Sign extend from 16 bits to 32 bits
cpu->regs.x[instruction->rd] |= (cpu->regs.x[instruction->rd] & 0x8000 ? 0xFFFF0000 : 0); cpu->regs.x[instruction->rd] |= (cpu->regs.x[instruction->rd] & 0x8000 ? 0xFFFF0000 : 0);
break; break;
case FUNC3_LW: case FUNC3_LW:
// Load Word (32-bits) // Load Word (32-bits)
cpu->regs.x[instruction->rd] = *((uint32_t*) &memory[mmu_translate(address)]); cpu->regs.x[instruction->rd] = mem_read32(address);
break; break;
case FUNC3_LBU: case FUNC3_LBU:
// Load Byte Unsigned (8-bits) // Load Byte Unsigned (8-bits)
cpu->regs.x[instruction->rd] = memory[mmu_translate(address)]; cpu->regs.x[instruction->rd] = mem_read8(address);
break; break;
case FUNC3_LHU: case FUNC3_LHU:
// Load Halfword Unsigned (16-bits) // 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; break;
default: default:
fprintf(stderr, "FATAL: Unknown func3 0x%x for load instruction, could not execute\n", instruction->func3); 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: case FUNC3_SB:
// Store Byte (8-bits) // Store Byte (8-bits)
memory[mmu_translate(address)] = cpu->regs.x[instruction->rs2] & 0xFF; mem_write8(address, cpu->regs.x[instruction->rs2] & 0xFF);
break; break;
case FUNC3_SH: case FUNC3_SH:
// Store Halfword (16-bits) // 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; break;
case FUNC3_SW: case FUNC3_SW:
// Store Word (32-bits) // Store Word (32-bits)
*((uint32_t*) &memory[mmu_translate(address)]) = cpu->regs.x[instruction->rs2]; mem_write32(address, cpu->regs.x[instruction->rs2]);
break; break;
default: default:
fprintf(stderr, "FATAL: Unknown func3 0x%x for store instruction, could not execute\n", instruction->func3); 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 // 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 address = cpu->regs.x[instruction->rs1];
uint32_t* ptr = ((uint32_t*) &memory[mmu_translate(address)]);
switch(instruction->func7 >> 2) switch(instruction->func7 >> 2)
{ {
case FUNC75_LRW: case FUNC75_LRW:
// Load-Reserved Word // 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 // TODO register reservation set that subsumes the bytes in word
fprintf(stderr, "LR.W\n"); fprintf(stderr, "LR.W\n");
break; break;
case FUNC75_SCW: case FUNC75_SCW:
// Store-Conditional Word // Store-Conditional Word
// TODO succeed only if the reservation is still valid and the reservation set contains the bytes written // 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 cpu->regs.x[instruction->rd] = 0; // TODO write 1 in rd on failure
fprintf(stderr, "SC.W\n"); fprintf(stderr, "SC.W\n");
break; break;
case FUNC75_AMOSWAPW: case FUNC75_AMOSWAPW:
// Atomic Memory Operation SWAP Word // 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 // 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) // Put in RS2 the value of RS1 addr (which is in RD)
cpu->regs.x[instruction->rs2] = cpu->regs.x[instruction->rd]; cpu->regs.x[instruction->rs2] = cpu->regs.x[instruction->rd];
break; break;
case FUNC75_AMOADDW: case FUNC75_AMOADDW:
// Atomic Memory Operation ADD Word // 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 // 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; break;
case FUNC75_AMOXORW: case FUNC75_AMOXORW:
// Atomic Memory Operation XOR Word // 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 // 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; break;
case FUNC75_AMOANDW: case FUNC75_AMOANDW:
// Atomic Memory Operation AND Word // 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 // 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; break;
case FUNC75_AMOORW: case FUNC75_AMOORW:
// Atomic Memory Operation OR Word // 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 // 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; break;
case FUNC75_AMOMINW: case FUNC75_AMOMINW:
// Atomic Memory Operation MIN Word // 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 // 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; break;
case FUNC75_AMOMAXW: case FUNC75_AMOMAXW:
// Atomic Memory Operation MAX Word // 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 // 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; break;
case FUNC75_AMOMINUW: case FUNC75_AMOMINUW:
// Atomic Memory Operation MIN Unsigned Word // 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 // 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; break;
case FUNC75_AMOMAXUW: case FUNC75_AMOMAXUW:
// Atomic Memory Operation MAX Unsigned Word // 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 // 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; break;
default: default:
fprintf(stderr, "FATAL: Unknown func7 0x%x for ATOMIC/0x2 instruction, could not execute\n", instruction->func7); 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) while(!cpu->sim_ticks_left)
pthread_cond_wait(&cpu0->sim_condition, &cpu0_mutex); pthread_cond_wait(&cpu0->sim_condition, &cpu0_mutex);
pthread_mutex_lock(&memory_mutex); // pthread_mutex_lock(&memory_mutex);
// Fetch // Fetch
raw_instruction_t raw_instruction; raw_instruction_t raw_instruction;
@ -723,7 +721,7 @@ void cpu_loop(rv32_cpu_t* cpu)
cpu->sim_ticks_left--; cpu->sim_ticks_left--;
// Let go of cpu and memory mutex // Let go of cpu and memory mutex
pthread_mutex_unlock(&memory_mutex); // pthread_mutex_unlock(&memory_mutex);
pthread_mutex_unlock(&cpu0_mutex); pthread_mutex_unlock(&cpu0_mutex);
} }
} }

@ -2,7 +2,6 @@
#include "cpu/rv32cpu.h" #include "cpu/rv32cpu.h"
#include "memory/memory.h" #include "memory/memory.h"
#include "memory/mmu/mmu.h"
#include <errno.h> #include <errno.h>
#include <stdlib.h> #include <stdlib.h>
@ -259,19 +258,13 @@ void gdbstub_thread_gdb()
uint32_t length; uint32_t length;
sscanf(packet + 1, "%x,%x", &address, &length); sscanf(packet + 1, "%x,%x", &address, &length);
// Aquire memory mutex
pthread_mutex_lock(&memory_mutex);
char data[length * 2 + 1]; char data[length * 2 + 1];
for(size_t i = 0; i < length; i++) 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); snprintf(data + i * 2, 3, "%02x", value);
} }
// Let go of memory mutex
pthread_mutex_unlock(&memory_mutex);
gdbstub_send_packet(data, length * 2); gdbstub_send_packet(data, length * 2);
} }
else if(packet[0] == 'M') else if(packet[0] == 'M')
@ -286,19 +279,13 @@ void gdbstub_thread_gdb()
data_start++; data_start++;
data_start++; data_start++;
// Aquire memory mutex
pthread_mutex_lock(&memory_mutex);
for(size_t i = 0; i < length; i++) for(size_t i = 0; i < length; i++)
{ {
uint32_t value; uint32_t value;
sscanf(packet + data_start + i * 2, "%02x", &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); gdbstub_send_packet("OK", 2);
} }
else if(packet[0] == 's') else if(packet[0] == 's')

@ -4,8 +4,209 @@
uint8_t* memory; uint8_t* memory;
pthread_mutex_t memory_mutex; 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() void mem_init()
{ {
memory = malloc(memory_size); memory = malloc(memory_size);
pthread_mutex_init(&memory_mutex, 0); 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;
} }

@ -5,8 +5,14 @@
#include <pthread.h> #include <pthread.h>
extern uint8_t* memory; extern uint8_t* memory;
extern pthread_mutex_t memory_mutex;
void mem_init(); 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 #endif

@ -1,6 +0,0 @@
#ifndef MMU_H
#define MMU_H
#define mmu_translate(vaddr) (vaddr)
#endif
Loading…
Cancel
Save