Last active
April 10, 2026 12:51
-
-
Save alwynallan/de68ef48fc50cb406c015c0d975fa435 to your computer and use it in GitHub Desktop.
PIO_LET - Raspberry Pi Pico SDK project for PRNG on a state machine
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ; Rename to just pio_let.pio, the arrow is to get it first in a Gist | |
| ; (C) 2026 alwynallan@gmail.com MIT License | |
| ; pio_let is an efficient PRNG that runs on a RP2040 family state machine | |
| ; Prove me wrong - LFSR on a PIO state machine is impossible! | |
| .program pio_let | |
| ; pio_sm_exec()s load initial state in osr; zero isr and y | |
| .wrap_target | |
| ;set pins, 1 ; LED for testing | |
| mov x, ::osr | |
| next_2_bits: | |
| out y, 2 | |
| dec_x_until_y_zero: | |
| jmp x-- nowhere | |
| nowhere: | |
| jmp y-- dec_x_until_y_zero | |
| in x, 2 | |
| jmp !osre next_2_bits | |
| mov osr, ~isr ; osr full | |
| mov isr, osr ; for push | |
| ;set pins, 0 | |
| push block ; mean cycle time 0.88 us | |
| ; at full rate, output will repeat every 63 minutes | |
| .wrap ; CBC mode! | |
| % c-sdk { | |
| void pio_let_program_init(PIO pio, uint sm, uint offset, uint led_pin, uint initial) { | |
| pio_gpio_init(pio, led_pin); | |
| pio_sm_set_consecutive_pindirs(pio, sm, led_pin, 1, true); | |
| pio_sm_config c = pio_let_program_get_default_config(offset); | |
| sm_config_set_in_shift(&c, false, false, 32); | |
| sm_config_set_out_shift(&c, true, false, 32); | |
| sm_config_set_set_pins(&c, led_pin, 1); | |
| sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); // makes initialization tricky | |
| pio_sm_init(pio, sm, offset, &c); | |
| // set initial value in x without TX_FIFO | |
| for(int i=7; i>=0; i--) { | |
| pio_sm_exec(pio, sm, pio_encode_set(pio_x, (initial>>(i*4)) & 15)); | |
| pio_sm_exec(pio, sm, pio_encode_in(pio_x, 4)); | |
| } | |
| pio_sm_exec(pio, sm, pio_encode_mov(pio_osr, pio_isr)); | |
| pio_sm_exec(pio, sm, pio_encode_set(pio_isr, 0)); // in case of restart | |
| pio_sm_exec(pio, sm, pio_encode_set(pio_y, 0)); // in case of restart | |
| } | |
| %} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Generated Cmake Pico project file | |
| cmake_minimum_required(VERSION 3.13) | |
| set(CMAKE_C_STANDARD 11) | |
| set(CMAKE_CXX_STANDARD 17) | |
| set(CMAKE_EXPORT_COMPILE_COMMANDS ON) | |
| # Initialise pico_sdk from installed location | |
| # (note this can come from environment, CMake cache etc) | |
| # == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == | |
| if(WIN32) | |
| set(USERHOME $ENV{USERPROFILE}) | |
| else() | |
| set(USERHOME $ENV{HOME}) | |
| endif() | |
| set(sdkVersion 2.2.0) | |
| set(toolchainVersion 14_2_Rel1) | |
| set(picotoolVersion 2.2.0-a4) | |
| set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) | |
| if (EXISTS ${picoVscode}) | |
| include(${picoVscode}) | |
| endif() | |
| # ==================================================================================== | |
| set(PICO_BOARD adafruit_feather_rp2350 CACHE STRING "Board type") | |
| # Pull in Raspberry Pi Pico SDK (must be before project) | |
| include(pico_sdk_import.cmake) | |
| project(pio_let_test C CXX ASM) | |
| set(PICO_SYS_CLOCK 150000000) | |
| # Initialise the Raspberry Pi Pico SDK | |
| pico_sdk_init() | |
| # Add executable. Default name is the project name, version 0.1 | |
| add_executable(pio_let_test pio_let_test.c ) | |
| pico_set_program_name(pio_let_test "pio_let_test") | |
| pico_set_program_version(pio_let_test "0.1") | |
| # Generate PIO header | |
| pico_generate_pio_header(pio_let_test ${CMAKE_CURRENT_LIST_DIR}/pio_let.pio) | |
| # Modify the below lines to enable/disable output over UART/USB | |
| pico_enable_stdio_uart(pio_let_test 0) | |
| pico_enable_stdio_usb(pio_let_test 1) | |
| # Add the standard library to the build | |
| target_link_libraries(pio_let_test | |
| pico_stdlib | |
| pico_rand) | |
| # Add the standard include files to the build | |
| target_include_directories(pio_let_test PRIVATE | |
| ${CMAKE_CURRENT_LIST_DIR} | |
| ) | |
| # Add any user requested libraries | |
| target_link_libraries(pio_let_test | |
| hardware_pio | |
| ) | |
| pico_add_extra_outputs(pio_let_test) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // (C) 2026 alwynallan@gmail.com MIT License | |
| #include <stdio.h> | |
| #include "pico/stdlib.h" | |
| #include "pico/rand.h" | |
| #include "hardware/pio.h" | |
| #include "hardware/clocks.h" | |
| #include "pio_let.pio.h" | |
| #define ROR32(x, n) (((x) >> (n)) | ((x) << (32 - (n)))) | |
| #define rev32(value) __builtin_arm_rbit(value) | |
| #define PIO_LET(osr) { \ | |
| uint32_t x = rev32(osr); \ | |
| uint32_t isr = 0; \ | |
| for(int i=0; i<16; i++) { \ | |
| x -= (osr & 3) + 1; \ | |
| osr = osr >> 2; \ | |
| isr = (isr << 2) | (x & 3); \ | |
| } \ | |
| osr = ~isr; \ | |
| } | |
| // https://www.reddit.com/r/RNG/comments/1sfsez6/comment/of1c837/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button | |
| uint32_t inv(uint32_t isr) { | |
| uint32_t x0; // will be the initial x = rev32(osr); | |
| uint32_t osr = 0; | |
| isr = ~isr; | |
| for (int i = 0; i < 16; i++) { | |
| osr <<= 2; | |
| if (i != 15) { | |
| osr |= ((isr >> 2) - isr - 1) & 3; | |
| } else { | |
| osr |= ( x0 - isr - 1) & 3; | |
| } | |
| if (i == 0) { | |
| x0 = (osr >> 1) | ((osr & 1) << 1); // bit reverse | |
| } | |
| isr >>= 2; | |
| } | |
| return osr; | |
| } | |
| int main() { | |
| stdio_init_all(); | |
| bool inv_correct = true; | |
| for(int i=0; i<1000; i++) { | |
| uint32_t a = get_rand_32(); | |
| uint32_t b = a; PIO_LET(b); | |
| uint32_t c = inv(b); | |
| if(a != c) inv_correct = false; | |
| } | |
| while(!stdio_usb_connected()) sleep_ms(100); // wait for usb host | |
| if(!inv_correct) printf("Error case found in inv()\n"); | |
| PIO pio = pio0; | |
| uint sm = 0; | |
| uint offset = pio_add_program(pio, &pio_let_program); | |
| printf("Loaded program at %d\n", offset); | |
| printf("System Clock: %u Hz\n", clock_get_hz(clk_sys)); | |
| //uint in = 0; | |
| uint in = get_rand_32(); | |
| pio_let_program_init(pio, sm, offset, PICO_DEFAULT_LED_PIN, in); | |
| pio_sm_set_enabled(pio, sm, true); | |
| uint exp = in, out, runs = 0; | |
| while (--runs) { | |
| uint t=time_us_32(); | |
| for(int i=0; i<2097152; i++) { | |
| out = pio_sm_get_blocking(pio, sm); | |
| //printf("%08x\n", out); stdio_flush(); | |
| PIO_LET(exp); | |
| } | |
| t = time_us_32() - t; | |
| printf("8MB in %u ms\n", t/1000); stdio_flush(); | |
| if(out != exp) { | |
| printf("Error\n"); | |
| stdio_flush(); | |
| } | |
| } | |
| while(1); // keeps USB alive for BOOTSEL | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment