// 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_tx ( input clk, input reset, output reg active, output reg tx, input [9:0] data, input strobe, output ready, input parity ); parameter CLOCKS_PER_BIT = 8; localparam IDLE = 0; localparam START_SEQUENCE_1 = 1; localparam START_SEQUENCE_2 = 2; localparam START_SEQUENCE_3 = 3; localparam START_SEQUENCE_4 = 4; localparam START_SEQUENCE_5 = 5; localparam START_SEQUENCE_6 = 6; localparam START_SEQUENCE_7 = 7; localparam START_SEQUENCE_8 = 8; localparam START_SEQUENCE_9 = 9; localparam SYNC_BIT = 10; localparam DATA_BIT = 11; localparam PARITY_BIT = 12; localparam END_SEQUENCE_1 = 13; localparam END_SEQUENCE_2 = 14; localparam END_SEQUENCE_3 = 15; reg [3:0] state = IDLE; reg [3:0] next_state; reg next_tx; reg end_sequence; reg next_end_sequence; reg [9:0] output_data; reg [9:0] next_output_data; reg output_data_full = 0; reg next_output_data_full; reg parity_bit; reg next_parity_bit; reg output_parity_bit; reg next_output_parity_bit; reg [3:0] bit_counter = 0; reg [3:0] next_bit_counter; reg bit_timer_reset = 0; reg next_bit_timer_reset; wire first_half; wire second_half; wire last_clock; coax_tx_bit_timer #( .CLOCKS_PER_BIT(CLOCKS_PER_BIT) ) bit_timer ( .clk(clk), .reset(bit_timer_reset), .first_half(first_half), .second_half(second_half), .last_clock(last_clock) ); always @(*) begin next_state = state; next_tx = 0; next_end_sequence = 0; next_output_data = output_data; next_output_data_full = output_data_full; next_parity_bit = parity_bit; next_output_parity_bit = output_parity_bit; if (strobe && ready) begin next_output_data = data; next_output_data_full = 1; // Parity includes the sync bit. next_parity_bit = (parity == 1 ? ^{ 1'b1, data } : ~^{ 1'b1, data }); end next_bit_counter = bit_counter; next_bit_timer_reset = 0; case (state) IDLE: begin next_bit_timer_reset = 1; if (output_data_full) begin next_state = START_SEQUENCE_1; end end START_SEQUENCE_1: begin next_tx = 1; // TODO... off by 1 if (second_half) begin next_bit_timer_reset = 1; next_state = START_SEQUENCE_2; end end START_SEQUENCE_2: begin next_tx = first_half ? 0 : 1; if (last_clock) next_state = START_SEQUENCE_3; end START_SEQUENCE_3: begin next_tx = first_half ? 0 : 1; if (last_clock) next_state = START_SEQUENCE_4; end START_SEQUENCE_4: begin next_tx = first_half ? 0 : 1; if (last_clock) next_state = START_SEQUENCE_5; end START_SEQUENCE_5: begin next_tx = first_half ? 0 : 1; if (last_clock) next_state = START_SEQUENCE_6; end START_SEQUENCE_6: begin next_tx = first_half ? 0 : 1; if (last_clock) next_state = START_SEQUENCE_7; end START_SEQUENCE_7: begin next_tx = 0; if (last_clock) next_state = START_SEQUENCE_8; end START_SEQUENCE_8: begin next_tx = first_half ? 0 : 1; if (last_clock) next_state = START_SEQUENCE_9; end START_SEQUENCE_9: begin next_tx = 1; if (last_clock) next_state = SYNC_BIT; end SYNC_BIT: begin next_tx = first_half ? 0 : 1; next_bit_counter = 9; if (last_clock) next_state = DATA_BIT; end DATA_BIT: begin next_tx = first_half ? ~output_data[9] : output_data[9]; if (last_clock) begin next_output_data = { output_data[8:0], output_data[9] }; next_bit_counter = bit_counter - 1; if (bit_counter == 0) begin next_output_data_full = 0; next_output_parity_bit = parity_bit; next_state = PARITY_BIT; end end end PARITY_BIT: begin next_tx = first_half ? ~output_parity_bit : output_parity_bit; if (last_clock) begin if (output_data_full) begin next_state = SYNC_BIT; end else begin next_end_sequence = 1; next_state = END_SEQUENCE_1; end end end END_SEQUENCE_1: begin next_tx = first_half ? 1 : 0; next_end_sequence = 1; if (last_clock) next_state = END_SEQUENCE_2; end END_SEQUENCE_2: begin next_tx = 1; next_end_sequence = 1; if (last_clock) next_state = END_SEQUENCE_3; end END_SEQUENCE_3: begin next_tx = 1; next_end_sequence = 1; if (last_clock) begin next_tx = 0; next_end_sequence = 0; next_state = IDLE; end end endcase end always @(posedge clk) begin state <= next_state; active <= (state != IDLE); // TODO: this causes active to remain high one additional clock cycle tx <= next_tx; end_sequence <= next_end_sequence; output_data <= next_output_data; output_data_full <= next_output_data_full; parity_bit <= next_parity_bit; output_parity_bit <= next_output_parity_bit; bit_counter <= next_bit_counter; bit_timer_reset <= next_bit_timer_reset; if (reset) begin state <= IDLE; active <= 0; tx <= 0; end_sequence <= 0; output_data_full <= 0; end end assign ready = (!output_data_full && !end_sequence); endmodule