`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