`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 09/27/2023 04:15:36 PM
// Design Name: 
// Module Name: uart_rx_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 rx_data contains the relevant data to decode:
//
//  7654 3210
//              LSB=1 means the RPi is sending info that will come on the next transmission
//              All transactions are 8 bits 
//  0000 0001   RPi is sending control register (on/off...etc)
//  0000 0011   "    "   "     blast register
//  0000 0101   "    "   "     threshold register
//  0000 0111   "    "   "     sumid register
//
//              LSB=0 means RPi wants FPGA to send stuff back
//  0000 0010   send back 16 bit ADC value from XADC block directly
//  0000 0100   send back 16 bit test value from slide switches
//  0000 0110   send back 16 bit firmware version
//  0000 1000   send back 8 bit control register (upper 8 bits are all zero)
//  0000 1010   send back 8 bit status register (ditto)
//  0000 1100   semd back 16 bit blast register
//  0000 1110   send back the ADC data fifo counts
//  0001 0000   send back the threshold register value
//  0001 0010   send back lower 16 bits of sumout
//  0001 0100   send back upper 16 bits of sumout
//  1000 0000   blast back 65536 data points from ADC fifo or until empty
//
`include "include.v"

module controller(
    input clock25,              // 25MHz input clock to match uart_rx and uart_tx clock and adc fifo read
    input fifo_clock,           // fifo input clock, which should be 44.1kHz
    input reset,                // reset (BTNR)
    output global_start,        // drive one of the LEDs with this line
    //
    //  rx fifo stuff
    //
    input rx_fifo_empty,        // fifo empty
    input [7:0] rx_fifo_data,   // 8 bits of fifo data
    output rx_fifo_rd_en,       // fifo read enable
    //
    //  uart_tx stuff
    //
    output [15:0] tx_transmit_out,      //  2 bytes to send out
    output tx_transmit,                 //  signal to send them
    input tx_done,                      //  asserted twice per transfer
    //
    //  data inputs
    //
    input [15:0] version,           //  set at the top of top.v
    input [15:0] test,              //  slide switches
    input [15:0] adc_data,          //  latched into r_adc_data
    //
    //  ADC fifo stuff
    //
    input adc_fifo_empty,                   //  fifo empty
    input adc_fifo_full,                    //  fifo full
    output reg adc_fifo_rd,                 //  read enable
    output adc_fifo_wr,                     //  write enable
    input [15:0] adc_fifo_data_count,       //  number left to read
    input [15:0] adc_fifo_data,             //  all 16 bits of data
    //
    //  debug
    //
    output [50:0] debug
    );

    //
    //  "blast" register tells how many data points from the ADC FIFO to send to the
    //  RPi in one continuous "blast"
    //
    //  note that the FIFO is 65535 deep, so we set the blast register to that value
    //  and the RPi will expect to receive 65536 x 2 bytes
    //
    reg [15:0] blast = 16'h7FF;
    //
    //  here is the control register, however the RPi serial input buffer is only 4096
    //  bytes deep.  So we only want to be blasting 2048-1 (starrting from 0) = 0x7FF 
    //  16-bit words at a time
    //
    reg [15:0] control = 0;
    assign global_start = control[0];
    assign test_mode = control[1];
    assign correlator_enable = control[2];
    assign reset_corr = control[3];
    //
    //  status register
    //
    wire [15:0] status = {11'b000,adc_fifo_full,adc_fifo_empty,rx_fifo_empty,1'b0,global_start};
    //
    //  threshold register for 1-bit ADC for the correlator
    //
    reg [11:0] threshold = 12'h9eb;
    wire digitized = adc_data[15:4] > threshold ? 1 : 0;
    //
    //  sumid register, which tells the autocorrelator which sum to send to the RPi
    //
    reg [15:0] sumid = 0;
    //
    //  instantiate the autocorrelator module here
    //
    wire [`SUM_WIDTH-1:0] sumout;
    autocorrelator CORR (
        .clock(fifo_clock),
        .reset(reset_corr),
        .enable(correlator_enable),
        .adc(digitized),
        .sum_id(sumid),
        .sum_out(sumout)
    );
    //
    //  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 [3: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,
        RX_OUTGOING_WAIT16=7, RX_OUTGOING_WAIT_BLAST=8;
    reg [3:0] rx_state;
    reg [7:0] rx_instructions;
    reg do_read_fifo, do_incoming, do_outgoing, do_blast;
    wire read_fifo_done, incoming_done, outgoing_done, blast_done;
    wire branch_to_incoming = rx_instructions[0];       // incoming or outgoing
    wire blast_out = rx_instructions[7];
    always @ (posedge clock25)
        if (reset) begin
            rx_state <= RX_WAIT;
            do_read_fifo <= 0;
            do_incoming <= 0;
            do_outgoing <= 0;
            rx_instructions <= 0;
            do_blast <= 0;
        end
        else case (rx_state)
            RX_WAIT: begin
                rx_instructions <= 0;
                do_read_fifo <= 0;
                do_incoming <= 0;
                do_outgoing <= 0;
                do_blast <= 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
                //
                //  check if we are outputing things like version, test, or adc, vs
                //  blasting data from the adc fifo
                //
                if (blast_out) rx_state <= RX_OUTGOING_WAIT_BLAST;
                else rx_state <= RX_OUTGOING_WAIT16;
            end
            RX_OUTGOING_WAIT_BLAST: begin
                do_blast <= 1;
                rx_state <= blast_done ? RX_WAIT : RX_OUTGOING_WAIT_BLAST;
            end
            RX_OUTGOING_WAIT16: begin
                do_outgoing <= 1;
                rx_state <= outgoing_done ? RX_WAIT : RX_OUTGOING_WAIT16;
            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;
    reg [1:0] incoming_what;
    always @ (posedge clock25)
        if (reset) begin
            in_state <= IN_WAIT;
            in_read_fifo <= 0;
            in_data <= 0;
            in_done <= 0;
            incoming_what <= 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;
                incoming_what <= rx_instructions[2:1];    // what incoming is coming in
                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
                //
                //  here is where we latch according to what the RPi wants to send
                //
                in_done <= 1;
                case (incoming_what)
                    2'b00: control <= in_data;
                    2'b01: blast <= in_data;
                    2'b10: threshold <= in_data[11:0];
                    2'b11: sumid <= in_data;
                endcase
                in_state <= do_incoming ? IN_DONE : IN_WAIT;
            end
            default:
                in_state <= IN_WAIT;
        endcase
    assign incoming_done = in_done;
    //
    //  rx_fifo_read can come from either the RX or IN state machine
    //
    assign rx_fifo_rd_en = do_read_fifo | in_read_fifo;
    //
    //  now make the state machine to handle sending 16 bit words to the RPi
    //    
    localparam [2:0] OUT_WAIT=0, OUT_LATCH=1, OUT_TRANSMIT=2, 
        OUT_TRANSMIT_WAIT1=3, OUT_TRANSMIT_WAIT2=4, OUT_DONE=5;
    reg [2:0] out_state;
    reg [15:0] tx_out;
    reg transmit_out;
    reg out_done;
    reg [3:0] outgoing_what;
    always @ (posedge clock25)
        if (reset) begin
            out_state <= OUT_WAIT;
            transmit_out <= 0;
            out_done <= 0;
            tx_out <= 0;
            outgoing_what <= 0;
        end
        else case (out_state)
            OUT_WAIT: begin
                transmit_out <= 0;
                tx_out <= 0;
                out_done <= 0;
                out_state <= do_outgoing ? OUT_LATCH : OUT_WAIT;
                outgoing_what <= rx_instructions[4:1];    // what is going out (only need 4 bits for now)
            end
            OUT_LATCH: begin
                case (outgoing_what)
                    4'b0001: tx_out <= adc_data;
                    4'b0010: tx_out <= test;
                    4'b0011: tx_out <= version;
                    4'b0100: tx_out <= control;
                    4'b0101: tx_out <= status;
                    4'b0110: tx_out <= blast;
                    4'b0111: tx_out <= adc_fifo_data_count;
                    4'b1000: tx_out <= {4'b0,threshold};
                    4'b1001: tx_out <= sumout[15:0];
                    4'b1010: tx_out <= sumout[`SUM_WIDTH-1:16];
                    default: tx_out <= 16'hDEAD;    // error!
                endcase
                out_state <= OUT_TRANSMIT;
            end
            OUT_TRANSMIT: begin
                transmit_out <= 1;
                out_state <= OUT_TRANSMIT_WAIT1;
            end
            OUT_TRANSMIT_WAIT1: begin
                transmit_out <= 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;
    //
    //  now make the state machine for blasting data back to the RPi.   
    //
    //  we will assume that the FIFO is full and just keep emptying it until it's empty
    //
    localparam [3:0] BLAST_WAIT=0, BLAST_EN1=1, BLAST_EN0=2, BLAST_LATCH=3,
        BLAST_TRANSMIT=4, BLAST_WAIT1_DONE=5, BLAST_WAIT1_0=6, BLAST_WAIT2_DONE=7, 
        BLAST_WAIT2_0=8, BLAST_INCREMENT=9, BLAST_CHECK=10, BLAST_DONE=11;
    reg [3:0] blast_state;
    reg [15:0] count;
    reg blast_all_done;
    reg [15:0] blast_data;
    reg [15:0] blast_count;
    reg blast_transmit;
    reg data_fifo_wr;
    wire fifo_last = (blast_count == 16'h1);
    wire count_done = count == blast; 
    wire all_done = adc_fifo_empty || count_done;
    always @ (posedge clock25)
        if (reset) begin
            count <= 0;
            blast_all_done <= 0;
            blast_transmit <= 0;
            adc_fifo_rd <= 0;
            data_fifo_wr <= 1;
            blast_data <= 0;
            blast_state <= BLAST_WAIT;
        end
        else case (blast_state)
            BLAST_WAIT: begin
                count <= 0;
                blast_all_done <= 0;
                blast_transmit <= 0;
                adc_fifo_rd <= 0;
                data_fifo_wr <= 1;
                blast_data <= 0;
                blast_state <= do_blast ? BLAST_EN1 : BLAST_WAIT;
//                blast_state <= do_blast & adc_fifo_full ? BLAST_EN1 : BLAST_WAIT;
            end
            BLAST_EN1: begin
                //
                //  assert fifo rd enable
                //
                data_fifo_wr <= 0;
                adc_fifo_rd <= 1;
                blast_state <= BLAST_EN0;
            end
            BLAST_EN0: begin
                //
                //  deassert fifo rd enable
                //
                adc_fifo_rd <= 0;
                blast_state <= BLAST_LATCH;
            end
            BLAST_LATCH: begin
                //
                //  data will have upper 12 bits of ADC, then 00, then fifo_empty, then fifo_last
                //
                blast_data <= test_mode ? count : 
                    {adc_fifo_data[15:4],2'b00,adc_fifo_empty,fifo_last};
                blast_count <= adc_fifo_data_count;
                blast_state <= BLAST_TRANSMIT;
            end
            BLAST_TRANSMIT: begin
                //
                //  assert the transmit signal, this goes to the TX state machine in TOP
                //  then go to the wait done state and release the blast_transmit so it
                //  doesn't confuse anything.  note that the blast_transmit only happens
                //  once per transmitting 16 bit words so we clear it in the next state
                //
                blast_transmit <= 1;
                blast_state <= BLAST_WAIT1_DONE;
            end
            BLAST_WAIT1_DONE: begin
                //
                //  now wait for tx_done to be asserted, which means the transmission of
                //  the first byte is finished
                //
                blast_transmit <= 0;
                blast_state <= tx_done ? BLAST_WAIT1_0 : BLAST_WAIT1_DONE;
            end
            BLAST_WAIT1_0: begin
                //
                //  wait for the uart_tx state machine to drop tx_done, which tells us that
                //  it's ready to send the next byte
                //
                blast_state <= tx_done ? BLAST_WAIT1_0 : BLAST_WAIT2_DONE;
            end
            BLAST_WAIT2_DONE: begin
                //
                //  2nd byte is being transmitted.  wait for tx_done to signal finished
                //
                blast_state <= tx_done ? BLAST_WAIT2_0 : BLAST_WAIT2_DONE;
            end
            BLAST_WAIT2_0: begin
                //
                //  again wait for tx_done to go away before continuing
                //
                blast_state <= tx_done ? BLAST_WAIT2_0 : BLAST_INCREMENT;
            end
            BLAST_INCREMENT: begin
                //
                //  increment the counter that counts number of 16-bit words sent
                //
                count <= count + 1;
                blast_state <= BLAST_CHECK;
            end
            BLAST_CHECK: begin
                //
                //  check to see we are all done transmitting all FIFO words
                //
                blast_state <= all_done ? BLAST_DONE : BLAST_EN1;
            end
            BLAST_DONE: begin
                //
                //  wait for do_blast to go away before going back to wait state
                //
                blast_all_done <= 1;
                blast_state <= do_blast ? BLAST_DONE : BLAST_WAIT;
            end            
        endcase
    assign blast_done = blast_all_done;
    assign adc_fifo_wr = global_start & data_fifo_wr;
    //
    //  now take care of merging OUT_ and BLAST_ uart_tx outputs
    //
    assign tx_transmit = blast_transmit | transmit_out;
    assign tx_transmit_out = do_blast ? blast_data : tx_out;


    assign debug = {
        //  50      digitized
        //  49      adc_fifo_empty
        //  48      count_done
        //  47:32   tx_transmit_out[15:0]
        //  31      do_outgoing
        //  30      transmit_out
        //  29      blast_transmit
        //  28       blast_done
        //  27       do_blast,
        //  26:23     blast_state[3:0]
        //  22      tx_done
        //  21      tx_transmit
        //  20      out_done
        //  19:17   out_state[2:0]
        //  16      in_done
        //  15      in_read_fifo
        //  14:12   in_state[2:0]
        //  11      do_incoming
        //  10      do_blast
        //  9       incoming
        //  8       rx_instructions[7]
        //  7:5     rx_instructions[3:1]
        //  4:1     rx_state
        //  0       clock25
        digitized,adc_fifo_empty,count_done,                                            //  50,49,48
        tx_transmit_out[15:0],                                                          //  47:32
        do_outgoing,                                                                    //  31
        transmit_out,blast_transmit,blast_done,do_blast,blast_state[3:0],               //  30:23
        tx_done,tx_transmit,out_done,out_state[2:0],in_done,                            //  22:16
        in_read_fifo,in_state[2:0],do_incoming,do_blast,incoming,rx_instructions[7],    //  15:8
        rx_instructions[3:1],rx_state[3:0],clock25};                                    //  0:7
endmodule