dreamsourcelab-dslogic: V2 simple-trigger encoding (follow-up to #292)#293
Open
huehuehuehueing wants to merge 9 commits into
Open
dreamsourcelab-dslogic: V2 simple-trigger encoding (follow-up to #292)#293huehuehuehueing wants to merge 9 commits into
huehuehuehueing wants to merge 9 commits into
Conversation
…0034)
Adds support for the DSLogic Plus hardware revision that enumerates
as USB ID 2a0e:0034 ("USB-based DSL Instrument v2") and ships with a
Pango FPGA bitstream. This revision speaks a newer envelope-style
control protocol than the existing DSLogic family; the driver gains a
parallel V2 path while leaving the V1 (flat-opcode) path untouched.
Architecture
------------
- struct dslogic_protocol_ops vtable in protocol.h with one slot per
operation whose wire format differs between protocol versions
(fpga_firmware_upload, fpga_config, acquisition_start/stop,
set_samplerate/voltage_threshold/trigger/external_clock/clock_edge,
security_check).
- enum dslogic_protocol_version per profile (DSL_PROTO_V1 or
DSL_PROTO_V2). All existing profiles tagged V1; PID 0x0034 tagged V2.
- dev_context gains a cached ops pointer resolved at dev_open.
- protocol_v1.c (new): thin facades over the existing V1 functions,
bound into dslogic_v1_ops. Pure refactor with no wire changes.
- protocol_v2.{c,h} (new): the envelope-protocol implementation.
Wire format (V2)
----------------
- Three control opcodes only: CMD_CTL_WR=0xb0, CMD_CTL_RD_PRE=0xb1,
CMD_CTL_RD=0xb2. The packed struct ctl_header (dest, offset, size)
carries the real destination via a DSL_CTL_* enum
(HW_STATUS, INTRDY, WORDWIDE, START, STOP, BULK_WR, NVM, I2C_REG,
I2C_STATUS, PROG_B, LED, FW_VERSION).
- command_ctl_rd_v2 is two-phase: PRE (OUT) writes the header, 10 ms
sleep, then RD (IN) collects the requested bytes.
- Register R/W (dsl_wr_reg_v2 / dsl_rd_reg_v2) wraps the envelope with
I2C_REG (write) / I2C_STATUS (read) destination and a 1-byte address
in offset. NVM reads use DSL_CTL_NVM.
Bitstream upload (V2)
---------------------
Mirrors DSView's dsl_fpga_config sequence:
PROG_B low -> LED off -> PROG_B high -> wait FPGA_INIT_B -> INTRDY
low -> DSL_CTL_BULK_WR with 3-byte filesize -> bulk transfer bitstream
on ep2 OUT -> INTRDY high -> wait GPIF_DONE -> INTRDY low -> wait
FPGA_DONE -> LED green -> WORDWIDE high.
Security challenge (V2)
-----------------------
The 0x0034 firmware gates capture on an 8-step challenge-response over
an I2C register block (SEC_CTRL_ADDR=0x73, SEC_DATA_ADDR=0x75). The
encryption blob is read from device NVM at SECU_EEP_ADDR=0x3C00 via
DSL_CTL_NVM. Implemented as v2_security_check; called from dev_open
after the FPGA bitstream is loaded. Mirrors DSView's dsl_secuCheck.
FPGA arm (V2)
-------------
WORDWIDE -> DSL_CTL_BULK_WR (3-byte word count = sizeof(setting)/2 =
186) -> poll bmSYS_CLR -> bulk transfer struct DSL_setting (372 bytes)
on ep2 OUT -> DSL_CTL_INTRDY -> read HW_STATUS once and check
bmGPIF_DONE. The DSL_setting layout (with its (register_index << 8) |
word_count header encoding) and the samplerate divider math
(hw_max=500 MHz, pre_div=5 for DSLogic Plus pgl12) mirror DSView's
dsl_fpga_arm. Sample count is shifted right by 4 because the FPGA's
minimum capture unit is 16 samples.
V2 acquisition stop is two-stage like DSView's: write CTR0_ADDR :=
bmFORCE_RDY first (soft FPGA abort that releases the GPIF capture
engine and resets the green LED to solid), then DSL_CTL_STOP.
dev_open changes
----------------
- Extends has_firmware product-string probe to recognise "USB-based
DSL Instrument v2" so the V2 device skips the legacy FX2 firmware
upload (which would renumerate it to 0x0020 with the V1 firmware).
- V2 path performs DSL_CTL_FW_VERSION + DSL_CTL_HW_STATUS reads at the
start of dev_open (matches DSView's hw_dev_open + dsl_dev_open
initialisation). The pre-existing V1 firmware version probe at
bRequest 0xb0 collides with V2's CMD_CTL_WR opcode and is now
V1-gated.
- After bitstream upload + security check, an initial voltage threshold
is written to VTH_ADDR via the new vtable slot.
Tested on real PID 0x2a0e:0x0034 hardware: scan, FPGA bitstream upload,
security challenge, FPGA arm, sample capture (100 samples / 16 channels
at 1 MHz returns 1959 bytes), and clean stop with idle-LED state.
Co-authored-by: Larry Hernandez <l.gr@dartmouth.edu>
After a close/reopen cycle (e.g. pulseview Stop then Run again) the kernel-side endpoint state can take a few ms to settle. The next dev_open's libusb_claim_interface then returns LIBUSB_ERROR_BUSY even though no other process holds the interface. Retry up to 10 times at 50 ms intervals (500 ms worst case) before giving up with the original error message. Independently testable: cycle PulseView's Stop and Run buttons repeatedly on the same device without replugging; the BUSY error should no longer surface.
…al open
Two related fixes for the PulseView Stop->Run cycle:
1. v2_fpga_firmware_upload now reads HW_STATUS first and skips the
PROG_B/bitstream sequence if bmFPGA_DONE is already set. In that
case it just writes CTR0_ADDR=0 ("dessert clear"), mirroring
DSView's behaviour in dsl_dev_open's already-configured branch
(dsl.c). Re-running the full PROG_B cycle on a live FPGA
wedges the post-INTRDY FPGA_DONE poll because the previous capture
engine has not been torn down on the host side. Symptom was:
sr: dreamsourcelab-dslogic: Timeout waiting for HW_STATUS bit 0x40
2. dev_open now releases + closes the USB handle on any post-claim
failure (fpga_firmware_upload or security_check returning non-OK).
libsigrok does not call dev_close on a failed dev_open, so without
this unwind the kernel still saw the interface as claimed by us;
the next dev_open's libusb_claim_interface then returned
LIBUSB_ERROR_BUSY ("Another program or driver has already claimed
it") even with the retry loop added in 2ee3757. The retry loop
stays as a belt-and-braces for genuinely transient cases.
Together these turn the "Stop, then Run again" PulseView cycle into a
reliable no-op (the device is already configured, we skip the upload,
arm runs against the warm FPGA).
DSLogic Plus exposes seven distinct channel-count/samplerate presets in DSView (DSL_BUFFER100x16 / DSL_BUFFER200x8 / DSL_BUFFER400x4 plus four stream modes from 20 to 100 MHz). Each preset has its own FPGA base clock and pre-divider, so the samplerate divider math has to key off the active preset. This commit introduces the table and uses the profile's default preset (DSL_BUFFER100x16: 16 channels buffered, max 100 MHz, hw_max=100 MHz, pre_div=1). Foundation for a follow-up commit that exposes SR_CONF_CHANNEL_MODE so users can pick a different preset (more channels at lower rates, or fewer channels at higher rates / streaming). This also fixes a latent bug: the previous implementation hardcoded hw_max=500 MHz and pre_div=5 (values that belong to the _3DN2 mode variants the DSLogic Plus does not enable), producing a div_h value that almost certainly resulted in a sample rate different from the one the user requested. After this change, the divider matches DSView's computation for the active channel mode. V1 hardware is untouched: the ch_mode_id field on dev_context is V2 only, and the V1 path does not look at it. Adds the relevant DS_MODE_* bit positions to protocol_v2.h and sets DS_MODE_STREAM_MODE_BIT in setting.mode for stream channel modes. ch_en mask is derived from the active mode's num_channels (16-bit only; the DSLogic Plus has no channels above 15).
Wire dslogic_plus_auto_pick_mode_id into the V2 set_samplerate path and re-pick at arm time. The auto-pick chooses the smallest-channel mode whose max_samplerate covers the requested rate (minimising USB bandwidth so high samplerates can stream cleanly). sigrok-cli orders -c flags independently of -C, so the enabled- channel mask may still be stale at config_set time; the arm-time re-pick in v2_build_default_setting sees the FINAL mask just before the FPGA arm. When no mode covers (requested samplerate x enabled channels), clamp cur_samplerate down to the picked mode's max_samplerate and warn so the user knows to drop a channel or switch off continuous mode. Avoids a silent "Device only sent N samples" USB-bandwidth abort mid-acquisition.
…wer mode
V2 captures at 50/100/200 MHz buffered with all 16 channels enabled
were sending ~100-200 MB/s on the USB IN endpoint - twice USB 2.0 HS's
~50 MB/s ceiling. The FPGA-side buffer filled with pre-arm idle data
and real bursts never made it across.
Two interlocking changes:
1. v2_build_default_setting derives setting.ch_en_l from the sigrok
enabled_channel_mask intersected with the active mode's capability
cap, instead of unconditionally enabling channels 0..num_channels-1.
With sigrok-cli's -C 0,1,2,3 (or PulseView's channel checkboxes),
the FPGA now only captures those channels, dropping the IN-rate.
2. dslogic_plus_auto_pick_mode_id now takes need_channels =
max_enabled_index+1 and picks the smallest num_channels mode that
satisfies BOTH the requested samplerate AND need_channels. At
50 MHz with -C 0,1,2,3 this picks DSL_BUFFER400x4 (4-channel mode,
max 400 MHz) which streams at 25 MB/s instead of 100 MB/s.
Promotes enabled_channel_count and enabled_channel_mask from file-
static to SR_PRIV so V2 code can use them.
Independently testable:
sigrok-cli -d dreamsourcelab-dslogic -C 0,1,2,3 \
-c samplerate=50000000 --samples 100000000 -O bits
captures real SPI activity (verified against DSView CSV reference of
the same DUT: SCK on ch0, MOSI on ch1, MISO on ch2, CS on ch3 - all
four lines show the expected DUT-reset burst pattern).
Wire three V2-only configuration knobs through the libsigrok config API and into the DSL_setting blob's mode bitfield at arm time: - SR_CONF_RLE -> mode bit 3 (RLE_MODE) - SR_CONF_FILTER -> mode bit 8 (FILTER) - SR_CONF_EXTERNAL_CLOCK -> mode bit 1 (CLK_TYPE) These give applications access to the DSLogic Plus FPGA's run-length encoding (compresses idle samples to fit USB 2.0 HS bandwidth at high samplerates), the 1T glitch filter (suppresses single-sample spikes in noisy environments), and external clocking. SR_CONF_FILTER is wired as SR_T_BOOL (matches its registration in libsigrok's config table), not a string enum; passing a string would make PulseView's libsigrokcxx bindings throw std::bad_cast when the runtime type didn't match the declared boolean type.
The FPGA's trigger-position header reports remain_cnt in two halves (remain_cnt_l/h, 64-bit total). In BUFFERED mode with RLE this is the count of samples the FPGA was short of limit_samples (the compressed buffer ran out of room before reaching the requested capture length); without honoring it, the acquisition-stop check never trips, sent_samples never reaches limit_samples, and the capture hangs until the empty-transfer timeout aborts. Stash the FPGA-actual count in dev_context.actual_samples and use it (when non-zero) as the acquisition-stop budget. Falls back to limit_samples for non-RLE captures. Gate the shortening to !continuous_mode. In streaming mode remain_cnt is an in-flight "samples remaining to send" counter that updates continuously as the FPGA streams; subtracting it from limit_samples gives a meaningless small number that would end streaming captures almost immediately on the first trigger header. In streaming mode let limit_samples remain the budget.
Wire libsigrok's sr_session_trigger to the V2 DSL_setting trig_*
fields and enable the FPGA's trigger comparator. Before this patch
the V2 driver had a no-op v2_set_trigger stub and hardcoded
"always-true" trig defaults, so -t flags were silently dropped and
captures started immediately on arm.
Encoding mirrors DSView's SIMPLE_TRIGGER path:
- v2_encode_trigger walks the session trigger stages and packs
per-channel ZERO/ONE/RISING/FALLING/EDGE matches into
trig_mask0/value0/edge0[0] (mirrored to the *1 halves), with
unused stages left as mask=0xffff/logic=2 ("always true").
- trig_glb gets the enabled-channel count in its upper 5 bits and
(num_stages - 1) in the low byte.
- trig_pos is computed from capture_ratio (percentage of
limit_samples that should sit before the trigger), clamped to
10% in streaming mode and 90% in buffered mode and aligned to
the FPGA's 64-sample atomic unit.
- mode bit 0 (TRIG_EN) is set when at least one stage has matches.
Without this bit the FPGA ignores the trig_* fields entirely;
this was the missing piece that made the first iteration look
like the encoding was wrong.
Verified end-to-end: 100 MHz buffered RLE on channels 0..3 with
-t '3=f' (CS-falling) captures the SX1276's actual SPI traffic,
sigrok_pd:chip=sx1276 decodes 21 register transactions including
Burst R/W RegOpmode and FIFO reads in a 0.1s window.
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds real trigger encoding to the V2 (DSLogic Plus Pango) path. Before this,
v2_set_triggerwas a no-op stub and theDSL_settingtrig_*fields were hardcoded to "always true" — so-tflags on V2 devices were silently dropped and captures started immediately on arm. With this,sigrok-cli -t '3=f'(CS-falling-edge) works the same way it does in DSView.Stacked on #292 (and transitively #291). The diff here will narrow to one commit once the bases land.
What's in this PR (1 commit)
V2 simple-trigger encoding — walks
sr_session_trigger_get()'s stages, packs per-channel matches (ZERO / ONE / RISING / FALLING / EDGE) into stage 0 of theDSL_setting'strig_mask0/value0/edge0(mirrored tomask1/value1/edge1), and enables the FPGA's trigger comparator via mode bit 0 (TRIG_EN). WithoutTRIG_EN, the FPGA ignores thetrig_*fields entirely; this was the missing piece behind the first iteration looking like the encoding was wrong.Trigger position from
capture_ratio, aligned to the FPGA's 64-sample atomic unit, clamped to 10% in streaming mode and 90% in buffered mode (matches DSView'sDS_MAX_TRIG_PERCENT).trig_glbpacks the enabled-channel count + stage count per DSView's layout.Encoding mirrors DSView's
SIMPLE_TRIGGERpath: only stage 0 carries the user condition; remaining stages stay asmask=0xffff/logic=2("always true"). Sufficient for the single-condition / single-channel triggerssigrok-cli's-texposes (SR_TRIGGER_MATCHlist in the driver advertises ZERO / ONE / RISING / FALLING / EDGE).Verification
End-to-end: 100 MHz buffered RLE on channels 0..3 with
-t '3=f'(CS-falling) captures the actual SPI traffic on a live SX1276 bus. With a plainspidecoder downstream, transactions decode cleanly — burst register reads/writes and FIFO accesses across the post-trigger window.Test plan
makebuilds cleansigrok-cli -d dreamsourcelab-dslogic -C 0,1,2,3 -c samplerate=100000000 -c rle=on --samples 10000000 -t '3=f' -O bitswaits for the trigger then captures-t '0=r',-t '0=f',-t '0=0',-t '0=1',-t '0=e'each gate correctly on the corresponding edge / level / change