Files
templates/hdl/scope-acquisition/rtl/trigger_engine.sv
T

122 lines
4.7 KiB
Systemverilog

`timescale 1ns/1ps
// Trigger decision is combinational for the currently accepted sample; the
// acquisition buffer can write that same sample as its trigger record.
module trigger_engine (
input wire clk,
input wire rst,
input wire arm,
input wire stop,
input wire session_fault,
input wire [2:0] cfg_source,
input wire [1:0] cfg_mode,
input wire cfg_falling,
input wire [9:0] cfg_level,
input wire [9:0] cfg_hysteresis,
input wire [31:0] cfg_auto_timeout,
input wire [63:0] sample_data,
input wire [63:0] sample_index,
input wire sample_valid,
input wire ext_level,
input wire eligible,
output wire trigger_hit,
output wire auto_hit,
output reg armed,
output reg [63:0] trigger_index,
output reg config_error
);
reg [2:0] active_source;
reg [1:0] active_mode;
reg active_falling;
reg [9:0] active_level, active_hysteresis;
reg [31:0] active_timeout, accepted_count;
reg crossing_armed;
reg ext_seen, ext_previous;
reg [9:0] selected_sample;
wire [10:0] high_sum = {1'b0, active_level} + {1'b0, active_hysteresis};
wire [9:0] high_threshold = high_sum > 11'd1023 ? 10'd1023 : high_sum[9:0];
wire [9:0] low_threshold = active_level > active_hysteresis ?
active_level - active_hysteresis : 10'd0;
always @* begin
case (active_source)
3'd0: selected_sample = sample_data[9:0];
3'd1: selected_sample = sample_data[25:16];
3'd2: selected_sample = sample_data[41:32];
3'd3: selected_sample = sample_data[57:48];
default: selected_sample = 10'd0;
endcase
end
wire analog_arm_condition = active_falling ?
(active_hysteresis == 0 ? selected_sample > active_level : selected_sample >= high_threshold) :
(active_hysteresis == 0 ? selected_sample < active_level : selected_sample <= low_threshold);
wire analog_cross_condition = active_falling ?
selected_sample <= low_threshold : selected_sample >= high_threshold;
wire analog_event = crossing_armed && analog_cross_condition;
wire external_event = ext_seen && (active_falling ?
(ext_previous && !ext_level) : (!ext_previous && ext_level));
wire edge_event = active_source == 3'd4 ? external_event : analog_event;
// Counter describes preceding accepted samples. N expires on sample N;
// zero expires immediately, but fallback still waits for eligibility.
wire auto_expired = active_timeout == 0 || accepted_count >= active_timeout - 1'b1;
wire auto_event = active_mode == 2'd0 && auto_expired;
assign trigger_hit = !rst && !arm && !stop && !session_fault && armed &&
sample_valid && eligible && (edge_event || auto_event);
assign auto_hit = trigger_hit && !edge_event && auto_event;
always @(posedge clk) begin
if (rst) begin
armed <= 0;
trigger_index <= 0;
config_error <= 0;
active_source <= 0;
active_mode <= 0;
active_falling <= 0;
active_level <= 0;
active_hysteresis <= 0;
active_timeout <= 0;
accepted_count <= 0;
crossing_armed <= 0;
ext_seen <= 0;
ext_previous <= 0;
end else if (stop || session_fault) begin
armed <= 0;
accepted_count <= 0;
crossing_armed <= 0;
ext_seen <= 0;
end else if (arm) begin
active_source <= cfg_source;
active_mode <= cfg_mode;
active_falling <= cfg_falling;
active_level <= cfg_level;
active_hysteresis <= cfg_hysteresis;
active_timeout <= cfg_auto_timeout;
armed <= (cfg_source <= 3'd4) && (cfg_mode <= 2'd2);
if (cfg_source > 3'd4 || cfg_mode > 2'd2)
config_error <= 1'b1;
trigger_index <= 0;
accepted_count <= 0;
crossing_armed <= 0;
ext_seen <= 0;
ext_previous <= 0;
end else if (armed && sample_valid) begin
if (accepted_count != 32'hffffffff)
accepted_count <= accepted_count + 1'b1;
if (active_source == 3'd4) begin
ext_previous <= ext_level;
ext_seen <= 1'b1;
end else if (analog_event) begin
// Consume the crossing even before sufficient prehistory;
// eligibility becoming true later must not replay this edge.
crossing_armed <= 1'b0;
end else if (analog_arm_condition) begin
crossing_armed <= 1'b1;
end
if (trigger_hit) begin
armed <= 1'b0;
trigger_index <= sample_index;
end
end
end
endmodule