Files
lowobservable.coax/interface2/fpga/rtl/coax_tx.v
2021-03-29 16:38:10 -05:00

309 lines
7.7 KiB
Verilog

// 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