Skip to content

Commit

Permalink
Merge pull request #2 from lupyuen/timer
Browse files Browse the repository at this point in the history
Timer
  • Loading branch information
lupyuen authored Jan 24, 2024
2 parents 35b7ac5 + ec1077b commit 14bb110
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 48 deletions.
70 changes: 53 additions & 17 deletions riscv_cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
#include "iomem.h"
#include "riscv_cpu.h"

void print_console(void *machine0, const char *buf, int len);
#define _info(...) {} ////
// #define _info printf ////

#ifndef MAX_XLEN
#error MAX_XLEN must be defined
Expand All @@ -42,7 +43,7 @@ void print_console(void *machine0, const char *buf, int len);
#error CONFIG_RISCV_MAX_XLEN must be defined
#endif

#define DUMP_INVALID_MEM_ACCESS
// #define DUMP_INVALID_MEM_ACCESS
#define DUMP_MMU_EXCEPTIONS
#define DUMP_INTERRUPTS
#define DUMP_INVALID_CSR
Expand All @@ -52,6 +53,13 @@ void print_console(void *machine0, const char *buf, int len);

#include "riscv_cpu_priv.h"

void print_console(void *machine0, const char *buf, int len); ////
extern uint64_t ecall_addr;
extern uint64_t rdtime_addr;
extern uint64_t dcache_iall_addr;
extern uint64_t sync_s_addr;
extern uint64_t real_time;

#if FLEN > 0
#include "softfp.h"
#endif
Expand Down Expand Up @@ -394,12 +402,12 @@ int target_read_slow(RISCVCPUState *s, mem_uint_t *pval,

// Console Input: BL808_UART_INT_STS (0x30002020) must return UART_INT_STS_URX_END_INT (1 << 1)
case 0x30002020:
puts("read BL808_UART_INT_STS");
_info("read BL808_UART_INT_STS\n");
ret = (1 << 1); break;

// Console Input: BL808_UART_INT_MASK (0x30002024) must NOT return UART_INT_MASK_CR_URX_END_MASK (1 << 1)
case 0x30002024:
puts("read BL808_UART_INT_MASK");
_info("read BL808_UART_INT_MASK\n");
ret = 0; break;

// Console Input: BL808_UART_FIFO_RDATA_OFFSET (0x3000208c) returns the Input Char
Expand Down Expand Up @@ -517,7 +525,7 @@ int target_write_slow(RISCVCPUState *s, target_ulong addr,
}
// Console Input: Clear the interrupt after setting BL808_UART_INT_CLEAR (0x30002028)
case 0x30002028: {
printf("write BL808_UART_INT_CLEAR: 0x%x\n", val);
_info("write BL808_UART_INT_CLEAR: 0x%x\n", val);
void virtio_ack_irq(void *device0);
virtio_ack_irq(NULL);
break;
Expand All @@ -527,6 +535,8 @@ int target_write_slow(RISCVCPUState *s, target_ulong addr,
printf("target_write_slow: invalid physical address 0x");
print_target_ulong(paddr);
printf("\n");
#else
break;
#endif
}
//// End Test
Expand Down Expand Up @@ -1106,10 +1116,17 @@ static void set_priv(RISCVCPUState *s, int priv)
static void raise_exception2(RISCVCPUState *s, uint32_t cause,
target_ulong tval)
{
printf("raise_exception2: cause=%d, tval=%p\n", cause, (void *)tval);////
_info("raise_exception2: cause=%d, tval=%p, pc=%p\n", cause, (void *)tval, s->pc);////
BOOL deleg;
target_ulong causel;


//// Begin Test: Quit if Illegal Instruction, otherwise it will loop forever
if (cause == 2) {
printf("\ntinyemu: Illegal instruction, quitting: pc=%p, instruction=%p\n", s->pc, tval);
exit(1);
}
//// End Test

#if defined(DUMP_EXCEPTIONS) || defined(DUMP_MMU_EXCEPTIONS) || defined(DUMP_INTERRUPTS)
{
int flag;
Expand All @@ -1129,7 +1146,6 @@ static void raise_exception2(RISCVCPUState *s, uint32_t cause,
#ifdef DUMP_EXCEPTIONS
flag = 1;
flag = (cause & CAUSE_INTERRUPT) == 0;
//// Previously: if (cause == CAUSE_SUPERVISOR_ECALL || cause == CAUSE_ILLEGAL_INSTRUCTION)
if (cause == CAUSE_SUPERVISOR_ECALL || cause == CAUSE_USER_ECALL) ////
flag = 0;
#endif
Expand All @@ -1146,12 +1162,38 @@ static void raise_exception2(RISCVCPUState *s, uint32_t cause,
}
#endif

//// Begin Test: Emulate OpenSBI for System Timer
//// Begin Test: Emulate Patched Instructions and OpenSBI for System Timer
if (cause == CAUSE_SUPERVISOR_ECALL) {
puts("TODO: Emulate OpenSBI for System Timer");

if (s->pc == ecall_addr) {
// For OpenSBI Set Timer: Clear the pending timer interrupt bit
// https://github.com/riscv-non-isa/riscv-sbi-doc/blob/v1.0.0/riscv-sbi.adoc#61-function-set-timer-fid-0
_info("Set Timer\n");
_info(" reg %s=%p\n", reg_name[16], s->reg[16]); //// A6 is X16 (fid)
_info(" reg %s=%p\n", reg_name[17], s->reg[17]); //// A7 is X17 (extid)
_info(" reg %s=%p\n", reg_name[10], s->reg[10]); //// A0 is X10 (parm0)
riscv_cpu_reset_mip(s, MIP_STIP);

// If parm0 is not -1, set the System Timer (timecmp)
uint64_t timecmp = s->reg[10]; // A0 is X10 (parm0)
if (timecmp != (uint64_t) -1) {
void set_timecmp(void *machine0, uint64_t timecmp);
set_timecmp(NULL, timecmp);
}
} else if (s->pc == rdtime_addr) {
// For RDTIME: Return the time
// https://five-embeddev.com/riscv-isa-manual/latest/counters.html#zicntr-standard-extension-for-base-counters-and-timers
_info("Get Time\n");
// static uint64_t t = 0;
// s->reg[10] = t++; // Not too much or usleep will hang
s->reg[10] = real_time;
_info(" Return reg %s=%p\n", reg_name[10], s->reg[10]); //// A0 is X10
}

s->pc += 4; // Jump to the next instruction (ret)
return;
}
if (cause == CAUSE_USER_ECALL) { _info("User ECALL: pc=%p\n", s->pc); } ////
//// End Test

if (s->priv <= PRV_S) {
Expand Down Expand Up @@ -1191,17 +1233,11 @@ static void raise_exception2(RISCVCPUState *s, uint32_t cause,
set_priv(s, PRV_M);
s->pc = s->mtvec;
}
//// Begin Test: Quit if cause=2, otherwise it will loop forever
if (cause == 2) { puts("tinyemu: Unknown mcause 2, quitting"); exit(1); }
//// End Test
}

static void raise_exception(RISCVCPUState *s, uint32_t cause)
{
printf("raise_exception: cause=%d\n", cause);////
#ifndef EMSCRIPTEN ////
// printf("raise_exception: sleep\n"); sleep(4);////
#endif //// EMSCRIPTEN
_info("raise_exception: cause=%d\n", cause);////
raise_exception2(s, cause, 0);
}

Expand Down
95 changes: 74 additions & 21 deletions riscv_machine.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
#include "elf.h"
#include "compress.h"

#define _info(...) {} ////
// #define _info printf ////

/* RISCV machine */

typedef struct RISCVMachine {
Expand Down Expand Up @@ -80,6 +83,15 @@ typedef struct RISCVMachine {
#define RTC_FREQ_DIV 16 /* arbitrary, relative to CPU freq to have a
10 MHz frequency */

void set_timecmp(RISCVMachine *machine0, uint64_t timecmp);
void print_console(RISCVMachine *machine0, const char *buf, int len);

uint64_t ecall_addr = 0;
uint64_t rdtime_addr = 0;
uint64_t dcache_iall_addr = 0;
uint64_t sync_s_addr = 0;
uint64_t real_time = 0;

static uint64_t rtc_get_real_time(RISCVMachine *s)
{
struct timespec ts;
Expand Down Expand Up @@ -245,10 +257,10 @@ static void plic_update_mip(RISCVMachine *s)
uint32_t mask;
mask = s->plic_pending_irq & ~s->plic_served_irq;
if (mask) {
printf("plic_update_mip: set_mip, pending=0x%x, served=0x%x\n", s->plic_pending_irq, s->plic_served_irq);////
_info("plic_update_mip: set_mip, pending=0x%x, served=0x%x\n", s->plic_pending_irq, s->plic_served_irq);////
riscv_cpu_set_mip(cpu, MIP_MEIP | MIP_SEIP);
} else {
printf("plic_update_mip: reset_mip, pending=0x%x, served=0x%x\n", s->plic_pending_irq, s->plic_served_irq);////
_info("plic_update_mip: reset_mip, pending=0x%x, served=0x%x\n", s->plic_pending_irq, s->plic_served_irq);////
riscv_cpu_reset_mip(cpu, MIP_MEIP | MIP_SEIP);
}
}
Expand All @@ -258,7 +270,7 @@ static void plic_update_mip(RISCVMachine *s)

static uint32_t plic_read(void *opaque, uint32_t offset, int size_log2)
{
printf("plic_read: offset=0x%x\n", offset);////
_info("plic_read: offset=0x%x\n", offset);////
RISCVMachine *s = opaque;
uint32_t val, mask;
int i;
Expand Down Expand Up @@ -288,7 +300,7 @@ static uint32_t plic_read(void *opaque, uint32_t offset, int size_log2)
static void plic_write(void *opaque, uint32_t offset, uint32_t val,
int size_log2)
{
printf("plic_write: offset=0x%x, val=0x%x\n", offset, val);////
_info("plic_write: offset=0x%x, val=0x%x\n", offset, val);////
RISCVMachine *s = opaque;

assert(size_log2 == 2);
Expand All @@ -307,7 +319,7 @@ static void plic_write(void *opaque, uint32_t offset, uint32_t val,

static void plic_set_irq(void *opaque, int irq_num, int state)
{
printf("plic_set_irq: irq_num=%d, state=%d\n", irq_num, state);////
_info("plic_set_irq: irq_num=%d, state=%d\n", irq_num, state);////
RISCVMachine *s = opaque;
uint32_t mask;

Expand Down Expand Up @@ -902,24 +914,45 @@ static void copy_bios(RISCVMachine *s, const uint8_t *buf, int buf_len,
q[pc++] = 0x34129073; // csrw mepc, t0
q[pc++] = 0x30200073; // mret

// Sentinel to catch overrun
q[pc++] = 0x12345678;

// Machine Mode ECALL: Always return
// *(uint32_t *)(ram_ptr + 0x0) = 0x30200073; // mret

// Patch the RDTTIME (Read System Timer) with NOP for now. We will support later.
// Patch the Unknown Instructions to become ECALL and handle later
uint8_t *kernel_ptr = get_ram_ptr(s, RAM_BASE_ADDR, TRUE);
for (int i = 0; i < 0x10000; i++) {
// Patch RDTTIME to NOP
// ECALL Instruction
// 00000073 ecall
const uint8_t ecall[] = { 0x73, 0x00, 0x00, 0x00 };
if (memcmp(&kernel_ptr[i], ecall, sizeof(ecall)) == 0) {
ecall_addr = RAM_BASE_ADDR + i;
printf("Found ECALL (Start System Timer) at %p\n", ecall_addr);
}

// Patch RDTIME to become ECALL
// (Read System Time)
// c0102573 rdtime a0
const uint8_t search[] = { 0x73, 0x25, 0x10, 0xc0 };
// 00010001 nop ; nop
const uint8_t replace[] = { 0x01, 0x00, 0x01, 0x00 };
const uint8_t rdtime[] = { 0x73, 0x25, 0x10, 0xc0 };
if (memcmp(&kernel_ptr[i], rdtime, sizeof(rdtime)) == 0) {
memcpy(&kernel_ptr[i], ecall, sizeof(ecall));
rdtime_addr = RAM_BASE_ADDR + i;
printf("Patched RDTIME (Read System Time) at %p\n", rdtime_addr);
}

if (memcmp(&kernel_ptr[i], search, sizeof(search)) == 0) {
memcpy(&kernel_ptr[i], replace, sizeof(replace));
printf("Patched RDTTIME (Read System Timer) at %p\n", RAM_BASE_ADDR + i);
// Patch DCACHE.IALL to become ECALL
// (Invalidate all Page Table Entries in the D-Cache)
// 0020000b DCACHE.IALL
const uint8_t dcache_iall[] = { 0x0b, 0x00, 0x20, 0x00 };
if (memcmp(&kernel_ptr[i], dcache_iall, sizeof(dcache_iall)) == 0) {
memcpy(&kernel_ptr[i], ecall, sizeof(ecall));
dcache_iall_addr = RAM_BASE_ADDR + i;
printf("Patched DCACHE.IALL (Invalidate all Page Table Entries in the D-Cache) at %p\n", dcache_iall_addr);
}

// Patch SYNC.S to become ECALL
// (Ensure that all Cache Operations are completed)
// 0190000b SYNC.S
const uint8_t sync_s[] = { 0x0b, 0x00, 0x90, 0x01 };
if (memcmp(&kernel_ptr[i], sync_s, sizeof(sync_s)) == 0) {
memcpy(&kernel_ptr[i], ecall, sizeof(ecall));
sync_s_addr = RAM_BASE_ADDR + i;
printf("Patched SYNC.S (Ensure that all Cache Operations are completed) at %p\n", sync_s_addr);
}
}
//// End Test
Expand Down Expand Up @@ -1013,7 +1046,6 @@ static VirtMachine *riscv_machine_init(const VirtMachineParams *p)
if (p->console) {
//// Begin Test: Save the Console
const char *msg = "TinyEMU Emulator for Ox64 BL808 RISC-V SBC\r\n";
void print_console(RISCVMachine *machine0, const char *buf, int len);
print_console(s, msg, strlen(msg));
//// End Test
vbus->irq = &s->plic_irq[irq_num];
Expand Down Expand Up @@ -1102,6 +1134,7 @@ static VirtMachine *riscv_machine_init(const VirtMachineParams *p)
p->cmdline,
p->files[VM_FILE_INITRD].buf, p->files[VM_FILE_INITRD].len);

set_timecmp(s, 0); //// Init the System Timer
return (VirtMachine *)s;
}

Expand Down Expand Up @@ -1136,6 +1169,16 @@ static int riscv_machine_get_sleep_duration(VirtMachine *s1, int delay)
}
if (!riscv_cpu_get_power_down(s))
delay = 0;

//// Begin Test: Trigger the Supervisor-Mode Timer Interrupt
real_time = rtc_get_time(m);
if (!(riscv_cpu_get_mip(s) & MIP_STIP)) {
const int64_t delay2 = m->timecmp - rtc_get_time(m);
if (delay2 <= 0) {
riscv_cpu_set_mip(s, MIP_STIP);
}
}
//// End Test
return delay;
}

Expand Down Expand Up @@ -1180,7 +1223,17 @@ const VirtMachineClass riscv_machine_class = {
riscv_vm_send_key_event,
};

//// Begin Test: Print to Console
//// Begin Test
// Set Timer
void set_timecmp(RISCVMachine *machine0, uint64_t timecmp) {
static RISCVMachine *machine = NULL;
if (machine0 != NULL) { machine = machine0; return; }
if (machine == NULL) { puts("set_timecmp: machine is null"); return; }
machine->timecmp = timecmp;
_info("set_timecmp: timecmp=%p\n", timecmp);
}

// Print to Console
void print_console(RISCVMachine *machine0, const char *buf, int len) {
static RISCVMachine *machine = NULL;
if (machine0 != NULL) { machine = machine0; }
Expand Down
21 changes: 11 additions & 10 deletions virtio.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
#include "list.h"
#include "virtio.h"

#define _info(...) {} ////
// #define _info printf ////

#define DEBUG_VIRTIO ////

/* MMIO addresses - from the Linux kernel */
Expand Down Expand Up @@ -1300,7 +1303,7 @@ BOOL virtio_console_can_write_data(VIRTIODevice *s)
QueueState *qs = &s->queue[0];
uint16_t avail_idx;

//if (!qs->ready) { printf("virtio_console_can_write_data: ready=%d\n", qs->ready); }////
//if (!qs->ready) { _info("virtio_console_can_write_data: ready=%d\n", qs->ready); }////
if (!qs->ready)
return FALSE;
avail_idx = virtio_read16(s, qs->avail_addr + 2);
Expand Down Expand Up @@ -1337,7 +1340,7 @@ int virtio_console_write_data(VIRTIODevice *s, const uint8_t *buf, int buf_len)
{
//// To handle a keypress, we trigger the UART3 Interrupt.
//// Pass the keypress to VM Guest
printf("[%c]\n", buf[0]); ////
_info("[%c]\n", buf[0]); ////
set_input(buf[0]);
s->int_status |= 1;
set_irq(s->irq, 1);
Expand All @@ -1348,19 +1351,19 @@ int virtio_console_write_data(VIRTIODevice *s, const uint8_t *buf, int buf_len)
int desc_idx;
uint16_t avail_idx;

printf("virtio_console_write_data: ready=%d\n", qs->ready);////
_info("virtio_console_write_data: ready=%d\n", qs->ready);////
if (!qs->ready)
return 0;
avail_idx = virtio_read16(s, qs->avail_addr + 2);
printf("virtio_console_write_data: last_avail_idx=%d, avail_idx=%d\n", qs->last_avail_idx, avail_idx);////
_info("virtio_console_write_data: last_avail_idx=%d, avail_idx=%d\n", qs->last_avail_idx, avail_idx);////
if (qs->last_avail_idx == avail_idx)
return 0;
desc_idx = virtio_read16(s, qs->avail_addr + 4 +
(qs->last_avail_idx & (qs->num - 1)) * 2);
memcpy_to_queue(s, queue_idx, desc_idx, 0, buf, buf_len);
virtio_consume_desc(s, queue_idx, desc_idx, buf_len);
qs->last_avail_idx++;
printf("virtio_console_write_data: buf[0]=%c, buf_len=%d\n", buf[0], buf_len);////
_info("virtio_console_write_data: buf[0]=%c, buf_len=%d\n", buf[0], buf_len);////
return buf_len;
#endif // NOTUSED
}
Expand Down Expand Up @@ -2687,11 +2690,9 @@ void virtio_ack_irq(VIRTIODevice *device0) {
if (device0 != NULL) { device = device0; return; }
if (device == NULL) { puts("virtio_ack_irq: Missing device"); }

puts("virtio_ack_irq");
// device->int_status &= ~val;
// if (device->int_status == 0) {
set_irq(device->irq, 0);
// }
// Trigger the Device IRQ
_info("virtio_ack_irq");
set_irq(device->irq, 0);
}

//// Remember and return the Input Char
Expand Down

0 comments on commit 14bb110

Please sign in to comment.