RISCV Bare Metal

 

main.c 

#include <timer.h>

#include "trap.h"
#include "uart.h"

int main() {
    uart_init();
    print_s("Hello world!\n");
    print_s("Raise exception to enable timer...\n");
    print_s("Back to user mode\n");
    while (1)
        ;
    return 0;
}

trap.c

#include "riscv.h"
#include "uart.h"
#define CLINT_BASE 0x2000000
#define MTIME (volatile unsigned long long int *)(CLINT_BASE + 0xbff8)
#define MTIMECMP (volatile unsigned long long int *)(CLINT_BASE + 0x4000)

int count = 0;

void handle_interrupt(uint64_t mcause) {
    if ((mcause << 1 >> 1) == 0x7) {
        print_s("Timer Interrupt: ");
        print_i(++count);
        print_s("\n");

        *MTIMECMP = *MTIME + 0xfffff * 5;
        if (count == 10) {
            unsigned long long int mie;
            asm volatile("csrr %0, mie" : "=r"(mie));
            mie &= ~(1 << 7);
            asm volatile("csrw mie, %0" : "=r"(mie));
        }
    } else {
        print_s("Unknown interrupt: ");
        print_i(mcause << 1 >> 1);
        print_s("\n");
        while (1)
            ;
    }
}

void handle_exception(uint64_t mcause) {
    unsigned long long int mie;

    if (mcause == 0x8) {
        print_s("process exception \n ");
        *MTIMECMP = *MTIME + 0xfffff * 5;

        asm volatile("csrr %0, mie" : "=r"(mie));
        mie |= (1 << 7);
        asm volatile("csrw mie, %0" : "=r"(mie));
    } else {
        print_s("Unknown exception: ");
        print_i(mcause << 1 >> 1);
        print_s("\n");
        while (1)
            ;
    }
}

void handle_trap() {
    uint64_t mcause, mepc;
    asm volatile("csrr %0, mcause" : "=r"(mcause));
    asm volatile("csrr %0, mepc" : "=r"(mepc));

    if (mcause >> 63) {
        handle_interrupt(mcause);
    } else {
        handle_exception(mcause);
        asm volatile("csrr t0, mepc");
        asm volatile("addi t0, t0, 0x4");
        asm volatile("csrw mepc, t0");
    }
}

uart.c

[root@centos7 src]# cat uart.c 
#include <stdint.h>

#include "uart.h"

static volatile uint8_t *uart;

void uart_init() {
    uart = (uint8_t *)(void *)NS16550A_UART0_CTRL_ADDR;
    uint32_t uart_freq = UART0_CLOCK_FREQ;
    uint32_t baud_rate = UART0_BAUD_RATE;
    uint32_t divisor = uart_freq / (16 * baud_rate);
    uart[UART_LCR] = UART_LCR_DLAB;
    uart[UART_DLL] = divisor & 0xff;
    uart[UART_DLM] = (divisor >> 8) & 0xff;
    uart[UART_LCR] = UART_LCR_PODD | UART_LCR_8BIT;
}

static int ns16550a_putchar(int ch) {
    while ((uart[UART_LSR] & UART_LSR_RI) == 0)
        ;
    return uart[UART_THR] = ch & 0xff;
}

void uart_send(char c) { ns16550a_putchar(c); }

void print_s(const char *s) {
    while (*s != '\0') {
        /* convert newline to carrige return + newline */
        if (*s == '\n') uart_send('\r');
        uart_send(*s++);
    }
}

void print_c(char c) { uart_send(c); }

void print_i(unsigned long int x) {
    if (x < 0) {
        print_c('-');
        x = -x;
    }
    if (x >= 10) print_i(x / 10);
    print_c(x % 10 + '0');
}

void print_h(unsigned long int d) {
    unsigned int n;
    int c;
    for (c = 28; c >= 0; c -= 4) {
        // get highest tetrad
        n = (d >> c) & 0xF;
        // 0-9 => '0'-'9', 10-15 => 'A'-'F'
        n += n > 9 ? 0x37 : 0x30;
        uart_send(n);
    }
}

start.c

[root@centos7 src]# cat  start.c 
#include "main.h"
#include "riscv.h"
#include "stdint.h"
#include "timer.h"

// extern void timervec();
extern void trap_entry();

void start() {
    // set M Previous Privilege mode to Supervisor, for mret.
    unsigned long x = r_mstatus();
    x &= ~MSTATUS_MPP_MASK;
    x |= MSTATUS_MPP_S;
    w_mstatus(x);

    // set M Exception Program Counter to main, for mret.
    // requires gcc -mcmodel=medany
    w_mepc((uint64_t)main);

    // disable paging for now.
    w_satp(0);

    // delegate all interrupts and exceptions to supervisor mode.
    w_medeleg(0xffff);
    w_mideleg(0xffff);

    // setup trap_entry
    w_mtvec((uint64_t)trap_entry);

    // keep each CPU's hartid in its tp register, for cpuid().
    int id = r_mhartid();
    w_tp(id);

    timer_init();

    // switch to supervisor mode and jump to main().
    asm volatile("mret");
}

 

[root@centos7 src]# cat startup.s 
.equ REGBYTES, 8
.equ STACK_SIZE,  ((1 << 12) - 128) 

.section .text.start

.globl _start
_start:
    csrr   t0, mhartid
    lui    t1, 0
    beq    t0, t1, 2f

1:  wfi
    j      1b

2:
    # initialize global pointer
    la gp, _gp
  
    # initialize stack pointer
    la sp, stack_top
    call   start

[root@centos7 src]# 

 

[root@centos7 riscv-bare-metal]# qemu-system-riscv64 -M virt -kernel kernel.img -bios none -serial stdio -display none
Hello world!
Raise exception to enable timer...
Back to user mode
Timer Interrupt: 1
Timer Interrupt: 2
Timer Interrupt: 3
Timer Interrupt: 4
Timer Interrupt: 5
Timer Interrupt: 6
Timer Interrupt: 7
Rqemu-system-riscv64: terminating on signal 2
[root@centos7 riscv-bare-metal]#

 

上一篇:FPGA实现串口功能


下一篇:【面包】STM32学习笔记(二) --- USART 串口通信学习总结