资源与支持

SiFive 博客

来自 RISC-V 专家的最新洞察与深度技术解析

July 12, 2018

Interrupts on the SiFive E2 Series

Last week SiFive launched the new E2 Series RISC-V Core IP. The E2 Series represents SiFive’s smallest, most efficient Core IP Series and is targeted specifically for embedded microcontroller designs. One of the reasons it is great for microcontroller applications is because of its extremely small area footprint, just 0.023mm2 in 28nm for the entire E20 Standard Core! Another reason it's great for the embedded market is its configurability. The E2 Series can be configured even smaller than the E20 Standard Core by removing things like the Interrupt Controller and support for the M extension. Another major reason the E2 Series is great for microcontroller applications is its support for the new RISC-V Core Local Interrupt Controller (CLIC) which allows for extremely low latency interrupt operation, hardware preemption, and hardware prioritization of all interrupts. The CLIC specification is a result of collaboration between RISC-V members in the RISC-V Foundation’s Fast Interrupts Technical Group and the draft specification can be found here.

Core Local Interrupt Controller

Hardware vectoring is an important aspect of traditional microcontroller applications. Vectoring allows for each interrupt to trap into a unique handler. This is important for performance as it reduces latency by moving the complexity of determining the source of the interrupt from software to hardware. The CLIC vector table, which is relocatable, contains addresses of interrupt handlers. Thanks to toolchain support for the interrupt attribute (see the section below), this means that the vector table can be populated with addresses of C functions. The CLIC supports two modes of operation, Direct or Vectored. In Vectored mode, all interrupts trap to unique handlers as described above. In Direct mode, all interrupts trap to a single handler. However, while in Direct mode, it is also possible to enable selectively vectored interrupts, where all interrupts trap to a single handler except for specifically selected interrupts which vector to their own unique handlers. Hardware preemption is supported regardless of the CLIC mode.

When interrupt vectoring is coupled with the E2 Series's efficient pipeline and guaranteed single cycle TIM access latency, it is possible to execute the first instruction of an interrupt's C handler as few as six cycles.

The CLIC specification supports up to 16 levels of preemption and programmable priorities within each level. The bits, which determine the number of supported levels and priorities, are configurable in the E2 Series with the E21 implementing four bits. The split between bits used for levels and priorities are software defined using the CLIC's configuration register.

Simple Software

Gone are the days of pushing and popping registers in assembly! RISC-V GCC now supports the interrupt function attribute. This attribute tells the compiler that the specified function is an interrupt handler, which results in the function saving the registers it needs upon entry, and executing an mret upon exit. With interrupt entry and exit now taken care of by the compiler, it becomes possible for the vector table to consist entirely of function pointers.

The example below defines a vector table (localISR), populates all interrupts with a safe default handler, and then points the CLIC to it.

typedef void (*interrupt_function_ptr_t) (void);
interrupt_function_ptr_t localISR[CLIC_NUM_INTERRUPTS] __attribute__((aligned(64)));

...
void default_handler(void)__attribute__((interrupt));;
void default_handler(void) {
  while(1);
}

...
// initialize vector table
int i = 0;
while (i < CLIC_NUM_INTERRUPTS) {
  localISR[i++] = default_handler;
}
// point mtvt to localISR
write_csr(mtvt, localISR);

Notice that the mtvt 64 byte alignment requirements are also able to be implemented using the aligned function attribute.

The interrupt attribute does not enable preemption as it leaves interrupts disabled until exiting the handler with mret. Thankfully enabling pre-emption is as simple using a different function attribute: SiFive-CLIC-preemptible. The SiFive-CLIC-preemptible attribute does the same thing as the interrupt attribute, but it first saves mcause and mepc, and then re-enables interrupts making the handler preemptible as soon as possible.

Toolchain support for the interrupt and SiFive-CLIC-preemptible attributes can be found in SiFive's pre-built RISC-V GCC toolchain, or in source form in Freedom-E-SDK.

Code Examples - The CLIC in Action

The example below, taken from Freedom-E-SDK's clic_vectored application, demonstrates a very simple but real interrupt routine that increments a counter and clears the interrupt.

void msi_isr()__attribute((interrupt));
void msi_isr() {
  // clear the  SW interrupt
  CLIC0_REG8(CLIC_INTIP + CSIPID) = 0;
  // increment COUNT
  COUNT++;
}

The resulting assembly code consists of only 13 instructions including the mret to exit the handler. An entire C code handler in just 13 instructions! This can be further reduced 12 cycles if using the RISC-V atomic add instruction which allows you to perform the load/add/store in two instructions instead of three. So six cycles to get into the handler, plus 12 cycles to execute the handler, means an entire interrupt routine on the E2 Series can execute in just 18 cycles!

          csip_isr:
8000813c:   addi    sp,sp,-16
8000813e:   sw      a4,12(sp)
80008140:   sw      a5,8(sp)
117         CLIC0_REG8(CLIC_INTIP + CSIPID) = 0;
80008142:   lui     a5,0x2800
80008146:   sb      zero,12(a5) # 0x280000c
118         COUNT++;
8000814a:   addi    a4,gp,-1804
8000814e:   lw      a5,0(a4)
80008150:   addi    a5,a5,1
80008152:   sw      a5,0(a4)
80008154:   lw      a4,12(sp)
80008156:   lw      a5,8(sp)
80008158:   addi    sp,sp,16
8000815a:   mret

The example also demonstrates hardware interrupt preemption using the buttons on the Arty board. These buttons are wired directly into the local interrupts, so a given interrupt will stay asserted as long as the button is being pushed. The C handlers are defined using the SiFive-CLIC-preemptible attribute allowing for preemption and given different interrupt levels (button 0 is lower than button 1 which is lower than button 2). The code for button 2 is shown below.

void button_2_isr(void) __attribute__((interrupt("SiFive-CLIC-preemptible")));
void button_2_isr(void) {
  // Toggle Red LED
  uint8_t level = clic_get_int_level(&clic, (LOCALINTIDBASE + LOCAL_INT_BTN_2));
  printf("Button 2 pressed, interrupt level %d. Pending CSIPID and toggle Green.\n", level);
  GPIO_REG(GPIO_OUTPUT_VAL) = GPIO_REG(GPIO_OUTPUT_VAL) ^ (0x1 << GREEN_LED_OFFSET);
  // pend a software interrupt
  clic_set_pending(&clic, CSIPID);
  wait_ms(1000);
  GPIO_REG(GPIO_OUTPUT_VAL) = GPIO_REG(GPIO_OUTPUT_VAL) ^ (0x1 << GREEN_LED_OFFSET);
}

void button_2_setup(void) {
  clic_install_handler(&clic, (LOCALINTIDBASE + LOCAL_INT_BTN_2), button_2_isr);
  clic_set_int_level(&clic, (LOCALINTIDBASE + LOCAL_INT_BTN_2), 3);
  clic_enable_interrupt(&clic, (LOCALINTIDBASE + LOCAL_INT_BTN_2));
}

Pressing button 0 will trigger a 10-second interrupt handler. While in button 0's handler, it is possible to press button 1. Since button 1 is a higher interrupt level, it will preempt button 0's handler and begin executing its own five-second handler. Button 2 is higher priority still, and pressing its button will trigger another preemption to execute button 2's handler. The handlers will then return in order from button 2's handler to button 1's, and then from button 1's to button 0's, and eventually from button 0's back to regular code execution.

Note that the button 2 handler also triggers the CLIC's software interrupt. In the example code, the software interrupt is set to a lower interrupt level (level 1) which will result in the CSIP handler being executed prior to returning to normal code execution. This is a common scenario in Real Time Operating Systems. For example a high level interrupt may trigger a context switch, but it could be undesirable to execute the context switch from the handler's interrupt level (blocking all lower levels) and it's inconvenient to wait for the next OS tick.

Putting it all Together

The CLIC is an advanced interrupt controller well suited to all embedded applications thanks to its capabilities and configurability. When taken together with the performance and efficiency of the E2 Series Core, the E2 Series is a very compelling choice for your next embedded application. The SiFive E2 Series has free RTL and FPGA evaluations available today for both the E21 and E20 Standard Cores.

Drew Barbier
Drew Barbier
Senior Director of Product Marketing

Read more Insights from the RISC-V Experts

P570 Gen 3:系统视角
最新文章
P570 Gen 3:系统视角
然而,CPU 的需求横跨性能、功耗和成本等多个维度。在某些细分市场中,需要在不同的功耗与成本约束下实现性能提升。基于这类 CPU 的系统需要可信赖的产品路线图,才能切实交付新的系统能力。尽管部分供应商已退出“低端市场”,SiFive 仍坚持在整条性能曲线上持续创新。本次发布的 P570 Gen 3 Performance IP,旨在为中低端、具备 Linux 能力的系统提供显著的性价比与能效比提升。
SiFive Performance™ P570 Gen 3 深度解析:面向下一代消费级与商用应用的高性能能效设计
最新文章
SiFive Performance™ P570 Gen 3 深度解析:面向下一代消费级与商用应用的高性能能效设计
SiFive 的核心是 RISC-V,这是 SiFive 创始人在公司成立 5 年前发明的指令集架构 (ISA)。SiFive 正持续演进基于 RISC-V 的 IP 基础模块,重新定义并推动各类计算平台的普及化发展。在技术领域,演进并非一串随机变化的时间线,而是一系列精心规划、环环相扣的里程碑。每一步演进都会创造一系列新的环境条件,从而推动下一次更复杂的跨越成为必然。要赢得这场竞赛,关键在于具备适应变化的灵活性与持续创新能力,而这两点正是 SiFive 与 RISC-V 的核心价值观所在。
全力投入:开启增长新篇章
最新文章
全力投入:开启增长新篇章
我们自信地宣布公司发展历程中最重要的里程碑之一:完成 4 亿美元 的融资。本轮融资由 Atreides Management 领投,其他顶级投资机构\*包括 Apollo Global Management、NVIDIA(英伟达)、Point72 Turion 和 T. Rowe Price Investment Management, Inc.,以及现有投资者 Prosperity7 Ventures 和 Sutter Hill Ventures 参投。此次融资使公司估值达到 36.5 亿美元,并将加速 SiFive 的 RISC-V CPU 及 AI IP 解决方案推向数据中心和 AI 基础设施市场的核心地带。