Add wfi timer loop example, and add mtime/mtimecmp to non-debug testbench

This commit is contained in:
Luke Wren 2021-11-06 09:59:27 +00:00
parent cc6a6c09ba
commit c1f17b0b23
3 changed files with 97 additions and 3 deletions

View File

@ -15,9 +15,13 @@ uint8_t mem[MEM_SIZE];
static const unsigned int IO_BASE = 0x80000000;
enum {
IO_PRINT_CHAR = 0,
IO_PRINT_U32 = 4,
IO_EXIT = 8
IO_PRINT_CHAR = 0x000,
IO_PRINT_U32 = 0x004,
IO_EXIT = 0x008,
IO_MTIME = 0x100,
IO_MTIMEH = 0x104,
IO_MTIMECMP = 0x108,
IO_MTIMECMPH = 0x10c
};
const char *help_str =
@ -115,6 +119,9 @@ int main(int argc, char **argv) {
top.p_ahblm__hready.set<bool>(true);
#endif
uint64_t mtime = 0;
uint64_t mtimecmp = 0;
// Reset + initial clock pulse
top.step();
top.p_clk.set<bool>(true);
@ -132,6 +139,11 @@ int main(int argc, char **argv) {
top.p_clk.set<bool>(true);
top.step();
top.step(); // workaround for github.com/YosysHQ/yosys/issues/2780
// Default update logic for mtime, mtimecmp
++mtime;
top.p_timer__irq.set<bool>(mtime >= mtimecmp);
// Handle current data phase, then move current address phase to data phase
uint32_t rdata = 0;
if (bus_trans && bus_write) {
@ -158,6 +170,18 @@ int main(int argc, char **argv) {
printf("Ran for %ld cycles\n", cycle + 1);
break;
}
else if (bus_addr == IO_BASE + IO_MTIME) {
mtime = (mtime & 0xffffffff00000000u) | wdata;
}
else if (bus_addr == IO_BASE + IO_MTIMEH) {
mtime = (mtime & 0x00000000ffffffffu) | ((uint64_t)wdata << 32);
}
else if (bus_addr == IO_BASE + IO_MTIMECMP) {
mtimecmp = (mtimecmp & 0xffffffff00000000u) | wdata;
}
else if (bus_addr == IO_BASE + IO_MTIMECMPH) {
mtimecmp = (mtimecmp & 0x00000000ffffffffu) | ((uint64_t)wdata << 32);
}
}
else if (bus_trans && !bus_write) {
bus_addr &= ~0x3u;
@ -168,6 +192,18 @@ int main(int argc, char **argv) {
mem[bus_addr + 2] << 16 |
mem[bus_addr + 3] << 24;
}
else if (bus_addr == IO_BASE + IO_MTIME) {
rdata = mtime;
}
else if (bus_addr == IO_BASE + IO_MTIMEH) {
rdata = mtime >> 32;
}
else if (bus_addr == IO_BASE + IO_MTIMECMP) {
rdata = mtimecmp;
}
else if (bus_addr == IO_BASE + IO_MTIMECMPH) {
rdata = mtimecmp >> 32;
}
}
#ifdef DUAL_PORT
top.p_d__hrdata.set<uint32_t>(rdata);

View File

@ -0,0 +1,5 @@
SRCS := ../common/init.S main.c
APP := wfi_loop
CCFLAGS = -march=rv32imc -Os
include ../common/src_only_app.mk

53
test/sim/wfi_loop/main.c Normal file
View File

@ -0,0 +1,53 @@
#include "tb_cxxrtl_io.h"
#define TIMER_INTERVAL 1000
#define MAX_IRQ_COUNT 10
#define __wfi() asm volatile ("wfi")
#define __compiler_mb() asm volatile ("" ::: "memory")
typedef struct {
volatile uint32_t mtime;
volatile uint32_t mtimeh;
volatile uint32_t mtimecmp;
volatile uint32_t mtimecmph;
} timer_hw;
timer_hw *const timer = (timer_hw*)(IO_BASE + 0x100);
int irq_count;
void __attribute__((interrupt)) isr_machine_timer() {
__compiler_mb();
++irq_count;
__compiler_mb();
tb_printf("IRQ %d\n", irq_count);
// Disable timer IRQ via MTIE, or set the next timer IRQ
if (irq_count >= MAX_IRQ_COUNT)
asm ("csrc mie, %0" :: "r" (1u << 7));
else
timer->mtimecmp = timer->mtime + TIMER_INTERVAL;
}
int main() {
irq_count = 0;
__compiler_mb();
// Per-IRQ enable for timer IRQ
asm ("csrs mie, %0" :: "r" (1u << 7));
tb_puts("Enabling IRQS...\n");
// Global IRQ enable. Timer IRQ will fire immediately.
asm ("csrsi mstatus, 0x8");
// Count the number of sleep loop iterations to make sure the wfi waits
int wait_spin_count;
while (irq_count < MAX_IRQ_COUNT) {
++wait_spin_count;
__wfi();
__compiler_mb();
}
tb_printf("Took %d IRQs, span %d times\n", irq_count, wait_spin_count);
return irq_count != wait_spin_count + 1;
}