pico-coder / sigrok-pico

Use a raspberry pi pico (rp2040) as a logic analyzer and oscilloscope with sigrok
812 stars 92 forks source link

HW Trigger #22

Open X-Ryl669 opened 2 years ago

X-Ryl669 commented 2 years ago

This project seems to have a very smart implementation of HW trigger that's using PIO code to handle many triggering conditions in only 2 cycles (so 100Msps is achievable).

pico-coder commented 2 years ago

Tracking this here: https://github.com/gusmanb/logicanalyzer/issues/4

rgrr commented 1 year ago

If you are interested, I have implemented some kind of auto trigger for my sigrok-pico module. Auto triggering works til 1/7 of system clock, on my probe these are 24MHz.

See here: https://github.com/rgrr/yapicoprobe, especially https://github.com/rgrr/yapicoprobe/blob/master/src/pico-sigrok/sigrok.pio

Code fragment to switch between the generated fetch and the above:

/**
 * Setup PIO operation.
 * PIO is configured, so that it reads input in \a sr_dev.pin_count chunks and writes the data in 32bit words.
 * That means, that 4bit reads require 8 samples to do one 32bit write and so on.
 *
 * \note
 *    used PIO is \a SIGROK_PIO.
 *
 * \pre
 *    - sr_dev.d_mask != 0
 *    - sr_dev.pin_count (4/8/16/32) and sr_dev.sample_rate must be set
 *    - sr_dev.sample_rate must be a multiple of 1000
 *
 * \post
 *    - PIO is configured but not started
 *    - unused pins within pincount are pulled down to prevent those lines from false triggers
 */
static void setup_pio(void)
{
    const uint32_t trigger_delay = 7;      // see sigrok.pio
    uint32_t sample_rate_khz = 0;
    pio_sm_config pio_conf;
    uint offset;
    uint32_t f_clk_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_SYS);

    assert(sr_dev.d_mask != 0);
    assert(sr_dev.pin_count == 4  ||  sr_dev.pin_count == 8  ||  sr_dev.pin_count == 16  || sr_dev.pin_count == 32);

    pio_clear_instruction_memory(SIGROK_PIO);

    if (sr_dev.sample_rate / 1000 <= f_clk_sys / trigger_delay  &&  sr_dev.a_mask == 0) {
        //
        // Auto triggering for 4/8/16bit: acquisition is triggered if there are changes on the enabled input lines
        // TODO currently no auto trigger with analog/digital signal mix
        //
        Dprintf("Capturing with auto trigger\n");
        if (sr_dev.pin_count == 4) {
            offset = pio_add_program(SIGROK_PIO, &sigrok_d4_triggered_program);
            pio_conf = sigrok_d4_triggered_program_get_default_config(offset);
            sample_rate_khz = (trigger_delay * sr_dev.sample_rate) / 1000;
        }
        else if (sr_dev.pin_count == 8) {
            offset = pio_add_program(SIGROK_PIO, &sigrok_1b_triggered_program);
            pio_conf = sigrok_1b_triggered_program_get_default_config(offset);
            sample_rate_khz = (trigger_delay * sr_dev.sample_rate) / 1000;
        }
        else if (sr_dev.pin_count == 16) {
            offset = pio_add_program(SIGROK_PIO, &sigrok_2b_triggered_program);
            pio_conf = sigrok_2b_triggered_program_get_default_config(offset);
            sample_rate_khz = (trigger_delay * sr_dev.sample_rate) / 1000;
        }
    }

    if (sample_rate_khz == 0) {
        //
        // this is too fast for auto triggering: just do sampling of the input
        //
        uint16_t capture_prog_instr;

        Dprintf("Fast immediate capturing\n");
        capture_prog_instr = pio_encode_in(pio_pins, sr_dev.pin_count);
        struct pio_program capture_prog = {
                .instructions = &capture_prog_instr,
                .length = 1,
                .origin = -1
        };
        offset = pio_add_program(SIGROK_PIO, &capture_prog);
        // Configure state machine to loop over this `in` instruction forever,
        // with autopush enabled.
        pio_conf = pio_get_default_sm_config();
        sm_config_set_wrap(&pio_conf, offset, offset);
        sample_rate_khz = sr_dev.sample_rate / 1000;
    }

    uint32_t div_256 = (256 * f_clk_sys + sample_rate_khz / 2) / sample_rate_khz;
    uint32_t div_int  = div_256 >> 8;
    uint32_t div_frac = div_256 & 0xff;
    if (div_int == 0) {
        div_int  = 1;
        div_frac = 0;
    }
    else if (div_int > 0xffff) {
        div_int  = 0xffff;
        div_frac = 0xff;
    }
    Dprintf("PIO sample clk %lukHz / (%lu + %lu/256) = %lukHz, requested %lukHz\n",
            f_clk_sys, div_int, div_frac, sample_rate_khz, sr_dev.sample_rate / 1000);
    sm_config_set_clkdiv_int_frac(&pio_conf, div_int, div_frac);

    sm_config_set_in_pins(&pio_conf, SR_BASE_D_CHAN);                                // start at SR_BASE_D_CHAN
                                                                                     // enable pull-down on unused pins within pin_count
    for (int i = 0;  i < (sr_dev.pin_count < SR_NUM_D_CHAN ? sr_dev.pin_count : SR_NUM_D_CHAN);  ++i) {
        if ((sr_dev.d_mask & (1 << i)) == 0) {
            // pull down seems to be less noise sensitive
            gpio_pull_down(SR_BASE_D_CHAN + i);
        }
    }

    sm_config_set_in_shift(&pio_conf, true, true, 32);                               // shift right, autopush, threshold=32
    sm_config_set_fifo_join(&pio_conf, PIO_FIFO_JOIN_RX);
    pio_sm_init(SIGROK_PIO, SIGROK_SM, offset, &pio_conf);
    pio_sm_set_enabled(SIGROK_PIO, SIGROK_SM, false);
    pio_sm_clear_fifos(SIGROK_PIO, SIGROK_SM);
    pio_sm_restart(SIGROK_PIO, SIGROK_SM);
}   // setup_pio

And in the CMakeLists.txt one has to add:

pico_generate_pio_header(picoprobe ${CMAKE_CURRENT_LIST_DIR}/src/pico-sigrok/sigrok.pio)