The svc mode can separate SDK (API) and Application, and can also omit a lot of repeated code. For example, if USB is used in bootload and you want to reuse it in application, you can use svc
Principle: Similar to software interrupt, a software interrupt source is added to indicate the function interface to be called through the interrupt source
The source program is as follows:
#ifndef NRF_SVC__
#define NRF_SVC__
#ifdef SVCALL_AS_NORMAL_FUNCTION
#define SVCALL(number, return_type, signature) return_type signature
#else
#ifndef SVCALL
#if defined (__CC_ARM)
#define SVCALL(number, return_type, signature) return_type __svc(number) signature
#elif defined (__GNUC__)
#define SVCALL(number, return_type, signature)
_Pragma("GCC diagnostic ignored "-Wunused-function"")
_Pragma("GCC diagnostic push")
_Pragma("GCC diagnostic ignored "-Wreturn-type"")
__attribute__((naked)) static return_type signature
{
__asm(
"svc %0n"
"bx r14" : : "I" (number) : "r0"
);
}
_Pragma("GCC diagnostic pop")
#elif defined (__ICCARM__)
#define PRAGMA(x) _Pragma(#x)
#define SVCALL(number, return_type, signature)
PRAGMA(swi_number = number)
__swi return_type signature;
#else
#define SVCALL(number, return_type, signature) return_type signature
#endif
#endif // SVCALL
#endif // SVCALL_AS_NORMAL_FUNCTION
#endif // NRF_SVC__
void C_SVC_Handler(uint8_t svc_num, uint32_t * p_svc_args)
{
switch (svc_num)
{
case NRF_SEC_SVC_HASH:
p_svc_args[0] = nrf_sec_hash((nrf_sec_data_t *) p_svc_args[0],
(uint8_t *) p_svc_args[1],
(nrf_sec_hash_func_t) p_svc_args[2]);
break;
case NRF_SEC_SVC_VERIFY:
p_svc_args[0] = nrf_sec_verify((nrf_sec_data_t *) p_svc_args[0],
(nrf_sec_ecc_point_t *) p_svc_args[1],
(nrf_sec_ecc_signature_t *) p_svc_args[2],
(nrf_sec_algo_t) p_svc_args[3]);
break;
default:
p_svc_args[0] = NRF_ERROR_SVC_HANDLER_MISSING;
break;
}
}
#if defined ( __CC_ARM )
__asm void SVC_Handler(void)
{
EXC_RETURN_CMD_PSP EQU 0xFFFFFFFD ; EXC_RETURN using PSP for ARM Cortex. If Link register contains this value it indicates the PSP was used before the SVC, otherwise the MSP was used.
IMPORT C_SVC_Handler
LDR R0, =EXC_RETURN_CMD_PSP ; Load the EXC_RETURN into R0 to be able to compare against LR to determine stack pointer used.
CMP R0, LR ; Compare the link register with R0. If equal then PSP was used, otherwise MSP was used before SVC.
BNE UseMSP ; Branch to code fetching SVC arguments using MSP.
MRS R1, PSP ; Move PSP into R1.
B Call_C_SVC_Handler ; Branch to Call_C_SVC_Handler below.
UseMSP
MRS R1, MSP ; MSP was used, therefore Move MSP into R1.
Call_C_SVC_Handler
LDR R0, [R1, #24] ; The arguments for the SVC was stacked. R1 contains Stack Pointer, the values stacked before SVC are R0, R1, R2, R3, R12, LR, PC (Return address), xPSR.
; R1 contains current SP so the PC of the stacked frame is at SP + 6 words (24 bytes). We load the PC into R0.
SUBS R0, #2 ; The PC before the SVC is in R0. We subtract 2 to get the address prior to the instruction executed where the SVC number is located.
LDRB R0, [R0] ; SVC instruction low octet: Load the byte at the address before the PC to fetch the SVC number.
LDR R2, =C_SVC_Handler ; Load address of C implementation of SVC handler.
BX R2 ; Branch to C implementation of SVC handler. R0 is now the SVC number, R1 is the StackPointer where the arguments (R0-R3) of the original SVC are located.
ALIGN
}
#elif defined ( __GNUC__ )
void __attribute__ (( naked )) SVC_Handler(void)
{
const uint32_t exc_return = 0xFFFFFFFD; // EXC_RETURN using PSP for ARM Cortex. If Link register contains this value it indicates the PSP was used before the SVC, otherwise the MSP was used.
__asm volatile(
"cmp lr, %0tn" // Compare the link register with argument 0 (%0), which is exc_return. If equal then PSP was used, otherwise MSP was used before SVC.
"bne UseMSPtn" // Branch to code fetching SVC arguments using MSP.
"mrs r1, psptn" // Move PSP into R1.
"b Call_C_SVC_Handlertn" // Branch to Call_C_SVC_Handler below.
"UseMSP: tn" //
"mrs r1, msptn" // MSP was used, therefore Move MSP into R1.
"Call_C_SVC_Handler: tn" //
"ldr r0, [r1, #24]tn" // The arguments for the SVC was stacked. R1 contains Stack Pointer, the values stacked before SVC are R0, R1, R2, R3, R12, LR, PC (Return address), xPSR.
// R1 contains current SP so the PC of the stacked frame is at SP + 6 words (24 bytes). We load the PC into R0.
"sub r0, r0, #2tn" // The PC before the SVC is in R0. We subtract 2 to get the address prior to the instruction executed where the SVC number is located.
"ldrb r0, [r0]tn" // SVC instruction low octet: Load the byte at the address before the PC to fetch the SVC number.
"bx %1tn" // Branch to C implementation of SVC handler, argument 1 (%1). R0 is now the SVC number, R1 is the StackPointer where the arguments (R0-R3) of the original SVC are located.
".aligntn"
:: "r" (exc_return), "r" (C_SVC_Handler) // Argument list for the gcc assembly. exc_return is %0, C_SVC_Handler is %1.
: "r0", "r1" // List of register maintained manually.
);
}
#else
#error Compiler not supported.
#endif
__asm void SVCHandler(void)
{
IMPORT SVCHandler_main
TST lr, #4
ITE EQ
MRSEQ R0, MSP
MRSNE R0, PSP
B SVCHandler_main
}
void SVCHandler_main(unsigned int * svc_args)
{
unsigned int svc_number;
/*
* Stack contains:
* R0, R1, R2, R3, R12, R14, the return address and xPSR
* First argument (R0) is svc_args[0]
*/
svc_number = ((char *)svc_args[6])[-2];
switch(svc_number)
{
case SVC_00:
/* Handle SVC 00 */
break;
case SVC_01:
/* Handle SVC 01 */
break;
default:
/* Unknown SVC */
break;
}
}
#define SVC_00 0x00
#define SVC_01 0x01
void __svc(SVC_00) svc_zero(const char *string);
void __svc(SVC_01) svc_one(const char *string);
int call_system_func(void)
{
svc_zero("String to pass to SVC handler zero");
svc_one("String to pass to a different OS function");
}
In ARM state, it is 0.
Both the ARM and Thumb instruction sets have SVC instructions. When calling SVC in Thumb state, the following must be considered:
The address of the instruction is at lr–2, not at lr–4.
The instruction itself is 16-bit and thus requires a half-word load, see Figure 6.3.
In ARM state, the SVC number is stored as 8 bits instead of 24 bits.
Example 6.8. SVC handler
PRESERVE8
AREA SVC_Area, CODE, READONLY
EXPORT SVC_Handler IMPORT C_SVC_Handler
T_bit EQU 0x20 ; Thumb bit (5) of CPSR/SPSR.
SVC_Handler
STMFD sp!, {r0-r3, r12, lr} ; Store registers
MOV r1, sp ; Set pointer to parameters
MRS r0, spsr ; Get spsr
STMFD sp!, {r0, r3} ; Store spsr onto stack and another
; register to maintain 8-byte-aligned stack
TST r0, #T_bit ; Occurred in Thumb state?
LDRNEH r0, [lr,#-2] ; Yes: Load halfword and...
BICNE r0, r0, #0xFF00 ; ...extract comment field
LDREQ r0, [lr,#-4] ; No: Load word and...
BICEQ r0, r0, #0xFF000000 ; ...extract comment field
; r0 now contains SVC number
; r1 now contains pointer to stacked registers
BL C_SVC_Handler ; Call main part of handler
LDMFD sp!, {r0, r3} ; Get spsr from stack
MSR SPSR_cxsf, r0 ; Restore spsr
LDMFD sp!, {r0-r3, r12, pc}^ ; Restore registers and return
END
Previous article:AD9850 Function Signal Generator Production
Next article:Pedometer algorithm based on ARM7 ADUC7020+MPU6050
Recommended ReadingLatest update time:2024-11-16 13:29
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- [National Technology N32G457 Review] RT_Thread Studio drives CAN and STM32F103VE communication
- 16 Years of Taiwanese New Year
- 【Project Source Code】Digital Signal Processing Learning——Mixer
- Chapter 4: Use of Timers and PWM
- Ask an outrageous question, why do we need to use a resistor to form a discharge path for the capacitor to discharge?
- An annular solar eclipse is coming. Have those in the annular eclipse zone seen it?
- How to set differential traces for high-speed USB positive and negative in AD16 PCB
- 【Infineon XENSIV PAS CO2 sensor】Official data study
- Looking for a brushless gate driver design
- Voltage boost circuit and electrode detachment detection circuit