`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