PLIC简介&&cva6之PLIC模块阅读笔记

PLIC简介

PLIC结构

PLIC简介&&cva6之PLIC模块阅读笔记_第1张图片

门户的作用主要是将中断源来的中断电气信号转换为 MSI,然后交由交换矩阵来处理;另外一个作用是当来自某个中断源的中断正在被处理时,阻止接收同一中断源的后续中断。

对某个 HART 来说,如果中断发生,交换矩阵会通知 HART,而这种通知的方式可以有多种实现方式。对于复杂的系统,这种通知本身就可以是 MSI;对于相对简单的系统,这种通知可以是简单的硬连线,直接连接到 HART 内部中断寄存器的等待中断位上。

HART 在收到来自交换矩阵的中断通知后,需要读取对应的读取 / 完成寄存器来确定中断源。读取 / 完成寄存器是一个内存映射寄存器,当其被读取时,会返回中断源ID(Indentififier)。同时,读取寄存器的动作也会被 PLIC 认定为对中断的读取,从而修正 PLIC 中中断等待的状态。

当 HART 结束对中断的处理后,需要将刚才处理完成的中断源 ID 再写入读取 / 完成寄存器。PLIC 在收到这个写入动作后,会修改门户的状态,以允许接收对应中断源的后续中断。

PLIC中断目标

plic中断目标通常是RISC-V架构的一个特定模式下的Hart(Hardware Thread:表示一个硬件线程。利用硬件超线程技术,可以在一个处理器核中实现多份hart。每套线程有自己独立的寄存器组等上下文资源,但是大多数运算资源被所有hart复用)

PLIC中断源

PLIC为每个中断源分配:

  • 闸口(gateway)和IP(中断源的等待标志寄存器)

闸口将不同触发类型的外部中断(如电平触发、边沿触发等)转换成统一的内部中断请求,一次只发送一个请求,闸口启动屏蔽直到中断处理完成。(硬件在闸口发送内部中断请求后会将IP置高)

  • 编号(ID)

从编号1起可以用来连接有效的外部中断源(一源一号),0表示不存在的中断,理论上支持任意多个。

  • 外部中断源优先级

每个中断源可以设置特定的优先级,其寄存器是存储器地址映射的可读可写寄存器。优先级的数字越大,优先级越高,0意味着不可能中断,相当于将中断源屏蔽。硬件实现时可以根据选择的优先级寄存器的有效位来确定可以支持的优先级数目。(每个中断目标可以设置优先级阈值,只有中断源优先级高于此阈值,中断才能被发给中断目标)

  • 中断使能

每个中断源配置一个中断使能寄存器(IE),当其被编程置0,此中断源被中断目标屏蔽,置1则被打开。

PLIC中断处理机制

  • 中断通知

中断源经过闸口发送,IE置为1,优先级大于0
从所有参与仲裁的中断源中选择优先级最高的,若优先级相同则选择编号最小的。
仲裁出的中断源优先级高于中断目标的阈值
发往中断目标的中断通知是一根电平触发的中断线。(若中断目标是M mode,则中断线的值会反应在mip的MEIP域)

  • 中断响应

中断目标收到中断通知后,读取一个PLIC实现的存储器地址映射的可读寄存器,PLIC会返回中断源的ID编号,若为0,则无中断请求。PLIC返回中断ID后,会将对应中断源的IP清0。

  • 中断完成

中断目标完成中断处理操作后,将中断ID写入一个PLIC实现的存储器地址映射的可写寄存器,PLIC接收到之后,将相应中断源的闸口解除屏蔽。

cva6之PLIC模块阅读

rv_plic_gateway.sv

src表示中断源是否有中断请求,当前有30个源;
le为0表示水平触发,1表示边沿触发;
set用于根据中断源设置ip,当前跳过了边沿触发,即src为1且le为0时,将ip设置为1;
claim表示中断是否被响应;
complete表示中断是否完成;
ip表示中断源的等待标志寄存器,中断响应(claim:1)时ip清零;
ia表示中断活跃状态,当中断完成(complete:1)时清零。

当中断源被置1时,相应ip,ia均被置1,此时ip无法接收中断源信号,直到claim被置1,ip清零,但是仍然无法接收中断源信号,当complete被置1,ia清零后,重复前述过程。

rv_plic_target.sv

此模块根据IE&&IP、中断源优先级以及中断阈值来发出中断通知。
此模块实现了两种仲裁算法,在此只介绍SEQUENTIAL这一种:

always_comb begin
    max_prio = threshold + 1'b1; // Priority strictly greater than threshold
    irq_id_next = '0; // default: No Interrupt
    irq_next = 1'b0;
    for (int i = N_SOURCE-1 ; i >= 0 ; i--) begin
      if ((ip[i] & ie[i]) == 1'b1 && prio[i] >= max_prio) begin
        max_prio = prio[i];
        irq_id_next = SRCW'(i+1);
        irq_next = 1'b1;
      end
    end // for i
  end

这段代码为中断仲裁的关键代码,ip[i] & ie[i]) == 1’b1表示第i个中断源的中断被开启且检测到中断信号,用max_prio表示遍历过程中的最高优先级,irq_id_next表示该优先级对应的中断源编号(若中断优先级相同,则选择编号小的),irq_next表示是否有大于阈值的中断。

plic_regmap.sv

这是一个由gen_plic_addrmap.py生成的文件,实现对prio、ie、ip、threshold、cc进行内存映射寄存器的读写,组合逻辑,其写寄存器请求req_i来自总线,resp_o信号将读寄存器的结果返回总线。

plic_top.sv

req_i和resp_o信号的信号注释如下:
Transactions consist of only one phase. The master sets the address, write, write data, and write strobe signals and pulls valid high. Once pulled high, valid must remain high and none of the signals may change. The transaction completes when both valid and ready are high. Valid must not depend on ready. The slave presents the read data and error signals. These signals must be constant while valid and ready are both high.

其中req_i的4位wstrb信号是写入选通信号,当传输的数据位(burst size)小于总线位宽时,可以通过选通信号进行部分写入。
claim_id[i]表示中断目标i的中断源编号;
claim_re[i]表示是否有对中断目标i的中断请求;
若有中断请求且中断源编号不为0,则中断编号发往中断目标且中断目标会响应中断,claim相应位置1。
complete_we[i]表示中断目标i是否要写寄存器,即是否完成中断;
complete_id[i]表示中断目标i完成的中断的编号;
若有中断完成且中断编号不为0,则中断目标会将编号写入寄存器表示中断处理完成,complete相应位置1。

PLIC的主要代码介绍完毕,下面介绍几个用于生成文件的py文件:
这两个py文件主要用到了argparse这个python包,点此进入学习。

reg_rv_plic.py

这个文件用于产生hjson配置文件,包括中断源数量、中断目标数量和中断优先级最大值的设置。
HJSON 是 JSON 格式的扩展,增加了注释,并去掉多余的标识符。)
如果要改变参数设置并重新生成配置文件,可用如下命令:

python3 reg_rv_plic.py -s 30 -t 2 -p 7 rv_plic_reg.tpl.hjson > rv_plic_reg.hjson

其中,s,t,p分别表示中断源数量、中断目标数量和中断优先级最大值。
args.input.read()读入的实际就是rv_plic_reg.tpl.hjson文件的内容,这是一个模板,用于生成rv_plic_reg.hjson。

gen_plic_addrmap.py

这个文件用于生成plic_regmap.sv,可以设置内存映射寄存器的基地址。

plic_base = 0xC000000
priorityBase = plic_base + 0x0
enableBase = plic_base + 0x2000
hartBase = plic_base + 0x200000

生成命令如下:

python3 .\gen_plic_addrmap.py -s 30 -t 2 -p 7 > plic_regmap.sv

s、p、t 的含义与上述相同,注意保持一致。

你可能感兴趣的:(ariane/cva6,硬件工程,fpga开发)