Simple example for unlimited streaming capture on Husky (20-24 MS/s, 8bit)

I am trying to piece together a very basic example for CW Husky, how to capture unlimited streaming trace, looking at docs and some examples where it’s used (e.g. CW305 demos).

Though I can’t piece it together. Doesn’t have to be jupyter notebook, maybe for performance reasons a python script would be more reasonable.

What I’d like to see for STM32F3 (on UFO) or ATSAM4S2AA target (CW313 board):

  1. Capture predefined large amount of traces (1M or 1G), at max possible speed (which is around 20 or 24 MS/s, 8-bit based on docs). Trigger is TIO4 (let’s use simpleserial-aes for this)
  2. Capture the traces like in #1 but as soon as cell is started, do not wait for any trigger
  3. You can’t capture any pin, just the one connected to measurement port, right? What if you connected the measure port to some other pin, would shunt resistor interfere?

Saving trace I’d probably do something simplistic like pickle it or put into CSV from array in RAM. On-the-fly saving would be for later, if it’s even possible for OS+python+disc to handle such write speed reliably.

Any simple example for the unlimited capture?

Hm maybe I did it, just took a while to graph the 1M+ sample graph (wouldn’t work in PyCharm, needed browser due to ZMQ race condition). Not sure at this point, since I didn’t set 8-bit mode, neither the speed, just basically scope.adc.stream_mode = True and scope.adc.samples = 1200000, rising edge on TIO4.

Looks about corrent judging the very left corner for AES rounds, but 12-bit? Here is print(scope):

cwhusky Device
sn             = 50203120374a38503230343138303037
fpga_buildtime = 3/2/2023, 21:35
fw_version = 
    major = 1
    minor = 5
    debug = 0
gain = 
    mode = high
    gain = 7
    db   = 18.211009174311926
adc = 
    state                    = False
    basic_mode               = rising_edge
    timeout                  = 2
    offset                   = 0
    presamples               = 10000
    samples                  = 1200000
    decimate                 = 1
    trig_count               = 8129
    stream_mode              = True
    test_mode                = False
    bits_per_sample          = 12
    segments                 = 1
    segment_cycles           = 0
    segment_cycle_counter_en = False
    clip_errors_disabled     = False
    lo_gain_errors_disabled  = False
    errors                   = False
clock = 
    clkgen_src             = system
    clkgen_freq            = 7370129.87012987
    adc_mul                = 1
    adc_freq               = 7370129.87012987
    freq_ctr               = 0
    freq_ctr_src           = extclk
    clkgen_locked          = True
    adc_phase              = 0
    extclk_monitor_enabled = False
    extclk_error           = False
    extclk_tolerance       = 102.996826171875
trigger = 
    module   = basic
    triggers = tio4
io = 
    tio1            = serial_rx
    tio2            = serial_tx
    tio3            = high_z
    tio4            = high_z
    pdid            = high_z
    pdic            = high_z
    nrst            = high_z
    glitch_hp       = False
    glitch_lp       = False
    extclk_src      = hs1
    hs2             = clkgen
    target_pwr      = True
    tio_states      = (1, 1, 1, 0)
    cdc_settings    = bytearray(b'\x00\x00\x00\x00')
    aux_io_mcx      = high_z
    glitch_trig_mcx = trigger
glitch = 
    enabled           = False
    mmcm_locked       = False
    num_glitches      = 1
    clk_src           = target
    width             = 0
    offset            = 0
    trigger_src       = manual
    arm_timing        = after_scope
    ext_offset        = 0
    repeat            = 1
    output            = clock_xor
    phase_shift_steps = 4592
ADS4128 = 
    mode      = normal
    low_speed = True
    hi_perf   = 2
LA = 
    present                  = True
    enabled                  = False
    clkgen_enabled           = False
    locked                   = False
    clk_source               = pll
    trigger_source           = glitch
    oversampling_factor      = 1
    sampling_clock_frequency = 0.0
    downsample               = 1
    capture_group            = glitch
    capture_depth            = 0
trace = 
    present      = True
    enabled      = False
    errors       = False
    trace_synced = False
    trace_mode   = parallel
    trace_width  = 4
    clock = 
        fe_clock_alive   = True
        fe_clock_src     = usb_clock
        clkgen_enabled   = False
        fe_freq          = 96000000.0
        swo_clock_locked = False
        swo_clock_freq   = 0.0
    capture = 
        trigger_source         = firmware trigger
        use_husky_arm          = False
        raw                    = True
        rules_enabled          = []
        rules                  = []
        mode                   = while_trig
        count                  = 0
        max_triggers           = 1
        triggers_generated     = 1
        record_syncs           = False
        matched_pattern_data   = 0000000000000000
        matched_pattern_counts = [0, 0, 0, 0, 0, 0, 0, 0]
XADC = 
    status                               = good
    current temperature [C]              = 50.3
    maximum temperature [C]              = 52.4
    user temperature alarm trigger [C]   = 80.0
    user temperature reset trigger [C]   = 59.9
    device temperature alarm trigger [C] = 89.9
    device temperature reset trigger [C] = 59.9
    vccint                               = 0.998
    vccaux                               = 1.797
    vccbram                              = 0.998
userio = 
    mode       = normal
    direction  = 0
    drive_data = 0
    status     = 511
LEDs = 
    setting = 0 (default, as labelled)
errors = 
    sam_errors      = False
    sam_led_setting = Default
    XADC errors     = False
    ADC errors      = False
    extclk error    = False
    trace errors    = False

Let’s re-run and recheck everything on SAM4S2AA on CW313 - it works, but I wonder how it’s possible to push 1.2M 12-bit samples through USB2 (notebook and wave attached at the end):

  1. connect CW
  2. program simpleserial-aes built for this (make PLATFORM=CW308_SAM4S), reset
  3. check with external debugger - Ozone - that the program is there and runs
  4. do a basic trigger on 120e3 samples, tio4 trigger (graph1)
  5. set scope to streaming mode, 1.2e6 samples, tio4 trigger
  6. run and show (graph2)
  7. save the wave into pickle for later
  8. zoom into graph2 to see it looks like graph1 (let’s name the zoom “graph3”)

OK so graph1 win 120000 samples looks like this, about right:

Graph2 with 1.2e6 samples looks like this unzoomed:

Zoomed graph2 into graph3 looks similar to original, but the samples seem “compressed”, but not any error in scope.errors

Can’t make easily abs difference, because streaming mode has no presamples. Not seeing any decimate setting. Does it look correct? Tried reset target before 1.2M capture, doesn’t seem to make difference.

Adding the ipynb notebook file and pickled wave into one zip file:
streaming_capture.zip (1.3 MB)

All you need to do is set scope.adc.stream_mode = True, and scope.adc.samples to what you want. What you can actually achieve in terms of max sample rate and number of samples will depend on a lot of factors. If Husky can’t keep up you’ll get the flashing red lights and scope.adc.errors will report FIFO over- or under-flows. (If this happens you may find that you need to hard-reboot Husky to recover.)

scope.sc.arm(False)
scope.arm()
scope.sc.triggerNow()
scope.sc.arm(False)
assert scope.capture() == False
raw = scope.get_last_trace()

The ADC is only connected to the measure port.

1 Like