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
, , - .
- ISA RISC-V RISC-V :
ISA
ISA - GD32VF103. , . GD32VF103CBT6 โ GD32 RISC-V Microcontroller
- GD32VF103 Bumblebee, Nuclei processor core
- ECLIC.
- . .
- Nuclei RISC-V , IAR n200 drivers
- GigaDevice, IAR
- FreeRTOS uCOS II n200 sdk
- , 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 . , . .
, , , :
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 ) ( ) , .
, , , :
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 .
.
, , . โ .
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). .
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 .
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.
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