// Copyright (c) 2020, Andrew Kay // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. `default_nettype none module coax_rx ( input clk, input reset, input rx, output reg active, output reg error, output reg [9:0] data, output reg strobe = 0, input parity ); parameter CLOCKS_PER_BIT = 8; localparam ERROR_LOSS_OF_MID_BIT_TRANSITION = 10'b0000000001; localparam ERROR_PARITY = 10'b0000000010; localparam ERROR_INVALID_END_SEQUENCE = 10'b0000000100; localparam STATE_IDLE = 0; localparam STATE_START_SEQUENCE_1 = 1; localparam STATE_START_SEQUENCE_2 = 2; localparam STATE_START_SEQUENCE_3 = 3; localparam STATE_START_SEQUENCE_4 = 4; localparam STATE_START_SEQUENCE_5 = 5; localparam STATE_START_SEQUENCE_6 = 6; localparam STATE_START_SEQUENCE_7 = 7; localparam STATE_START_SEQUENCE_8 = 8; localparam STATE_START_SEQUENCE_9 = 9; localparam STATE_SYNC_BIT = 10; localparam STATE_DATA_BIT = 11; localparam STATE_PARITY_BIT = 12; localparam STATE_END_SEQUENCE_1 = 13; localparam STATE_END_SEQUENCE_2 = 14; localparam STATE_ERROR = 15; reg [3:0] state = STATE_IDLE; reg [3:0] next_state; reg [7:0] state_counter; reg [7:0] next_state_counter; reg previous_rx; reg bit_timer_reset = 0; reg next_bit_timer_reset; reg [9:0] next_data; reg next_strobe; reg [9:0] input_data; reg [9:0] next_input_data; reg input_data_parity; reg [3:0] bit_counter = 0; reg [3:0] next_bit_counter; reg next_active; reg next_error; wire sample; wire synchronized; coax_rx_bit_timer #( .CLOCKS_PER_BIT(CLOCKS_PER_BIT) ) bit_timer ( .clk(clk), .rx(rx), .reset(bit_timer_reset), .sample(sample), .synchronized(synchronized) ); always @(*) begin next_state = state; next_state_counter = state_counter + 1; next_bit_timer_reset = 0; next_data = data; next_strobe = 0; next_input_data = input_data; next_bit_counter = bit_counter; next_active = 0; next_error = 0; case (state) STATE_IDLE: begin next_bit_timer_reset = 1; if (!rx && previous_rx) begin next_state = STATE_START_SEQUENCE_1; next_state_counter = 0; end end STATE_START_SEQUENCE_1: begin if (sample) begin if (synchronized && rx) next_state = STATE_START_SEQUENCE_2; else next_state = STATE_IDLE; next_state_counter = 0; end else if (state_counter >= (CLOCKS_PER_BIT * 2)) begin next_state = STATE_IDLE; next_state_counter = 0; end end STATE_START_SEQUENCE_2: begin if (sample) begin if (synchronized && rx) next_state = STATE_START_SEQUENCE_3; else next_state = STATE_IDLE; next_state_counter = 0; end end STATE_START_SEQUENCE_3: begin if (sample) begin if (synchronized && rx) next_state = STATE_START_SEQUENCE_4; else next_state = STATE_IDLE; next_state_counter = 0; end end STATE_START_SEQUENCE_4: begin if (sample) begin if (synchronized && rx) next_state = STATE_START_SEQUENCE_5; else next_state = STATE_IDLE; next_state_counter = 0; end end STATE_START_SEQUENCE_5: begin if (sample) begin if (synchronized && rx) next_state = STATE_START_SEQUENCE_6; else next_state = STATE_IDLE; next_state_counter = 0; end end STATE_START_SEQUENCE_6: begin if (!rx) begin next_state = STATE_START_SEQUENCE_7; next_state_counter = 0; end else if (state_counter >= CLOCKS_PER_BIT) begin next_state = STATE_IDLE; next_state_counter = 0; end end STATE_START_SEQUENCE_7: begin if (rx) begin next_state = STATE_START_SEQUENCE_8; next_state_counter = 0; end else if (state_counter >= (CLOCKS_PER_BIT * 2)) begin next_state = STATE_IDLE; next_state_counter = 0; end end STATE_START_SEQUENCE_8: begin if (!rx) begin next_bit_timer_reset = 1; next_state = STATE_START_SEQUENCE_9; next_state_counter = 0; end else if (state_counter >= (CLOCKS_PER_BIT * 2)) begin next_state = STATE_IDLE; next_state_counter = 0; end end STATE_START_SEQUENCE_9: begin // This is really the first STATE_SYNC_BIT but we treat it // differently and consider it part of the start // sequence. if (sample && synchronized) begin if (rx) begin next_bit_counter = 0; next_state = STATE_DATA_BIT; end else begin next_state = STATE_IDLE; end next_state_counter = 0; end else if (state_counter >= CLOCKS_PER_BIT) begin next_state = STATE_IDLE; next_state_counter = 0; end end STATE_SYNC_BIT: begin next_active = 1; if (sample) begin if (synchronized) begin if (rx) begin next_bit_counter = 0; next_state = STATE_DATA_BIT; end else begin next_state = STATE_END_SEQUENCE_1; end next_state_counter = 0; end else begin next_data = ERROR_LOSS_OF_MID_BIT_TRANSITION; next_state = STATE_ERROR; next_state_counter = 0; end end end STATE_DATA_BIT: begin next_active = 1; if (sample) begin if (synchronized) begin next_input_data = { input_data[8:0], rx }; if (bit_counter < 9) begin next_bit_counter = bit_counter + 1; end else begin next_state = STATE_PARITY_BIT; end next_state_counter = 0; end else begin next_data = ERROR_LOSS_OF_MID_BIT_TRANSITION; next_state = STATE_ERROR; next_state_counter = 0; end end end STATE_PARITY_BIT: begin next_active = 1; if (sample) begin if (synchronized) begin if (rx == input_data_parity) begin next_strobe = 1; next_data = input_data; next_state = STATE_SYNC_BIT; end else begin next_data = ERROR_PARITY; next_state = STATE_ERROR; end next_state_counter = 0; end else begin next_data = ERROR_LOSS_OF_MID_BIT_TRANSITION; next_state = STATE_ERROR; next_state_counter = 0; end end end STATE_END_SEQUENCE_1: begin if (rx) begin next_state = STATE_END_SEQUENCE_2; next_state_counter = 0; end else if (state_counter >= CLOCKS_PER_BIT) begin next_data = ERROR_INVALID_END_SEQUENCE; next_state = STATE_ERROR; next_state_counter = 0; end end STATE_END_SEQUENCE_2: begin // TODO: should this go to ERROR on timeout? if (!rx) begin next_state = STATE_IDLE; next_state_counter = 0; end else if (state_counter >= (CLOCKS_PER_BIT * 2)) begin next_state = STATE_IDLE; next_state_counter = 0; end end STATE_ERROR: begin next_error = 1; end endcase end always @(posedge clk) begin state <= next_state; state_counter <= next_state_counter; bit_timer_reset <= next_bit_timer_reset; data <= next_data; strobe <= next_strobe; input_data <= next_input_data; // Parity includes the sync bit. input_data_parity <= (parity == 1 ? ^{ 1'b1, input_data } : ~^{ 1'b1, input_data }); bit_counter <= next_bit_counter; active <= next_active; error <= next_error; if (reset) begin bit_timer_reset <= 1; state <= STATE_IDLE; strobe <= 0; active <= 0; error <= 0; end previous_rx <= rx; end endmodule