PolarFire® SoC FreeRTOS RISC-V 'systick' 定时器 mtime

PolarFire® SoC 尝试移植 FreeRTOS - 所长 - 博客园

 

PolarFire SoC MSS Technical Reference Manual

image

 

mtime是一个64位读写寄存器,用于对rtc_toggle信号的周期进行计数。当mtime的值大于或等于mtimecmp寄存器中的值时,将产生待处理的定时器中断。该定时器中断会反映在《机器中断待处理寄存器(mip)》所述的mip寄存器mtip位中。复位时,mtime被清零,但mtimecmp寄存器保持原值不变。

mtime寄存器每1微秒(1MHz)递增一次,这与RTC时钟的输入频率保持一致。关于rtc_toggle信号的具体应用,请参阅GitHub页面driver-examples/mss/mss-rtc目录下的示例项目。

 

看这意思, mtime 的增长 是 根据 RTC 输入时钟确定的?  PolarFire SoC MSS Configurator User Guide

 RTC/MAC SGMII reference clock input source   ,, You can select the NW PLL ports or I/Os from Bank 5.  

 

  Bank 5 可是 125Mhz; 编写 以下代码, 将 RTC 时钟源 改为 1Mhz;  这样 mtime 时钟源 也固定了! 

 

/mpfs-mmuart-interrupt/src/boards/icicle-kit/fpga_design_config/clocks/hw_mss_clks.h

#define LIBERO_SETTING_MSS_EXT_SGMII_REF_CLK    125000000

#define LIBERO_SETTING_MSS_RTC_TOGGLE_CLK    1000000

 

SYSREG->RTC_CLOCK_CR &= ~BIT_SET;
SYSREG->RTC_CLOCK_CR = LIBERO_SETTING_MSS_EXT_SGMII_REF_CLK / LIBERO_SETTING_MSS_RTC_TOGGLE_CLK;
SYSREG->RTC_CLOCK_CR |= BIT_SET;

 

image

 

寄存器地址;

image

 

 

 

 

 

  FreeRTOS/FreeRTOS/Demo/RISC-V_RV64_PolarFire_SoftConsole/FreeRTOSConfig.h at main · FreeRTOS/FreeRTOS 

  FreeRTOSConfig.h  配置文件中 mtime 地址配置

 

#define configMTIME_BASE_ADDRESS ( CLINT_CTRL_ADDR + 0xBFF8UL )
#define configMTIMECMP_BASE_ADDRESS ( CLINT_CTRL_ADDR + 0x4000UL )

 

mtime 如何使用,怎么产生中断的?

 

咨询 deepseek 

 

RISC-V 架构通过 mtime 和 mtimecmp 寄存器来实现定时器功能,下面是详细的使用方法:

RISC-V 定时器架构

核心寄存器:

  • mtime - 机器模式时间计数器(64位,只读)

  • mtimecmp - 机器模式时间比较寄存器(64位,读写)

  • mtime 不断递增,当 mtime >= mtimecmp 时产生定时器中断

 

MTIME 中断号在 polarfire soc 中 是 7, 下面表格:

image

 

代码编写 产生 MTIME 中断,以 FreeRTOS 为例子:

1、FreeRTOSConfig.h 文件中

#define configMTIME_BASE_ADDRESS ( CLINT_CTRL_ADDR + 0xBFF8UL )
#define configMTIMECMP_BASE_ADDRESS ( CLINT_CTRL_ADDR + 0x4000UL )

 2、FreeRTOS port.c 文件中 

UBaseType_t const ullMachineTimerCompareRegisterBase = configMTIMECMP_BASE_ADDRESS;

 

这个函数里 ,会自动 读取 当前 hart id ,  根据 hart id  自动 计算 mtimecmp 寄存器的 地址, 好高级!!!

    void vPortSetupTimerInterrupt( void )
    {
        uint32_t ulCurrentTimeHigh, ulCurrentTimeLow;
        volatile uint32_t * const pulTimeHigh = ( volatile uint32_t * const ) ( ( configMTIME_BASE_ADDRESS ) + 4UL ); /* 8-byte type so high 32-bit word is 4 bytes up. */
        volatile uint32_t * const pulTimeLow = ( volatile uint32_t * const ) ( configMTIME_BASE_ADDRESS );
        volatile uint32_t ulHartId;

        __asm volatile ( "csrr %0, mhartid" : "=r" ( ulHartId ) );

        pullMachineTimerCompareRegister = ( volatile uint64_t * ) ( ullMachineTimerCompareRegisterBase + ( ulHartId * sizeof( uint64_t ) ) );

        do
        {
            ulCurrentTimeHigh = *pulTimeHigh;
            ulCurrentTimeLow = *pulTimeLow;
        } while( ulCurrentTimeHigh != *pulTimeHigh );

        ullNextTime = ( uint64_t ) ulCurrentTimeHigh;
        ullNextTime <<= 32ULL; /* High 4-byte word is 32-bits up. */
        ullNextTime |= ( uint64_t ) ulCurrentTimeLow;
        ullNextTime += ( uint64_t ) uxTimerIncrementsForOneTick;
        *pullMachineTimerCompareRegister = ullNextTime;

        /* Prepare the time to use after the next tick interrupt. */
        ullNextTime += ( uint64_t ) uxTimerIncrementsForOneTick;
    }

 

3、 使能 MTIME 的 中断

 

    /* If there is a CLINT then it is ok to use the default implementation
     * in this file, otherwise vPortSetupTimerInterrupt() must be implemented to
     * configure whichever clock is to be used to generate the tick interrupt. */
    vPortSetupTimerInterrupt();

    #if ( ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIMECMP_BASE_ADDRESS != 0 ) )
    {
        /* Enable mtime and external interrupts.  1<<7 for timer interrupt,
         * 1<<11 for external interrupt.  _RB_ What happens here when mtime is
         * not present as with pulpino? */
        __asm volatile ( "csrs mie, %0" ::"r" ( 0x880 ) );
    }
    #endif /* ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIMECMP_BASE_ADDRESS != 0 ) */

 

image

 

 

汇编指令 :

__asm volatile ( "csrs mie, %0" ::"r" ( 0x880 ) );

   使用的汇编指令是 csrs (Control and Status Register set),它的作用是:读取 mie 寄存器的当前值,或(OR) 上操作数 %0(即 0x880)的值,然后将结果写回 mie 寄存器。

  

这样 是开启  M 状态下总中断 , 以及 mtime 的中断 , 而且 还不影响 其他 中断;

 

deepseek 真他妈好用!

在 RISC-V 中,控制和状态寄存器(CSR)的写入操作有以下几种类型,用于避免常见的“读-修改-写”竞态条件:

  1. csrs (CSR Set):

    • 功能: 将 CSR 中指定的比特位置 1。

    • 操作: t = CSRs[csr]; t = t | x; CSRs[csr] = t

    • 您的代码: csrs mie, %0 等价于 mie = mie | 0x880;

    • 效果: 只会开启 0x880 所对应的中断位,而其他所有位(例如 MEIEMTIEMSIE 等)保持原来的状态不变。这是一种安全的设置方式。

  2. csrc (CSR Clear):

    • 功能: 将 CSR 中指定的比特位置 0。

    • 操作: t = CSRs[csr]; t = t & ~x; CSRs[csr] = t

    • 例如: csrc mie, %0 会关闭 0x880 对应的中断位。

  3. csrw (CSR Write):

    • 功能: 直接将值写入 CSR。

    • 操作: CSRs[csr] = x

    • 如果您这样写: __asm volatile ( "csrw mie, %0" ::"r" ( 0x880 ) );

    • 效果: 这会用 0x880 直接覆盖整个 mie 寄存器的值。所有其他之前被设置的位(比如机器定时器中断 MTIE)都会被清零,这很可能导致系统异常或中断丢失。这是一种危险的操作,除非你确知要清除所有其他位。

关于 0x880
这个值是两个中断使能位的掩码组合:

    • 0x800 (第 11 位):机器外部中断使能 (MEIE)

    • 0x080 (第 7 位):机器定时器中断使能 (MTIE)
      所以,您的这行代码的目的是同时启用机器模式下的定时器中断和外部中断。

posted on 2025-09-08 20:33  所长  阅读(28)  评论(0)    收藏  举报

导航