Skip to content

DSLogic Plus PGL12 (0x0034) support, trigger support, and CLI diagnostics - #12

Open
amin005 wants to merge 6 commits into
LISTENAI:masterfrom
amin005:dslogic-plus-pgl12-and-triggers
Open

amin005 wants to merge 6 commits into
LISTENAI:masterfrom
amin005:dslogic-plus-pgl12-and-triggers

Conversation

@amin005

@amin005 amin005 commented Sep 10, 2026

Copy link
Copy Markdown

Six commits from getting a PGL12-revision DSLogic Plus working headlessly. Each
builds on its own, and the whole branch is verified on hardware.

Happy to split this into separate PRs if you would rather review the device
support, the trigger work and the UX changes independently — the commits are
already cut along those lines.

Device support (closes #11)

patches: add DSLogic Plus / U2Basic PGL12-2 device profiles

A DSLogic Plus with PID 0x0034 is not detected: the pinned submodule's
profile table stops at 0x0030. The submodule already ships the bitstream
the 0x0034 profile refers to — DSView/res/DSLogicPlus-pgl12-2.bin is
byte-identical (blob ce921c35) to the one in DSView master — so only the table
entry is missing and no submodule bump is needed. A bump would pull in 94 files
and ~8 months of unrelated churn to fix a lookup table; this is 55 additive
lines.

patches: security check pass is not an error

DSView logs the successful security-check branch with sr_err(). It is gated
on CAPS_FEATURE_SECURITY, which the PGL12 profiles set, so affected boards
print three spurious error lines per capture that survive any log-level setting.

Triggers

feat(sys): bind ds_log_level and ds_trigger_* from the DSView runtime
feat(capture): simple trigger support

The runtime exports a complete simple-trigger API and none of it was reachable.
This wires it through:

--trigger 0=r,1=1     0 low, 1 high, r rising, f falling, c either, x don't care
                      channels may be written 0 or D0
--trigger-position N  percent of the buffer kept from before the trigger

Two review notes:

  • The trigger is programmed where the two capture-preparation paths converge,
    just before start_collect(). Putting it in apply_capture_config() silently
    skips every capture that passes device options, because those go through
    apply_device_options() instead. That failure mode is indistinguishable from
    a trigger that does not work, and it cost me a debugging cycle.
  • libsigrok4DSL keeps trigger state in a process global, so apply always resets
    first.

A trigger naming a channel --channels does not enable is rejected up front,
since the device cannot trigger on a channel it is not sampling.

Usability

feat(cli): quiet the native logger by default, add --log-level

Every command printed 14 lines of scan chatter to stderr. libsigrok4DSL defaults
to info and nothing lowered it. Now defaults to warn, with --log-level to
raise it. The level must be set between loading the library and init() — the
scan happens inside init(), so setting it afterwards suppresses nothing.

feat(cli): surface unsupported devices, coerced settings, and size the wait

  • devices list returned empty + exit 0 whether nothing was plugged in or an
    unsupported DreamSourceLab device was present. The native scan logs the skip
    at info, so quieting the logger hid the only clue. The bus is now probed
    separately (Linux/sysfs) and the difference reported with a non-zero exit. An
    empty bus still exits 0.
  • The device silently overrides settings it cannot meet — internal-test turns
    a requested 1 MHz into 100 MHz. Mismatches are now reported on stderr and in a
    warnings array. Buffer alignment (200000 → 200704) is within a 1% tolerance
    and stays quiet, because a warning that always fires is one nobody reads.
  • --wait-timeout-ms defaulted to a flat 10 s, which aborts large captures
    mid-flight. Default 0 now derives it from sample rate and limit.

Verification

Hardware: DSLogic Plus 2a0e:0034 (PGL12), Ubuntu 24.04, rustc 1.98.1.
Stimulus: a Trion T20 FPGA driving known UART / SPI / I2C / counter patterns.

  • cargo test --workspace: 285 passed, 0 failed
  • Every commit compiles independently (checked in a separate worktree)
  • Capture decodes correctly through sigrok's decoders: UART Hello LA!\r\n,
    SPI MOSI A5 3C 00 FF / MISO 5A C3 FF 00, I2C Start, Addr 42, ACK, DE, ACK, AD, ACK, Stop, and the device's internal test pattern as an exact
    binary counter
  • Trigger, against a UART source emitting one frame per ~10 ms captured in a
    2 ms window: untriggered caught the frame in 1 of 5 runs; --trigger 1=r
    caught it in 3 of 3 with correct framing
  • The unsupported-device diagnostic was verified by reverting the profile patch
    and rebuilding, which reproduces the original empty-list failure and now
    reports devices_present_but_unsupported with exit 1

Known limitation

feat(sys) binds both the trigger and log-level symbols in one commit. My
additions to bridge_runtime.c are textually interleaved and cannot be split by
hunk; separating them means editing the C file rather than re-staging. Say the
word if you want that split.

amin005 added 6 commits September 10, 2026 16:39
A DSLogic Plus with USB PID 0x0034 (the Pango PGL12 board revision) is not
detected: the pinned DSView submodule's profile table in
libsigrok4DSL/hardware/DSL/dsl.h stops at 0x0030, so the native scan skips
the device and `devices list` comes back empty.

The submodule already ships the bitstream the 0x0034 profile refers to --
DSView/res/DSLogicPlus-pgl12-2.bin is byte-identical (blob ce921c35) to the
one in DSView master -- so no submodule bump is needed. Only the table entry
is missing.

This adds the 0x0034 and 0x0035 profiles verbatim from DSView master
(2e9e2c8), as a patch rather than a submodule bump: the bump would drag in
94 files and roughly eight months of unrelated decoder and UI churn to fix a
lookup table, where this is 55 additive lines.

Verified on hardware. With the patch applied the device enumerates and
capture works; the device's own internal test pattern decodes as an exact
binary counter, and UART, SPI and I2C traffic decode correctly through
sigrok's protocol decoders.

Refs: LISTENAI#11
DSView logs the *successful* security-check branch with sr_err():

    if (ret != SR_OK){
        sr_err("Security check failed!");
    } else {
        sr_err("Security check pass!");   // success, logged as an error
    }

The check is gated on CAPS_FEATURE_SECURITY, which the PGL12 profiles set,
so affected boards print three spurious error lines on every capture. Being
logged at error level, they survive any sane log-level setting.

Demoted to sr_info so the failure branch stays visible and the success
branch stops shouting.
The runtime exports a full simple-trigger API and a log-verbosity setter, and
none of it was reachable: dsview-sys bound neither. This adds the FFI layer
only -- typedefs, dsview_bridge_api members, dlsym resolution, and the
dsview_bridge_ds_* wrappers, plus the safe Rust methods over them. The
features that use these land in the following commits.

Symbols bound:

  ds_log_level          native stderr verbosity (0 none .. 5 detail)
  ds_trigger_reset      clear trigger state
  ds_trigger_probe_set  per-probe condition
  ds_trigger_set_en     arm/disarm
  ds_trigger_set_mode   simple vs staged
  ds_trigger_set_pos    pre/post-trigger ratio
  ds_trigger_set_stage  staged-trigger count

Trigger conditions are the uppercase characters libsigrok4DSL matches on --
'X' don't care, '0'/'1' level, 'R' rising, 'F' falling, 'C' either edge --
and ds_trigger_probe_set() writes into the slot past the staged-trigger
array, which is what the driver reads for SIMPLE_TRIGGER.
Every command printed a dozen-plus lines of scan chatter to stderr:

    sr: lib_main: Init libsigrok.
    sr: lib_main: Scan all connected hardware device.
    sr: dslogic: Scan DSLogic device...
    ...

libsigrok4DSL defaults to its `info` level and nothing lowered it, which is
noise in a tool whose output is meant to be piped. Discovery now defaults to
`warn` and `--log-level none|error|warn|info|debug|detail` raises it again
for diagnosing a device that will not enumerate.

The level has to be set between loading the library and calling init(): the
device scan happens inside init(), so setting it afterwards suppresses
nothing. Hence connect_with_log_level() rather than a setter on Discovery.

A plain `devices list` goes from 14 lines of stderr to 0.
…e wait

Three cases where the tool knew something and did not say it.

Unsupported device on the bus
  `devices list` returned an empty list and exit 0 whether nothing was
  plugged in or a DreamSourceLab device was present that this build cannot
  drive. The native scan does log the skip, but at `info`, which is below the
  default log level -- so quieting the logger hid the only clue. The bus is
  now probed separately (Linux, via sysfs) and the difference reported:

      code: devices_present_but_unsupported
      "found DreamSourceLab USB device(s) [2a0e:0034] that this build does
       not recognise..."

  with a non-zero exit. An empty bus still exits 0.

Coerced settings
  The device silently overrides values it cannot meet; selecting
  `internal-test` forces its own sample rate, turning a requested 1 MHz into
  100 MHz. Both figures were already in the JSON, but only for a caller who
  thought to diff them. Mismatches are now reported on stderr and in a
  `warnings` array.

  Buffer-limit alignment (200000 -> 200704) happens on essentially every
  capture and is not a surprise, so the sample-limit check uses a 1%
  tolerance. A warning that always fires is one nobody reads; a clamp that
  matters, such as hitting hardware capacity, is far larger than the
  alignment step.

Wait timeout
  --wait-timeout-ms defaulted to a flat 10 s, which aborts a large capture
  while it is still running. It now defaults to 0, meaning derive it from
  sample rate and limit, with 3x headroom for upload and a 10 s floor.
Add --trigger and --trigger-position so a capture can be armed on an event
instead of grabbing an arbitrary window and hoping the event is inside it.

    --trigger 0=r,1=1     0 low, 1 high, r rising, f falling,
                          c either edge, x don't care
                          channels may be written 0 or D0
    --trigger-position N  percent of the buffer kept from before the trigger

Conditions map onto the uppercase characters libsigrok4DSL matches on.
SIMPLE_TRIGGER evaluates two comparator banks per probe, so both are set to
the same character to express one condition.

Two things worth knowing for review:

- The trigger is programmed at the point the two capture-preparation paths
  converge, immediately before start_collect(). Putting it in
  apply_capture_config() silently skips every capture that supplies device
  options, because those take the other path through apply_device_options().
  That failure mode looks exactly like a trigger that does not work.

- libsigrok4DSL keeps trigger state in a process global, so apply always
  resets first; otherwise a probe armed by an earlier capture in the same
  process is still live.

A trigger naming a channel that --channels does not enable is rejected up
front: the device cannot trigger on a channel it is not sampling, so it
would simply never fire.

Verified on hardware against a UART source emitting one frame per ~10 ms,
captured in a 2 ms window: untriggered caught the frame in 1 of 5 runs,
--trigger 1=r caught it in 3 of 3 with correct framing.

@seasonyuu seasonyuu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review findings

I found two issues worth addressing:

[P1] Clean up the capture session when trigger setup fails

crates/dsview-core/src/lib.rs:2673 calls apply_trigger() after the device has been opened, streaming VCD has started, and acquisition callbacks have been registered. If any trigger FFI call fails, the ? return bypasses the cleanup path below, so callbacks and the temporary streaming output can remain active. Please handle this error like a start failure by calling session.cleanup(&self.runtime, true) and aborting streaming VCD before returning.

[P2] Report coercions for all device options

crates/dsview-cli/src/main.rs:3054-3092 only compares sample rate, sample limit, enabled channels, and channel mode. CaptureDeviceOptionSnapshot also contains operation_mode_id, stop_option_id, threshold_volts, and filter_id; coercions in those fields are currently silent. The PR description says setting mismatches are reported on stderr and in warnings, so these fields should be compared too (with an appropriate float comparison for threshold voltage).

The new trigger parsing/programming path also has no focused tests for parsing, position validation, or cleanup on FFI failure. cargo test --workspace could not complete in this environment because pkg-config --cflags glib-2.0 is unavailable.

I found no separate repository standards violations.

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.

DSLogic Plus PGL12 (PID 0x0034) not detected: pinned DSView ships the bitstream but dsl.h has no profile entry

2 participants