integrate picolibc and its stdio

This commit is contained in:
Harald Welte 2021-03-28 14:58:57 +02:00
parent 77eda9fcb7
commit 8bcec75693
10 changed files with 327 additions and 17 deletions

View File

@ -0,0 +1,4 @@
#pragma once
#include <stdint.h>
void iob_init(uint32_t usart);

View File

@ -0,0 +1,5 @@
#pragma once
int watchdog_init(void);
void watchdog_fini(void);
void watchdog_refresh(void);

62
libcommon/src/fault.c Normal file
View File

@ -0,0 +1,62 @@
#include <stdio.h>
#include <libopencm3/cm3/scb.h>
#include <libopencm3/cm3/nvic.h>
#ifndef STM32F1
#include <libopencm3/stm32/syscfg.h>
#endif
struct hardfault_args {
unsigned long r0;
unsigned long r1;
unsigned long r2;
unsigned long r3;
unsigned long r12;
unsigned long lr;
unsigned long pc;
unsigned long psr;
};
extern void hard_fault_handler_c(struct hardfault_args *args);
__attribute__((naked))
void hard_fault_handler(void)
{
__asm( ".syntax unified\n"
"movs r0, #4 \n"
"mov r1, lr \n"
"tst r0, r1 \n"
"beq _MSP \n"
"mrs r0, psp \n"
"b _CHAND \n"
"_MSP: \n"
"mrs r0, msp \n"
"_CHAND: \n"
"b hard_fault_handler_c\n"
".syntax divided\n");
}
void hard_fault_handler_c(struct hardfault_args *args)
{
//usart_stop(1, 0);
fprintf(stderr, "HARDFAULT\r\n");
fprintf(stderr, "R0=%08lx, R1=%08lx, R2=%08lx, R3=%08lx, R12=%08lx\r\n",
args->r0, args->r1, args->r2, args->r3, args->r12);
fprintf(stderr, "LR[R14]=%08lx, PC[R15]=%08lx, PSR=%08lx\r\n",
args->lr, args->pc, args->psr);
/* we better reset the CPU to avoid getting stuck in some state */
scb_reset_system();
}
void nmi_handler(void)
{
/* We better do a reset in case we receive a NMI */
fprintf(stderr, "NMI\r\n");
#ifdef SYSCFG_CFGR2
if (SYSCFG_CFGR2 & 0x10) {
fprintf(stderr, "PARITY\r\n");
SYSCFG_CFGR2 = 0x10;
}
#endif
scb_reset_system();
}

View File

@ -0,0 +1,37 @@
#include <stdio.h>
#include <sys/cdefs.h>
#include <libopencm3/stm32/usart.h>
#include <libcommon/iob.h>
/* picolibc iob implementation for blocking I/O on USART */
static uint32_t stdio_usart;
void iob_init(uint32_t usart)
{
stdio_usart = usart;
}
static int my_putc(char c, FILE *file)
{
(void) file;
usart_send_blocking(stdio_usart, c);
return c;
}
static int my_getc(FILE *file)
{
(void) file;
return usart_recv_blocking(stdio_usart);
}
static int my_flush(FILE *file)
{
(void) file;
return 0;
}
static FILE __stdio = FDEV_SETUP_STREAM(my_putc, my_getc, my_flush, _FDEV_SETUP_RW);
FILE *const __iob[3] = { &__stdio, &__stdio, &__stdio };

View File

@ -0,0 +1,91 @@
#include <stdio.h>
#include <sys/cdefs.h>
#include <libopencm3/cm3/assert.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/usart.h>
#include <libcommon/iob.h>
#include <librfn/ringbuf.h>
/* picolibc iob implementation for non-blocking I/O on USART via ring-buffer */
static uint32_t stdio_usart;
static uint8_t outbuf[1024];
static ringbuf_t outring = RINGBUF_VAR_INIT(outbuf, sizeof(outbuf));
static uint8_t inbuf[256];
static ringbuf_t inring = RINGBUF_VAR_INIT(inbuf, sizeof(inbuf));
void iob_init(uint32_t usart)
{
cm3_assert(usart == USART2);
stdio_usart = usart;
nvic_enable_irq(NVIC_USART2_IRQ);
/* Enable RX non-empty interrupt */
USART_CR1(stdio_usart) |= USART_CR1_RXNEIE;
}
/* FIXME: this obviously doesn't work with iob_init() getting passed a dynamic UART */
void usart2_isr(void)
{
if ((USART_CR1(stdio_usart) & USART_CR1_RXNEIE) &&
(USART_SR(stdio_usart) & USART_SR_RXNE)) {
uint16_t c = usart_recv(stdio_usart);
ringbuf_put(&inring, c);
}
if ((USART_CR1(stdio_usart) & USART_CR1_TXEIE) &&
(USART_SR(stdio_usart) & USART_SR_TXE)) {
int data = ringbuf_get(&outring);
if (data == -1) {
USART_CR1(stdio_usart) &= ~USART_CR1_TXEIE;
} else {
usart_send(stdio_usart, data);
}
}
}
/* buffered output to stdout */
static int my_stdout_putc(char c, FILE *file)
{
(void) file;
cm3_assert(stdio_usart);
ringbuf_put(&outring, c);
USART_CR1(stdio_usart) |= USART_CR1_TXEIE;
return c;
}
/* unbuffered, blocking output to stderr (e.g. for assert, fault handlers, ...) */
static int my_stderr_putc(char c, FILE *file)
{
(void) file;
cm3_assert(stdio_usart);
usart_send_blocking(stdio_usart, c);
return c;
}
static int my_stdin_getc(FILE *file)
{
(void) file;
int data = ringbuf_get(&inring);
if (data == -1)
return EOF;
else
return data;
}
static int my_stdout_flush(FILE *file)
{
(void) file;
do { } while (!ringbuf_empty(&outring));
return 0;
}
static FILE __stdin = FDEV_SETUP_STREAM(NULL, my_stdin_getc, NULL, _FDEV_SETUP_READ);
static FILE __stdout = FDEV_SETUP_STREAM(my_stdout_putc, NULL, my_stdout_flush, _FDEV_SETUP_WRITE);
static FILE __stderr = FDEV_SETUP_STREAM(my_stderr_putc, NULL, NULL, _FDEV_SETUP_WRITE);
FILE *const __iob[3] = { &__stdin, &__stdout, &__stderr };

8
libcommon/src/kill.c Normal file
View File

@ -0,0 +1,8 @@
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
pid_t getpid(void) { return 1; }
int kill(pid_t pid, int sig) { if (pid == 1) _exit(sig << 8); errno = ESRCH; return -1; }

70
libcommon/src/watchdog.c Normal file
View File

@ -0,0 +1,70 @@
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/wwdg.h>
#include <libcommon/watchdog.h>
void watchdog_refresh(void)
{
/* set to the maximum a 7-bit counter supports */
WWDG_CR = 0x7f;
}
#ifdef DEBUG_WATCHDOG
void WWDG_IRQHandler(void)
{
WWDG_ClearFlag();
/* instead of triggering a reset, we hang in this endless loop to wait
* for openocd/gdb attach and investigation of the stack */
watchdog_fini();
printf("WWDG\r\n");
while(1) {}
}
#endif
void watchdog_fini(void)
{
/* documentation claims we cannot stop it. lol. */
watchdog_refresh();
rcc_periph_clock_disable(RCC_WWDG);
}
int watchdog_init(void)
{
#ifdef RCC_DBGMCU
/* make sure the watchdog stops during JTAG stop */
rcc_periph_clock_enable(RCC_DBGMCU);
DBGMCU_CR |= DBGMCU_CR_WWDG_STOP
#endif
rcc_periph_clock_enable(RCC_WWDG);
/* Set WWDG clock to (PCLK1/4096)/8 */
/* At 8 MHz PCLK1 this is 244.14 Hz */
WWDG_CFR = (WWDG_CFR & ~WWDG_CFR_WDGTB_LSB) | WWDG_CFR_WDGTB_CK_DIV8;
#ifdef DEBUG_WATCHDOG
NVIC_InitTypeDef nvic_init = {
.NVIC_IRQChannelPriority = 0,
.NVIC_IRQChannel = WWDG_IRQn,
.NVIC_IRQChannelCmd = ENABLE,
};
WWDG_EnableIT();
NVIC_Init(&nvic_init);
#endif
/* highest possible window value */
WWDG_CFR = (WWDG_CFR & WWDG_CFR_WDGTB_LSB) | 0x7f;
/* this means, that we have 0x80-0x40= 64 cycles of the 214.14Hz
* watchdog clock tore-fresh the counter, before it reaches
* 0x3F, at which point a reset will be issued to the CPU. If my
* calculations are correct, this is 0.262 seconds, i.e.
* refreshing every time the systick timer fires (and we return
* to the main loop) should be sufficient. */
WWDG_CR = WWDG_CR_WDGA | 0x7f;
return 0;
}

View File

@ -12,6 +12,6 @@ OBJS += \
vpath %.c $(LIBRFN_DIR)/librfn
vpath %.c $(LIBRFN_DIR)/librfn/libopencm3
CPPFLAGS += -DNDEBUG
CPPFLAGS += -DCONFIG_CONSOLE_FROM_ISR=1
#CPPFLAGS += -DNDEBUG
#CPPFLAGS += -DCONFIG_CONSOLE_FROM_ISR=1
CPPFLAGS += -I$(LIBRFN_DIR)/include

View File

@ -19,6 +19,8 @@
BINARY = rfdsatt
OBJS = attenuator.o board_rfdsatt_4ch.o
LDSCRIPT = ./stm32f103-openblt.ld
@ -36,7 +38,15 @@ OOCD ?= openocd
OOCD_INTERFACE ?= stlink-v2
OOCD_TARGET ?= stm32f1x
ARCH_FLAGS += --specs=/usr/lib/picolibc/arm-none-eabi/picolibc.specs
LIBCOMMON_DIR = ../../libcommon
CPPFLAGS += -I$(LIBCOMMON_DIR)/include
vpath %.c $(LIBCOMMON_DIR)/src/
OBJS += watchdog.o fault.o iob_stm32_nonblocking.o kill.o
all: elf bin srec
include ../../mk/librfn.mk
include ../rules.mk

View File

@ -24,9 +24,16 @@
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/cm3/systick.h>
#include <libcommon/iob.h>
#include <stdio.h>
#include "attenuator.h"
#include "misc.h"
extern const struct attenuator_cfg board_att_cfg;
extern struct attenuator_state *board_att_st[];
volatile uint32_t jiffies;
void sys_tick_handler(void)
@ -64,12 +71,35 @@ static void clock_setup(void)
rcc_periph_clock_enable(RCC_USART2);
}
static void i2c_setup(void)
{
rcc_periph_clock_enable(RCC_I2C1);
gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ,
GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN,
GPIO_I2C1_SCL | GPIO_I2C1_SDA);
/* disable before making config changes */
i2c_peripheral_disable(I2C1);
/* APB1 runs at 24 MHz */
i2c_set_clock_frequency(I2C1, I2C_CR2_FREQ_24MHZ);
/* 400 kHz fast mode I2C */
//i2c_set_fast_mode(I2C1);
//...
i2c_peripheral_enable(I2C1);
}
static void usart_setup(void)
{
/* USART1: connected to debug header */
/* Setup GPIO pin GPIO_USART1_TX. */
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ,
GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART1_TX);
/* Setup UART parameters. */
usart_set_baudrate(USART1, 115200);
usart_set_databits(USART1, 8);
@ -77,14 +107,14 @@ static void usart_setup(void)
usart_set_mode(USART1, USART_MODE_TX);
usart_set_parity(USART1, USART_PARITY_NONE);
usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
/* Finally enable the USART. */
usart_enable(USART1);
/* USART2: connected to 2.5mm stereo jack */
/* Setup GPIO pin GPIO_USART2_TX. */
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ,
GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART2_TX);
/* Setup UART parameters. */
usart_set_baudrate(USART2, 115200);
usart_set_databits(USART2, 8);
@ -92,7 +122,6 @@ static void usart_setup(void)
usart_set_mode(USART2, USART_MODE_TX);
usart_set_parity(USART2, USART_PARITY_NONE);
usart_set_flow_control(USART2, USART_FLOWCONTROL_NONE);
/* Finally enable the USART. */
usart_enable(USART2);
}
@ -106,24 +135,18 @@ static void gpio_setup(void)
int main(void)
{
int i, j = 0, c = 0;
clock_setup();
gpio_setup();
usart_setup();
iob_init(USART2);
i2c_setup();
attenuator_init(&board_att_cfg, board_att_st);
/* Blink the LED (PB15) on the board with every transmitted byte. */
while (1) {
int i;
gpio_toggle(GPIOB, GPIO15); /* LED on/off */
usart_send_blocking(USART1, c + '0'); /* USART1: Send byte. */
usart_send_blocking(USART2, c + '0'); /* USART2: Send byte. */
c = (c == 9) ? 0 : c + 1; /* Increment c. */
if ((j++ % 80) == 0) { /* Newline after line full. */
usart_send_blocking(USART1, '\r');
usart_send_blocking(USART1, '\n');
usart_send_blocking(USART2, '\r');
usart_send_blocking(USART2, '\n');
}
printf("Hello world\r\n");
for (i = 0; i < 800000; i++) /* Wait a bit. */
__asm__("nop");
}