`timescale 1ns / 1ps // Record a finite window from the first matching sample, then read it out. // Input samples are already synchronized to clk. Only the readout can stall. module la_trigger_buffer #( parameter integer ADDR_WIDTH = 16, parameter integer EXTERNAL_TRIGGER = 0 )( input wire clk, reset, arm, stop, input wire [7:0] sample, input wire [16:0] divider, input wire [ADDR_WIDTH-1:0] last_address, input wire [ADDR_WIDTH-1:0] pretrigger, input wire use_external, external_hit, output wire trigger_tick, input wire [7:0] level_mask, level_value, edge_mask, edge_value, any_edge_mask, output reg capturing, output wire stored, output reg data_valid, output reg [7:0] data, input wire ready ); reg [16:0] period, timer; reg [ADDR_WIDTH-1:0] write_address, read_address, pre, history, post_left, read_left; reg [7:0] levels, values, directed, direction, edges, previous; reg previous_valid, triggered, external_mode; reg [1:0] read_pending; wire [7:0] memory_data; wire sample_tick = capturing && timer == 0 && !reset && !stop && !arm; wire match_level = (sample & levels) == values; wire match_edge = edges == 0 || (previous_valid && ((sample ^ previous) & edges) == edges && (sample & directed) == direction); assign trigger_tick = sample_tick && !triggered && history == 0; wire fire = trigger_tick && (external_mode ? external_hit : match_level && match_edge); assign stored = sample_tick && (triggered || fire); reg write_pending; reg [ADDR_WIDTH-1:0] pending_address; reg [7:0] pending_sample; always @(posedge clk) begin write_pending <= sample_tick && (pre != 0 || triggered || fire); pending_address <= write_address; pending_sample <= sample; end la_capture_buffer #(.SAMPLE_WIDTH(8), .ADDR_WIDTH(ADDR_WIDTH)) memory ( .wr_clk(clk), .wr_en(write_pending && !reset), .wr_addr(pending_address), .wr_data(pending_sample), .rd_clk(clk), .rd_addr(read_address), .rd_data(memory_data) ); always @(posedge clk) begin if (reset || stop) begin capturing <= 0; data_valid <= 0; read_pending <= 0; period <= 0; timer <= 0; pre <= 0; history <= 0; post_left <= 0; read_left <= 0; write_address <= 0; read_address <= 0; levels <= 0; values <= 0; directed <= 0; direction <= 0; edges <= 0; previous <= 0; previous_valid <= 0; triggered <= 0; external_mode <= 0; end else if (arm) begin capturing <= 1; data_valid <= 0; read_pending <= 0; period <= divider - 1'b1; timer <= 0; pre <= EXTERNAL_TRIGGER && use_external ? pretrigger : 0; history <= EXTERNAL_TRIGGER && use_external ? pretrigger : 0; post_left <= last_address - (EXTERNAL_TRIGGER && use_external ? pretrigger : 0); read_left <= last_address; write_address <= 0; read_address <= 0; levels <= level_mask; values <= level_value & level_mask; directed <= edge_mask; direction <= edge_value & edge_mask; edges <= edge_mask | any_edge_mask; previous_valid <= 0; triggered <= 0; external_mode <= EXTERNAL_TRIGGER && use_external; end else if (capturing) begin if (sample_tick) begin timer <= period; previous <= sample; previous_valid <= 1; if (history != 0) history <= history - 1'b1; if (pre != 0 || triggered || fire) write_address <= write_address + 1'b1; if (fire) begin triggered <= 1; read_address <= write_address - pre; if (post_left == 0) begin capturing <= 0; read_pending <= 3; end end else if (triggered) begin post_left <= post_left - 1'b1; if (post_left == 1) begin capturing <= 0; read_pending <= 3; end end end else begin timer <= timer - 1'b1; end end else if (read_pending != 0) begin read_pending <= read_pending - 1'b1; // Keep the BRAM read mux out of the downstream FIFO write path. if (read_pending == 1) begin data <= memory_data; data_valid <= 1; end end else if (data_valid && ready) begin data_valid <= 0; if (read_left != 0) begin read_left <= read_left - 1'b1; read_address <= read_address + 1'b1; read_pending <= 2; end end end endmodule