From 08c0f6967bc30beb1914678152a0ff9df16cfbc3 Mon Sep 17 00:00:00 2001 From: Joppe Blondel Date: Thu, 5 Mar 2026 16:24:29 +0100 Subject: [PATCH] Trigger selection --- .../signal_scope/rtl/signal_scope_q15.v | 65 +++++++++++++------ .../signal/signal_scope/tool/capture_plot.py | 15 ++++- 2 files changed, 59 insertions(+), 21 deletions(-) diff --git a/cores/signal/signal_scope/rtl/signal_scope_q15.v b/cores/signal/signal_scope/rtl/signal_scope_q15.v index 8425655..7d2fc1f 100644 --- a/cores/signal/signal_scope/rtl/signal_scope_q15.v +++ b/cores/signal/signal_scope/rtl/signal_scope_q15.v @@ -30,9 +30,10 @@ module signal_scope_q15 #( reg scope_armed; reg scope_triggered; reg capture_done; + reg [1:0] trigger_channel; reg [15:0] trig_val; - reg [15:0] signal_a_prev; - reg signal_a_prev_valid; + reg [15:0] trigger_prev; + reg trigger_prev_valid; reg [15:0] signal_a; reg [15:0] signal_b; @@ -54,6 +55,8 @@ module signal_scope_q15 #( wire [aw-1:0] wb_mem_idx = wb_adr[aw+2:3]; wire wb_is_reg = (wb_adr[31:28] == reg_base_addr[31:28]); wire [3:0] wb_reg_idx = wb_adr[5:2]; + reg [15:0] trigger_sample; + reg trigger_sample_valid; jtag_wb_bridge #( .chain(chain), @@ -72,6 +75,27 @@ module signal_scope_q15 #( .o_cmd_reset() ); + always @(*) begin + case(trigger_channel) + 2'd0: begin + trigger_sample = i_signal_a; + trigger_sample_valid = i_signal_valid_a; + end + 2'd1: begin + trigger_sample = i_signal_b; + trigger_sample_valid = i_signal_valid_b; + end + 2'd2: begin + trigger_sample = i_signal_c; + trigger_sample_valid = i_signal_valid_c; + end + default: begin + trigger_sample = i_signal_d; + trigger_sample_valid = i_signal_valid_d; + end + endcase + end + always @(posedge i_clk) begin if(i_rst) begin counter <= {aw{1'b0}}; @@ -81,11 +105,12 @@ module signal_scope_q15 #( scope_armed <= 1'b0; scope_triggered <= 1'b0; capture_done <= 1'b0; + trigger_channel <= 2'd0; wb_ack <= 1'b0; wb_rdt <= 32'b0; trig_val <= 16'h0000; - signal_a_prev <= 16'h0000; - signal_a_prev_valid <= 1'b0; + trigger_prev <= 16'h0000; + trigger_prev_valid <= 1'b0; signal_a <= 0; signal_b <= 0; signal_c <= 0; @@ -100,18 +125,6 @@ module signal_scope_q15 #( if(i_signal_valid_a) begin signal_a <= i_signal_a; signal_a_pending <= 1'b1; - - // Trigger on signal_a rising across trig_val. - if(scope_armed && trigger_enable && !count_enable) begin - if(signal_a_prev_valid && - ($signed(signal_a_prev) < $signed(trig_val)) && - ($signed(i_signal_a) >= $signed(trig_val))) begin - count_enable <= 1'b1; - scope_triggered <= 1'b1; - end - signal_a_prev <= i_signal_a; - signal_a_prev_valid <= 1'b1; - end end if(i_signal_valid_b) begin signal_b <= i_signal_b; @@ -126,6 +139,18 @@ module signal_scope_q15 #( signal_d_pending <= 1'b1; end + // Trigger on selected channel rising across trig_val. + if(scope_armed && trigger_enable && !count_enable && trigger_sample_valid) begin + if(trigger_prev_valid && + ($signed(trigger_prev) < $signed(trig_val)) && + ($signed(trigger_sample) >= $signed(trig_val))) begin + count_enable <= 1'b1; + scope_triggered <= 1'b1; + end + trigger_prev <= trigger_sample; + trigger_prev_valid <= 1'b1; + end + // Arm/rearm capture. If trigger is disabled, start capture immediately. if(arm_req) begin counter <= {aw{1'b0}}; @@ -133,7 +158,7 @@ module signal_scope_q15 #( scope_armed <= 1'b1; scope_triggered <= !trigger_enable; capture_done <= 1'b0; - signal_a_prev_valid <= 1'b0; + trigger_prev_valid <= 1'b0; signal_a_pending <= 1'b0; signal_b_pending <= 1'b0; signal_c_pending <= 1'b0; @@ -178,6 +203,8 @@ module signal_scope_q15 #( arm_req <= 1'b1; // Bit 1: trigger enable. trigger_enable <= wb_dat[1]; + // Bits [3:2]: trigger channel (0=a,1=b,2=c,3=d). + trigger_channel <= wb_dat[3:2]; end end reg_trig_val: begin @@ -191,8 +218,8 @@ module signal_scope_q15 #( end else begin if(wb_is_reg) begin case(wb_reg_idx) - // [1]=trigger_enable, [0]=arm bit is write-pulse only. - reg_control: wb_rdt <= {30'b0, trigger_enable, 1'b0}; + // [3:2]=trigger_channel, [1]=trigger_enable, [0]=arm(write pulse only/read 0). + reg_control: wb_rdt <= {28'b0, trigger_channel, trigger_enable, 1'b0}; // [0]=triggered, [1]=capturing, [2]=armed, [3]=done reg_status: wb_rdt <= {28'b0, capture_done, scope_armed, count_enable, scope_triggered}; reg_trig_val: wb_rdt <= {16'b0, trig_val}; diff --git a/cores/signal/signal_scope/tool/capture_plot.py b/cores/signal/signal_scope/tool/capture_plot.py index e15abee..e9b1689 100755 --- a/cores/signal/signal_scope/tool/capture_plot.py +++ b/cores/signal/signal_scope/tool/capture_plot.py @@ -53,6 +53,12 @@ def parse_args() -> argparse.Namespace: default=None, help="Optional trigger threshold (16-bit, signal_a rising crossing). If omitted, triggering is disabled.", ) + parser.add_argument( + "--trigger-channel", + choices=["a", "b", "c", "d"], + default="a", + help="Trigger source channel when triggering is enabled", + ) parser.add_argument( "--unsigned", action="store_true", @@ -81,15 +87,20 @@ def parse_args() -> argparse.Namespace: def capture_once(bridge, args: argparse.Namespace) -> list[tuple[int, int, int, int]]: samples = [] frame_count = args.depth + trigger_channel_map = {"a": 0, "b": 1, "c": 2, "d": 3} + trigger_channel = trigger_channel_map[args.trigger_channel] if args.trigger_value is None: print("[signal_scope] Arming scope with trigger disabled...") bridge.write32(REG_CONTROL, 0x1) # bit0: arm pulse, bit1: trigger enable=0 else: trig_val = args.trigger_value & 0xFFFF - print(f"[signal_scope] Config trigger: trig_val=0x{trig_val:04x}, source=signal_a rising") + print( + f"[signal_scope] Config trigger: trig_val=0x{trig_val:04x}, " + f"source=signal_{args.trigger_channel} rising" + ) bridge.write32(REG_TRIG_VAL, trig_val) print("[signal_scope] Arming scope with trigger enabled...") - bridge.write32(REG_CONTROL, 0x3) # bit0: arm pulse, bit1: trigger enable + bridge.write32(REG_CONTROL, 0x3 | (trigger_channel << 2)) # bit0: arm, bit1: trig_en, bits[3:2]: channel # Wait until the new arm command is active, then wait for its trigger event. while (bridge.read32(REG_STATUS) & 0x4) == 0: