//------------------------------------------------------------------
// Logic Analyzer Component 
//------------------------------------------------------------------

module lac #(
	parameter       uart_freq_hz   =   100000000,
	parameter       uart_baud      =   115200,
	parameter       adr_width      =   11,
	parameter       width          =   8
) (
	input              reset,
	input              uart_clk,
	input              uart_rxd,
	output             uart_cts,
	output             uart_txd,
	input              uart_rts,
	// actual probe input
	input              probe_clk,
	input  [width-1:0] probe,
	output reg   [7:0] select
);

parameter cmd_nop      = 8'h20;
parameter cmd_arm      = 8'h01;
parameter cmd_disarm   = 8'h02;

//------------------------------------------------------------------
// uart instantiation
//------------------------------------------------------------------
assign uart_cts = 1;

reg          tx_wr;
wire         tx_busy;
reg    [7:0] tx_data;

wire   [7:0] rx_data;
wire         rx_avail;
reg          rx_ack;

uart #(
	.freq_hz( uart_freq_hz ),
	.baud(    uart_baud    )
) uart0 (
	.reset(       reset         ),
	.clk(         uart_clk      ),
	// UART
	.uart_txd(    uart_txd      ),
	.uart_rxd(    uart_rxd      ),
	//
	.rx_data(     rx_data       ),
	.rx_avail(    rx_avail      ),
	.rx_error(    rx_error      ),
	.rx_ack(      rx_ack        ),
	.tx_data(     tx_data       ),
	.tx_wr(       tx_wr         ),
	.tx_busy(     tx_busy       )
);

//------------------------------------------------------------------
// handshake signal between clock domains
//------------------------------------------------------------------
reg            [7:0] trig_mask;
reg            [7:0] trig_cond;
reg            [7:0] trig_pre;

reg  [adr_width-1:0] start_adr;   // set in probe_clk domain

reg                  armed;       // set in uart_clk domain
reg                  triggered;   // set in probe_clk domain

//------------------------------------------------------------------
// uart state machine
//------------------------------------------------------------------
wire   rx_req;
assign rx_req = rx_avail & ~rx_ack;

reg                  triggered_synced;
wire     [width-1:0] read_dat;
reg  [adr_width-1:0] read_adr;
wire [adr_width-1:0] read_adr_next;

assign read_adr_next = read_adr + 1;

reg [2:0] state;

parameter s_idle       = 0;
parameter s_read_select= 1;
parameter s_read_mask  = 2;
parameter s_read_comp  = 3;
parameter s_read_pre   = 4;
parameter s_triggered  = 5;
parameter s_send_byte  = 6;

always @(posedge uart_clk)
begin
	if (reset) begin
		state     <= s_idle;
		select    <= 0;
		armed     <= 0;
		tx_wr     <= 0;
	end else begin
		triggered_synced <= triggered;

		rx_ack <= 0;     // default until set otherwise
		tx_wr  <= 0;

		case (state)
		s_idle: begin

			if (rx_req) begin
				case (rx_data)
				cmd_arm: begin
					rx_ack <= 1;
					state  <= s_read_select;
				end
				cmd_disarm: begin
					rx_ack <= 1;
					armed  <= 0;
				end
				default: begin
					rx_ack <= 1;
				end
				endcase
			end

			if (~rx_req && triggered_synced) begin
				state  <= s_triggered;
			end
		end
		s_read_select: begin
			if (rx_req) begin
				rx_ack    <= 1;
				select    <= rx_data;
				state     <= s_read_mask;
			end
		end
		s_read_mask: begin
			if (rx_req) begin
				rx_ack    <= 1;
				trig_mask <= rx_data;
				state     <= s_read_comp;
			end
		end
		s_read_comp: begin
			if (rx_req) begin
				rx_ack    <= 1;
				trig_cond <= rx_data;
				armed     <= 1;
				state     <= s_read_pre;
			end
		end
		s_read_pre: begin
			if (rx_req) begin
				rx_ack    <= 1;
				trig_pre  <= rx_data;
				armed     <= 1;
				state     <= s_idle;
			end
		end
		s_triggered: begin
			armed    <= 0;
			read_adr <= start_adr;
			tx_data  <= adr_width;
			tx_wr    <= 1;
			state    <= s_send_byte;
		end
		s_send_byte: begin
			tx_wr <= 0;

			if (~tx_busy & ~tx_wr) begin
				if (read_adr_next == start_adr)
					state    <= s_idle;

				read_adr <= read_adr_next;
				tx_data  <= read_dat;
				tx_wr    <= 1;
			end
		end
		default:
			state    <= s_idle;
		endcase
	end
end

//------------------------------------------------------------------
// Sampling clock domain
//------------------------------------------------------------------

// register probe input for better F_max
reg        [width-1:0] probe_r;    

always @(posedge probe_clk)
	probe_r <= probe;

// Sampling machinery
reg                    armed_synced;
reg                    armed_synced2;
reg                    sampling;

reg    [adr_width-1:0] write_adr;
wire   [adr_width-1:0] next_adr;
assign                 next_adr = write_adr + 1;

wire    cond_match;
assign  cond_match = (probe_r & trig_mask) == (trig_cond & trig_mask) && armed_synced2;

always @(posedge probe_clk)
begin
	if (reset) begin
		armed_synced  <= 0;
		armed_synced2 <= 0;
		sampling      <= 0;
		triggered     <= 0;
		write_adr     <= 0;
	end else begin
		armed_synced  <= armed;
		armed_synced2 <= armed_synced;
	
		if (armed_synced2 || sampling) begin
			write_adr <= next_adr;
		end

		if (~triggered && armed_synced2) begin
			if (cond_match) begin
				sampling  <= 1;
				triggered <= 1;
				start_adr <= write_adr;
			end
		end
		
		if (sampling && next_adr == start_adr) begin
			sampling <= 0;
		end

		if (~sampling && ~armed_synced2 && triggered) 
			triggered <= 0;
	end
end


//------------------------------------------------------------------
// dual port memory
//------------------------------------------------------------------
wire   write_en;
assign write_en = sampling || cond_match;

dp_ram #(
	.adr_width( adr_width ),
	.dat_width(     width )
) ram0 (
	// read port a 
	.clk_a(     uart_clk    ),
	.adr_a(     read_adr    ),
	.dat_a(     read_dat    ),
	// write port b
	.clk_b(     probe_clk   ),
	.adr_b(     write_adr   ),
	.dat_b(     probe_r     ),
	.we_b(      write_en    )
);

endmodule