`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 			Raptor Engineering
// Engineer: 			Timothy Pearson
// 
// Design Name:			Remote Access Driver
// Module Name:    		remote_access 
// Project Name: 		Remote Access Driver
// Target Devices: 		Any
// Description: 		Serial remote access driver and LCD display driver
//
// Dependencies: 
//
// (c) 2007-2013 Timothy Pearson, Raptor Engineering
// Released into the Public Domain
//
//////////////////////////////////////////////////////////////////////////////////
module remote_access(
	input main_fifty_clock,				// 50MHz clock in
	input [3:0] remote_access_4_bit_output,		// 4 bit output from the user program to remote access client
	output [3:0] remote_access_4_bit_input,		// 4 bit input from the remote access client to user program
	input [7:0] remote_access_8_bit_output,		// 8 bit output from the user program to remote access client
	output [7:0] remote_access_8_bit_input,		// 8 bit input from the remote access client to user program
	input [15:0] remote_access_16_bit_output,	// 16 bit output from the user program to the remote access client
	output [15:0] remote_access_16_bit_input,	// 16 bit input from the remote access client to the user program
	input serial_port_receiver,
	output serial_port_transmitter,
	input remote_access_input_enable,
	input [7:0] local_input,
	input seize_serial_tx,
	input [7:0] serial_tx_data,
	input serial_tx_strobe,
	output [7:0] serial_rx_data,
	output serial_rx_strobe,
	input [5:0] lcd_data_in_address,
	input [7:0] lcd_data_in_data,
	input lcd_data_in_enable,
	input sram_wren_in,
	input sram_clock_in,
	input [7:0] sram_data_in,
	input [(RAM_ADDR_BITS-1):0] sram_address_in,
	output [7:0] sram_data_out,
	output sram_available,
	input sram_processing_done,
	input [7:0] led_segment_bus,
	input [3:0] led_digit_select,

	// For use on Digilent Spartan 3E or compatible board only
	output [3:0] remote_access_lcd_data_out,
	output remote_access_lcd_rs_out,
	output remote_access_lcd_rw_out,
	output remote_access_lcd_enable_out);

	parameter RAM_ADDR_BITS = 14;
	
	reg [7:0] remote_access_4_bit_input_reg;
	reg [7:0] remote_access_8_bit_input_reg;
	reg [15:0] remote_access_16_bit_input_reg;
	reg [3:0] remote_access_lcd_data_out_reg;
	reg remote_access_lcd_rs_out_reg;
	reg remote_access_lcd_rw_out_reg;
	reg remote_access_lcd_enable_out_reg;
	reg [7:0] serial_rx_data_reg;
	reg serial_rx_strobe_reg;
	reg sram_available_reg;
	reg startup_needed = 1;
	
	assign remote_access_4_bit_input = remote_access_4_bit_input_reg[3:0];
	assign remote_access_8_bit_input = remote_access_8_bit_input_reg;
	assign remote_access_16_bit_input = remote_access_16_bit_input_reg;
	assign remote_access_lcd_data_out = remote_access_lcd_data_out_reg;
	assign remote_access_lcd_rs_out = remote_access_lcd_rs_out_reg;
	assign remote_access_lcd_rw_out = remote_access_lcd_rw_out_reg;
	assign remote_access_lcd_enable_out = remote_access_lcd_enable_out_reg;
	assign serial_rx_data = serial_rx_data_reg;
	assign serial_rx_strobe = serial_rx_strobe_reg;
	assign sram_available = sram_available_reg;
	
	//-----------------------------------------------------------------------------------
	//
	// Create a 4.16MHz clock for the LCD display driver and a 25MHz clock 
	// for the serial receiver.
	//
	//-----------------------------------------------------------------------------------
	
	reg four_mhz_clk;
	reg clk_div_by_two;
	reg clk_div_by_two_oneeighty;
	reg clk_div_by_four;
	reg clk_div_by_eight;
	reg [3:0] fifty_clock_divider = 0;
	
	always @(posedge main_fifty_clock) begin
		fifty_clock_divider = fifty_clock_divider + 1;
		if (fifty_clock_divider > 12) begin
			four_mhz_clk = !four_mhz_clk;
			fifty_clock_divider = 0;
		end
	end
	
	always @(posedge main_fifty_clock) begin
		clk_div_by_two = !clk_div_by_two;
	end

	always @(negedge main_fifty_clock) begin
		clk_div_by_two_oneeighty = !clk_div_by_two_oneeighty;
	end
	
	always @(posedge clk_div_by_two_oneeighty) begin
		clk_div_by_four = !clk_div_by_four;
	end
	
	always @(posedge clk_div_by_four) begin
		clk_div_by_eight = !clk_div_by_eight;
	end
	
	//-----------------------------------------------------------------------------------
	//
	// Keep track of what is on the LED display
	//
	//-----------------------------------------------------------------------------------
	
	reg [7:0] led_display_bytes [3:0];
	reg [17:0] digit_blanker_1 = 0;
	reg [17:0] digit_blanker_2 = 0;
	reg [17:0] digit_blanker_3 = 0;
	reg [17:0] digit_blanker_4 = 0;

	reg [7:0] led_segment_bus_latch;
	reg [3:0] led_digit_select_latch;
	
	always @(negedge clk_div_by_eight) begin
		led_segment_bus_latch = led_segment_bus;
		led_digit_select_latch = led_digit_select;

		if (led_digit_select_latch[0] == 0) begin
			led_display_bytes[0] = led_segment_bus_latch;
			digit_blanker_1 = 0;
			digit_blanker_2 = digit_blanker_2 + 1;
			digit_blanker_3 = digit_blanker_3 + 1;
			digit_blanker_4 = digit_blanker_4 + 1;
		end
		
		if (led_digit_select_latch[1] == 0) begin
			led_display_bytes[1] = led_segment_bus_latch;
			digit_blanker_1 = digit_blanker_1 + 1;
			digit_blanker_2 = 0;
			digit_blanker_3 = digit_blanker_3 + 1;
			digit_blanker_4 = digit_blanker_4 + 1;
		end
		
		if (led_digit_select_latch[2] == 0) begin
			led_display_bytes[2] = led_segment_bus_latch;
			digit_blanker_1 = digit_blanker_1 + 1;
			digit_blanker_2 = digit_blanker_2 + 1;
			digit_blanker_3 = 0;
			digit_blanker_4 = digit_blanker_4 + 1;
		end
		
		if (led_digit_select_latch[3] == 0) begin
			led_display_bytes[3] = led_segment_bus_latch;
			digit_blanker_1 = digit_blanker_1 + 1;
			digit_blanker_2 = digit_blanker_2 + 1;
			digit_blanker_3 = digit_blanker_3 + 1;
			digit_blanker_4 = 0;
		end

		if (digit_blanker_1 > 128000) begin
			led_display_bytes[0] = 255;
		end
		
		if (digit_blanker_2 > 128000) begin
			led_display_bytes[1] = 255;
		end
		
		if (digit_blanker_3 > 128000) begin
			led_display_bytes[2] = 255;
		end
		
		if (digit_blanker_4 > 128000) begin
			led_display_bytes[3] = 255;
		end
	end
	
	//-----------------------------------------------------------------------------------
	//
	// Instantiate the data storage RAM for signal processing
	//
	//-----------------------------------------------------------------------------------

	reg data_storage_remote_enable = 0;
	wire data_storage_clka;
	wire [7:0] data_storage_dina;
	wire [(RAM_ADDR_BITS-1):0] data_storage_addra;
	wire data_storage_write_enable;
	wire [7:0] data_storage_data_out;
	
	reg [7:0] data_storage_dina_reg;
	reg [(RAM_ADDR_BITS-1):0] data_storage_addra_reg;
	reg data_storage_write_enable_reg;

	data_storage #(RAM_ADDR_BITS) data_storage(.clka(data_storage_clka), .dina(data_storage_dina), .addra(data_storage_addra), 
		.wea(data_storage_write_enable), .douta(data_storage_data_out));
	
	assign data_storage_clka = (data_storage_remote_enable) ? main_fifty_clock : sram_clock_in;
	assign data_storage_dina = (data_storage_remote_enable) ? data_storage_dina_reg : sram_data_in;
	assign data_storage_addra = (data_storage_remote_enable) ? data_storage_addra_reg : sram_address_in;
	assign data_storage_write_enable = (data_storage_remote_enable) ? data_storage_write_enable_reg : sram_wren_in;
	
	assign sram_data_out = data_storage_data_out;
	
	// -----------------------------------------------------------------------------------------------
	//
	// Here is the serial receiver and transmitter
	//
	// -----------------------------------------------------------------------------------------------

	reg [7:0] transmit_all_data_state = 0;

	wire RxD_data_ready;
	wire [7:0] RxD_data;
	wire RxD_endofpacket;
	wire RxD_idle;

	reg TxD_start;
	reg [7:0] TxD_data;
	wire TxD_busy;
	wire [4:0] state;
	
	reg [7:0] transmitter_4_bit_state = 0;
	reg [7:0] transmitter_8_bit_state = 0;
	reg [15:0] transmitter_16_bit_state = 0;
	reg [7:0] transmitter_main_state = 0;
	reg [7:0] transmitter_input_state = 0;

	async_transmit asyncTX(.clk(clk_div_by_two), .TxD_start(TxD_start), .TxD_data(TxD_data), .TxD(serial_port_transmitter), .TxD_busy(TxD_busy), .state(state));
	async_receiver asyncRX(.clk(clk_div_by_two), .RxD(serial_port_receiver), .RxD_data_ready(RxD_data_ready), .RxD_data(RxD_data), .RxD_endofpacket(RxD_endofpacket), .RxD_idle(RxD_idle));

	reg tx_toggle = 0;
	
	reg transmit_4_bit_status = 0;
	reg transmit_4_bit_status_done = 0;
	
	reg transmit_8_bit_status = 0;
	reg transmit_8_bit_status_done = 0;
	
	reg transmit_16_bit_status = 0;
	reg transmit_16_bit_pass_two = 0;
	reg transmit_16_bit_status_done = 0;
	
	reg transmit_main_status = 0;
	reg transmit_main_status_done = 0;

	reg transmit_dsp_ram_size = 0;
	reg transmit_dsp_ram_size_done = 0;
	
	reg transmit_input_status = 0;
	reg transmit_input_status_done = 0;
	
	reg transmit_lcd_status = 0;
	reg transmit_lcd_status_done = 0;
	reg [7:0] transmit_lcd_status_counter = 0;
	
	reg enable_remote_access_input = 1;
	reg remote_access_input_enable_prev = 0;
	
	reg [7:0] lcd_display_string [31:0];
	
	reg transmit_dsp_status = 0;
	reg transmit_dsp_status_done = 0;
	reg transmit_dsp_status_holdoff = 0;
	reg [RAM_ADDR_BITS:0] transmit_dsp_status_counter = 0;
	
	reg transmit_led_status = 0;
	reg transmit_led_status_done = 0;
	reg [7:0] transmit_led_status_counter = 0;

	reg transmit_dsp_rx_complete = 0;
	reg transmit_dsp_rx_complete_done = 0;
	
	// Transmit!
	always @(posedge clk_div_by_two) begin
		transmitter_4_bit_state = remote_access_4_bit_output;
		transmitter_8_bit_state = remote_access_8_bit_output;
		transmitter_16_bit_state = remote_access_16_bit_output;
		
		transmitter_main_state = 0;
		transmitter_main_state[0] = enable_remote_access_input;
		
		transmitter_input_state = local_input;
	
		if (seize_serial_tx == 1) begin
			TxD_start = serial_tx_strobe;
			TxD_data = serial_tx_data;
		end else begin
			if (tx_toggle == 0) begin
				if ((transmit_4_bit_status == 1) && (transmit_4_bit_status_done == 0)) begin
					TxD_data = transmitter_4_bit_state;

					TxD_start = 1;
					tx_toggle = 1;
					
					transmit_4_bit_status_done = 1;
				end
				
				if ((transmit_8_bit_status == 1) && (transmit_8_bit_status_done == 0)) begin
					TxD_data = transmitter_8_bit_state;

					TxD_start = 1;
					tx_toggle = 1;
					
					transmit_8_bit_status_done = 1;
				end
				
				if ((transmit_16_bit_status == 1) && (transmit_16_bit_status_done == 0)) begin
					if (transmit_16_bit_pass_two == 0) begin
						TxD_data = transmitter_16_bit_state[15:8];
						
						TxD_start = 1;
						tx_toggle = 1;	
						
						transmit_16_bit_pass_two = 1;
					end else begin
						TxD_data = transmitter_16_bit_state[7:0];
						
						TxD_start = 1;
						tx_toggle = 1;	
						
						transmit_16_bit_status_done = 1;
					end
				end
				
				if ((transmit_main_status == 1) && (transmit_main_status_done == 0)) begin
					TxD_data = transmitter_main_state;

					TxD_start = 1;
					tx_toggle = 1;
					
					transmit_main_status_done = 1;
				end

				if ((transmit_dsp_ram_size == 1) && (transmit_dsp_ram_size_done == 0)) begin
					TxD_data = RAM_ADDR_BITS;

					TxD_start = 1;
					tx_toggle = 1;
					
					transmit_dsp_ram_size_done = 1;
				end
				
				if ((transmit_input_status == 1) && (transmit_input_status_done == 0)) begin
					TxD_data = transmitter_input_state;

					TxD_start = 1;
					tx_toggle = 1;
					
					transmit_input_status_done = 1;
				end
				
				if ((transmit_lcd_status == 1) && (transmit_lcd_status_done == 0)) begin
					TxD_data = lcd_display_string[transmit_lcd_status_counter];

					TxD_start = 1;
					tx_toggle = 1;
					
					transmit_lcd_status_counter = transmit_lcd_status_counter + 1;
					if (transmit_lcd_status_counter > 31) begin
						transmit_lcd_status_done = 1;
					end
				end
				
				if ((transmit_led_status == 1) && (transmit_led_status_done == 0)) begin
					TxD_data = led_display_bytes[transmit_led_status_counter];

					TxD_start = 1;
					tx_toggle = 1;
					
					transmit_led_status_counter = transmit_led_status_counter + 1;
					if (transmit_led_status_counter > 3) begin
						transmit_led_status_done = 1;
					end
				end

				if ((transmit_dsp_rx_complete == 1) && (transmit_dsp_rx_complete_done == 0)) begin
					TxD_data = 77;

					TxD_start = 1;
					tx_toggle = 1;
					
					transmit_dsp_rx_complete_done = 1;
				end
				
				if ((transmit_dsp_status == 1) && (transmit_dsp_rx_complete == 0) && (transmit_dsp_status_done == 0)) begin
					if (transmit_dsp_status_holdoff == 0) begin
						transmit_dsp_status_holdoff = 1;
						data_storage_write_enable_reg = 0;
						data_storage_addra_reg = 0;			// Initial data value
					end else begin
						data_storage_write_enable_reg = 0;
						TxD_data = data_storage_data_out;

						TxD_start = 1;
						tx_toggle = 1;
						
						transmit_dsp_status_counter = transmit_dsp_status_counter + 1;
						data_storage_addra_reg = transmit_dsp_status_counter[(RAM_ADDR_BITS-1):0];
						if (transmit_dsp_status_counter >= (2**RAM_ADDR_BITS)) begin
							transmit_dsp_status_done = 1;
							data_storage_write_enable_reg = 1'bz;
							data_storage_addra_reg = {(RAM_ADDR_BITS){1'bz}};
						end
					end
				end
			end else begin
				if (state == 5'b10000) begin	// Wait for transmission of byte to complete
					TxD_start = 0;
					tx_toggle = 0;
				end
			end
		end
		
		if (transmit_4_bit_status == 0) begin
			transmit_4_bit_status_done = 0;
		end
		
		if (transmit_8_bit_status == 0) begin
			transmit_8_bit_status_done = 0;
		end
		
		if (transmit_16_bit_status == 0) begin
			transmit_16_bit_pass_two = 0;
			transmit_16_bit_status_done = 0;
		end
		
		if (transmit_main_status == 0) begin
			transmit_main_status_done = 0;
		end

		if (transmit_dsp_ram_size == 0) begin
			transmit_dsp_ram_size_done = 0;
		end
		
		if (transmit_input_status == 0) begin
			transmit_input_status_done = 0;
		end
		
		if (transmit_lcd_status == 0) begin
			transmit_lcd_status_done = 0;
			transmit_lcd_status_counter = 0;
		end
		
		if (transmit_led_status == 0) begin
			transmit_led_status_done = 0;
			transmit_led_status_counter = 0;
		end

		if (transmit_dsp_rx_complete == 0) begin
			transmit_dsp_rx_complete_done = 0;
		end
		
		if (transmit_dsp_status == 0) begin
			transmit_dsp_status_done = 0;
			transmit_dsp_status_holdoff = 0;
			transmit_dsp_status_counter = 0;
		end
	end

	reg [7:0] lcd_display_initialization_state = 0;
	reg serial_character_received = 0;
	reg [7:0] serial_receiver_toggler = 0;
	reg [7:0] serial_command_buffer = 0;
	reg [2:0] next_byte_is_command = 0;
	reg [7:0] next_byte_is_command_prev_command = 0;
	reg [7:0] serial_command_timer = 0;
	reg update_lcd_display = 0;
	reg [7:0] serial_update_counter = 0;
	reg [RAM_ADDR_BITS:0] dsp_update_counter = 0;
	reg [7:0] received_lcd_display_string [31:0];
	reg data_write_timer = 0;
	reg waiting_on_dsp_processing = 0;
	
	// Receive serial commands
	always @(posedge clk_div_by_two) begin
		if (startup_needed == 1) begin
			startup_needed = 0;
			transmit_dsp_status = 1;
		end
	
		if (lcd_data_in_enable == 1) begin
			received_lcd_display_string[lcd_data_in_address] = lcd_data_in_data;
			update_lcd_display = 1;
			serial_command_timer = 255;
		end
	
		if ((remote_access_input_enable == 1) && (remote_access_input_enable_prev == 0)) begin
			enable_remote_access_input = !enable_remote_access_input;
		end
		remote_access_input_enable_prev = remote_access_input_enable;
	
		if (enable_remote_access_input == 0) begin
			// Enable local input
			remote_access_8_bit_input_reg = local_input;
		end
	
		if (serial_command_timer > 0) begin
			serial_command_timer = serial_command_timer - 1;
		end else begin
			update_lcd_display = 0;
		end
		
		if (transmit_4_bit_status_done == 1) begin
			transmit_4_bit_status = 0;
			if (transmit_all_data_state == 1) begin
				transmit_8_bit_status = 1;
			end
		end
		
		if (transmit_8_bit_status_done == 1) begin
			transmit_8_bit_status = 0;
			if (transmit_all_data_state == 1) begin
				transmit_16_bit_status = 1;
			end
		end
		
		if (transmit_16_bit_status_done == 1) begin
			transmit_16_bit_status = 0;
			if (transmit_all_data_state == 1) begin
				transmit_led_status = 1;
			end
		end
		
		if (transmit_led_status_done == 1) begin
			transmit_led_status = 0;
			if (transmit_all_data_state == 1) begin
				transmit_all_data_state = 0;
			end
		end

		if (transmit_dsp_rx_complete_done == 1) begin
			transmit_dsp_rx_complete = 0;
		end
		
		if (transmit_main_status_done == 1) begin
			transmit_main_status = 0;
			if (transmit_all_data_state == 1) begin
				transmit_dsp_ram_size = 1;
			end
		end

		if (transmit_dsp_ram_size_done == 1) begin
			transmit_dsp_ram_size = 0;
			if (transmit_all_data_state == 1) begin
				transmit_4_bit_status = 1;
			end
		end
		
		if (transmit_input_status_done == 1) begin
			transmit_input_status = 0;
		end
		
		if (transmit_lcd_status_done == 1) begin
			transmit_lcd_status = 0;
			if (transmit_all_data_state == 1) begin
				transmit_main_status = 1;
			end
		end
		
		if (transmit_dsp_status_done == 1) begin
			transmit_dsp_status = 0;
			data_storage_remote_enable = 0;
		end
		
		if (transmit_dsp_status == 1) begin
			data_storage_remote_enable = 1;
		end
		
		if (data_write_timer > 1) begin
			data_write_timer = data_write_timer - 1;
		end else begin
			if (data_write_timer == 1) begin
				data_storage_write_enable_reg = 0;
				data_write_timer = 0;
			end
		end
		
		if ((waiting_on_dsp_processing == 1) && (sram_processing_done == 1)) begin
			waiting_on_dsp_processing = 0;
			transmit_dsp_status = 1;
		end
	
		serial_rx_strobe_reg = 0;		// Make sure that this get reset!
		
		if ((sram_processing_done == 1) && (sram_available_reg == 1)) begin
			sram_available_reg = 0;
			transmit_dsp_status = 1;
		end
	
		if (RxD_data_ready == 1) begin
			if (serial_character_received == 0) begin
				serial_rx_data_reg = RxD_data;
				serial_rx_strobe_reg = 1;		// Signal new data...
				if (seize_serial_tx == 0) begin
					if (next_byte_is_command_prev_command == 77) begin
						// DSP input data
						if (dsp_update_counter < (2**RAM_ADDR_BITS)) begin
							data_storage_remote_enable = 1;
							data_storage_addra_reg = dsp_update_counter[(RAM_ADDR_BITS-1):0];
							data_storage_dina_reg = serial_rx_data_reg;
							data_storage_write_enable_reg = 1;
							data_write_timer = 3;
							dsp_update_counter = dsp_update_counter + 1;
							
							// TESTING ONLY!!!
							//if (dsp_update_counter < 17) begin
							//	received_lcd_display_string[dsp_update_counter - 1] = serial_command_buffer;
							//end

							if (dsp_update_counter >= (2**RAM_ADDR_BITS)) begin
								next_byte_is_command = 0;
								data_storage_write_enable_reg = 0;
								data_storage_remote_enable = 0;
								sram_available_reg = 1;
								data_storage_write_enable_reg = 1'bz;
								data_storage_addra_reg = {(RAM_ADDR_BITS){1'bz}};
								waiting_on_dsp_processing = 1;
								transmit_dsp_rx_complete = 1;
								next_byte_is_command_prev_command = 0;
								
								// TESTING ONLY!!!
								//transmit_dsp_status = 1;
							end
						end
					end else begin
						// Parse the command and see what it is
						serial_character_received = 1;
						if (serial_rx_data_reg == 13) begin
							// Carriage Return!  The serial_command_buffer holds the command!  Parse it!
							if (next_byte_is_command == 0) begin
								if (serial_command_buffer == 65) begin
									// Display update requested
									next_byte_is_command = 1;
									serial_update_counter = 0;
									next_byte_is_command_prev_command = 65;
								end
								
								if (serial_command_buffer == 66) begin
									// 8 bit input update
									if (enable_remote_access_input == 1) begin
										next_byte_is_command = 1;
										serial_update_counter = 0;
										next_byte_is_command_prev_command = 66;
									end
								end
								
								if (serial_command_buffer == 67) begin
									// 16 bit input update
									next_byte_is_command = 1;
									serial_update_counter = 0;
									next_byte_is_command_prev_command = 67;
								end
								
								if (serial_command_buffer == 68) begin
									// 8 bit output status
									transmit_8_bit_status = 1;
								end
								
								if (serial_command_buffer == 69) begin
									// 16 bit output status
									transmit_16_bit_status = 1;
								end
								
								if (serial_command_buffer == 70) begin
									// System status
									transmit_main_status = 1;
								end
								
								if (serial_command_buffer == 71) begin
									// Simulate center button press
									enable_remote_access_input = !enable_remote_access_input;
								end
								
								if (serial_command_buffer == 72) begin
									// Local input status
									transmit_input_status = 1;
								end
								
								if (serial_command_buffer == 73) begin
									// 4 bit input update
									if (enable_remote_access_input == 1) begin
										next_byte_is_command = 1;
										serial_update_counter = 0;
										next_byte_is_command_prev_command = 73;
									end
								end
								
								if (serial_command_buffer == 74) begin
									// 4 bit output status
									transmit_4_bit_status = 1;
								end
								
								if (serial_command_buffer == 75) begin
									// Transmit the contents of the LCD...
									transmit_lcd_status = 1;
								end
								
								if (serial_command_buffer == 76) begin
									// Transmit the contents of the LCD...
									transmit_all_data_state = 1;
									transmit_lcd_status = 1;
								end
								
								if (serial_command_buffer == 77) begin
									// Receive offline DSP data
									next_byte_is_command = 1;
									dsp_update_counter = 0;
									next_byte_is_command_prev_command = 77;
								end
								
								if (serial_command_buffer == 78) begin
									// Transmit the contents of RAM...
									transmit_dsp_status = 1;
								end

								if (serial_command_buffer == 79) begin
									// Transmit the DSP RAM size
									transmit_dsp_ram_size = 1;
								end
							end else begin
								if (next_byte_is_command == 1) begin
									// The previous byte was the command--now load in the data!
									if (next_byte_is_command_prev_command == 65) begin
										if (serial_update_counter < 32) begin
											received_lcd_display_string[serial_update_counter] = serial_command_buffer;
											serial_update_counter = serial_update_counter + 1;
										end else begin
											update_lcd_display = 1;
											serial_command_timer = 255;
											next_byte_is_command = 0;
										end
									end
									
									// 4 bit input update
									if (next_byte_is_command_prev_command == 73) begin
										remote_access_4_bit_input_reg = serial_command_buffer;
										next_byte_is_command = 0;
									end
									
									// 8 bit input update
									if (next_byte_is_command_prev_command == 66) begin
										remote_access_8_bit_input_reg = serial_command_buffer;
										next_byte_is_command = 0;
									end
									
									// 16 bit input update
									if (next_byte_is_command_prev_command == 67) begin
										if (serial_update_counter == 0) begin
											remote_access_16_bit_input_reg[15:8] = serial_command_buffer;
											serial_update_counter = 1;
										end else begin
											remote_access_16_bit_input_reg[7:0] = serial_command_buffer;
											next_byte_is_command = 0;
										end
									end
								end
							end
						end
					end
				end
				
				//if (RxD_data != 10) begin		// Ignore linefeeds
					serial_command_buffer = RxD_data;
				//end
			
				serial_receiver_toggler = serial_receiver_toggler + 1;
			end
		end
		
		if (RxD_data_ready == 0) begin
			serial_character_received = 0;
		end
	end
	
	//-----------------------------------------------------------------------------------
	//
	// This routine will display the contents of lcd_display_string on the LCD display
	//
	//-----------------------------------------------------------------------------------
	
	reg [15:0] lcd_display_wait_counter = 0;
	reg [7:0] lcd_display_current_character = 0;
	reg lcd_display_line_two = 0;		// Are we trying to write to line two?
	
	always @(posedge four_mhz_clk) begin	
		case (lcd_display_initialization_state)
			// Initialize the display according to the reference manual
			0:begin
				// Set up the default display...
				lcd_display_string[0] = 73;		// I
				lcd_display_string[1] = 110;		// n
				lcd_display_string[2] = 105;		// i
				lcd_display_string[3] = 116;		// t
				lcd_display_string[4] = 105;		// i
				lcd_display_string[5] = 97;		// a
				lcd_display_string[6] = 108;		// l
				lcd_display_string[7] = 105;		// i
				lcd_display_string[8] = 122;		// z
				lcd_display_string[9] = 97;		// a
				lcd_display_string[10] = 116;	// t
				lcd_display_string[11] = 105;	// i
				lcd_display_string[12] = 111;	// o
				lcd_display_string[13] = 110;	// n
				lcd_display_string[14] = 32;		// <blank>
				lcd_display_string[15] = 32;		// <blank>
				lcd_display_string[16] = 79;		// O
				lcd_display_string[17] = 75;		// k
				lcd_display_string[18] = 32;		// <blank>
				lcd_display_string[19] = 32;		// <blank>
				lcd_display_string[20] = 32;		// <blank>
				lcd_display_string[21] = 32;		// <blank>
				lcd_display_string[22] = 32;		// <blank>
				lcd_display_string[23] = 32;		// <blank>
				lcd_display_string[24] = 32;		// <blank>
				lcd_display_string[25] = 32;		// <blank>
				lcd_display_string[26] = 32;		// <blank>
				lcd_display_string[27] = 32;		// <blank>
				lcd_display_string[28] = 32;		// <blank>
				lcd_display_string[29] = 32;		// <blank>
				lcd_display_string[30] = 32;		// <blank>
				lcd_display_string[31] = 32;		// <blank>
			
				lcd_display_current_character = 0;
				
				lcd_display_line_two = 0;
				remote_access_lcd_data_out_reg = 3;
				remote_access_lcd_enable_out_reg = 1;
				remote_access_lcd_rs_out_reg = 0;
				remote_access_lcd_rw_out_reg = 0;
				lcd_display_wait_counter = 17083;	// Wait 15mS
				lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			1:begin
				remote_access_lcd_enable_out_reg = 0;
				lcd_display_wait_counter = lcd_display_wait_counter - 1;
				if (lcd_display_wait_counter == 0) lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			2:begin
				remote_access_lcd_data_out_reg = 3;
				remote_access_lcd_enable_out_reg = 1;
				lcd_display_wait_counter = 417;		// Wait 100uS
				lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			3:begin
				remote_access_lcd_enable_out_reg = 0;
				lcd_display_wait_counter = lcd_display_wait_counter - 1;
				if (lcd_display_wait_counter == 0) lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			4:begin
				remote_access_lcd_data_out_reg = 3;
				remote_access_lcd_enable_out_reg = 1;
				lcd_display_wait_counter = 167;		// Wait 40uS
				lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			5:begin
				remote_access_lcd_enable_out_reg = 0;
				lcd_display_wait_counter = lcd_display_wait_counter - 1;
				if (lcd_display_wait_counter == 0) lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			6:begin
				remote_access_lcd_data_out_reg = 2;
				remote_access_lcd_enable_out_reg = 1;
				lcd_display_wait_counter = 167;		// Wait 40uS
				lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			7:begin
				remote_access_lcd_enable_out_reg = 0;
				lcd_display_wait_counter = lcd_display_wait_counter - 1;
				if (lcd_display_wait_counter == 0) lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			// Display is now initialized
			
			// Send Function Set command
			8:begin
				remote_access_lcd_data_out_reg = 2;
				remote_access_lcd_enable_out_reg = 1;
				lcd_display_wait_counter = 5;		// Wait 1uS
				lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			9:begin
				remote_access_lcd_enable_out_reg = 0;
				lcd_display_wait_counter = lcd_display_wait_counter - 1;
				if (lcd_display_wait_counter == 0) lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			10:begin
				remote_access_lcd_data_out_reg = 8;
				remote_access_lcd_enable_out_reg = 1;
				lcd_display_wait_counter = 167;		// Wait 40uS
				lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			11:begin
				remote_access_lcd_enable_out_reg = 0;
				lcd_display_wait_counter = lcd_display_wait_counter - 1;
				if (lcd_display_wait_counter == 0) lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			
			// Send Entry Mode Set command
			12:begin
				remote_access_lcd_data_out_reg = 0;
				remote_access_lcd_enable_out_reg = 1;
				lcd_display_wait_counter = 5;		// Wait 1uS
				lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			13:begin
				remote_access_lcd_enable_out_reg = 0;
				lcd_display_wait_counter = lcd_display_wait_counter - 1;
				if (lcd_display_wait_counter == 0) lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			14:begin
				remote_access_lcd_data_out_reg = 6;
				remote_access_lcd_enable_out_reg = 1;
				lcd_display_wait_counter = 167;		// Wait 40uS
				lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			15:begin
				remote_access_lcd_enable_out_reg = 0;
				lcd_display_wait_counter = lcd_display_wait_counter - 1;
				if (lcd_display_wait_counter == 0) lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			
			// Send Display On command and disable cursors and blinking
			16:begin
				remote_access_lcd_data_out_reg = 0;
				remote_access_lcd_enable_out_reg = 1;
				lcd_display_wait_counter = 5;		// Wait 1uS
				lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			17:begin
				remote_access_lcd_enable_out_reg = 0;
				lcd_display_wait_counter = lcd_display_wait_counter - 1;
				if (lcd_display_wait_counter == 0) lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			18:begin
				remote_access_lcd_data_out_reg = 12;
				remote_access_lcd_enable_out_reg = 1;
				lcd_display_wait_counter = 167;		// Wait 40uS
				lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			19:begin
				remote_access_lcd_enable_out_reg = 0;
				lcd_display_wait_counter = lcd_display_wait_counter - 1;
				if (lcd_display_wait_counter == 0) lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			
			// Send Clear Display command
			20:begin
				remote_access_lcd_data_out_reg = 0;
				remote_access_lcd_enable_out_reg = 1;
				lcd_display_wait_counter = 5;		// Wait 1uS
				lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			21:begin
				remote_access_lcd_enable_out_reg = 0;
				lcd_display_wait_counter = lcd_display_wait_counter - 1;
				if (lcd_display_wait_counter == 0) lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			22:begin
				remote_access_lcd_data_out_reg = 1;
				remote_access_lcd_enable_out_reg = 1;
				lcd_display_wait_counter = 6833;		// Wait 1.64uS
				lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			23:begin
				remote_access_lcd_enable_out_reg = 0;
				lcd_display_wait_counter = lcd_display_wait_counter - 1;
				if (lcd_display_wait_counter == 0) lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			
			// Set DD RAM Address to 0 if lcd_display_line_two is 0, or 0x40 if it is 1
			24:begin
				remote_access_lcd_rs_out_reg = 0;
				if (lcd_display_line_two == 0) begin
					remote_access_lcd_data_out_reg = 8;
				end
				if (lcd_display_line_two == 1) begin
					remote_access_lcd_data_out_reg = 12;
				end
				remote_access_lcd_enable_out_reg = 1;
				lcd_display_wait_counter = 5;		// Wait 1uS
				lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			25:begin
				remote_access_lcd_enable_out_reg = 0;
				lcd_display_wait_counter = lcd_display_wait_counter - 1;
				if (lcd_display_wait_counter == 0) lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			26:begin
				remote_access_lcd_data_out_reg = 0;
				remote_access_lcd_enable_out_reg = 1;
				lcd_display_wait_counter = 167;		// Wait 40uS
				lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			27:begin
				remote_access_lcd_enable_out_reg = 0;
				lcd_display_wait_counter = lcd_display_wait_counter - 1;
				if (lcd_display_wait_counter == 0) lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			
			// Display the characters
			28:begin
				remote_access_lcd_data_out_reg = lcd_display_string[lcd_display_current_character][7:4];
				remote_access_lcd_rs_out_reg = 1;
				remote_access_lcd_enable_out_reg = 1;
				lcd_display_wait_counter = 5;		// Wait 1uS
				lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			29:begin
				remote_access_lcd_enable_out_reg = 0;
				lcd_display_wait_counter = lcd_display_wait_counter - 1;
				if (lcd_display_wait_counter == 0) lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			30:begin
				remote_access_lcd_data_out_reg = lcd_display_string[lcd_display_current_character][3:0];
				remote_access_lcd_enable_out_reg = 1;
				lcd_display_wait_counter = 167;		// Wait 40uS
				lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			31:begin
				remote_access_lcd_enable_out_reg = 0;
				lcd_display_wait_counter = lcd_display_wait_counter - 1;
				if (lcd_display_wait_counter == 0) lcd_display_initialization_state = lcd_display_initialization_state + 1;
			end
			32:begin
				lcd_display_current_character = lcd_display_current_character + 1;
				lcd_display_initialization_state = lcd_display_initialization_state + 1;
				if (lcd_display_current_character < 32) begin
					lcd_display_initialization_state = 28;
				end
				if (lcd_display_current_character == 16) begin
					if (lcd_display_line_two == 0) begin
						lcd_display_line_two = 1;
						lcd_display_initialization_state = 24;
					end
				end
			end
			33:begin
				// End!
				remote_access_lcd_rs_out_reg = 0;
				remote_access_lcd_enable_out_reg = 0;
				lcd_display_line_two = 0;
				lcd_display_current_character = 0;
				if (update_lcd_display == 1) begin
					lcd_display_line_two = 0;
					lcd_display_current_character = 0;
					lcd_display_initialization_state = 24;
					lcd_display_string[0] = received_lcd_display_string[0];
					lcd_display_string[1] = received_lcd_display_string[1];
					lcd_display_string[2] = received_lcd_display_string[2];
					lcd_display_string[3] = received_lcd_display_string[3];
					lcd_display_string[4] = received_lcd_display_string[4];
					lcd_display_string[5] = received_lcd_display_string[5];
					lcd_display_string[6] = received_lcd_display_string[6];
					lcd_display_string[7] = received_lcd_display_string[7];
					lcd_display_string[8] = received_lcd_display_string[8];
					lcd_display_string[9] = received_lcd_display_string[9];
					lcd_display_string[10] = received_lcd_display_string[10];
					lcd_display_string[11] = received_lcd_display_string[11];
					lcd_display_string[12] = received_lcd_display_string[12];
					lcd_display_string[13] = received_lcd_display_string[13];
					lcd_display_string[14] = received_lcd_display_string[14];
					lcd_display_string[15] = received_lcd_display_string[15];
					lcd_display_string[16] = received_lcd_display_string[16];
					lcd_display_string[17] = received_lcd_display_string[17];
					lcd_display_string[18] = received_lcd_display_string[18];
					lcd_display_string[19] = received_lcd_display_string[19];
					lcd_display_string[20] = received_lcd_display_string[20];
					lcd_display_string[21] = received_lcd_display_string[21];
					lcd_display_string[22] = received_lcd_display_string[22];
					lcd_display_string[23] = received_lcd_display_string[23];
					lcd_display_string[24] = received_lcd_display_string[24];
					lcd_display_string[25] = received_lcd_display_string[25];
					lcd_display_string[26] = received_lcd_display_string[26];
					lcd_display_string[27] = received_lcd_display_string[27];
					lcd_display_string[28] = received_lcd_display_string[28];
					lcd_display_string[29] = received_lcd_display_string[29];
					lcd_display_string[30] = received_lcd_display_string[30];
					lcd_display_string[31] = received_lcd_display_string[31];
				end
			end
		endcase
	end
endmodule

module async_receiver(clk, RxD, RxD_data_ready, RxD_data, RxD_endofpacket, RxD_idle);
	input clk, RxD;
	output RxD_data_ready;  // onc clock pulse when RxD_data is valid
	output [7:0] RxD_data;
	
	parameter ClkFrequency = 25000000;	// 25MHz
	parameter Baud = 115200;
	
	// We also detect if a gap occurs in the received stream of characters
	// That can be useful if multiple characters are sent in burst
	//  so that multiple characters can be treated as a "packet"
	output RxD_endofpacket;  // one clock pulse, when no more data is received (RxD_idle is going high)
	output RxD_idle;  // no data is being received
	
	// Baud generator (we use 8 times oversampling)
	parameter Baud8 = Baud*8;
	parameter Baud8GeneratorAccWidth = 16;
	wire [Baud8GeneratorAccWidth:0] Baud8GeneratorInc = ((Baud8<<(Baud8GeneratorAccWidth-7))+(ClkFrequency>>8))/(ClkFrequency>>7);
	reg [Baud8GeneratorAccWidth:0] Baud8GeneratorAcc;
	always @(posedge clk) Baud8GeneratorAcc <= Baud8GeneratorAcc[Baud8GeneratorAccWidth-1:0] + Baud8GeneratorInc;
	wire Baud8Tick = Baud8GeneratorAcc[Baud8GeneratorAccWidth];
	
	////////////////////////////
	reg [1:0] RxD_sync_inv;
	always @(posedge clk) if(Baud8Tick) RxD_sync_inv <= {RxD_sync_inv[0], ~RxD};
	// we invert RxD, so that the idle becomes "0", to prevent a phantom character to be received at startup
	
	reg [1:0] RxD_cnt_inv;
	reg RxD_bit_inv;
	
	always @(posedge clk)
	if(Baud8Tick)
	begin
		if( RxD_sync_inv[1] && RxD_cnt_inv!=2'b11) RxD_cnt_inv <= RxD_cnt_inv + 2'h1;
		else 
		if(~RxD_sync_inv[1] && RxD_cnt_inv!=2'b00) RxD_cnt_inv <= RxD_cnt_inv - 2'h1;
	
		if(RxD_cnt_inv==2'b00) RxD_bit_inv <= 1'b0;
		else
		if(RxD_cnt_inv==2'b11) RxD_bit_inv <= 1'b1;
	end
	
	reg [3:0] state;
	reg [3:0] bit_spacing;
	
	// "next_bit" controls when the data sampling occurs
	// depending on how noisy the RxD is, different values might work better
	// with a clean connection, values from 8 to 11 work
	wire next_bit = (bit_spacing==4'd10);
	
	always @(posedge clk)
	if(state==0)
		bit_spacing <= 4'b0000;
	else
	if(Baud8Tick)
		bit_spacing <= {bit_spacing[2:0] + 4'b0001} | {bit_spacing[3], 3'b000};
	
	always @(posedge clk)
	if(Baud8Tick)
	case(state)
		4'b0000: if(RxD_bit_inv) state <= 4'b1000;  // start bit found?
		4'b1000: if(next_bit) state <= 4'b1001;  // bit 0
		4'b1001: if(next_bit) state <= 4'b1010;  // bit 1
		4'b1010: if(next_bit) state <= 4'b1011;  // bit 2
		4'b1011: if(next_bit) state <= 4'b1100;  // bit 3
		4'b1100: if(next_bit) state <= 4'b1101;  // bit 4
		4'b1101: if(next_bit) state <= 4'b1110;  // bit 5
		4'b1110: if(next_bit) state <= 4'b1111;  // bit 6
		4'b1111: if(next_bit) state <= 4'b0001;  // bit 7
		4'b0001: if(next_bit) state <= 4'b0000;  // stop bit
		default: state <= 4'b0000;
	endcase
	
	reg [7:0] RxD_data;
	always @(posedge clk)
	if(Baud8Tick && next_bit && state[3]) RxD_data <= {~RxD_bit_inv, RxD_data[7:1]};
	
	reg RxD_data_ready;
	always @(posedge clk)
	begin
		RxD_data_ready <= (Baud8Tick && next_bit && state==4'b0001 && ~RxD_bit_inv);  // ready only if the stop bit is received
	end
	
	reg [4:0] gap_count;
	always @(posedge clk) if (state!=0) gap_count<=5'h00; else if(Baud8Tick & ~gap_count[4]) gap_count <= gap_count + 5'h01;
	assign RxD_idle = gap_count[4];
	reg RxD_endofpacket; always @(posedge clk) RxD_endofpacket <= Baud8Tick & (gap_count==5'h0F);
endmodule

module async_transmit(clk, TxD_start, TxD_data, TxD, TxD_busy, state);
	input clk, TxD_start;
	input [7:0] TxD_data;
	output TxD, TxD_busy;
	output [4:0] state;
	parameter ClkFrequency = 25000000;	// 25MHz
	//parameter ClkFrequency = 50000000;	// 50MHz
	parameter Baud = 115200;
	parameter RegisterInputData = 1;	// in RegisterInputData mode, the input doesn't have to stay valid while the character is been transmitted
	
	// Baud generator
	parameter BaudGeneratorAccWidth = 16;
	reg [BaudGeneratorAccWidth:0] BaudGeneratorAcc;
	`ifdef DEBUG
	wire [BaudGeneratorAccWidth:0] BaudGeneratorInc = 17'h10000;
	`else
	wire [BaudGeneratorAccWidth:0] BaudGeneratorInc = ((Baud<<(BaudGeneratorAccWidth-4))+(ClkFrequency>>5))/(ClkFrequency>>4);
	`endif
	
	wire BaudTick = BaudGeneratorAcc[BaudGeneratorAccWidth];
	wire TxD_busy;
	always @(posedge clk) if(TxD_busy) BaudGeneratorAcc <= BaudGeneratorAcc[BaudGeneratorAccWidth-1:0] + BaudGeneratorInc;
	
	// Transmitter state machine
	reg [4:0] state;
	wire TxD_ready = (state==0);
	assign TxD_busy = ~TxD_ready;
	
	reg [7:0] TxD_dataReg;
	always @(posedge clk) if(TxD_ready & TxD_start) TxD_dataReg <= TxD_data;
	wire [7:0] TxD_dataD = RegisterInputData ? TxD_dataReg : TxD_data;
	
	always @(posedge clk) begin
		if (TxD_start == 0) state <= 5'b00000;
	
		case(state)
			5'b00000: if(TxD_start) state <= 5'b00001;
			5'b00001: if(BaudTick) state <= 5'b00100;
			5'b00100: if(BaudTick) state <= 5'b01000;  // start
			5'b01000: if(BaudTick) state <= 5'b01001;  // bit 0
			5'b01001: if(BaudTick) state <= 5'b01010;  // bit 1
			5'b01010: if(BaudTick) state <= 5'b01011;  // bit 2
			5'b01011: if(BaudTick) state <= 5'b01100;  // bit 3
			5'b01100: if(BaudTick) state <= 5'b01101;  // bit 4
			5'b01101: if(BaudTick) state <= 5'b01110;  // bit 5
			5'b01110: if(BaudTick) state <= 5'b01111;  // bit 6
			5'b01111: if(BaudTick) state <= 5'b00010;  // bit 7
			5'b00010: if(BaudTick) state <= 5'b00011;  // stop1
			//4'b0011: if(BaudTick) state <= 4'b0000;  // stop2
			5'b00011: if(BaudTick) state <= 5'b10000;  // stop2
			//default: if(BaudTick) state <= 4'b0000;
		endcase
	end
	
	// Output mux
	reg muxbit;
	always @( * )
	case(state[2:0])
		3'd0: muxbit <= TxD_dataD[0];
		3'd1: muxbit <= TxD_dataD[1];
		3'd2: muxbit <= TxD_dataD[2];
		3'd3: muxbit <= TxD_dataD[3];
		3'd4: muxbit <= TxD_dataD[4];
		3'd5: muxbit <= TxD_dataD[5];
		3'd6: muxbit <= TxD_dataD[6];
		3'd7: muxbit <= TxD_dataD[7];
	endcase
	
	// Put together the start, data and stop bits
	reg TxD;
	always @(posedge clk) TxD <= (state<4) | (state[3] & muxbit) | state[4];  // register the output to make it glitch free

endmodule
