RISCV基础开发(十九)

gewenbin
gewenbin
gewenbin
188
文章
15
评论
2021年8月22日11:30:58 评论 1,297

QEMU OpenSBI 裸机开发之定时器中断

1. 中断处理准备

在s模式下的中断设置和处理逻辑和原来的基本一致,首先在entry.S中添加中断处理汇编入口,如下所示:

#
# supervisor trap entry.
#
.globl supervisor_trap_entry
.align 4
supervisor_trap_entry:

    # sscratch holds smode trap stack address,
    # swap current sp and smode trap stack address
    csrrw sp, sscratch, sp

    // call the C trap handler in trap.c
    call supervisor_trap

    # restore old sp value
    csrrw sp, sscratch, sp

    sret

这里就是设置好中断栈,然后跳转到c函数处理,supervisor_trap和之前的也一样,定义在trap.c中,如下所示。

#include "csr.h"
#include "sbi.h"
#include "timer.h"
#include "printf.h"

#define S_SOFT_INT      (1)
#define M_SOFT_INT      (3)
#define S_TIMER_INT     (5)
#define M_TIMER_INT     (7)
#define S_EXT_INT       (9)
#define M_EXT_INT       (11)

#define INSTRUCTION_ADDR_MISALIGNED     (0)
#define INSTRUCTION_ACCESS_FAULT        (1)
#define ILLEGAL_INSTRUCTION             (2)
#define BREAK_POINT                     (3)
#define LOAD_ADDR_MISALIGNED            (4)
#define LOAD_ACCESS_FAULT               (5)
#define STORE_ADDR_MISALIGNED           (6)
#define STORE_ACCESS_FAULT              (7)
#define ECALL_FROM_UMODE                (8)
#define ECALL_FROM_SMODE                (9)
#define ECALL_FROM_MMODE                (11)
#define INSTRUCTION_PAGE_FAULT          (12)
#define LOAD_PAGE_FAULT                 (13)
#define STORE_PAGE_FAULT                (15)

static char *interrupt_cause[] = {
    "Reserved",
    "Supervisor software interrupt",
    "Reserved",
    "Machine software interrupt",
    "Reserved",
    "Supervisor timer interrupt",
    "Reserved",
    "Machine timer interrupt",
    "Reserved",
    "Supervisor external interrupt",
    "Reserved",
    "Machine external interrupt",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved"
};

static char *exception_cause[] = {
    "Instruction address misaligned",
    "Instruction access fault",
    "Illegal instruction",
    "Breakpoint",
    "Load address misaligned",
    "Load access fault",
    "Store/AMO address misaligned",
    "Store/AMO access fault",
    "Environment call from U-mode",
    "Environment call from S-mode",
    "Reserved",
    "Environment call from M-mode",
    "Instruction page fault",
    "Load page fault",
    "Reserved",
    "Store/AMO page fault"
};

void supervisor_trap(void)
{ 
    unsigned long tval = stval_get();
    unsigned long sepc = sepc_get();
    unsigned long cause = scause_get();

    int is_int = (cause & (1l << 63l)) ? 1 : 0;
    int scode = cause & 0xff;

    if (scode >= 16) {
        printf("%s : %s.\r\n", is_int ? "Interrupt" : "Exception", "Unknown code");
        return;
    }

    if (is_int) {
        printf("Interrupt : %s.\r\n", interrupt_cause[scode]); 
        switch (scode) {
        case S_SOFT_INT:
            // acknowledge the software interrupt by clearing
            // the SSIP bit in sip.
            sip_set(sip_get() & (~SIP_SSIP));
            break;
        case S_TIMER_INT:
            // acknowledge the timer interrupt by set new val.
            sbi_set_timer(mtime_get() + TIMER_CLK_RATE);
            break;
        }
    } else {
        printf("Exception : %s.\r\n", exception_cause[scode]); 
        switch (scode) {
        case ILLEGAL_INSTRUCTION:
            printf("tval = %p\r\n", tval);
            printf("sepc = %p\r\n", sepc);
            break;
        }

        sepc_set(sepc + 4);
    }

    return;
}

在使用中断之前需要设置好中断相关寄存器,这是在start.c中处理的,逻辑和原来的基本一样,如下所示。

static void supervisor_trap_init(void)
{
    //this stack is used for supervisor_trap_entry in entry.S
    sscratch_set((unsigned long)(__stack_start + 4096 * 2));

    // set the supervisor trap handler.
    stvec_set((unsigned long)supervisor_trap_entry);

    // enable supervisor interrupts.
    sstatus_set(sstatus_get() | SSTATUS_SIE);

    // enable supervisor timer and soft interrupts.
    sie_set(sie_get() | SIE_STIE | SIE_SSIE);
}

2. 设置定时器

在start.c中,通过SBI接口来设置定时器比较器的值,如下所示。

void start(void)
{
    printf("%s %d.\r\n", __func__, __LINE__);

    supervisor_trap_init();

    sbi_set_timer(mtime_get() + TIMER_CLK_RATE);
}

这里我们读取定时器的当前值用了一个新的接口mtime_get,定时器的值可以通过MMIO映射的寄存器来访问,也可以通过csr指令来访问,用csr指令访问的汇编代码如下。

static inline unsigned long mtime_get(void)
{
    unsigned long val;
    asm volatile("csrr %0, time" : "=r" (val) );
    return val;
}

sbi_set_timer这个函数的参数是要设置的比较器新值,所以需要将当前值先读取出来然后加上一个间隔值,这里加上的是定时器工作频率值,也就是设置1s产生1个中断。

在定时器中断处理中我们需要再次调用sbi_set_timer来设置新的值,这样定时器就可以每隔1s产生一个中断,如下所示。

case S_TIMER_INT:
    // acknowledge the timer interrupt by set new val.
    sbi_set_timer(mtime_get() + TIMER_CLK_RATE);
    break;

在RISCV中,定时器的中断是通过设置新的值来清除的,如果不想下次产生中断,可以设置一个比当前定时器值小的值来实现。

3. 测试

在命令行执行“make qemu”,可以每隔1s打印一条s模式定时器中断信息,如下所示。

gewenbin@gewenbin-virtual-machine:~/Desktop/qemu_test/lesson14$ make qemu
riscv64-unknown-elf-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie   -c -o trap.o trap.c
riscv64-unknown-elf-ld -z max-page-size=4096 -T kernel.ld -o kernelimage entry.o start.o printf.o trap.o 
riscv64-unknown-elf-objdump -S kernelimage > kernel.asm
riscv64-unknown-elf-objdump -t kernelimage | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$/d' > kernel.sym
qemu-system-riscv64 -machine virt -bios fw_jump.elf -kernel kernelimage -m 128M -smp 1 -nographic

OpenSBI v0.8-81-g7dcb1e1
   ____                    _____ ____ _____
  / __ \                  / ____|  _ \_   _|
 | |  | |_ __   ___ _ __ | (___ | |_) || |
 | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
 | |__| | |_) |  __/ | | |____) | |_) || |_
  \____/| .__/ \___|_| |_|_____/|____/_____|
        | |
        |_|

Platform Name             : riscv-virtio,qemu
Platform Features         : timer,mfdeleg
Platform HART Count       : 1
Firmware Base             : 0x80000000
Firmware Size             : 112 KB
Runtime SBI Version       : 0.2

Domain0 Name              : root
Domain0 Boot HART         : 0
Domain0 HARTs             : 0*
Domain0 Region00          : 0x0000000080000000-0x000000008001ffff ()
Domain0 Region01          : 0x0000000000000000-0xffffffffffffffff (R,W,X)
Domain0 Next Address      : 0x0000000080200000
Domain0 Next Arg1         : 0x0000000082200000
Domain0 Next Mode         : S-mode
Domain0 SysReset          : yes

Boot HART ID              : 0
Boot HART Domain          : root
Boot HART ISA             : rv64imafdcsu
Boot HART Features        : scounteren,mcounteren,time
Boot HART PMP Count       : 16
Boot HART PMP Granularity : 4
Boot HART PMP Address Bits: 54
Boot HART MHPM Count      : 0
Boot HART MHPM Count      : 0
Boot HART MIDELEG         : 0x0000000000000222
Boot HART MEDELEG         : 0x000000000000b109
start 29.
Interrupt : Supervisor timer interrupt.
Interrupt : Supervisor timer interrupt.
Interrupt : Supervisor timer interrupt.
Interrupt : Supervisor timer interrupt.
Interrupt : Supervisor timer interrupt.

4. 工程源码

工程源码:链接:https://pan.baidu.com/s/1TnTYr7mywdKj5bxpdmWnyA,提取码:q772,见lesson14。

gewenbin
  • 本文由 发表于 2021年8月22日11:30:58
  • 转载请务必保留本文链接:http://www.databusworld.cn/10557.html
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: