Hoy hablaremos sobre el de moda: sobre el microcontrolador RIS-V. Hace tiempo que quería familiarizarme con este kernel y esperé a que apareciera algo similar a STM32 y ahora esperé, conocí, el GigaDevice chino, GD32V.
La infraestructura de este microcontrolador no es tan extensa como la del STM32, pero hay todo lo que necesita para empezar a utilizarlo. Afortunadamente, las placas de depuración se pueden pedir en alikexpress, por ejemplo, aquí: Longan Nano GD32VF103CBT6 RISC-V MCU
Los chinos están promoviendo el entorno de desarrollo Platform IO para este microcontrolador, que se puede entregar como una extensión para Visual Studio Code. Pero no lo usaremos, esto no está de acuerdo con los conceptos de ingeniería, somos ingenieros y queremos resolverlo nosotros mismos. Intentemos ejecutar el tablero en IAR, escribiendo todo desde cero.
Por cierto, IAR está distribuyendo un kit de depuración (placa de depuración + depurador I-Jet + licencia completa de 30 días) Kit de evaluación IAR RISC-V GD32V. Aquí puede dejar una solicitud de herramientas de desarrollo . No estoy seguro de si enviarán el kit a todos, pero lo recibimos en 5 días. ¡Agradéceles por eso!
Bueno, quienes estén interesados, bienvenidos bajo el corte.
Introducción
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 dejó una doble impresión, por un lado es una cosa muy expandible y flexible, puedes hacer cualquier cosa, por otro lado, existe el riesgo de que los productores sean llevados al costado y todos cercarán su propio jardín. Espero que pronto se aprueben todas las especificaciones y todo se calme.
Correcciones, por instrucción Ryppka reemplazó la estructura NonVectoredInt con espacio de nombres