#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define TEST_STORAGE_ADDRESS ((uintptr_t)0x1000U) int __clzsi2(unsigned int value); long long __divdi3(long long dividend, long long divisor); int __ffssi2(unsigned int value); unsigned long long __udivdi3(unsigned long long dividend, unsigned long long divisor); unsigned long long __umoddi3(unsigned long long dividend, unsigned long long divisor); static uint8_t storage[32]; static uint32_t image_call_count; void putch(char ch) { (void)ch; } void lcd_fill_image(uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end, uint32_t *data) { (void)x_start; (void)y_start; (void)x_end; (void)y_end; (void)data; ++image_call_count; } rs_status_t rs_spisd_read_bytes(void *buffer, size_t byte_count, uintptr_t address) { size_t offset; if ((buffer == NULL) || (address < TEST_STORAGE_ADDRESS)) { return RS_EINVAL; } offset = (size_t)(address - TEST_STORAGE_ADDRESS); if (byte_count > (sizeof(storage) - offset)) { return RS_EIO; } for (size_t index = 0U; index < byte_count; ++index) { ((uint8_t *)buffer)[index] = storage[offset + index]; } return RS_OK; } static rs_status_t test_reader_read(void *context, uint32_t offset, void *buffer, size_t byte_count) { const uint8_t *bytes = context; if ((bytes == NULL) || (buffer == NULL)) { return RS_EINVAL; } for (size_t index = 0U; index < byte_count; ++index) { ((uint8_t *)buffer)[index] = bytes[offset + index]; } return RS_OK; } static int test_string_helpers(void) { char buffer[8] = {'x', '\0'}; int32_t value; if (strncpy(buffer, "z", 0U) != buffer || buffer[0] != 'x') { return 1; } if (strncmp("left", "right", 0U) != 0) { return 2; } if (rs_strlcpy(buffer, "abcdefghi", sizeof(buffer)) != 9U || strcmp(buffer, "abcdefg") != 0) { return 3; } if (rs_strlcat(buffer, "x", sizeof(buffer)) != 8U || strcmp(buffer, "abcdefg") != 0) { return 4; } if (rs_strtoi32("-2147483648", &value) != RS_OK || value != INT32_MIN || rs_strtoi32("2147483648", &value) != RS_EFORMAT || rs_strtoi32("12x", &value) != RS_EFORMAT) { return 5; } return 0; } static int test_formatter(void) { char buffer[32]; if (rs_snprintf(buffer, sizeof(buffer), "%#08x:%lld:%zu", 0x2AU, -42LL, (size_t)7U) != 14 || strcmp(buffer, "0x00002a:-42:7") != 0) { return 1; } if (rs_snprintf(buffer, 4U, "abcdef") != 6 || strcmp(buffer, "abc") != 0) { return 2; } if (rs_snprintf(NULL, 0U, "value=%llu", UINT64_MAX) != 26) { return 3; } if (rs_snprintf(buffer, sizeof(buffer), "%p", (void *)(uintptr_t)0x1234U) != 6 || strcmp(buffer, "0x1234") != 0) { return 4; } return 0; } static int test_compiler_helpers(void) { if ((__clzsi2(0U) != 32) || (__clzsi2(1U) != 31) || (__ffssi2(0U) != 0) || (__ffssi2(1U) != 1) || (__ffssi2(0x10U) != 5)) { return 1; } if ((__udivdi3(UINT64_MAX, 3U) != 6148914691236517205ULL) || (__umoddi3(UINT64_MAX, 3U) != 0U) || (__udivdi3(UINT64_MAX, UINT64_MAX) != 1U) || (__umoddi3(100U, 7U) != 2U) || (__divdi3(-100LL, 7LL) != -14LL)) { return 2; } return 0; } static int test_wait_helper(void) { volatile uint32_t value = 0x30U; if (rs_wait_mask(&value, 0x30U, 0x30U, 1U) != RS_OK || rs_wait_mask(&value, 0x40U, 0x40U, 1U) != RS_ETIMEOUT || rs_wait_mask(NULL, 1U, 1U, 1U) != RS_EINVAL || rs_wait_value(&value, 0x30U, 1U) != RS_OK || rs_wait_not_value(&value, 0U, 1U) != RS_OK || rs_wait_not_value(&value, 0x30U, 1U) != RS_ETIMEOUT) { return 1; } return 0; } static int test_ws2812_helpers(void) { const rs_ws2812_config_t config = { 72000000U, RS_WS2812_DEFAULT_BIT_PERIOD_NS, RS_WS2812_DEFAULT_T0H_NS, RS_WS2812_DEFAULT_T1H_NS, RS_WS2812_DEFAULT_RESET_NS, 4U, }; rs_ws2812_config_t invalid = config; rs_ws2812_timing_t timing; if ((rs_ws2812_pack_grb(0x12U, 0x34U, 0x56U) != UINT32_C(0x341256)) || (rs_ws2812_timing_from_ns(&config, &timing) != RS_OK) || (timing.bit_cycles != 90U) || (timing.t0h_cycles != 25U) || (timing.t1h_cycles != 50U) || (timing.reset_cycles != 21600U)) { return 1; } invalid.t0h_ns = invalid.t1h_ns; if ((rs_ws2812_timing_from_ns(&invalid, &timing) != RS_EINVAL) || (rs_ws2812_timing_from_ns(NULL, &timing) != RS_EINVAL) || (rs_ws2812_timing_from_ns(&config, NULL) != RS_EINVAL)) { return 2; } invalid = config; invalid.source_clock_hz = 1U; if (rs_ws2812_timing_from_ns(&invalid, &timing) != RS_EINVAL) { return 3; } return 0; } static int test_timer_helpers(void) { rs_timer_period_t period; if ((rs_timer_period_from_ms(72000000U, 1U, &period) != RS_OK) || (period.prescale != 0U) || (period.load != 71999U)) { return 1; } if ((rs_timer_period_from_ms(72000000U, 1000U, &period) != RS_OK) || (period.prescale != 0U) || (period.load != 71999999U)) { return 2; } if ((rs_timer_period_from_ms(0U, 1U, &period) != RS_EINVAL) || (rs_timer_period_from_ms(72000000U, 0U, &period) != RS_EINVAL) || (rs_timer_period_from_ms(72000000U, 1U, NULL) != RS_EINVAL) || (rs_timer_period_from_ms(UINT32_MAX, UINT32_MAX, &period) != RS_EINVAL)) { return 3; } return 0; } static int test_i2s_helpers(void) { uint8_t sclk_div; uint8_t lrck_div; uint16_t first; uint16_t second; if (rs_i2s_pack_stereo16(0x1111U, 0x2222U) != UINT32_C(0x22221111)) return 1; rs_i2s_unpack_stereo16(UINT32_C(0x22221111), &first, &second); if ((first != 0x1111U) || (second != 0x2222U)) return 2; if ((rs_i2s_div_from_hz(18432000U, 48000U, 16U, &sclk_div, &lrck_div) != RS_OK) || (sclk_div != 5U) || (lrck_div != 15U)) return 3; if ((rs_i2s_div_from_hz(18432000U, 96000U, 24U, &sclk_div, &lrck_div) != RS_OK) || (sclk_div != 1U) || (lrck_div != 23U)) return 4; if ((rs_i2s_rxdata_address() - rs_i2s_txdata_address()) != (uintptr_t)4U) return 6; if ((rs_i2s_div_from_hz(0U, 48000U, 16U, &sclk_div, &lrck_div) != RS_EINVAL) || (rs_i2s_div_from_hz(18432000U, 48000U, 20U, &sclk_div, &lrck_div) != RS_EINVAL) || (rs_i2s_div_from_hz(18432000U, 48000U, 16U, NULL, &lrck_div) != RS_EINVAL)) return 5; return 0; } static int test_sdram_helpers(void) { rs_sdram_timing_t timing; if ((rs_sdram_timing_from_hz(72000000U, 0U, &timing) != RS_OK) || (timing.trp_cycles != 1U) || (timing.trcd_cycles != 1U) || (timing.tras_cycles != 2U) || (timing.trc_cycles != 3U) || (timing.twr_cycles != 1U) || (timing.trfc_cycles != 3U) || (timing.trrd_cycles != 2U) || (timing.twtr_cycles != 2U) || (timing.trtp_cycles != 1U) || (timing.tmrd_cycles != 2U) || (timing.txsr_cycles != 3U) || (timing.trefi_cycles != 281U) || (timing.powerup_cycles != 3600U) || (timing.actual_sdram_hz != 36000000U)) { return 1; } if ((rs_sdram_timing_from_hz(0U, 0U, &timing) != RS_EINVAL) || (rs_sdram_timing_from_hz(72000000U, 4U, &timing) != RS_EINVAL) || (rs_sdram_timing_from_hz(72000000U, 0U, NULL) != RS_EINVAL)) { return 2; } return 0; } static int test_psram_helpers(void) { rs_psram_timing_t timing; if ((rs_psram_timing_from_hz(72000000U, 36000000U, &timing) != RS_OK) || (timing.half_period_cycles != 1U) || (timing.cs_setup_cycles != 1U) || (timing.cs_high_cycles != 4U) || (timing.cs_hold_cycles != 3U) || (timing.powerup_cycles != 10800U) || (timing.cs_max_low_cycles != 576U) || (timing.actual_sclk_hz != 36000000U) || timing.above_84mhz) { return 1; } if ((rs_psram_timing_from_hz(192000000U, 133000000U, &timing) != RS_OK) || (timing.half_period_cycles != 1U) || (timing.actual_sclk_hz != 96000000U) || !timing.above_84mhz) { return 2; } if ((rs_psram_timing_from_hz(0U, 36000000U, &timing) != RS_EINVAL) || (rs_psram_timing_from_hz(72000000U, 0U, &timing) != RS_EINVAL) || (rs_psram_timing_from_hz(72000000U, 134000000U, &timing) != RS_EINVAL) || (rs_psram_timing_from_hz(72000000U, 36000000U, NULL) != RS_EINVAL)) { return 3; } return 0; } static int test_uart_helpers(void) { rs_uart_timing_t timing; if ((rs_uart_timing_calculate(72000000U, 921600U, &timing) != RS_OK) || (timing.baud_integer != 78U) || (timing.baud_fraction != 32U)) { return 1; } if ((rs_uart_timing_calculate(16000000U, 1000000U, &timing) != RS_OK) || (timing.baud_integer != 16U) || (timing.baud_fraction != 0U)) { return 2; } if ((rs_uart_timing_calculate(0U, 115200U, &timing) != RS_EINVAL) || (rs_uart_timing_calculate(72000000U, 0U, &timing) != RS_EINVAL) || (rs_uart_timing_calculate(1000000U, 1000000U, &timing) != RS_EINVAL) || (rs_uart_timing_calculate(72000000U, 115200U, NULL) != RS_EINVAL)) { return 3; } return 0; } static int test_i2c_helpers(void) { rs_i2c_timing_t timing; if ((rs_i2c_timing_calculate(72000000U, 400000U, &timing) != RS_OK) || (timing.scl_low_cycles != 136U) || (timing.scl_high_cycles != 44U) || (timing.start_hold_cycles != 44U) || (timing.start_setup_cycles != 44U) || (timing.data_setup_cycles != 8U) || (timing.stop_setup_cycles != 44U) || (timing.bus_free_cycles != 94U)) { return 1; } if ((rs_i2c_timing_calculate(72000000U, 1000000U, &timing) != RS_OK) || (timing.scl_low_cycles != 53U) || (timing.scl_high_cycles != 19U) || (timing.data_setup_cycles != 4U)) { return 2; } if ((rs_i2c_timing_calculate(0U, 400000U, &timing) != RS_EINVAL) || (rs_i2c_timing_calculate(72000000U, 0U, &timing) != RS_EINVAL) || (rs_i2c_timing_calculate(72000000U, 1000001U, &timing) != RS_EINVAL) || (rs_i2c_timing_calculate(72000000U, 400000U, NULL) != RS_EINVAL)) { return 3; } return 0; } static int test_gpio_helpers(void) { rs_gpio_filter_timing_t timing; if ((rs_gpio_filter_timing_from_us(72000000U, 2U, 3U, &timing) != RS_OK) || (timing.divider != 143U) || (timing.stable_samples != 3U)) { return 1; } if ((rs_gpio_filter_timing_from_us(1U, 1U, 1U, &timing) != RS_OK) || (timing.divider != 0U)) { return 2; } if ((rs_gpio_filter_timing_from_us(0U, 1U, 1U, &timing) != RS_EINVAL) || (rs_gpio_filter_timing_from_us(72000000U, 0U, 1U, &timing) != RS_EINVAL) || (rs_gpio_filter_timing_from_us(72000000U, 2U, 0U, &timing) != RS_EINVAL) || (rs_gpio_filter_timing_from_us(72000000U, 2U, 16U, &timing) != RS_EINVAL) || (rs_gpio_filter_timing_from_us(72000000U, 1000U, 3U, &timing) != RS_EINVAL) || (rs_gpio_filter_timing_from_us(72000000U, 2U, 3U, NULL) != RS_EINVAL)) { return 3; } return 0; } static int test_dma_config_validation(void) { rs_dma_config_t config = { .kind = RS_DMA_KIND_MM_TO_MM, .request = RS_DMA_REQUEST_SOFTWARE, .source = (uintptr_t)UINT32_C(0x40000000), .destination = (uintptr_t)UINT32_C(0x41000000), .byte_count = 64U, .width = RS_DMA_WIDTH_32, .source_increment = true, .destination_increment = true, .priority = 1U, .burst_beats = RS_DMA_MAX_BURST_BEATS, }; if (rs_dma_config_validate(RS_DMA_CHANNEL_BULK, &config) != RS_OK) { return 1; } config.width = RS_DMA_WIDTH_16; if (rs_dma_config_validate(RS_DMA_CHANNEL_BULK, &config) != RS_EINVAL) { return 2; } config.width = RS_DMA_WIDTH_32; config.byte_count = 6U; if (rs_dma_config_validate(RS_DMA_CHANNEL_BULK, &config) != RS_EINVAL) { return 3; } config.byte_count = 64U; config.kind = RS_DMA_KIND_MM_TO_STREAM; config.request = RS_DMA_REQUEST_I2S_TX; config.destination = (uintptr_t)0U; if (rs_dma_config_validate(RS_DMA_CHANNEL_BULK, &config) != RS_OK) { return 4; } config.source_increment = false; if (rs_dma_config_validate(RS_DMA_CHANNEL_BULK, &config) != RS_EINVAL) { return 5; } return 0; } static int test_ps2_decoders(void) { ps2_keyboard_decoder_t keyboard; ps2_mouse_decoder_t mouse; ps2_key_event_t key_event; ps2_mouse_event_t mouse_event; ps2_keyboard_decoder_init(&keyboard); if (!ps2_keyboard_decode_byte(&keyboard, UINT8_C(0x1C), &key_event) || !key_event.pressed || key_event.extended || (key_event.scan_code != UINT16_C(0x001C))) { return 1; } if (ps2_keyboard_decode_byte(&keyboard, UINT8_C(0xE0), &key_event) || ps2_keyboard_decode_byte(&keyboard, UINT8_C(0xF0), &key_event) || !ps2_keyboard_decode_byte(&keyboard, UINT8_C(0x75), &key_event) || key_event.pressed || !key_event.extended || (key_event.scan_code != UINT16_C(0xE075))) { return 2; } ps2_mouse_decoder_init(&mouse, UINT8_C(4)); if (ps2_mouse_decode_byte(&mouse, UINT8_C(0x08), &mouse_event) || ps2_mouse_decode_byte(&mouse, UINT8_C(0x02), &mouse_event) || ps2_mouse_decode_byte(&mouse, UINT8_C(0x01), &mouse_event) || !ps2_mouse_decode_byte(&mouse, UINT8_C(0x3F), &mouse_event) || (mouse_event.dx != 2) || (mouse_event.dy != 1) || (mouse_event.wheel != -1) || ((mouse_event.buttons & UINT8_C(0x18)) != UINT8_C(0x18))) { return 3; } return 0; } static int test_wav_parser(void) { static const uint8_t wav[] = { 'R', 'I', 'F', 'F', 40U, 0U, 0U, 0U, 'W', 'A', 'V', 'E', 'f', 'm', 't', ' ', 16U, 0U, 0U, 0U, 1U, 0U, 1U, 0U, 0x40U, 0x1FU, 0U, 0U, 0x80U, 0x3EU, 0U, 0U, 2U, 0U, 16U, 0U, 'd', 'a', 't', 'a', 4U, 0U, 0U, 0U, 0U, 1U, 2U, 3U, }; rs_wav_info_t info; const rs_wav_reader_t reader = {test_reader_read, (void *)wav, sizeof(wav)}; if (rs_wav_parse(&reader, &info) != RS_OK || info.sample_rate != 8000U || info.block_align != 2U || info.data_offset != 44U || info.data_size != 4U) { return 1; } if (rs_wav_parse(&(rs_wav_reader_t){test_reader_read, (void *)wav, 12U}, &info) != RS_EFORMAT) { return 2; } return 0; } static int test_video_parser(void) { rs_video_info_t info; (void)memset(storage, 0, sizeof(storage)); storage[0] = 2U; storage[4] = 2U; storage[8] = 1U; if (rs_video_parse(storage, 24U, &info) != RS_OK || info.frame_size != 8U || rs_video_parse(storage, 16U, &info) != RS_EFORMAT) { return 1; } image_call_count = 0U; if (rs_video_show_spisd(TEST_STORAGE_ADDRESS, 24U) != RS_OK || image_call_count != 1U) { return 2; } return 0; } int main(void) { const int results[] = { test_string_helpers(), test_formatter(), test_compiler_helpers(), test_wait_helper(), test_ws2812_helpers(), test_timer_helpers(), test_psram_helpers(), test_sdram_helpers(), test_uart_helpers(), test_i2s_helpers(), test_i2c_helpers(), test_gpio_helpers(), test_dma_config_validation(), test_ps2_decoders(), test_wav_parser(), test_video_parser(), }; for (size_t index = 0U; index < (sizeof(results) / sizeof(results[0])); ++index) { if (results[index] != 0) { return (int)(index + 1U); } } return 0; }