ethernet/rtl/descramble.v

112 lines
3.3 KiB
Verilog

// SPDX-License-Identifier: AGPL-3.0-Only
/*
* Copyright (C) 2022 Sean Anderson <seanga2@gmail.com>
*/
`include "common.vh"
module descramble (
input clk,
input [1:0] scrambled, scrambled_valid,
input signal_status, test_mode,
output reg locked,
output reg [1:0] unscrambled, unscrambled_valid
);
reg locked_next;
reg [1:0] ldd, unscrambled_next;
reg [10:0] lfsr, lfsr_next;
/*
* The number of consecutive idle bits to require when locking, as
* well as the number necessary to prevent unlocking. For the first
* case, this must be less than 60 bits (7.2.3.1.1), including the
* bits necessary to initialize the lfsr. For the second, this must be
* less than 29 bits (7.2.3.3(f)). We use 29 to meet these requirements;
* it is increased by 1 to allow for an easier implementation of the
* counter, and decreased by 1 to allow easier implementation when
* scrambled_valid = 2. The end result is that only 28 bits might be
* required in certain situations.
*/
localparam CONSECUTIVE_IDLES = 5'd29;
reg [4:0] idle_counter = CONSECUTIVE_IDLES, idle_counter_next;
/*
* The amount of time without recieving consecutive idles before we
* unlock. This must be greater than 361us (7.2.3.3(f)). 2^16-1 works
* out to around 524us at 125MHz.
*/
localparam UNLOCK_TIME = 16'hffff;
/* 5us, or around one minimum-length packet plus some extra */
localparam TEST_UNLOCK_TIME = 16'd625;
reg [15:0] unlock_counter, unlock_counter_next;
always @(*) begin
ldd = { lfsr[8] ^ lfsr[10], lfsr[7] ^ lfsr[9] };
unscrambled_next = scrambled ^ ldd;
/*
* We must invert scrambled before adding it to the lfsr in
* order to remove the ^1 from the input idle. This doesn't
* affect the output of the lfsr during the sample state
* because two bits from the lfsr are xor'd together,
* canceling out the inversion.
*/
lfsr_next = lfsr;
if (scrambled_valid[0])
lfsr_next = { lfsr[9:0], locked ? ldd[1] : ~scrambled[1] };
else if (scrambled_valid[1])
lfsr_next = { lfsr[8:0], locked ? ldd : ~scrambled };
idle_counter_next = idle_counter;
if (scrambled_valid[1]) begin
if (unscrambled_next[1] && unscrambled_next[0])
idle_counter_next = idle_counter - 2;
else if (unscrambled_next[0])
idle_counter_next = idle_counter - 1;
else
idle_counter_next = CONSECUTIVE_IDLES;
end else if (scrambled_valid[0]) begin
if (unscrambled_next[1])
idle_counter_next = idle_counter - 1;
else
idle_counter_next = CONSECUTIVE_IDLES;
end
locked_next = 1;
unlock_counter_next = unlock_counter;
if (!idle_counter_next[4:1]) begin
unlock_counter_next = test_mode ? TEST_UNLOCK_TIME : UNLOCK_TIME;
/*
* Reset the counter to 2 to ensure we can always
* subtract idles without underflowing
*/
idle_counter_next = 2;
end else if (|unlock_counter) begin
unlock_counter_next = unlock_counter - 1;
end else begin
locked_next = 0;
end
end
always @(posedge clk) begin
unscrambled <= unscrambled_next;
if (signal_status) begin
lfsr <= lfsr_next;
idle_counter <= idle_counter_next;
unlock_counter <= unlock_counter_next;
locked <= locked_next;
unscrambled_valid <= scrambled_valid;
end else begin
lfsr <= 0;
idle_counter <= CONSECUTIVE_IDLES;
unlock_counter <= 0;
locked <= 0;
unscrambled_valid <= 0;
end
end
`DUMP(0)
endmodule