Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • add_menu_vibration
  • blinkisync-as-preload
  • ch3/api-speed-eval2
  • ch3/dual-core
  • ch3/genapi-refactor
  • ch3/leds-api
  • ch3/splashscreen
  • dualcore
  • dx/flatten-config-module
  • dx/meh-bdf-to-stm
  • freertos-btle
  • genofire/ble-follow-py
  • koalo/bhi160-works-but-dirty
  • koalo/factory-reset
  • koalo/wip/i2c-for-python
  • master
  • msgctl/faultscreen
  • msgctl/textbuffer_api
  • plaetzchen/ios-workaround
  • rahix/bhi
  • rahix/bluetooth-app-favorite
  • rahix/bma
  • rahix/user-space-ctx
  • renze/hatchery_apps
  • renze/safe_mode
  • schleicher-test
  • schneider/212-reset-hardware-when-entering-repl
  • schneider/ancs
  • schneider/ble-buffers
  • schneider/ble-central
  • schneider/ble-ecg-stream-visu
  • schneider/ble-fixes-2020-3
  • schneider/ble-mini-demo
  • schneider/ble-stability
  • schneider/ble-stability-new-phy
  • schneider/bonding
  • schneider/bonding-fail-if-full
  • schneider/bootloader-update-9a0d158
  • schneider/deepsleep
  • schneider/deepsleep2
  • schneider/deepsleep4
  • schneider/default-main
  • schneider/freertos-list-debug
  • schneider/fundamental-test
  • schneider/iaq-python
  • schneider/ir
  • schneider/max30001
  • schneider/max30001-epicaridum
  • schneider/max30001-pycardium
  • schneider/maxim-sdk-update
  • schneider/mp-exception-print
  • schneider/mp-for-old-bl
  • schneider/png
  • schneider/schleicher-test
  • schneider/sdk-0.2.1-11
  • schneider/sdk-0.2.1-7
  • schneider/sleep-display
  • schneider/spo2-playground
  • schneider/stream-locks
  • schneider/v1.17-changelog
  • bootloader-v1
  • release-1
  • v0.0
  • v1.0
  • v1.1
  • v1.10
  • v1.11
  • v1.12
  • v1.13
  • v1.14
  • v1.15
  • v1.16
  • v1.17
  • v1.18
  • v1.2
  • v1.3
  • v1.4
  • v1.5
  • v1.6
  • v1.7
  • v1.8
  • v1.9
82 results

Target

Select target project
  • card10/firmware
  • annejan/firmware
  • astro/firmware
  • fpletz/firmware
  • gerd/firmware
  • fleur/firmware
  • swym/firmware
  • l/firmware
  • uberardy/firmware
  • wink/firmware
  • madonius/firmware
  • mot/firmware
  • filid/firmware
  • q3k/firmware
  • hauke/firmware
  • Woazboat/firmware
  • pink/firmware
  • mossmann/firmware
  • omniskop/firmware
  • zenox/firmware
  • trilader/firmware
  • Danukeru/firmware
  • shoragan/firmware
  • zlatko/firmware
  • sistason/firmware
  • datenwolf/firmware
  • bene/firmware
  • amedee/firmware
  • martinling/firmware
  • griffon/firmware
  • chris007/firmware
  • adisbladis/firmware
  • dbrgn/firmware
  • jelly/firmware
  • rnestler/firmware
  • mh/firmware
  • ln/firmware
  • penguineer/firmware
  • monkeydom/firmware
  • jens/firmware
  • jnaulty/firmware
  • jeffmakes/firmware
  • marekventur/firmware
  • pete/firmware
  • h2obrain/firmware
  • DooMMasteR/firmware
  • jackie/firmware
  • prof_r/firmware
  • Draradech/firmware
  • Kartoffel/firmware
  • hinerk/firmware
  • abbradar/firmware
  • JustTB/firmware
  • LuKaRo/firmware
  • iggy/firmware
  • ente/firmware
  • flgr/firmware
  • Lorphos/firmware
  • matejo/firmware
  • ceddral7/firmware
  • danb/firmware
  • joshi/firmware
  • melle/firmware
  • fitch/firmware
  • deurknop/firmware
  • sargon/firmware
  • markus/firmware
  • kloenk/firmware
  • lucaswerkmeister/firmware
  • derf/firmware
  • meh/firmware
  • dx/card10-firmware
  • torben/firmware
  • yuvadm/firmware
  • AndyBS/firmware
  • klausdieter1/firmware
  • katzenparadoxon/firmware
  • xiretza/firmware
  • ole/firmware
  • techy/firmware
  • thor77/firmware
  • TilCreator/firmware
  • fuchsi/firmware
  • dos/firmware
  • yrlf/firmware
  • PetePriority/firmware
  • SuperVirus/firmware
  • sur5r/firmware
  • tazz/firmware
  • Alienmaster/firmware
  • flo_h/firmware
  • baldo/firmware
  • mmu_man/firmware
  • Foaly/firmware
  • sodoku/firmware
  • Guinness/firmware
  • ssp/firmware
  • led02/firmware
  • Stormwind/firmware
  • arist/firmware
  • coon/firmware
  • mdik/firmware
  • pippin/firmware
  • royrobotiks/firmware
  • zigot83/firmware
  • mo_k/firmware
106 results
Select Git revision
  • ch3/api-speed-eval2
  • ch3/dual-core
  • ch3/genapi-refactor
  • ch3/leds-api
  • color-2
  • color-html
  • color-html-2
  • color-html-3
  • dualcore
  • freertos-btle
  • master
  • micro-modules
  • patch-1
  • patch-2
  • patch-3
  • rahix/bma
  • schneider/mp-for-old-bl
  • wink/trng
18 results
Show changes
Showing
with 849 additions and 56 deletions
with import <nixpkgs> {};
let
py = python36;
# crc16 does not exist in pythonPackages - bring in our own.
crc16 = py.pkgs.buildPythonPackage rec {
pname = "crc16";
version = "0.1.1";
src = py.pkgs.fetchPypi {
inherit pname version;
sha256 = "15nkx0pa4lskwin84flpk8fsw3jqg6wic6v3s83syjqg76h6my61";
};
};
in stdenv.mkDerivation rec {
name = "card10";
nativeBuildInputs = [
bash
crc16
gcc-arm-embedded
git
meson
ninja
py
py.pkgs.pillow
];
src = ./.;
buildCommand = ''
build=$(pwd)/build
# Copy source over, as dev sources from '.' thend to have odd permissions.
cp -r $src $(pwd)/src
cd $(pwd)/src
# Ensure we have write right to patch shebangs.
chmod -R +w .
# The nix sandbox does not have /usr/bin/env bash, patch things up.
for f in lib/micropython/*.sh tools/*.sh; do
patchShebangs "$f"
done
# Actually run the build.
meson --cross-file card10-cross.ini "$build"
ninja -C "$build" -j $NIX_BUILD_CORES
# Copy ELFs for debugging
install -D -m 444 "$build/bootloader/bootloader.elf" -t "$out/lib/bootloader.elf"
install -D -m 444 "$build/epicardium/epicardium.elf" -t "$out/lib/epicardium.elf"
install -D -m 444 "$build/pycardium/pycardium.elf" -t "$out/lib/pycardium.elf"
# Create new flash contents
install -D -m 444 "$build/pycardium/pycardium_epicardium.bin" "$out/card10/card10.bin"
install -m 444 preload/*.py -t $out/card10/
cp -ar preload/apps $out/card10/
'';
}
import ws2812, gpio, bluetooth, time, display
from micropython import const
_IRQ_GATTS_WRITE = const(3)
WS2812_SERVICE_UUID = \
bluetooth.UUID("23238000-2342-2342-2342-234223422342")
SET_ALL = (
bluetooth.UUID("23238001-2342-2342-2342-234223422342"),
bluetooth.FLAG_WRITE
)
WS2812_SERVICE = (
WS2812_SERVICE_UUID,
(SET_ALL,)
)
def irq(event, data):
if event == _IRQ_GATTS_WRITE:
conn_handle, value_handle = data
value = ble.gatts_read(value_handle)
ws2812.set_all(gpio.WRISTBAND_3, [value] * 3)
if __name__ == "__main__":
display.open().backlight(0)
gpio.set_mode(gpio.WRISTBAND_3, gpio.mode.OUTPUT)
ble = bluetooth.BLE()
ble.active(True)
ble.irq(irq)
ble.gatts_register_services((WS2812_SERVICE,))
print("Waiting for connection!")
while True:
time.sleep(1)
#!/usr/bin/env python3
import bluepy
import time
import colorsys
# Change this to the MAC of your card10
p = bluepy.btle.Peripheral("CA:4D:10:01:ff:64")
c = p.getCharacteristics(
uuid='23238001-2342-2342-2342-234223422342')[0]
hue = 0
while 1:
r,g,b = colorsys.hsv_to_rgb(hue, 1, 0.1)
c.write(b"%c%c%c" %
(int(r*255), int(g*255), int(b*255)), True)
time.sleep(.1)
hue += 0.1
FROM ubuntu
RUN apt-get update && apt-get -y install gcc-arm-none-eabi binutils-arm-none-eabi libnewlib-arm-none-eabi python3 python3-pip ninja-build git
RUN pip3 install meson crc16 pillow
VOLUME /firmware
WORKDIR /firmware
CMD ./bootstrap.sh && ninja -C build && chown -R --reference=/firmware build
FROM debian:stretch-backports
RUN set -e -x ;\
export DEBIAN_FRONTEND=noninteractive ;\
apt-get update -y ;\
apt-get install -y \
git \
gcc-arm-none-eabi \
python3-pip ;\
apt-get install -y -t stretch-backports \
meson ;\
pip3 install crc16 pillow ;\
rm -rf /var/lib/apt/lists
#!/usr/bin/env bash
# Build and push the build env Docker container to the Gitlab container registry.
set -e
function rev() {
( cd $1; git describe --always --match "v[0-9].*" --dirty )
}
IMAGE=derq3k/card10-build-env
TAG=$(TZ=UTC date +%Y%m%d-%H%M%SZ)-$(rev .)
docker build -t $IMAGE:$TAG .
docker push $IMAGE:$TAG
echo "Pushed $IMAGE:$TAG"
FROM ubuntu:focal
RUN set -e -x ;\
export DEBIAN_FRONTEND=noninteractive ;\
apt-get update -y ;\
apt-get install -y \
clang \
git \
libclang-dev \
llvm \
python3-pip ;\
pip3 install \
clang==10.0.1 \
sphinx \
sphinx_rtd_theme ;\
rm -rf /var/lib/apt/lists
#!/usr/bin/env bash
# Build and push the deploy env Docker container to the Gitlab container registry.
set -e
function rev() {
( cd $1; git describe --always --match "v[0-9].*" --dirty )
}
IMAGE=derq3k/card10-deploy-env
TAG=$(TZ=UTC date +%Y%m%d-%H%M%SZ)-$(rev .)
docker build -t $IMAGE:$TAG .
docker push $IMAGE:$TAG
echo "Pushed $IMAGE:$TAG"
FROM ubuntu:bionic
RUN set -e -x ;\
export DEBIAN_FRONTEND=noninteractive ;\
apt-get update -y ;\
apt-get install -y \
curl \
ca-certificates \
clang-format \
git \
python3 \
python3-pip ;\
python3 -m pip install black ;\
rm -rf /var/lib/apt/lists
#!/usr/bin/env bash
# Build and push the deploy env Docker container to the Gitlab container registry.
set -e
function rev() {
( cd $1; git describe --always --match "v[0-9].*" --dirty )
}
IMAGE=derq3k/card10-lint-env
TAG=$(TZ=UTC date +%Y%m%d-%H%M%SZ)-$(rev .)
docker build -t $IMAGE:$TAG .
docker push $IMAGE:$TAG
echo "Pushed $IMAGE:$TAG"
#ifndef FREERTOS_CONFIG_H #ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H #define FREERTOS_CONFIG_H
#define MXC_ASSERT_ENABLE
#include "mxc_assert.h"
#include "max32665.h" #include "max32665.h"
#include <assert.h>
/* CMSIS keeps a global updated with current system clock in Hz */ /* CMSIS keeps a global updated with current system clock in Hz */
#define configCPU_CLOCK_HZ ((unsigned long)120000000) #define configCPU_CLOCK_HZ ((unsigned long)96000000)
/* TODO: Adjust this for tickless idle */ /* TODO: Adjust this for tickless idle */
#define configTICK_RATE_HZ ((portTickType)1000) #define configTICK_RATE_HZ ((portTickType)1000)
...@@ -16,7 +15,7 @@ ...@@ -16,7 +15,7 @@
* *
* Heap is managed by libc (heap_3.c). * Heap is managed by libc (heap_3.c).
*/ */
#define configMINIMAL_STACK_SIZE ((unsigned short)128) #define configMINIMAL_STACK_SIZE ((unsigned short)256)
/* FIXME: Assign proper priorities to all interrupts */ /* FIXME: Assign proper priorities to all interrupts */
#define configMAX_PRIORITIES 5 #define configMAX_PRIORITIES 5
...@@ -51,17 +50,28 @@ ...@@ -51,17 +50,28 @@
#define INCLUDE_vTaskSuspend 1 #define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelay 1 #define INCLUDE_vTaskDelay 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_uxTaskGetStackHighWaterMark 1
#define INCLUDE_xTimerPendFunctionCall 1
#define INCLUDE_xSemaphoreGetMutexHolder 1
/* Allow static allocation of data structures */ /* Allow static allocation of data structures */
#define configSUPPORT_STATIC_ALLOCATION 1 #define configSUPPORT_STATIC_ALLOCATION 1
/*
* Enable stack overflow detector.
*
* TODO: Remove for production.
*/
#define configCHECK_FOR_STACK_OVERFLOW 2
/* Alias the default handler names to match CMSIS weak symbols */ /* Alias the default handler names to match CMSIS weak symbols */
#define vPortSVCHandler SVC_Handler #define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler #define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler #define xPortSysTickHandler SysTick_Handler
/* Assert */ /* Assert */
#define configASSERT(x) MXC_ASSERT(x) #define configASSERT(x) assert(x)
/* Tickless idle hooks */ /* Tickless idle hooks */
typedef uint32_t TickType_t; typedef uint32_t TickType_t;
...@@ -73,4 +83,11 @@ void post_idle_sleep(TickType_t xExpectedIdleTime); ...@@ -73,4 +83,11 @@ void post_idle_sleep(TickType_t xExpectedIdleTime);
#define configPOST_SLEEP_PROCESSING(xExpectedIdleTime) \ #define configPOST_SLEEP_PROCESSING(xExpectedIdleTime) \
post_idle_sleep(xExpectedIdleTime) post_idle_sleep(xExpectedIdleTime)
/*
* Uncomment to trace FreeRTOS malloc wrapper.
*
*/
// extern int printf (const char *__restrict __format, ...);
// #define traceMALLOC( pvAddress, uiSize ) printf("[%s:%d] %p %d\n", __FILE__, __LINE__, pvAddress, uiSize)
#endif /* FREERTOS_CONFIG_H */ #endif /* FREERTOS_CONFIG_H */
...@@ -5,8 +5,17 @@ ...@@ -5,8 +5,17 @@
#define MXC_ASSERT_ENABLE #define MXC_ASSERT_ENABLE
#include "mxc_assert.h" #include "mxc_assert.h"
static uint32_t irq_save = 0;
void *_api_call_start(api_id_t id, uintptr_t size) void *_api_call_start(api_id_t id, uintptr_t size)
{ {
/*
* Disable all maskable interrupts here, to be turned on again at the
* end of _api_call_transact().
*/
irq_save = __get_PRIMASK();
__set_PRIMASK(1);
while (SEMA_GetSema(_API_SEMAPHORE) == E_BUSY) { while (SEMA_GetSema(_API_SEMAPHORE) == E_BUSY) {
} }
...@@ -51,5 +60,74 @@ void *_api_call_transact(void *buffer) ...@@ -51,5 +60,74 @@ void *_api_call_transact(void *buffer)
API_CALL_MEM->call_flag = _API_FLAG_IDLE; API_CALL_MEM->call_flag = _API_FLAG_IDLE;
SEMA_FreeSema(_API_SEMAPHORE); SEMA_FreeSema(_API_SEMAPHORE);
/*
* Re-enable interrupts (if previously enabled) after completing the API
* call.
*/
__set_PRIMASK(irq_save);
return API_CALL_MEM->buffer; return API_CALL_MEM->buffer;
} }
__attribute__((noreturn)) void epic_exit(int ret)
{
/*
* Call __epic_exit() and then jump to the reset routine/
*/
void *buffer;
buffer = _api_call_start(API_SYSTEM_EXIT, sizeof(int));
*(int *)buffer = ret;
_api_call_transact(buffer);
API_CALL_MEM->reset_stub();
/* unreachable */
while (1)
;
}
int epic_exec(char *name)
{
/*
* Call __epic_exec(). If it succeeds, jump to the reset routine.
* Otherwise, return the error code.
*/
void *buffer;
buffer = _api_call_start(API_SYSTEM_EXEC, sizeof(char *));
*(char **)buffer = name;
int ret = *(int *)_api_call_transact(buffer);
if (ret < 0) {
return ret;
}
API_CALL_MEM->reset_stub();
/* unreachable */
while (1)
;
}
int api_fetch_args(char *buf, size_t cnt)
{
if (API_CALL_MEM->id != 0) {
/*
* When any call happened before the args are fetched, they are
* overwritten and no longer accessible.
*/
return (-1);
}
if (API_CALL_MEM->buffer[0x20] == '\0') {
return 0;
}
size_t i;
for (i = 0; i < cnt && API_CALL_MEM->buffer[i + 0x20] != '\0'; i++) {
buf[i] = API_CALL_MEM->buffer[i + 0x20];
}
return i - 1;
}
...@@ -27,3 +27,12 @@ void *_api_call_start(api_id_t id, uintptr_t size); ...@@ -27,3 +27,12 @@ void *_api_call_start(api_id_t id, uintptr_t size);
* - Pointer to a buffer containing the return value * - Pointer to a buffer containing the return value
*/ */
void *_api_call_transact(void *buffer); void *_api_call_transact(void *buffer);
/*
* Fetch arguments from the API buffer. This function will only work properly
* directly after startup of core 1. If api_fetch_args() is called after other
* calls have already happened, it will return -1.
*
* Otherwise it will return the length of data which was read.
*/
int api_fetch_args(char *buf, size_t cnt);
#pragma once
#include "epicardium.h"
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
...@@ -6,6 +9,7 @@ ...@@ -6,6 +9,7 @@
* TODO: Replace this with a LDREX/STREX based implementation * TODO: Replace this with a LDREX/STREX based implementation
*/ */
#define _API_SEMAPHORE 0 #define _API_SEMAPHORE 0
#define _CONTROL_SEMAPHORE 1
/* Type of API IDs */ /* Type of API IDs */
typedef uint32_t api_id_t; typedef uint32_t api_id_t;
...@@ -16,6 +20,13 @@ typedef uint32_t api_id_t; ...@@ -16,6 +20,13 @@ typedef uint32_t api_id_t;
/* Layout of the shared memory for API calls */ /* Layout of the shared memory for API calls */
struct api_call_mem { struct api_call_mem {
/*
* Reset stub. The reset stub is a small function provided by
* epicardium that should be called by a payload when receiving the
* reset interrupt.
*/
void (*reset_stub)();
/* /*
* Flag for synchronization of API calls. When this flag * Flag for synchronization of API calls. When this flag
* is set, the caller has issued a call and is waiting for * is set, the caller has issued a call and is waiting for
...@@ -26,6 +37,9 @@ struct api_call_mem { ...@@ -26,6 +37,9 @@ struct api_call_mem {
/* ID if the ongoing API call */ /* ID if the ongoing API call */
api_id_t id; api_id_t id;
/* ID of the current interrupt */
volatile api_int_id_t int_id;
/* /*
* Buffer for arguments/return value. This buffer will be * Buffer for arguments/return value. This buffer will be
* *overflown*, because there is guaranteed space behind it. * *overflown*, because there is guaranteed space behind it.
......
#include "epicardium.h"
#include "api/dispatcher.h"
#include "card10.h"
#include "max32665.h"
#include "sema.h"
#include "tmr.h"
static void __core1_init(void);
extern void interrupt_trigger_sync(api_int_id_t id);
struct core1_info {
/* Location of core1's interrupt vector table */
volatile uintptr_t ivt_addr;
/* Whether core 1 is ready for a new IVT */
volatile bool ready;
};
/*
* Information passing structure for controlling core 1.
*/
static volatile struct core1_info core1_info = {
.ivt_addr = 0x00,
.ready = false,
};
/*
* Minimal IVT needed for initial startup. This IVT only contains the initial
* stack pointer and reset-handler and is used to startup core 1. Afterwards,
* the payload's IVT is loaded into VTOR and used from then on.
*/
static uintptr_t core1_initial_ivt[] = {
/* Initial Stack Pointer */
0x20080000,
/* Reset Handler */
(uintptr_t)__core1_reset,
};
/*
* Reset Handler
*
* Calls __core1_init() to reset & prepare the core for loading a new payload.
*/
__attribute__((naked)) void __core1_reset(void)
{
/* Reset stack to MSP and set it to 0x20080000 */
__asm volatile(
"mov r0, #0\n\t"
"msr control, r0\n\t"
"mov sp, %0\n\t"
: /* No Outputs */
: "r"(core1_initial_ivt[0])
: "r0");
/* Reset FPU */
SCB->CPACR = 0x00000000;
FPU->FPDSCR = 0x00000000;
FPU->FPCCR = 0x00000000;
__DSB();
__ISB();
__core1_init();
}
/*
* Init core 1. This function will reset the core and wait for a new IVT
* address from Epicardium. Once this address is received, it will start
* execution with the supplied reset handler.
*/
void __core1_init(void)
{
/*
* Clear any pending API interrupts.
*/
TMR_IntClear(MXC_TMR5);
/*
* Disable the SysTick
*/
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk;
/*
* Reset Interrupts
*
* To ensure proper operation of the new payload, disable all interrupts
* and clear all pending ones.
*/
for (int i = 0; i < MXC_IRQ_EXT_COUNT; i++) {
NVIC_DisableIRQ(i);
NVIC_ClearPendingIRQ(i);
NVIC_SetPriority(i, 0);
}
/*
* Check whether we catched the core during an interrupt. If this is
* the case, try returning from the exception handler first and call
* __core1_reset() again in thread context.
*/
if ((SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0) {
/*
* Construct an exception frame so the CPU will jump back to our
* __core1_reset() function once we exit from the exception
* handler.
*
* To exit the exception, a special "EXC_RETURN" value is loaded
* into the link register and then branched to.
*/
__asm volatile(
"ldr r0, =0x41000000\n\t"
"ldr r1, =0\n\t"
"push { r0 }\n\t" /* xPSR */
"push { %0 }\n\t" /* PC */
"push { %0 }\n\t" /* LR */
"push { r1 }\n\t" /* R12 */
"push { r1 }\n\t" /* R3 */
"push { r1 }\n\t" /* R2 */
"push { r1 }\n\t" /* R1 */
"push { r1 }\n\t" /* R0 */
"ldr lr, =0xFFFFFFF9\n\t"
"bx lr\n\t"
: /* No Outputs */
: "r"((uintptr_t)__core1_reset)
: "pc", "lr");
/* unreachable */
while (1)
;
}
/* Wait for the IVT address */
while (1) {
while (SEMA_GetSema(_CONTROL_SEMAPHORE) == E_BUSY) {
}
__DMB();
__ISB();
/*
* The IVT address is reset to 0 by Epicardium before execution
* gets here. Once a new address has been set, core 1 can use
* the new IVT.
*/
if (core1_info.ivt_addr != 0x00) {
break;
}
/* Signal that we are ready for an IVT address */
core1_info.ready = true;
/*
* Reset the API interrupt so we never block Epicardium when it
* attempts to trigger an interrupt.
*/
API_CALL_MEM->int_id = (-1);
SEMA_FreeSema(_CONTROL_SEMAPHORE);
__WFE();
}
uintptr_t *ivt = (uintptr_t *)core1_info.ivt_addr;
core1_info.ivt_addr = 0x00;
SEMA_FreeSema(_CONTROL_SEMAPHORE);
/*
* Reset the call-flag before entering the payload so API calls behave
* properly. This is necessary because epic_exec() will set the flag
* to "returning" on exit.
*/
API_CALL_MEM->call_flag = _API_FLAG_IDLE;
/*
* Set the IVT
*/
SCB->VTOR = (uintptr_t)ivt;
/*
* Clear any pending API interrupts.
*/
TMR_IntClear(MXC_TMR5);
NVIC_ClearPendingIRQ(TMR5_IRQn);
/*
* Jump to payload's reset handler
*/
__asm volatile(
"ldr r0, %0\n\t"
"blx r0\n\r"
: /* No Outputs */
: "m"(*(ivt + 1))
: "r0");
}
void core1_boot(void)
{
/*
* Boot using the initial IVT. This will place core 1 into a loop,
* waiting for a payload.
*/
core1_start(&core1_initial_ivt);
}
void core1_trigger_reset(void)
{
/*
* Signal core 1 that we intend to load a new payload.
*
* This needs to be synchroneous because otherwise we will deadlock
* (Lifecycle task busy-spins and interrupt can never get dispatched).
*/
interrupt_trigger_sync(EPIC_INT_RESET);
}
bool core1_is_ready(void)
{
bool ready;
while (SEMA_GetSema(_CONTROL_SEMAPHORE) == E_BUSY) {
}
/*
* core 1 will set the ready flag once it is spinning in the
* above loop, waiting for a new IVT.
*/
ready = core1_info.ready;
SEMA_FreeSema(_CONTROL_SEMAPHORE);
return ready;
}
void core1_wait_ready(void)
{
/* Wait for the core to accept */
while (1) {
if (core1_is_ready()) {
break;
}
for (int i = 0; i < 10000; i++) {
}
}
/*
* TODO: If the other core does not respond within a certain grace
* period, we need to force it into our desired state by overwriting
* all of its memory. Yes, I don't like this method either ...
*/
}
void core1_load(void *ivt, char *args)
{
while (SEMA_GetSema(_CONTROL_SEMAPHORE) == E_BUSY) {
}
/* If the core is currently in an API call, reset it. */
API_CALL_MEM->call_flag = _API_FLAG_IDLE;
API_CALL_MEM->id = 0;
API_CALL_MEM->int_id = (-1);
api_prepare_args(args);
core1_info.ivt_addr = (uintptr_t)ivt;
core1_info.ready = false;
__DMB();
__ISB();
SEMA_FreeSema(_CONTROL_SEMAPHORE);
__SEV();
__WFE();
}
#include <stdlib.h>
#include "sema.h"
#include "api/dispatcher.h" #include "api/dispatcher.h"
#include "max32665.h" #include "max32665.h"
#include "sema.h"
#include <stdlib.h>
#include <string.h>
/* This function is defined by the generated dispatcher code */
void __api_dispatch_call(api_id_t id, void *buffer);
static volatile bool call_pending = false;
int api_dispatcher_init() int api_dispatcher_init()
{ {
int ret; int ret;
ret = SEMA_Init(NULL); ret = SEMA_Init(NULL);
SEMA_FreeSema(_API_SEMAPHORE);
API_CALL_MEM->reset_stub = __core1_reset;
API_CALL_MEM->call_flag = _API_FLAG_IDLE; API_CALL_MEM->call_flag = _API_FLAG_IDLE;
API_CALL_MEM->id = 0;
API_CALL_MEM->int_id = (-1);
/* /*
* Enable TX events for both cores. * Enable TX events for both cores.
...@@ -20,11 +32,9 @@ int api_dispatcher_init() ...@@ -20,11 +32,9 @@ int api_dispatcher_init()
return ret; return ret;
} }
static bool event_ready = false;
bool api_dispatcher_poll_once() bool api_dispatcher_poll_once()
{ {
if (event_ready) { if (call_pending) {
return false; return false;
} }
...@@ -36,22 +46,27 @@ bool api_dispatcher_poll_once() ...@@ -36,22 +46,27 @@ bool api_dispatcher_poll_once()
return false; return false;
} }
event_ready = true; call_pending = true;
return true; return true;
} }
bool api_dispatcher_poll() bool api_dispatcher_poll()
{ {
if (event_ready) { if (call_pending) {
return true; return true;
} }
return api_dispatcher_poll_once(); return api_dispatcher_poll_once();
} }
bool api_dispatcher_call_pending()
{
return call_pending;
}
api_id_t api_dispatcher_exec() api_id_t api_dispatcher_exec()
{ {
if (!event_ready) { if (!call_pending) {
return 0; return 0;
} }
...@@ -59,7 +74,7 @@ api_id_t api_dispatcher_exec() ...@@ -59,7 +74,7 @@ api_id_t api_dispatcher_exec()
__api_dispatch_call(id, API_CALL_MEM->buffer); __api_dispatch_call(id, API_CALL_MEM->buffer);
API_CALL_MEM->call_flag = _API_FLAG_RETURNED; API_CALL_MEM->call_flag = _API_FLAG_RETURNED;
event_ready = false; call_pending = false;
SEMA_FreeSema(_API_SEMAPHORE); SEMA_FreeSema(_API_SEMAPHORE);
/* Notify the caller that we returned */ /* Notify the caller that we returned */
...@@ -68,3 +83,15 @@ api_id_t api_dispatcher_exec() ...@@ -68,3 +83,15 @@ api_id_t api_dispatcher_exec()
return id; return id;
} }
void api_prepare_args(char *args)
{
/*
* The args are stored with an offset of 0x20 to make sure they won't
* collide with any integer return value of API calls like epic_exec().
*/
API_CALL_MEM->id = 0;
for (size_t i = 0; i <= strlen(args); i++) {
API_CALL_MEM->buffer[i + 0x20] = args[i];
}
}
...@@ -15,6 +15,12 @@ int api_dispatcher_init(); ...@@ -15,6 +15,12 @@ int api_dispatcher_init();
bool api_dispatcher_poll_once(); bool api_dispatcher_poll_once();
bool api_dispatcher_poll(); bool api_dispatcher_poll();
/*
* Check if the other core requested a call or if we are already excuting it.
* Only returns a cached version, without acquiring any locks.
*/
bool api_dispatcher_call_pending();
/* /*
* Attempt to dispatch a call, if one had been polled using * Attempt to dispatch a call, if one had been polled using
* api_dispatcher_poll(). Will return 0 if no call was dispatched or the ID of * api_dispatcher_poll(). Will return 0 if no call was dispatched or the ID of
...@@ -22,5 +28,31 @@ bool api_dispatcher_poll(); ...@@ -22,5 +28,31 @@ bool api_dispatcher_poll();
*/ */
api_id_t api_dispatcher_exec(); api_id_t api_dispatcher_exec();
/* This function is defined by the generated dispatcher code */ /*
void __api_dispatch_call(api_id_t id, void *buffer); * Fill the API buffer with data for l0dable/pycardium startup.
*
* The data is a NULL-terminated string.
*/
void api_prepare_args(char *args);
/*********************************************************************
* core 1 control *
*********************************************************************/
/* Startup core1 into a state where it is ready to receive a payload. */
void core1_boot(void);
/* Reset core 1 into a state where it can accept a new payload */
void core1_trigger_reset(void);
/* Check if core 1 is ready for a new payload */
bool core1_is_ready(void);
/* Wait for core 1 to respond that it is ready for a new payload */
void core1_wait_ready(void);
/* Load a payload into core 1 */
void core1_load(void *ivt, char *args);
/* core 1 reset stub. See epicardium/api/control.c for details. */
void __core1_reset(void);
...@@ -5,18 +5,20 @@ import subprocess ...@@ -5,18 +5,20 @@ import subprocess
import sys import sys
MATCH_EXPANSION = re.compile( MATCH_API_EXPANSION = re.compile(
r"__GENERATE_API \$ __GEN_ID_(?P<id>\w+) \$ (?P<decl>.*?) \$", r"__GENERATE_API \$ __GEN_ID_(?P<id>\w+) \$ (?P<decl>.*?) \$",
re.DOTALL | re.MULTILINE, re.DOTALL | re.MULTILINE,
) )
MATCH_DECLARATION = re.compile( MATCH_ISR_EXPANSION = re.compile(
r"^(?P<typename>.*?)\s*\((?P<args>.*)\)$", r"__GENERATE_API_ISR \$ __GEN_ID_(?P<id>\w+) \$ (?P<isr>.*?) \$",
re.DOTALL, re.DOTALL | re.MULTILINE,
) )
MATCH_DECLARATION = re.compile(r"^(?P<typename>.*?)\s*\((?P<args>.*)\)$", re.DOTALL)
MATCH_TYPENAME = re.compile( MATCH_TYPENAME = re.compile(
r"^(?P<type>(?:const )?(?:struct )?\w+[*\s]+)(?P<name>\w+)$", r"^(?P<type>(?:const )?(?:struct |enum |union )?\w+[*\s]+)(?P<name>\w+)$"
) )
...@@ -34,7 +36,7 @@ def bailout(message, *args, **kwargs): ...@@ -34,7 +36,7 @@ def bailout(message, *args, **kwargs):
def parse_declarations(source): def parse_declarations(source):
"""Parse all declarations in the given source.""" """Parse all declarations in the given source."""
declarations = [] declarations = []
for exp in MATCH_EXPANSION.finditer(source): for exp in MATCH_API_EXPANSION.finditer(source):
id = exp.group("id") id = exp.group("id")
decl = MATCH_DECLARATION.match(exp.group("decl")) decl = MATCH_DECLARATION.match(exp.group("decl"))
...@@ -58,24 +60,40 @@ def parse_declarations(source): ...@@ -58,24 +60,40 @@ def parse_declarations(source):
if arg is None: if arg is None:
bailout("Failed to parse argument '{}'", arg_str.strip()) bailout("Failed to parse argument '{}'", arg_str.strip())
args.append({ args.append(
{
"type": arg.group("type").strip(), "type": arg.group("type").strip(),
"name": arg.group("name"), "name": arg.group("name"),
"sizeof": "sizeof({})".format(arg.group("type").strip()), "sizeof": "sizeof({})".format(arg.group("type").strip()),
"offset": sizeof(args), "offset": sizeof(args),
}) }
)
declarations.append({ declarations.append(
{
"id": id, "id": id,
"return_type": typename.group("type").strip(), "return_type": typename.group("type").strip(),
"name": typename.group("name"), "name": typename.group("name"),
"args": args, "args": args,
"args_str": args_str, "args_str": args_str,
}) }
)
return declarations return declarations
def parse_interrupts(source):
"""Parse all isr declarations in the given source."""
interrupts = []
for exp in MATCH_ISR_EXPANSION.finditer(source):
id = exp.group("id")
isr = exp.group("isr")
interrupts.append({"id": id, "isr": isr})
return interrupts
def main(): def main():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Generate the API stubs from a header file." description="Generate the API stubs from a header file."
...@@ -98,6 +116,7 @@ def main(): ...@@ -98,6 +116,7 @@ def main():
# a way we can find later on. # a way we can find later on.
api_src = """\ api_src = """\
#define API(id, def) __GENERATE_API $ __GEN_ID_##id $ def $ #define API(id, def) __GENERATE_API $ __GEN_ID_##id $ def $
#define API_ISR(id, isr) __GENERATE_API_ISR $ __GEN_ID_##id $ isr $
#include "{header}" #include "{header}"
""".format( """.format(
header=os.path.relpath(args.header) header=os.path.relpath(args.header)
...@@ -105,19 +124,20 @@ def main(): ...@@ -105,19 +124,20 @@ def main():
# Evaluate the preprocessor # Evaluate the preprocessor
source = subprocess.check_output( source = subprocess.check_output(
["gcc", "-E", "-"], input=api_src.encode() ["gcc", "-E", "-P", "-"], input=api_src.encode()
).decode() ).decode()
declarations = parse_declarations(source) declarations = parse_declarations(source)
fmt_header = { interrupts = parse_interrupts(source)
"header": os.path.basename(args.header)
} fmt_header = {"header": os.path.basename(args.header)}
# Generate Client {{{ # Generate Client {{{
with open(args.client, "w") as f_client: with open(args.client, "w") as f_client:
tmp = """\ tmp = """\
#include <stdio.h> #include <stdio.h>
#define API_ISR(id, isr)
#include "{header}" #include "{header}"
#include "api/caller.h" #include "api/caller.h"
""" """
...@@ -130,18 +150,18 @@ def main(): ...@@ -130,18 +150,18 @@ def main():
/* Autogenerated stub for {id} */ /* Autogenerated stub for {id} */
{return_type} {name}({args_str}) {return_type} {name}({args_str})
{{ {{
const int size = {total_size}; const int epc__apistub_size = {total_size};
void*buffer; void*epc__apistub_buffer;
buffer = _api_call_start({id}, size); epc__apistub_buffer = _api_call_start({id}, epc__apistub_size);
/* TODO: Check if buffer is not NULL */ /* TODO: Check if epc__apistub_buffer is not NULL */
""" """
f_client.write(tmp.format(**decl)) f_client.write(tmp.format(**decl))
for i, arg in enumerate(decl["args"]): for i, arg in enumerate(decl["args"]):
tmp = """\ tmp = """\
*({type}*)(buffer + {offset}) = {name}; *({type}*)(epc__apistub_buffer + {offset}) = {name};
""" """
f_client.write(tmp.format(**arg)) f_client.write(tmp.format(**arg))
...@@ -149,26 +169,87 @@ def main(): ...@@ -149,26 +169,87 @@ def main():
# Don't return if return type is void # Don't return if return type is void
tmp = """\ tmp = """\
_api_call_transact(buffer); _api_call_transact(epc__apistub_buffer);
}} }}
""" """
f_client.write(tmp.format(**decl)) f_client.write(tmp.format(**decl))
else: else:
tmp = """\ tmp = """\
return *({return_type}*)_api_call_transact(buffer); return *({return_type}*)_api_call_transact(epc__apistub_buffer);
}} }}
""" """
f_client.write(tmp.format(**decl)) f_client.write(tmp.format(**decl))
tmp = """\
/* Weakly linked stubs for ISRs */
"""
f_client.write(tmp)
for isr in interrupts:
tmp = """\
void {isr}(api_int_id_t id)
__attribute__((weak, alias("__epic_isr_default_handler")));
"""
f_client.write(tmp.format(**isr))
tmp = """\
/* Default handler stub */
__attribute__((weak)) void epic_isr_default_handler(api_int_id_t id)
{
;
}
/*
* This function is needed because aliasing the weak default handler will
* lead to issues.
*/
void __epic_isr_default_handler(api_int_id_t id)
{
epic_isr_default_handler(id);
}
/*
* __dispatch_isr() will be called from the actual isr which was triggered
* by core 0. It will then call the appropriate isr.
*/
void __dispatch_isr(api_int_id_t id)
{
switch (id) {
"""
f_client.write(tmp)
for isr in interrupts:
tmp = """\
case {id}:
{isr}(id);
break;
"""
f_client.write(tmp.format(**isr))
tmp = """\
case (-1):
/* Ignore a spurious interrupt */
break;
default:
epic_isr_default_handler(id);
break;
}
}
"""
f_client.write(tmp)
# END: Generate Client }}} # END: Generate Client }}}
# Generate Dispatcher {{{ # Generate Dispatcher {{{
with open(args.server, "w") as f_dispatcher: with open(args.server, "w") as f_dispatcher:
tmp = """\ tmp = """\
#include <stdio.h> #include "os/core.h"
#include "{header}" #include "{header}"
void __api_dispatch_call(uint32_t id, void*buffer) void __api_dispatch_call(uint32_t id, void*epc__apistub_buffer)
{{ {{
switch (id) {{ switch (id) {{
""" """
...@@ -183,13 +264,13 @@ void __api_dispatch_call(uint32_t id, void*buffer) ...@@ -183,13 +264,13 @@ void __api_dispatch_call(uint32_t id, void*buffer)
else: else:
tmp = """\ tmp = """\
case {id}: case {id}:
*(({return_type}*)buffer) = {name}(""" *(({return_type}*)epc__apistub_buffer) = {name}("""
f_dispatcher.write(tmp.format(**decl)) f_dispatcher.write(tmp.format(**decl))
for i, arg in enumerate(decl["args"]): for i, arg in enumerate(decl["args"]):
arg["comma"] = "" if i == 0 else "," arg["comma"] = "" if i == 0 else ","
tmp = """{comma} tmp = """{comma}
*({type}*)(buffer + {offset})""" *({type}*)(epc__apistub_buffer + {offset})"""
f_dispatcher.write(tmp.format(**arg)) f_dispatcher.write(tmp.format(**arg))
tmp = """ tmp = """
...@@ -201,7 +282,7 @@ void __api_dispatch_call(uint32_t id, void*buffer) ...@@ -201,7 +282,7 @@ void __api_dispatch_call(uint32_t id, void*buffer)
tmp = """\ tmp = """\
default: default:
/* TODO: Better error handling */ /* TODO: Better error handling */
printf("Error: API function %lx is unknown!!\\n", id); LOG_ERR("api-dispatcher", "API function 0x%lx is unknown!!", id);
break; break;
}} }}
}} }}
......
#include "max32665.h"
#include "tmr.h"
#include "api/common.h"
#include "epicardium.h"
void __dispatch_isr(api_int_id_t);
/* Timer Interrupt used for control char notification */
void TMR5_IRQHandler(void)
{
TMR_IntClear(MXC_TMR5);
__dispatch_isr(API_CALL_MEM->int_id);
API_CALL_MEM->int_id = (-1);
}
/* Reset Handler */
void __epic_isr_reset(void)
{
API_CALL_MEM->int_id = (-1);
API_CALL_MEM->reset_stub();
}
#include "api/interrupt-sender.h"
#include "api/common.h"
#include "tmr_utils.h"
#include <assert.h>
void api_interrupt_init(void)
{
API_CALL_MEM->int_id = (-1);
}
bool api_interrupt_is_ready(void)
{
return API_CALL_MEM->int_id == (api_int_id_t)(-1);
}
void api_interrupt_trigger(api_int_id_t id)
{
assert(API_CALL_MEM->int_id == (api_int_id_t)(-1));
API_CALL_MEM->int_id = id;
TMR_TO_Start(MXC_TMR5, 1, 0);
}