`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 10/24/2023 01:55:27 PM
// Design Name: 
// Module Name: controller
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////
//
//  this module assumes that in the level above, there's a FIFO that buffers all
//  UART receive transmissions.
//
//  the byte receive in the rx_fifo_data input to this module
//  contains the relevant data to decode:
//
//  to make things easier, the uart rx instruction will be 1 byte but the data coming
//  into the FPGA or out of the FPGA will be 2 bytes.   
//
//  control register: we probably only need 8 bits.  will pack the LSB to MSB
//  
//  status register:  ditto
//
//  threshold register:  the data is only 12 bits long and we likely won't
//      need a threshold that is in the first 4 bits so we really only need 8 bits.
//      so we will use the bottom 8 bits of the threshold data sent and compare that
//      to the upper 8 bits of the ADC data
//
//  prefill register: use all bits,  to get the trigger to land in the middle of the
//      fifo, set this to 16'hFFFF FFFE; (that is all but the LSB set)
//
//  bit 0 asserted means that the RPi is sending instructions for what data it will be sending
//      next so that the FPGA knows where to put it
//  if bit 0 = 1, then the next bits tell the FPGA what data is coming in:
//  bits 2:0 =
//      001     control register (16 bits, but probably don't need very many)
//      011     threshold register (16 bits, but probably only the upper 8 bits are necessary)
//      101     prefill register (16 bits)
//
//  if bit 0 = 0 then the RPi is reading from the FPGA, so the FPGA has to transmit
//  bits 7:0 =
//      7654 3210
//      0000 0000    control register
//      0000 0010    status register 
//      0000 0100    threshold register
//      0000 0110    prefill register
//      0000 1000    r_adc_data
//      0000 1010    firmware version
//      0000 1100    test vector
//      0001 0000    16 bits of data from the ADC fifo.  note that the upper 12 bits will
//                   be the ADC data, and we will pack the bottom 4 bits with information
//                   that will tell us things about the status and use the LSB to tell the
//                   RPi that the FIFO is empty
//      0001 0010    16 bits of fifo data count (not really needed by the RPi but....)
//                   any other value will cause the FPGA to send back 16'hdeadbeef as an error indicator
//

module controller(
    input clock,
    input reset,
    input rx_fifo_empty,
    input [7:0] rx_fifo_data,
    output rx_fifo_rd,
    input tx_done,
    output reg tx_transmit,
    output reg [15:0] tx_out,
    input [15:0] firmware_version,
    input [15:0] test_vector,
    input [15:0] r_adc_data,
    output data_fifo_rd_en,
    output data_fifo_wr_en,
    input data_fifo_empty,
    input data_fifo_full,
    input [12:0] data_fifo_data,        // MSB is triggered, next 12 bits are ADC
    input [15:0] data_fifo_count,
    output [15:0] led,
    output triggered,
    output [31:0] debug
    );
    //
    //  control register.  bits are:
    //  0   set to 1 to enable writing into the data FIFO
    //  1   set to 1 to enable comparing incoming data to the trigger threshold
    //
    reg [15:0] control;
    wire global_start = control[0];
    wire trigger_start = control[1];
    //
    //  threshold and prefill registers
    //  for threshold triggering, compare the 12 bits of ADC data in r_adc_data
    //  to the bottom 12 bits of threshold
    //
    reg [15:0] threshold;
    wire trigger = trigger_start && (r_adc_data[15:4] > threshold[11:0]);
    assign triggered = trigger;
    reg [15:0] prefill;
    //
    //  status register.  bits are:
    //  0   global start enabled
    //  1   trigger start enabled
    //  2   rx fifo empty
    //  3   data fifo empty
    //  4   data fifo full
    //  5   event triggered
    //  
    wire [15:0] status = {trigger,data_fifo_full,data_fifo_empty,rx_fifo_empty,trigger_start,global_start};
    //
    //  here is the rx state machine, it looks at the rx fifo empty flag to know if it has
    //  anything to process
    //
    wire incoming = ~rx_fifo_empty;  // this is the signal that the fifo has something in it to process
    localparam [2:0] RX_WAIT=0, RX_READ_EN1=1, RX_READ_EN0=2,
        RX_LATCH_FIFO=3, RX_BRANCH=4, RX_INCOMING=5, RX_OUTGOING=6;
    reg [2:0] rx_state;
    reg [7:0] rx_instructions;
    reg do_read_fifo, do_incoming, do_outgoing;
    wire read_fifo_done, incoming_done, outgoing_done;
    wire branch_to_incoming = rx_instructions[0];       // incoming or outgoing
    wire [1:0] incoming_what = rx_instructions[2:1];    // what incoming is coming in
    wire [3:0] outgoing_what = rx_instructions[3:0];    // what is going out
    wire out_from_data_fifo = rx_instructions[4];
    always @ (posedge clock)
        if (reset) begin
            rx_state <= RX_WAIT;
            do_read_fifo <= 0;
            do_incoming <= 0;
            do_outgoing <= 0;
            rx_instructions <= 0;
        end
        else case (rx_state)
            RX_WAIT: begin
                rx_instructions <= 0;
                do_read_fifo <= 0;
                do_incoming <= 0;
                do_outgoing <= 0;
                rx_state <= incoming ? RX_READ_EN1 : RX_WAIT;
            end
            RX_READ_EN1: begin
                do_read_fifo <= 1;
                rx_state <= RX_READ_EN0;
            end
            RX_READ_EN0: begin
                do_read_fifo <= 0;
                rx_state <= RX_LATCH_FIFO;
            end
            RX_LATCH_FIFO: begin
                rx_instructions <= rx_fifo_data;
                rx_state <= RX_BRANCH;
            end
            RX_BRANCH: begin
                rx_state <= branch_to_incoming ? RX_INCOMING : RX_OUTGOING;
            end
            RX_INCOMING: begin
                do_incoming <= 1;
                rx_state <= incoming_done ? RX_WAIT : RX_INCOMING;
            end
            RX_OUTGOING: begin
                do_outgoing <= 1;
                rx_state <= outgoing_done ? RX_WAIT : RX_OUTGOING;
            end
            default: begin
                rx_state <= RX_WAIT;
            end
        endcase
    //
    //  here is the "incoming" FSM that responds to the RPi sending data to the FPGA
    //
    localparam [3:0] IN_WAIT=0, IN_WAIT_NOT_EMPTY1=1, IN_READ1_EN1=2, IN_READ1_EN0=3, IN_READ1_LATCH=4,
        IN_WAIT_NOT_EMPTY2=5, IN_READ2_EN1=6, IN_READ2_EN0=7, IN_READ2_LATCH=8, IN_DONE=9;
    reg [3:0] in_state;
    reg in_read_fifo;
    reg in_done;
    reg [15:0] in_data;
    always @ (posedge clock)
        if (reset) begin
            in_state <= IN_WAIT;
            in_read_fifo <= 0;
            in_data <= 0;
            in_done <= 0;
        end
        else case (in_state) 
            IN_WAIT: begin
                in_read_fifo <= 0;
                in_data <= 0;
                in_done <= 0;
                in_state <= do_incoming ? IN_WAIT_NOT_EMPTY1 : IN_WAIT;
            end
            IN_WAIT_NOT_EMPTY1: begin
                in_state <= incoming ? IN_READ1_EN1 : IN_WAIT_NOT_EMPTY1;
            end
            IN_READ1_EN1: begin
                in_read_fifo <= 1;
                in_state <= IN_READ1_EN0;
            end
            IN_READ1_EN0: begin
                in_read_fifo <= 0;
                in_state <= IN_READ1_LATCH;
            end
            IN_READ1_LATCH: begin
                in_data[7:0] <= rx_fifo_data;
                in_state <= IN_WAIT_NOT_EMPTY2;
            end
            IN_WAIT_NOT_EMPTY2: begin
                in_state <= incoming ? IN_READ2_EN1 : IN_WAIT_NOT_EMPTY2;
            end
            IN_READ2_EN1: begin
                in_read_fifo <= 1;
                in_state <= IN_READ2_EN0;
            end
            IN_READ2_EN0: begin
                in_read_fifo <= 0;
                in_state <= IN_READ2_LATCH;
            end
            IN_READ2_LATCH: begin
                in_data[15:8] <= rx_fifo_data;
                in_state <= IN_DONE;
            end
            IN_DONE: begin
              in_done <= 1;
              case (incoming_what)
                2'b00: control <= in_data;
                2'b01: threshold <= in_data;
                2'b10: prefill <= in_data;
                2'b11: in_data <= in_data;  // should be an error condition!!!
              endcase
              in_state <= do_incoming ? IN_DONE : IN_WAIT;
            end
        endcase
    assign incoming_done = in_done;
    //
    //  rx_fifo_read driver
    //
    assign rx_fifo_rd = do_read_fifo | in_read_fifo;
    //
    //  now make the state machine to handle sending 16 bit words to the RPi
    //    
    localparam [3:0] OUT_WAIT=0, OUT_CHECK=1, OUT_LATCH=2, OUT_FIFO1=3, OUT_FIFO0=4,
        OUT_FIFO_LATCH=5, OUT_TRANSMIT=6, OUT_TRANSMIT_WAIT1=7, OUT_TRANSMIT_WAIT2=8,
        OUT_DONE=9;
    reg [3:0] out_state;
    reg out_done;
    reg out_read_fifo;
    wire fifo_last = (data_fifo_count == 16'h1);
    always @ (posedge clock)
        if (reset) begin
            out_state <= OUT_WAIT;
            tx_transmit <= 0;
            out_done <= 0;
            tx_out <= 0;
            out_read_fifo <= 0;
        end
        else case (out_state)
            OUT_WAIT: begin
                tx_transmit <= 0;
                tx_out <= 0;
                out_done <= 0;
                out_read_fifo <= 0;
                out_state <= do_outgoing ? OUT_CHECK : OUT_WAIT;
            end
            OUT_CHECK: begin
                //
                //  check if we are going to send data from the ADC FIFO
                //
                out_state <= out_from_data_fifo ? OUT_FIFO1 : OUT_LATCH;
            end
            OUT_FIFO1: begin
                //
                //  issue a fifo read and then latch the result
                //
                out_read_fifo <= 1;
                out_state <= OUT_FIFO0;     
            end
            OUT_FIFO0: begin
                out_read_fifo <= 0;
                out_state <= OUT_FIFO_LATCH;
            end
            OUT_FIFO_LATCH: begin
                case (outgoing_what)
                    //
                    //  for sending back data, pack the upper 12 bits with adc value and the next 4 bits
                    //  with triggered,global_start,data_fifo_empty,fifo_last
                    //  
                    //  the RPi will look at the LSB fifo_last to know if this is the last word in the fifo
                    //
                    4'b0000: tx_out <= {data_fifo_data[11:0],data_fifo_data[12],global_start,data_fifo_empty,fifo_last};
                    4'b0001: tx_out <= data_fifo_count;
                    default: tx_out <= tx_out;
                endcase
                out_state <= OUT_TRANSMIT;
            end
            OUT_LATCH: begin
//      7654 3210
//      0000 0000    control register
//      0000 0010    status register 
//      0000 0100    threshold register
//      0000 0110    prefill register
//      0000 1000    r_adc_data
//      0000 1010    firmware version
//      0000 1100    test vector
                case (outgoing_what)
                    4'b0000: tx_out <= control;
                    4'b0010: tx_out <= status;
                    4'b0100: tx_out <= threshold;
                    4'b0110: tx_out <= prefill;
                    4'b1000: tx_out <= r_adc_data;
                    4'b1010: tx_out <= firmware_version;
                    4'b1100: tx_out <= test_vector;
                    default: tx_out <= tx_out;
                endcase
                out_state <= OUT_TRANSMIT;
            end
            OUT_TRANSMIT: begin
                tx_transmit <= 1;
                out_state <= OUT_TRANSMIT_WAIT1;
            end
            OUT_TRANSMIT_WAIT1: begin
                tx_transmit <= 0;
                out_state <= tx_done ? OUT_TRANSMIT_WAIT2 : OUT_TRANSMIT_WAIT1 ;
            end
            OUT_TRANSMIT_WAIT2: begin
                out_state <= tx_done ? OUT_DONE : OUT_TRANSMIT_WAIT2;                
            end
            OUT_DONE: begin
                out_done <= 1;
                out_state <= do_outgoing ? OUT_DONE : OUT_WAIT;                
            end
        endcase
    assign outgoing_done = out_done;
    //
    //  here is a state machine that takes care of filling the data fifo
    //
    reg [2:0] f_state;
    localparam [2:0] DF_WAIT=0, DF_PREFILL=1, DF_WAIT_TRIGGER=2,
        DF_WAIT_FULL=3, DF_WAIT_EMPTY=4, DF_READ1=5, DF_WAIT_ACK=6;
    reg df_rd_en, df_wr_en;
    reg [15:0] count_fill;
    wire counted = count_fill == prefill;
    always @ (posedge clock) begin
        if (reset) begin
            count_fill <= 0;
            df_rd_en <= 0;
            df_wr_en <= 0;
            f_state <= DF_WAIT;
        end
        else case (f_state)
            DF_WAIT: begin
                //
                //  wait for global_start
                //
                count_fill <= 0;
                df_rd_en <= 0;
                df_wr_en <= 0;
                f_state <= global_start ? DF_PREFILL : DF_WAIT;
            end
            DF_PREFILL: begin
                //
                //  prefill number of data equal to value of prefill register
                //  this means prefill should never be zero!
                //  prefill means enable write but not read
                //
                df_wr_en <= 1;
                count_fill <= count_fill + 1;
                f_state <= counted ? DF_WAIT_TRIGGER : DF_PREFILL;
            end
            DF_WAIT_TRIGGER: begin
                //
                //  prefill reached.  start reading on every clock tick so that
                //  the number of words remains constant.  then wait for a 
                //  trigger
                //
                df_rd_en <= 1;
                f_state <= trigger ? DF_WAIT_FULL : DF_WAIT_TRIGGER;
            end
            DF_WAIT_FULL: begin
                //
                //  trigger!   stop reading and wait for the fifo to fill up
                //
                df_rd_en <= 0;
                f_state <= data_fifo_full ? DF_WAIT_EMPTY : DF_WAIT_FULL;
            end
            DF_WAIT_EMPTY: begin
                //
                //  fifo is full. wait for it to be emptied via UART.  here is where
                //  we also have to control the fifo read enable, waiting for a signal
                //  from rx_state to read 1 data from the fifo
                //
                df_wr_en <= 0;
                f_state <= data_fifo_empty ? DF_WAIT : DF_WAIT_EMPTY;
            end
            default: begin
                count_fill <= 0;
                df_rd_en <= 0;
                df_wr_en <= 0;
                f_state <= DF_WAIT;
            end
        endcase
    end
    assign data_fifo_wr_en = df_wr_en;
    assign data_fifo_rd_en = out_read_fifo | df_rd_en;
    //
    //  drive the output led signals
    //
    //  0       global_start
    //  1       trigger_start
    //  2       data_fifo_empty
    //  3       data_fifo_full
    //  6:4     rx_state
    //  9:7     f_state
    //  14:10   rx_instructions
    assign led = {1'b0,rx_instructions[4:0],f_state[2:0],rx_state[2:0],
                data_fifo_full,data_fifo_empty,trigger_start,global_start};
    //
    //  2:0 rx_state
    //  3   outgoing_done
    //  4   incoming_done
    //  5   do_outgoing
    //  6   do_incoming
    //  11:7    rx_instructions
    //  15:12   in_state
    //  19:16   out_state
    //  20      tx_transmit
    //  21      out_read_fifo   which is for the ADC fifo
    //  22      in_read_fifo   which is for the rx fifo
    //  23      do_read_fifo    also for the rx fifo
    //  26:24   f_state
    //  27      df_wr_en
    //  28      df_rd_en
    //  29      counted
    //  30      data_fifo_empty
    //  31      data_fifo_full
    assign debug = {data_fifo_full,data_fifo_empty,counted,df_rd_en,df_wr_en,    //31:27
        f_state[2:0],do_read_fifo,in_read_fifo,out_read_fifo,tx_transmit,           //26:20
        out_state[3:0],in_state[3:0],rx_instructions[4:0],                          //19:7
        do_incoming,do_outgoing,incoming_done,outgoing_done,rx_state[2:0]};         //6:0

endmodule