LED, timer and interrupts on RISC-V from scratch (for example, GD32VF103 and IAR C ++)



Today we will talk about the fashionable one - about the RIS-V microcontroller. I have long wanted to get acquainted with this kernel and waited for something similar to STM32 to appear and now I waited, meet - the Chinese GigaDevice - GD32V.



The infrastructure for this microcontroller is not as extensive as for the STM32, but there is everything you need to get started with it. Fortunately, debug boards can be ordered on alikexpress, for example, here: Longan Nano GD32VF103CBT6 RISC-V MCU



The Chinese are promoting the Platform IO development environment for this microcontroller, which can be delivered as an extension for Visual Studio Code. But we will not use it, this is not according to engineering concepts, we are engineers and we want to figure it out ourselves. So let's try to run the board on IAR, writing everything from scratch.



By the way, IAR is distributing a debug kit (debug board + I-Jet debugger + 30 Days full license) IAR RISC-V GD32V Evaluation kit. Here you can leave a Request for Development Tools . Not sure if they send the kit to everyone, but we received it within 5 days. Thank them for that!



Well, who are interested, welcome under the cut



Introduction



CortexM, - IAR ( IAR ), , , .



โ€” , . , , , , , , , , , , RISC-V , , , RISC-V .





, ( RISC-V). , , RISC-V.



RISC-V , , , . , , , , , , , .



RISC-V ( RISC-V, Syntacore), , , . , .



GD32VF103



, , - .



  1. ISA RISC-V RISC-V :

    ISA

    ISA
  2. GD32VF103. , . GD32VF103CBT6 โ€” GD32 RISC-V Microcontroller
  3. GD32VF103 Bumblebee, Nuclei processor core
  4. ECLIC.
  5. . .
  6. Nuclei RISC-V , IAR n200 drivers
  7. GigaDevice, IAR
  8. FreeRTOS uCOS II n200 sdk
  9. , Platform IO , . .


4 , ++ โ€” , . :



100 200




RISC-V . , , RISC-V, :



  • Hart ( ) โ€” , . (hart) . (hart) ID 0. hart.
  • Trap() โ€” , . , โ€” , โ€” , , . :

    • (exception) โ€” , . , NMI.
    • (interrupt) โ€” , , . , NMI, .
    • (NMI) โ€” . NMI NMI, NMI , NMI . , , , .
  • Machine () โ€” โ€” , , . machine() . , , , .


โ€ฆ .



RISC-V



, :



RISC-V (-) โ€” (ISA โ€” Instruction Set Architecture) RISC . , . .

, , , :



RISC-V
RV32I 32- 32 2.1 Ratified
RV32E 32- 16 1.9 Draft
RV64I 64- 32 2.1 Ratified
RV128I 128- 1.7 Draft
M (Integer Multiplication and Division) 2.0 Ratified
A (Atomic Instructions) 2.1 Ratified
F (Single-Precision Floating-Point) 2.2 Ratified
D (Double-Precision Floating-Point) 2.2 Ratified
G / /
Q 2.2 Ratified
L (Decimal Floating-Point) 0.0 Open
C (Compressed Instructions) 2.2 Ratified
B (Bit Manipulation) 0.36 Open
J (Dynamically Translated Languages) 0.0 Open
T (Transactional Memory) 0.0 Open
P SIMD- (Packed-SIMD Instructions) 0.1 Open
V (Vector Operations) 0.2 Open
N (User-Level Interrupts) 1.1 Open


, , . .



, 32 , :



:



  • RV32: 32 16
  • RV32I: 32 32


:



  • M:
  • C: 16
  • :
  • F:
  • D:


โ€” .



.





RISC-V 32 x0-x31. ABI .



:

t0-t6(x5-x7, x28-x31) a0-a7(x10-x17), . - , .



:

s0-s11 (x8, x9, x18-x27 ) ( ) , .



, , , :



32
Register ABI Name Description Saver
x0 zero Hard-wired zero โ€”
x1 ra Return address Caller
x2 sp Stack pointer Callee
x3 gp Global pointer โ€”
x4 tp Thread pointer โ€”
x5 t0 Temporary/alternate link register Caller
x6โ€“7 t1โ€“2 Temporaries Caller
x8 s0/fp Saved register/frame pointer Callee
x9 s1 Saved register Callee
x10โ€“11 a0โ€“1 Function arguments/return values Caller
x12โ€“17 a2โ€“7 Function arguments Caller
x18โ€“27 s2โ€“11 Saved registers Callee
x28โ€“31 t3โ€“6 Temporaries Caller
pc pc Program counter


.



x0/zero:

0 CSR( ), , CSRRS (Atomic Read and Set Bits in CSR), x0 , CSR . , CSR, , zero.



x1/ra:

(Link register Return Address ). . , , , ret, .



x2/sp:

. โ€” . , CortexM, .



x3/gp:

(The global pointer register). (gp/x3) 4 .



gp, 4 , /pc- gp- , . .



4K , , , . .



x4/tp:

(The thread pointer). . (Thread Local Storage (TLS)), thread_local ++.



, โ€” , .





, , , ISA, . :





.





, .. . : ISA





โ€” , Linux, .



, , .



, . : ISA



, , . , , .



, - , .





RISC-V 3 . (, ). , , .

:



0 00 User/Application U
1 01 Supervisor S
2 10 Reserved
3 11 Machine M


M
M, U
M,S,U Unix


, , GD32VF103 M U. . U, , mtvt, mepc, . , , GD32VF103, .



.. M . โ€” ecall โ€” , , . mret โ€” .



, , , , .



:

RISC-V, (, ).



GD32VF103



F GD32VF103 โ€” , GD32F103 CortexM3. . , , - GD32F103 STM32F103 โ€ฆ GD32VF103 c GD32F103. ( , ), GD32F103, GD32VF103.



RV32IMAC โ€” RISC-V 32- 32- , , 16 .



, :



  • (Machine Mode), , .
  • (User Mode), .


, . , . : Nuclei privileged ISA





4 :



  • (Normal Mode โ€” 0x0)

    , (NMI) .
  • (Exception Handling Mode โ€” 0x2)

    .
  • (NMI Handling Mode โ€” 0x3)

    NMI.
  • (Interrupt Handling Mode โ€” 0x1)

    .


TYP msumbm



0 ( ) , .

, , , , , . , , mret โ€” , mstatus MPP , mepc. .



CSR (Control and Status Registers)



mstatus, mepc, msumbm, mtvt ..., ?



, , cssr csrr.



, , , - , .



, .



, . โ€” , , IAR CSR . ( IAR, , GCC ).





 // Get    ,     ,   
 template<typename T = AccessMode,
        class = typename std::enable_if_t<std::is_base_of<ReadMode, T>::value ||
                                          std::is_base_of<ReadWriteMode, T>::value>>
 inline static Type Get()
 {
   return __read_csr(address) ;
 }




unsigned long get_mstatus()
{
  unsigned long value;
  asm volatile("csrr %0, 0x300" : "=r"(value));
  return value;
}

auto mstatus = get_mstatus() ;




auto mstatus = CSR::MSTATUS::Get() ;


, , , . CSR , x, xstatus โ€” mstatus โ€” , ustatus โ€” . , , m , u .



.



CSR
, RISC-V CSR (Machine Mode)
0xF11 MRO mvendorid (Machine Vendor ID Register) , JEDEC
0xF12 MRO marchid (Machine Microacrhitecture ID Register)
0xF13 MRO mimpid (Machine Implementation ID Register) .
0xF14 MRO mhartid (Hart ID Register) , .
0x300 MRW mstatus (Machine Status Register)
0x301 MRO misa (Machine ISA Register) ,
0x304 MRW mie (Machine Interrupt Enable Register) PLIC (platform-level interrupt controller)
0x305 MRW mtvec (Machine Trap-Vector Base-Address Register) () .
0x307 MRW mtvt (ECLIC Interrupt Vector Table Base Address) ECLIC . , .
0x340 MRW mscratch (Machine Scratch Register) - , . , (sp) mscratch. - (sp). .
0x341 MRW mepc (Machine Exception Program Counter) , , . . 0.
0x342 MRW mcause (Machine Cause Register) , .
0x343 MRW mtval (Machine Trap Value Register). , , , .
0x344 MRW mip (Machine Interrupt Pending Register). , PLIC (platform-level interrupt controller).
Ox345 MRW mnxti (Next Interrupt Handler Address and Interrupt-Enable CSR) , . , , / .
0x346 MRO mintstatus (Current Interrupt Levels). .
0x348 MRW mscratchcsw (Scratch swap register for privileged mode). , mscratch ( sp mscratch). .
0x348 MRW mscratchcswl (Scratch swap register for interrupt levels). mscratch, . .
0xB00 MRW mcycle (Lower 32 bits of Cycle counter). 32
0xB80 MRW mcycleh (Upper 32 bits of Cycle counter). 32
0xB02 MRW minstret (Lower 32 bits of Instructions-retired counter). 32 .
0xB82 MRW minstreth (Lower 32 bits of Instructions-retired counter). 32 .
. RISC-V Standard CSR ๏ผˆUser Mode๏ผ‰
0xC00 URO cycle mycle,
0xC01 URO time mtime, 32 .
0xC02 URO instret minstret,
0xC80 URO cycleh mcycleh, .
0xC81 URO timeh mtimeh, 32 .
0xC82 URO instreth minstreth,
0x810 MRW wfe -
** Bumblebee. Bumblebee Customized CSR&&
0x320 MRW mcountinhibit (Customized register for counters on & off). ( mcycle) (minstret).
0x7c3 MRO mnvec (NMI Entry Address). NMI.
0x7c4 MRW msubm (Customized Register Storing Type of Trap). .
0x7d0 MRW mmisc_ctl (Customized Register holding NMI Handler Entry Address). NMI.
0x7d6 MRW msavestatus (Customized Register holding the value of mstatus). mstatus msubm, , NMI.
0x7d7 MRW msaveepc1 (Customized Register holding the value of mepc for the first-level preempted NMI or Exception). mepc.
0x7d8 MRW msavecause1 (Customized Register holding the value of mcause for the first-level preempted NMI or Exception). mcause.
0x7d9 MRW msaveepc2 (Customized Register holding the value of mepc for the second-level preempted NMI or Exception). mepc.
0x7eb MRW pushmsubm (Push msubm to stack). , msubm .
0x7ec MRW mtvt2 (ECLIC non-vectored interrupt handler address register). - .
0x7ed MRW jalmnxti (Jumping to next interrupt handler address and interrupt-enable register). , , .
0x7ee MRW pushmcause (Push mcause to stack). , mcause .
0x7ef MRW pushmepc (Push mepc to stack). , mepc .
0x810 MRW wfe (Wait for Event Control Register) , NMI .
0x811 MRW sleepvalue (WFI Sleep Mode Register).
0x812 MRW txevt (Send Event Register).


, , . โ€” .



mcause



. , .



mcause
INTERRUPT 31 : 0x0: NMI 0x1:
MINHV 30 , . ECLIC .
MPP 29:28 mstastus.MIE : 0x0: , 0x1: , 0x2: , 0x3: .
MPIE 27 mstastus.MIE : 0x1: . 0x0:
Reserved 26:24 Reserved 0
MPIL 23:16
Reserved 15:12 Reserved 0
EXCCODE 11:0 (ID) . EXCCODE NMI 0x1 0xfff. mmisc_ctl.


mtvt2



- ECLIC .



CMMON-CODE-ENTRY 31:2 mtvt2.MTVT2EN=1, - ECLIC .
1 0
MTVT2EN 0 mtvt2. 0x0: - ECLIC mtvec. 0x1: - ECLIC mtvt2.CMMON-CODE-ENTRY


msumb



Bumblebee, , .



31:10 0
PTYP 9:8 . 0x0: , 0x1: 0x2: 0x3: NMI
TYP 7:6 . 0x0: , 0x1: x02: 0x3: NMI
5:0 0


mstatus



mstatus (hart). .



mstatus
SD 31 SD โ€” , , FS XS Dirty , . : SD = (((FS == 0x3)) or (DS == 0x3)). SD , , FPU
XS 16:15 XS , CSR . 0x0: โ€” (Off) , , 0x1: (Initial) , 0x2: (Clear) , . 0x3: (Dirty) .
FS 13:14 XS FPU, (f0-f31) CSR . 0x0: โ€” (Off) FPU , FPU , 0x1: (Initial) , 0x2 โ€” (Clear) , . 0x3: (Dirty) .
MPP 11:12 . 0x0: , 0x1: , 0x3: โ€”
MPIE 7 MIE .
MIE 3 0x0: โ€” . 0x1: โ€” .


mmisc_ctl



, mnvec, NMI.



โ€” 9 (NMI_CAUSE_FF). , 9 โ€” , .



, NMI_CAUSE_FF( 9) 0x0, mnvec . 0x1, mnvec , mtvec, .. NMI , 0xFFF.



mepc



. . pc.

, RTOS .



mtvec



. , , mmisc_ctl



, . .





, 3 , .



  • ( ),
  • NMI( ),
  • ( ).


-.





. Bumblebee :



/ /
0 PC . ( ).
1
2
3 RISC-V BREAK. . , .
4 () Bumblebee , .
5
6 () Bumblebee , .
7
8 ( ecall) RISC-V ECALL. . .
11 ( ecall) RISC-V ECALL. . .


:





โ€” . RISC-V PLIC(Platform-Level Interrupt Controller) CLIC (Core-Local Interrupt Controller). PLIC , CLIC



PLIC mie and mip, RISC-V. , Linux.



CLIC. CLIC.



PLIC CLIC. CSR mtvec . (00b) PLIC - .



CLIC 11b. GD32VF103 ECLIC ( ) โ€” CLIC,



:

4096 , , . , 19 , . ECLIC .



:

, / , , , , .



. , . 87 .



87 GD32VF103
3 CLIC_INT_SFT 0x0000_000C
7 CLIC_INT_TMR 0x0000_001C
17 CLIC_INT_BWEI 0x0000_0044
18 CLIC_INT_PMOVI 0x0000_0048
19 WWDGT interrupt 0x0000_004C
20 LVD from EXTI interrupt 0x0000_0050
21 Tamper interrupt 0x0000_0054
22 RTC global interrupt 0x0000_0058
23 FMC global interrupt 0x0000_005C
24 RCU global interrupt 0x0000_0060
25 EXTI Line0 interrupt 0x0000_0064
26 EXTI Line1 interrupt 0x0000_0068
27 EXTI Line2 interrupt 0x0000_006C
28 EXTI Line3 interrupt 0x0000_0070
29 EXTI Line4 interrupt 0x0000_0074
30 DMA0 channel0 global interrupt 0x0000_0078
31 DMA0 channel1 global interrupt 0x0000_007C
32 DMA0 channel2 global interrupt 0x0000_0080
33 DMA0 channel3 global interrupt 0x0000_0084
34 DMA0 channel4 global interrupt 0x0000_0088
35 DMA0 channel5 global interrupt 0x0000_008C
36 DMA0 channel6 global interrupt 0x0000_0090
37 ADC0 and ADC1 global interrupt 0x0000_0094
38 CAN0 TX interrupts 0x0000_0098
39 CAN0 RX0 interrupts 0x0000_009C
40 CAN0 RX1 interrupts 0x0000_00A0
41 CAN0 EWMC interrupts 0x0000_00A4
42 EXTI line[9:5] interrupts 0x0000_00A8
43 TIMER0 break interrupt 0x0000_00AC
44 TIMER0 update interrupt 0x0000_00B0
45 TIMER0 trigger and channel commutation interrupts 0x0000_00B4
46 TIMER0 channel capture compare interrupt 0x0000_00B8
47 TIMER1 global interrupt 0x0000_00BC
48 TIMER2 global interrupt 0x0000_00C0
49 TIMER3 global interrupt 0x0000_00C4
50 I2C0 event interrupt 0x0000_00C8
51 I2C0 error interrupt 0x0000_00CC
52 I2C1 event interrupt 0x0000_00D0
53 I2C1 error interrupt 0x0000_00D4
54 SPI0 global interrupt 0x0000_00D8
55 SPI1 global interrupt 0x0000_00DC
56 USART0 global interrupt 0x0000_00E0
57 USART1 global interrupt 0x0000_00E4
58 USART2 global interrupt 0x0000_00E8
59 EXTI line[15:10] interrupts 0x0000_00EC
60 RTC alarm from EXTI interrupt 0x0000_00F0
61 USBFS wakeup from EXTI interrupt 0x0000_00F4
62 Reserved 0x0000_00F8
63 Reserved 0x0000_00FC
64 Reserved 0x0000_0100
65 Reserved 0x0000_0104
66 Reserved 0x0000_0108
67 Reserved 0x0000_010C
68 Reserved 0x0000_0110
69 TIMER4 global interrupt 0x0000_0114
70 SPI2 global interrupt 0x0000_0118
71 UART3 global interrupt 0x0000_011C
72 UART4 global interrupt 0x0000_0120
73 TIMER5 global interrupt 0x0000_0124
74 TIMER6 global interrupt 0x0000_0128
75 DMA1 channel0 global interrupt 0x0000_012C
76 DMA1 channel1 global interrupt 0x0000_0130
77 DMA1 channel2 global interrupt 0x0000_0134
78 DMA1 channel3 global interrupt 0x0000_0138
79 DMA1 channel4 global interrupt 0x0000_013C
80 Reserved 0x0000_0140
81 Reserved 0x0000_0144
82 CAN1 TX interrupt 0x0000_0148
83 CAN1 RX0 interrupt 0x0000_014C
84 CAN1 RX1 interrupt 0x0000_0150
85 CAN1 EWMC interrupt 0x0000_0154
86 USBFS global interrupt 0x0000_0158




. , //NMI , :





  • CSR

    • mcause
    • mepc
    • mstatus
    • mintstatus
  • PC , โ€” , NMI. .


, .



โ€” , .





, .





, :



  • , PC , mepc
  • CSR :

    • mstatus
    • mcause
    • mintstatus
  • , , .


.



RISC-V stacking unstacking CortexM , , 16 .



. โ€” , - โ€” . (, Interrupt Tail-Chaining) .



, , - . .





ECLIC โ€” , . . , (- ). , , , . , . -.





, ( CortexM).



-



- . .. .



CLICINTATTR[i] SHV. 0 โ€” - , .. , , mtvec mtvt2, .



, . , mcause โ€” EXCCODE.



ECLIC



. 7 , , i โ€” . .. 87 clicintip, 87 clicintie, 87 clicintattr 87 clicintctl, .



0x0000 RW cliccfg 8-bit
0x0004 R clicinfo 32-bit
0x000b RW mth 8-bit
0x1000+4*i RW clicintip[i] 8-bit
0x1001+4*i RW clicintie[i] 8-bit
0x1002+4*i RW clicintattr[i] 8-bit
0x1003+4*i RW clicintctl[i] 8-bit


, , โ€ฆ



MTH



, . , , , - , , .





, clicintctl[i].



โ€” mth. 8- , .



CLICINTCTL[i]



. , ( ), CLICCFG , โ€” . CLICINFO CLICINTCTLBITS.



CLICCFG



. , . , .





? nlbits , .



7 R N/A , 0
nmbits 6:5 R N/A . 0: .
nlbits 4:1 RW 0 clicintctl[i]. .. 4, clicintctl[i] 4 , . 2 8.
nvbits 0 R N/A 1: . , 0


CLICINFO





31:25 R N/A , 0
CLICINTCTLBITS 24:21 R N/A clicintctl[i].
VERSION 20:13 R N/A .
NUM_INTERRUPT 12:0 R N/A , .


CLICINTIP[i]



. i โ€” . 87 , 87 .



7:1 RO N/A , 0
IP 0 RW 0 . 1 โ€” . , . .


CLICINTIE[i]



. 87.



7:1 RO N/A , 0
IE 0 RW 0 1 โ€”


CLICINTATTR[i]



. , . , , . 87.



7:6 R N/A , 11b
5:3 R N/A , 0
TRIG 2:1 RW 0 00b 10b: . 01b: . 11b: .
SHV 0 RW 0 0x0: . 0x1: .


, , โ€ฆ .





โ€” CortexM, . , (hart). mtime mtimecmp.

, CSR. , , .



. mtime 0xd1000000, mtimecmp 0xd1000008.



64 . :



  • mtime โ€”
  • mtimecmp โ€” . mtime mtimecmp CLICINTIP[7].


, , . , - , .



Writes to mtime and mtimecmp are guaranteed to be reflected in MTIP eventually, but not necessarily immediately.



A spurious timer interrupt might occur if an interrupt handler increments mtimecmp then immediately returns, because MTIP might not yet have fallen in the interim. All software should be

written to assume this event is possible, but most software should assume this event is extremely unlikely. It is almost always more performant to incur an occasional spurious timer interrupt than to poll MTIP until it falls.

, mtimecmp, mtime 0 , .



msip โ€” . . RTOS , .



7:1 RO N/A , 0
MSIP 0 RW 0 1 โ€”




STM32, , . .



, Port control register 0 (GPIOx_CTL0, x=A..E).



Port output control register (GPIOx_OCTL, x=A..E), , .



svd , STM32. , .





โ€ฆ , , . . , . , ECLIC - .



, , mcause, mepc msubm, , , , . .



, , __interrupt, , , mret .



, stackless ++ โ€ฆ .



, .



__interrupt void IrqEntry()
{
  const auto mcause = CSR::MCAUSE::Get(); 
  const auto mepc = CSR::MEPC::Get(); 
  const auto msubm = CSRCUSTOM::MSUBM::Get(); 
  //    mcause
  const auto exceptionCode =  mcause & 0xFFF ; 
  //   
  NonVectoredInt::HandleInterrupt(exceptionCode); 

  __disable_interrupt();
  CSR::MCAUSE::Write(mcause); 
  CSR::MEPC::Write(mepc); 
  CSRCUSTOM::MSUBM::Write(msubm) ; 
}


-, ( , __interrupt), .



-, CSR , , , .



-, . , ( IAR).



namespace NonVectoredInt
{
  static void  HandleInterrupt(std::uint32_t interruptId)
  {
    // ,        
    assert(interruptId < InterruptVectorTable.size());
    //         
    tInterruptFunction fp = InterruptVectorTable[interruptId];
    if (fp != nullptr)
    {
      fp(); //  
    }
  }
} ;


, .



using tInterruptFunction = void(*)() ;

inline constexpr std::array<tInterruptFunction,87> InterruptVectorTable
{
  nullptr,
  nullptr,
  nullptr,
  DummyModule::HandleInterrupt,// 
  nullptr,
  nullptr,
  nullptr,
  SystemTimer::HandleInterrupt, //      
  nullptr,
  nullptr,
  nullptr,
  nullptr,
  nullptr,
  nullptr,
  nullptr,
  nullptr,
  nullptr,
  DummyModule::HandleInterrupt,//eclic_bwei_handler,
  DummyModule::HandleInterrupt, //eclic_pmovi_handler,
  DummyModule::HandleInterrupt, //WWDGT_IRQHandler,
  DummyModule::HandleInterrupt,//LVD_IRQHandler,
....
} ;


, 7 , , , SystemTimer::HandleInterrupt



struct SystemTimer
{
    static void HandleInterrupt()
    {
        auto mtime = MACHINETIMER::MTIME::Get() - 
                     MACHINETIMER::MTIMECMP::Get();
        MACHINETIMER::MTIME::Write(mtime);
        AppTimerService::OnSystemTick() ;        
    }
};


OnSystemTick() . , NMI.



, "". , 12.



inline constexpr std::array<tInterruptFunction,12> ExceptionVectorTable
{
  DummyModule::HandleInterrupt,  //0 - Instruction address misaligned
  DummyModule::HandleInterrupt,  //1 - Instruction access fault
  DummyModule::HandleInterrupt,  //2 - Illegal instruction
  DummyModule::HandleInterrupt,  //3 - Breakpoint
  DummyModule::HandleInterrupt,  //4 - Load address misaligned
  DummyModule::HandleInterrupt,  //5 - Load access fault
  DummyModule::HandleInterrupt,  //6 - Store/AMO address  misaligned
  DummyModule::HandleInterrupt,  //7 - Store/AMO access fault
  EnvironmentCall::HandleInterrupt,  //8 - Environment call from  U-mode
  nullptr,
  nullptr,
  EnvironmentCall::HandleInterrupt,  //11 - Environment call from  M-mode
};


namespace NonVectoredInt
{    
  static void  HandleException(std::uint32_t exceptiontId)
  {
    assert(exceptiontId < ExceptionVectorTable.size());
    tInterruptFunction fp = ExceptionVectorTable[exceptiontId];
    if (fp != nullptr)
    {
      fp();
    }
  }
} ;


NMI . โ€” 0xFFF.



__interrupt void ExceptionEntry()
{
  const auto mcause = CSR::MCAUSE::Get();
  const auto mepc = CSR::MEPC::Get();
  const auto msubm = CSRCUSTOM::MSUBM::Get();

  const auto exceptionCode =  mcause & 0xFFF ;
  if (exceptionCode != 0xFFF) //   NMI
  {
    NonVectoredInt::HandleException(exceptionCode);
  } else
  {
    DummyModule::HandleInterrupt() ; //    NMI
  }

  __disable_interrupt();
  CSR::MCAUSE::Write(mcause); 
  CSR::MEPC::Write(mepc); 
  CSRCUSTOM::MSUBM::Write(msubm) ; 
}


, NMI , , mtvec, NMI 0xFFF, MMISC_CTL .



 //     NMI  , 
 //     mtvec.   NMI  0xFFF
 CSRCUSTOM::MMISC_CTL::NMI_CAUSE_FFF::MnvecIsMtvecNmiIsFFF::Set();




//     . 
// ,       MTVT2
CSRCUSTOM::MTVT2::Write(
           CSRCUSTOM::MTVT2::MTVT2EN::Mtvt2IsTrapAddress::Value |
           reinterpret_cast<std::uintptr_t>(&NonVectoredInt::IrqEntry));


a ECLIC NMI mtvec



 //      ECLIC      
 CSR::MTVEC::Write(
      CSR::MTVEC::MODE::Eclic::Value |
      reinterpret_cast<std::uintptr_t>(&NonVectoredInt::ExceptionEntry));        


โ€ฆ .



extern "C"
{
int __low_level_init(void)
{
 {
   CriticalSection cs;        
   //     NMI   , 
   //     mtvec.   NMI  0xFFF
   CSRCUSTOM::MMISC_CTL::NMI_CAUSE_FFF::MnvecIsMtvecNmiIsFFF::Set(); 

   //     . 
   // ,       MTVT2
   CSRCUSTOM::MTVT2::Write(
              CSRCUSTOM::MTVT2::MTVT2EN::Mtvt2IsTrapAddress::Value |
              reinterpret_cast<std::uintptr_t>(&NonVectoredInt::IrqEntry));

   //      ECLIC 
   //      
   CSR::MTVEC::Write(CSR::MTVEC::MODE::Eclic::Value |
       reinterpret_cast<std::uintptr_t>(&NonVectoredInt::ExceptionEntry));

   //       mycycle_minstret
   CSRCUSTOM::MCOUNTINHIBITPack<CSRCUSTOM::MCOUNTINHIBIT::IR::MinstretOn,
                CSRCUSTOM::MCOUNTINHIBIT::CY::McyclesOn
                >::Set();
   }
}


.



//         
//   CLICINTCTL_7.   3 
ECLIC::CLICCFG::NLBITS::MaxBitsForLevel3::Set();
//     0
ECLIC::MTH::Write<0U>();
//      
ECLIC::CLICINTATTR_7::SHV::NonVectored::Set();
//    1,    
ECLIC::CLICINTCTL_7::Write<
       1U << (8U - ECLIC::CLICCFG::NLBITS::MaxBitsForLevel3::Value)>();

//  .      1 .     
MACHINETIMER::MTIMECMP::MTIMEField::Value<SystemTimerPeriod>::Write() ;
MACHINETIMER::MTIME::Write<0U>();

//   -   7
ECLIC::CLICINTIE_7::IE::Enable::Write();

//   
CSR::MSTATUSPack<CSR::MSTATUS::MIE::InterruptEnabled>::SetValueBitsAtomic();


, . GPIOC.7 GPIOB.6



RCU::APB2EN::PCEN::Enable::Set(); 
RCU::APB2EN::PBEN::Enable::Set();
GPIOC::CTL0::CTLMD7::GpioOutputPushPull50Mhz::Set();
GPIOB::CTL0::CTLMD6::GpioOutputPushPull50Mhz::Set();


#include "gpiocregisters.hpp"
#include "gpiobregisters.hpp"
#include "rcuregisters.hpp"  //for RCU
#include "csrregisters.hpp" //for CSR
#include "eclicregisters.hpp" // for ECLIC
#include "machinetimerregisters.hpp"
#include "systemconfig.hpp" // for SystemTimerPeriod
#include "criticalsection.hpp" // for CriticalSection
#include "csrcustomregisters.hpp"
#include "vectortable.hpp" //for InterruptVectorTable

namespace NonVectoredInt
{
    static void HandleInterrupt(std::uint32_t interruptId)
    {
        assert(interruptId < InterruptVectorTable.size());
        tInterruptFunction fp = InterruptVectorTable[interruptId];
        if (fp != nullptr)
        {
            fp();
        }
    }

    static void HandleException(std::uint32_t exceptiontId)
    {
        assert(exceptiontId < ExceptionVectorTable.size());
        tInterruptFunction fp = ExceptionVectorTable[exceptiontId];
        if (fp != nullptr)
        {
            fp();
        }
    }

    __interrupt void ExceptionEntry()
    {
        const auto mcause = CSR::MCAUSE::Get();
        const auto mepc = CSR::MEPC::Get();
        const auto msubm = CSRCUSTOM::MSUBM::Get();
        const auto exceptionCode = mcause & 0xFFF;

        if (exceptionCode != 0xFFF) // if not NMI
        {
            NonVectoredInt::HandleException(exceptionCode);
        }
        else
        {
            DummyModule::HandleInterrupt(); // for NMI handling
        }

        __disable_interrupt();
        CSR::MCAUSE::Write(mcause);
        CSR::MEPC::Write(mepc);
        CSRCUSTOM::MSUBM::Write(msubm);
    }

    __interrupt void IrqEntry()
    {
        const auto mcause = CSR::MCAUSE::Get();
        const auto mepc = CSR::MEPC::Get();
        const auto msubm = CSRCUSTOM::MSUBM::Get();
        const auto exceptionCode = mcause & 0xFFF;

        NonVectoredInt::HandleInterrupt(exceptionCode);

        __disable_interrupt();
        CSR::MCAUSE::Write(mcause);
        CSR::MEPC::Write(mepc);
        CSRCUSTOM::MSUBM::Write(msubm);
    }
}

extern "C"
{
int __low_level_init(void)
{
    {
        CriticalSection cs;

        //     NMI   , 
        //     mtvec.   NMI  0xFFF
        CSRCUSTOM::MMISC_CTL::NMI_CAUSE_FFF::MnvecIsMtvecNmiIsFFF::Set(); 

        //     . 
        // ,       MTVT2
        CSRCUSTOM::MTVT2::Write(
                   CSRCUSTOM::MTVT2::MTVT2EN::Mtvt2IsTrapAddress::Value |
                   reinterpret_cast<std::uintptr_t>(&NonVectoredInt::IrqEntry));

        //      ECLIC   
        //    
        CSR::MTVEC::Write(
                    CSR::MTVEC::MODE::Eclic::Value |
                    reinterpret_cast<std::uintptr_t>(&NonVectoredInt::ExceptionEntry));

        //       mycycle_minstret
        CSRCUSTOM::MCOUNTINHIBITPack<CSRCUSTOM::MCOUNTINHIBIT::IR::MinstretOn,
                                     CSRCUSTOM::MCOUNTINHIBIT::CY::McyclesOn
                                    >::Set();
    }
    ECLIC::CLICCFG::NLBITS::MaxBitsForLevel3::Set();

    //     0
     ECLIC::MTH::Write(0U);
    //      
     ECLIC::CLICINTATTR_7::SHV::NonVectored::Set();

    //    1,    
    ECLIC::CLICINTCTL_7::Write<
           1U << (8U - ECLIC::CLICCFG::NLBITS::MaxBitsForLevel3::Value)>();

    MACHINETIMER::MTIMECMP::MTIMECMPField::Value<SystemTimerPeriod>::Write() ;
    MACHINETIMER::MTIME::Write<0U>();

    //   -   7
    ECLIC::CLICINTIE_7::IE::Enable::Write();

    //Enable machine interrupt
    CSR::MSTATUSPack<CSR::MSTATUS::MIE::InterruptEnabled>::SetValueBits();

    RCU::APB2ENPack<RCU::APB2EN::PCEN::Enable,
                    RCU::APB2EN::PBEN::Enable>::Set();
    GPIOC::CTL0::CTLMD7::GpioOutputPushPull50Mhz::Set();
    GPIOB::CTL0::CTLMD6::GpioOutputPushPull50Mhz::Set();

    return 1;
}
}

int main()
{
    while (true)
    {
        asm volatile(" ");
    }

    return 0;
}


.

, . . OnTick()



template<typename ...Timers>
struct TimerService
{
  static void OnSystemTick()
  {
    (Timers::OnTick(), ...);
  }
};


โ€” , OnTimeout(), .



template <std::uint32_t TimerFrequency, std::uint32_t msPeriod, typename ... Subscribers>
class SoftwareTimer 
{
public:
  static void OnTick()
  {
    --ticksRemain ;
    if (ticksRemain == 0U)
    {       
      ticksRemain = ticksReload ;
      (Subscribers::OnTimeout(),...) ;
    }
  }

private:

  static constexpr std::uint32_t msInSec = 1000UL ;
  static constexpr std::uint32_t ticksReload =  
         static_cast<std::uint32_t>((msPeriod * TimerFrequency) / msInSec) ;

  static inline volatile std::uint32_t ticksRemain = ticksReload;
} ;


, 100 ms, 200ms



//   
using Led1Timer = SoftwareTimer<SystemTimerPeriod, 100UL, Led1> ;
using Led2Timer = SoftwareTimer<SystemTimerPeriod, 200UL, Led2> ;
// 
using AppTimerService = TimerService<Led1Timer, Led2Timer> ; 


, Led1 Led2 โ€” GPIOB.6 GPIOC.7



template<typename Pin>
struct Leds
{
  static void OnTimeout()
  {
    //  ,      
    Pin::Toggle(); 
  }
};

template<typename Port, uint32_t num>
struct DummyPin
{
  static void Toggle()
  {
    Port::OCTL::Toggle(1 << num);
  }
};

using Led1 = Leds<DummyPin<GPIOC, 7>>;
using Led2 = Leds<DummyPin<GPIOB, 6>>;


main, ,



int main()
{
  while (true)
  {
    asm volatile(" ");
  }
  return 0;
}


, :



. 1 , OnTick() . , , OnTimeout(), .



, - , RISC-V.



, IAR 1.31 for RISC-V.





ZY RISC-V left a double impression, on the one hand it is a very expandable and flexible thing, you can do anything, on the other hand, there is a risk that the producers will be brought into the side, and everyone will fence their own garden. I hope that all the specifications will be approved soon and everything will settle down.



Corrections, by instruction Ryppka replaced the NonVectoredInt structure with namespace




All Articles