Mikael Ågren 4877e0ab99
PoC: Add example firmware with embedded that calls syscalls implemented in C
App is embedded in firmware and is loaded into app RAM when firmware
starts.
App continuously calls SET_LED syscalls.

Simulation: `make tb_application_fpga_irqpoc_c_example`
2025-02-07 12:54:01 +01:00

262 lines
5.2 KiB
ArmAsm

/*
* Copyright (C) 2022, 2023 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
// This firmware copies an app from ROM to app RAM.
// The app continuosly calls the SET_LED syscalls in firmware (main.c).
//
#include "../tk1_mem.h"
#include "custom_ops.S" // PicoRV32 custom instructions
#define illegal_insn() .word 0
#define FW_SP_STORAGE (TK1_MMIO_FW_RAM_BASE + TK1_MMIO_FW_RAM_SIZE - 4)
#define FW_STACK_TOP (TK1_MMIO_FW_RAM_BASE + TK1_MMIO_FW_RAM_SIZE - 2*4)
#define LED_RED (1 << TK1_MMIO_TK1_LED_R_BIT)
#define LED_GREEN (1 << TK1_MMIO_TK1_LED_G_BIT)
#define LED_BLUE (1 << TK1_MMIO_TK1_LED_B_BIT)
.section ".text.init"
.globl _start
_start:
j init
//
// IRQ handler
//
.=0x10 // IRQ handler at fixed address 0x10
irq_handler:
// PicoRV32 stores the IRQ bitmask in x4.
// If bit 31 is 1: IRQ31 was triggered.
// If bit 30 is 1: IRQ30 was triggered.
irq_syscall_lo_check:
li t4, (1 << 30)
bne x4, t4, irq_syscall_hi_check
// Run low privilege syscall handler
call syscall_lo_handler
j irq_source_check_done
irq_syscall_hi_check:
li t4, (1 << 31)
bne x4, t4, unexpected_irq
// Save app stack pointer. App is responsible for saving the rest of
// the registers.
la t0, FW_SP_STORAGE
sw sp, 0(t0)
// Setup firmware stack pointer
la sp, FW_STACK_TOP
// Run high privilege syscall handler
call syscall_hi_handler
// Restore app stack pointer
la t0, FW_SP_STORAGE
lw sp, 0(t0)
j irq_source_check_done
unexpected_irq:
illegal_insn()
irq_source_check_done:
// Verify that interrupt return address is in app RAM
li t0, TK1_RAM_BASE // 0x40000000
blt x3, t0, x3_invalid
li t0, TK1_RAM_BASE + TK1_RAM_SIZE // 0x40020000
bge x3, t0, x3_invalid
j x3_valid
x3_invalid:
illegal_insn()
j x3_invalid
x3_valid:
// Remove data left over from the syscall handling
mv x0, zero
mv x1, zero
// x2 (sp) is assumed to be preserved by the interrupt handler
// x3 (interrupt return address) assumed preserved
mv x4, zero
mv x5, zero
mv x6, zero
mv x7, zero
mv x8, zero
mv x9, zero
// x10 (a0) contains syscall return value. And should not be destroyed.
mv x11, zero
mv x12, zero
mv x13, zero
mv x14, zero
mv x15, zero
mv x16, zero
mv x17, zero
mv x18, zero
mv x19, zero
mv x20, zero
mv x21, zero
mv x22, zero
mv x23, zero
mv x24, zero
mv x25, zero
mv x26, zero
mv x27, zero
mv x28, zero
mv x29, zero
mv x30, zero
mv x31, zero
picorv32_retirq_insn() // Return from interrupt
//
// Init
//
.=0x100
init:
li t0, TK1_MMIO_TK1_LED
li t1, LED_RED
sw t1, 0(t0)
// Enable IRQs
li t0, 0x3fffffff // IRQ31 & IRQ30 mask
picorv32_maskirq_insn(zero, t0) // Enable IRQs
// Copy app to App RAM
la t0, app_start
la t1, app_end
li t2, 0x40000000 // 0x40000000: App RAM
copy_app:
lw t3, 0(t0)
sw t3, 0(t2)
addi t0, t0, 4
addi t2, t2, 4
bleu t0, t1, copy_app
// Jump to app
li t2, 0x40000000 // 0x40000000: App RAM
jalr zero, 0(t2)
//
// App
//
#define APP_SYSCALL_HI (1 << 31)
#define APP_SYSCALL_LO 0
#define APP_SYSCALL_HI_SET_LED (APP_SYSCALL_HI | 10)
#define APP_SYSCALL_LO_SET_LED (APP_SYSCALL_LO | 10)
.=0x1000
app_start:
// Set stack pointer to end of app RAM
li sp, 0x4001fffc
app_loop:
li a0, APP_SYSCALL_LO_SET_LED
li a1, LED_GREEN
call app_syscall
li a0, APP_SYSCALL_LO_SET_LED
li a1, LED_BLUE
call app_syscall
li a0, APP_SYSCALL_HI_SET_LED
li a1, LED_RED | LED_GREEN
call app_syscall
li a0, APP_SYSCALL_HI_SET_LED
li a1, LED_RED | LED_BLUE
call app_syscall
j app_loop
app_syscall:
// Save registers to stack
addi sp, sp, -32*4
sw x0, 0*4(sp)
sw x1, 1*4(sp)
// x2 (sp) is assumed to be preserved by the interrupt handler.
sw x3, 3*4(sp)
sw x4, 4*4(sp)
sw x5, 5*4(sp)
sw x6, 6*4(sp)
sw x7, 7*4(sp)
sw x8, 8*4(sp)
sw x9, 9*4(sp)
// x10 (a0) will contain syscall return value. And should not be saved.
sw x11, 11*4(sp)
sw x12, 12*4(sp)
sw x13, 13*4(sp)
sw x14, 14*4(sp)
sw x15, 15*4(sp)
sw x16, 16*4(sp)
sw x17, 17*4(sp)
sw x18, 18*4(sp)
sw x19, 19*4(sp)
sw x20, 20*4(sp)
sw x21, 21*4(sp)
sw x22, 22*4(sp)
sw x23, 23*4(sp)
sw x24, 24*4(sp)
sw x25, 25*4(sp)
sw x26, 26*4(sp)
sw x27, 27*4(sp)
sw x28, 28*4(sp)
sw x29, 29*4(sp)
sw x30, 30*4(sp)
sw x31, 31*4(sp)
// Raise interrupt depending on bit 31 in a0
// 0: Low privilege syscall
// 1: High privilege syscall
li t0, (1 << 31)
and t0, a0, t0
beqz t0, app_syscall_low_priv
li t1, 0xe1000000 // High privilege interrupt
sw zero, 0(t1) // Trigger interrupt
j app_syscall_restore_registers
app_syscall_low_priv:
li t1, 0xe0000000 // Low privelege interrupt
sw zero, 0(t1) // Trigger interrupt
app_syscall_restore_registers:
// Restore registers from stack
lw x0, 0*4(sp)
lw x1, 1*4(sp)
// x2 (sp) is assumed to be preserved by the interrupt handler.
lw x3, 3*4(sp)
lw x4, 4*4(sp)
lw x5, 5*4(sp)
lw x6, 6*4(sp)
lw x7, 7*4(sp)
lw x8, 8*4(sp)
lw x9, 9*4(sp)
// x10 (a0) contains syscall return value. And should not be destroyed.
lw x11, 11*4(sp)
lw x12, 12*4(sp)
lw x13, 13*4(sp)
lw x14, 14*4(sp)
lw x15, 15*4(sp)
lw x16, 16*4(sp)
lw x17, 17*4(sp)
lw x18, 18*4(sp)
lw x19, 19*4(sp)
lw x20, 20*4(sp)
lw x21, 21*4(sp)
lw x22, 22*4(sp)
lw x23, 23*4(sp)
lw x24, 24*4(sp)
lw x25, 25*4(sp)
lw x26, 26*4(sp)
lw x27, 27*4(sp)
lw x28, 28*4(sp)
lw x29, 29*4(sp)
lw x30, 30*4(sp)
lw x31, 31*4(sp)
addi sp, sp, 32*4
ret
.align 4
app_end: