2024-12-23 04:39 AM - last edited on 2024-12-23 08:16 AM by Andrew Neil
Hello,
I have simple project in assembly and can't understand/explain why one time delay loop takes much longer than the other one (LEDs should be blinking one on, second off and vice versa). But one such state takes approx. 5 times more time than the other, although loops seem to be written equally demanding
Maybe I'm doing something obvious wrong. BTW, we have also spotted such behaviour also on some other projects - one loop taking much more time, although it should take same time. I've tried running the code from Flash or RAM and same happens in both situations.
Any hint, advice? Thanks...
Short description: main loop has two identical SW delay loops, in between on/off state is written to 3 variables. SysTick Handler is triggered each ms and reads the state of variables and reflects to real outputs. Green LED is on approx. 5 times longer that red one.
/*
* Main.s
*/
.syntax unified
.cpu cortex-m7
.thumb
///////////////////////////////////////////////////////////////////////////////
// Definitions
///////////////////////////////////////////////////////////////////////////////
// Definitions section. Define all the registers and
// constants here for code readability.
// Constants
.equ LEDDELAY, 6400
// For LOOPTC Software delay
// By default 64MHz internal HSI clock is enabled
// Internal loop takes N cycles
// Register Addresses
// You can find the base addresses for all peripherals from Memory Map section 2.3.2
// RM0433 on page 131. Then the offsets can be found on their relevant sections.
// RCC base address is 0x58024400
// AHB4ENR register offset is 0xE0
.equ RCC_AHB4ENR, 0x580244E0 // RCC AHB4 peripheral clock reg
// GPIOA base address is 0x58020000
.equ GPIOA_BASE, 0x58020000 // GPIOI base address)
// GPIOI base address is 0x58022000
.equ GPIOI_BASE, 0x58022000 // GPIOI base address)
// GPIOJ base address is 0x58022000
.equ GPIOJ_BASE, 0x58022400 // GPIOJ base address)
// MODER register offset is 0x00
.equ GPIOx_MODER, 0x00 // GPIOx port mode register
// ODR register offset is 0x14
.equ GPIOx_ODR, 0x14 // GPIOx output data register
// BSSR register offset is 0x18
.equ GPIOx_BSRR, 0x18 // GPIOx port set/reset register
// Values for BSRR register - pin PI13: LED is on, when GPIO is off (Red)
.equ LED2_OFF, 0x00002000 // Setting pin to 1 -> LED is off
.equ LED2_ON, 0x20000000 // Setting pin to 0 -> LED is on
// Values for BSRR register - pin PJ2: LED is on, when GPIO is off (Green)
.equ LED1_OFF, 0x00000004 // Setting pin to 1 -> LED is off
.equ LED1_ON, 0x00040000 // Setting pin to 0 -> LED is on
// Values for BSRR register - pin PA3: PA3
.equ PA3_ON, 0x00000008 // Setting pin to 1
.equ PA3_OFF, 0x00080000 // Setting pin to 0
// Vector table offset register definition
// Important for relocated Vector table on running from RAM
.equ VTOR,0xE000ED08
// SysTick Timer definitions
.equ SCS_BASE,0xe000e000
.equ SCS_SYST_CSR,0x10// Control/Status register
.equ SCS_SYST_RVR,0x14// Value to countdown from
.equ SCS_SYST_CVR,0x18// Current value
.equ SYSTICK_RELOAD_1MS, 63999 //1 msec at 64MHz ...
// Start of data section
.data
.align
LED1: .word 0 // LED1 State (Green)
LED2: .word 0 // LED2 State (Red)
PA3: .word 0 // PA3 pin State
// Start of text section
.text
.type main, %function
.global main
.align
main:
bl INIT // Priprava V/I in sistemskih naprav za kontrolo LED diod in PA3
ldr r1,=LED1
ldr r2,=LED2
ldr r3,=PA3
mov r4,#0xff // LED(Pin) On value
mov r5,#0 // LED(Pin) Off value
loop:
str r4,[r1] // Vklop LED1 diode (Green)
str r5,[r2] // Izklop LED2 diode (Red)
str r4,[r3] // Vklop PA3
// bl WRITEOUT // Prenesi na prikljucke
@ delay half cycle
mov r0,#500
ZAN1: ldr r6, =LEDDELAY
ZAN1n: subs r6, r6,#1
bne ZAN1n
subs r0,r0,#1
bne ZAN1
str r5,[r1] // Izklop LED1 diode (Green)
str r4,[r2] // Vklop LED2 diode (Red)
str r5,[r3] // Izklop PA3
// bl WRITEOUT // Prenesi na prikljucke
@ delay half cycle
mov r0,#500
ZAN2: ldr r6, =LEDDELAY
ZAN2n: subs r6, r6,#1
bne ZAN2n
subs r0,r0,#1
bne ZAN2
b loop // skok na vrstico loop:
__end: b __end
INIT:
push {r0,r1,lr}
bl INIT_IO
// If running code from FLASH comment next 3 lines!!!
ldr r1, =VTOR // Set Vector table addr. to 0x24000000
ldr r0, =0x24000000
str r0, [r1]
bl INIT_TC_PSP // Priprava SysTick časovnika s prek
pop {r0,r1,pc}
INIT_IO:
push {r5, r6, lr}
// Enable GPIOA,I,J Peripheral Clock (bit 8 in AHB4ENR register)
ldr r6, = RCC_AHB4ENR // Load peripheral clock reg address to r6
ldr r5, [r6] // Read its content to r5
orr r5, #0x00000300 // Set bits 8 and 9 to enable GPIOI,J clock
orr r5, #0x00000001 // Set bits 1 to enable GPIOA clock
str r5, [r6] // Store result in peripheral clock register
// Make GPIOA Pin3 as output pin (bits 7:6 in MODER register)
ldr r6, =GPIOA_BASE // Load GPIOA BASE address to r6
ldr r5, [r6,#GPIOx_MODER] // Read GPIOA_MODER content to r5
and r5, #0xFFFFFF3F // Clear bits 7-6 for PA3
orr r5, #0x00000040 // Write 01 to bits 7-6 for PA3
str r5, [r6] // Store result in GPIO MODER register
// Make GPIOI Pin13 as output pin (bits 27:26 in MODER register)
ldr r6, =GPIOI_BASE // Load GPIOI BASE address to r6
ldr r5, [r6,#GPIOx_MODER] // Read GPIOI_MODER content to r5
and r5, #0xF3FFFFFF // Clear bits 27-26 for P13
orr r5, #0x04000000 // Write 01 to bits 27-26 for P13
str r5, [r6] // Store result in GPIO MODER register
// Make GPIOJ Pin2 as output pin (bits 5:4 in MODER register)
ldr r6, =GPIOJ_BASE // Load GPIOJ BASE address to r6
ldr r5, [r6,#GPIOx_MODER] // Read GPIOJ_MODER content to r5
and r5, #0xFFFFFFCF // Clear bits 5-4 for P2
orr r5, #0x00000010 // Write 01 to bits 5-4 for PJ2
str r5, [r6] // Store result in GPIO MODER register
pop {r5, r6, pc}
INIT_TC_PSP:
push {r0, r1, lr}
ldr r1, =SCS_BASE
ldr r0, =SYSTICK_RELOAD_1MS
str r0, [r1, #SCS_SYST_RVR]
mov r0, #0
str r0, [r1, #SCS_SYST_CVR]
mov r0, #0b111 // Set TickInt to 1 as well
str r0, [r1, #SCS_SYST_CSR]
pop {r0, r1, pc}
.global SysTick_Handler
.section .text.SysTick_Handler,"ax",%progbits
.type SysTick_Handler, %function
SysTick_Handler:
push {r3, r4, r5, r6, lr}
// -----------------------------------
// Set LED1 from LED1 variable
ldr r3,=LED1 // Load LED1 value
ldr r4,[r3]
cmp r4,#0
beq L1ON
mov r5, #LED1_OFF
b CONT1
L1ON: mov r5, #LED1_ON
CONT1: // Set GPIOJ Pins through BSRR register
ldr r6, =GPIOJ_BASE // Load GPIOD BASE address to r6
str r5, [r6,#GPIOx_BSRR] // Write to BSRR register
// -----------------------------------
// Set LED2 from LED2 variable
ldr r3,=LED2 // Load LED1 value
ldr r4,[r3]
cmp r4,#0
beq L2ON
mov r5, #LED2_OFF
b CONT2
L2ON: mov r5, #LED2_ON
CONT2: // Set GPIOI Pins through BSRR register
ldr r6, =GPIOI_BASE // Load GPIOD BASE address to r6
str r5, [r6,#GPIOx_BSRR] // Write to BSRR register
// -----------------------------------
// Set PA3 from PA3 variable
ldr r3,=PA3 // Load PA3 value
ldr r4,[r3]
cmp r4,#0
beq L3ON
mov r5, #PA3_OFF
b CONT3
L3ON: mov r5, #PA3_ON
CONT3: // Set GPIOA Pins through BSRR register
ldr r6, =GPIOA_BASE // Load GPIOD BASE address to r6
str r5, [r6,#GPIOx_BSRR] // Write to BSRR register
RET: pop {r3, r4, r5, r6, pc }
2024-12-23 07:30 AM
When checking timing, drive the pins directly so you can scope them. I'm not sure of the value of establishing different beat frequencies from setting variables, and then actioning them on a 1 KHz tick.
You can measure cycles via DWT CYCCNT
Check alignments of branch targets.
Perhaps put the delay in subroutine, to confirm that the same code in the same location has consistent behaviour.