Rocket-Chip-PLIC

这次我们介绍的功能是PLIC中断。

首先我们先看一下指令集《The RISC-V Instruction Set Manual Volume II: Privileged Architecture Privileged Architecture Version 1.10》是如何定义的。
文档链接:https://riscv.org/specifications/privileged-isa/
在这里插入图片描述
在这里插入图片描述
PLIC是全局的中断单元,用于I/O设备,入口可以很多个,但上报给CORE的只有一个,也就是多个中断来临时,是有优先级限制的。上图的红色箭头是一些使能位置,后面我会一个个进行说明。

协议中的7.5节规定了ID 0是没有中断的含义,且中断ID值从1开始,当多个优先级相同的中断来临时,ID越小的优先级越高,关于优先级的说明后面会详细分析。

更多关于PLIC的协议内容,大家可以直接查看指令集,我就不在这里说了。

现在我们参考SiFive E3的使用文档来使用rocket-chip的PLIC模块。
首先先看一下rocket-chip的PLIC模块的物理地址,如下图。
在这里插入图片描述
可以看出是物理地址是对得上的,那我们直接看E3怎么用就是了。
下面看的是E3关于PLIC的寄存器内容。

在这里插入图片描述
具体的寄存器说明大家看文档吧。需要注意的是,pending相关的寄存器是只读的,priorities & enables相关的寄存器复位后为x值,priority thresholds寄存器 为零时允许所有具有非零优先级的中断,而值为7则屏蔽所有中断。claim process & completion寄存器是同一个寄存器。读取0表示没有中断悬挂。非零就说明有中断,且记录当前中断产生中优先级最高的中断ID。对该寄存器写入当前产生中断中最高的中断ID,则说明系统已知道该中断发生,且已处理或丢弃该中断。

接下来介绍的是与中断相关的CSR寄存器。
在这里插入图片描述
mie CSR寄存器,用于管理各种中断的使能,mip CSR寄存器用于管理各种中断悬挂的情况,mstatus CSR寄存器是中断的总开关,也记录中断来临时系统的特权模式,mcause CSR寄存器是记录异常和中断的内容,还有一个是mtvec CSR寄存器,这里没有贴出来,是用于设置中断处理函数的入口,如果看过我博客的内容,这个入口就是trap_entry函数的起始地址,是在底层汇编*.S中进行配置的。
https://blog.csdn.net/a_weiming/article/details/89006615

从mie中看到,PLIC中断模块使能是MEIE bit,即使PLIC模块内部有很多中断源,但从CORE来看也是只有一个入口。PLIC模块中断悬挂可以查看mip的MEIP bit。mstatus中的MIE是整个CPU的中断总开关,不开的话,全部中断都不会上报,这个开关是最后开的。

看完一些说明内容,下面我们将介绍PLIC的使用流程。

PLIC中断的优先级,PLIC优先级符合以下规律。

  1. 相同优先级的情况下,ID越小优先级越高,即ID1和ID15的中断源优先级均为7,中断同时来临时,先响应ID1中断。
  2. 不同优先级的情况下,按优先级来处理,优先级配置为0~7,即ID1中断源优先级为1,ID15中断源优先级为4,两中断同时来时,先响应ID15中断。
  3. 中断源的优先级要高于priority thresholds设置的值,不然该中断不会上报。

PLIC中断的使用开关。

  1. 各外设模块的中断使能开关。
  2. PLIC总的阈值寄存器priority thresholds可以配置为0,但各个ID中断源的优先级(priorities)必须配置优先级大于1,不然不会上报中断,理由是:priority thresholds寄存器为零时允许所有具有非零优先级的中断上报,如果中断源的优先级为0,就不会上报,所以优先级是第一季使能开关,对应下图Priority的红色箭头。
  3. PLIC的enables寄存器,使能各ID中断源,允许中断进入PLIC模块,对应下图IE的红色箭头。
  4. PLIC各ID中断源优先级高于priority
    thresholds的值,允许中断传到CORE中,对应下图Threshold的箭头。
  5. 出了PLIC后是CORE里面的CSR寄存器,首先是mie寄存器,使能MEIE bit,允许PLIC上报模块中断。
  6. 最后是mstatus中的MIE bit,允许CORE产生中断,对应下图最右边的箭头。
    在这里插入图片描述

PLIC中断配置的建议。

  1. 复位各ID中断源优先级,复位各ID中源使能信号。
  2. 配置PLIC模块的优先级阈值。
  3. 配置各ID中断源的优先级。
  4. 配置各ID中断源的使能。
  5. 配置mie.MEIE使能。
  6. 配置mstatus.MIE使能。
  7. 等待中断。
  8. 读取PLIC当前中断中最有优先级的ID源。
  9. 进入中断处理函数,关闭外设模块中断信号。
  10. 中断函数处理完后,请出PLIC中断源的ID。

PLIC中断的特殊说明。

  1. 中断源可以使电平,也可以是脉冲,只要hold timing符合要求即刻。
  2. 中断源ID 0虽然不产生中断,但它对应的寄存器位置是存在的,所以配置时要注意。
  3. 当读了claim process & completion寄存器后,pending对应位置的位会被拉低,也就是说PLIC认为你读claim process & completion时已经知道该中断源被悬挂过。

下面看的是我写的测试代码。

#include "encoding.h"

#define U32         *(volatile unsigned int *)
#define DEBUG_SIG   0x70000000
#define DEBUG_VAL   0x70000004
#define PLIC_BASE   0x0C000000

void handle_trap();
void csr_cfg();
void PLIC_init_cfg(unsigned int num, unsigned int threshold);
void PLIC_id_clr(unsigned int id);
void PLIC_sourceX_cfg(unsigned int num, unsigned int priority, unsigned int enable);
unsigned int PLIC_id_read();
unsigned int PLIC_read_pending();

//--------------------------------------------------------------------------
// handle_trap function

void handle_trap()
{
    unsigned int id_temp;

    //enter handle_trap()
    U32(DEBUG_SIG) = 0x2;
    //print PLIC interrupt id
    id_temp = PLIC_id_read(); 
    U32(DEBUG_VAL) = id_temp;
    //clear the PLIC interrupt id
    PLIC_id_clr(id_temp);
    //if PLIC pending is not 0x0, some other interrupts is pending
    while(PLIC_read_pending() != 0x0)
    {
        //print PLIC interrupt id
        id_temp = PLIC_id_read(); 
        U32(DEBUG_VAL) = id_temp;
        //clear the PLIC interrupt id
        PLIC_id_clr(id_temp);
    }
    U32(DEBUG_SIG) = 0xFF;
}

//--------------------------------------------------------------------------
// CSR interrupt configuration function

void csr_cfg()
{
    unsigned int csr_tmp;

    //mie.MEIE
    csr_tmp = read_csr(mie);
    U32(DEBUG_VAL) = csr_tmp;
    //write_csr(mie,0x0);
    write_csr(mie,(csr_tmp | 0xFFFF0888));

    //mstatus.MIE
    csr_tmp = read_csr(mstatus);
    U32(DEBUG_VAL) = csr_tmp;
    //write_csr(mstatus,0x0);
    write_csr(mstatus,(csr_tmp | 0x8));
}

//--------------------------------------------------------------------------
// PLIC initial configuration function

void PLIC_init_cfg(unsigned int num, unsigned int threshold)
{
    unsigned int i;

    //source x priority
    for(i=1; i<=num; i++)
    {
        U32(PLIC_BASE + 0x0000 + 4*i) = 0x0;
    }

    //PLIC interrupt enable0
    U32(PLIC_BASE + 0x2000) = 0x0;
    //PLIC interrupt enable1
    if(num >= 32)
    {
        U32(PLIC_BASE + 0x2004) = 0x0;
    }

    //set PLIC interrupt threshold
    U32(PLIC_BASE + 0x200000) = threshold;
}

//--------------------------------------------------------------------------
// PLIC ID clear function

unsigned int PLIC_id_read()
{
    //read PIIL interrupt id
    unsigned int id;

    id = U32(PLIC_BASE + 0x200004);
    return id;
}

void PLIC_id_clr(unsigned int id)
{
    //clear PIIL interrupt id
    U32(PLIC_BASE + 0x200004) = id;
}

//--------------------------------------------------------------------------
// PLIC read pending function

unsigned int PLIC_read_pending()
{
    //read PIIL interrupt pending
    unsigned int pending;

    pending = U32(PLIC_BASE + 0x1000);
    return pending;
}

//--------------------------------------------------------------------------
// PLIC sourceX configuration function

void PLIC_sourceX_cfg(unsigned int num, unsigned int priority, unsigned int enable)
{
    //set source X priority
    U32(PLIC_BASE + 0x0000 + 4*num) = priority;
    //set source X enable
    U32(PLIC_BASE + 0x2000) = U32(PLIC_BASE + 0x2000) | (enable<<num);
}

//--------------------------------------------------------------------------
// Main

void main()
{
    PLIC_init_cfg(0x2,0x1);
    PLIC_sourceX_cfg(0x1,0x2,0x1);
    PLIC_sourceX_cfg(0x2,0x3,0x1);
    csr_cfg();
    U32(DEBUG_SIG) = 0x1;
    while(1) {asm volatile ("wfi");}
}

接下来就是仿真的波形。
在这里插入图片描述
从上图中可以看到,是同时将PLIC ID1中断源和ID2中断源拉高的,结合上面的C语言代码可以知道,ID2中断源的优先级更高的,所以先处理ID2中断。在中断函数中不断判断pending的值,只要有悬挂的就一并处理了,所以看到蓝色箭头和绿色箭头是相继被拉低的。拉低的动作是通过axi4协议的mmio端口完成的。当中断来临的时候mcasue是会变化的,具体的变化值可以参考SiFive E3的文档。
在这里插入图片描述
放大波形看一下,可以看到io_pc在中断来临的时候是变为0x8000_0005C,也就是我们设定的trap_enrty函数入口,在trap_enrty函数中再跳到handle_trap函数中。

中断进入和退出的说明可以参考以下说明。
在这里插入图片描述
到这里,关于PLIC的内容已经介绍完毕了,如果觉得还是有用的那就给个赞吧,谢谢。

  • 9
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值