diff --git a/Firmware/LogicAnalyzer_V2/CMakeLists.txt b/Firmware/LogicAnalyzer_V2/CMakeLists.txt index d5f4bead..22add812 100644 --- a/Firmware/LogicAnalyzer_V2/CMakeLists.txt +++ b/Firmware/LogicAnalyzer_V2/CMakeLists.txt @@ -9,19 +9,19 @@ 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.1.1) -set(toolchainVersion 14_2_Rel1) -set(picotoolVersion 2.1.1) -set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) -if (EXISTS ${picoVscode}) - include(${picoVscode}) -endif() +# == 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.1.1) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() # ==================================================================================== include(LogicAnalyzer_Build_Settings.cmake) @@ -91,6 +91,7 @@ endif() # Create C header file with the name .pio.h pico_generate_pio_header(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR}/LogicAnalyzer.pio + ${CMAKE_CURRENT_LIST_DIR}/fpga_clock.pio ) pico_set_program_name(LogicAnalyzer "LogicAnalyzer") @@ -141,6 +142,10 @@ if(BOARD_TYPE STREQUAL "BOARD_INTERCEPTOR") message(STATUS "Configuring for Interceptor") add_compile_definitions(BUILD_INTERCEPTOR) endif() +if(BOARD_TYPE STREQUAL "BOARD_PICO_ICE") + message(STATUS "Configuring for Pico-ICE") + add_compile_definitions(BUILD_PICO_ICE) +endif() # Add any user requested librariesUna pregu target_link_libraries(LogicAnalyzer diff --git a/Firmware/LogicAnalyzer_V2/LogicAnalyzer.c b/Firmware/LogicAnalyzer_V2/LogicAnalyzer.c index 1a4d4aa1..1880878d 100644 --- a/Firmware/LogicAnalyzer_V2/LogicAnalyzer.c +++ b/Firmware/LogicAnalyzer_V2/LogicAnalyzer.c @@ -1,5 +1,9 @@ #include "LogicAnalyzer_Board_Settings.h" +#ifdef BUILD_PICO_ICE + #include "LogicAnalyzer_FPGA.h" +#endif + #include #include #include "pico/stdlib.h" @@ -614,6 +618,11 @@ int main() //Initialize USB stdio stdio_init_all(); + #ifdef BUILD_PICO_ICE + // Initialize FPGA (release reset, wait for CDONE) and start 10 MHz clock on GPIO24 + fpga_init(); + #endif + #if defined (BUILD_PICO_W) || defined (BUILD_PICO_2_W) cyw43_arch_init(); #elif defined (BUILD_PICO_W_WIFI) || defined (BUILD_PICO_2_W_WIFI) diff --git a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Board_Settings.h b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Board_Settings.h index 45430f70..da709583 100644 --- a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Board_Settings.h +++ b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Board_Settings.h @@ -203,6 +203,39 @@ #define CAPTURE_BUFFER_SIZE (128 * 1024) #define MAX_CHANNELS 28 + #elif defined (BUILD_PICO_ICE) + + #define BOARD_NAME "PICO_ICE" + #define SUPPORTS_COMPLEX_TRIGGER + #define INPUT_PIN_BASE 0 + #define COMPLEX_TRIGGER_OUT_PIN 0 + #define COMPLEX_TRIGGER_IN_PIN 1 + #define GPIO_LED + #define LED_IO 12 // Use green LED to avoid conflict with capture pins + #define PIN_MAP {0,1,2,3,4,5,6,7,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,COMPLEX_TRIGGER_IN_PIN} + + // FPGA control pins for pico-ice + #define PIN_FPGA_CRESETN 27 // CRESET_B (active-low) + #define PIN_FPGA_CDONE 26 // CDONE + #define PIN_CLOCK 24 // Clock to FPGA (10MHz) + + // FPGA SPI configuration pins (set as high-Z during config) + #define PIN_ICE_SI 8 // SPI MOSI to FPGA flash + #define PIN_ICE_SO 11 // SPI MISO from FPGA flash + #define PIN_ICE_SCK 10 // SPI clock to FPGA flash + #define PIN_ICE_SSN 9 // SPI CS to FPGA flash (active-low) + #define PIN_RAM_SS 14 // PSRAM chip select + + #ifdef TURBO_MODE + #define MAX_FREQ 200000000 + #define MAX_BLAST_FREQ 400000000 + #else + #define MAX_FREQ 100000000 + #define MAX_BLAST_FREQ 200000000 + #endif + #define CAPTURE_BUFFER_SIZE (128 * 1024) + #define MAX_CHANNELS 24 + #endif #endif \ No newline at end of file diff --git a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Build_Settings.cmake b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Build_Settings.cmake index 53d570cc..16358954 100644 --- a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Build_Settings.cmake +++ b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Build_Settings.cmake @@ -1,11 +1,11 @@ # This file controls the build settings, set your board version -# Current versions: "BOARD_PICO", "BOARD_PICO_W", "BOARD_PICO_W_WIFI", "BOARD_ZERO", "BOARD_PICO_2" -set(BOARD_TYPE "BOARD_PICO") +# Current versions: "BOARD_PICO", "BOARD_PICO_W", "BOARD_PICO_W_WIFI", "BOARD_ZERO", "BOARD_PICO_2", "BOARD_PICO_ICE" +set(BOARD_TYPE "BOARD_PICO_ICE") # Set to 1 to enable 200Mhz mode (warning! extreme overclock and overvoltage!) # Set to 0 to disable turbo mode # Not available for the Pico W -set(TURBO_MODE 1) +set(TURBO_MODE 0) # Uncomment to be able to debug the build # set(DEBUG_BUILD 1) diff --git a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Capture.c b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Capture.c index 4469b003..fc5eac0a 100644 --- a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Capture.c +++ b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Capture.c @@ -213,11 +213,23 @@ void disable_gpios() gpio_deinit(COMPLEX_TRIGGER_IN_PIN); #endif + // Universal pin state preservation: Don't change ANY pin states after capture + // This preserves all pin configurations (inputs, outputs, high-Z) for user applications + // Logic Analyzer only needs to READ pins during capture, not control them after + // Comment out this entire section if you need the old behavior that changed pins to high-Z + /* for(uint8_t i = 0; i < lastCapturePinCount; i++) - gpio_deinit(lastCapturePins[i]); - + { + gpio_deinit(lastCapturePins[i]); // Old behavior - disrupted user applications + } + */ - gpio_set_inover(lastTriggerPin, 0); + // Universal trigger pin preservation: Only reset inover for input trigger pins + // This preserves output trigger pins in their output state for user applications + if (!gpio_is_dir_out(lastTriggerPin)) + { + gpio_set_inover(lastTriggerPin, 0); + } } @@ -640,7 +652,15 @@ bool StartCaptureFast(uint32_t freq, uint32_t preLength, uint32_t postLength, co pio_gpio_init(capturePIO, COMPLEX_TRIGGER_IN_PIN); for(uint8_t i = 0; i < MAX_CHANNELS; i++) - pio_gpio_init(capturePIO, pinMap[i]); + { + // Universal output protection: Don't take PIO ownership of pins already configured as outputs + // This allows monitoring of output signals while preserving their output state + // Comment out the gpio_is_dir_out() check if you need PIO to control output pins + if (!gpio_is_dir_out(pinMap[i])) + { + pio_gpio_init(capturePIO, pinMap[i]); + } + } //Configure capture SM sm_Capture = pio_claim_unused_sm(capturePIO, true); @@ -650,7 +670,15 @@ bool StartCaptureFast(uint32_t freq, uint32_t preLength, uint32_t postLength, co //Modified for the W for(int i = 0; i < MAX_CHANNELS; i++) - pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false); + { + // Universal output protection: Only change pin direction for pins not already configured as outputs + // This preserves output pins (like FPGA clocks, LEDs, etc.) while still allowing monitoring + // Comment out this loop and use pio_sm_set_consecutive_pindirs() directly to revert + if (!gpio_is_dir_out(pinMap[i])) + { + pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false); + } + } //Configure state machines pio_sm_config smConfig = FAST_CAPTURE_program_get_default_config(captureOffset); @@ -807,7 +835,15 @@ bool StartCaptureComplex(uint32_t freq, uint32_t preLength, uint32_t postLength, pio_gpio_init(capturePIO, COMPLEX_TRIGGER_IN_PIN); for(uint8_t i = 0; i < MAX_CHANNELS; i++) - pio_gpio_init(capturePIO, pinMap[i]); + { + // Universal output protection: Don't take PIO ownership of pins already configured as outputs + // This allows monitoring of output signals while preserving their output state + // Comment out the gpio_is_dir_out() check if you need PIO to control output pins + if (!gpio_is_dir_out(pinMap[i])) + { + pio_gpio_init(capturePIO, pinMap[i]); + } + } //Configure capture SM sm_Capture = pio_claim_unused_sm(capturePIO, true); @@ -816,7 +852,15 @@ bool StartCaptureComplex(uint32_t freq, uint32_t preLength, uint32_t postLength, captureOffset = pio_add_program(capturePIO, &COMPLEX_CAPTURE_program); for(int i = 0; i < MAX_CHANNELS; i++) - pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false); + { + // Universal output protection: Only change pin direction for pins not already configured as outputs + // This preserves output pins (like FPGA clocks, LEDs, etc.) while still allowing monitoring + // Comment out this loop and use pio_sm_set_consecutive_pindirs() directly to revert + if (!gpio_is_dir_out(pinMap[i])) + { + pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false); + } + } //Configure state machines pio_sm_config smConfig = COMPLEX_CAPTURE_program_get_default_config(captureOffset); @@ -978,14 +1022,39 @@ bool StartCaptureBlast(uint32_t freq, uint32_t length, const uint8_t* capturePin //Configure capture pins for(int i = 0; i < MAX_CHANNELS; i++) - pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false); + { + // Universal output protection: Only change pin direction for pins not already configured as outputs + // This preserves output pins (like FPGA clocks, LEDs, etc.) while still allowing monitoring + // Comment out this loop and use pio_sm_set_consecutive_pindirs() directly to revert + if (!gpio_is_dir_out(pinMap[i])) + { + pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false); + } + } for(uint8_t i = 0; i < MAX_CHANNELS; i++) - pio_gpio_init(capturePIO, pinMap[i]); + { + // Universal output protection: Don't take PIO ownership of pins already configured as outputs + // This allows monitoring of output signals while preserving their output state + // Comment out the gpio_is_dir_out() check if you need PIO to control output pins + if (!gpio_is_dir_out(pinMap[i])) + { + pio_gpio_init(capturePIO, pinMap[i]); + } + } //Configure trigger pin - pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, triggerPin, 1, false); - pio_gpio_init(capturePIO, triggerPin); + // Universal output protection: Only change pin direction for pins not already configured as outputs + if (!gpio_is_dir_out(triggerPin)) + { + pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, triggerPin, 1, false); + } + // Universal output protection: Don't take PIO ownership of pins already configured as outputs + // This preserves output signals while still allowing them to be monitored for triggers + if (!gpio_is_dir_out(triggerPin)) + { + pio_gpio_init(capturePIO, triggerPin); + } if(!invertTrigger) gpio_set_inover(triggerPin, 1); @@ -1115,14 +1184,39 @@ bool StartCaptureSimple(uint32_t freq, uint32_t preLength, uint32_t postLength, //Configure capture pins for(int i = 0; i < MAX_CHANNELS; i++) - pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false); + { + // Universal output protection: Only change pin direction for pins not already configured as outputs + // This preserves output pins (like FPGA clocks, LEDs, etc.) while still allowing monitoring + // Comment out this loop and use pio_sm_set_consecutive_pindirs() directly to revert + if (!gpio_is_dir_out(pinMap[i])) + { + pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false); + } + } for(uint8_t i = 0; i < MAX_CHANNELS; i++) - pio_gpio_init(capturePIO, pinMap[i]); + { + // Universal output protection: Don't take PIO ownership of pins already configured as outputs + // This allows monitoring of output signals while preserving their output state + // Comment out the gpio_is_dir_out() check if you need PIO to control output pins + if (!gpio_is_dir_out(pinMap[i])) + { + pio_gpio_init(capturePIO, pinMap[i]); + } + } //Configure trigger pin - pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, triggerPin, 1, false); - pio_gpio_init(capturePIO, triggerPin); + // Universal output protection: Only change pin direction for pins not already configured as outputs + if (!gpio_is_dir_out(triggerPin)) + { + pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, triggerPin, 1, false); + } + // Universal output protection: Don't take PIO ownership of pins already configured as outputs + // This preserves output signals while still allowing them to be monitored for triggers + if (!gpio_is_dir_out(triggerPin)) + { + pio_gpio_init(capturePIO, triggerPin); + } //Configure state machines pio_sm_config smConfig = measureBursts? diff --git a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_FPGA.c b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_FPGA.c new file mode 100644 index 00000000..be0a5e72 --- /dev/null +++ b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_FPGA.c @@ -0,0 +1,161 @@ +#include "LogicAnalyzer_FPGA.h" + +#ifdef BUILD_PICO_ICE + +#include "hardware/gpio.h" +#include "hardware/pio.h" +#include "hardware/clocks.h" +#include "pico/time.h" +#include "fpga_clock.pio.h" + +// PIO instance for FPGA clock generation (avoid conflicts with logic analyzer) +static PIO fpga_pio = pio1; +static int fpga_sm = -1; +static uint fpga_clock_offset = 0; + +// Use generated PIO program (fpga_clock_program) + +bool fpga_init(void) +{ + // Initialize all FPGA control pins + + // Configure ICE_RESET pin (active-low) - start with FPGA in reset + gpio_init(PIN_FPGA_CRESETN); + gpio_set_dir(PIN_FPGA_CRESETN, GPIO_OUT); + gpio_put(PIN_FPGA_CRESETN, 0); // Hold FPGA in reset initially + + // Configure CDONE pin (input, no pulls) - FPGA asserts this when configuration complete + gpio_init(PIN_FPGA_CDONE); + gpio_set_dir(PIN_FPGA_CDONE, GPIO_IN); + gpio_disable_pulls(PIN_FPGA_CDONE); + + // Leave sysCONFIG SPI pins high-Z so FPGA can self-configure from flash + gpio_init(PIN_ICE_SI); gpio_set_dir(PIN_ICE_SI, GPIO_IN); gpio_disable_pulls(PIN_ICE_SI); + gpio_init(PIN_ICE_SO); gpio_set_dir(PIN_ICE_SO, GPIO_IN); gpio_disable_pulls(PIN_ICE_SO); + gpio_init(PIN_ICE_SCK); gpio_set_dir(PIN_ICE_SCK, GPIO_IN); gpio_disable_pulls(PIN_ICE_SCK); + gpio_init(PIN_ICE_SSN); gpio_set_dir(PIN_ICE_SSN, GPIO_IN); gpio_disable_pulls(PIN_ICE_SSN); + + // Ensure PSRAM SS is also high-Z during config + gpio_init(PIN_RAM_SS); gpio_set_dir(PIN_RAM_SS, GPIO_IN); gpio_disable_pulls(PIN_RAM_SS); + + // Configure clock output pin (will be controlled by PIO) + gpio_init(PIN_CLOCK); + gpio_set_dir(PIN_CLOCK, GPIO_OUT); + gpio_put(PIN_CLOCK, 0); // Start low + + // Small delay to ensure pins are stable + sleep_ms(10); + + // Release FPGA from reset to start configuration from flash + gpio_put(PIN_FPGA_CRESETN, 1); // This should make GPIO27 go HIGH + + // Wait for FPGA configuration to complete (CDONE assertion) + // Timeout after 1000ms - allow time for flash configuration + uint32_t timeout_count = 0; + const uint32_t timeout_limit = 1000; // 1000ms timeout + + while (!gpio_get(PIN_FPGA_CDONE) && timeout_count < timeout_limit) { + sleep_ms(1); + timeout_count++; + } + + if (timeout_count >= timeout_limit) { + // FPGA configuration failed - keep reset HIGH and report failure silently + return false; + } + + // Start the 10 MHz clock after configuration completes + if (!fpga_start_clock()) { + return false; // Clock generation failed + } + + return true; +} + +bool fpga_start_clock(void) +{ + // Clear any previous program to free space (only our clock on pio1) + // Note: avoid clearing pio0 which capture uses. + // Load program + if (!pio_can_add_program(fpga_pio, &fpga_clock_program)) { + // try reclaiming a SM and proceed anyway + } + + fpga_clock_offset = pio_add_program(fpga_pio, &fpga_clock_program); + + // Get a free state machine + fpga_sm = pio_claim_unused_sm(fpga_pio, true); + + // Configure SM for sideset pin + pio_sm_config c = fpga_clock_program_get_default_config(fpga_clock_offset); + sm_config_set_sideset_pins(&c, PIN_CLOCK); + + // Prepare the pin + pio_gpio_init(fpga_pio, PIN_CLOCK); + pio_sm_set_consecutive_pindirs(fpga_pio, fpga_sm, PIN_CLOCK, 1, true); + + // Each loop is 2 instructions; with sideset toggling each instr, period = 2 cycles + // f_out = f_sys / (clkdiv * 2) => clkdiv = f_sys / (f_out*2) + float system_freq = (float)clock_get_hz(clk_sys); + float target_freq = 10000000.0f; // 10 MHz + float clkdiv = system_freq / (target_freq * 2.0f); + sm_config_set_clkdiv(&c, clkdiv); + + // Initialize and start SM + pio_sm_init(fpga_pio, fpga_sm, fpga_clock_offset, &c); + pio_sm_set_enabled(fpga_pio, fpga_sm, true); + + return true; +} + +void fpga_stop_clock(void) +{ + if (fpga_sm >= 0) { + pio_sm_set_enabled(fpga_pio, fpga_sm, false); + pio_sm_unclaim(fpga_pio, fpga_sm); + fpga_sm = -1; + } + if (fpga_clock_offset) { + pio_remove_program(fpga_pio, &fpga_clock_program, fpga_clock_offset); + fpga_clock_offset = 0; + } + + // Set clock pin low + gpio_init(PIN_CLOCK); + gpio_set_dir(PIN_CLOCK, GPIO_OUT); + gpio_put(PIN_CLOCK, 0); +} + +bool fpga_is_configured(void) +{ + return gpio_get(PIN_FPGA_CDONE); +} + +void fpga_reset(void) +{ + // Stop clock during reset + fpga_stop_clock(); + + // Pull reset low + gpio_put(PIN_FPGA_CRESETN, 0); + sleep_ms(10); // Hold reset for 10ms + + // Release reset + gpio_put(PIN_FPGA_CRESETN, 1); + + // Wait for configuration to complete + uint32_t timeout_count = 0; + const uint32_t timeout_limit = 100; + + while (!gpio_get(PIN_FPGA_CDONE) && timeout_count < timeout_limit) { + sleep_ms(1); + timeout_count++; + } + + // Restart clock if configuration succeeded + if (gpio_get(PIN_FPGA_CDONE)) { + fpga_start_clock(); + } +} + +#endif // BUILD_PICO_ICE \ No newline at end of file diff --git a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_FPGA.h b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_FPGA.h new file mode 100644 index 00000000..601d4651 --- /dev/null +++ b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_FPGA.h @@ -0,0 +1,56 @@ +#ifndef __LOGICANALYZER_FPGA__ +#define __LOGICANALYZER_FPGA__ + +#include "pico/stdlib.h" +#include "LogicAnalyzer_Board_Settings.h" + +#ifdef BUILD_PICO_ICE + +/** + * @brief Initialize the FPGA subsystem for pico-ice + * + * This function performs the complete FPGA initialization sequence: + * 1. Configure all FPGA control pins + * 2. Release ICE_RESET to start FPGA configuration from flash + * 3. Wait for CDONE to be asserted + * 4. Start the 10MHz clock generation using PIO + * + * @return true if initialization successful, false if failed + */ +bool fpga_init(void); + +/** + * @brief Start the 10MHz clock output to the FPGA + * + * Uses PIO to generate a precise 10MHz clock signal on PIN_CLOCK (GPIO24). + * The frequency can be adjusted by modifying the divider calculation. + * + * @return true if clock started successfully, false if failed + */ +bool fpga_start_clock(void); + +/** + * @brief Stop the FPGA clock output + * + * Stops the PIO clock generation and sets the clock pin to low. + */ +void fpga_stop_clock(void); + +/** + * @brief Check if FPGA configuration is complete + * + * @return true if CDONE is asserted (FPGA configured), false otherwise + */ +bool fpga_is_configured(void); + +/** + * @brief Reset the FPGA + * + * Pulls ICE_RESET low, waits briefly, then releases it to restart + * the FPGA configuration process. + */ +void fpga_reset(void); + +#endif // BUILD_PICO_ICE + +#endif // __LOGICANALYZER_FPGA__ \ No newline at end of file diff --git a/Firmware/LogicAnalyzer_V2/fpga_clock.pio b/Firmware/LogicAnalyzer_V2/fpga_clock.pio new file mode 100644 index 00000000..00e931b8 --- /dev/null +++ b/Firmware/LogicAnalyzer_V2/fpga_clock.pio @@ -0,0 +1,8 @@ +.program fpga_clock +.side_set 1 ; use 1 sideset bit to drive the clock pin + +; Simple 50% duty cycle clock: toggle the pin every instruction +.wrap_target + nop side 1 ; pin high + nop side 0 ; pin low +.wrap diff --git a/Firmware/LogicAnalyzer_V2/pico-ice-readme.md b/Firmware/LogicAnalyzer_V2/pico-ice-readme.md new file mode 100644 index 00000000..7a3e39ab --- /dev/null +++ b/Firmware/LogicAnalyzer_V2/pico-ice-readme.md @@ -0,0 +1,225 @@ +# Using the Logic Analyzer with pico-ice + +The **pico-ice** is an FPGA development board featuring the **ICE40UP5K FPGA** paired with the **RP2040** microcontroller. This guide explains how to use Dr. Gusman's Logic Analyzer firmware with the pico-ice board for debugging FPGA projects. + +## Overview + +The pico-ice Logic Analyzer firmware provides a seamless integration between FPGA development and logic analysis. The RP2040 handles both FPGA configuration management and high-speed logic capture, making it an ideal tool for FPGA debugging and verification. + +## Prerequisites + +### 1. FPGA Flash Programming Required + +**⚠️ IMPORTANT: You must program the FPGA flash memory before using the Logic Analyzer.** + +The pico-ice Logic Analyzer firmware does **NOT** program the FPGA flash. You need to: + +1. Program your FPGA bitstream to the onboard flash memory using standard pico-ice tools +2. Ensure your FPGA design is stored in flash and ready for configuration +3. Only then use the Logic Analyzer firmware + +**Programming Tools:** +- Use the official pico-ice SDK and tools for flash programming +- Refer to the [pico-ice documentation](https://pico-ice.tinyvision.ai/md_getting__started.html) for detailed programming instructions +- Popular tools include `iceprog`, `openFPGALoader`, or the pico-ice MicroPython utilities + +### 2. Hardware Requirements + +- pico-ice development board +- USB cable for connection to host computer +- paper clip to put the pico-ice in bootsel mode +- FPGA bitstream programmed in flash memory + +## How It Works + +### FPGA Initialization Sequence + +When the Logic Analyzer firmware starts: + +1. **Reset Release**: The RP2040 pulls `CRESETN` (GPIO27) high, releasing the FPGA from reset +2. **Configuration Loading**: The ICE40UP5K automatically loads its configuration from onboard flash memory +3. **Configuration Complete**: The FPGA asserts `CDONE` (GPIO26) when configuration is successful +4. **Clock Generation**: Once `CDONE` is high, the RP2040 generates a **10MHz clock** on `PIN_CLOCK` (GPIO24) using PIO +5. **Logic Analysis Ready**: The Logic Analyzer is now ready to capture signals while maintaining FPGA operation + +### Dual Functionality + +The firmware provides **dual functionality**: +- **FPGA Support**: Manages FPGA configuration, reset control, and clock generation +- **Logic Analysis**: Captures high-speed digital signals from GPIO pins for analysis + +## Pin Configuration + +### FPGA Control Pins +- **GPIO27** (`CRESETN`) - FPGA reset control (active-low) - **OUTPUT** +- **GPIO26** (`CDONE`) - FPGA configuration done status - **INPUT** +- **GPIO24** (`PIN_CLOCK`) - FPGA clock output (10MHz) - **OUTPUT** + +### SPI Flash Programming Pins +- **GPIO8** (`ICE_SI`) - SPI MOSI to FPGA +- **GPIO11** (`ICE_SO`) - SPI MISO from FPGA +- **GPIO10** (`ICE_SCK`) - SPI clock +- **GPIO9** (`ICE_SSN`) - SPI chip select (active-low) +- **GPIO14** (`RAM_SS`) - External PSRAM chip select + +### Logic Analyzer Capture Pins and Channel Mapping + +The Logic Analyzer can monitor these GPIO pins: +- **GPIO0-GPIO7** - General purpose I/O +- **GPIO12-GPIO27** - Extended I/O range + +**Channel to GPIO Pin Mapping:** +``` +Logic Analyzer Channel → GPIO Pin +Channel 0 → GPIO0 Channel 12 → GPIO20 +Channel 1 → GPIO1 Channel 13 → GPIO21 +Channel 2 → GPIO2 Channel 14 → GPIO22 +Channel 3 → GPIO3 Channel 15 → GPIO23 +Channel 4 → GPIO4 Channel 16 → GPIO24 (FPGA Clock) 🕐 +Channel 5 → GPIO5 Channel 17 → GPIO25 +Channel 6 → GPIO6 Channel 18 → GPIO26 (CDONE) 📡 +Channel 7 → GPIO7 Channel 19 → GPIO27 (CRESETN) 🔄 +Channel 8 → GPIO12 +Channel 9 → GPIO13 Note: 🕐 = 10MHz FPGA clock output +Channel 10 → GPIO14 📡 = FPGA configuration done status +Channel 11 → GPIO15 🔄 = FPGA reset control (active-low) +``` + +**Important Notes:** +- **Channels 16, 18, 19** (GPIO24, 26, 27) have special FPGA functions but can still be monitored +- **GPIO24** shows the 10MHz FPGA clock - useful for timing reference +- **GPIO26** shows FPGA configuration status (high when FPGA is configured) +- **GPIO27** shows FPGA reset control (should stay high during normal operation) + +**Note**: GPIO24 and GPIO27 are **output pins** but can still be **monitored** by the Logic Analyzer. The firmware uses universal pin state preservation to maintain their output state while allowing signal capture. **All pin configurations remain unchanged** after capture operations. + +### Status LEDs +- **GPIO13** - Red LED (active-low) +- **GPIO12** - Green LED (active-low) +- **GPIO15** - Blue LED (active-low) + +## Clock Configuration + +The FPGA receives a **10MHz clock** generated by the RP2040's PIO system. + +### Changing the FPGA Clock Frequency + +To modify the clock frequency, edit the `fpga_start_clock()` function in `LogicAnalyzer_FPGA.c`: + +```c +void fpga_start_clock() { + // Calculate divider for desired frequency + // Current: 10MHz = system_clock / divider + float divider = (float)clock_get_hz(clk_sys) / 10000000.0f; // 10MHz + + // For different frequencies: + // 5MHz: float divider = (float)clock_get_hz(clk_sys) / 5000000.0f; + // 25MHz: float divider = (float)clock_get_hz(clk_sys) / 25000000.0f; + + pio_sm_set_clkdiv(pio1, fpga_clock_sm, divider); +} +``` + +## Usage Instructions + +### 1. Build and Flash the Firmware + +```bash +# Configure for pico-ice +cmake -DBOARD_TYPE=BOARD_PICO_ICE .. +make -j8 + +# Flash the resulting .uf2 file to your pico-ice +``` + +### 2. Connect to Logic Analyzer Software + +1. Connect the pico-ice to your computer via USB +2. The device will identify as "Logic Analyzer (pico-ice)" +3. Use Dr. Gusman's Logic Analyzer software to connect +4. The software communicates via USB CDC using escaped binary protocol + +### 3. Verify FPGA Operation + +Before capturing signals: +1. Check that your FPGA configuration loaded successfully +2. Verify the 10MHz clock is present on GPIO24 +3. Confirm CRESETN (GPIO27) is at 3.3V +4. Ensure CDONE (GPIO26) is high + +### 4. Capture Logic Signals + +- Configure capture channels in the Logic Analyzer software +- You can monitor both FPGA I/O and RP2040 GPIO pins +- GPIO24 (FPGA clock) and GPIO27 (CRESETN) can be captured while maintaining their output functions + +## Advanced Features + +### Multi-Core Architecture + +The pico-ice Logic Analyzer uses both RP2040 cores: +- **Core 0**: Main Logic Analyzer functionality, USB communication, FPGA management +- **Core 1**: Available for user applications + +**Core 1 is available for custom user code!** You can implement additional functionality on Core 1 while the Logic Analyzer runs on Core 0. + +### Universal Pin State Preservation + +The firmware implements complete pin state preservation: +- **All pin states are preserved** during and after Logic Analyzer capture operations +- **Output pins** (like FPGA clock) maintain their output state and continue driving +- **Input pins** maintain their input configuration and pull-up/pull-down settings +- **High-Z pins** remain in high-impedance state +- **Core 1 user applications** are completely unaffected by Logic Analyzer operations +- **FPGA applications** continue running without any pin state disruption +- Logic Analyzer only **reads** pin states - never changes pin configurations + +### Real-time FPGA Monitoring + +The Logic Analyzer can capture FPGA signals in real-time while the FPGA continues normal operation: +- Monitor FPGA I/O signals +- Debug timing relationships +- Capture FPGA clock for timing reference +- Analyze FPGA-to-RP2040 communication + +## Troubleshooting + +### FPGA Not Configuring +- Verify FPGA flash is programmed with valid bitstream +- Check that CRESETN (GPIO27) reaches 3.3V +- Ensure CDONE (GPIO26) goes high after configuration + +### No FPGA Clock +- Verify CDONE is high before expecting clock +- Check GPIO24 with oscilloscope for 10MHz signal +- Ensure Logic Analyzer capture isn't interfering with clock output + +### Logic Analyzer Connection Issues +- Verify USB connection and drivers +- Check that device identifies as "Logic Analyzer (pico-ice)" +- Ensure Logic Analyzer software supports the pico-ice variant + +## Technical Specifications + +- **MCU**: RP2040 (Dual ARM Cortex-M0+ @ 125MHz) +- **FPGA**: ICE40UP5K (5K LUTs, 128KB BRAM, 8 DSP blocks) +- **Logic Analyzer Channels**: Up to 24 channels (GPIO0-7, 12-27) +- **Max Sample Rate**: 100 Msps +- **Capture Memory**: 32KB buffer +- **FPGA Clock**: 10MHz (user configurable) +- **Communication**: USB CDC (escaped binary protocol) + +## References + +- [pico-ice Official Documentation](https://pico-ice.tinyvision.ai/md_getting__started.html) +- [ICE40UP5K FPGA Datasheet](https://www.latticesemi.com/en/Products/FPGAandCPLD/iCE40UltraPlus) +- [Dr. Gusman's Logic Analyzer Project](https://github.com/gusmanb/logicanalyzer) +- [RP2040 Datasheet](https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf) + +## Contributing + +This pico-ice support is designed to be merged with Dr. Gusman's main Logic Analyzer repository. Contributions, bug reports, and improvements are welcome! + +--- + +**Note**: This firmware provides FPGA development board support while maintaining full compatibility with the original Logic Analyzer functionality. The universal pin state preservation ensures reliable operation with any FPGA design and preserves all user application pin configurations. \ No newline at end of file