; ARM assembly tutorial for Asmonite, example 21: the clock (Raspberry Pi Pico 2, Arm Cortex-M33).
; Adapted from the ARM-ASM-Tutorial by Niklas Guertler (section "Clock configuration"); see LICENSE.txt.
;
; After a reset the RP2350 runs from its ring oscillator (ROSC), a few MHz that depend on the chip and the
; temperature. The program switches to the 12 MHz crystal of the Pico 2 (XOSC) and from there, with the
; PLL, to 150 MHz, the speed the RP2350 is made for:
;   1. start the XOSC and wait until it is stable
;   2. clk_ref from the XOSC (12 MHz)
;   3. clk_sys back to clk_ref while the PLL changes
;   4. PLL_SYS: 12 MHz / 1 (REFDIV) * 125 (FBDIV) = 1500 MHz (the VCO), / 5 / 2 (POSTDIV1, POSTDIV2) = 150 MHz
;   5. clk_sys from the PLL
; Then a SysTick blink as in 17_systick.asm: the SysTick counts the core clock, so with TIMER_VALUE =
; 15000000 an event is 0.1 s. The program runs in the RAM: the flash would need slower timing at 150 MHz.
.target pico2
.arch thumb
.imagebase 0x20000000

RESETS_BASE         equ 0x40020000
RESETS_RESET_DONE   equ 0x08
RESET_BITS          equ (1 << 6) | (1 << 9)     ; IO_BANK0, PADS_BANK0
IO_BANK0_BASE       equ 0x40028000
GPIO25_CTRL         equ 0xcc
FUNCSEL_SIO         equ 5
PADS_BANK0_BASE     equ 0x40038000
PADS_GPIO25         equ 0x68
PAD_ISO             equ 1 << 8
SIO_BASE            equ 0xd0000000
SIO_GPIO_OUT_SET    equ 0x18
SIO_GPIO_OUT_CLR    equ 0x20
SIO_GPIO_OE_SET     equ 0x38
ALIAS_CLR           equ 0x3000
RESET_PLL_SYS       equ 1 << 14
XOSC_BASE           equ 0x40048000
XOSC_CTRL           equ 0x00
XOSC_STATUS         equ 0x04
XOSC_STARTUP        equ 0x0c
XOSC_RANGE_1_15MHZ  equ 0xaa0                     ; FREQ_RANGE 1 .. 15 MHz
XOSC_ENABLE_1_15MHZ equ (0xfab << 12) | 0xaa0     ; and ENABLE
XOSC_STARTUP_DELAY  equ 282                       ; 282 * 256 cycles of 12 MHz: about 6 ms, as the Pico SDK
                                                  ; (1 ms is too short: the crystal starts slowly)
CLOCKS_BASE         equ 0x40010000
CLK_REF_CTRL        equ 0x30
CLK_REF_SELECTED    equ 0x38
CLK_SYS_CTRL        equ 0x3c
CLK_SYS_SELECTED    equ 0x44
PLL_SYS_BASE        equ 0x40050000
PLL_CS              equ 0x00
PLL_PWR             equ 0x04
PLL_FBDIV_INT       equ 0x08
PLL_PRIM            equ 0x0c
PLL_PWR_PD          equ 1 << 0
PLL_PWR_POSTDIVPD   equ 1 << 3
PLL_PWR_VCOPD       equ 1 << 5
SCS_BASE            equ 0xe000e000
SYST_CSR            equ 0x10
SYST_RVR            equ 0x14
SYST_CVR            equ 0x18
COUNTFLAG           equ 1 << 16
TIMER_VALUE         equ 15000000                  ; 0.1 s at 150 MHz

_start:
    bl   clock_150mhz
    bl   setup_led
    ldr  r0, lit_timer_value
    bl   start_systick
    ldr  r4, lit_sio
    ldr  r5, lit_led_bit
blink_loop:
    str  r5, [r4, #SIO_GPIO_OUT_SET]
    movs r0, #2
    bl   wait_systick
    str  r5, [r4, #SIO_GPIO_OUT_CLR]
    movs r0, #8
    bl   wait_systick
    b    blink_loop

; ---- XOSC, PLL_SYS 150 MHz, clk_sys from it; changes r0 .. r3 ----
clock_150mhz:
    ; 1. the crystal
    ldr  r1, lit_xosc
    movw r0, #XOSC_RANGE_1_15MHZ
    str  r0, [r1, #XOSC_CTRL]   ; the frequency range first
    movw r0, #XOSC_STARTUP_DELAY
    str  r0, [r1, #XOSC_STARTUP]
    ldr  r0, lit_xosc_enable
    str  r0, [r1, #XOSC_CTRL]   ; then on
xosc_wait:
    ldr  r0, [r1, #XOSC_STATUS]
    cmp  r0, #0
    bge  xosc_wait              ; bit 31 STABLE: the word is negative when it is set
    ; 2. clk_ref = XOSC (SRC 2)
    ldr  r1, lit_clocks
    movs r0, #2
    str  r0, [r1, #CLK_REF_CTRL]
ref_wait:
    ldr  r0, [r1, #CLK_REF_SELECTED]
    cmp  r0, #1 << 2            ; one bit for each source: bit 2 = XOSC
    bne  ref_wait
    ; 3. clk_sys = clk_ref (SRC 0) while the PLL changes
    movs r0, #0
    str  r0, [r1, #CLK_SYS_CTRL]
sys_ref_wait:
    ldr  r0, [r1, #CLK_SYS_SELECTED]
    cmp  r0, #1                 ; bit 0: clk_ref
    bne  sys_ref_wait
    ; 4. PLL_SYS: reset, then 12 MHz * 125 = 1500 MHz, / 5 / 2 = 150 MHz
    ldr  r2, lit_resets
    ldr  r3, lit_reset_pll
    ldr  r0, lit_resets_set
    str  r3, [r0]               ; PLL_SYS into reset (the set alias) ...
    ldr  r0, lit_resets_clr
    str  r3, [r0]               ; ... and out again
pll_reset_wait:
    ldr  r0, [r2, #RESETS_RESET_DONE]
    tst  r0, r3
    beq  pll_reset_wait
    ldr  r2, lit_pll_sys
    movs r0, #1
    str  r0, [r2, #PLL_CS]      ; REFDIV 1
    movs r0, #125
    str  r0, [r2, #PLL_FBDIV_INT]
    ldr  r0, [r2, #PLL_PWR]
    bic  r0, r0, #PLL_PWR_PD | PLL_PWR_VCOPD
    str  r0, [r2, #PLL_PWR]     ; power on the PLL and its VCO
pll_lock_wait:
    ldr  r0, [r2, #PLL_CS]
    cmp  r0, #0
    bge  pll_lock_wait          ; bit 31 LOCK
    ldr  r0, lit_postdiv
    str  r0, [r2, #PLL_PRIM]    ; POSTDIV1 5, POSTDIV2 2
    ldr  r0, [r2, #PLL_PWR]
    bic  r0, r0, #PLL_PWR_POSTDIVPD
    str  r0, [r2, #PLL_PWR]     ; power on the post dividers
    ; 5. clk_sys = the aux source (SRC 1), which is PLL_SYS (AUXSRC 0)
    movs r0, #1
    str  r0, [r1, #CLK_SYS_CTRL]
sys_pll_wait:
    ldr  r0, [r1, #CLK_SYS_SELECTED]
    cmp  r0, #2                 ; bit 1: the aux source
    bne  sys_pll_wait
    bx   lr

start_systick:
    ldr  r1, lit_scs
    str  r0, [r1, #SYST_RVR]
    movs r0, #0
    str  r0, [r1, #SYST_CVR]
    movs r0, #5
    str  r0, [r1, #SYST_CSR]
    bx   lr

wait_systick:
    ldr  r1, lit_scs
wait_loop:
    ldr  r2, [r1, #SYST_CSR]
    tst  r2, #COUNTFLAG
    beq  wait_loop
    subs r0, r0, #1
    bne  wait_loop
    bx   lr

; ---- IO_BANK0 and PADS_BANK0 out of reset, GPIO 25: SIO, output; changes r0 .. r2 ----
setup_led:
    ldr  r1, lit_resets_clr
    ldr  r0, lit_reset_bits
    str  r0, [r1]
    ldr  r1, lit_resets
setup_wait:
    ldr  r2, [r1, #RESETS_RESET_DONE]
    and  r2, r2, r0
    cmp  r2, r0
    bne  setup_wait
    ldr  r1, lit_pads_clr
    mov  r0, #PAD_ISO
    str  r0, [r1, #PADS_GPIO25]
    ldr  r1, lit_io_bank0
    movs r0, #FUNCSEL_SIO
    str  r0, [r1, #GPIO25_CTRL]
    ldr  r1, lit_sio
    ldr  r0, lit_led_bit
    str  r0, [r1, #SIO_GPIO_OE_SET]
    bx   lr

    align 4
lit_resets:      dd RESETS_BASE
lit_resets_clr:  dd RESETS_BASE + ALIAS_CLR
lit_reset_bits:  dd RESET_BITS
lit_pads_clr:    dd PADS_BANK0_BASE + ALIAS_CLR
lit_io_bank0:    dd IO_BANK0_BASE
lit_sio:         dd SIO_BASE
lit_led_bit:     dd 1 << 25
lit_resets_set:  dd RESETS_BASE + 0x2000
lit_reset_pll:   dd RESET_PLL_SYS
lit_xosc:        dd XOSC_BASE
lit_xosc_enable: dd XOSC_ENABLE_1_15MHZ
lit_clocks:      dd CLOCKS_BASE
lit_pll_sys:     dd PLL_SYS_BASE
lit_postdiv:     dd (5 << 16) | (2 << 12)
lit_scs:         dd SCS_BASE
lit_timer_value: dd TIMER_VALUE
