`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