Skip to content

Conversation

@frohro
Copy link

@frohro frohro commented Sep 26, 2025

This contains my previous pull request and adds support for the pico2-ice.

gusmanb and others added 28 commits March 6, 2025 09:04
…sabled.

Corrected bug in burst measurement in turbo mode.
Added guards for burst measurement.
Added support for Pico 2 W
Corrected bug in channel selector for non mod 4 values.
Created sync function for the GUI
Sanitized Python.cfg input
Corrected shift/ctrl modifiers
Inverted wheel direction on sample viewer/previewer
Added inferred frequency
- Add BOARD_PICO_ICE configuration to LogicAnalyzer_Board_Settings.h
- Add BOARD_PICO_ICE handling to CMakeLists.txt
- Update LogicAnalyzer_Build_Settings.cmake to include new board option
- Update SDK version to 2.2.0 for Linux compatibility
- Set TURBO_MODE=0 for stability

Pin configuration for pico-ice:
- INPUT_PIN_BASE 2 (GPIO2-GPIO28 available for capture)
- WS2812_LED on GPIO15
- Complex trigger support with GPIO0/GPIO1
- 24 channels, 100/200 MHz frequencies
- Fix SPI pin definitions to match actual pico-ice pinout (GPIO 8,11,10,9,14)
- Change LED from GPIO15 (blue, WS2812) to GPIO12 (green, GPIO) to avoid capture conflicts
- Set INPUT_PIN_BASE to 0 to match GPIO0-7 capture range
- Add PIN_MAP definition for GPIO0-7, GPIO12-27 capture channels
- Build now succeeds without errors
- Add protection for GPIO24 (FPGA clock) and GPIO27 (CRESETN) pins in all capture functions
- Prevents PIO from taking ownership of critical FPGA output pins during captures
- Allows monitoring these pins without interfering with FPGA operation
- Maintains minimal changes approach - only protects the two critical output pins
- Replace all pico-ice specific PIN_FPGA_CRESETN/PIN_CLOCK checks with gpio_is_dir_out()
- Apply universal protection to all capture modes: Simple, Fast, Complex, Blast
- Protect both pio_gpio_init() and pio_sm_set_consecutive_pindirs() calls
- Preserve output pins (FPGA clocks, LEDs, etc.) while allowing monitoring
- Add clear comments explaining how to revert if needed
- Remove all BUILD_PICO_ICE conditional compilation from capture logic
- Solution works for any board with output pins, not just pico-ice
…utting output pins into high-Z state after capture completion
- Add BOARD_PICO_ICE configuration with FPGA control pins
- Implement FPGA initialization: CRESETN control, CDONE monitoring, 10MHz PIO clock
- Add universal pin state preservation during/after capture operations
- Create complete pico-ice documentation with channel mapping
- Preserve all pin states (input/output/high-Z) for Core 1 user applications
- Maintain FPGA clock and reset signals throughout capture operations
- Add BOARD_PICO2_ICE configuration for RP2350B-based pico2-ice board
- Update pin mappings for pico2-ice hardware (GPIO20-43 capture range)
- Implement immediate clock start for both boards to avoid CDONE voltage issues
- Add comprehensive pico2-ice README documentation
- Update pico-ice README to reflect unified immediate clock approach
- Maintain full backward compatibility with existing pico-ice functionality
- Support larger capture buffer (384KB) for RP2350B boards
- Handle missing PSRAM on pico2-ice (PIN_RAM_SS = -1)

Both pico-ice and pico2-ice now use reliable immediate clock start approach
for improved FPGA initialization robustness across hardware variants.
@gusmanb
Copy link
Owner

gusmanb commented Sep 26, 2025

Hi. You are trying to merge the v6.5 with v6.0 and it's still not released.

Once v6.5 gets promoted to master I will review the PR.

Cheers.

@frohro
Copy link
Author

frohro commented Sep 26, 2025

Thanks! I prefer v6.5. You made some of the changes I wanted. :-)

@gusmanb
Copy link
Owner

gusmanb commented Oct 30, 2025

Hey, I have been checking the changes as I hope to release very soon the V6.5 and... are you sure this works properly?

I mean, I have checked the pin mapping:

20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43

The GPIOs are mapped to the PIO machines on 32 GPIO blocks that must start at GPIO0 or GPIO16, so you can only map from 0 to 31 or from 16 to 48, this mapping will fit but you must change the GPIO base to 16 using pio_set_gpio_base to the PIO capture unit, right now only channels up to GPIO31 will work.

Also I see that GPIO31 is the FPGA CRESET_B, will this not reset the FPGA when the PIO for capture sets the pin to high impedance?

@frohro
Copy link
Author

frohro commented Oct 31, 2025

We have been using it. I need to look at the channels you mentioned. I have tested with channels 2, 3, and 4. I just made the complex trigger work. It should not reset the FPGA when the pin is in high impedance; it should only reset if it is an output and low. I have not tried beyond 31, so I need to do that. If I change the pio_set_gpio_base to 16, will this mess with the complex trigger? I set the complex trigger pins to GPIO2 and GPIO3, because the pico2-ice does not have a header connected to GPIO0 or GPIO1.

@frohro
Copy link
Author

frohro commented Oct 31, 2025

Looking at the pico2-ice, it looks like if it was put into high impendace, it would reset the FPGA, but experimentally this does not happen. It has been a while since I worked on this, so my memory is fuzzy, but I remembered that I had worked to keep this from happening. Here is the code from LogicAnalyzer_Capture.c:

// Lines 656-661, 839-844, 1026-1031, 1188-1193, etc.
// Universal output protection: Don't take PIO ownership of pins already configured as outputs
if (!gpio_is_dir_out(pinMap[i]))
{
    pio_gpio_init(capturePIO, pinMap[i]);
    pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false);
}

So that is one answer. I am waiting to see if one of my students will test pins beyond GPIO31.
Thanks for looking at this.
Rob

@gusmanb
Copy link
Owner

gusmanb commented Oct 31, 2025

Cool :)

Never tried to read a GPIO that is not under the PIO's control so not sure what will be read... I suppose the worst that could happen is that you read noise. Just by sheer curiosity, did you tried it? Does it read the value present in the output?

@frohro
Copy link
Author

frohro commented Oct 31, 2025

Oh, wow, this is probably why you don't have a fantastic feature that this logic analyzer really needs! You can read any GPIO that is an output just the way you can one that is an input! This pull request gives you the template for making this analyzer so much more useful. Think of this scenario: You are developing an app for the pico, and it sends and receives data to other logic. You need to know if it is working correctly. Now, you have to have another one of your logic analyzers, and (the worst part) you have to connect wires to all of your communications GPIO from that analyzer. You can then see what is going on. But think of this: your logic analyzer was running in core 1 and your program was running in core 0. All you have to do is pass a message to the logic analyzer core to start the logic analysis when you are ready. If you want this to work for someone using CDC USB in their pico program, you could use TinyUSB (what the official pico framework uses for USB) to set up dual CDC USB ports over the same physical pico USB port. The logic analyzer connects to one, and the user connects to the other. Ask the AI for the details on how to do this. It would also be nice if your analyzer had a pattern generator to exercise the communications, but this is even better for pico programmers, and I think they would be the vast majority of users. With this, they can use your logic analyzer running on an unused core of their pico, and it generates the patterns automatically.
I think this would make your logic analyzer a killer app for pico/pico2 programmers all over the world, and it wouldn't be hard to add!
Rob

@frohro
Copy link
Author

frohro commented Oct 31, 2025

Here is a video showing how to make a second CDC port for the logic analyzer. This leaves the regular one for the pico programmer.

@frohro
Copy link
Author

frohro commented Oct 31, 2025

Your logic analyzer runs at many MHz. When you have data/clocks running fast, adding wires to your device is a problem because adding wires causes reflections, and the square waves are no longer square. If you implement this idea for your logic analyzer, it becomes much more useful than using a separate instance of your logic analyzer on another pico or your custom hardware, because those wires are not needed, so there are no new reflections that raise havoc with the signals when you hook up the logic analyzer. You measure the actual signals present, not the corrupted signals caused by the wires you had to use to connect an external logic analyzer.

@gusmanb
Copy link
Owner

gusmanb commented Oct 31, 2025

Cool, I will check it, in fact the next version that I'm preparing is based on a RP2350B and should have 31 input channels and 8 output channels and possibly SPI, I2C and RS232 ports to stimulate external devices, so it will be very useful this info :)

Regarding the use base... the most users use this for the retro scene, a lot of people use it for debugging and repairing things like the ZX Spectrum, C64 and so on 😄

Using it for development in the same pico is nearly impossible, the analyzer eats all the memory so you would not be able to fit anything else :(
I have to store all the samples in memory, is the biggest flaw of the RP2XXX, the USB port is slow as heck, being 1.1 only, makes it useless for anything that requires high speed unfortunatelly, and the PIO USB implementations are also only 1.1 full speed so the effective maximum speed is around 180Kb/s with luck...

@frohro
Copy link
Author

frohro commented Oct 31, 2025

TinyUSB is set up on Core0, which handles the USB CDC communications. You would probably need a double buffer and FIFO with an intercore interrupt and non-blocking write to minimize Core0's time spent sending USB data. The idea would be to minimize interaction with the user's program.

Another difficulty would be that some users want to use a different framework than the pico-sdk, for example, Arduino-Pico, which would require a port of your logic analyzer to that framework. I bet you could get some help from Earl Philhower if you were to make this available to Arduino-Pico programmers, though. :-)

Another option you might consider is leaving your logic analyzer on Core0 and have the user use Core1. This would probably be less satisfying to users though, because they would need to use Core1, and they all start out with Core0, so instead of just adding your logic analyzer to Core1 with a few subroutines in Core0 to support it, they have to move their program to Core1.

This idea trades wires with their reflections and extra hardware for a little extra software, and some of the USB bandwidth. Instead of hooking up physical wires, they have to add some software into their code.

@frohro
Copy link
Author

frohro commented Oct 31, 2025

On the memory problem, you could considerably cut that down, by monitoring only GPIO that the user selects, so there would be some left for the programmer. I agree that the USB is too slow. I wish they had incorporated USB 2 like the Teensy, but sending only data from selected GPIO would solve that too.

@frohro
Copy link
Author

frohro commented Oct 31, 2025

The reason your logic analyzer is used mostly by retro computer geeks is that external logic analyzers are not good for high-speed circuits, because when you hook up analyzer cables to a high-speed circuit, you mess up the circuit. In the old days, the speeds were slow, so a logic analyzer was a useful tool, because the pulses were long, so a little distortion at the beginning and end didn't matter. Now, unless it is built into your device with known good connections (in this case, right inside the RP2350B) you cannot use one to measure the signals. In times past, there were a lot more parallel interfaces. At high speed, parallel interfaces don't work as well because the traces connecting them need to be so well matched, or you will have signal skew and distortion, and the more parallel signals you have, the more difficult it is.

The pico memory available limits the number of samples/signals that can be stored, but for many use cases, you don't need a lot, and having your logic analyzer built into a pico project would be very handy. It could also be very useful for boards like the pico2-ice and pico-ice, where FPGAs are connected to the pico, because designers are often working on both transmit and receive of a digital communication exchange.

@frohro
Copy link
Author

frohro commented Oct 31, 2025

You are correct on the fact that only the first GPIO20...31 work, and those thereafter do not. I played around with it a bit and have not made GPIO32...43 work. It behaved the same with pio_set_gpio_base() in LogicAnalyzer_Capture.c as it did without it. I played around a little, but did not figure out what else I might need. Do you have any ideas? If not, I guess I could change the documentation so it says it works with GPIO20...31.

@gusmanb
Copy link
Owner

gusmanb commented Oct 31, 2025

How have you configured it? You must place it in every StartCaptureXXX, just after the assignment of the capture PIO unit, something like this:


    //Store the PIO unit and clear program memory
    capturePIO = pio0;
    pio_clear_instruction_memory(capturePIO);
    //Change PIO base GPIO to GPIO16
    pio_set_gpio_base(capturePIO, 16);

If it is set after any of the GPIO initialization or program assignment it will not work.

@frohro
Copy link
Author

frohro commented Nov 1, 2025

I put pio_set_gpio_base(capturePIO, 16); in the SimpleCapture only because, for now, I just want to test with that. It does make it so I can read the actual levels correctly on all the channels, but what should have appeared on Channel 2 now appears on Channel 18 (16 too high), and what should have appeared on Channel 15 (three channels into what wasn't displayed before), now appears on Channel 3. I did a little fiddling around trying to fix that misnumbering, but my efforts were not successful and just made things worse. I suspect you want to have it so this change is easy for those of us porting your code to other platforms, and you understand the numbering of the Channels so much better than I do. What do you think the solution to this is?

@gusmanb
Copy link
Owner

gusmanb commented Nov 1, 2025

Ok, is nothing in the code of the analyzer itself, is a mix of inconsistency in the SDK and the need of the definition of a new board for it.

The SDK uses a board definition file to do some base configurations, it configures in the define file the size of the flash, the intended defaults for SPI channels and pins, the same for RS232, etc. Also, one of the things that is defined in the board is which version of the RP2350 is used (in the case of being an RP2350). This is done by a define:

#define PICO_RP2350A 1

If set to 1 the model is A, if set to 0 the model is B.

Ok, till here all sounds right, the problem is how they use it. if you check pio_set_gpio_base you will see that they do NOT check that define in order to set up the base pin but the define PICO_PIO_VERSION that is always set if the MCU is any variant of the RP2350. But then, all the functions like sm_config_set_in_pin_base use a different define, PICO_PIO_USE_GPIO_BASE that is derived of the PICO_RP2350A define. So what happens is that they are allowing to set the PIO base GPIO to 16 but if the PIO is not the RP2350B the offset is not subtracted from the assignments and we end with a proper base but an incorrect offset configuration in the SMs...

To me this is a complete mess and totally inconsistent driving to errors that could drive someone crazy trying to understand what is happening, if it is allowed to change the base offset in the RP2350A it should always be checked this in all the other functions, or else do not allow to set the base offset in the RP2350A.

In any case, the solution is to create a custom board definition, the creation itself is simple but we will need to modify the build system.

First, you will need to create a folder in the root of the solution, let's call it custom_boards. Then, copy the pico2.h header from your SDK, it is in %SDK_ROOT%/src/boards/include/boards (it will depend on your installation, for me for example is C:\Users\(username)\.pico-sdk\sdk\2.1.1\src\boards\include\boards).

Next, modify the file. I use the same "base" for each model as for the project I do not use any of the predefined defines, and as the code must fit in the smallest flash it does not matter if the board has more than the minimum 4Mb, so let's call this new definition pico2_b.h and we can use it for any other device using the RP2350B.

Now, edit the file and change the define from

// --- RP2350 VARIANT ---
#define PICO_RP2350A 1

to

// --- RP2350 VARIANT ---
#define PICO_RP2350A 0

Ok, now we have the file, but we must do some modifications to the build. In the new define you have created on CMakeLists.txt

elseif(BOARD_TYPE STREQUAL "BOARD_PICO2_ICE")
    message(STATUS "Setting PICO_BOARD to pico2")
    set(PICO_BOARD pico2 CACHE STRING "Board type")

change it like this:

elseif(BOARD_TYPE STREQUAL "BOARD_PICO2_ICE")
    message(STATUS "Using custom definition board path")
    set(PICO_BOARD_HEADER_DIRS ${CMAKE_SOURCE_DIR}/custom_boards) 
    message(STATUS "Setting PICO_BOARD to pico2_b")
    set(PICO_BOARD pico2_b CACHE STRING "Board type")

And with this, it should work properly. At least with these already in place any other new board type that we would like top include that uses the B variant just by adding it to the new elseif will allow to use the new pio base and the extended channels.

Try it and if you find any problem I will try to help you.

@frohro
Copy link
Author

frohro commented Nov 1, 2025

Wow! Thanks for the detailed explanation. Changing the pico-sdk seems like quite a way to do this. It is tempting to just renumber the pinout in the pico2-readme.md to the way it turned out. I'm happy to make it the way you would prefer, but it sounds messier to do as you suggest. What do you think?

@frohro
Copy link
Author

frohro commented Nov 1, 2025

As I think about my solution of just changing the readme, there is one major problem with that. Since there are 24 channels, I will lose eight channels that went from 25 to 32.

@gusmanb
Copy link
Owner

gusmanb commented Nov 2, 2025

You do not need to modify the SDK, you copy a file (a simple header, you will see that does not contain much and is the official way of doing it) from the SDK to our project and create a new version based on it, nothing too fancy :)

@frohro
Copy link
Author

frohro commented Nov 2, 2025

Okay, your idea is good, but not perfect. Now the first 16 channels are okay, but the last ones are offset by 4, so what should be 15 is 19. Any ideas on this?

@gusmanb
Copy link
Owner

gusmanb commented Nov 2, 2025

Ok, we are in the right path :)

Before anything else, check that you undid the modifications added when you were trying to sort the channels, let's avoid to fight with a ghost 😄

Now, I need a clarification, is channel 1 to 16 in place and then from 17 and onwards are shifted 4 channels or is channel 15 present in channel 19 and only 14 channels are in place? Those numbers don't fit...

I'm checking the schematic of the pico2-ice and I see something.

imagen

The PMOD header is not in order with your channel definition:

{20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,COMPLEX_TRIGGER_IN_PIN}

It is defined as a sequence, so the mapping for the PMOD header will be:

  • GPIO32 -> Channel 13 -> PMOD_A3
  • GPIO33 -> Channel 14 -> PMOD_A1
  • GPIO34 -> Channel 15 -> PMOD_A4
  • GPIO35 -> Channel 16 -> PMOD_A2
  • GPIO36 -> Channel 17 -> PMOD_B3
  • GPIO37 -> Channel 18 -> PMOD_B1
  • GPIO38 -> Channel 19 -> PMOD_B4
  • GPIO39 -> Channel 20 -> PMOD_B2

If I assume that your example is really right and you have seen in channel 19 what you expected to see it in channel 15 that means that PMOD_A4 and PMOD_B4 are swapped... Are you sure that is not a human error and you injected the signal into PMOD_B4 instead of PMOD_A4?

It fits so well... 😄

@frohro
Copy link
Author

frohro commented Nov 2, 2025

You were right! I was on the PMOD_A4! Good show! Everything seems fine now. I just need to make the changes to the other triggers.

@frohro
Copy link
Author

frohro commented Nov 3, 2025

Does this look right for the fast capture?

    //Store the PIO units and clear program memory
    capturePIO = pio1; //Cannot clear it in PIO1 because the W uses PIO1 to transfer data
    triggerPIO = pio0;


    pio_clear_instruction_memory(triggerPIO);
    pio_set_gpio_base(triggerPIO, PIO_GPIO_BASE);
    pio_set_gpio_base(capturePIO, PIO_GPIO_BASE);

Thanks,
Rob

@frohro
Copy link
Author

frohro commented Nov 3, 2025

So everything but fast capture (for complex trigger) works now. I can't get it to trigger. I tried as shown above, and without setting the base on the triggerPIO, but neither works for me. I don't know if the fast trigger was working before. I did not try it before.
Added note: 11/17/25
I tried to do a fast capture on a Pico with GPIO0 and GPIO1 tied together. It would not trigger. Complex trigger works fine, so I think it is most likely just me. I don't know how to do a fast capture, or it isn't supposed to work with my boards or something.
Thanks!
Rob

@frohro
Copy link
Author

frohro commented Nov 5, 2025

I noticed I could add more channels in the board settings, but the GUI seems set at 24. Is there a point in doing that? I could add GPIO44 and possibly GPIO16-19, which might be useful in some situations.

@frohro
Copy link
Author

frohro commented Nov 17, 2025

I made the FPGA clock a build time parameter so some of my students could have the frequency they wanted for the FPGA clock. Really, I think I should modify the GUI and make changing the FPGA clock frequency a runtime option like networking parameters are now. It should not show up unless you have a pico2-ice or pico-ice. I may do that, but I'm afraid this pull request is getting a bit large already.

@frohro
Copy link
Author

frohro commented Dec 1, 2025

So everything but fast capture (for complex trigger) works now. I can't get it to trigger. I tried as shown above, and without setting the base on the triggerPIO, but neither works for me. I don't know if the fast trigger was working before. I did not try it before. Added note: 11/17/25 I tried to do a fast capture on a Pico with GPIO0 and GPIO1 tied together. It would not trigger. Complex trigger works fine, so I think it is most likely just me. I don't know how to do a fast capture, or it isn't supposed to work with my boards or something. Thanks! Rob

I think I must not have understood fast capture correctly. It seems to work just fine now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants