diff options
37 files changed, 1962 insertions, 229 deletions
diff --git a/Makefile.preamble b/Makefile.preamble index 62b221a..75f3afb 100644 --- a/Makefile.preamble +++ b/Makefile.preamble @@ -6,7 +6,7 @@ CFLAGS?=$(OPT) -mcpu=cortex-m4 -mthumb -g -lgcc -static -nostartfiles -Iinclude LD_FLAGS?=-T linker/linker_script.ld -nostdlib --cref -Map linker/main.map -static TEST_PREFIX=x86_64-pc-linux-gnu- -TEST_CFLAGS=-Iinclude -Iinclude/arch/x86_64 -Itest_harness -g3 -ggdb -DFOR_TESTING -Wall +TEST_CFLAGS=-Iinclude -Iinclude/arch/x86_64 -Itest_harness -g3 -ggdb -gdwarf-2 -DFOR_TESTING -Wall all: _$(PREFIX)_obs/main.elf diff --git a/include/arch/arm/arch.h b/include/arch/arm/arch.h index e4ebb8d..7dfc343 100644 --- a/include/arch/arm/arch.h +++ b/include/arch/arm/arch.h @@ -44,6 +44,11 @@ #define STACK_TOP (0x2000c000) #define MPU_BASE (0xE000ED90) +#define SYSCFG_BASE (0x40010000) +#define EXTI_BASE (0x40010400) + +#define TIM2_BASE (0x40000000) + #include <stdint.h> #ifndef DRY_RUN _Static_assert(sizeof(void*) == sizeof(uint32_t), "Pointers must be 32 bits"); diff --git a/include/arch/stm32l4xxx/peripherals/exti.h b/include/arch/stm32l4xxx/peripherals/exti.h new file mode 100644 index 0000000..6776d10 --- /dev/null +++ b/include/arch/stm32l4xxx/peripherals/exti.h @@ -0,0 +1,35 @@ +#ifndef PERIPHERALS_EXTI_H_ +#define PERIPHERALS_EXTI_H_ + +#include "arch.h" + +#define EXTI (*(exti_regs_t*)(EXTI_BASE)) + +typedef struct { +#define exti_im_n(n) (1 << (n)) + uint32_t im_r1; /* Interrupt mask register 1. */ + +#define exti_em_n(n) (1 << (n)) + uint32_t em_r1; /* Event mask register 1. */ + +#define exti_rt_n(n) (1 << (n)) + uint32_t rts_r1; /* Rising trigger selection register 1. */ + +#define exti_ft_n(n) (1 << (n)) + uint32_t fts_r1; /* Falling trigger selection register 1. */ + +#define exti_swi_n(n) (1 << (n)) + uint32_t swie_r1; /* Software interrupt event register 1. */ + +#define exti_pif_n(n) (1 << (n)) + uint32_t p_r1; /* Pending register 1. */ + + uint32_t im_r2; /* Interrupt mask register 2. */ + uint32_t em_r2; /* Event mask register 2. */ + uint32_t rts_r2; /* Rising trigger selection register 2. */ + uint32_t fts_r2; /* Falling trigger selection register 2. */ + uint32_t swie_r2; /* Software interrupt event register 2. */ + uint32_t p_r2; /* Pending register 2. */ +} exti_regs_t; + +#endif /* PERIPHERALS_EXTI_H_ */ diff --git a/include/arch/stm32l4xxx/peripherals/syscfg.h b/include/arch/stm32l4xxx/peripherals/syscfg.h new file mode 100644 index 0000000..65290a0 --- /dev/null +++ b/include/arch/stm32l4xxx/peripherals/syscfg.h @@ -0,0 +1,87 @@ +#ifndef PERIPHERALS_SYSCFG_H_ +#define PERIPHERALS_SYSCFG_H_ + +#include "arch.h" +#include "kern/common.h" + +#define SYSCFG (*(syscfg_t*)SYSCFG_BASE) + +typedef __IO struct { +#define syscfg_memmode (7 << 0) /* Memory mapping selection. */ +#define syscfg_fbmode (1 << 8) /* Flash bank mode selection. */ + uint32_t memrmp_r; /* Configuration on memory map. */ + +#define syscfg_fwdis (1 << 0) /* Disable firewall. */ +#define syscfg_boosten (1 << 8) /* Enable analog boost. */ +#define syscfg_i2c4_fmp \ + (1 << 23) /* Fast-mode Plus driving capability activation (this bit is only \ + available on */ +#define syscfg_i2c3_fmp \ + (1 << 22) /* I2C3 Fast-mode Plus driving capability activation */ +#define syscfg_i2c2_fmp \ + (1 << 21) /* I2C2 Fast-mode Plus driving capability activation */ +#define syscfg_i2c1_fmp \ + (1 << 20) /* I2C1 Fast-mode Plus driving capability activation */ +#define syscfg_i2c_pb9_fmp \ + (1 << 19) /* Fast-mode Plus (Fm+) driving capability activation on PB9 */ +#define syscfg_i2c_pb8_fmp \ + (1 << 18) /* Fast-mode Plus (Fm+) driving capability activation on PB8 */ +#define syscfg_i2c_pb7_fmp \ + (1 << 17) /* Fast-mode Plus (Fm+) driving capability activation on PB7 */ +#define syscfg_i2c_pb6_fmp \ + (1 << 16) /* Fast-mode Plus (Fm+) driving capability activation on PB6 */ +#define syscfg_fpe_ie (0x3f << 26) /* Floating point interrupts enabled. */ + uint32_t cfg_r1; /* Configuration register 1. */ + +#define syscfg_exti0 (0xf << 0) +#define syscfg_exti1 (0xf << 4) +#define syscfg_exti2 (0xf << 8) +#define syscfg_exti3 (0xf << 12) + uint32_t extic_r1; /* External interrupt configuration register 1. */ + +#define syscfg_exti4 (0xf << 0) +#define syscfg_exti5 (0xf << 4) +#define syscfg_exti6 (0xf << 8) +#define syscfg_exti7 (0xf << 12) + uint32_t extic_r2; /* External interrupt configuration register 2. */ + +#define syscfg_exti8 (0xf << 0) +#define syscfg_exti9 (0xf << 4) +#define syscfg_exti10 (0xf << 8) +#define syscfg_exti11 (0xf << 12) + uint32_t extic_r3; /* External interrupt configuration register 3. */ + +#define syscfg_exti12 (0xf << 0) +#define syscfg_exti13 (0xf << 4) +#define syscfg_exti14 (0xf << 8) +#define syscfg_exti15 (0xf << 12) + uint32_t extic_r4; /* External interrupt configuration register 4. */ + +#define syscfg_sram2er (1 << 0) +#define syscfg_sram2bsy (1 << 1) + uint32_t scs_r; /* SRAM2 control & status register. */ + +#define syscfg_spf (1 << 8) /* SRAM2 parity error flag */ +#define syscfg_eccl (1 << 3) /* ECC Lock */ +#define syscfg_pvdl (1 << 2) /* PVD lock enable bit */ +#define syscfg_spl (1 << 1) /* SRAM2 parity lock bit */ +#define syscfg_cll (1 << 0) /* Cortex-M4 LOCKUP (Hardfault) output enable bit */ + uint32_t cfg_r2; /* Configuration register 2. */ + +#define syscfg_pnwp(n) (1 << (n)) /* Page n write protection on sram2. */ + uint32_t swp_r1; /* SRAM2 software protection register 1. */ + + union { + uint8_t sk_r; /* SRAM2 write protection key for software erase. */ + uint32_t reserved; + }; + + uint32_t swp_r2; /* SRAM2 software protection register 2. */ + + +} syscfg_t; + +static_assert( + offsetof(syscfg_t, swp_r2) == 0x28, "Offset check failed"); + +#endif /* PERIPHERALS_SYSCFG_H_ */ diff --git a/include/arch/stm32l4xxx/peripherals/tim.h b/include/arch/stm32l4xxx/peripherals/tim.h new file mode 100644 index 0000000..145622a --- /dev/null +++ b/include/arch/stm32l4xxx/peripherals/tim.h @@ -0,0 +1,175 @@ +#ifndef ARCH_STM32L4XXX_PERIPHERALS_TIM_H_ +#define ARCH_STM32L4XXX_PERIPHERALS_TIM_H_ + +#include "arch.h" +#include "kern/common.h" + +#define TIM2 (*((tim_regs_t*)TIM2_BASE)) + +typedef __IO struct { +#define tim_uifremap (1 << 11) /* UIF status bit remapping */ +#define tim_ckd (3 << 8) /* Clock Divisor */ +#define tim_arpe (1 << 7) /* Auto-reload preload enable */ +#define tim_cms (3 << 5) /* Center align mode selection */ +#define tim_dir (1 << 4) /* Direction */ +#define tim_opm (1 << 3) /* One-pulse mode */ +#define tim_urs (1 << 2) /* Update request source */ +#define tim_udis (1 << 1) /* Update disable */ +#define tim_cen (1 << 0) /* Counter enable */ + uint32_t c_r1; + +#define tim_ti1s (1 << 7) +#define tim_mms (7 << 4) +#define tim_ccds (1 << 3) + uint32_t c_r2; + +#define tim_sms_3 (1 << 16) +#define tim_etp (1 << 15) +#define tim_ece (1 << 14) +#define tim_etps (3 << 12) +#define tim_etf (0xF << 8) +#define tim_msm (1 << 7) +#define tim_ts (7 << 4) +#define tim_occs (1 << 3) +#define tim_sms_0_2 (7 << 0) + uint32_t smc_r; + +#define tim_tde (1 << 14) /* Trigger DMA request enable */ +#define tim_cc4de (1 << 12) /* Capture/Compare 4 DMA request enable */ +#define tim_cc3de (1 << 11) /* Capture/Compare 3 DMA request enable */ +#define tim_cc2de (1 << 10) /* Capture/Compare 2 DMA request enable */ +#define tim_cc1de (1 << 9) /* Capture/Compare 1 DMA request enable */ +#define tim_ude (1 << 8) /* Update DMA request enable */ +#define tim_tie (1 << 6) /* Trigger interrupt enable */ +#define tim_cc4ie (1 << 4) /* Capture/Compare 4 interrupt enable */ +#define tim_cc3ie (1 << 3) /* Capture/Compare 3 interrupt enable */ +#define tim_cc2ie (1 << 2) /* Capture/Compare 2 interrupt enable */ +#define tim_cc1ie (1 << 1) /* Capture/Compare 1 interrupt enable */ +#define tim_uie (1 << 0) /* Update interrupt enable */ + uint32_t die_r; /* Dma interrupt/enable register */ + +#define tim_cc4of (1 << 12) /* Capture/Compare 4 overcapture flag */ +#define tim_cc3of (1 << 11) /* Capture/Compare 3 overcapture flag */ +#define tim_cc2of (1 << 10) /* Capture/compare 2 overcapture flag */ +#define tim_cc1of (1 << 9) /* Capture/Compare 1 overcapture flag */ +#define tim_tif (1 << 6) /* Trigger interrupt flag */ +#define tim_cc4if (1 << 4) /* Capture/Compare 4 interrupt flag */ +#define tim_cc3if (1 << 3) /* Capture/Compare 3 interrupt flag */ +#define tim_cc2if (1 << 2) /* Capture/Compare 2 interrupt flag */ +#define tim_cc1if (1 << 1) /* Capture/compare 1 interrupt flag */ +#define tim_uif (1 << 0) /* Update interrupt flag */ + uint32_t s_r; /* Status register */ + +#define tim_tg (1 << 6) /* Trigger generation */ +#define tim_cc4g (1 << 4) /* Capture/compare 4 generation */ +#define tim_cc3g (1 << 3) /* Capture/compare 3 generation */ +#define tim_cc2g (1 << 2) /* Capture/compare 2 generation */ +#define tim_cc1g (1 << 1) /* Capture/compare 1 generation */ +#define tim_ug (1 << 0) /* Update generation */ + uint32_t eg_r; /* Event generation register. */ + +#define tim_ic2f (15 << 12) /* Input capture 2 filter */ +#define tim_ic2psc (3 << 10) /* Input capture 2 prescaler */ +#define tim_cc2s (3 << 8) /* Capture/compare 2 selection */ +#define tim_ic1f (15 << 4) /* Input capture 1 filter */ +#define tim_ic1psc (3 << 2) /* Input capture 1 prescaler */ +#define tim_cc1s (3 << 0) /* Capture/Compare 1 selection */ + /* Alternate */ +#define tim_oc2ce (1 << 15) /* Output compare 2 clear enable */ +#define tim_oc2m_3 (1 << 24) /* Output compare 2 mode, bit 3. */ +#define tim_oc2m_0_2 (7 << 12) /* Output compare 2 mode, bits 0-2 */ +#define tim_oc2pe (1 << 11) /* Output compare 2 preload enable */ +#define tim_oc2fe (1 << 10) /* Output compare 2 fast enable */ +#define tim_cc2s (3 << 8) /* Capture/Compare 2 selection */ +#define tim_oc1ce (1 << 7) /* Output compare 1 clear enable */ +#define tim_oc1m_3 (1 << 16) /* Output compare 1 mode, bit 3. */ +#define tim_oc1m_0_2 (7 << 4) /* Output compare 1 mode, bits 0-2. */ +#define tim_oc1pe (1 << 3) /* Output compare 1 preload enable */ +#define tim_oc1fe (1 << 2) /* Output compare 1 fast enable */ +#define tim_cc1s (3 << 0) /* Capture/Compare 1 selection */ + uint32_t ccm_r1; /* Capture/compare mode register 1. */ + +#define tim_ic4f (15 << 12) /* Input capture 4 filter */ +#define tim_ic4psc (3 << 10) /* Input capture 4 prescaler */ +#define tim_cc4s (3 << 8) /* Capture/Compare 4 selection */ +#define tim_ic3f (15 << 4) /* Input capture 3 filter */ +#define tim_ic3psc (3 << 2) /* Input capture 3 prescaler */ +#define tim_cc3s (3 << 0) /* Capture/Compare 3 selection */ + /* Alternate */ +#define tim_oc4ce (1 << 15) /* Output compare 4 clear enable */ +#define tim_oc4m_3 (1 << 24) /* Output compare 4 mode, bit 3. */ +#define tim_oc4m_0_2 (7 << 12) /* Output compare 4 mode, bits 0-2 */ +#define tim_oc4pe (1 << 11) /* Output compare 4 preload enable */ +#define tim_oc4fe (1 << 10) /* Output compare 4 fast enable */ +#define tim_cc4s (3 << 8) /* Capture/Compare 4 selection */ +#define tim_oc3ce (1 << 7) /* Output compare 3 clear enable */ +#define tim_oc3m_3 (1 << 16) /* Output compare 3 mode, bit 3. */ +#define tim_oc3m_0_2 (7 << 4) /* Output compare 3 mode, bits 0-2. */ +#define tim_oc3pe (1 << 3) /* Output compare 3 preload enable */ +#define tim_oc3fe (1 << 2) /* Output compare 3 fast enable */ +#define tim_cc3s (3 << 0) /* Capture/Compare 3 selection */ + uint32_t ccm_r2; /* Capture/compare mode register 2. */ + +#define tim_cc4np (1 << 15) /* Capture/Compare 4 output Polarity. */ +#define tim_cc4p (1 << 13) /* Capture/Compare 4 output Polarity. */ +#define tim_cc4e (1 << 12) /* Capture/Compare 4 output enable. */ +#define tim_cc3np (1 << 11) /* Capture/Compare 3 output Polarity. */ +#define tim_cc3p (1 << 9) /* Capture/Compare 3 output Polarity. */ +#define tim_cc3e (1 << 8) /* Capture/Compare 3 output enable. */ +#define tim_cc2np (1 << 7) /* Capture/Compare 2 output Polarity. */ +#define tim_cc2p (1 << 5) /* Capture/Compare 2 output Polarity. */ +#define tim_cc2e (1 << 4) /* Capture/Compare 2 output enable. */ +#define tim_cc1np (1 << 3) /* Capture/Compare 1 output Polarity. */ +#define tim_cc1p (1 << 1) /* Capture/Compare 1 output Polarity. */ +#define tim_cc1e (1 << 0) /* Capture/Compare 1 output enable. */ + uint32_t cce_r; /* Copture/compare enable register. */ + + /* Mapping depends on UIFREMAP. */ +#define tim_cnt (0x7fffffff << 0) /* Counter value */ +#define tim_uifcpy (1 << 31) + uint32_t cnt; + + uint16_t psc; /* Prescaler. */ + uint16_t reserved0; + + uint32_t ar_r; /* Auto reload register. */ + + uint32_t reserved1; + + uint32_t cc_r1; /* Capture compare register 1. */ + uint32_t cc_r2; /* Capture compare register 2. */ + uint32_t cc_r3; /* Capture compare register 3. */ + uint32_t cc_r4; /* Capture compare register 4. */ + + uint32_t reserved2; + +#define tim_dbl (31 << 8) /* DMA burst length */ +#define tim_dba (31 << 0) /* DMA base address */ + uint32_t dc_r; /* dma control register. */ + + uint16_t dma_r; /* dma address for full transfer. */ + uint16_t reserved3; /* dma address for full transfer. */ + + union { +#define tim_ti4_rmp (3 << 2) /* Input Capture 4 remap */ +#define tim_etr_rmp (1 << 1) /* External trigger remap */ +#define tim_itr1_rmp (1 << 0) /* Internal trigger 1 remap */ + uint32_t tim2_o_r1; +#define tim_ti1_rmp (3 << 0) + uint32_t tim3_o_r1; + }; + + uint32_t reserved4[3]; + +#define tim_etrsel (7 << 14) /* ETR source selection */ + union { + uint32_t tim2_o_r2; + uint32_t tim3_o_r2; + }; +} tim_regs_t; + +static_assert(offsetof(tim_regs_t, ar_r) == 0x2c, "Offset check failed"); +static_assert(offsetof(tim_regs_t, dc_r) == 0x48, "Offset check failed"); +static_assert(offsetof(tim_regs_t, tim3_o_r2) == 0x60, "Offset check failed"); + +#endif /* ARCH_STM32L4XXX_PERIPHERALS_TIM_H_ */ diff --git a/include/arch/x86_64/arch.h b/include/arch/x86_64/arch.h index ab26d1e..8bd3468 100644 --- a/include/arch/x86_64/arch.h +++ b/include/arch/x86_64/arch.h @@ -34,11 +34,15 @@ #define SPI3_BASE (load_fake_spi3__()) #define MPU_BASE (load_fake_mpu__()) +#define SYSCFG_BASE (load_fake_syscfg__()) +#define EXTI_BASE (load_fake_exti__()) + +#define TIM2_BASE (load_fake_exti__()) // Pretend there's a data segement at the start of SRAM1 for more accurate // testing. #define GHOST_DATA_SEGMENT_SIZE 1200 #define HEAP_START (*(((unsigned char*)SRAM1_BASE) + GHOST_DATA_SEGMENT_SIZE)) -#define HEAP_STOP (*(&HEAP_START + 16384 - GHOST_DATA_SEGMENT_SIZE)) +#define HEAP_STOP (*(&HEAP_START + 49152 - GHOST_DATA_SEGMENT_SIZE)) #endif /* ARCH_H_ */ diff --git a/include/drv/ir/control.h b/include/drv/ir/control.h new file mode 100644 index 0000000..11e3604 --- /dev/null +++ b/include/drv/ir/control.h @@ -0,0 +1,14 @@ +#ifndef _DRV_IR_CONTROL_H_ +#define _DRV_IR_CONTROL_H_ + +#include "kern/common.h" + +#define add_ir_code_callback(code, fn, closure) \ + add_ir_code_callback_(code, (void (*)(uint32_t, void*))(fn), closure) + +void add_ir_code_callback_( + uint32_t code, void (*fn)(uint32_t code, void* closure), void* closure); + +void enable_ir_control(); + +#endif /* _DRV_IR_CONTROL_H_ */ diff --git a/include/drv/ir/ir.h b/include/drv/ir/ir.h new file mode 100644 index 0000000..0a747e8 --- /dev/null +++ b/include/drv/ir/ir.h @@ -0,0 +1,34 @@ +#ifndef _DRV_IR_IR_H_ +#define _DRV_IR_IR_H_ + +#include "kern/common.h" + +typedef struct { + uint16_t ts[64]; /* Timestamps. Microseconds. */ + uint8_t n; +} ir_code_t; + +typedef struct { + uint32_t c; +} decoded_ir; + +/* + * Attempt to decode the provided ir code. + * + * The generic ir is: + * + * The frame starts out as really-long, half(ish) as long, short. If + * this basic frame header does not exist decoding cannot continue. + * + * A short code followed by a short code = 0 + * A long code followed by a short code = 1 + * + * returns non-zero on successful decode. + */ +int ir_generic_decode(const ir_code_t* code, uint32_t* out); + +void add_ir_callback(void(*callback)(const ir_code_t*)); + +void ir_begin_listen(); + +#endif /* _DRV_IR_IR_H_ */ diff --git a/include/drv/ir/lg_remote_codes.h b/include/drv/ir/lg_remote_codes.h new file mode 100644 index 0000000..77cbb45 --- /dev/null +++ b/include/drv/ir/lg_remote_codes.h @@ -0,0 +1,17 @@ +#ifndef _DRV_IR_LG_REMOTE_CODES_H_ +#define _DRV_IR_LG_REMOTE_CODES_H_ + +#define RC_HIGH 0x08e76800 +#define RC_TEMP_UP 0x08e7a800 +#define RC_DRY 0x08e72800 +#define RC_LOW 0x08e77000 +#define RC_TEMP_DOWN 0x08e7b000 +#define RC_COOL 0x08e73000 +#define RC_CONTINUOUS 0x08e75000 +#define RC_FAN 0x08e71000 +#define RC_SLEEP 0x08e76000 +#define RC_UNITS 0x08e72000 +#define RC_TIMER 0x08e78000 +#define RC_POWER 0x08e70080 + +#endif /* _DRV_IR_LG_REMOTE_CODES_H_ */ diff --git a/include/drv/ws2812B/ws2812b.h b/include/drv/ws2812B/ws2812b.h index 3189907..d7137a1 100644 --- a/include/drv/ws2812B/ws2812b.h +++ b/include/drv/ws2812B/ws2812b.h @@ -30,7 +30,4 @@ void ws2812b_write_rgb_arr_sync(ws2812b_t* drv, rgb_t* arr, size_t arr_len); void ws2812b_delete(ws2812b_t* drv); -/* Returns round(sin(2πn / 256) * 127.5 + 127.5). */ -uint8_t byte_sin(uint8_t n); - #endif /* WS2812B_H_ */ diff --git a/include/kern/common.h b/include/kern/common.h index 021229b..0acc9d3 100644 --- a/include/kern/common.h +++ b/include/kern/common.h @@ -39,6 +39,7 @@ typedef enum { ENDIANNESS_LITTLE, ENDIANNESS_BIG } endianness_t; #define ptr2reg(ptr) ((uint32_t)(ptrdiff_t)(ptr)) typedef __IO uint32_t bits_t; +typedef uint16_t msize_t; #define regset(reg, mask, val) ((reg) = ((reg) & ~mask) | (val << CTZ(mask))) diff --git a/include/kern/exti/exti_manager.h b/include/kern/exti/exti_manager.h new file mode 100644 index 0000000..aa39b4f --- /dev/null +++ b/include/kern/exti/exti_manager.h @@ -0,0 +1,32 @@ +#ifndef _KERN_EXTI_EXTI_MANAGER_H_ +#define _KERN_EXTI_EXTI_MANAGER_H_ + +#include "kern/common.h" +#include "kern/gpio/gpio_manager.h" +#include "shared/linked_list.h" + +#define EXTI_ERROR_ALREADY_IN_USE 1 + +struct EXTI_HANDLE; + + +typedef struct { + void (*fn)(struct EXTI_HANDLE, void*); + void* closure; +} exti_callback_t; + +LINKED_LIST_DECL(exti_callback_t); +typedef struct EXTI_HANDLE { + uint8_t id; + linked_list_t(exti_callback_t)* callbacks; +} exti_handle_t; + +exti_handle_t* enable_exti_for_gpio(gpio_pin_t gpio_pin, int* ec); + +exti_handle_t* exti_add_callback(exti_handle_t*, exti_callback_t* callback); + +gpio_pin_t exti_gpio_pin(exti_handle_t* handle); + + + +#endif /* _KERN_EXTI_EXTI_MANAGER_H_ */ diff --git a/include/kern/gpio/gpio_manager.h b/include/kern/gpio/gpio_manager.h index 922a423..cb61059 100644 --- a/include/kern/gpio/gpio_manager.h +++ b/include/kern/gpio/gpio_manager.h @@ -1,8 +1,8 @@ #ifndef KERN_GPIO_GPIO_MANAGE_H_ #define KERN_GPIO_GPIO_MANAGE_H_ -#include "kern/common.h" #include "arch/stm32l4xxx/peripherals/gpio.h" +#include "kern/common.h" #define GPIO_ERROR_IN_USE 1 #define GPIO_ERROR_INVALID_PIN_FOR_ALTERNATE_FUNCTION 2 @@ -10,8 +10,7 @@ typedef enum { /* Creates vaules GPIO_PIN_<port><num> i.e. GPIO_PIN_A0 */ -#define PORT(p, pn) \ - GPIO_PIN_P ## p ## pn, +#define PORT(p, pn) GPIO_PIN_P##p##pn, #include "arch/stm32l4xxx/peripherals/tables/stm32l432xx/gpio/port_table.inc" #undef PORT @@ -20,16 +19,14 @@ typedef enum { /* Alternate functions. */ typedef enum { -#define AFN(fn, ...) \ - GPIO_ALTERNATE_FUNCTION_ ## fn, +#define AFN(fn, ...) GPIO_ALTERNATE_FUNCTION_##fn, #include "arch/stm32l4xxx/peripherals/tables/stm32l432xx/gpio/afn_table.inc" #undef AFN GPIO_ALTERNATE_FUNCTION_EVENTOUT, } gpio_alternate_function_t; #define gpio_pin_for_alternate_function(af) ((af) / 16) -#define gpio_pin_out_of_range(pin) \ - ((pin) < 0 || (pin) >= N_GPIO_PINS) +#define gpio_pin_out_of_range(pin) ((pin) < 0 || (pin) >= N_GPIO_PINS) typedef enum { GPIO_PORT_A, @@ -83,41 +80,43 @@ typedef enum { inline static gpio_port_t get_port_for_pin(gpio_pin_t pin) { switch (pin) { -#define PORT(p, pn) \ - case GPIO_PIN_P ## p ## pn: return GPIO_PORT_ ## p; +#define PORT(p, pn) \ + case GPIO_PIN_P##p##pn: \ + return GPIO_PORT_##p; #include "arch/stm32l4xxx/peripherals/tables/stm32l432xx/gpio/port_table.inc" #undef PORT - case N_GPIO_PINS: return N_GPIO_PORTS; + case N_GPIO_PINS: + return N_GPIO_PORTS; } /* Should be unreachable. */ } -#define DEFAULT_GPIO_OPTS_OUTPUT \ - (gpio_pin_opts_t) { \ - .mode = GPIO_MODE_OUTPUT, \ - .pull_dir = GPIO_PULL_DIR_DOWN, \ - .output_opts.speed = GPIO_OUTPUT_SPEED_MEDIUM, \ - .output_opts.type = GPIO_OUTPUT_TYPE_PUSH_PULL, \ +#define DEFAULT_GPIO_OPTS_OUTPUT \ + (gpio_pin_opts_t) \ + { \ + .mode = GPIO_MODE_OUTPUT, .pull_dir = GPIO_PULL_DIR_DOWN, \ + .output_opts.speed = GPIO_OUTPUT_SPEED_MEDIUM, \ + .output_opts.type = GPIO_OUTPUT_TYPE_PUSH_PULL, \ } -#define DEFAULT_GPIO_OPTS_INPUT \ - (gpio_pin_opts_t) { \ - .mode = GPIO_MODE_OUTPUT, \ - .pull_dir = GPIO_PULL_DIR_DOWN, \ +#define DEFAULT_GPIO_OPTS_INPUT \ + (gpio_pin_opts_t) \ + { \ + .mode = GPIO_MODE_INPUT, .pull_dir = GPIO_PULL_DIR_DOWN, \ } typedef struct { - gpio_mode_t mode; - gpio_pull_dir_t pull_dir; + gpio_mode_t mode; + gpio_pull_dir_t pull_dir; union { struct { } input_opts; struct { - gpio_output_speed_t speed; - gpio_output_type_t type; + gpio_output_speed_t speed; + gpio_output_type_t type; } output_opts; struct { @@ -171,9 +170,7 @@ gpio_reserved_pin_t reserve_gpio_pin( * */ gpio_reserved_pin_t gpio_enable_alternate_function( - gpio_alternate_function_t fn, - gpio_pin_t hint, - int* error_out); + gpio_alternate_function_t fn, gpio_pin_t hint, int* error_out); /* * Releases the GPIO pin so it can be reserved again in the future. @@ -184,4 +181,9 @@ gpio_reserved_pin_t gpio_enable_alternate_function( */ void release_gpio_pin(gpio_reserved_pin_t gpio_pin); +/* + * Returns whether the GPIO pin has high or low input. + */ +bool get_gpio_pin_input_state(gpio_reserved_pin_t pin); + #endif /* KERN_GPIO_GPIO_MANAGE_H_ */ diff --git a/include/kern/mem.h b/include/kern/mem.h index 20b09bb..bc6c4e2 100644 --- a/include/kern/mem.h +++ b/include/kern/mem.h @@ -22,6 +22,8 @@ int debug_kalloc_assert_consistency(char* error, size_t len); void debug_print_blocks(); +int debug_is_heap_empty(); + #endif diff --git a/include/shared/array_list.h b/include/shared/array_list.h new file mode 100644 index 0000000..8b942ad --- /dev/null +++ b/include/shared/array_list.h @@ -0,0 +1,105 @@ +#ifndef SHARED_ARRAY_LIST_H_ +#define SHARED_ARRAY_LIST_H_ + +#include "kern/common.h" +#include "kern/mem.h" + +#define ALLOC kalloc +#define FREE kfree + +#define array_list_t(type) array_list_##type##_t +#define array_list_new(type) array_list_##type##_new +#define array_list_length(type) array_list_##type##_length +#define array_list_reserve(type) array_list_##type##_reserve +#define array_list_add(type) array_list_##type##_add +#define array_list_remove(type) array_list_##type##remove +#define array_list_ref(type) array_list_##type##_get +#define array_list_free(type) array_list_##type##_free + +#define array_list_foreach(ar, val) \ + typeof((ar)->elements[0]) val; \ + if ((ar)->size > 0) val = (ar)->elements[0]; \ + for (size_t _idx_ = 0; _idx_ < (ar)->size; \ + val = (++_idx_) >= (ar)->size ? val : (ar)->elements[_idx_]) + +#define ARRAY_LIST_DECL(type) \ + typedef struct { \ + size_t reserved; \ + size_t size; \ + type* elements; \ + } array_list_t(type); \ + size_t array_list_length(type)(const array_list_t(type)*); \ + array_list_t(type) * array_list_new(type)(); \ + bool array_list_reserve(type)(array_list_t(type)*, size_t len); \ + bool array_list_add(type)(array_list_t(type)*, type val); \ + bool array_list_remove(type)(array_list_t(type)*, size_t idx, type * out); \ + type* array_list_ref(type)(array_list_t(type)*, size_t idx); \ + void array_list_free(type)(array_list_t(type)*); + +#define ARRAY_LIST_IMPL(type) \ + array_list_t(type) * array_list_new(type)() \ + { \ + array_list_t(type)* ret = ALLOC(sizeof(array_list_t(type))); \ + ret->size = 0; \ + ret->reserved = 0; \ + ret->elements = NULL; \ + return ret; \ + } \ + size_t array_list_length(type)(const array_list_t(type) * lst) \ + { \ + return lst->size; \ + } \ + bool array_list_reserve(type)(array_list_t(type) * lst, size_t newlen) \ + { \ + if (lst->reserved < newlen) { \ + type* new_arr = ALLOC(sizeof(type) * newlen); \ + if (!new_arr) { \ + return 0; \ + } \ + for (size_t i = 0; i < lst->size; ++i) { \ + new_arr[i] = lst->elements[i]; \ + } \ + FREE(lst->elements); \ + lst->elements = new_arr; \ + lst->reserved = newlen; \ + } \ + return 1; \ + } \ + bool array_list_add(type)(array_list_t(type) * lst, type v) \ + { \ + if (lst->size == lst->reserved) { \ + if (!array_list_reserve(type)( \ + lst, lst->reserved == 0 ? 4 : lst->reserved * 2)) { \ + return 0; \ + } \ + } \ + lst->elements[lst->size++] = v; \ + return 1; \ + } \ + bool array_list_remove(type)( \ + array_list_t(type) * lst, size_t idx, type * out) \ + { \ + if (idx >= lst->size) { \ + return 0; \ + } \ + if (out) *out = lst->elements[idx]; \ + for (size_t i = idx; i < lst->size - 1; ++i) { \ + lst->elements[i] = lst->elements[i + 1]; \ + } \ + lst->size--; \ + return 1; \ + } \ + type* array_list_ref(type)(array_list_t(type) * lst, size_t idx) \ + { \ + if (idx >= lst->size) { \ + return NULL; \ + } \ + return &lst->elements[idx]; \ + } \ + void array_list_free(type)(array_list_t(type) * lst) \ + { \ + FREE(lst->elements); \ + FREE(lst); \ + } + +#endif /* SHARED_ARRAY_LIST_H_ */ diff --git a/include/shared/avl_tree.h b/include/shared/avl_tree.h new file mode 100644 index 0000000..2b174d3 --- /dev/null +++ b/include/shared/avl_tree.h @@ -0,0 +1,270 @@ +#ifndef _SHARED_AVL_TREE_H_ +#define _SHARED_AVL_TREE_H_ + +#define ALLOC kalloc +#define FREE kfree + +#include <assert.h> +#include <stdio.h> + +#include "kern/common.h" +#include "kern/mem.h" +#include "shared/stdmacro.h" + +#define get_height(n) ((n) == NULL ? 0 : (n)->height) +#define max(a, b) ((a) < (b) ? (b) : (a)) +#define reset_height(node) \ + (node)->height = \ + (max(get_height((node)->right), get_height((node)->left)) + 1) + +#define avl_tree_t(T) CONCAT(avl_tree__, T) +#define avl_tree_node_t(T) CONCAT(avl_tree_node__, T) + +#define avl_tree_new(T) CONCAT(avl_tree_new__, T) +#define avl_tree_free(T) CONCAT(avl_tree_free__, T) +#define avl_tree_size(T) CONCAT(avl_tree_size__, T) +#define avl_tree_insert(T) CONCAT(avl_tree_insert__, T) +#define avl_tree_find(T) CONCAT(avl_tree_find__, T) +#define avl_tree_erase(T) CONCAT(avl_tree_erase__, T) +#define avl_tree_height(T) CONCAT(avl_tree_height__, T) + +#define null_dtor(a) + +#define AVL_TREE_DECL(T) \ + typedef struct CONCAT(AVL_TREE_NODE_, T) { \ + T value; \ + size_t height; \ + struct CONCAT(AVL_TREE_NODE_, T) * left; \ + struct CONCAT(AVL_TREE_NODE_, T) * right; \ + } avl_tree_node_t(T); \ + \ + typedef struct { \ + avl_tree_node_t(T) * root; \ + } avl_tree_t(T); \ + \ + size_t avl_tree_height(T)(avl_tree_t(T) * tree); \ + avl_tree_t(T) * avl_tree_new(T)(); \ + void avl_tree_free(T)(avl_tree_t(T) * tree); \ + size_t avl_tree_size(T)(const avl_tree_t(T) * avl_tree); \ + T* avl_tree_insert(T)(avl_tree_t(T) * avl_tree, T value); \ + T* avl_tree_find(T)(const avl_tree_t(T)*, T val); \ + bool avl_tree_erase(T)(avl_tree_t(T) * tree, T val, T * out); + +#define AVL_TREE_IMPL(T, CMP, DTOR) \ + avl_tree_t(T) * avl_tree_new(T)() \ + { \ + avl_tree_t(T)* ret = ALLOC(sizeof(avl_tree_t(T))); \ + ret->root = NULL; \ + return ret; \ + } \ + static void CONCAT(avl_loose_free__, T)(avl_tree_node_t(T) * node) \ + { \ + if (!node) return; \ + CONCAT(avl_loose_free__, T)(node->right); \ + CONCAT(avl_loose_free__, T)(node->left); \ + DTOR(node->value); \ + FREE(node); \ + } \ + void avl_tree_free(T)(avl_tree_t(T) * tree) \ + { \ + CONCAT(avl_loose_free__, T)(tree->root); \ + FREE(tree); \ + } \ + static inline size_t CONCAT( \ + loose_size__, T)(const avl_tree_node_t(T) * node) \ + { \ + if (!node) return 0; \ + return 1 + CONCAT(loose_size__, T)(node->left) + \ + CONCAT(loose_size__, T)(node->right); \ + } \ + size_t avl_tree_size(T)(const avl_tree_t(T) * tree) \ + { \ + return CONCAT(loose_size__, T)(tree->root); \ + } \ + static int CONCAT(balance_factor, T)(avl_tree_node_t(T) * node) \ + { \ + return get_height(node->left) - get_height(node->right); \ + } \ + static avl_tree_node_t(T) * CONCAT(ll_rotate, T)(avl_tree_node_t(T) * node) \ + { \ + avl_tree_node_t(T)* child = node->left; \ + node->left = child->right; \ + reset_height(node); \ + child->right = node; \ + reset_height(child); \ + return child; \ + } \ + static avl_tree_node_t(T) * CONCAT(rr_rotate, T)(avl_tree_node_t(T) * node) \ + { \ + avl_tree_node_t(T)* child = node->right; \ + node->right = child->left; \ + reset_height(node); \ + child->left = node; \ + reset_height(child); \ + return child; \ + } \ + static avl_tree_node_t(T) * CONCAT(rl_rotate, T)(avl_tree_node_t(T) * node) \ + { \ + avl_tree_node_t(T)* child = node->right; \ + node->right = CONCAT(ll_rotate, T)(child); \ + reset_height(node); \ + return CONCAT(rr_rotate, T)(node); \ + } \ + static avl_tree_node_t(T) * CONCAT(lr_rotate, T)(avl_tree_node_t(T) * node) \ + { \ + avl_tree_node_t(T)* child = node->left; \ + node->left = CONCAT(rr_rotate, T)(child); \ + reset_height(node); \ + return CONCAT(ll_rotate, T)(node); \ + } \ + static avl_tree_node_t(T) * \ + CONCAT(avl_tree_balance_, T)(avl_tree_node_t(T) * node) \ + { \ + int d = CONCAT(balance_factor, T)(node); \ + if (d > 1) { \ + if (CONCAT(balance_factor, T)(node->left) > 0) { \ + return CONCAT(ll_rotate, T)(node); \ + } else { \ + return CONCAT(lr_rotate, T)(node); \ + } \ + } else if (d < -1) { \ + if (CONCAT(balance_factor, T)(node->right) > 0) { \ + return CONCAT(rl_rotate, T)(node); \ + } else { \ + return CONCAT(rr_rotate, T)(node); \ + } \ + } \ + \ + return node; \ + } \ + static avl_tree_node_t(T) * \ + CONCAT(avl_tree_loose_insert_, T)( \ + avl_tree_node_t(T) * node, T value, T * *ptr_out) \ + { \ + if (!node) { \ + node = ALLOC(sizeof(avl_tree_node_t(T))); \ + assert(node); \ + node->left = NULL; \ + node->right = NULL; \ + node->value = value; \ + node->height = 1; \ + *ptr_out = &node->value; \ + } else { \ + typeof(CMP(node->value, value)) cmp = CMP(node->value, value); \ + if (cmp < 0) { \ + node->left = \ + CONCAT(avl_tree_loose_insert_, T)(node->left, value, ptr_out); \ + reset_height(node); \ + node = CONCAT(avl_tree_balance_, T)(node); \ + } else if (cmp > 0) { \ + node->right = \ + CONCAT(avl_tree_loose_insert_, T)(node->right, value, ptr_out); \ + reset_height(node); \ + node = CONCAT(avl_tree_balance_, T)(node); \ + } \ + } \ + return node; \ + } \ + T* avl_tree_insert(T)(avl_tree_t(T) * tree, T value) \ + { \ + T* ret; \ + tree->root = CONCAT(avl_tree_loose_insert_, T)(tree->root, value, &ret); \ + return ret; \ + } \ + size_t avl_tree_height(T)(avl_tree_t(T) * tree) \ + { \ + if (!tree) return 0; \ + return get_height(tree->root); \ + } \ + static T* CONCAT(loose_avl_tree_find, T)(avl_tree_node_t(T) * node, T value) \ + { \ + if (!node) return NULL; \ + \ + typeof(CMP(node->value, value)) cmp = CMP(node->value, value); \ + if (cmp > 0) { \ + return CONCAT(loose_avl_tree_find, T)(node->right, value); \ + } else if (cmp < 0) { \ + return CONCAT(loose_avl_tree_find, T)(node->left, value); \ + } \ + return &node->value; \ + } \ + T* avl_tree_find(T)(const avl_tree_t(T) * tree, T val) \ + { \ + if (!tree) return NULL; \ + return CONCAT(loose_avl_tree_find, T)(tree->root, val); \ + } \ + static avl_tree_node_t(T) * \ + CONCAT(pluck_left, T)(avl_tree_node_t(T) * node, T * into) \ + { \ + if (node->left) { \ + node->left = CONCAT(pluck_left, T)(node->left, into); \ + reset_height(node); \ + return CONCAT(avl_tree_balance_, T)(node); \ + } else { \ + *into = node->value; \ + FREE(node); \ + return node->right; \ + } \ + } \ + static avl_tree_node_t(T) * \ + CONCAT(pluck_right, T)(avl_tree_node_t(T) * node, T * into) \ + { \ + if (node->right) { \ + node->right = CONCAT(pluck_right, T)(node->right, into); \ + reset_height(node); \ + return CONCAT(avl_tree_balance_, T)(node); \ + } else { \ + *into = node->value; \ + FREE(node); \ + return node->left; \ + } \ + } \ + avl_tree_node_t(T) * \ + CONCAT(loose_avl_tree_erase, T)( \ + avl_tree_node_t(T) * node, T value, bool* out, T* erased) \ + { \ + if (!node) { \ + *out = 0; \ + return NULL; \ + } \ + typeof(CMP(node->value, value)) cmp = CMP(node->value, value); \ + if (cmp == 0) { \ + if (erased) *erased = node->value; \ + *out = 1; \ + if (!node->right && !node->left) { \ + FREE(node); \ + return NULL; \ + } \ + if (get_height(node->right) > get_height(node->left)) { \ + node->right = CONCAT(pluck_left, T)(node->right, &node->value); \ + reset_height(node); \ + node = CONCAT(avl_tree_balance_, T)(node); \ + return node; \ + } \ + node->left = CONCAT(pluck_right, T)(node->left, &node->value); \ + reset_height(node); \ + node = CONCAT(avl_tree_balance_, T)(node); \ + return node; \ + } else if (cmp < 0) { \ + node->left = \ + CONCAT(loose_avl_tree_erase, T)(node->left, value, out, erased); \ + reset_height(node); \ + node = CONCAT(avl_tree_balance_, T)(node); \ + } else { \ + node->right = \ + CONCAT(loose_avl_tree_erase, T)(node->right, value, out, erased); \ + reset_height(node); \ + node = CONCAT(avl_tree_balance_, T)(node); \ + } \ + return node; \ + } \ + bool avl_tree_erase(T)(avl_tree_t(T) * tree, T val, T * erased) \ + { \ + if (!tree) return 0; \ + bool out; \ + tree->root = \ + CONCAT(loose_avl_tree_erase, T)(tree->root, val, &out, erased); \ + return out; \ + } + +#endif /* _SHARED_AVL_TREE_H_ */ diff --git a/include/shared/avl_tree.h.gch b/include/shared/avl_tree.h.gch Binary files differnew file mode 100644 index 0000000..78fc043 --- /dev/null +++ b/include/shared/avl_tree.h.gch diff --git a/include/shared/linked_list.h b/include/shared/linked_list.h index 3f8e075..ec72377 100644 --- a/include/shared/linked_list.h +++ b/include/shared/linked_list.h @@ -1,15 +1,21 @@ #ifndef SHARED_LINKED_LIST_H_ #define SHARED_LINKED_LIST_H_ -#define linked_list_t(type) linked_list_##type##_t -#define linked_list_node_t(type) linked_list_node_##type##_t -#define linked_list_push_back(type) linked_list_push_back_##type -#define linked_list_push_front(type) linked_list_push_front_##type -#define linked_list_pop_front(type) linked_list_pop_front_##type -#define linked_list_pop_back(type) linked_list_pop_back_##type -#define linked_list_front(type) linked_list_front_##type -#define linked_list_back(type) linked_list_back_##type -#define linked_list_length(type) linked_list_length_##type +#include "kern/common.h" + +#define linked_list_t(T) linked_list___##T##___t +#define linked_list_node_t(T) linked_list_node___##T##___t +#define linked_list_itr_t(T) linked_list_node_itr___##T##___t +#define linked_list_push_back(T) linked_list_push_back___##T +#define linked_list_push_front(T) linked_list_push_front___##T +#define linked_list_pop_front(T) linked_list_pop_front___##T +#define linked_list_pop_back(T) linked_list_pop_back___##T +#define linked_list_front(T) linked_list_front___##T +#define linked_list_back(T) linked_list_back___##T +#define linked_list_length(T) linked_list_length___##T +#define linked_list_find_match(T) linked_list_find_match___##T +#define linked_list_remove_first_match(T) linked_list_remove_first_match___##T +#define linked_list_remove(T) linked_list_remove___##T #define LINKED_LIST_INIT \ { \ @@ -24,108 +30,171 @@ #define NO_ATTRIBUTE -#define LINKED_LIST_DECL(type) LINKED_LIST_INTERNAL_DECL(type, NO_ATTRIBUTE) +#define LINKED_LIST_DECL(T) LINKED_LIST_INTERNAL_DECL(T, NO_ATTRIBUTE) -#define LINKED_LIST_STATIC_DECL(type) LINKED_LIST_INTERNAL_DECL(type, static) +#define LINKED_LIST_STATIC_DECL(T) LINKED_LIST_INTERNAL_DECL(T, static inline) -#define LINKED_LIST_INTERNAL_DECL(type, attr) \ - typedef struct LINKED_LIST_NODE_##type { \ - type value; \ - struct LINKED_LIST_NODE_##type* next; \ - } linked_list_node_t(type); \ +#define LINKED_LIST_INTERNAL_DECL(T, attr) \ + typedef struct LINKED_LIST_NODE_##T { \ + T value; \ + struct LINKED_LIST_NODE_##T* next; \ + } linked_list_node_t(T); \ + \ + typedef struct { \ + linked_list_node_t(T) * prev; \ + linked_list_node_t(T) * cur; \ + } linked_list_itr_t(T); \ \ typedef struct { \ - linked_list_node_t(type) * head; \ - } linked_list_t(type); \ + linked_list_node_t(T) * head; \ + } linked_list_t(T); \ \ - attr void linked_list_push_back(type)(linked_list_t(type) * ll, type value); \ - attr void linked_list_push_front(type)( \ - linked_list_t(type) * ll, type value); \ - attr void linked_list_pop_back(type)(linked_list_t(type) * ll); \ - attr void linked_list_pop_front(type)(linked_list_t(type) * ll); \ - attr type* linked_list_front(type)(linked_list_t(type) * ll); \ - attr type* linked_list_back(type)(linked_list_t(type) * ll); + attr void linked_list_push_back(T)(linked_list_t(T) * ll, T value); \ + attr void linked_list_push_front(T)(linked_list_t(T) * ll, T value); \ + attr void linked_list_pop_back(T)(linked_list_t(T) * ll); \ + attr void linked_list_pop_front(T)(linked_list_t(T) * ll); \ + attr size_t linked_list_length(T)(const linked_list_t(T) * ll); \ + attr T* linked_list_front(T)(const linked_list_t(T) * ll); \ + attr bool linked_list_find_match(T)( \ + const linked_list_t(T) * ll, \ + linked_list_itr_t(T) * itr_out, \ + bool (*match)(T, void*), \ + void* closure); \ + attr bool linked_list_remove_first_match(T)( \ + linked_list_t(T) * ll, T * out, bool (*match)(T, void*), void* closure); \ + attr T* linked_list_back(T)(const linked_list_t(T) * ll); -#define LINKED_LIST_IMPL(type) LINKED_LIST_INTERNAL_IMPL(type, NO_ATTRIBUTE) +#define LINKED_LIST_IMPL(T) LINKED_LIST_INTERNAL_IMPL(T, NO_ATTRIBUTE) -#define LINKED_LIST_STATIC_IMPL(type) LINKED_LIST_INTERNAL_IMPL(type, static) +#define LINKED_LIST_STATIC_IMPL(T) LINKED_LIST_INTERNAL_IMPL(T, static inline) -#define LINKED_LIST_INTERNAL_IMPL(type, attr) \ - attr void linked_list_push_back(type)(linked_list_t(type) * ll, type value) \ - { \ - if (!ll->head) { \ - ll->head = kalloc(sizeof(linked_list_node_t(type))); \ - ll->head->value = value; \ - ll->head->next = NULL; \ - } else { \ - linked_list_node_t(type)* c = ll->head; \ - while (c->next) c = c->next; \ - c->next = kalloc(sizeof(linked_list_node_t(type))); \ - c->next->next = NULL; \ - c->next->value = value; \ - } \ - } \ - attr void linked_list_push_front(type)(linked_list_t(type) * ll, type value) \ - { \ - if (!ll->head) { \ - ll->head = kalloc(sizeof(linked_list_node_t(type))); \ - ll->head->value = value; \ - ll->head->next = NULL; \ - } else { \ - linked_list_node_t(type)* node = \ - kalloc(sizeof(linked_list_node_t(type))); \ - node->next = ll->head; \ - node->value = value; \ - ll->head = node; \ - } \ - } \ - attr void linked_list_pop_back(type)(linked_list_t(type) * ll) \ - { \ - if (!ll->head) { \ - return; \ - } \ - if (!ll->head->next) { \ - kfree(ll->head); \ - ll->head = NULL; \ - return; \ - } \ - linked_list_node_t(type)* c = ll->head; \ - while (c->next->next) c = c->next; \ - kfree(c->next); \ - c->next = NULL; \ - return; \ - } \ - attr void linked_list_pop_front(type)(linked_list_t(type) * ll) \ - { \ - if (!ll->head) { \ - return; \ - } \ - linked_list_node_t(type)* node = ll->head; \ - ll->head = node->next; \ - kfree(node); \ - } \ - attr type* linked_list_front(type)(linked_list_t(type) * ll) \ - { \ - if (!ll->head) { \ - return NULL; \ - } \ - return &ll->head->value; \ - } \ - attr type* linked_list_back(type)(linked_list_t(type) * ll) \ - { \ - if (!ll->head) { \ - return NULL; \ - } \ - linked_list_node_t(type)* cur = ll->head; \ - while (cur->next) cur = cur->next; \ - return &cur->value; \ - } \ - attr size_t linked_list_length(type)(linked_list_t(type) * ll) \ - { \ - linked_list_node_t(type) * cur; \ - size_t ret = 0; \ - for (cur = ll->head; cur; cur = cur->next) ++ret; \ - return ret; \ - } +#define PRAGMA(x) _Pragma(#x) +#define LINKED_LIST_INTERNAL_IMPL(T, attr) \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wunused-function\"") \ + attr void linked_list_push_back(T)(linked_list_t(T) * ll, T value) \ + { \ + if (!ll->head) { \ + ll->head = kalloc(sizeof(linked_list_node_t(T))); \ + ll->head->value = value; \ + ll->head->next = NULL; \ + } else { \ + linked_list_node_t(T)* c = ll->head; \ + while (c->next) c = c->next; \ + c->next = kalloc(sizeof(linked_list_node_t(T))); \ + c->next->next = NULL; \ + c->next->value = value; \ + } \ + } \ + attr void linked_list_push_front(T)(linked_list_t(T) * ll, T value) \ + { \ + if (!ll->head) { \ + ll->head = kalloc(sizeof(linked_list_node_t(T))); \ + ll->head->value = value; \ + ll->head->next = NULL; \ + } else { \ + linked_list_node_t(T)* node = kalloc(sizeof(linked_list_node_t(T))); \ + node->next = ll->head; \ + node->value = value; \ + ll->head = node; \ + } \ + } \ + attr void linked_list_pop_back(T)(linked_list_t(T) * ll) \ + { \ + if (!ll->head) { \ + return; \ + } \ + if (!ll->head->next) { \ + kfree(ll->head); \ + ll->head = NULL; \ + return; \ + } \ + linked_list_node_t(T)* c = ll->head; \ + while (c->next->next) c = c->next; \ + kfree(c->next); \ + c->next = NULL; \ + return; \ + } \ + attr void linked_list_pop_front(T)(linked_list_t(T) * ll) \ + { \ + if (!ll->head) { \ + return; \ + } \ + linked_list_node_t(T)* node = ll->head; \ + ll->head = node->next; \ + kfree(node); \ + } \ + attr T* linked_list_front(T)(const linked_list_t(T) * ll) \ + { \ + if (!ll->head) { \ + return NULL; \ + } \ + return &ll->head->value; \ + } \ + attr T* linked_list_back(T)(const linked_list_t(T) * ll) \ + { \ + if (!ll->head) { \ + return NULL; \ + } \ + linked_list_node_t(T)* cur = ll->head; \ + while (cur->next) cur = cur->next; \ + return &cur->value; \ + } \ + attr size_t linked_list_length(T)(const linked_list_t(T) * ll) \ + { \ + linked_list_node_t(T) * cur; \ + size_t ret = 0; \ + for (cur = ll->head; cur; cur = cur->next) ++ret; \ + return ret; \ + } \ + attr bool linked_list_find_match(T)( \ + const linked_list_t(T) * ll, \ + linked_list_itr_t(T) * itr_out, \ + bool (*match)(T, void*), \ + void* closure) \ + { \ + linked_list_node_t(T)* prev = NULL; \ + linked_list_node_t(T)* cur = NULL; \ + if (!ll || !ll->head) { \ + return 0; \ + } \ + for (cur = ll->head; cur != NULL; cur = (prev = cur)->next) { \ + if (match(cur->value, closure)) { \ + itr_out->prev = prev; \ + itr_out->cur = cur; \ + return 1; \ + } \ + } \ + return 0; \ + } \ + attr bool linked_list_remove_first_match(T)( \ + linked_list_t(T) * ll, T * out, bool (*match)(T, void*), void* closure) \ + { \ + linked_list_itr_t(T) ctx; \ + if (!linked_list_find_match(T)(ll, &ctx, match, closure)) { \ + return 0; \ + } \ + if (out) *out = ctx.cur->value; \ + if (!ctx.prev) { \ + ll->head = ctx.cur->next; \ + } else { \ + ctx.prev->next = ctx.cur->next; \ + } \ + kfree(ctx.cur); \ + return 1; \ + } \ + int memcmp(const void* s1, const void* s2, size_t n); \ + static inline bool linked_list___##T##___eq(T elem, void* other) \ + { \ + return memcmp(&elem, other, sizeof(T)) == 0; \ + } \ + attr bool linked_list_remove(T)(linked_list_t(T) * ll, T val) \ + { \ + bool (*fn)(T, void*) = &linked_list___##T##___eq; \ + bool ret = 0; \ + while (linked_list_remove_first_match(T)(ll, NULL, fn, &val)) ret = 1; \ + return ret; \ + } \ + _Pragma("GCC diagnostic pop") #endif /* SHARED_LINKED_LIST_H_ */ diff --git a/include/shared/map.h b/include/shared/map.h new file mode 100644 index 0000000..c9ee6b9 --- /dev/null +++ b/include/shared/map.h @@ -0,0 +1,90 @@ +#ifndef _SHARED_MAP_H_ +#define _SHARED_MAP_H_ + +#include "shared/avl_tree.h" +#include "shared/stdmacro.h" + +#define MAP_PASTE_KV(K, V) CONCAT(CONCAT(K, ___), V) +#define map_entry_t(K, V) CONCAT(map_entry_t__, MAP_PASTE_KV(K, V)) +#define map_t(K, V) CONCAT(map_t__, MAP_PASTE_KV(K, V)) + +#define map_new(K, V) CONCAT(map_new__, MAP_PASTE_KV(K, V)) +#define map_free(K, V) CONCAT(map_free__, MAP_PASTE_KV(K, V)) +#define map_put(K, V) CONCAT(map_put__, MAP_PASTE_KV(K, V)) +#define map_get(K, V) CONCAT(map_get__, MAP_PASTE_KV(K, V)) +#define map_erase(K, V) CONCAT(map_erase__, MAP_PASTE_KV(K, V)) +#define _map_compare_(K, V) CONCAT(_map_compare__, MAP_PASTE_KV(K, V)) +#define _map_dtor_(K, V) CONCAT(_map_dtor__, MAP_PASTE_KV(K, V)) + +#define MAP_DECL(K, V) \ + typedef struct { \ + K key; \ + V value; \ + } map_entry_t(K, V); \ + AVL_TREE_DECL(map_entry_t(K, V)); \ + typedef struct { \ + avl_tree_t(map_entry_t(K, V)) * tree; \ + } map_t(K, V); \ + map_t(K, V) * map_new(K, V)(); \ + void map_free(K, V)(map_t(K, V) * map); \ + V* map_get(K, V)(map_t(K, V) * map, K key); \ + V* map_put(K, V)(map_t(K, V) * map, K key, V value); \ + bool map_erase(K, V)(map_t(K, V) * map, K key, V * out); + +#define MAP_IMPL(K, V, CMP, DTOR) \ + static inline int _map_compare_(K, V)( \ + map_entry_t(K, V) e1, map_entry_t(K, V) e2) \ + { \ + return CMP(e1.key, e2.key); \ + } \ + static inline void _map_dtor_(K, V)(map_entry_t(K, V) e1) \ + { \ + DTOR(e1.value); \ + } \ + AVL_TREE_IMPL(map_entry_t(K, V), _map_compare_(K, V), _map_dtor_(K, V)) \ + map_t(K, V) * map_new(K, V)() \ + { \ + map_t(K, V)* ret = ALLOC(sizeof(map_t(K, V))); \ + ret->tree = avl_tree_new(map_entry_t(K, V))(); \ + return ret; \ + } \ + void map_free(K, V)(map_t(K, V) * map) { \ + avl_tree_free(map_entry_t(K, V))(map->tree); \ + FREE(map); \ + }\ + V* map_get(K, V)(map_t(K, V) * map, K key) \ + { \ + map_entry_t(K, V) key_; \ + key_.key = key; \ + map_entry_t(K, V)* entry = \ + avl_tree_find(map_entry_t(K, V))(map->tree, key_); \ + \ + if (!entry) { \ + return NULL; \ + } \ + \ + return &entry->value; \ + } \ + V* map_put(K, V)(map_t(K, V) * map, K key, V val) \ + { \ + map_entry_t(K, V) entry; \ + entry.key = key; \ + entry.value = val; \ + map_entry_t(K, V)* ref = \ + avl_tree_insert(map_entry_t(K, V))(map->tree, entry); \ + return &ref->value; \ + } \ + bool map_erase(K, V)(map_t(K, V) * map, K key, V * out) \ + { \ + map_entry_t(K, V) key_; \ + key_.key = key; \ + \ + map_entry_t(K, V) entry_out; \ + bool ret = avl_tree_erase(map_entry_t(K, V))(map->tree, key_, &entry_out); \ + if (ret) { \ + *out = entry_out.value; \ + } \ + return ret; \ + } + +#endif /* _SHARED_MAP_H_ */ diff --git a/include/shared/math.h b/include/shared/math.h new file mode 100644 index 0000000..6aec0e7 --- /dev/null +++ b/include/shared/math.h @@ -0,0 +1,19 @@ +#ifndef _SHARED_MATH_H_ +#define _SHARED_MATH_H_ + +#include "kern/common.h" + +/* Returns round(sin(2πn / 256) * 127.5 + 127.5). */ +uint8_t byte_sin(uint8_t n); + +static inline uint8_t byte_scale(uint8_t n, uint8_t sc) { + return n * sc / 255; +} + +#define min(a, b) (a) < (b) ? (a) : (b) +#define max(a, b) (a) > (b) ? (a) : (b) + +/* returns (in / 256)^n * 256. */ +uint8_t amp(uint8_t in, uint8_t n); + +#endif /* _SHARED_MATH_H_ */ diff --git a/include/shared/stdmacro.h b/include/shared/stdmacro.h new file mode 100644 index 0000000..6010982 --- /dev/null +++ b/include/shared/stdmacro.h @@ -0,0 +1,7 @@ +#ifndef _SHARED_STDMACRO_H_ +#define _SHARED_STDMACRO_H_ + +#define CONCAT_(a, b) a ## b +#define CONCAT(a, b) CONCAT_(a, b) + +#endif /* _SHARED_STDMACRO_H_ */ diff --git a/src/drv/ir/control.c b/src/drv/ir/control.c new file mode 100644 index 0000000..6be2cf1 --- /dev/null +++ b/src/drv/ir/control.c @@ -0,0 +1,43 @@ +#include "drv/ir/control.h" + +#include "drv/ir/ir.h" +#include "shared/map.h" + +typedef struct { + void* closure; + void (*fn)(uint32_t code, void* closure); +} ir_code_listener_t; + +#define integer_cmp_(a, b) (a - b) +MAP_DECL(uint32_t, ir_code_listener_t); +MAP_IMPL(uint32_t, ir_code_listener_t, integer_cmp_, null_dtor); + +static map_t(uint32_t, ir_code_listener_t) * listeners; + +static void ir_callback(const ir_code_t* ir) +{ + uint32_t code; + if (ir_generic_decode(ir, &code)) { + ir_code_listener_t* l = + map_get(uint32_t, ir_code_listener_t)(listeners, code); + + if (l) { + l->fn(code, l->closure); + } + } +} + +void add_ir_code_callback_( + uint32_t code, void (*fn)(uint32_t, void*), void* closure) +{ + ir_code_listener_t l; + l.fn = fn; + l.closure = closure; + map_put(uint32_t, ir_code_listener_t)(listeners, code, l); +} + +void enable_ir_control() +{ + listeners = map_new(uint32_t, ir_code_listener_t)(); + add_ir_callback(ir_callback); +} diff --git a/src/drv/ir/ir.c b/src/drv/ir/ir.c new file mode 100644 index 0000000..c84417d --- /dev/null +++ b/src/drv/ir/ir.c @@ -0,0 +1,160 @@ +#include "drv/ir/ir.h" + +#include "arch/stm32l4xxx/peripherals/exti.h" +#include "arch/stm32l4xxx/peripherals/irq.h" +#include "arch/stm32l4xxx/peripherals/rcc.h" +#include "arch/stm32l4xxx/peripherals/syscfg.h" +#include "arch/stm32l4xxx/peripherals/tim.h" +#include "kern/gpio/gpio_manager.h" +#include "kern/mem.h" +#include "kern/log.h" +#include "kern/panic.h" +#include "shared/linked_list.h" + +typedef void (*ir_callback_t)(const ir_code_t*); + +LINKED_LIST_DECL(ir_callback_t); +LINKED_LIST_IMPL(ir_callback_t); + +static linked_list_t(ir_callback_t) callbacks; +static ir_code_t read_into; + +void on_exti9_5() +{ + EXTI.p_r1 |= 1 << 6; + /* The IR pin has been activated. */ + + if (read_into.n == 0) { + /* Starting fresh, start the timer. */ + regset(TIM2.c_r1, tim_cen, 1); + read_into.ts[read_into.n ++] = 0; + } else { + uint32_t ts = TIM2.cnt; + if (read_into.n < 64) { + read_into.ts[read_into.n++] = ts; + } + } +} + +typedef enum { + PARITY_LOW = 0, + PARITY_HIGH = 1 +} ir_parity_t; + +static ir_parity_t ir_discover_parity(const ir_code_t* code) +{ + uint32_t max_even = 0; + uint32_t min_even = (uint32_t) -1; + + uint32_t max_odd = 0; + uint32_t min_odd = (uint32_t) -1; + + for (int i = 3; i < code->n; ++ i) { + uint32_t n = code->ts[i] - code->ts[i-1]; + if (i % 2 == 0) { + if (n < min_even) { + min_even = n; + } else if (n > max_even) { + max_even = n; + } + } else { + if (n < min_odd) { + min_odd = n; + } else if (n > max_odd) { + max_odd = n; + } + } + } + + if (max_even - min_even > max_odd - min_odd) { + return PARITY_LOW; + } else { + return PARITY_HIGH; + } +} + +int ir_generic_decode( + const ir_code_t* code, uint32_t* out_) +{ + if (code->n < 5) { + return 0; + } + + uint32_t min = (uint32_t) -1; + uint32_t max = 0; + + uint32_t start = 4 + ir_discover_parity(code); + + for (int i = start; i < code->n; i += 2) { + uint32_t n = code->ts[i] - code->ts[i-1]; + + if (n > max) { + max = n; + } else if (n < min) { + min = n; + } + } + + uint32_t mid = (max + min) / 2; + + uint32_t out = 0; + uint32_t mask = 0x80000000; + + for (int i = start; i < code->n; i += 2) { + if ((code->ts[i] - code->ts[i-1]) >= mid) { + out |= mask; + } + mask >>= 1; + } + + *out_ = out; + return 1; +} + +void on_tim2() +{ + if (regget(TIM2.s_r, tim_uif)) { + regset(TIM2.s_r, tim_uif, 0); + } + + /* The timer has run out. */ + linked_list_foreach(callbacks, cb) { cb(&read_into); } + + read_into.n = 0; +} + +void add_ir_callback(ir_callback_t callback) +{ + linked_list_push_front(ir_callback_t)(&callbacks, callback); +} + +void ir_begin_listen() +{ + int ec; + + gpio_pin_opts_t opts = DEFAULT_GPIO_OPTS_INPUT; + opts.pull_dir = GPIO_PULL_DIR_NONE; + gpio_reserved_pin_t pb6 = reserve_gpio_pin(GPIO_PIN_PB6, &opts, &ec); + + regset(RCC.apb1en1_r, rcc_tim2en, 1); + regset(RCC.apb2en_r, rcc_syscfgen, 1); + + enable_interrupt(IRQ_TIM2); + enable_interrupt(IRQ_EXTI9_5); + + /* Configure the timer. */ + TIM2.psc = 80; /* Counts every microsecond. */ + TIM2.ar_r = 50000; /* Stop after 50ms. */ + regset(TIM2.die_r, tim_uie, 1); /* Enable interrupts. */ + regset(TIM2.c_r1, tim_opm, 1); /* Set one-pulse-mode. */ + + /* Configure the external interrupts. */ + regset(SYSCFG.extic_r2, syscfg_exti6, 1 /* Port B. */); + regset(EXTI.im_r1, exti_im_n(6), 1); /* Enable the EXTI interrupt. */ + regset(EXTI.fts_r1, exti_ft_n(6), 1); /* Enable for falling edge. */ + regset(EXTI.rts_r1, exti_rt_n(6), 1); /* Enable for rising edge. */ + + if (ec) { + panic("Unable to reserve GPIO pin ec=%d\n", ec); + } +} diff --git a/src/drv/ws2812B/ws2812b.c b/src/drv/ws2812B/ws2812b.c index 3cf3570..aeb3784 100644 --- a/src/drv/ws2812B/ws2812b.c +++ b/src/drv/ws2812B/ws2812b.c @@ -4,32 +4,6 @@ #include "kern/mem.h" #include "kern/panic.h" -uint8_t sintable[256] = { - 128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 162, 165, 167, 170, - 173, 176, 179, 182, 185, 188, 190, 193, 196, 198, 201, 203, 206, 208, 211, - 213, 215, 218, 220, 222, 224, 226, 228, 230, 232, 234, 235, 237, 238, 240, - 241, 243, 244, 245, 246, 248, 249, 250, 250, 251, 252, 253, 253, 254, 254, - 254, 255, 255, 255, 255, 255, 255, 255, 254, 254, 254, 253, 253, 252, 251, - 250, 250, 249, 248, 246, 245, 244, 243, 241, 240, 238, 237, 235, 234, 232, - 230, 228, 226, 224, 222, 220, 218, 215, 213, 211, 208, 206, 203, 201, 198, - 196, 193, 190, 188, 185, 182, 179, 176, 173, 170, 167, 165, 162, 158, 155, - 152, 149, 146, 143, 140, 137, 134, 131, 128, 124, 121, 118, 115, 112, 109, - 106, 103, 100, 97, 93, 90, 88, 85, 82, 79, 76, 73, 70, 67, 65, - 62, 59, 57, 54, 52, 49, 47, 44, 42, 40, 37, 35, 33, 31, 29, - 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, 10, 9, 7, 6, - 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, 10, 11, - 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, 37, - 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76, - 79, 82, 85, 88, 90, 93, 97, 100, 103, 106, 109, 112, 115, 118, 121, - 124, -}; - -uint8_t byte_sin(uint8_t n) -{ - return sintable[n]; -} - ws2812b_t* ws2812b_new(spi_select_t spi_select, int* ec) { spi_opts_t spi_opts = DEFAULT_SPI_OPTS; diff --git a/src/kern/gpio/gpio_manager.c b/src/kern/gpio/gpio_manager.c index 749ae79..ea720e9 100644 --- a/src/kern/gpio/gpio_manager.c +++ b/src/kern/gpio/gpio_manager.c @@ -388,3 +388,12 @@ void set_gpio_pin_low(gpio_reserved_pin_t pin) regset(portcfg->od_r, (1 << off), 0); } + +bool get_gpio_pin_input_state(gpio_reserved_pin_t pin) +{ + int off; + gpio_port_config_t* portcfg; + get_gpio_pin_port_off(pin.v_, &portcfg, &off); + + return !!regget(portcfg->id_r, (1 << off)); +} diff --git a/src/kern/lib.c b/src/kern/lib.c index 24e1658..e0d3e23 100644 --- a/src/kern/lib.c +++ b/src/kern/lib.c @@ -37,6 +37,11 @@ void decimalify(int v, char* into) *into = 0; return; } else { + if (v < 0) { + v = -v; + *(into++) = '-'; + } + while (v > 0) { *(into++) = 0x30 + (v % 10); v /= 10; diff --git a/src/kern/main.c b/src/kern/main.c index fab2eb9..cdaa2b1 100644 --- a/src/kern/main.c +++ b/src/kern/main.c @@ -1,11 +1,19 @@ +#include <assert.h> + #include "arch.h" #include "arch/arm/cortex-m4/mpu.h" #include "arch/stm32l4xxx/peripherals/clock.h" #include "arch/stm32l4xxx/peripherals/dma.h" +#include "arch/stm32l4xxx/peripherals/exti.h" #include "arch/stm32l4xxx/peripherals/irq.h" #include "arch/stm32l4xxx/peripherals/rcc.h" #include "arch/stm32l4xxx/peripherals/spi.h" +#include "arch/stm32l4xxx/peripherals/syscfg.h" #include "arch/stm32l4xxx/peripherals/system.h" +#include "arch/stm32l4xxx/peripherals/tim.h" +#include "drv/ir/control.h" +#include "drv/ir/ir.h" +#include "drv/ir/lg_remote_codes.h" #include "drv/ws2812B/ws2812b.h" #include "kern/delay.h" #include "kern/dma/dma_manager.h" @@ -19,8 +27,23 @@ #include "kern/priv.h" #include "kern/spi/spi_manager.h" #include "kern/systick/systick_manager.h" +#include "shared/math.h" #include "user/syscall.h" +volatile struct { + uint32_t time; + signed int timetick; + ws2812b_t* drv; + uint8_t brightness; + uint8_t n_leds; + uint8_t off; + uint8_t n_snow; + uint8_t n_red; + bool sleep; + bool power; + bool cool; +} state; + void on_hard_fault() { panic("Hard fault encountered!\n"); @@ -49,100 +72,189 @@ void configure_gpio() } } -static uint8_t* compiled; -static size_t compiled_len; +void printit(uint32_t code, const char* str) +{ + (void)code; + klogf("%s\n", str); +} + +volatile bool do_redraw; +static void on_systick() +{ + if (!state.sleep) state.time += state.timetick; +} + +void redraw() +{ + uint32_t red = 0, green = 0, blue = 0; + + for (int i = 0; i < state.n_leds; ++i) { + if (!state.power) { + ws2812b_write_rgb_sync(state.drv, 0, 0, 0); + continue; + } + + red = byte_scale(byte_sin(state.time / 1000 + i * 4), 255 - state.n_red) + + state.n_red; + green = 255 - red; + + if (state.cool) { + uint32_t tmp = green; + green = blue; + blue = tmp; + } -extern uint8_t sintable[256]; + /* Add a little white flair that comes around every once in a while. */ -static uint32_t time; + uint32_t whitesum = 0; + if (state.n_snow) { + uint32_t white[] = { + /* Parallax "snow" */ + state.time / 179 + i * 8, + state.time / 193 + i * 12, + state.time / 211 + i * 16, + (state.time) / 233 + i * 8, + // (state.time + 128) / 233 + i * 8, + }; -static void on_systick(void* nil) + for (int i = 0; i < sizeof(white) / sizeof(uint32_t); ++i) { + if ((white[i] / 256) % state.n_snow == 0) { + white[i] = amp(byte_sin(white[i]), 20); + } else { + white[i] = 0; + } + whitesum += white[i]; + } + } + + ws2812b_write_rgb_sync( + state.drv, + byte_scale(min(red + whitesum, 255), state.brightness), + byte_scale(min(green + whitesum, 255), state.brightness), + byte_scale(min(blue + whitesum, 255), state.brightness)); + } + + ws2812b_latch(state.drv); + + delay(100000); +} + +void increment_it_u8(uint32_t unused, uint8_t* v) { - ++time; + (*v)++; } -#define min(a, b) (a) < (b) ? (a) : (b) +void decrement_it_u8(uint32_t unused, uint8_t* v) +{ + (*v)--; +} -static uint8_t amp(uint8_t in) +void timetick_up(uint32_t unused, void* nil) { - uint32_t out = in; + state.timetick += 5; +} - for (int i = 0; i < 20; ++i) { - out = (out * in) / 256; +void timetick_down(uint32_t unused, void* nil) +{ + state.timetick -= 5; +} + +void toggle_brightness(uint32_t unused, void* nil) +{ + klogf("Toggle %d\n", state.brightness); + if (state.brightness == 255) { + state.brightness = 32; + } else { + state.brightness = 255; } +} + +static void toggle_sleep(uint32_t unused, void* nil) +{ + state.sleep = !state.sleep; +} + +static void toggle_power(uint32_t unused, void* nil) +{ + state.power = !state.power; +} + +static void toggle_cool(uint32_t unused, void* nil) +{ + state.cool = !state.cool; +} + +static void set_snow() +{ + state.n_snow = (state.n_snow + 1) % 10; +} - return min(out, 255); +static void set_red() +{ + state.n_red += 50; + state.n_red %= 250; } -static uint32_t bytescale(uint32_t n, uint32_t sc) +static void reset_state() { - return n * sc / 255; + ws2812b_t* tmp = state.drv; + memset((void*) &state, 0, sizeof(state)); + state.drv = tmp; + state.brightness = 255; + state.n_leds = 250; + state.off = 8; + state.timetick = 10; + state.power = 1; + state.n_snow = 2; } /* Main function. This gets executed from the interrupt vector defined above. */ int main() { - klogf("Entering main\n"); - gpio_reserved_pin_t sysled = get_sysled(); + klogf("Entering Main\n"); systick_add_callback(on_systick, NULL); - enable_systick(1000); + enable_systick(10000); configure_gpio(); + ir_begin_listen(); + enable_ir_control(); + + add_ir_code_callback(RC_HIGH, printit, "RC_HIGH"); + add_ir_code_callback(RC_TEMP_UP, timetick_up, NULL); + add_ir_code_callback(RC_DRY, set_red, NULL); + add_ir_code_callback(RC_LOW, printit, "RC_LOW"); + add_ir_code_callback(RC_TEMP_DOWN, timetick_down, NULL); + add_ir_code_callback(RC_COOL, toggle_cool, NULL); + add_ir_code_callback(RC_CONTINUOUS, set_snow, "RC_CONTINUOUS"); + add_ir_code_callback(RC_FAN, toggle_brightness, NULL); + add_ir_code_callback(RC_SLEEP, toggle_sleep, NULL); + add_ir_code_callback(RC_UNITS, printit, "RC_UNITS"); + add_ir_code_callback(RC_TIMER, reset_state, NULL); + add_ir_code_callback(RC_POWER, toggle_power, NULL); + int ec; - ws2812b_t* drv = ws2812b_new(SPI_SELECT_SPI1, &ec); + state.drv = ws2812b_new(SPI_SELECT_SPI1, &ec); - if (ec || !drv) { + if (ec || !state.drv) { panic("Unable to create WS2812b driver :( (%d)\n", ec); } -#define SIZE 256 - rgb_t rgb[SIZE]; - for (int i = 0; i < SIZE; ++i) { - rgb[i].g = 0xff; - rgb[i].r = 0xff; - rgb[i].b = 0xff; - } - - uint32_t red = 0x40; - uint32_t green = 0x40; - uint32_t brightness = 255; + reset_state(); - for (int i = 0; i < 100; ++i) { - ws2812b_write_rgb_sync(drv, 0, 0, 0); + for (int i = 0; i < state.n_leds; ++i) { + /* Clear the LED strip. */ + disable_all_interrupts(); + ws2812b_write_rgb_sync(state.drv, 0, 0, 0); + enable_all_interrupts(); } - - ws2812b_latch(drv); + ws2812b_latch(state.drv); for (;;) { - set_gpio_pin_high(sysled); - ws2812b_latch(drv); - - int i; - for (i = 0; i < SIZE; ++i) { - red = byte_sin(time / 1000 + i * 4); - green = 255 - red; - - brightness = 3 * byte_sin(time / 5000) / 4 + 63; - - /* Add a little white flair that comes around every once in a while. */ - uint32_t white = time / 137 + i * 4; - if ((white / 256) % 8 == 0) { - white = amp(byte_sin(white)); - } else { - white = 0; - } - - ws2812b_write_rgb_sync( - drv, - bytescale(min(red + white, 255), brightness), - bytescale(min(green + white, 255), brightness), - bytescale(white, brightness)); - } - - set_gpio_pin_low(sysled); - - ws2812b_latch(drv); + // while (!do_redraw) + // ; + // do_redraw = 0; + if (!state.sleep) redraw(); } } diff --git a/src/kern/mem.c b/src/kern/mem.c index aa221ff..d7f7dc3 100644 --- a/src/kern/mem.c +++ b/src/kern/mem.c @@ -50,7 +50,7 @@ typedef uint64_t ptrint_t; typedef uint32_t ptrint_t; #endif -#define CANARY ((uint32_t) 0xdeadbeee) +#define CANARY ((uint32_t)0xdeadbeee) #define kalloc_node_in_use(node) ((node)->used_and_canary & 1) #define kalloc_node_get_canary(node) ((node)->used_and_canary & (~1)) #define WORD_SIZE (sizeof(uint32_t)) @@ -153,7 +153,7 @@ static void coalesce(kalloc_node_t* cur) /* Find the next used block. */ cur = orig; - while (!kalloc_node_in_use(cur) && !kalloc_node_out_of_range(cur)) { + while (!kalloc_node_out_of_range(cur) && !kalloc_node_in_use(cur)) { cur = kalloc_node_next(cur); } @@ -212,6 +212,8 @@ void debug_print_blocks() printf("---------------------------\n"); kalloc_node_t* cur = kalloc_node_at_off(0); + int total_words = 0; + int total_blocks = 0; while (!kalloc_node_out_of_range(cur)) { printf( "header (%04x)@%p {used=%d, size=%5d, prev=%04x, canary=%04x}\n", @@ -221,8 +223,13 @@ void debug_print_blocks() cur->size_words, cur->prev, kalloc_node_get_canary(cur)); + total_words += cur->size_words; + total_blocks ++; cur = kalloc_node_next(cur); } + + printf("Total words allocated: %d\n", total_words); + printf("Total blocks allocated: %d\n", total_blocks); } /* Tests that we can walk up and down the allocated blocks and that they @@ -305,4 +312,10 @@ int debug_kalloc_assert_consistency(char* error, size_t len) return 1; } +int debug_is_heap_empty() +{ + return (void*)((uint8_t*)kalloc_start + kalloc_start->size_words * sizeof(uint32_t) + sizeof(kalloc_node_t)) == + (void*)&HEAP_STOP; +} + #endif diff --git a/src/kern/stdlibrepl.c b/src/kern/stdlibrepl.c index 588191b..676b820 100644 --- a/src/kern/stdlibrepl.c +++ b/src/kern/stdlibrepl.c @@ -1,4 +1,5 @@ #include "arch.h" +#include "kern/panic.h" /* * Replacement for common stdlib functions that don't exist * on the ARM bare-metal compilation environment. @@ -15,14 +16,43 @@ size_t strlen(char* ch) #ifdef ARCH_STM32L4 -void memcpy(void* dest, void* src, size_t size) +void* memcpy(void* dest, const void* src, size_t size) { uint8_t* dest_ = dest; - uint8_t* src_ = src; + const uint8_t* src_ = src; - while(size --) { + while (size--) { *(dest_++) = *(src_++); } + + return dest; +} + +int memcmp(const void* s1_, const void* s2_, size_t size) +{ + const uint8_t* s1 = s1_; + const uint8_t* s2 = s2_; + + while (size--) { + if (*s1 != *s2) { + if (*s1 > *s2) { + return 1; + } else { + return -1; + } + } + + ++s1; + ++s2; + } + + return 0; +} + +void __assert_func( + const char* file, int line, const char* func, const char* failedexpr) +{ + panic("Assertion failed: %s:%d in %s at %s\n", file, line, func, failedexpr); } #endif diff --git a/src/shared/math.c b/src/shared/math.c new file mode 100644 index 0000000..900cced --- /dev/null +++ b/src/shared/math.c @@ -0,0 +1,38 @@ +#include "shared/math.h" + +uint8_t sintable[256] = { + 128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 162, 165, 167, 170, + 173, 176, 179, 182, 185, 188, 190, 193, 196, 198, 201, 203, 206, 208, 211, + 213, 215, 218, 220, 222, 224, 226, 228, 230, 232, 234, 235, 237, 238, 240, + 241, 243, 244, 245, 246, 248, 249, 250, 250, 251, 252, 253, 253, 254, 254, + 254, 255, 255, 255, 255, 255, 255, 255, 254, 254, 254, 253, 253, 252, 251, + 250, 250, 249, 248, 246, 245, 244, 243, 241, 240, 238, 237, 235, 234, 232, + 230, 228, 226, 224, 222, 220, 218, 215, 213, 211, 208, 206, 203, 201, 198, + 196, 193, 190, 188, 185, 182, 179, 176, 173, 170, 167, 165, 162, 158, 155, + 152, 149, 146, 143, 140, 137, 134, 131, 128, 124, 121, 118, 115, 112, 109, + 106, 103, 100, 97, 93, 90, 88, 85, 82, 79, 76, 73, 70, 67, 65, + 62, 59, 57, 54, 52, 49, 47, 44, 42, 40, 37, 35, 33, 31, 29, + 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, 10, 9, 7, 6, + 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, 10, 11, + 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, 37, + 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76, + 79, 82, 85, 88, 90, 93, 97, 100, 103, 106, 109, 112, 115, 118, 121, + 124, +}; + +uint8_t byte_sin(uint8_t n) +{ + return sintable[n]; +} + +uint8_t amp(uint8_t in, uint8_t n) +{ + uint32_t out = in; + + for (int i = 0; i < n; ++i) { + out = (out * in) / 256; + } + + return min(out, 255); +} diff --git a/test_harness/fake_env.c b/test_harness/fake_env.c index 261e9e2..bae6298 100644 --- a/test_harness/fake_env.c +++ b/test_harness/fake_env.c @@ -62,3 +62,8 @@ DEFINE_MEMORY_SEGMENT(mpu, 0xE000ED90, 0xE000EDA4) /* Serial Peripheral Interface */ DEFINE_MEMORY_SEGMENT(spi1, 0x40013000, 0x400133FF) DEFINE_MEMORY_SEGMENT(spi3, 0x40003C00, 0x40003FFF) + +DEFINE_MEMORY_SEGMENT(syscfg, 0x40010000, 0x4001002F) +DEFINE_MEMORY_SEGMENT(exti, 0x40010400, 0x40010800) + +DEFINE_MEMORY_SEGMENT(tim2, 0x40000000, 0x400003FF) diff --git a/test_harness/fake_env.h b/test_harness/fake_env.h index b4451df..83ac702 100644 --- a/test_harness/fake_env.h +++ b/test_harness/fake_env.h @@ -15,6 +15,9 @@ void* load_fake_rcc__(); void* load_fake_spi1__(); void* load_fake_spi3__(); void* load_fake_mpu__(); +void* load_fake_syscfg__(); +void* load_fake_exti__(); +void* load_fake_tim2__(); void wipeout_fake_env(); diff --git a/tests/test_array_list.c b/tests/test_array_list.c new file mode 100644 index 0000000..71ffa55 --- /dev/null +++ b/tests/test_array_list.c @@ -0,0 +1,65 @@ +#include "kern/mem.h" +#include "shared/array_list.h" +#include "test_harness.h" + +ARRAY_LIST_DECL(int); +ARRAY_LIST_IMPL(int); + +TEST(array_list, smell) +{ + array_list_t(int)* list = array_list_new(int)(); + + array_list_add(int)(list, 1); + array_list_add(int)(list, 2); + array_list_add(int)(list, 3); + array_list_add(int)(list, 4); + array_list_add(int)(list, 5); + + int expected[5] = {1, 2, 3, 4, 5}; + + for (size_t i = 0; i < 5; ++i) { + ASSERT_EQ(*array_list_ref(int)(list, i), expected[i]); + } + + ASSERT_EQ(array_list_length(int)(list), 5); + *array_list_ref(int)(list, 3) = 8; + expected[3] = 8; + + for (size_t i = 0; i < 5; ++i) { + ASSERT_EQ(*array_list_ref(int)(list, i), expected[i]); + } + + int out; + array_list_remove(int)(list, 2, &out); + ASSERT_EQ(out, 3); + ASSERT_EQ(array_list_length(int)(list), 4); + + int expected2[4] = {1, 2, 8, 5}; + for (size_t i = 0; i < 4; ++i) { + ASSERT_EQ(*array_list_ref(int)(list, i), expected2[i]); + } + + return 0; +} + +TEST(array_list, foreach) +{ + array_list_t(int)* arl = array_list_new(int)(); + array_list_add(int)(arl, 3); + array_list_add(int)(arl, 2); + array_list_add(int)(arl, 1); + + int i = 0; + int values[3]; + array_list_foreach(arl, val) { + values[i] = val; + ++ i; + } + + ASSERT_EQ(values[0], 3); + ASSERT_EQ(values[1], 2); + ASSERT_EQ(values[2], 1); + + return 0; + +} diff --git a/tests/test_avl_tree.c b/tests/test_avl_tree.c new file mode 100644 index 0000000..2a7260f --- /dev/null +++ b/tests/test_avl_tree.c @@ -0,0 +1,149 @@ +#include "shared/avl_tree.h" +#include "test_harness.h" + +#define integer_cmp_(a, b) (a - b) +AVL_TREE_DECL(int); +AVL_TREE_IMPL(int, integer_cmp_, null_dtor); + +static inline void avl_tree_debug_print(avl_tree_node_t(int) * node, int tab) +{ + if (node) { + avl_tree_debug_print(node->right, tab + 1); + } + for (int i = 0; i < tab; ++i) { + printf(" "); + } + if (!node) { + printf("(nil)\n"); + return; + } + printf("%d\n", node->value); + avl_tree_debug_print(node->left, tab + 1); +} + +TEST(avl_tree, insert) +{ + avl_tree_t(int)* tree = avl_tree_new(int)(); + avl_tree_insert(int)(tree, 5); + + ASSERT_EQ(avl_tree_size(int)(tree), 1); + + avl_tree_insert(int)(tree, 4); + avl_tree_insert(int)(tree, 6); + + ASSERT_EQ(avl_tree_size(int)(tree), 3); + ASSERT_EQ(avl_tree_height(int)(tree), 2); + + return 0; +} + +TEST(avl_tree, insert_rotate_asc) +{ + avl_tree_t(int)* tree = avl_tree_new(int)(); + + avl_tree_insert(int)(tree, 1); + avl_tree_insert(int)(tree, 2); + avl_tree_insert(int)(tree, 3); + avl_tree_insert(int)(tree, 4); + + ASSERT_EQ(avl_tree_size(int)(tree), 4); + ASSERT_EQ(avl_tree_height(int)(tree), 3); + + return 0; +} + +TEST(avl_tree, insert_rotate_desc) +{ + avl_tree_t(int)* tree = avl_tree_new(int)(); + + avl_tree_insert(int)(tree, 4); + avl_tree_insert(int)(tree, 3); + avl_tree_insert(int)(tree, 2); + avl_tree_insert(int)(tree, 1); + + ASSERT_EQ(avl_tree_size(int)(tree), 4); + ASSERT_EQ(avl_tree_height(int)(tree), 3); + + return 0; +} + +TEST(avl_tree_erase, erase) +{ + avl_tree_t(int)* tree = avl_tree_new(int)(); + + avl_tree_insert(int)(tree, 4); + avl_tree_insert(int)(tree, 3); + avl_tree_insert(int)(tree, 2); + avl_tree_insert(int)(tree, 1); + + ASSERT_EQ(avl_tree_size(int)(tree), 4); + ASSERT_EQ(avl_tree_height(int)(tree), 3); + + int out; + bool b = avl_tree_erase(int)(tree, 3, &out); + + ASSERT_EQ(b, 1); + ASSERT_EQ(out, 3); + ASSERT_EQ(avl_tree_size(int)(tree), 3); + ASSERT_EQ(avl_tree_height(int)(tree), 2); + + b = avl_tree_erase(int)(tree, 1, &out); + + ASSERT_EQ(b, 1); + ASSERT_EQ(out, 1); + ASSERT_EQ(avl_tree_size(int)(tree), 2); + ASSERT_EQ(avl_tree_height(int)(tree), 2); + + return 0; +} + +TEST(avl_tree, erase_onesided) +{ + avl_tree_t(int)* tree = avl_tree_new(int)(); + for (int i = 0; i < 16; ++i) { + avl_tree_insert(int)(tree, i); + } + + ASSERT_EQ(avl_tree_erase(int)(tree, 0, NULL), 1); + ASSERT_EQ(avl_tree_erase(int)(tree, 1, NULL), 1); + ASSERT_EQ(avl_tree_erase(int)(tree, 2, NULL), 1); + ASSERT_EQ(avl_tree_erase(int)(tree, 3, NULL), 1); + ASSERT_EQ(avl_tree_erase(int)(tree, 4, NULL), 1); + ASSERT_EQ(avl_tree_erase(int)(tree, 5, NULL), 1); + ASSERT_EQ(avl_tree_erase(int)(tree, 6, NULL), 1); + + ASSERT_EQ(avl_tree_height(int)(tree), 4); + ASSERT_EQ(avl_tree_size(int)(tree), 9); + + avl_tree_free(int)(tree); + ASSERT_TRUE(debug_is_heap_empty()); + + return 0; +} + +TEST(avl_tree, find) +{ + avl_tree_t(int)* tree = avl_tree_new(int)(); + + for (int i = 0; i < 16; ++ i) { + avl_tree_insert(int)(tree, i); + } + + ASSERT_EQ(*avl_tree_find(int)(tree, 4), 4); + ASSERT_EQ(*avl_tree_find(int)(tree, 3), 3); + ASSERT_EQ(*avl_tree_find(int)(tree, 15), 15); + + ASSERT_EQ(avl_tree_find(int)(tree, 100), NULL); + + return 0; +} + +TEST (avl_tree, stress) +{ + avl_tree_t(int)* tree = avl_tree_new(int)(); + for (int i = 0; i < 512; ++ i) { + avl_tree_insert(int)(tree, i); + } + + return 0; +} diff --git a/tests/test_linked_list.c b/tests/test_linked_list.c index 7ec96b5..2c51625 100644 --- a/tests/test_linked_list.c +++ b/tests/test_linked_list.c @@ -60,3 +60,25 @@ TEST(linked_list, foreach) return 0; } + +TEST(linked_list, remove) +{ + linked_list_t(int) ll = LINKED_LIST_INIT; + linked_list_push_front(int)(&ll, 1); + linked_list_push_front(int)(&ll, 2); + linked_list_push_front(int)(&ll, 3); + linked_list_push_front(int)(&ll, 4); + linked_list_push_front(int)(&ll, 4); + + int ec = linked_list_remove(int)(&ll, 4); + ASSERT_EQ(!!ec, 1); + ec = linked_list_remove(int)(&ll, 2); + ASSERT_EQ(!!ec, 1); + ec = linked_list_remove(int)(&ll, 1); + ASSERT_EQ(!!ec, 1); + + ASSERT_EQ(linked_list_length(int)(&ll), 1); + ASSERT_EQ(*linked_list_front(int)(&ll), 3); + + return 0; +} diff --git a/tests/test_map.c b/tests/test_map.c new file mode 100644 index 0000000..29704f0 --- /dev/null +++ b/tests/test_map.c @@ -0,0 +1,113 @@ +#include <stdlib.h> + +#include "shared/map.h" +#include "test_harness.h" + +typedef const char* const_char_ptr; + +#define integer_cmp_(a, b) (a - b) +MAP_DECL(int, const_char_ptr); +MAP_IMPL(int, const_char_ptr, integer_cmp_, null_dtor); + +TEST(map, smoke) +{ + map_t(int, const_char_ptr)* map = map_new(int, const_char_ptr)(); + + map_put(int, const_char_ptr)(map, 6, "string1"); + map_put(int, const_char_ptr)(map, 8, "string2"); + map_put(int, const_char_ptr)(map, 9, "string3"); + + const char* str = *map_get(int, const_char_ptr)(map, 8); + ASSERT_EQ_STR(str, "string2"); + + str = *map_get(int, const_char_ptr)(map, 9); + ASSERT_EQ_STR(str, "string3"); + ASSERT_EQ(map_get(int, const_char_ptr)(map, 20), NULL); + + map_free(int, const_char_ptr)(map); + ASSERT_TRUE(debug_is_heap_empty()); + return 0; +} + +typedef uint32_t* int_ptr; +MAP_DECL(int, int_ptr); +MAP_IMPL(int, int_ptr, integer_cmp_, kfree); + +static inline void loose_map_print( + avl_tree_node_t(map_entry_t(int, int_ptr))* node, + int tab) { + if (node) { + loose_map_print(node->right, tab + 1); + } + for (int i = 0; i < tab; ++i) { + printf(" "); + } + if (!node) { + printf("(nil)\n"); + return; + } + printf("(%d => %d)\n", node->value.key, *node->value.value); + loose_map_print(node->left, tab + 1); +} +static inline void map_debug_print( + map_t(int, int_ptr) * map) +{ + printf("------\n"); + if (map->tree) { + loose_map_print(map->tree->root, 0); + } + printf("------\n"); +} +TEST(map, stress) +{ +#define N 256 + uint32_t* arr[N]; + memset(arr, 0, sizeof(arr)); + map_t(int, int_ptr)* map = map_new(int, int_ptr)(); + + for (int i = 0; i < N; ++i) { + uint32_t* a = kalloc(sizeof(uint32_t)); + *a = rand(); + arr[i] = a; + } + + for (int i = 0; i < N; ++i) { + map_put(int, int_ptr)(map, i, arr[i]); + } + + for (int i = 0; i < N; ++i) { + uint32_t** ptr = map_get(int, int_ptr)(map, i); + ASSERT_TRUE(ptr != NULL); + ASSERT_TRUE(*ptr != NULL); + ASSERT_EQ(**ptr, *arr[i]); + } + + int erased[N]; + memset(erased, 0, sizeof(erased)); + + for (int i = 0; i < (N / 2); ++i) { + int_ptr out; + int e = rand() % N; + if (map_erase(int, int_ptr)(map, e, &out)) { + ASSERT_EQ(*out, *arr[e]); + arr[e] = NULL; + kfree(out); + } + erased[e] = 1; + } + + for (int i = 0; i < N; ++ i) { + if (erased[i]) { + continue; + } + uint32_t** ptr = map_get(int, int_ptr)(map, i); + ASSERT_TRUE(ptr != NULL); + ASSERT_TRUE(*ptr != NULL); + ASSERT_EQ(**ptr, *arr[i]); + } + + map_free(int, int_ptr)(map); + ASSERT_TRUE(debug_is_heap_empty()); + + return 0; +} diff --git a/tests/test_mpu.c b/tests/test_mpu.c new file mode 100644 index 0000000..551e079 --- /dev/null +++ b/tests/test_mpu.c @@ -0,0 +1,27 @@ +#include "test_harness.c" + +#include "arch/arm/cortex-m4/mpu.h" +#include "kern/mpu/mpu_manager.h" + +TEST(mpu, smell) +{ + memory_region_opts_t memopts = { 0 }; + memopts.region = (void*) 0; + memopts.bufferable = 0; + memopts.cacheable = 1; + memopts.sharable = 1; + memopts.tex = 0; + memopts.size = REGION_SIZE_4Gb; + memopts.perms = ACCESS_PERMS_FULL; + memopts.subregion_disable = 0; + memopts.executable = 1; + memopts.enable = 1; + + mpu_configure_region(0, &memopts); + mpu_set_enabled(1); + + ASSERT_EQ(MPU.rba_r, 1 << 4); + ASSERT_EQ(MPU.ras_r, 0x0306003F); + + return 0; +} |