lin-win-share/DA4008_V1.2/sim/lvds/TB.sv

480 lines
16 KiB
Systemverilog

`timescale 1ns/1ps
module TB;
// ====================================================
// Parameters
// ====================================================
parameter FIFO_DEPTH = 64;
parameter SCRAMBLER_SEED = 32'hFFFFFFFF;
parameter CLK_PERIOD = 10; // 10ns
parameter PATN_COUNT = 20'd10; // Training match count
parameter TAP_STEP = 3'd3; // Delay adjustment step
// ====================================================
// Constants (moved to top for visibility)
// ====================================================
localparam [31:0] TRAINING_PATN = 32'h68666E6C; // "hfnl"
localparam [31:0] TRAINING_EXIT = 32'h65786974; // "exit"
localparam [31:0] FRAME_HEADER = 32'hBCBCBCBC;
initial begin
$fsdbAutoSwitchDumpfile(500, "./verdplus.fsdb", 1000000);
$fsdbDumpvars();
$fsdbDumpMDA();
end
// ====================================================
// Signals
// ====================================================
logic clk;
logic rst_n;
logic [3:0] serial_in;
logic [19:0] patn_count;
logic [2:0] tap_step;
logic link_down;
logic [2:0] delay_tap;
logic [12:0] wr_addr;
logic [511:0] wr_data;
logic wr_en;
logic [63:0] byte_mask;
logic crc_error;
logic [2 :0] tap_adj_mask = 3'b111; // Delay adjustment mask
logic [0 :0] tap_force = 1'b0 ; // Delay force to tap_step
logic tap_adj_req ;
logic frame_done ;
logic [31 :0] train_status ;
logic [31 :0] frame_status ;
logic always_on = 1'b0 ;
logic prefilling ;
logic prefill_start;
// Added for scrambler test
logic descram_en;
logic [31:0] lfsr_lane[0:3]; // 4 independent LFSRs for scrambling
logic train_ready;
logic force_train; // Force Training
// ====================================================
// DUT Instance
// ====================================================
ulink_rx #(
.FIFO_DEPTH ( FIFO_DEPTH )
,.SCRAMBLER_SEED ( SCRAMBLER_SEED )
) dut (
.clk ( clk )
,.rst_n ( rst_n )
,.serial_in ( serial_in )
,.patn_count ( patn_count )
,.tap_step ( tap_step )
,.descram_en ( descram_en ) // now variable
,.link_down ( link_down )
,.delay_tap ( delay_tap )
,.wr_addr ( wr_addr )
,.wr_data ( wr_data )
,.wr_en ( wr_en )
,.byte_mask ( byte_mask )
,.crc_error ( crc_error )
,.tap_adj_mask ( tap_adj_mask )
,.tap_force ( tap_force )
,.tap_adj_req ( tap_adj_req )
,.frame_done ( frame_done )
,.train_status ( train_status )
,.frame_status ( frame_status )
,.always_on ( always_on )
,.prefilling ( prefilling )
,.prefill_start ( prefill_start )
,.train_ready ( train_ready )
,.force_train ( force_train )
);
// ====================================================
// Clock Generation
// ====================================================
initial begin
clk = 0;
forever #(CLK_PERIOD/2) clk = ~clk;
end
// ====================================================
// Tasks for Stimulus Generation
// ====================================================
// Reset task
task reset;
begin
rst_n = 0;
repeat(5) @(posedge clk);
rst_n = 1;
repeat(2) @(posedge clk);
end
endtask
// Send one 128-bit block over 32 cycles
task send_block;
input [127:0] block;
begin
integer i;
for (i = 31; i >= 0; i = i - 1) begin
serial_in[0] = block[i];
serial_in[1] = block[32 + i];
serial_in[2] = block[64 + i];
serial_in[3] = block[96 + i];
@(posedge clk);
#0.1;
end
end
endtask
// Send multiple identical blocks
task send_blocks;
input [127:0] block;
input integer count;
begin
repeat(count) send_block(block);
end
endtask
// Send a sequence of 32-bit words (packed into 128-bit blocks)
task send_words;
input integer num_words;
input [31:0] words[0:255];
begin
integer word_idx;
integer j;
reg [127:0] block;
word_idx = 0;
while (word_idx < num_words) begin
block = 128'h0;
for (j = 0; j < 4; j = j + 1) begin
if (word_idx < num_words) begin
block[32*j +: 32] = words[word_idx];
word_idx = word_idx + 1;
end
end
send_block(block);
end
end
endtask
// ====================================================
// Scrambler tasks (used when descram_en=1)
// ====================================================
// Original scrambler (all lanes scrambled)
task scramble_block;
input [127:0] original;
output [127:0] scrambled;
integer i;
reg [31:0] lane_word;
begin
scrambled = 128'h0;
for (i = 0; i < 4; i = i + 1) begin
lane_word = original[32*i +: 32];
scrambled[32*i +: 32] = lane_word ^ lfsr_lane[i];
// Update LFSR: feedback = lfsr[31]^lfsr[30]^lfsr[29]^lfsr[28]
lfsr_lane[i] = {lfsr_lane[i][30:0],
lfsr_lane[i][31] ^ lfsr_lane[i][30] ^
lfsr_lane[i][29] ^ lfsr_lane[i][28]};
end
end
endtask
// New scrambler with per-lane mask (1=scramble that lane)
task scramble_block_masked;
input [127:0] original;
input [3:0] mask; // 1=scramble this lane
output [127:0] scrambled;
integer i;
reg [31:0] lane_word;
begin
scrambled = 128'h0;
for (i = 0; i < 4; i = i + 1) begin
lane_word = original[32*i +: 32];
if (mask[i]) begin
scrambled[32*i +: 32] = lane_word ^ lfsr_lane[i];
// Update LFSR only for scrambled lanes
lfsr_lane[i] = {lfsr_lane[i][30:0],
lfsr_lane[i][31] ^ lfsr_lane[i][30] ^
lfsr_lane[i][29] ^ lfsr_lane[i][28]};
end else begin
scrambled[32*i +: 32] = lane_word;
// No LFSR update for unscrambled lanes
end
end
end
endtask
// ====================================================
// CRC32 Computation Functions
// ====================================================
function automatic [31:0] crc32_next;
input [31:0] crc;
input [31:0] data;
reg [31:0] new_crc;
reg b;
integer i;
begin
new_crc = crc;
for (i = 0; i < 32; i = i + 1) begin
b = new_crc[31] ^ data[31-i];
new_crc = {new_crc[30:0], 1'b0};
if (b) new_crc = new_crc ^ 32'h04C11DB7;
end
crc32_next = new_crc;
end
endfunction
function automatic [31:0] crc32_compute;
input [31:0] words[0:255];
input integer num;
reg [31:0] crc;
integer i;
begin
crc = 32'hFFFFFFFF;
for (i = 0; i < num; i = i + 1) begin
crc = crc32_next(crc, words[i]);
end
crc32_compute = crc;
end
endfunction
// ====================================================
// Test Variables (module level)
// ====================================================
integer data_words;
reg [31:0] frame_words[0:255];
integer frame_len;
integer i;
// ====================================================
// Test Sequence
// ====================================================
initial begin
// Initialize
serial_in = 4'h0;
patn_count = PATN_COUNT;
tap_step = TAP_STEP;
descram_en = 0; // default disabled
force_train = 0;
$display("========================================");
$display("Testbench started at %0t", $time);
$display("========================================");
reset();
wait(prefill_start == 1);
repeat(10) @(posedge clk);
prefilling = 1;
repeat(100) @(posedge clk);
prefilling = 0;
// -------------------------------------------------
// Phase 1: Link Training (successful)
// -------------------------------------------------
$display("Phase 1: Training with correct patterns...");
send_blocks({4{TRAINING_PATN}}, PATN_COUNT - 0);
send_blocks({4{TRAINING_EXIT}}, 1);
wait(dut.u_train.train_ready == 1);
$display("Link ready at %0t", $time);
// -------------------------------------------------
// Phase 2: Send a correct frame
// -------------------------------------------------
$display("Phase 2: Sending a correct frame...");
data_words = 20;
frame_len = 2 + data_words + 1;
frame_words[0] = FRAME_HEADER;
frame_words[1] = (32'h1235 << 16) | data_words;
for (i = 0; i < data_words; i = i + 1) begin
frame_words[2+i] = 32'hA0000000 + i;
end
// Compute CRC over addr_len and data (1 + data_words words)
begin
reg [31:0] crc_tmp[0:255];
integer k;
for (k = 0; k < 1 + data_words; k = k + 1) begin
crc_tmp[k] = frame_words[1 + k];
end
frame_words[2 + data_words] = crc32_compute(crc_tmp, 1 + data_words);
end
send_words(frame_len, frame_words);
fork
begin
@(posedge wr_en);
$display("Write detected: addr=%0d data=%h mask=%h", wr_addr, wr_data, byte_mask);
if (wr_addr !== 13'h123) $error("Unexpected write address: %0d", wr_addr);
if (byte_mask[31:0] !== 32'hFFFF_0000) $error("Byte mask mismatch: %h", byte_mask);
if (byte_mask[63:32] !== 32'hFFFF_FFFF) $error("Byte mask high part not zero: %h", byte_mask[63:32]);
$display("Correct frame write verified.");
end
join_none
repeat(100) @(posedge clk);
// -------------------------------------------------
// Phase 3: Send a frame with bad CRC
// -------------------------------------------------
$display("Phase 3: Sending a frame with bad CRC...");
frame_words[2+data_words] = ~frame_words[2+data_words];
send_words(frame_len, frame_words);
@(posedge crc_error);
$display("CRC error detected at %0t", $time);
repeat(10) @(posedge clk);
if (link_down) $display("Link down as expected.");
else $error("Link not down after CRC error.");
// -------------------------------------------------
// Phase 4: Re-train and send another correct frame
// -------------------------------------------------
$display("Phase 4: Re-training...");
send_blocks({4{TRAINING_PATN}}, PATN_COUNT - 0);
send_blocks({4{TRAINING_EXIT}}, 1);
wait(dut.u_train.train_ready == 1);
$display("Link ready again.");
// Compute CRC over addr_len and data (1 + data_words words)
begin
reg [31:0] crc_tmp[0:255];
integer k;
for (k = 0; k < 1 + data_words; k = k + 1) begin
crc_tmp[k] = frame_words[1 + k];
end
frame_words[2 + data_words] = crc32_compute(crc_tmp, 1 + data_words);
end
send_words(frame_len, frame_words);
repeat(100) @(posedge clk);
// -------------------------------------------------
// Phase 5: Test delay_tap adjustment on match failure
// -------------------------------------------------
$display("Phase 5: Testing delay_tap adjustment...");
force_train = 1;
repeat(1) @(posedge clk);
force_train = 0;
send_blocks(128'h0, 5);
repeat(200) @(posedge clk);
$display("Final delay_tap = %0d", delay_tap);
// -------------------------------------------------
// Phase 6: Test with scrambler enabled (header not scrambled)
// -------------------------------------------------
$display("Phase 6: Testing with descrambler enabled (header not scrambled)...");
// Reset and train with descram_en=0 (training patterns are not scrambled)
descram_en = 0;
force_train = 1;
repeat(1) @(posedge clk);
force_train = 0;
send_blocks({4{TRAINING_PATN}}, PATN_COUNT - 0);
send_blocks({4{TRAINING_EXIT}}, 1);
wait(dut.u_train.train_ready == 1);
$display("Link ready for scrambled data.");
// Enable descrambler in DUT and initialize LFSRs for transmission
descram_en = 1;
for (i = 0; i < 4; i = i + 1) lfsr_lane[i] = SCRAMBLER_SEED;
// Construct a simple frame (small data length)
data_words = 10;
frame_len = 2 + data_words + 1;
frame_words[0] = FRAME_HEADER;
frame_words[1] = (32'h1234 << 16) | data_words; // base_addr=0x1234, offset=0, len=10
for (i = 0; i < data_words; i = i + 1) begin
frame_words[2+i] = 32'hB0000000 + i; // different pattern from Phase 2
end
// Compute CRC over addr_len and data (1 + data_words words)
begin
reg [31:0] crc_tmp[0:255];
integer k;
for (k = 0; k < 1 + data_words; k = k + 1) begin
crc_tmp[k] = frame_words[1 + k];
end
frame_words[2 + data_words] = crc32_compute(crc_tmp, 1 + data_words);
end
// Now scramble each 128-bit block and send
begin
integer word_idx;
integer j;
integer block_num;
reg [127:0] orig_block, scrambled_block;
reg [3:0] block_mask;
word_idx = 0;
block_num = 0;
while (word_idx < frame_len) begin
orig_block = 128'h0;
// Fill block with up to 4 words, pad with 0 if needed
for (j = 0; j < 4; j = j + 1) begin
if (word_idx < frame_len) begin
orig_block[32*j +: 32] = frame_words[word_idx];
word_idx = word_idx + 1;
end else begin
orig_block[32*j +: 32] = 32'h0; // pad with 0
end
end
// Determine mask: first block leaves lane0 unscrambled (header), others all scrambled
block_mask = (block_num == 0) ? 4'b1110 : 4'b1111;
scramble_block_masked(orig_block, block_mask, scrambled_block);
send_block(scrambled_block);
block_num = block_num + 1;
end
end
// Monitor writes
fork
begin
@(posedge wr_en);
$display("Scrambled test write: addr=%0d data=%h mask=%h", wr_addr, wr_data, byte_mask);
end
join_none
repeat(200) @(posedge clk);
// Check that no CRC error happened
if (crc_error) $error("CRC error in scrambled test");
else $display("Scrambled test passed (no CRC error).");
// Disable scrambler for rest of test
descram_en = 0;
// -------------------------------------------------
// End of test
// -------------------------------------------------
repeat(50) @(posedge clk);
$display("========================================");
$display("Testbench finished at %0t", $time);
$display("========================================");
$finish;
end
// ====================================================
// Monitor and Assertions
// ====================================================
always @(posedge clk) begin
if (wr_en) $display("WRITE: addr=%0d data=%h mask=%h", wr_addr, wr_data, byte_mask);
if (crc_error) $display("CRC_ERROR pulse at %0t", $time);
end
always @(posedge clk) begin
if (dut.u_train.train_ready && link_down)
$error("Inconsistent state: train_ready and link_down both high");
end
endmodule