RISC-V MCU中文社区

【分享】 蜂鸟E203内核中断管理模块sirv_plic_man代码分析

发表于 开源蜂鸟E203 2023-05-26 19:29:16
0
1208
2

报名编号:CICC1699

称团队名:到底叫啥队

所选杯赛:芯来RISC-V杯

指导教师:王雪岩

开发板:MCU200T

这段代码是一个 System-level Interrupt Request (PLIC) 控制器的 Verilog HDL 模块,它用于管理来自多个源的中断请求并将它们分派给处理器的中断请求线。

PLIC_PRIO_WIDTH:中断优先级的位宽。

PLIC_IRQ_NUM:可处理的最大中断请求数目。

PLIC_IRQ_NUM_LOG2:2 的对数,表示中断请求数目,例如,当 IRQ_NUM 为 32 时,此参数为 5。

PLIC_ICB_RSP_FLOP:是否将 ICB 响应通道加入时序流水线。

PLIC_IRQ_I_FLOP:是否将输入的中断请求信号加入时序流水线。

PLIC_IRQ_O_FLOP:是否将输出的中断请求信号加入时序流水线。


模块的输入包括时钟和复位信号,还有用于与总线交互的信号(例如 icb_cmd_valid、icb_cmd_addr、icb_cmd_read 等)。模块的输出包括与总线交互的信号(例如 icb_cmd_ready、icb_rsp_valid 等)以及中断请求信号(plic_irq_o)。模块还包括多个中间变量,用于辅助处理中断请求,例如,irq_pend_set、irq_pend_clr 等用于设置和清除中断挂起状态,irq_thod_nxt、irq_prio_nxt 等用于处理中断请求的优先级等

模块的输入和输出为:

clk:时钟信号。

rst_n: active-low复位信号。

icb_cmd_valid:是否有有效的ICB命令。

icb_cmd_ready:模块是否准备好接收ICB命令。

icb_cmd_addr: ICB命令地址。

icb_cmd_read: ICB命令是读还是写。

icb_cmd_wdata: ICB命令写数据。

icb_rsp_valid:是否有有效的ICB响应。

icb_rsp_ready:处理器是否准备好接收ICB响应。

icb_rsp_rdata: ICB命令读取的数据。

plic_irq_i:源的输入中断。

plic_irq_o:到核心目标的输出中断。


然后,该模块包含几条用于内部信号处理的线路,例如限制输入中断,设置和清除挂起的中断,以及确定每个中断的优先级。

总的来说,这部分负责处理来自外部设备的中断,并在将它们转发到处理器的中断控制器之前对它们进行优先级排序,使处理器能够及时有效地响应关键事件。


LevelGateway 模块用于与外界的中断信号进行交互

Irq_pend 模块用于实现中断清除和使能控制

Irq_prio 模块用于实现中断优先级的设置。

irq_i_gated 是一个中断控制器中用于选择最高优先级的中断的信号,其实现的功能包括:为每个中断源生成对应的 irq_i_gated_valid[i] 和 irq_i_gated_ready[i] 两个信号,分别表示该中断源产生有效中断以及是否可以处理该中断。对于所有中断源的 irq_i_gated_valid[i] 和 irq_i_gated_ready[i] 进行逐一判断,如果都为 1(即有中断产生且可以处理),则将其中优先级最高的中断输出,即 plic_irq_o 信号,否则将 plic_irq_o 置为 0。

在比较和选择最高优先级中断源的模块中,定义了三个数组:irq_prio_lvl_10、irq_id_lvl_10 和 irq_ip_lvl_10。这三个数组用于存储级联式中断处理器中每个中断源的优先级、ID 和挂起状态。


对于输入的中断信号 plic_irq_i,如果 PLIC_IRQ_I_FLOP 为 1,则将其输入到 D 触发器 sirv_gnrl_dffr 中进行寄存,否则直接连线到寄存器的输入端。这一处理方式用于避免输入的中断信号由于时序问题而出现干扰等问题。

对于输出的中断信号 plic_irq_o,如果 PLIC_IRQ_O_FLOP 为 1,则先对其进行多个 D 触发器的级联寄存,然后输出到终端。如果 PLIC_IRQ_O_FLOP 为 0,则直接将处理后的值通过连线传递到输出端。


通过 sirv_LevelGateway 模块处理中断信号的到达,输出到 plic_irq_i_r 寄存器中进行存储,以及通过 icb_complete_irq 输出完成信号。

根据中断请求的优先级与已有中断请求的情况,判断是否需要设置/清除/使能/屏蔽中断请求。对于设置/清除操作,需要判断对应的中断位是否已被占用。对于使能/屏蔽操作,需要根据赋值的掩码来控制哪些中断优先级是有效的。在这个过程中,使用了 sirv_gnrl_dfflr 模块实现中断请求寄存器的存储功能。

对于每个处理器,使能或禁用某些中断请求。对于这个功能,通过对中断使能寄存器 irq_enab_r 和 irq_enab_nxt 进行比较,并更新使能情况,最终输出更新结果。


该循环对每一个中断进行处理,在 icb_cmd_sel_enab[i] icb_cmd_wr_hsked 两个信号进行 AND 运算,以判断是否需要进行中断使能操作,从而控制 irq_enab_ena[i] 是否置位。

使用一个 D 触发器模块 sirv_gnrl_dfflr #(32) 将新的中断使能状态值保存到 irq_enab_r[i] 中。其中,irq_enab_nxt[i] 表示当该中断使能状态被更新时,新的状态值保存到这里。irq_enab_r[i] 是所有中断的使能寄存器的集合。clk rst_n 分别表示时钟和复位信号输入。

for 循环中,如果当前到达的是第一个中断,则该中断的状态总是设置为 0,并且将 icb_cmd_wdata 的高 31 位复制到 irq_enab_nxt[i] 的低 31 位。如果当前到达的是最后一个中断,那么现在只考虑 PLIC_IRQ_NUM 32 取模的情况(也就是说,中断数量小于等于 32)。如果 PLIC_IRQ_NUM 能够被 32 整除,那么将 icb_cmd_wdata 传输的值直接保存到 irq_enab_nxt[i] 中;否则只将 icb_cmd_wdata 的低 (PLIC_IRQ_NUM % 32) 位保存到 irq_enab_nxt[i] 中。如果当前中断号不是第一个也不是最后一个,则直接将 icb_cmd_wdata 的值保存到 irq_enab_nxt[i] 中。因此,这个循环实现了对 PLIC 中断的使能状态编程,使得用户可以通过 ICB 总线控制单个或多个中断的使能状态。


在上述代码中,irq_thod 用于实现中断请求响应的阈值功能。具体来说,根据该阈值的大小,中断请求优先级高于这个阈值的中断才能被处理,否则暂不响应。将 icb_cmd_sel_thod icb_cmd_wr_hsked 两个信号进行与运算,判断当前传输是否为阈值设置操作(通过 ICB 总线传输方式进行),如果是则进一步处理设置值。irq_thod_nxt 取自 icb_cmd_wdata,表示新的阈值设置值。这里的 PLIC_PRIO_WIDTH 表示优先级宽度。


中断请求的优先级由 plic_irq_prio 寄存器保存,并通过 irq_thod_r 信号连接到 PLIC 控制逻辑 irq_thod_r 则是 PLIC 配置寄存器中的一个值,表示中断请求响应的最低阈值。如果当前的中断请求优先级和 PLIC 的控制寄存器中的中断响应阈值都满足要求,则会将处理后的中断请求信号 plic_irq_o 输出到终端。如果不满足要求,则会忽略该中断请求,等待更高优先级的中断请求到来。这一阈值机制有助于提高中断系统的响应效率,并且可以通过修改控制寄存器来适应不同的中断请求场景。

使用另一个模块 sirv_gnrl_dfflr #(PLIC_PRIO_WIDTH) 将新的阈值 irq_thod_nxt 保存到 irq_thod_r 中。其中,irq_thod_ena 表示是否需要启用 irq_thod_nxt 的值,即通过 icb_cmd_sel_thod 指定是否是阈值设置操作来控制。最后,这个 D 触发器模块可以使得阈值设置能够立即生效,提高了中断响应的实时性。

 

上面的代码生成一个二叉树结构来比较和选择具有最大优先级的挂起中断源及其ID。树状结构由级联比较器组成,每一层的比较器数量是前一层的一半。在树的每一层,选择优先级最高的中断并传递到下一层,直到只剩下一个中断源。

代码首先生成10级信号,这些信号是二叉树的输入信号。如果一个中断挂起并启用,那么该中断的优先级将被相应的中断启用位掩盖。如果中断未启用,则其优先级设置为零。

接下来,该代码通过比较相邻的10级信号对来生成9级信号。对于每一对,比较器输出具有最高优先级的中断及其优先级的索引。如果左中断的优先级高于右中断,则选择左中断;否则,选择正确的中断。

然后,该代码使用相同的方法生成8级、7级、6级和5级信号。每一层的信号数是前一层的一半。最后的输出信号是irq_prio_lvl_0irq_id_lvl_0irq_ip_lvl_0,它们表示具有最高优先级的中断及其ID

总的来说,这段代码实现了一个优先级编码器,它在挂起和启用的中断中选择优先级最高的中断。优先级编码器使用二叉树结构,这减少了确定最高优先级中断所需的比较器的数量。

这段代码是一个 generate 模块,它创建了三个循环,分别用于选择处理器优先级、中断挂起状态和目标处理器使能状态的地址。其中,每个循环内部使用 assign 语句,将 icb_cmd_sel_prio[i]icb_cmd_sel_pend[i] icb_cmd_sel_enab[i] 分别与地址是否等于对应的基地址关联起来,从而在读取数据时可以根据地址选择相应的信号进行输出。

此外,代码还包括了三个 mux 模块,用于从不同的信号源中选择读出的数据。每个 mux 模块都是一个 always 块,其输出是 rsp_rdata_priorsp_rdata_pend rsp_rdata_targ 三个寄存器。在这些 always 块中,使用 for 循环结合 assign 语句,将不同的数据源通过或运算合并起来,最终得到输出的数据。具体来说,rsp_rdata_prio 包括 IRQ 的优先级,rsp_rdata_pend 包括 IRQ 的挂起状态,rsp_rdata_targ 包括 IRQ 的使能状态、目标处理器的优先级阈值和声明/完成标记。

喜欢2
用户评论
妖刀村雨

妖刀村雨 实名认证

言灵-审判

积分
问答
粉丝
关注
  • RV-STAR 开发板
  • RISC-V处理器设计系列课程
  • 培养RISC-V大学土壤 共建RISC-V教育生态
RV-STAR 开发板