Rocket-chip-CLINT

本文深入解析RISC-V架构中的CLINT中断机制,包括软件中断与定时器中断的配置与触发流程,通过具体代码示例展示如何实现中断处理,并讨论了mtime与mtimecmp寄存器在32-bit系统中的操作细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

首先我们先看一下指令集《The RISC-V Instruction Set Manual Volume II: Privileged Architecture Privileged Architecture Version 1.10》是如何定义的。
文档链接:
https://riscv.org/specifications/privileged-isa/
在这里插入图片描述
再看看E3关于CLINT的说明。
在这里插入图片描述
红色箭头是一个软件中断位,配置为1会产生中断,配置为0清除中断。
蓝色箭头是timer比较数值的配置,当mtime的数值等于mtimecmp时,会产生timer中断,当然前提是打开了这个中断的开关。
绿色箭头是mtime的值,根据时钟每次+1。
需要注意的是mtime和mtimecmp都是64-bit的寄存器,所以32-bit总线系统需要操作两次。还有rocket-chip这边是没有单独引出rtc_toggle这个时钟输入端口的,它是直接采用core的时钟进行100分频,如果想像E3那样采用外部异步时钟的话,需要修改scala代码。而且需要保证rtc_toggle与core_clk有一定的倍数关系,例如:rtc_toggle≤core_clk/2。
还需要注意的是,mtime在复位后是会被清0的,但mtimecmp不会。

接下来直接看测试代码。

#include "encoding.h"

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

void handle_trap();
void csr_cfg();
void mtimecmp_cfg();
void msip_cfg();

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

void handle_trap()
{
    //enter handle_trap()
    U32(DEBUG_SIG) = 0x5;

    //clear msip interrupt
    U32(0x2000000)  = 0x0;

    if((read_csr(mip) & 0x00000080) != 0x0)
    {
        //clear mtime register
        U32(0x200BFF8) = 0x0;
        U32(0x200BFFC) = 0x0;
        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));
}

//--------------------------------------------------------------------------
// mtimecmp configuration function

void mtimecmp_cfg()
{
    //mtiemcmp0 - low bit
    U32(DEBUG_VAL) = U32(0x2004000);
    U32(0x2004000)  = 0x00000010;

    //mtiemcmp1 - high bit
    U32(DEBUG_VAL) = U32(0x2004004);
    U32(0x2004004)  = 0x00000000;
    
    //clear mtime register
    U32(0x200BFF8)  = 0x0;
    U32(0x200BFFC)  = 0x0;
}

//--------------------------------------------------------------------------
// msip configuration function

void msip_cfg()
{
    U32(0x2000000)  = 0x1;
}

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

void main()
{
    unsigned int i;

    //configuration
    mtimecmp_cfg();
    csr_cfg();

    //delay
    for(i=0;i<20;i++)
    {
        U32(0x70000008+i*4) = i;
    }

    //msip software interrupt
    msip_cfg();

    while(1) {asm volatile ("wfi");}
}

仿真波形如下。 在这里插入图片描述
测试代码一次完成了两个中断。第一次是在配置完寄存器后,延迟一小段时间,然后往msip写1产生软件中断,进入中断函数后往msip写0清除中断,此时并没有立刻结束仿真,还在等mtime的计数,当mtime计数值等于mtimecmp时,timer中断产生,进入中断函数,清除中断状态,最后结束仿真。

### Rocket-Chip与AXI4接口的实现细节 Rocket-Chip 是一个基于 RISC-V 架构的开源 SoC 平台,其设计支持多种总线协议,包括 AXI4、AXI4-Lite 和 TileLink。以下是对 Rocket-Chip 中 AXI4 接口实现细节的分析: #### 1. AXI4 协议简介 AXI4(Advanced eXtensible Interface 4)是一种高性能、低延迟的总线协议,广泛应用于 ARM 系统中。它支持高带宽数据传输,并通过分离地址和数据通道来优化性能[^2]。 #### 2. Rocket-Chip 的 AXI4 支持 Rocket-Chip 默认使用 TileLink 作为内部总线协议,但可以通过适配器将 TileLink 转换为 AXI4 或其他外部协议。这种转换通常由 `sifive-blocks` 提供的模块完成,例如 `TLToAXI4` 和 `AXI4ToTL`。 - **TLToAXI4 模块**:该模块将 Rocket-Chip 内部的 TileLink 信号转换为 AXI4 信号,以便与其他 AXI4 兼容的外设通信[^3]。 - **AXI4ToTL 模块**:该模块将 AXI4 信号转换回 TileLink 信号,适用于需要将外部 AXI4 设备连接到 Rocket-Chip 的场景[^3]。 #### 3. 修改 Rocket-Chip 的 AXI4 接口 根据引用中的描述,用户希望将 Rocket-Chip 的 AXI4 协议改为 AHB 协议。这一修改可以通过以下步骤实现: - 替换现有的 `TLToAXI4` 模块为支持 AHB 协议的适配器。 -Rocket-Chip 的顶层文件中重新定义总线接口,确保其符合 AHB 协议规范。 - 更新相关寄存器映射和地址解码逻辑以适应新的协议[^4]。 #### 4. DCache Enable CSR 寄存器的功能扩展 通过引入 DCache Enable 的 CSR 寄存器,可以动态控制数据缓存的行为。具体实现如下: ```python # 定义一个新的 CSR 寄存器用于控制 DCache class DCacheEnableCSR(Module): def __init__(self): self.io = IO( enable=Output(Bool), write=Input(Bool), data_in=Input(UInt(32)), data_out=Output(UInt(32)) ) # 初始化寄存器值 self.reg_value = RegInit(0.U(32.W)) # 写入逻辑 with when(self.io.write): self.reg_value @= self.io.data_in # 输出当前寄存器值 self.io.data_out @= self.reg_value # 根据寄存器值控制 DCache 启用状态 self.io.enable @= (self.reg_value != 0.U) ``` 上述代码展示了如何定义一个简单的 CSR 寄存器,并通过其值动态启用或禁用 DCache 功能[^5]。 #### 5. 总线物理访问地址的修改 修改各总线(如 mem/MMIO)的物理访问地址可以通过调整 Rocket-Chip 的内存映射配置文件实现。例如,在 `MemoryMap.scala` 文件中重新定义地址范围: ```scala val memoryRegions = Seq( MemoryRegion("io", 0x1000, 0x10000), // IO 地址范围 MemoryRegion("ram", 0x80000000, 0x1000000) // RAM 地址范围 ) ``` 通过这种方式,可以灵活地调整各个外设的物理地址映射[^6]。 --- ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值