diff options
Diffstat (limited to 'circuitpython/ports/raspberrypi/sdk/test')
22 files changed, 2957 insertions, 0 deletions
diff --git a/circuitpython/ports/raspberrypi/sdk/test/CMakeLists.txt b/circuitpython/ports/raspberrypi/sdk/test/CMakeLists.txt new file mode 100644 index 0000000..7ed2a00 --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/CMakeLists.txt @@ -0,0 +1,11 @@ +add_subdirectory(pico_test) + +add_subdirectory(pico_stdlib_test) +add_subdirectory(pico_time_test) +add_subdirectory(pico_divider_test) +if (PICO_ON_DEVICE) + add_subdirectory(pico_float_test) + add_subdirectory(kitchen_sink) + add_subdirectory(hardware_pwm_test) + add_subdirectory(cmsis_test) +endif()
\ No newline at end of file diff --git a/circuitpython/ports/raspberrypi/sdk/test/cmsis_test/CMakeLists.txt b/circuitpython/ports/raspberrypi/sdk/test/cmsis_test/CMakeLists.txt new file mode 100644 index 0000000..da8ecbf --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/cmsis_test/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(cmsis_test cmsis_test.c) + +target_link_libraries(cmsis_test PRIVATE pico_stdlib cmsis_core) +pico_add_extra_outputs(cmsis_test)
\ No newline at end of file diff --git a/circuitpython/ports/raspberrypi/sdk/test/cmsis_test/cmsis_test.c b/circuitpython/ports/raspberrypi/sdk/test/cmsis_test/cmsis_test.c new file mode 100644 index 0000000..a436391 --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/cmsis_test/cmsis_test.c @@ -0,0 +1,23 @@ +#include <stdio.h> + +#include "RP2040.h" +#include "pico/stdio.h" + +__STATIC_FORCEINLINE int some_function(int i) { + return __CLZ(i); +} + +static bool pendsv_called; + +void PendSV_Handler(void) { + pendsv_called = true; +} + +int main(void) { + stdio_init_all(); + for(int i=0;i<10;i++) { + printf("%d %d\n", i, some_function(i)); + } + SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; + puts(pendsv_called ? "SUCCESS" : "FAILURE"); +} diff --git a/circuitpython/ports/raspberrypi/sdk/test/hardware_pwm_test/CMakeLists.txt b/circuitpython/ports/raspberrypi/sdk/test/hardware_pwm_test/CMakeLists.txt new file mode 100644 index 0000000..c0e08a4 --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/hardware_pwm_test/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(hardware_pwm_test hardware_pwm_test.c) + +target_link_libraries(hardware_pwm_test PRIVATE pico_test hardware_pwm) +pico_add_extra_outputs(hardware_pwm_test)
\ No newline at end of file diff --git a/circuitpython/ports/raspberrypi/sdk/test/hardware_pwm_test/hardware_pwm_test.c b/circuitpython/ports/raspberrypi/sdk/test/hardware_pwm_test/hardware_pwm_test.c new file mode 100644 index 0000000..ecd2c4d --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/hardware_pwm_test/hardware_pwm_test.c @@ -0,0 +1,168 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdio.h> +#include "pico/stdlib.h" +#include "pico/test.h" +#include "pico/time.h" +#include "hardware/irq.h" +#include "hardware/resets.h" +#include "hardware/pwm.h" + +PICOTEST_MODULE_NAME("PWM", "PWM SDK Test harness"); + +/* In a struct for future expansion of the interrupt testv */ +struct interrupt_state { + int count; +} interrupt_states[NUM_PWM_SLICES] = {0}; + +void on_pwm_wrap() { + for (int pwm = 0; pwm < NUM_PWM_SLICES; pwm++) { + // See if this pwm, is the one that fired. + if (pwm_get_irq_status_mask() & (1 << pwm)) { + // Clear the interrupt flag that brought us here + pwm_clear_irq(pwm); + + interrupt_states[pwm].count++; + } + } +} + +int main() { + reset_block(RESETS_RESET_PWM_BITS); + unreset_block_wait(RESETS_RESET_PWM_BITS); + + setup_default_uart(); + + PICOTEST_START(); + + pwm_config config = pwm_get_default_config(); + + + // Test that config sets works on all PWMs by comparing what we pass in + // via the API with what the registers contains afterwards + + pwm_config_set_phase_correct(&config, true); + pwm_config_set_clkdiv(&config, 42.5); + pwm_config_set_clkdiv_mode(&config, PWM_DIV_B_HIGH); + pwm_config_set_output_polarity(&config, false, true); + pwm_config_set_wrap(&config, 0x1234); + + PICOTEST_START_SECTION("PWM config init tests"); + for (int pwm = 0; pwm < NUM_PWM_SLICES; pwm++) { + pwm_slice_hw_t *slice = &pwm_hw->slice[pwm]; + + pwm_init(pwm, &config, false); + + uint div = (uint)(42.5f * (float)(1 << PWM_CH0_DIV_INT_LSB)); + + PICOTEST_CHECK_CHANNEL(pwm, slice->top == config.top, "HW top does not match requested config"); + //PICOTEST_CHECK_CHANNEL(pwm, slice->ctr == 0x1234, "HW counter does not match config"); + PICOTEST_CHECK_CHANNEL(pwm, slice->cc == PWM_CH0_CC_RESET, "HW compares does not match config"); + PICOTEST_CHECK_CHANNEL(pwm, slice->div == div, "HW divider does not match config"); + PICOTEST_CHECK_CHANNEL(pwm, slice->csr == + (1 << PWM_CH0_CSR_PH_CORRECT_LSB | 0 << PWM_CH0_CSR_A_INV_LSB | 1 << PWM_CH0_CSR_B_INV_LSB | + PWM_CH0_CSR_DIVMODE_VALUE_LEVEL << PWM_CH0_CSR_DIVMODE_LSB), "HW CSR does not match config"); + } + PICOTEST_END_SECTION(); + + + // Need to test the SDK APIs do the right thing + + PICOTEST_START_SECTION("PWM SDK API tests"); + for (int pwm = 0; pwm < NUM_PWM_SLICES; pwm++) { + pwm_slice_hw_t *slice = &pwm_hw->slice[pwm]; + int v = 100 + pwm * 10; + + pwm_set_wrap(pwm, v); + PICOTEST_CHECK_CHANNEL(pwm, slice->top == v, "pwm_set_wrap() failed to set register"); + + pwm_set_both_levels(pwm, v + 1, v); + PICOTEST_CHECK_CHANNEL(pwm, slice->cc == (((v) << PWM_CH0_CC_B_LSB) | ((v + 1) << PWM_CH0_CC_A_LSB)), + "pwm_set_compare() failed to set register"); + + float divider = 100.5; + int i = (int16_t) divider; + int f = (int8_t) ((divider - i) * 16); + pwm_set_clkdiv(pwm, divider); + PICOTEST_CHECK_CHANNEL(pwm, slice->div == (i << 4 | f), "pwm_set_divider_fract() failed to set register"); + + i++; + pwm_set_clkdiv_int_frac(pwm, i, f); + PICOTEST_CHECK_CHANNEL(pwm, slice->div == (i << 4 | f), + "pwm_set_divider_int_fract() failed to set register"); + + int c = 1234; + pwm_set_counter(pwm, c); + PICOTEST_CHECK_CHANNEL(pwm, slice->ctr == c, "pwm_set_count() failed to set register"); + + int cc = pwm_get_counter(pwm); + PICOTEST_CHECK_CHANNEL(pwm, slice->ctr == cc && cc == c, "pwm_get_count() failed to get register"); + + pwm_set_output_polarity(pwm, false, false); + PICOTEST_CHECK_CHANNEL(pwm, + !(slice->csr & PWM_CH0_CSR_A_INV_BITS) && !(slice->csr & PWM_CH0_CSR_B_INV_BITS), + "pwm_set_output_polarity() (F/F)"); + + pwm_set_output_polarity(pwm, true, false); + PICOTEST_CHECK_CHANNEL(pwm, (slice->csr & PWM_CH0_CSR_A_INV_BITS) && !(slice->csr & PWM_CH0_CSR_B_INV_BITS), + "pwm_set_output_polarity() (T/F)"); + + pwm_set_output_polarity(pwm, false, true); + PICOTEST_CHECK_CHANNEL(pwm, !(slice->csr & PWM_CH0_CSR_A_INV_BITS) && (slice->csr & PWM_CH0_CSR_B_INV_BITS), + "pwm_set_output_polarity() (F/T)"); + + pwm_set_output_polarity(pwm, true, true); + PICOTEST_CHECK_CHANNEL(pwm, (slice->csr & PWM_CH0_CSR_A_INV_BITS) && (slice->csr & PWM_CH0_CSR_B_INV_BITS), + "pwm_set_output_polarity() (T/T)"); + + pwm_set_phase_correct(pwm, true); + PICOTEST_CHECK_CHANNEL(pwm, (slice->csr & PWM_CH0_CSR_PH_CORRECT_BITS), "pwm_set_phase_correction(T)"); + + pwm_set_phase_correct(pwm, false); + PICOTEST_CHECK_CHANNEL(pwm, !(slice->csr & PWM_CH0_CSR_PH_CORRECT_BITS), "pwm_set_phase_correction(F)"); + + for (int m = PWM_DIV_FREE_RUNNING; m <= PWM_DIV_B_FALLING; m++) { + pwm_set_clkdiv_mode(pwm, m); + PICOTEST_CHECK_CHANNEL(pwm, ((slice->csr & PWM_CH0_CSR_DIVMODE_BITS) >> PWM_CH0_CSR_DIVMODE_LSB) == m, + "pwm_set_divider_mode"); + } + } + PICOTEST_END_SECTION(); + + PICOTEST_START_SECTION("PWM IRQ tests"); + + irq_set_exclusive_handler(PWM_IRQ_WRAP, on_pwm_wrap); + irq_set_enabled(PWM_IRQ_WRAP, true); + + config = pwm_get_default_config(); + + // Slow down the interrupt rate a load, don't need it high. + // This give about 40 per second on Picoboard + pwm_config_set_clkdiv(&config, 50); + + for (int pwm = 0; pwm < NUM_PWM_SLICES; pwm++) { + pwm_init(pwm, &config, false); + pwm_clear_irq(pwm); + pwm_set_irq_enabled(pwm, true); + } + + // Now enable all the PWM at the same time. + pwm_set_mask_enabled(0xff); + + sleep_ms(1000); + + int err = 0; + + for (int p = 0; p < NUM_PWM_SLICES; p++) { + PICOTEST_CHECK_CHANNEL(p, interrupt_states[p].count != 0, "No interrupts detected from PWM %d\n"); + } + + PICOTEST_END_SECTION(); + + PICOTEST_END_TEST(); +} + diff --git a/circuitpython/ports/raspberrypi/sdk/test/kitchen_sink/CMakeLists.txt b/circuitpython/ports/raspberrypi/sdk/test/kitchen_sink/CMakeLists.txt new file mode 100644 index 0000000..024d42c --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/kitchen_sink/CMakeLists.txt @@ -0,0 +1,120 @@ +add_library(kitchen_sink_libs INTERFACE) +target_sources(kitchen_sink_libs INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/kitchen_sink.c +) +target_link_libraries(kitchen_sink_libs INTERFACE + hardware_adc + hardware_clocks + hardware_divider + hardware_exception + hardware_dma + hardware_flash + hardware_gpio + hardware_i2c + hardware_interp + hardware_irq + hardware_pio + hardware_pll + hardware_pwm + hardware_resets + hardware_rtc + hardware_uart + hardware_spi + hardware_sync + hardware_timer + hardware_uart + hardware_vreg + hardware_watchdog + hardware_xosc + pico_bit_ops + pico_bootrom + pico_bootsel_via_double_reset + pico_divider + pico_double + pico_fix_rp2040_usb_device_enumeration + pico_float + pico_int64_ops + pico_malloc + pico_mem_ops + pico_multicore + pico_platform + pico_stdlib + pico_sync + pico_time + pico_unique_id + pico_util +) +# todo this is full of warnings atm +#if (TARGET tinyusb_device) +# target_include_directories(kitchen_sink_libs INTERFACE ${CMAKE_CURRENT_LIST_DIR}) +# target_link_libraries(kitchen_sink_libs INTERFACE tinyusb_device) +#endif() + +add_library(kitchen_sink_options INTERFACE) + +target_compile_options(kitchen_sink_options INTERFACE + -Werror + -Wall + -Wextra + # -pedantic + + -Wnull-dereference + -Wuninitialized + -Wunused + -Wcast-align + -Wall + -Wcast-qual + -Wfloat-equal + -Wmissing-format-attribute + -Wconversion + -Wsign-compare + $<$<COMPILE_LANGUAGE:C>:-Wstrict-prototypes> + + -Wno-inline + # todo not sure these are true, but investigate + #-Wpacked + + # todo we have some of these in usb_device_tiny to try to make it more readable.. perhaps doxygen would help here instead + -Wredundant-decls + + -Wno-shadow + -Wno-missing-field-initializers + -Wno-missing-braces + -Wno-multichar + + # todo useful but fix later + #-Wundef + ) + +target_compile_definitions(kitchen_sink_libs INTERFACE + PARAM_ASSERTIONS_ENABLE_ALL=1 # want to check all the assertions for compilation warnings + PICO_AUDIO_DMA_IRQ=1 +) + +add_executable(kitchen_sink) +target_link_libraries(kitchen_sink kitchen_sink_libs kitchen_sink_options) +pico_set_program_name(kitchen_sink "Wombat tentacles") +pico_add_extra_outputs(kitchen_sink) + +add_executable(kitchen_sink_extra_stdio) +if (COMMAND suppress_tinyusb_warnings) + # Explicitly suppress warnings in TinyUSB files which have them (this has to be done + # from the project that uses them per CMake "feature"). Note the function comes from + # TinyUSB itself, so we have to guard against TinyUSB not being present with the above if + suppress_tinyusb_warnings() +endif() +target_link_libraries(kitchen_sink_extra_stdio kitchen_sink_libs kitchen_sink_options) +pico_add_extra_outputs(kitchen_sink_extra_stdio) +pico_enable_stdio_usb(kitchen_sink_extra_stdio 1) +pico_enable_stdio_semihosting(kitchen_sink_extra_stdio 1) + +add_executable(kitchen_sink_copy_to_ram) +pico_set_binary_type(kitchen_sink_copy_to_ram copy_to_ram) +target_link_libraries(kitchen_sink_copy_to_ram kitchen_sink_libs kitchen_sink_options) +pico_add_extra_outputs(kitchen_sink_copy_to_ram) + +add_executable(kitchen_sink_no_flash) +pico_set_binary_type(kitchen_sink_no_flash no_flash) +target_link_libraries(kitchen_sink_no_flash kitchen_sink_libs kitchen_sink_options) +pico_add_extra_outputs(kitchen_sink_no_flash) + diff --git a/circuitpython/ports/raspberrypi/sdk/test/kitchen_sink/kitchen_sink.c b/circuitpython/ports/raspberrypi/sdk/test/kitchen_sink/kitchen_sink.c new file mode 100644 index 0000000..57d4ef7 --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/kitchen_sink/kitchen_sink.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdio.h> +// Include all headers to check for compiler warnings +#include "hardware/adc.h" +#include "hardware/claim.h" +#include "hardware/clocks.h" +#include "hardware/divider.h" +#include "hardware/dma.h" +#include "hardware/exception.h" +#include "hardware/flash.h" +#include "hardware/gpio.h" +#include "hardware/i2c.h" +#include "hardware/interp.h" +#include "hardware/irq.h" +#include "hardware/pio.h" +#include "hardware/pio_instructions.h" +#include "hardware/pll.h" +#include "hardware/pwm.h" +#include "hardware/resets.h" +#include "hardware/rtc.h" +#include "hardware/spi.h" +#include "hardware/sync.h" +#include "hardware/timer.h" +#include "hardware/uart.h" +#include "hardware/vreg.h" +#include "hardware/watchdog.h" +#include "hardware/xosc.h" +#include "pico/binary_info.h" +#include "pico/bit_ops.h" +#include "pico/bootrom.h" +#include "pico/divider.h" +#include "pico/double.h" +#include "pico/fix/rp2040_usb_device_enumeration.h" +#include "pico/float.h" +#include "pico/int64_ops.h" +#include "pico/malloc.h" +#include "pico/multicore.h" +#include "pico/printf.h" +#include "pico/runtime.h" +#include "pico/stdio.h" +#include "pico/stdlib.h" +#include "pico/sync.h" +#include "pico/time.h" +#include "pico/unique_id.h" + +#include "hardware/structs/adc.h" +#include "hardware/structs/bus_ctrl.h" +#include "hardware/structs/clocks.h" +#include "hardware/structs/dma.h" +#include "hardware/structs/i2c.h" +#include "hardware/structs/interp.h" +#include "hardware/structs/iobank0.h" +#include "hardware/structs/ioqspi.h" +#include "hardware/structs/mpu.h" +#include "hardware/structs/padsbank0.h" +#include "hardware/structs/pads_qspi.h" +#include "hardware/structs/pio.h" +#include "hardware/structs/pll.h" +#include "hardware/structs/psm.h" +#include "hardware/structs/pwm.h" +#include "hardware/structs/resets.h" +#include "hardware/structs/rosc.h" +#include "hardware/structs/rtc.h" +#include "hardware/structs/scb.h" +#include "hardware/structs/sio.h" +#include "hardware/structs/spi.h" +#include "hardware/structs/ssi.h" +#include "hardware/structs/syscfg.h" +#include "hardware/structs/systick.h" +#include "hardware/structs/timer.h" +#include "hardware/structs/uart.h" +#include "hardware/structs/usb.h" +#include "hardware/structs/vreg_and_chip_reset.h" +#include "hardware/structs/watchdog.h" +#include "hardware/structs/xip_ctrl.h" +#include "hardware/structs/xosc.h" + +bi_decl(bi_block_device( + BINARY_INFO_MAKE_TAG('K', 'S'), + "foo", + 0x80000, + 0x40000, + NULL, + BINARY_INFO_BLOCK_DEV_FLAG_READ | BINARY_INFO_BLOCK_DEV_FLAG_WRITE | + BINARY_INFO_BLOCK_DEV_FLAG_PT_UNKNOWN)); + +//#pragma GCC push_options +//#pragma GCC optimize ("O3") + +uint32_t *foo = (uint32_t *) 200; + +uint32_t dma_to = 0; +uint32_t dma_from = 0xaaaa5555; + +void spiggle(void) { + dma_channel_config c = dma_channel_get_default_config(1); + channel_config_set_bswap(&c, true); + channel_config_set_transfer_data_size(&c, DMA_SIZE_16); + channel_config_set_ring(&c, true, 13); + dma_channel_set_config(1, &c, false); + dma_channel_transfer_from_buffer_now(1, foo, 23); +} + +void __isr dma_handler_a(void) { + printf("HELLO A\n"); + if (dma_hw->ints1 & 1) { + dma_hw->ints1 = 1; + printf("A WINS DMA_TO %08x\n", (uint) dma_to); + irq_remove_handler(DMA_IRQ_1, dma_handler_a); + } +} + +void __isr dma_handler_b(void) { + printf("HELLO B\n"); + if (dma_hw->ints1 & 1) { + dma_hw->ints1 = 1; + printf("B WINS DMA_TO %08x\n", (uint) dma_to); +// irq_remove_handler(DMA_IRQ_1, dma_handler_b); + } +} + +//#pragma GCC pop_options + +int main(void) { + spiggle(); + + stdio_init_all(); + + printf("HI %d\n", (int)time_us_32()); + puts("Hello Everything!"); + puts("Hello Everything2!"); + + irq_add_shared_handler(DMA_IRQ_1, dma_handler_a, 0x80); + irq_add_shared_handler(DMA_IRQ_1, dma_handler_b, 0xC0); + + dma_channel_config config = dma_channel_get_default_config(0); +// set_exclusive_irq_handler(DMA_IRQ_1, dma_handler_a); + dma_channel_set_irq1_enabled(0, true); + irq_set_enabled(DMA_IRQ_1, true); + dma_channel_configure(0, &config, &dma_to, &dma_from, 1, true); + dma_channel_set_config(0, &config, false); + + // note this loop expects to cause a breakpoint!! + for (int i = 0; i < 20; i++) { + puts("sleepy"); + sleep_ms(1000); + dma_channel_configure(0, &config, &dma_to, &dma_from, 1, true); + if (i==3) { + irq_remove_handler(DMA_IRQ_1, dma_handler_a); + } + if (i==2) { + irq_remove_handler(DMA_IRQ_1, dma_handler_b); + } + } + // this should compile as we are Cortex M0+ + __asm volatile("SVC #3"); +} diff --git a/circuitpython/ports/raspberrypi/sdk/test/pico_divider_test/CMakeLists.txt b/circuitpython/ports/raspberrypi/sdk/test/pico_divider_test/CMakeLists.txt new file mode 100644 index 0000000..d2ab012 --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/pico_divider_test/CMakeLists.txt @@ -0,0 +1,38 @@ +PROJECT(pico_divider_test) + +if (PICO_ON_DEVICE) + add_executable(pico_divider_test + pico_divider_test.c + ) + + target_link_libraries(pico_divider_test pico_stdlib) + + pico_set_divider_implementation(pico_divider_test hardware_explicit) # want to compare against compiler impl + + pico_add_extra_outputs(pico_divider_test) + + target_compile_definitions(pico_divider_test PRIVATE +# PICO_DIVIDER_DISABLE_INTERRUPTS=1 +# TURBO + ) + + # this is a separate test as hardware_explicit above causes it not to be tested at all! + add_library(pico_divider_nesting_test_core INTERFACE) + target_sources(pico_divider_nesting_test_core INTERFACE + pico_divider_nesting_test.c + ) + target_link_libraries(pico_divider_nesting_test_core INTERFACE pico_stdlib hardware_dma) + + add_executable(pico_divider_nesting_test_with_dirty_check) + target_link_libraries(pico_divider_nesting_test_with_dirty_check pico_divider_nesting_test_core) + pico_set_divider_implementation(pico_divider_nesting_test_with_dirty_check hardware) + pico_add_extra_outputs(pico_divider_nesting_test_with_dirty_check) + + add_executable(pico_divider_nesting_test_with_disable_irq) + target_link_libraries(pico_divider_nesting_test_with_disable_irq pico_divider_nesting_test_core) + target_compile_definitions(pico_divider_nesting_test_with_disable_irq PRIVATE + PICO_DIVIDER_DISABLE_INTERRUPTS=1) + pico_set_divider_implementation(pico_divider_nesting_test_with_disable_irq hardware) + pico_add_extra_outputs(pico_divider_nesting_test_with_disable_irq) + +endif()
\ No newline at end of file diff --git a/circuitpython/ports/raspberrypi/sdk/test/pico_divider_test/pico_divider_nesting_test.c b/circuitpython/ports/raspberrypi/sdk/test/pico_divider_test/pico_divider_nesting_test.c new file mode 100644 index 0000000..3b5a745 --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/pico_divider_test/pico_divider_nesting_test.c @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include "pico/stdlib.h" +#include "hardware/dma.h" +#include "hardware/irq.h" + +volatile bool failed; +volatile uint32_t count[3]; +volatile bool done; + +#define FAILED() ({ failed = true; }) +//#define FAILED() ({ failed = true; __breakpoint(); }) + +bool timer_callback(repeating_timer_t *t) { + count[0]++; + static int z; + for (int i=0; i<100;i++) { + z += 23; + int a = z / 7; + int b = z % 7; + if (z != a * 7 + b) { + FAILED(); + } + a = z / -7; + b = z % -7; + if (z != a * -7 + b) { + FAILED(); + } + } + float fz = z; + float fa = fz / 11.0f; + float fb = fmodf(fz, 11.0f); + if (fabsf(fz - (fa * 11.0 + fb) > 1e-9)) { + FAILED(); + } + double dz = z; + double da = dz / 11.0; + double db = fmod(dz, 11.0); + if (fabsf(dz - (da * 11.0 + db) > 1e-9)) { + FAILED(); + } + + return !done; +} + +void do_dma_start(uint ch) { + static uint32_t word[2]; + assert(ch < 2); + dma_channel_config c = dma_channel_get_default_config(ch); + // todo remove this; landing in a separate PR +#ifndef DREQ_DMA_TIMER0 +#define DREQ_DMA_TIMER0 0x3b +#endif + channel_config_set_dreq(&c, DREQ_DMA_TIMER0); + dma_channel_configure(ch, &c, &word[ch], &word[ch], 513 + ch * 23, true); +} + +double d0c, d0s, d0t, dz; +float f0c, f0s, f0t, fz; + +void test_irq_handler0() { + count[1]++; + dma_hw->ints0 |= 1u; + static uint z; + static uint dz; + for (int i=0; i<80;i++) { + z += 31; + uint a = z / 11; + uint b = z % 11; + if (z != a * 11 + b) { + FAILED(); + } + } + if (done) dma_channel_abort(0); + else do_dma_start(0); +} + +void test_irq_handler1() { + static uint z; + dma_hw->ints1 |= 2u; + count[2]++; + for (int i=0; i<130;i++) { + z += 47; + uint a = z / -13; + uint b = z % -13; + if (z != a * -13 + b) { + FAILED(); + } + static uint64_t z64; + z64 -= 47; + uint64_t a64 = z64 / -13; + uint64_t b64 = z64 % -13; + if (z64 != a64 * -13 + b64) { + FAILED(); + } + } + + if (done) dma_channel_abort(1); + else do_dma_start(1); +} + +void test_nesting() { + uint z = 0; + + // We have 3 different IRQ handlers, one for timer, two for DMA completion (on DMA_IRQ0/1) + // thus we expect re-entrancy even between IRQs + // + // They all busily make use of the dividers, to expose any issues with nested use + + repeating_timer_t timer; + add_repeating_timer_us(929, timer_callback, NULL, &timer); + irq_set_exclusive_handler(DMA_IRQ_0, test_irq_handler0); + irq_set_exclusive_handler(DMA_IRQ_1, test_irq_handler1); + + dma_set_irq0_channel_mask_enabled(1u, true); + dma_set_irq1_channel_mask_enabled(2u, true); + dma_hw->timer[0] = (1 << 16) | 32; // run at 1/32 system clock + + irq_set_enabled(DMA_IRQ_0, 1); + irq_set_enabled(DMA_IRQ_1, 1); + do_dma_start(0); + do_dma_start(1); + absolute_time_t end = delayed_by_ms(get_absolute_time(), 10000); + int count_local=0; + while (!time_reached(end)) { + for(uint i=0;i<100;i++) { + z += 31; + uint a = z / 11; + uint b = z % 11; + if (z != a * 11 + b) { + FAILED(); + } + int zz = (int)z; + int aa = zz / -11; + int bb = zz % -11; + if (zz != aa * -11 + bb) { + FAILED(); + } + aa = -zz / -11; + bb = -zz % -11; + if (-zz != aa * -11 + bb) { + FAILED(); + } + aa = -zz / 11; + bb = -zz % 11; + if (-zz != aa * 11 + bb) { + FAILED(); + } + a = 0xffffffffu / 11; + b = 0xffffffffu % 11; + if (0xffffffffu != a * 11 + b) { + FAILED(); + } + static uint64_t z64; + z64 -= 47; + uint64_t a64 = z64 / -13635; + uint64_t b64 = z64 % -13635; + if (z64 != a64 * -13635 + b64) { + FAILED(); + } + // specifically check 64/32 divide + static uint64_t c64 = 0x13ffffffffull; + static uint32_t cd = 1; + a64 = c64 / cd; + b64 = c64 % cd; + if (c64 != a64 * cd + b64) { + FAILED(); + } + cd++; + + } + // these use the divider + for(uint i=0;i<=100;i+=20) { + // both in and out bootrom range (we perform mod in wrapper code if necessarry) + f0t = tanf(i * 50); + f0c = cosf(i * 50); + f0s = sinf(i * 50); + d0t = tan(i * 1000); + d0c = cos(i * 1000); + d0s = sin(i * 1000); + } + count_local++; + } + done = true; + cancel_repeating_timer(&timer); + printf("%d: %d %d %d\n", count_local, (int)count[0], (int)count[1], (int)count[2]); + + // make sure all the IRQs ran + if (!(count_local && count[0] && count[1] && count[2])) { + printf("DID NOT RUN\n"); + exit(1); + } + if (failed) { + printf("FAILED\n"); + exit(1); + } +} + +int main() { +#ifndef uart_default +#warning test/pico_divider requires a default uart +#else + stdio_init_all(); +#endif + test_nesting(); + printf("PASSED\n"); + return 0; +} + diff --git a/circuitpython/ports/raspberrypi/sdk/test/pico_divider_test/pico_divider_test.c b/circuitpython/ports/raspberrypi/sdk/test/pico_divider_test/pico_divider_test.c new file mode 100644 index 0000000..fc0e9ef --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/pico_divider_test/pico_divider_test.c @@ -0,0 +1,385 @@ +// make test-div64_64.bin && qemu-system-arm -M lm3s6965evb -cpu cortex-m3 -nographic -serial null -monitor null -semihosting -kernel test-div64_64.bin +#include <stdio.h> +#include <math.h> +#include "pico/divider.h" +#include "pico/stdlib.h" + +#ifdef TURBO +#include "hardware/vreg.h" +#endif + +typedef uint64_t ui64; +typedef int64_t i64; +typedef uint32_t ui32; +typedef int32_t i32; + +void test_mulib_divu64u64(ui64*y,ui64*x,ui64*q,ui64*r) { + *q = divmod_u64u64_rem(*y, *x, r); +} +void test_mulib_divs64s64( i64*y, i64*x, i64*q, i64*r) { + *q = divmod_s64s64_rem(*y, *x, r); +} + +ui32 hwdiv_data[4]; + +void hwdiv_sim() { + hwdiv_data[2]=hwdiv_data[0]/hwdiv_data[1]; + hwdiv_data[3]=hwdiv_data[0]%hwdiv_data[1]; +// ostr("HWS: "); +// o8hex(hwdiv_data[0]); osp(); +// o8hex(hwdiv_data[1]); osp(); +// o8hex(hwdiv_data[2]); osp(); +// o8hex(hwdiv_data[3]); onl(); +} + +ui64 ntests=0; + + +#ifdef uart_default +void o1ch(int c) { + uart_putc(uart_default, c); +} + +void ostr(char*p) { while(*p) o1ch(*p++); } +void onl() {ostr("\r\n");} +void osp() {o1ch(' ');} +void ostrnl(char*p) { ostr(p); onl();} +void o1hex(int u) {u&=0x0f; if(u>=10) o1ch(u-10+'A'); else o1ch(u+'0');} +void o2hex(int u) {o1hex(u>> 4); o1hex(u);} +void o4hex(int u) {o2hex(u>> 8); o2hex(u);} +void o8hex(int u) {o4hex(u>>16); o4hex(u);} +void o16hex(ui64 u) {o8hex(u>>32); o8hex(u);} +unsigned int odig(unsigned int*pv,unsigned int d,int zf) { + char c='0'; + unsigned int v=*pv; + while(v>=d) v-=d,c++; + if(zf==1&&c=='0') osp(); + else o1ch(c),zf=0; + *pv=v; + return zf; +} + +void odec(int u) { + unsigned int v=u; + int zf=1; + if(u<0) o1ch('-'),v=-v; + zf=odig(&v,1000000000,zf); + zf=odig(&v,100000000,zf); + zf=odig(&v,10000000,zf); + zf=odig(&v,1000000,zf); + zf=odig(&v,100000,zf); + zf=odig(&v,10000,zf); + zf=odig(&v,1000,zf); + zf=odig(&v,100,zf); + zf=odig(&v,10,zf); + zf=odig(&v,1,0); +} +#endif + + +int xdigval(int c) { + if(c>='0'&&c<='9') return c-'0'; + if(c>='A'&&c<='F') return c-'A'+10; + if(c>='a'&&c<='f') return c-'a'+10; + return -1; +} + +ui64 seed; + +ui64 rnd64() { + if(seed&1) seed=(seed>>1)^0x800000000000000dULL; + else seed= seed>>1; + return seed; +} + +unsigned int rnd32() { + return rnd64(); +} + +//#define RANDOMISE +//#define rfn "/dev/random" + + +#ifdef uart_default +void test_divu64u64(ui64 y,ui64 x) { + ui64 q,r; + test_mulib_divu64u64(&y,&x,&q,&r); +#if !PICO_ON_DEVICE + if (!x) return; +#endif + if(q==y/x&&r==y%x) ; + else { + ostr("U "); + o16hex(y); osp(); + o16hex(x); osp(); + o16hex(q); osp(); + o16hex(r); + ostr(" : "); + o16hex(y/x); osp(); + o16hex(y%x); onl(); + } + ntests++; +} + +void test_divs64s64(i64 y,i64 x) { + i64 q,r; +#if !PICO_ON_DEVICE + if (y == INT64_MIN) return; +#endif + test_mulib_divs64s64(&y,&x,&q,&r); +#if !PICO_ON_DEVICE + if (!x) return; +#endif + if(q==y/x&&r==y%x) ; + else { + ostr("S "); + o16hex(y); osp(); + o16hex(x); osp(); + o16hex(q); osp(); + o16hex(r); + ostr(" : "); + o16hex(y/x); osp(); + o16hex(y%x); onl(); + } + ntests++; +} + + +// for all x and y consisting of a single run of 1:s, test a region around (x,y) +void test_special() { + int i0,j0,i1,j1,dy,dx; + ui64 y,x; + for(i0=0;i0<64;i0++) { + y=0; + for(i1=i0;i1<65;i1++) { + for(j0=0;j0<64;j0++) { + x=0; + for(j1=j0;j1<65;j1++) { +#define A 2 + for(dy=-A;dy<=A;dy++) { + for(dx=-A;dx<=A;dx++) { + test_divu64u64( y+dy, x+dx); + test_divs64s64( y+dy, x+dx); + test_divs64s64( y+dy,-x-dx); + test_divs64s64(-y-dy, x+dx); + test_divs64s64(-y-dy,-x-dx); + } + } + x|=1ULL<<j1; + } + } + y|=1ULL<<i1; + } + odec(i0+1); ostr(" "); odec(i1+1); ostr(" specials\n"); + } +} + +void test_random() { + int i,j; + ui64 y,x,m; + for(i=0;;i++) { + for(j=0;j<200000;j++) { + m=1ULL<<(rnd32()%48+15); m+=m-1; y=rnd64()&m; + m=1ULL<<(rnd32()%48+15); m+=m-1; x=rnd64()&m; + test_divu64u64( y, x); + test_divs64s64( y, x); + test_divs64s64( y,-x); + test_divs64s64(-y, x); + test_divs64s64(-y,-x); + } + odec(i+1); ostr("M\n"); + } +} +#endif + +uint32_t __attribute__((naked)) time_32(uint32_t a, uint32_t b, uint32_t (*func)(uint32_t a, uint32_t b)) { + asm( + ".syntax unified\n" + "push {r4, r5, lr}\n" + "ldr r4, =#0xe000e018\n" + "ldr r5, [r4]\n" + "blx r2\n" + "ldr r0, [r4]\n" + "subs r5, r0\n" + "lsls r0, r5, #8\n" + "asrs r0, #8\n" + "pop {r4, r5, pc}\n" + ); +} + +uint32_t __attribute__((naked)) time_64(uint64_t a, uint64_t b, uint64_t (*func64)(uint64_t a, uint64_t b)) { + asm( + ".syntax unified\n" + "push {r4-r6, lr}\n" + "ldr r6, [sp, #16]\n" + "ldr r4, =#0xe000e018\n" + "ldr r5, [r4]\n" + "blx r6\n" + "ldr r0, [r4]\n" + "subs r5, r0\n" + "lsls r0, r5, #8\n" + "asrs r0, #8\n" + "pop {r4-r6, pc}\n" + ); +} + +uint32_t compiler_div_s32(uint32_t a, uint32_t b) { + return ((int32_t)a) / (int32_t)b; +} + +uint32_t pico_div_s32(uint32_t a, uint32_t b) { + return div_s32s32(a, b); +} + +uint32_t compiler_div_u32(uint32_t a, uint32_t b) { + return a/b; +} + +uint32_t pico_div_u32(uint32_t a, uint32_t b) { + return div_u32u32(a, b); +} + +uint64_t compiler_div_s64(uint64_t a, uint64_t b) { + return ((int64_t)a) / (int64_t)b; +} + +uint64_t pico_div_s64(uint64_t a, uint64_t b) { + return div_s64s64(a, b); +} + +uint64_t compiler_div_u64(uint64_t a, uint64_t b) { + return a/b; +} + +uint64_t pico_div_u64(uint64_t a, uint64_t b) { + return div_u64u64(a, b); +} + + +void perf_test() { + *(volatile unsigned int *)0xe000e010=5; // enable SYSTICK at core clock + + for(int bit = 30; bit>=0; bit--) { + int div = 1u << (31-bit); + const int N = 1000; + int tc = 0, tp = 0; + for (int i = 0; i < N; i++) { + int a = rnd32(); + int b; + do { + b = rnd32() / div; + } while (b == 0); + tc += time_32(a, b, compiler_div_s32); + tp += time_32(a, b, pico_div_s32); + } + printf(" S32 %d %f\t%f\n", bit, tc / 1000.0, tp / 1000.0); + } + + for(int bit = 30; bit>=0; bit--) { + int div = 1u << (31-bit); + const int N = 1000; + int tc = 0, tp = 0; + for (int i = 0; i < N; i++) { + int a = rnd32(); + int b; + do { + b = rnd32() / div; + } while (b == 0); + tc += time_32(a, b, compiler_div_u32); + tp += time_32(a, b, pico_div_u32); + } + printf(" U32 %d %f\t%f\n", bit, tc / 1000.0, tp / 1000.0); + } + + for(int extra = 0; extra <= 48; extra+=16) + { + for(int bit = 62; bit>=0; bit--) { + int64_t div = 1ull << (62-bit); + const int N = 1000; + int tc = 0, tp = 0; + for (int i = 0; i < N; i++) { + int64_t a = rnd64() / (1u << extra); + int64_t b; + do { + b = ((int64_t)rnd64()) / div; + } while (b == 0); + tc += time_64(a, b, compiler_div_s64); + tp += time_64(a, b, pico_div_s64); + } + printf(" S64 %d %d %f\t%f\n", extra, bit, tc / 1000.0, tp / 1000.0); + } + + for(int bit = 62; bit>=0; bit--) { + int64_t div = 1ull << (62-bit); + const int N = 1000; + int tc = 0, tp = 0; + for (int i = 0; i < N; i++) { + uint64_t a = rnd64(); + uint64_t b; + do { + b = rnd64() / div; + } while (b == 0); + tc += time_64(a, b, compiler_div_u64); + tp += time_64(a, b, pico_div_u64); + } + printf(" U64 %d %d %f\t%f\n", extra, bit, tc / 1000.0, tp / 1000.0); + } + } +} + +int main() { +#ifndef uart_default +#warning test/pico_divider requires a default uart +#else +#ifdef TURBO + vreg_set_voltage(VREG_VOLTAGE_MAX); + set_sys_clock_khz(48000*8, true); +#endif + setup_default_uart(); +#ifdef RANDOMISE + int u; + ifh=sys_host(SYS_OPEN,(int)rfn,0,strlen(rfn)); + u=sys_host(SYS_READ,ifh,(int)&seed,sizeof(seed)); + if(u) {ostrnl("Error reading random stream"); return 16;} + sys_host(SYS_CLOSE,ifh,0,0); +#else + seed=12233524287791987605ULL; +#endif + perf_test(); + ostr("begin\n"); + test_divu64u64( 38, 6); + test_divs64s64( 38, 6); + test_divs64s64( 38,-6); + test_divs64s64(-38, 6); + test_divs64s64(-38,-6); + test_divu64u64(1234567890123ULL,6); + test_divu64u64(0x0000000100000000ULL,6); + test_divu64u64(0xffffffffffffffffULL,6); + test_special(); + o16hex(ntests); + ostr(" special tests done; starting random tests\n"); + test_divu64u64(0xf123456789abcdefULL,0x0000000100000000ULL); + test_divu64u64(0xf123456789abcdefULL,0x00000001ffffffffULL); + test_divu64u64(0xf123456789abcdefULL,0x00000003ffffffffULL); + test_divu64u64(0xf123456789abcdefULL,0x00000007ffffffffULL); + test_divu64u64(0xf123456789abcdefULL,0x0000000fffffffffULL); + test_divu64u64(0xf123456789abcdefULL,0x0000001fffffffffULL); + test_divu64u64(0xf123456789abcdefULL,0x0000003fffffffffULL); + test_divu64u64(0xf123456789abcdefULL,0x0000007fffffffffULL); + test_divu64u64(0xf123456789abcdefULL,0x000000ffffffffffULL); + test_divu64u64(0xf123456789abcdefULL,0x000001ffffffffffULL); + test_divu64u64(0xf123456789abcdefULL,0x000003ffffffffffULL); + test_divu64u64(0xf123456789abcdefULL,0x000007ffffffffffULL); + test_divu64u64(0xf123456789abcdefULL,0x00000fffffffffffULL); + test_divu64u64(0xf123456789abcdefULL,0x00001fffffffffffULL); + test_divu64u64(0xf123456789abcdefULL,0x00003fffffffffffULL); + test_divu64u64(0xf123456789abcdefULL,0x00007fffffffffffULL); + test_divu64u64(0xf123456789abcdefULL,0x0000ffffffffffffULL); + + test_random(); + + ostr("END\n"); + return 0; +#endif +} + diff --git a/circuitpython/ports/raspberrypi/sdk/test/pico_float_test/CMakeLists.txt b/circuitpython/ports/raspberrypi/sdk/test/pico_float_test/CMakeLists.txt new file mode 100644 index 0000000..26204d3 --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/pico_float_test/CMakeLists.txt @@ -0,0 +1,42 @@ +PROJECT(pico_float_test) + +add_executable(pico_float_test + pico_float_test.c + llvm/call_apsr.S + ) + +add_executable(pico_double_test + pico_double_test.c + llvm/call_apsr.S + ) + + +#todo split out variants with different flags +target_compile_definitions(pico_float_test PRIVATE + PICO_USE_CRT_PRINTF=1 # want full precision output +# PICO_FLOAT_PROPAGATE_NANS=1 +# PICO_DIVIDER_DISABLE_INTERRUPTS=1 +) + +#todo split out variants with different flags +target_compile_definitions(pico_double_test PRIVATE + PICO_USE_CRT_PRINTF=1 # want full precision output + PICO_FLOAT_PROPAGATE_NANS=1 + #PICO_DOUBLE_PROPAGATE_NANS=1 + #PICO_DIVIDER_DISABLE_INTERRUPTS=1 + ) + +# handy for testing we aren't pulling in extra stuff +#target_link_options(pico_float_test PRIVATE -nodefaultlibs) + +target_include_directories(pico_float_test PRIVATE ${CMAKE_CURRENT_LIST_DIR}/llvm) +target_link_libraries(pico_float_test pico_float pico_stdlib) +pico_add_extra_outputs(pico_float_test) +#pico_set_float_implementation(pico_float_test compiler) +#pico_set_double_implementation(pico_float_test compiler) + +target_include_directories(pico_double_test PRIVATE ${CMAKE_CURRENT_LIST_DIR}/llvm) +target_link_libraries(pico_double_test pico_double pico_stdlib) +pico_add_extra_outputs(pico_double_test) +#pico_set_float_implementation(pico_double_test compiler) +#pico_set_double_implementation(pico_double_test compiler)
\ No newline at end of file diff --git a/circuitpython/ports/raspberrypi/sdk/test/pico_float_test/llvm/LICENSE.TXT b/circuitpython/ports/raspberrypi/sdk/test/pico_float_test/llvm/LICENSE.TXT new file mode 100644 index 0000000..ff63f2b --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/pico_float_test/llvm/LICENSE.TXT @@ -0,0 +1,68 @@ +============================================================================== +LLVM Release License +============================================================================== +University of Illinois/NCSA +Open Source License + +Copyright (c) 2003-2017 University of Illinois at Urbana-Champaign. +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. + +============================================================================== +Copyrights and Licenses for Third Party Software Distributed with LLVM: +============================================================================== +The LLVM software contains code written by third parties. Such software will +have its own individual LICENSE.TXT file in the directory in which it appears. +This file will describe the copyrights, license, and restrictions which apply +to that code. + +The disclaimer of warranty in the University of Illinois Open Source License +applies to all code in the LLVM Distribution, and nothing in any of the +other licenses gives permission to use the names of the LLVM Team or the +University of Illinois to endorse or promote products derived from this +Software. + +The following pieces of software have additional or alternate copyrights, +licenses, and/or restrictions: + +Program Directory +------- --------- +Google Test llvm/utils/unittest/googletest +OpenBSD regex llvm/lib/Support/{reg*, COPYRIGHT.regex} +pyyaml tests llvm/test/YAMLParser/{*.data, LICENSE.TXT} +ARM contributions llvm/lib/Target/ARM/LICENSE.TXT +md5 contributions llvm/lib/Support/MD5.cpp llvm/include/llvm/Support/MD5.h diff --git a/circuitpython/ports/raspberrypi/sdk/test/pico_float_test/llvm/call_apsr.S b/circuitpython/ports/raspberrypi/sdk/test/pico_float_test/llvm/call_apsr.S new file mode 100644 index 0000000..6c7d361 --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/pico_float_test/llvm/call_apsr.S @@ -0,0 +1,38 @@ +//===-- call_apsr.S - Helpers for ARM EABI floating point tests -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements helpers for ARM EABI floating point tests for the +// compiler_rt library. +// +//===- + +.syntax unified +.cpu cortex-m0plus +.thumb + +.align 2 + +.global call_apsr_f +.type call_apsr_f,%function +.thumb_func +call_apsr_f: + push {lr} + blx r2 + mrs r0, apsr + pop {pc} + +.global call_apsr_d +.type call_apsr_d,%function +.thumb_func +call_apsr_d: + push {r4, lr} + ldr r4, [sp, #8] + blx r4 + mrs r0, apsr + pop {r4, pc} diff --git a/circuitpython/ports/raspberrypi/sdk/test/pico_float_test/llvm/call_apsr.h b/circuitpython/ports/raspberrypi/sdk/test/pico_float_test/llvm/call_apsr.h new file mode 100644 index 0000000..36a7489 --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/pico_float_test/llvm/call_apsr.h @@ -0,0 +1,40 @@ +//todo check license +//===-- call_apsr.h - Helpers for ARM EABI floating point tests -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares helpers for ARM EABI floating point tests for the +// compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#ifndef CALL_APSR_H +#define CALL_APSR_H + +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ +#error big endian support not implemented +#endif + +union cpsr { + struct { + uint32_t filler: 28; + uint32_t v: 1; + uint32_t c: 1; + uint32_t z: 1; + uint32_t n: 1; + } flags; + uint32_t value; +}; + +extern __attribute__((pcs("aapcs"))) +uint32_t call_apsr_f(float a, float b, __attribute__((pcs("aapcs"))) void (*fn)(float, float)); + +extern __attribute__((pcs("aapcs"))) +uint32_t call_apsr_d(double a, double b, __attribute__((pcs("aapcs"))) void (*fn)(double, double)); + +#endif // CALL_APSR_H diff --git a/circuitpython/ports/raspberrypi/sdk/test/pico_float_test/pico_double_test.c b/circuitpython/ports/raspberrypi/sdk/test/pico_float_test/pico_double_test.c new file mode 100644 index 0000000..6c095e7 --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/pico_float_test/pico_double_test.c @@ -0,0 +1,505 @@ +//===-- aeabi_cdcmpeq.c - Test __aeabi_cdcmpeq ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file tests __aeabi_cdcmpeq for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <pico/double.h> +#include "pico/stdlib.h" +#include "inttypes.h" + +extern int __aeabi_dcmpun(double a, double b); + +#if __arm__ + +#include "call_apsr.h" + +extern __attribute__((pcs("aapcs"))) void __aeabi_cdcmpeq(double a, double b); + +int test__aeabi_cdcmpeq(double a, double b, int expected) { + uint32_t cpsr_value = call_apsr_d(a, b, __aeabi_cdcmpeq); + union cpsr cpsr = {.value = cpsr_value}; + if (expected != cpsr.flags.z) { + printf("error in __aeabi_cdcmpeq(%f, %f) => Z = %08x, expected %08x\n", + a, b, cpsr.flags.z, expected); + return 1; + } + return 0; +} + +#endif + +int test_cdcmpeq() { +#if __arm__ + if (test__aeabi_cdcmpeq(1.0, 1.0, 1)) + return 1; + if (test__aeabi_cdcmpeq(1234.567, 765.4321, 0)) + return 1; + if (test__aeabi_cdcmpeq(-123.0, -678.0, 0)) + return 1; + if (test__aeabi_cdcmpeq(0.0, -0.0, 1)) + return 1; + if (test__aeabi_cdcmpeq(0.0, 0.0, 1)) + return 1; + if (test__aeabi_cdcmpeq(-0.0, -0.0, 1)) + return 1; + if (test__aeabi_cdcmpeq(-0.0, 0.0, 1)) + return 1; + if (test__aeabi_cdcmpeq(0.0, -1.0, 0)) + return 1; + if (test__aeabi_cdcmpeq(-0.0, -1.0, 0)) + return 1; + if (test__aeabi_cdcmpeq(-1.0, 0.0, 0)) + return 1; + if (test__aeabi_cdcmpeq(-1.0, -0.0, 0)) + return 1; + if (test__aeabi_cdcmpeq(1.0, NAN, 0)) + return 1; + if (test__aeabi_cdcmpeq(NAN, 1.0, 0)) + return 1; + if (test__aeabi_cdcmpeq(NAN, NAN, 0)) + return 1; + if (test__aeabi_cdcmpeq(INFINITY, 1.0, 0)) + return 1; + if (test__aeabi_cdcmpeq(0.0, INFINITY, 0)) + return 1; + if (test__aeabi_cdcmpeq(-INFINITY, 0.0, 0)) + return 1; + if (test__aeabi_cdcmpeq(0.0, -INFINITY, 0)) + return 1; + if (test__aeabi_cdcmpeq(INFINITY, INFINITY, 1)) + return 1; + if (test__aeabi_cdcmpeq(-INFINITY, -INFINITY, 1)) + return 1; +#else + printf("skipped\n"); +#endif + return 0; +} + +#if __arm__ + +extern __attribute__((pcs("aapcs"))) void __aeabi_cdcmple(double a, double b); + +extern __attribute__((pcs("aapcs"))) void __aeabi_cdrcmple(double a, double b); + +int test_dcmple_gt(double a, double b, int expected) { + if ((a <= b) != expected) { + printf("error in dcmple(%f, %f) => %d, expected %d\n", + a, b, a <= b, expected); + return 1; + } + if ((a > b) == expected && !isnan(a) && !isnan(b)) { + printf("error in dcmpgt(%f, %f) => %d, expected %d\n", + a, b, a > b, !expected); + return 1; + } + return 0; +} + +int test_dcmplt_ge(double a, double b, int expected) { + if ((a < b) != expected) { + printf("error in dcmplt(%f, %f) => %d, expected %d\n", + a, b, a < b, expected); + return 1; + } + if ((a >= b) == expected && !isnan(a) && !isnan(b)) { + printf("error in dcmpge(%f, %f) => %d, expected %d\n", + a, b, a >= b, !expected); + return 1; + } + return 0; +} + +int test__aeabi_cdcmple(double a, double b, int expected) { + int32_t cpsr_value = call_apsr_d(a, b, __aeabi_cdcmple); + int32_t r_cpsr_value = call_apsr_d(b, a, __aeabi_cdrcmple); + int32_t cpsr_value2 = call_apsr_d(b, a, __aeabi_cdcmple); + int32_t r_cpsr_value2 = call_apsr_d(a, b, __aeabi_cdrcmple); + + if (cpsr_value != r_cpsr_value) { + printf("error: __aeabi_cdcmple(%f, %f) != __aeabi_cdrcmple(%f, %f)\n", a, b, b, a); + return 1; + } + + int expected_z, expected_c; + if (expected == -1) { + expected_z = 0; + expected_c = 0; + } else if (expected == 0) { + expected_z = 1; + expected_c = 1; + } else { + // a or b is NaN, or a > b + expected_z = 0; + expected_c = 1; + } +#if PICO_DOUBLE_COMPILER + // gcc has this backwards it seems - not a good thing, but I guess it doesn't ever call them + expected_c ^= 1; +#endif + + union cpsr cpsr = {.value = cpsr_value}; + if (expected_z != cpsr.flags.z || expected_c != cpsr.flags.c) { + printf("error in __aeabi_cdcmple(%f, %f) => (Z = %d, C = %d), expected (Z = %d, C = %d)\n", + a, b, cpsr.flags.z, cpsr.flags.c, expected_z, expected_c); + return 1; + } + + cpsr.value = r_cpsr_value; + if (expected_z != cpsr.flags.z || expected_c != cpsr.flags.c) { + printf("error in __aeabi_cfrcmple(%f, %f) => (Z = %d, C = %d), expected (Z = %d, C = %d)\n", + a, b, cpsr.flags.z, cpsr.flags.c, expected_z, expected_c); + return 1; + } + return 0; +} + +#endif + +int test_cdcmple() { +#if __arm__ + if (test__aeabi_cdcmple(1.0, 1.0, 0)) + return 1; + if (test__aeabi_cdcmple(1234.567, 765.4321, 1)) + return 1; + if (test__aeabi_cdcmple(765.4321, 1234.567, -1)) + return 1; + if (test__aeabi_cdcmple(-123.0, -678.0, 1)) + return 1; + if (test__aeabi_cdcmple(-678.0, -123.0, -1)) + return 1; + if (test__aeabi_cdcmple(-123.0, 678.0, -1)) + return 1; + if (test__aeabi_cdcmple(678.0, -123.0, 1)) + return 1; + if (test__aeabi_cdcmple(0.0, -0.0, 0)) + return 1; + if (test__aeabi_cdcmple(1.0, NAN, 1)) + return 1; + if (test__aeabi_cdcmple(NAN, 1.0, 1)) + return 1; + if (test__aeabi_cdcmple(NAN, NAN, 1)) + return 1; +#else + printf("skipped\n"); +#endif + return 0; +} + +int test_cmple_gt() { + if (test_dcmple_gt(1.0, 1.0, 1)) + return 1; + if (test_dcmple_gt(1234.567, 765.4321, 0)) + return 1; + if (test_dcmple_gt(765.4321, 1234.567, 1)) + return 1; + if (test_dcmple_gt(-123.0, -678.0, 0)) + return 1; + if (test_dcmple_gt(-678.0, -123.0, 1)) + return 1; + if (test_dcmple_gt(-123.0, 678.0, 1)) + return 1; + if (test_dcmple_gt(678.0, -123.0, 0)) + return 1; + if (test_dcmple_gt(0.0, -0.0, 1)) + return 1; + if (test_dcmple_gt(-0.0, 0.0, 1)) + return 1; + if (test_dcmple_gt(1.0, NAN, 0)) + return 1; + if (test_dcmple_gt(NAN, 1.0, 0)) + return 1; + if (test_dcmple_gt(NAN, NAN, 0)) + return 1; + return 0; +} + +int test_cmplt_ge() { + if (test_dcmplt_ge(1.0, 1.0, 0)) + return 1; + if (test_dcmplt_ge(1234.567, 765.4321, 0)) + return 1; + if (test_dcmplt_ge(765.4321, 1234.567, 1)) + return 1; + if (test_dcmplt_ge(-123.0, -678.0, 0)) + return 1; + if (test_dcmplt_ge(-678.0, -123.0, 1)) + return 1; + if (test_dcmplt_ge(-123.0, 678.0, 1)) + return 1; + if (test_dcmplt_ge(678.0, -123.0, 0)) + return 1; + if (test_dcmplt_ge(0.0, -0.0, 0)) + return 1; + if (test_dcmplt_ge(-0.0, 0.0, 0)) + return 1; + if (test_dcmplt_ge(1.0, NAN, 0)) + return 1; + if (test_dcmplt_ge(NAN, 1.0, 0)) + return 1; + if (test_dcmplt_ge(NAN, NAN, 0)) + return 1; + return 0; +} + +int check_dcmpun(double a, double b, bool expected, bool expect_equal) { + if (__aeabi_dcmpun(a, b) != expected) { + printf("Failed dcmpun(%f, %f)\n", a, b); + return 1; + } + if ((a == b) != expect_equal) { + printf("Failed equality check %f %f\n", a, b); + __breakpoint(); + if (b == a) { + printf("SAS\n"); + } + return 1; + } + return 0; +} + +int test_dcmpun() { + if (check_dcmpun(0, 0, false, true) || + check_dcmpun(-INFINITY, INFINITY, false, false) || + check_dcmpun(NAN, 0, true, false) || + check_dcmpun(0, NAN, true, false) || + check_dcmpun(NAN, NAN, true, false) || + check_dcmpun(-NAN, NAN, true, false)) { + return 1; + } + return 0; +} + +#define assert_nan(a) assert(isnan(a)) +#define check_nan(a) ({ assert_nan(a); a; }) + +double __aeabi_i2d(int32_t); +double __aeabi_ui2d(int32_t); +double __aeabi_l2d(int64_t); +double __aeabi_ul2d(int64_t); +int32_t __aeabi_d2iz(double); +int64_t __aeabi_d2lz(double); +double __aeabi_dmul(double, double); +double __aeabi_ddiv(double, double); +#if LIB_PICO_DOUBLE_PICO +double __real___aeabi_i2d(int); +double __real___aeabi_ui2d(int); +double __real___aeabi_l2d(int64_t); +double __real___aeabi_ul2d(int64_t); +double __real___aeabi_dmul(double, double); +double __real___aeabi_ddiv(double, double); +int32_t __real___aeabi_d2iz(double); +int64_t __real___aeabi_d2lz(double); +double __real_sqrt(double); +double __real_cos(double); +double __real_sin(double); +double __real_tan(double); +double __real_exp(double); +double __real_log(double); +double __real_atan2(double, double); +double __real_pow(double, double); +double __real_trunc(double); +double __real_ldexp(double, int); +double __real_fmod(double, double); + +#define EPSILON 1e-9 +#define assert_close(a, b) assert(((b - a) < EPSILON || (a - b) < EPSILON) || (isinf(a) && isinf(b) && (a < 0) == (b < 0))) +#define check1(func,p0) ({ typeof(p0) r = func(p0), r2 = __CONCAT(__real_, func)(p0); assert(r == r2); r; }) +#define check2(func,p0,p1) ({ typeof(p0) r = func(p0,p1), r2 = __CONCAT(__real_, func)(p0,p1); assert(r == r2); r; }) +#define check_close1(func,p0) ({ typeof(p0) r = func(p0), r2 = __CONCAT(__real_, func)(p0); if (isnan(p0)) assert_nan(r); else assert_close(r, r2); r; }) +#define check_close2(func,p0,p1) ({ typeof(p0) r = func(p0,p1), r2 = __CONCAT(__real_, func)(p0,p1); if (isnan(p0) || isnan(p1)) assert_nan(r); else assert_close(r, r2); r; }) +#else +#define check1(func,p0) func(p0) +#define check2(func,p0,p1) func(p0,p1) +#define check_close1(func,p0) func(p0) +#define check_close2(func,p0,p1) func(p0,p1) +#endif + +double aa = 0.5; +double bb = 1; + +int main() { + setup_default_uart(); + + bool fail = false; + + printf("%d\n", aa < bb); + for(double a = -1; a <= 1; a++) { + for(double b = -1; b <= 1; b++) { + printf("%f < %f ? %d\n", a, b, a < b); + } + } + for(double a = -1; a <=1; a++) { + for(double b = -1; b <= 1; b++) { + printf("%f > %f ? %d\n", a, b, a > b); + } + } + +#if 1 + for (double x = 0; x < 3; x++) { + printf("\n ----- %g\n", x); + printf("SQRT %10.18g\n", check_close1(sqrt, x)); + printf("COS %10.18g\n", check_close1(cos, x)); + printf("SIN %10.18g\n", check_close1(sin, x)); + printf("TAN %10.18g\n", check_close1(tan, x)); + printf("ATAN2 %10.18g\n", check_close2(atan2, x, 10.0)); + printf("ATAN2 %10.18g\n", check_close2(atan2, 10.0, x)); + printf("EXP %10.18g\n", check_close1(exp, x)); + printf("LN %10.18g\n", check_close1(log, x)); + printf("POW %10.18f\n", check_close2(pow, x, x)); + printf("TRUNC %10.18f\n", check_close1(trunc, x)); + printf("LDEXP %10.18f\n", check_close2(ldexp, x, x)); + printf("FMOD %10.18f\n", check_close2(fmod, x, 3.0f)); + double s, c; + sincos(x, &s, &c); + printf("SINCOS %10.18f %10.18f\n", s, c); + if (s != sin(x) || c != cos(x)) { + printf("SINCOS mismatch\n"); + fail = true; + } + } + +#if PICO_DOUBLE_PROPAGATE_NANS + { + float x = NAN; + printf("SQRT %10.18g\n", check_close1(sqrt, x)); + printf("COS %10.18g\n", check_close1(cos, x)); + printf("SIN %10.18g\n", check_close1(sin, x)); + printf("TAN %10.18g\n", check_close1(tan, x)); + printf("ATAN2 %10.18g\n", check_close2(atan2, x, 10.0)); + printf("ATAN2 %10.18g\n", check_close2(atan2, 10.0, x)); + printf("EXP %10.18g\n", check_close1(exp, x)); + printf("LN %10.18g\n", check_close1(log, x)); + printf("POW %10.18f\n", check_nan(pow(x, x))); + printf("TRUNC %10.18f\n", check_nan(trunc(x))); + printf("LDEXP %10.18f\n", check_nan(ldexp(x, x))); + printf("FMOD %10.18f\n", check_nan(fmod(x, 3.0f))); + double s, c; + sincos(x, &s, &c); + printf("SINCOS %10.18f %10.18f\n", check_nan(s), check_nan(c)); + + for(int j=0;j<2;j++) { + for (int i = 1; i < 4; i++) { + char buf[4]; + sprintf(buf, "%d", i); + float f0 = -nanf(buf); + double d0 = -nan(buf); + // hmm nanf/nan seem to ignore payload + *(uint64_t *) &d0 |= i; + *(uint32_t *) &f0 |= i; + if (j) { + // try without top bit set + *(uint64_t *) &d0 &= ~0x0008000000000000ull; + *(uint32_t *) &f0 &= ~0x00400000u; + } + float f = (float) d0; + double d = (double) f0; + printf("f2d %f %08"PRIx32" -> %g %016"PRIx64"\n", f0, *(uint32_t *) &f0, d, *(uint64_t *) &d); + printf("d2f %f %016"PRIx64" -> %f %08"PRIx32"\n", d0, *(uint64_t *) &d0, f, *(uint32_t *) &f); + } + } + } +#endif + + { + int32_t y; +// for (int32_t x = 0; x>-512; x--) { +// printf("i %d->%f\n", (int)x, (float) x); +// } + for (int32_t x = -1; x; x <<= 1) { + printf("i %d->%f\n", x, (double) x); + check1(__aeabi_i2d, x); + } + for (int32_t x = 1; x; x <<= 1) { + printf("i %d->%f\n", x, (double) x); + check1(__aeabi_i2d, x); + y = x << 1; + } + for (int64_t x = 1; x; x <<= 1) { + printf("i %lld->%f\n", x, (double) x); + check1(__aeabi_l2d, x); + y = x << 1; + } + for (int64_t x = -1; x; x <<= 1) { + printf("i %lld->%f\n", x, (double) x); + check1(__aeabi_l2d, x); + y = x << 1; + } + printf("d %d->%f\n", y, (float) y); + } + + { + uint32_t y; + for(uint32_t x = 1; x; x <<= 1) { + printf("u %u->%f\n", x, (double)x); + check1(__aeabi_ui2d, x); + y = x << 1; + } + printf("u %u->%f\n", y, (double)y); + } + for(int64_t x = 1; x !=0; x <<= 1u) { + printf("%lld->%f\n", x, (double)x); + check1(__aeabi_l2d, x); + } + for(double x = -4294967296.f * 4294967296.f * 2.f; x<=-0.5f; x/=2.f) { + printf("d2i64 %f->%lld\n", x, (int64_t)x); + if (x < INT64_MIN) { + // seems like there is a bug in the gcc version! + assert(__aeabi_d2lz(x) == INT64_MIN); + } else { + check1(__aeabi_d2lz, x); + } + } + for(double x = 4294967296.f * 4294967296.f * 2.f; x>=0.5f; x/=2.f) { + printf("d2i64 %f->%lld\n", x, (int64_t)x); + if (x >= INT64_MAX) { + // seems like there is a bug in the gcc version! + assert(__aeabi_d2lz(x) == INT64_MAX); + } else { + check1(__aeabi_d2lz, x); + } + } + for(double x = -4294967296.f * 4294967296.f; x<=-0.5f; x/=2.f) { + printf("d2i32 %f->%d\n", x, (int32_t)x); + check1(__aeabi_d2iz, x); + } + for(double x = 4294967296.f * 4294967296.f; x>=0.5f; x/=2.f) { + printf("d2i32 %f->%d\n", x, (int32_t)x); + check1(__aeabi_d2iz, x); + } + + for (double x = 1; x < 11; x += 2) { + double f = x * x; + double g = 1.0 / x; + printf("%g %10.18g %10.18g, %10.18g, %10.18g %10.18g\n", x, f, x + 0.37777777777777777777777777777, + x - 0.377777777777777777777777777777, g, 123456789.0 / x); + check2(__aeabi_dmul, x, x); + check2(__aeabi_ddiv, 1.0, x); + } + + if (fail || + test_cdcmpeq() || + test_cdcmple() || + test_dcmpun() || + test_cmple_gt() || + test_cmplt_ge()) { + printf("FAILED\n"); + return 1; + } else { + printf("PASSED\n"); + return 0; + } +#endif +} diff --git a/circuitpython/ports/raspberrypi/sdk/test/pico_float_test/pico_float_test.c b/circuitpython/ports/raspberrypi/sdk/test/pico_float_test/pico_float_test.c new file mode 100644 index 0000000..5da23d2 --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/pico_float_test/pico_float_test.c @@ -0,0 +1,623 @@ +//===-- aeabi_cfcmpeq.c - Test __aeabi_cfcmpeq ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file tests __aeabi_cfcmpeq for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <pico/float.h> +#include "pico/stdlib.h" +#include "inttypes.h" + +extern int __aeabi_fcmpun(float a, float b); + +#if __arm__ + +#include "call_apsr.h" + +extern __attribute__((pcs("aapcs"))) void __aeabi_cfcmpeq(float a, float b); + +int test__aeabi_cfcmpeq(float a, float b, int expected) { + uint32_t cpsr_value = call_apsr_f(a, b, __aeabi_cfcmpeq); + union cpsr cpsr = {.value = cpsr_value}; + if (expected != cpsr.flags.z) { + printf("error in __aeabi_cfcmpeq(%f, %f) => Z = %08x, expected %08x\n", + a, b, cpsr.flags.z, expected); + return 1; + } + return 0; +} + +#endif + +int test_cfcmpeq() { +#if __arm__ + if (test__aeabi_cfcmpeq(1.0, 1.0, 1)) + return 1; + if (test__aeabi_cfcmpeq(1234.567, 765.4321, 0)) + return 1; + if (test__aeabi_cfcmpeq(-123.0, -678.0, 0)) + return 1; + if (test__aeabi_cfcmpeq(0.0, -0.0, 1)) + return 1; + if (test__aeabi_cfcmpeq(0.0, 0.0, 1)) + return 1; + if (test__aeabi_cfcmpeq(-0.0, -0.0, 1)) + return 1; + if (test__aeabi_cfcmpeq(-0.0, 0.0, 1)) + return 1; + if (test__aeabi_cfcmpeq(0.0, -1.0, 0)) + return 1; + if (test__aeabi_cfcmpeq(-0.0, -1.0, 0)) + return 1; + if (test__aeabi_cfcmpeq(-1.0, 0.0, 0)) + return 1; + if (test__aeabi_cfcmpeq(-1.0, -0.0, 0)) + return 1; + if (test__aeabi_cfcmpeq(1.0, NAN, 0)) + return 1; + if (test__aeabi_cfcmpeq(NAN, 1.0, 0)) + return 1; + if (test__aeabi_cfcmpeq(NAN, NAN, 0)) + return 1; + if (test__aeabi_cfcmpeq(INFINITY, 1.0, 0)) + return 1; + if (test__aeabi_cfcmpeq(0.0, INFINITY, 0)) + return 1; + if (test__aeabi_cfcmpeq(-INFINITY, 0.0, 0)) + return 1; + if (test__aeabi_cfcmpeq(0.0, -INFINITY, 0)) + return 1; + if (test__aeabi_cfcmpeq(INFINITY, INFINITY, 1)) + return 1; + if (test__aeabi_cfcmpeq(-INFINITY, -INFINITY, 1)) + return 1; +#else + printf("skipped\n"); +#endif + return 0; +} + +#if __arm__ + +extern __attribute__((pcs("aapcs"))) void __aeabi_cfcmple(float a, float b); + +extern __attribute__((pcs("aapcs"))) void __aeabi_cfrcmple(float a, float b); + +int test_fcmple_gt(float a, float b, int expected) { + if ((a <= b) != expected) { + printf("error in fcmple(%f, %f) => %d, expected %d\n", + a, b, a <= b, expected); + return 1; + } + if ((a > b) == expected && !isnanf(a) && !isnanf(b)) { + printf("error in fcmpgt(%f, %f) => %d, expected %d\n", + a, b, a > b, !expected); + return 1; + } + return 0; +} + +int test_fcmplt_ge(float a, float b, int expected) { + if ((a < b) != expected) { + printf("error in fcmplt(%f, %f) => %d, expected %d\n", + a, b, a < b, expected); + return 1; + } + if ((a >= b) == expected && !isnanf(a) && !isnanf(b)) { + printf("error in fcmpge(%f, %f) => %d, expected %d\n", + a, b, a >= b, !expected); + return 1; + } + return 0; +} + +int test__aeabi_cfcmple(float a, float b, int expected) { + int32_t cpsr_value = call_apsr_f(a, b, __aeabi_cfcmple); + int32_t r_cpsr_value = call_apsr_f(b, a, __aeabi_cfrcmple); + int32_t cpsr_value2 = call_apsr_f(b, a, __aeabi_cfcmple); + int32_t r_cpsr_value2 = call_apsr_f(a, b, __aeabi_cfrcmple); + + if (cpsr_value != r_cpsr_value) { + printf("error: __aeabi_cfcmple(%f, %f) != __aeabi_cfrcmple(%f, %f)\n", a, b, b, a); + return 1; + } + + int expected_z, expected_c; + if (expected == -1) { + expected_z = 0; + expected_c = 0; + } else if (expected == 0) { + expected_z = 1; + expected_c = 1; + } else { + // a or b is NaN, or a > b + expected_z = 0; + expected_c = 1; + } +#if PICO_FLOAT_COMPILER + // gcc has this backwards it seems - not a good thing, but I guess it doesn't ever call them + expected_c ^= 1; +#endif + + union cpsr cpsr = {.value = cpsr_value}; + if (expected_z != cpsr.flags.z || expected_c != cpsr.flags.c) { + printf("error in __aeabi_cfcmple(%f, %f) => (Z = %d, C = %d), expected (Z = %d, C = %d)\n", + a, b, cpsr.flags.z, cpsr.flags.c, expected_z, expected_c); + return 1; + } + + cpsr.value = r_cpsr_value; + if (expected_z != cpsr.flags.z || expected_c != cpsr.flags.c) { + printf("error in __aeabi_cfrcmple(%f, %f) => (Z = %d, C = %d), expected (Z = %d, C = %d)\n", + a, b, cpsr.flags.z, cpsr.flags.c, expected_z, expected_c); + return 1; + } + return 0; +} + +#endif + +int test_cfcmple() { +#if __arm__ + if (test__aeabi_cfcmple(1.0, 1.0, 0)) + return 1; + if (test__aeabi_cfcmple(1234.567, 765.4321, 1)) + return 1; + if (test__aeabi_cfcmple(765.4321, 1234.567, -1)) + return 1; + if (test__aeabi_cfcmple(-123.0, -678.0, 1)) + return 1; + if (test__aeabi_cfcmple(-678.0, -123.0, -1)) + return 1; + if (test__aeabi_cfcmple(-123.0, 678.0, -1)) + return 1; + if (test__aeabi_cfcmple(678.0, -123.0, 1)) + return 1; + if (test__aeabi_cfcmple(0.0, -0.0, 0)) + return 1; + if (test__aeabi_cfcmple(1.0, NAN, 1)) + return 1; + if (test__aeabi_cfcmple(NAN, 1.0, 1)) + return 1; + if (test__aeabi_cfcmple(NAN, NAN, 1)) + return 1; +#else + printf("skipped\n"); +#endif + return 0; +} + +int test_cmple_gt() { + if (test_fcmple_gt(1.0, 1.0, 1)) + return 1; + if (test_fcmple_gt(1234.567, 765.4321, 0)) + return 1; + if (test_fcmple_gt(765.4321, 1234.567, 1)) + return 1; + if (test_fcmple_gt(-123.0, -678.0, 0)) + return 1; + if (test_fcmple_gt(-678.0, -123.0, 1)) + return 1; + if (test_fcmple_gt(-123.0, 678.0, 1)) + return 1; + if (test_fcmple_gt(678.0, -123.0, 0)) + return 1; + if (test_fcmple_gt(0.0, -0.0, 1)) + return 1; + if (test_fcmple_gt(-0.0, 0.0, 1)) + return 1; + if (test_fcmple_gt(1.0, NAN, 0)) + return 1; + if (test_fcmple_gt(NAN, 1.0, 0)) + return 1; + if (test_fcmple_gt(NAN, NAN, 0)) + return 1; + return 0; +} + +int test_cmplt_ge() { + if (test_fcmplt_ge(1.0, 1.0, 0)) + return 1; + if (test_fcmplt_ge(1234.567, 765.4321, 0)) + return 1; + if (test_fcmplt_ge(765.4321, 1234.567, 1)) + return 1; + if (test_fcmplt_ge(-123.0, -678.0, 0)) + return 1; + if (test_fcmplt_ge(-678.0, -123.0, 1)) + return 1; + if (test_fcmplt_ge(-123.0, 678.0, 1)) + return 1; + if (test_fcmplt_ge(678.0, -123.0, 0)) + return 1; + if (test_fcmplt_ge(0.0, -0.0, 0)) + return 1; + if (test_fcmplt_ge(-0.0, 0.0, 0)) + return 1; + if (test_fcmplt_ge(1.0, NAN, 0)) + return 1; + if (test_fcmplt_ge(NAN, 1.0, 0)) + return 1; + if (test_fcmplt_ge(NAN, NAN, 0)) + return 1; + return 0; +} + +int check_fcmpun(float a, float b, bool expected, bool expect_equal) { + if (__aeabi_fcmpun(a, b) != expected) { + printf("Failed fcmpun(%f, %f)\n", a, b); + return 1; + } + if ((a == b) != expect_equal) { + printf("Failed equality check %f %f\n", a, b); + __breakpoint(); + if (b == a) { + printf("SAS\n"); + } + return 1; + } + return 0; +} + +int test_fcmpun() { + if (check_fcmpun(0, 0, false, true) || + check_fcmpun(-INFINITY, INFINITY, false, false) || + check_fcmpun(NAN, 0, true, false) || + check_fcmpun(0, NAN, true, false) || + check_fcmpun(NAN, NAN, true, false) || + check_fcmpun(-NAN, NAN, true, false)) { + return 1; + } + return 0; +} + +#define assert_nan(a) assert(isnan(a)) +#define check_nan(a) ({ assert_nan(a); a; }) + +float __aeabi_i2f(int32_t); +float __aeabi_ui2f(int32_t); +float __aeabi_l2f(int64_t); +float __aeabi_ul2f(int64_t); +int32_t __aeabi_f2iz(float); +int64_t __aeabi_f2lz(float); +float __aeabi_fmul(float, float); +float __aeabi_fdiv(float, float); +#if LIB_PICO_FLOAT_PICO +float __real___aeabi_i2f(int); +float __real___aeabi_ui2f(int); +float __real___aeabi_l2f(int64_t); +float __real___aeabi_ul2f(int64_t); +float __real___aeabi_fmul(float, float); +float __real___aeabi_fdiv(float, float); +int32_t __real___aeabi_f2iz(float); +int64_t __real___aeabi_f2lz(float); +float __real_sqrtf(float); +float __real_cosf(float); +float __real_sinf(float); +float __real_tanf(float); +float __real_expf(float); +float __real_logf(float); +float __real_atan2f(float, float); +float __real_powf(float, float); +float __real_truncf(float); +float __real_ldexpf(float, int); +float __real_fmodf(float, float); +#define EPSILON 1e-9 +#define assert_close(a, b) assert(((b - a) < EPSILON || (a - b) < EPSILON) || (isinf(a) && isinf(b) && (a < 0) == (b < 0))) +#define check1(func,p0) ({ typeof(p0) r = func(p0), r2 = __CONCAT(__real_, func)(p0); assert(r == r2); r; }) +#define check2(func,p0,p1) ({ typeof(p0) r = func(p0,p1), r2 = __CONCAT(__real_, func)(p0,p1); assert(r == r2); r; }) +#define check_close1(func,p0) ({ typeof(p0) r = func(p0), r2 = __CONCAT(__real_, func)(p0); if (isnan(p0)) assert_nan(r); else assert_close(r, r2); r; }) +#define check_close2(func,p0,p1) ({ typeof(p0) r = func(p0,p1), r2 = __CONCAT(__real_, func)(p0,p1); if (isnan(p0) || isnan(p1)) assert_nan(r); else assert_close(r, r2); r; }) +#else +#define check1(func,p0) func(p0) +#define check2(func,p0,p1) func(p0,p1) +#define check_close1(func,p0) func(p0) +#define check_close2(func,p0,p1) func(p0,p1) +#endif + +double aa = 0.5; +double bb = 1; + +int main() { + setup_default_uart(); + + bool fail = false; + + printf("%d\n", aa < bb); + for(float a = -1; a <= 1; a++) { + for(float b = -1; b <= 1; b++) { + printf("%f < %f ? %d\n", a, b, a < b); + } + } + for(float a = -1; a <=1; a++) { + for(float b = -1; b <= 1; b++) { + printf("%f > %f ? %d\n", a, b, a > b); + } + } + printf("F\n"); + for(float f = -1.0; f<=1.f; f+=0.25f) { + printf("%d\n", (int)f); + } + printf("D\n"); + for(double d = -1.0; d<=1.0; d+=0.25) { + printf("%d\n", (int)d); + } + printf("LD\n"); + for(double d = -1.0; d<=1.0; d+=0.25) { + printf("%lld\n", (int64_t)d); + } + + for(float d = -0.125; d>=-65536.0*65536.0*65536.0*65536.0*2; d*=2) { + printf("%g %d, %lld, %u, %llu\n", d, (int32_t)d, (int64_t)d, (uint32_t)d, (uint64_t)d); + } + for(float d = 0.125; d<=65536.0*65536.0*65536.0*65536.0*2; d*=2) { + printf("%g %d, %lld, %u, %llu\n", d, (int32_t)d, (int64_t)d, (uint32_t)d, (uint64_t)d); + } + + for(double d = -0.125; d>=-65536.0*65536.0*65536.0*65536.0*2; d*=2) { + printf("%g %d, %lld, %u, %llu\n", d, (int32_t)d, (int64_t)d, (uint32_t)d, (uint64_t)d); + } + for(double d = 0.125; d<=65536.0*65536.0*65536.0*65536.0*2; d*=2) { + printf("%g %d, %lld, %u, %llu\n", d, (int32_t)d, (int64_t)d, (uint32_t)d, (uint64_t)d); + } + + for(int i = (int32_t)0x80000000; i <0; i /= 2) { + printf("%d %f\n", i, (double)i); + } + for(int i = (1<<30); i >0; i /= 2) { + printf("%d %f\n", i, (double)i); + } + + printf("%f\n", 0.5); + printf("SQRT %10.18g\n", 0.5); + printf("SQRT %10.18g\n", 0.333333333333333333333333); + +#if 1 + for (float x = 0; x < 3; x++) { + printf("\n ----- %f\n", x); + printf("FSQRT %10.18f\n", check_close1(sqrtf, x)); + printf("FCOS %10.18f\n", check_close1(cosf, x)); + printf("FSIN %10.18f\n", check_close1(sinf, x)); + float s, c; + sincosf(x, &s, &c); + printf("FSINCOS %10.18f %10.18f\n", s, c); + printf("FTAN %10.18f\n", check_close1(tanf, x)); + printf("FATAN2 %10.18f\n", check_close2(atan2f, x, 10.f)); + printf("FATAN2 %10.18f\n", check_close2(atan2f, 10.f, x)); + printf("FEXP %10.18f\n", check_close1(expf, x)); + printf("FLN %10.18f\n", check_close1(logf, x)); + printf("POWF %10.18f\n", check_close2(powf, x, x)); + printf("TRUNCF %10.18f\n", check_close1(truncf, x)); + printf("LDEXPF %10.18f\n", check_close2(ldexpf, x, x)); + printf("FMODF %10.18f\n", check_close2(fmodf, x, 3.0f)); + sincosf(x, &s, &c); + printf("SINCOS %10.18f %10.18f\n", s, c); + if (s != sin(x) || c != cos(x)) { + printf("SINCOS mismatch\n"); + fail = true; + } + } + + for (double x = 0; x < 3; x++) { + printf("\n ----- %g\n", x); + printf("SQRT %10.18g\n", sqrt(x)); + printf("COS %10.18g\n", cos(x)); + printf("SIN %10.18g\n", sin(x)); + printf("TAN %10.18g\n", tan(x)); + printf("ATAN2 %10.18g\n", atan2(x, 10)); + printf("ATAN2 %10.18g\n", atan2(10, x)); + printf("EXP %10.18g\n", exp(x)); + printf("LN %10.18g\n", log(x)); + } + +#if PICO_FLOAT_PROPAGATE_NANS + { + float x = NAN; + printf("NANO %10.18f\n", x); + printf("FSQRT %10.18f\n", sqrtf(x)); + printf("FCOS %10.18f\n", cosf(x)); + printf("FSIN %10.18f\n", sinf(x)); + printf("FTAN %10.18f\n", tanf(x)); + printf("FATAN2 %10.18f\n", atan2f(x, 10)); + printf("FATAN2 %10.18f\n", atan2f(10, x)); + printf("FEXP %10.18f\n", expf(x)); + printf("FLN %10.18f\n", logf(x)); + printf("POWF %10.18f\n", powf(x, x)); + printf("TRUNCF %10.18f\n", truncf(x)); + printf("LDEXPF %10.18f\n", ldexpf(x, x)); + printf("FMODF %10.18f\n", fmodf(x, 3.0f)); + float s, c; +// sincosf(x, &s, &c); + printf("FSINCOS %10.18f %10.18f\n", s, c); + + for(int j=0;j<2;j++) { + for (int i = 1; i < 4; i++) { + char buf[4]; + sprintf(buf, "%d", i); + float f0 = -nanf(buf); + double d0 = -nan(buf); + // hmm nanf/nan seem to ignore payload + *(uint64_t *) &d0 |= i; + *(uint32_t *) &f0 |= i; + if (j) { + // try without top bit set + *(uint64_t *) &d0 &= ~0x0008000000000000ull; + *(uint32_t *) &f0 &= ~0x00400000u; + } + float f = (float) d0; + double d = (double) f0; + printf("f2d %f %08"PRIx32" -> %g %016"PRIx64"\n", f0, *(uint32_t *) &f0, d, *(uint64_t *) &d); + printf("d2f %f %016"PRIx64" -> %f %08"PRIx32"\n", d0, *(uint64_t *) &d0, f, *(uint32_t *) &f); + } + } + } +#endif + + { + int32_t y; +// for (int32_t x = 0; x>-512; x--) { +// printf("i %d->%f\n", (int)x, (float) x); +// } + for (int32_t x = -1; x; x <<= 1) { + printf("i %d->%f\n", x, (float) x); + check1(__aeabi_i2f, x); + } + for (int32_t x = 1; x; x <<= 1) { + printf("i %d->%f\n", x, (float) x); + check1(__aeabi_i2f, x); + y = x << 1; + } + for (int64_t x = 1; x; x <<= 1) { + printf("i %lld->%f\n", x, (float) x); + check1(__aeabi_l2f, x); + y = x << 1; + } + for (int64_t x = -1; x; x <<= 1) { + printf("i %lld->%f\n", x, (float) x); + check1(__aeabi_l2f, x); + y = x << 1; + } + printf("d %d->%f\n", y, (float) y); + } + + { + uint32_t y; + for(uint32_t x = 1; x; x <<= 1) { + printf("u %u->%f\n", x, (float)x); + check1(__aeabi_ui2f, x); + y = x << 1; + } + printf("u %u->%f\n", y, (float)y); + } + for(int64_t x = 1; x !=0; x <<= 1u) { + printf("%lld->%f\n", x, (float)x); + check1(__aeabi_l2f, x); + } + for(float x = -4294967296.f * 4294967296.f; x>=0.5f; x/=2.f) { + printf("f %f->%lld\n", x, (int64_t)x); + if (x < INT64_MIN) { + // seems like there is a bug in the gcc version! + assert(__aeabi_f2lz(x) == INT64_MIN); + } else { + check1(__aeabi_f2lz, x); + } + } + for(float x = 4294967296.f * 4294967296.f * 2.f; x>=0.5f; x/=2.f) { + printf("f2i64 %f->%lld\n", x, (int64_t)x); + if (x >= INT64_MAX) { + // seems like there is a bug in the gcc version! + assert(__aeabi_f2lz(x) == INT64_MAX); + } else { + check1(__aeabi_f2lz, x); + } + } + for(float x = -4294967296.f * 4294967296.f; x<=-0.5f; x/=2.f) { + printf("d2i32 %f->%d\n", x, (int32_t)x); + check1(__aeabi_f2iz, x); + } + for(float x = 4294967296.f * 4294967296.f; x>=0.5f; x/=2.f) { + printf("d2i32 %f->%d\n", x, (int32_t)x); + check1(__aeabi_f2iz, x); + } + + for (float x = 1; x < 11; x += 2) { + float f = x * x; + float g = 1.0f / x; + printf("%g %10.18g %10.18g, %10.18g, %10.18g %10.18g\n", x, f, x + 0.37777777777777777777777777777f, + x - 0.377777777777777777777777777777f, g, 123456789.0f / x); + check2(__aeabi_fmul, x, x); + check2(__aeabi_fdiv, 1.0f, x); + } + + if (fail || + test_cfcmpeq() || + test_cfcmple() || + test_fcmpun() || + test_cmple_gt() || + test_cmplt_ge()) { + printf("FAILED\n"); + return 1; + } else { + printf("PASSED\n"); + return 0; + } +#endif +} + +#if 0 +// todo need to add tests like these + +bool __noinline check(float x, float y) { + return x > y; +} + +bool __noinline checkd(double x, double y) { + return x >= y; +} + +int main() { + stdio_init_all(); +#if 0 + printf("0 op nan %d\n", check(0, nanf("sd"))); + printf("nan op 0 %d\n", check(nanf("sd"), 0)); + printf("0 op -nan %d\n", check(0, -nanf("sd"))); + printf("-nan op 0 %d\n", check(-nanf("sd"), 0)); + printf("-nan op nan %d\n", check(-nanf("xx"), nanf("xx"))); + printf("-nan op -nan %d\n", check(-nanf("xx"), -nanf("xx"))); + printf("nan op -nan %d\n", check(nanf("xx"), -nanf("xx"))); + printf("nan op nan %d\n", check(nanf("xx"), nanf("xx"))); + printf("0 op inf %d\n", check(0, infinityf())); + printf("inf op 0 %d\n", check(infinityf(), 0)); + printf("0 op -inf %d\n", check(0, -infinityf())); + printf("-inf op 0 %d\n", check(-infinityf(), 0)); + printf("-inf op inf %d\n", check(-infinityf(), infinityf())); + printf("-inf op -inf %d\n", check(-infinityf(), -infinityf())); + printf("inf op -inf %d\n", check(infinityf(), -infinityf())); + printf("inf op inf %d\n", check(infinityf(), infinityf())); + printf("1 op 1 %d\n", check(1, 1)); + printf("-1 op 1 %d\n", check(-1, 1)); + printf("1 op -1 %d\n", check(1, -1)); + printf("-1 op -1 %d\n", check(-1, -1)); + printf("1 op 2 %d\n", check(1, 2)); + printf("2 op 1 %d\n", check(2, 1)); + printf("-1 op -2 %d\n", check(-1, -2)); + printf("-2 op -1 %d\n", check(-2, -1)); +#else + printf("0 op nan %d\n", checkd(0, nan("sd"))); + printf("nan op 0 %d\n", checkd(nan("sd"), 0)); + printf("0 op -nan %d\n", checkd(0, -nan("sd"))); + printf("-nan op 0 %d\n", checkd(-nan("sd"), 0)); + printf("-nan op nan %d\n", checkd(-nan("xx"), nan("xx"))); + printf("-nan op -nan %d\n", checkd(-nan("xx"), -nan("xx"))); + printf("nan op -nan %d\n", checkd(nan("xx"), -nan("xx"))); + printf("nan op nan %d\n", checkd(nan("xx"), nan("xx"))); + printf("0 op inf %d\n", checkd(0, infinity())); + printf("inf op 0 %d\n", checkd(infinity(), 0)); + printf("0 op -inf %d\n", checkd(0, -infinity())); + printf("-inf op 0 %d\n", checkd(-infinity(), 0)); + printf("-inf op inf %d\n", checkd(-infinity(), infinity())); + printf("-inf op -inf %d\n", checkd(-infinity(), -infinity())); + printf("inf op -inf %d\n", checkd(infinity(), -infinity())); + printf("inf op inf %d\n", checkd(infinity(), infinity())); + printf("1 op 1 %d\n", checkd(1, 1)); + printf("-1 op 1 %d\n", checkd(-1, 1)); + printf("1 op -1 %d\n", checkd(1, -1)); + printf("-1 op -1 %d\n", checkd(-1, -1)); + printf("1 op 2 %d\n", checkd(1, 2)); + printf("2 op 1 %d\n", checkd(2, 1)); + printf("-1 op -2 %d\n", checkd(-1, -2)); + printf("-2 op -1 %d\n", checkd(-2, -1)); +#endif +} +#endif
\ No newline at end of file diff --git a/circuitpython/ports/raspberrypi/sdk/test/pico_stdlib_test/CMakeLists.txt b/circuitpython/ports/raspberrypi/sdk/test/pico_stdlib_test/CMakeLists.txt new file mode 100644 index 0000000..8de6a45 --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/pico_stdlib_test/CMakeLists.txt @@ -0,0 +1,8 @@ +add_executable(pico_stdlib_test pico_stdlib_test.c) + +target_compile_definitions(pico_stdlib_test PRIVATE + #PICO_ENTER_USB_BOOT_ON_EXIT=1 +) + +target_link_libraries(pico_stdlib_test PRIVATE pico_stdlib) +pico_add_extra_outputs(pico_stdlib_test) diff --git a/circuitpython/ports/raspberrypi/sdk/test/pico_stdlib_test/pico_stdlib_test.c b/circuitpython/ports/raspberrypi/sdk/test/pico_stdlib_test/pico_stdlib_test.c new file mode 100644 index 0000000..0adef83 --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/pico_stdlib_test/pico_stdlib_test.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdio.h> +#include <inttypes.h> +#include "pico/stdlib.h" +#include "pico/bit_ops.h" + +int main() { + setup_default_uart(); + + puts("Hellox, worlxxcd!"); + printf("Hello world %d\n", 2); +#if PICO_NO_HARDWARE + puts("This is native"); +#endif +#if PICO_NO_FLASH + puts("This is no flash"); +#endif + + for (int i = 0; i < 64; i++) { + uint32_t x = 1 << i; + uint64_t xl = 1ull << i; +// printf("%d %u %u %u %u \n", i, (uint)(x%10u), (uint)(x%16u), (uint)(xl %10u), (uint)(xl%16u)); + printf("%08x %08x %016llx %016llx\n", (uint) x, (uint) __rev(x), (unsigned long long) xl, + (unsigned long long) __revll(xl)); + } + + for (int i = 0; i < 8; i++) { + sleep_ms(500); + printf( "%" PRIu64 "\n", to_us_since_boot(get_absolute_time())); + } + absolute_time_t until = delayed_by_us(get_absolute_time(), 500000); + printf("\n"); + for (int i = 0; i < 8; i++) { + sleep_until(until); + printf("%" PRIu64 "\n", to_us_since_boot(get_absolute_time())); + until = delayed_by_us(until, 500000); + } + puts("DONE"); +} + +void test1() { + uint32_t x = 0; + for (int i = 0; i < 1000; i++) { + x += __builtin_popcount(i); + x += __builtin_popcountl(i); + x += __builtin_popcountll(i * 1234567ll); + x += __builtin_clz(i); + x += __builtin_clzl(i); + x += __builtin_clzll(i * 1234567ll); + x += __builtin_ctz(i); + x += __builtin_ctzl(i); + x += __builtin_ctzll(i * 1234567ll); + } + if (x > 12345677) { + puts("ok"); + } +} + +#if 0 +struct event { + +}; + +// something might be asyncrhonous.. it communicates the result via the event +void do_something(struct event *e, int a, unsigned int b, char *c) { + if (a == b) puts(c); +} + +int32_t event_result_timeout_ms(struct event *e, int32_t timeout_ms); +int32_t event_result_timeout_us(struct event *e, int32_t timeout_us); +bool is_event_done(struct event *e); +// asserts if not done +int32_t event_result(struct event *e); +void event_set_callback(struct event *e, void (*callback)(struct event *e)); +void init_multi_event(struct event *target, struct event **events, uint event_count); + +#define timeout_ms_result(f, timeout) ({ \ + struct event __event; \ + struct event *event = &__event; \ + (f); \ + event_result_timeout_ms(event, timeout); \ + }) + +#define blocking_result(f) timeout_ms_result(f, -1) +#define on_complete(f, cb) ({ \ + static struct event __event; \ + struct event *event = &__event; \ + (f); \ + event_set_callback(event, my_callback); \ + }) + +void test2() { + // just playing with blocking syntax + struct event e; + do_something(&e, 1, 1, "Hello"); + uint32_t result = event_result_timeout_ms(&e, -1); +} + +void test3() { + uint32_t result = blocking_result(do_something(event, 1, 1, "Hello")); +} + +void test4() { + struct event e; + do_something(&e, 1, 1, "Hello"); + // this would poll (down to hardware if there is no asynchronous mechanism) + while (!is_event_done(&e)) { + puts("waiting"); + } + int32_t result = event_result(&e); +} + +void my_callback(struct event *event) { + puts("Its done"); + int32_t result = event_result(event); +} + +void test5() { + static struct event e; + do_something(&e, 1, 1, "Hello"); + event_set_callback(&e, my_callback); +} + +void test6() { + on_complete(do_something(event, 1, 1, "Hello"), my_callback); +} + +static struct event e1; +static struct event e2; +static struct event *events[2] = {&e1, &e2}; +static struct event multi; + +void test7() { + init_multi_event(&multi,events, count_of(events)); + do_something(&e1, 1, 1, "Hello"); + do_something(&e2, 1, 3, "Hello"); + // something like this +} + +struct dimpl { + uint8_t type; +}; + +struct doodad { + struct dimpl *type; + uint32_t param; +}; + +struct dimpl indefinite_waiter = { + .type = 1 +}; + +extern struct dimpl INDEFINITE_WAIT; + +struct dimpl ms_waiter = { + .type = 1 +}; + +struct doodad blocking_with_timeout_ms(uint32_t ms) { + struct doodad rc = { + .type = &ms_waiter, + .param = ms + }; + return rc; +} + +struct result { + +}; + +struct result my_api_call(int arg, float x, struct doodad behavior) { + +} +#endif
\ No newline at end of file diff --git a/circuitpython/ports/raspberrypi/sdk/test/pico_test/CMakeLists.txt b/circuitpython/ports/raspberrypi/sdk/test/pico_test/CMakeLists.txt new file mode 100644 index 0000000..ed0bfcb --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/pico_test/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(pico_test INTERFACE) + +target_include_directories(pico_test INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) +target_link_libraries(pico_test INTERFACE pico_stdlib) diff --git a/circuitpython/ports/raspberrypi/sdk/test/pico_test/include/pico/test.h b/circuitpython/ports/raspberrypi/sdk/test/pico_test/include/pico/test.h new file mode 100644 index 0000000..07ad53d --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/pico_test/include/pico/test.h @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _PICO_TEST_H +#define _PICO_TEST_H + +#include "pico.h" + +/* Various macros to help with test harnesses + +Note: really need to change the returns to exit() +but not sure that is implemented yet. +*/ + +#define PICOTEST_MODULE_NAME(n,d) const char *picotest_module=n; const char *picotest_description=d; int picotest_error_code; + +#define PICOTEST_START() printf("Starting Picotest for %s\n", picotest_description); + +#define PICOTEST_START_SECTION(NAME) if (1) {const char *picotest_section_name=NAME; picotest_error_code = 0; + +#define PICOTEST_END_SECTION() if (picotest_error_code != 0) { \ + printf("Module %s: Section %s : Failed test\n", picotest_module, picotest_section_name);\ + return -1; \ + } else \ + printf("Module %s: Section %s : Passed\n", picotest_module, picotest_section_name); \ + } + +#define PICOTEST_CHECK(COND, MESSAGE) if (!(COND)) { \ + printf("Module %s: %s\n", picotest_module, MESSAGE); \ + picotest_error_code = -1; \ + } +#define PICOTEST_CHECK_CHANNEL(CHANNEL, COND, MESSAGE) if (!(COND)) { \ + printf("Module %s, channel %d: %s\n", picotest_module, CHANNEL, MESSAGE); \ + picotest_error_code = -1; \ + } + +#define PICOTEST_CHECK_AND_ABORT(COND, MESSAGE) if (!(COND)) { \ + printf("Module %s: %s\n", picotest_module, MESSAGE); \ + return -1; \ + } + +#define PICOTEST_CHECK_CHANNEL_AND_ABORT(CHANNEL, COND, MESSAGE) if (!(COND)) { \ + printf("Module %s, channel %d: %s\n", picotest_module, CHANNEL, MESSAGE); \ + return -1; \ + } + +#define PICOTEST_ABORT_IF_FAILED() if (picotest_error_code != 0) { \ + printf("Module %s: Aborting\n", picotest_module); \ + return -1; \ + } + +#define PICOTEST_END_TEST() if (picotest_error_code != 0) \ + {printf("%s: Failed\n", picotest_description); return -1;} \ + else \ + {printf("%s: Success\n", picotest_description); return 0;} + + +#endif
\ No newline at end of file diff --git a/circuitpython/ports/raspberrypi/sdk/test/pico_time_test/CMakeLists.txt b/circuitpython/ports/raspberrypi/sdk/test/pico_time_test/CMakeLists.txt new file mode 100644 index 0000000..d36a3da --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/pico_time_test/CMakeLists.txt @@ -0,0 +1,8 @@ +if (NOT PICO_TIME_NO_ALARM_SUPPORT) + add_executable(pico_time_test pico_time_test.c) + target_compile_definitions(pico_time_test PRIVATE + PICO_TIME_DEFAULT_ALARM_POOL_MAX_TIMERS=250 + ) + target_link_libraries(pico_time_test PRIVATE pico_test) + pico_add_extra_outputs(pico_time_test) +endif()
\ No newline at end of file diff --git a/circuitpython/ports/raspberrypi/sdk/test/pico_time_test/pico_time_test.c b/circuitpython/ports/raspberrypi/sdk/test/pico_time_test/pico_time_test.c new file mode 100644 index 0000000..2209fd5 --- /dev/null +++ b/circuitpython/ports/raspberrypi/sdk/test/pico_time_test/pico_time_test.c @@ -0,0 +1,250 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <hardware/sync.h> +#include "pico/stdlib.h" +#include "pico/test.h" +#include <inttypes.h> +PICOTEST_MODULE_NAME("pico_time_test", "pico_time test harness"); + +#define NUM_TIMEOUTS 500 +#define MAX_TIMERS_PER_POOL 250 +static_assert(PICO_TIME_DEFAULT_ALARM_POOL_MAX_TIMERS >= MAX_TIMERS_PER_POOL, ""); +#define TEST_LENGTH_US 2000000 + +#define NUM_REPEATING_TIMERS 50 +static struct repeating_timer repeating_timers[NUM_REPEATING_TIMERS]; +static uint repeating_timer_callback_count[NUM_REPEATING_TIMERS]; + +static struct timeout { + alarm_id_t alarm_id; + absolute_time_t target; + absolute_time_t fired_at; + uint pool; + uint fired_count; + bool cancelled; + bool not_cancelled; // tried to cancel but it was done +} timeouts[NUM_TIMEOUTS]; + +int64_t timer_callback1(alarm_id_t id, void *user_data) { + struct timeout *timeout = (struct timeout *)user_data; + assert(timeout >= timeouts && timeout < (timeouts + NUM_TIMEOUTS)); + timeout->fired_at = get_absolute_time(); + timeout->fired_count++; +// printf("%d %d %ld\n", timeout->pool, id, to_us_since_boot(timeout->target)); + return 0; +} + +int sort_by_target(const void *a, const void *b) { + const struct timeout *ta = (const struct timeout *)a; + const struct timeout *tb = (const struct timeout *)b; + int64_t delta = absolute_time_diff_us(tb->target, ta->target); + if (delta < 0) return -1; + else if (delta > 0) return 1; + return 0; +} + +static bool repeating_timer_callback(struct repeating_timer *t) { + // check we get the right timer structure + uint i = (uintptr_t)t->user_data; + hard_assert(i == (t - repeating_timers)); + repeating_timer_callback_count[i]++; + return true; +} + +#ifndef PICO_HARDWARE_TIMER_RESOLUTION_US +#define RESOLUTION_ALLOWANCE 0 +#else +#define RESOLUTION_ALLOWANCE PICO_HARDWARE_TIMER_RESOLUTION_US +#endif + +int issue_195_test(void); + +int main() { + setup_default_uart(); + alarm_pool_init_default(); + + PICOTEST_START(); + struct alarm_pool *pools[NUM_TIMERS]; + for(uint i=0; i<NUM_TIMERS; i++) { + if (i == alarm_pool_hardware_alarm_num(alarm_pool_get_default())) { + pools[i] = alarm_pool_get_default(); + } else { + pools[i] = alarm_pool_create(i, MAX_TIMERS_PER_POOL); + } + PICOTEST_CHECK_AND_ABORT(pools[i], "failed to create timer pool"); + } + + // Check default config has valid data in it + PICOTEST_START_SECTION("Alarm ordering test"); + + absolute_time_t time_base = get_absolute_time(); + uint32_t init_ms = 1000; + for(uint i = 0; i < NUM_TIMEOUTS; i++) { +// printf("%d %p\n", i); + absolute_time_t target; + uint pool; + if (1 == (i&127u)) { + // want occasional duplicate time + target = timeouts[i-1].target; + pool = timeouts[i-1].pool; + } else { + target = delayed_by_us(time_base, init_ms + (rand() % TEST_LENGTH_US)); + pool = rand() % 4; + } + timeouts[i].target = target; + timeouts[i].pool = pool; + alarm_id_t id = alarm_pool_add_alarm_at(pools[pool], target, timer_callback1, timeouts + i, true); + PICOTEST_CHECK_AND_ABORT(id >=0, "Failed to add timer"); + } + PICOTEST_CHECK(absolute_time_diff_us(time_base, get_absolute_time()) < init_ms * 1000, "This is a flaky test :-("); + + uint64_t last_fired_at[NUM_TIMERS]; + uint64_t last_target[NUM_TIMERS]; + memset(&last_fired_at, 0, sizeof(last_fired_at)); + printf("Sleeping...\n"); + sleep_us(TEST_LENGTH_US + 250000); + printf(" ...done\n"); + + qsort(timeouts, NUM_TIMEOUTS, sizeof(struct timeout), sort_by_target); + + uint64_t max_jitter = 0; + for(uint i = 0; i < NUM_TIMEOUTS; i++) { + printf("%d %d %"PRIi64" : %"PRIi64"\n", timeouts[i].pool, timeouts[i].fired_count, to_us_since_boot(timeouts[i].fired_at), to_us_since_boot(timeouts[i].target)); + PICOTEST_CHECK(timeouts[i].fired_count, "Timer should have fired"); + PICOTEST_CHECK(timeouts[i].fired_count < 2, "Timer should only have fired once"); + uint64_t fired_at = to_us_since_boot(timeouts[i].fired_at); + PICOTEST_CHECK(timeouts[i].fired_count != 1 || fired_at >= MAX(RESOLUTION_ALLOWANCE, + to_us_since_boot(timeouts[i].target)) - RESOLUTION_ALLOWANCE, "Timer fired early"); + // we need to be in order unless the targets are the same in which case order is arbitrary + PICOTEST_CHECK(timeouts[i].fired_count != 1 || fired_at > MAX(RESOLUTION_ALLOWANCE, last_fired_at[timeouts[i].pool]) - RESOLUTION_ALLOWANCE || + to_us_since_boot(timeouts[i].target) == last_target[timeouts[i].pool], "Timer fired out of order"); + last_fired_at[timeouts[i].pool] = fired_at; + last_target[timeouts[i].pool] = to_us_since_boot(timeouts[i].target); + if (timeouts[i].fired_count == 1) { + uint64_t jitter = absolute_time_diff_us(timeouts[i].target, timeouts[i].fired_at); + if (jitter > max_jitter) { + max_jitter = jitter; + } + } + } + printf("MAX JITTER: %dus\n", (uint)max_jitter); + + PICOTEST_END_SECTION(); + + PICOTEST_START_SECTION("Alarm completion or canceled"); + memset(timeouts, 0, sizeof(timeouts)); + + absolute_time_t time_base = get_absolute_time(); + // this runs concurrently with the firing, so some are in the past + uint approx_past_timeouts = 0; +// uint32_t save = save_and_disable_interrupts(); + for(uint i = 0; i < NUM_TIMEOUTS; i++) { +// printf("%d %p\n", i); + absolute_time_t target = delayed_by_us(time_base, (rand() % TEST_LENGTH_US)); + if (absolute_time_diff_us(target, get_absolute_time()) >= 0) { + approx_past_timeouts++; + } + uint pool = rand() % 4; + timeouts[i].target = target; + timeouts[i].pool = pool; + alarm_id_t id = alarm_pool_add_alarm_at(pools[pool], target, timer_callback1, timeouts + i, true); + timeouts[i].alarm_id = id; + PICOTEST_CHECK_AND_ABORT(id >=0, "Failed to add timer"); + if (id && !(rand() & 6)) { + uint j = rand() % (i + 1); + if (timeouts[j].alarm_id && !timeouts[j].cancelled && !timeouts[j].not_cancelled) { +// alarm_pool_dump(pools[pool]); +// printf("removing %d\n", timeouts[j].alarm_id); + if (alarm_pool_cancel_alarm(pools[timeouts[j].pool], timeouts[j].alarm_id)) { + timeouts[j].cancelled = true; + } else { + timeouts[j].not_cancelled = true; + } +// printf("removed %d\n", timeouts[j].alarm_id); +// alarm_pool_dump(pools[pool]); + } + } + busy_wait_us_32(2000); // we want to overlap with the firing + } + printf("approx past timeouts %d/%d\n", approx_past_timeouts, NUM_TIMEOUTS); + sleep_us(TEST_LENGTH_US - 2000 * NUM_TIMEOUTS / 4 + 250000); + for(uint i = 0; i < NUM_TIMEOUTS/4; i++) { + printf("%d %d %d/%d/%d %"PRIi64" : %"PRIi64"\n", timeouts[i].pool, (int)timeouts[i].alarm_id, timeouts[i].fired_count, timeouts[i].cancelled, + timeouts[i].not_cancelled, to_us_since_boot(timeouts[i].fired_at), to_us_since_boot(timeouts[i].target)); + uint total = timeouts[i].fired_count + timeouts[i].cancelled; + PICOTEST_CHECK( timeouts[i].not_cancelled ? timeouts[i].fired_count : true, "Timer that failed to cancel should have fired"); + PICOTEST_CHECK(total == 1, "Timer should have fired or been cancelled"); + } + + PICOTEST_END_SECTION(); + + PICOTEST_START_SECTION("Repeating timertest"); + for(uint i=0;i<NUM_REPEATING_TIMERS;i++) { + + add_repeating_timer_us(500+ (rand() & 1023), repeating_timer_callback, (void *)(uintptr_t)i, repeating_timers + i); + } + + sleep_ms(3000); + uint callbacks = 0; + for(uint i=0;i<NUM_REPEATING_TIMERS;i++) { + PICOTEST_CHECK(cancel_repeating_timer(repeating_timers + i), "Cancelling repeating timer should succeed"); + PICOTEST_CHECK(repeating_timer_callback_count[i] > 1, "Each repeating timer should have been called back multiple times"); + callbacks += repeating_timer_callback_count[i]; + } + uint callbacks2 = 0; + for(uint i=0;i<NUM_REPEATING_TIMERS;i++) { + PICOTEST_CHECK(!cancel_repeating_timer(repeating_timers + i), "Re-cancelling repeating timer should fail"); + callbacks2 += repeating_timer_callback_count[i]; + } + PICOTEST_CHECK(callbacks == callbacks2, "No repeating timers should have been called back after being cancelled") + + PICOTEST_END_SECTION(); + + PICOTEST_START_SECTION("end of time"); + PICOTEST_CHECK(absolute_time_diff_us(at_the_end_of_time, get_absolute_time()) < 0, "now should be before the end of time") + PICOTEST_CHECK(absolute_time_diff_us(get_absolute_time(), at_the_end_of_time) > 0, "the end of time should be after now") + PICOTEST_CHECK(absolute_time_diff_us(at_the_end_of_time, at_the_end_of_time) == 0, "the end of time should equal itself") + absolute_time_t near_the_end_of_time; + update_us_since_boot(&near_the_end_of_time, 0x7ffffeffffffffff); + PICOTEST_CHECK(absolute_time_diff_us(near_the_end_of_time, at_the_end_of_time) > 0, "near the end of time should be before the end of time") + PICOTEST_END_SECTION(); + + if (issue_195_test()) { + return -1; + } + + PICOTEST_END_TEST(); +} + +#define ISSUE_195_TIMER_DELAY 50 +volatile int issue_195_counter; +int64_t issue_195_callback(alarm_id_t id, void *user_data) { + issue_195_counter++; + return -ISSUE_195_TIMER_DELAY; +} + +int issue_195_test(void) { + PICOTEST_START_SECTION("Issue #195 race condition - without fix may hang on gcc 10.2.1 release builds"); + absolute_time_t t1 = get_absolute_time(); + int id = add_alarm_in_us(ISSUE_195_TIMER_DELAY, issue_195_callback, NULL, true); + for(uint i=0;i<5000;i++) { + sleep_us(100); + sleep_us(100); + uint delay = 9; // 9 seems to be the magic number (at least for reproducing on 10.2.1) + sleep_us(delay); + } + absolute_time_t t2 = get_absolute_time(); + cancel_alarm(id); + int expected_count = absolute_time_diff_us(t1, t2) / ISSUE_195_TIMER_DELAY; + printf("Timer fires approx_expected=%d actual=%d\n", expected_count, issue_195_counter); + PICOTEST_END_SECTION(); + return 0; +} + |