mirror of
https://github.com/lowobservable/coax.git
synced 2026-03-01 01:49:52 +00:00
270 lines
7.9 KiB
Verilog
270 lines
7.9 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_rx (
|
|
input clk,
|
|
input reset,
|
|
input rx,
|
|
output reg active,
|
|
output reg error,
|
|
output reg [9:0] data,
|
|
output reg strobe = 0,
|
|
input protocol,
|
|
input parity
|
|
);
|
|
parameter CLOCKS_PER_BIT = 8;
|
|
|
|
localparam CLOCKS_PER_HALF_BIT = CLOCKS_PER_BIT / 2;
|
|
localparam CLOCKS_PER_2_BIT = CLOCKS_PER_BIT * 2;
|
|
localparam CLOCKS_PER_3_BIT = CLOCKS_PER_BIT * 3;
|
|
localparam CLOCKS_LOSS_OF_MID_BIT_TRANSITION = CLOCKS_PER_BIT + (CLOCKS_PER_BIT / 2);
|
|
|
|
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_FIRST_SYNC_BIT = 1;
|
|
localparam STATE_SYNC_BIT = 2;
|
|
localparam STATE_DATA_BIT = 3;
|
|
localparam STATE_PARITY_BIT = 4;
|
|
localparam STATE_END_SEQUENCE_1 = 5;
|
|
localparam STATE_END_SEQUENCE_2 = 6;
|
|
localparam STATE_ERROR = 7;
|
|
|
|
reg [2:0] state = STATE_IDLE;
|
|
reg [2:0] next_state;
|
|
|
|
reg previous_rx;
|
|
|
|
reg [$clog2(CLOCKS_PER_3_BIT):0] mid_bit_counter;
|
|
reg [$clog2(CLOCKS_PER_3_BIT):0] next_mid_bit_counter;
|
|
|
|
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 ss_detector_strobe;
|
|
|
|
coax_rx_ss_detector #(
|
|
.CLOCKS_PER_BIT(CLOCKS_PER_BIT)
|
|
) ss_detector (
|
|
.clk(clk),
|
|
.reset(reset),
|
|
.enable(state == STATE_IDLE),
|
|
.rx(rx),
|
|
.strobe(ss_detector_strobe)
|
|
);
|
|
|
|
always @(*)
|
|
begin
|
|
next_state = state;
|
|
|
|
next_mid_bit_counter = mid_bit_counter + 1;
|
|
|
|
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_input_data = 10'b0;
|
|
|
|
if (ss_detector_strobe)
|
|
begin
|
|
// The start sequence ends with a code violation, so reset
|
|
// the mid bit counter as if the next mid-bit transition
|
|
// is half a bit away.
|
|
next_mid_bit_counter = CLOCKS_PER_HALF_BIT;
|
|
next_state = STATE_FIRST_SYNC_BIT;
|
|
end
|
|
end
|
|
|
|
STATE_FIRST_SYNC_BIT:
|
|
begin
|
|
// This is really the first STATE_SYNC_BIT, but we treat it
|
|
// differently and consider it part of the start sequence as
|
|
// it must be a 1 and we don't consider the receiver active
|
|
// until this has been detected.
|
|
next_bit_counter = (protocol == 1 ? 4 : 0);
|
|
|
|
if (rx != previous_rx && mid_bit_counter > CLOCKS_PER_HALF_BIT)
|
|
begin
|
|
next_mid_bit_counter = 0;
|
|
|
|
if (rx)
|
|
next_state = STATE_DATA_BIT;
|
|
else
|
|
next_state = STATE_IDLE;
|
|
end
|
|
else if (mid_bit_counter > CLOCKS_LOSS_OF_MID_BIT_TRANSITION)
|
|
begin
|
|
next_state = STATE_IDLE;
|
|
end
|
|
end
|
|
|
|
STATE_SYNC_BIT:
|
|
begin
|
|
next_active = 1;
|
|
next_bit_counter = 0;
|
|
|
|
if (rx != previous_rx && mid_bit_counter > CLOCKS_PER_HALF_BIT)
|
|
begin
|
|
next_mid_bit_counter = 0;
|
|
|
|
if (rx)
|
|
next_state = STATE_DATA_BIT;
|
|
else
|
|
next_state = STATE_END_SEQUENCE_1;
|
|
end
|
|
else if (mid_bit_counter > CLOCKS_LOSS_OF_MID_BIT_TRANSITION)
|
|
begin
|
|
next_data = ERROR_LOSS_OF_MID_BIT_TRANSITION;
|
|
next_state = STATE_ERROR;
|
|
end
|
|
end
|
|
|
|
STATE_DATA_BIT:
|
|
begin
|
|
next_active = 1;
|
|
|
|
if (rx != previous_rx && mid_bit_counter > CLOCKS_PER_HALF_BIT)
|
|
begin
|
|
next_mid_bit_counter = 0;
|
|
|
|
next_input_data = { input_data[8:0], rx };
|
|
|
|
if (bit_counter < 9)
|
|
next_bit_counter = bit_counter + 1;
|
|
else
|
|
next_state = STATE_PARITY_BIT;
|
|
end
|
|
else if (mid_bit_counter > CLOCKS_LOSS_OF_MID_BIT_TRANSITION)
|
|
begin
|
|
next_data = ERROR_LOSS_OF_MID_BIT_TRANSITION;
|
|
next_state = STATE_ERROR;
|
|
end
|
|
end
|
|
|
|
STATE_PARITY_BIT:
|
|
begin
|
|
next_active = 1;
|
|
|
|
if (rx != previous_rx && mid_bit_counter > CLOCKS_PER_HALF_BIT)
|
|
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_mid_bit_counter = 0;
|
|
end
|
|
else if (mid_bit_counter > CLOCKS_LOSS_OF_MID_BIT_TRANSITION)
|
|
begin
|
|
next_data = ERROR_LOSS_OF_MID_BIT_TRANSITION;
|
|
next_state = STATE_ERROR;
|
|
end
|
|
end
|
|
|
|
STATE_END_SEQUENCE_1:
|
|
begin
|
|
if (rx)
|
|
begin
|
|
next_state = STATE_END_SEQUENCE_2;
|
|
next_mid_bit_counter = 0;
|
|
end
|
|
else if (mid_bit_counter > CLOCKS_PER_BIT)
|
|
begin
|
|
next_data = ERROR_INVALID_END_SEQUENCE;
|
|
next_state = STATE_ERROR;
|
|
end
|
|
end
|
|
|
|
STATE_END_SEQUENCE_2:
|
|
begin
|
|
if (!rx)
|
|
begin
|
|
next_state = STATE_IDLE;
|
|
end
|
|
else if (mid_bit_counter > CLOCKS_PER_3_BIT)
|
|
begin
|
|
// TODO: should this go to ERROR on timeout?
|
|
next_state = STATE_IDLE;
|
|
end
|
|
end
|
|
|
|
STATE_ERROR:
|
|
begin
|
|
next_error = 1;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
always @(posedge clk)
|
|
begin
|
|
state <= next_state;
|
|
|
|
mid_bit_counter <= next_mid_bit_counter;
|
|
|
|
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
|
|
state <= STATE_IDLE;
|
|
|
|
strobe <= 0;
|
|
|
|
active <= 0;
|
|
error <= 0;
|
|
end
|
|
|
|
previous_rx <= rx;
|
|
end
|
|
endmodule
|