lin-win-share/DA4008_V1.2/model/LVDS_DRIVER.sv

476 lines
18 KiB
Systemverilog

///////////////////////////////////////////////////////////////////////////////
// LVDS_DRIVER.sv
// Enhanced LVDS Driver (4 lanes, 8 clock cycles per word)
// Reads a 32-bit data word from a text file and sends it, providing LVDS interface driving
// New features: automatic frame header detection, dynamic scrambler mask, CRC32 calculation, TXT frame integrity check
// Added training sequence support (configurable pattern count)
// Added option to ignore CRC errors (ignore_crc_error)
///////////////////////////////////////////////////////////////////////////////
`ifndef LVDS_DRIVER_SV
`define LVDS_DRIVER_SV
//-------------------------------------------------------------------------
// LVDS interface definition
//-------------------------------------------------------------------------
interface lvds_if (input bit clk);
logic valid; // Data valid indication
logic [3:0] data; // Data on 4 lanes
endinterface
//-------------------------------------------------------------------------
// DataReader class: Reads 32-bit hexadecimal data from a file and stores it in MSB-first queue
//-------------------------------------------------------------------------
class DataReader;
bit spi_data_queue[$]; // Bit-level queue (MSB first)
function void read_txt_file(input string filename);
int file_id;
string line;
bit [31:0] value;
int i;
file_id = $fopen(filename, "r");
if (file_id == 0) begin
$display("Error: Failed to open file %s", filename);
return;
end
while (!$feof(file_id)) begin
$fscanf(file_id, "%h\n", value);
// Store the 32-bit word into queue MSB first
for (i = 31; i >= 0; i--) begin
spi_data_queue.push_back(value[i]);
end
end
$fclose(file_id);
endfunction
function void get_data_queue(ref bit data_queue[$]);
data_queue = spi_data_queue;
spi_data_queue.delete();
endfunction
endclass
//-------------------------------------------------------------------------
// lvds_item class: Encapsulates a set of 32-bit data words
//-------------------------------------------------------------------------
class lvds_item;
// Data word queue (each element 32 bits)
bit [31:0] data[$];
function new(string name = "lvds_item");
endfunction
// Unpack from bit stream (MSB first)
function void unpack(ref bit stream[$]);
bit [31:0] word;
data.delete();
while (stream.size() >= 32) begin
for (int i = 0; i < 32; i++) begin
word[31-i] = stream.pop_front();
end
data.push_back(word);
end
endfunction
// Pack to bit stream (MSB first)
function void pack(ref bit stream[$]);
foreach (data[i]) begin
for (int j = 31; j >= 0; j--) begin
stream.push_back(data[i][j]);
end
end
endfunction
// Print item content (debug)
function void print();
$display("LVDS Item: %0d words", data.size());
foreach (data[i]) begin
$display(" data[%0d] = %h", i, data[i]);
end
endfunction
endclass
//-------------------------------------------------------------------------
// lvds_driver class: LVDS driver, supports file-driven, TB-compatible direct sending, and scrambling
//-------------------------------------------------------------------------
class lvds_driver;
// Original members
string file_path; // Data file path
virtual lvds_if drv_if; // Virtual interface
int interval = 0; // Inter-word gap (clock cycles), 0 means continuous transmission
int clk_period = 1600; // Clock period (ps), used only for debug
// Scrambler related members
bit [31:0] lfsr_lane[0:3]; // Independent LFSR states for 4 lanes
bit scrambler_en = 0; // Global scrambler enable (if set, scrambling is automatically applied during transmission)
bit [3:0] scrambler_mask = 4'b1111; // Default scrambler mask, 1 indicates that lane is scrambled
// Frame header auto-detection related
bit [31:0] frame_header = 32'hBCBCBCBC; // Default frame header
bit auto_detect_header = 1; // Whether to auto-detect header and adjust mask
// Frame integrity check enable
bit enable_frame_check = 1; // Whether to perform frame validation on file content before transmission
bit ignore_crc_error = 1; // If set, CRC errors will be warnings (not fatal)
// Training related constants and variables
const bit [31:0] TRAINING_PATN = 32'h68666E6C; // "hfnl"
const bit [31:0] TRAINING_EXIT = 32'h65786974; // "exit"
int train_count = 10; // Number of training pattern blocks to send (can be configured)
function new(string name = "lvds_driver");
// Initialize LFSR (default uses SCRAMBLER_SEED = 32'hFFFFFFFF)
for (int i = 0; i < 4; i++) lfsr_lane[i] = 32'hFFFFFFFF;
endfunction
//-------------------------------------------------------------------------
// Send a 32-bit word (fully compatible with TB approach)
// Place the word on lane0, other lanes constant 0, send MSB first over 32 clocks
//-------------------------------------------------------------------------
local task send_word(bit [31:0] word);
for (int i = 31; i >= 0; i--) begin
@(posedge drv_if.clk);
drv_if.valid <= 1'b1;
drv_if.data[0] <= word[i];
drv_if.data[3:1] <= 3'b0;
end
endtask
//-------------------------------------------------------------------------
// Send an lvds_item (internally calls send_word)
// Note: This method sends only one word on lane0 each time; for parallel transmission use send_words family tasks
//-------------------------------------------------------------------------
task send(lvds_item item);
foreach (item.data[i]) begin
send_word(item.data[i]);
repeat (interval) @(posedge drv_if.clk);
end
@(posedge drv_if.clk);
drv_if.valid <= 1'b0;
drv_if.data <= 4'b0;
endtask
//-------------------------------------------------------------------------
// Send a 128-bit block, fully compatible with TB's send_block (MSB first)
//-------------------------------------------------------------------------
task send_block(input [127:0] block);
for (int i = 31; i >= 0; i--) begin
@(posedge drv_if.clk);
drv_if.valid <= 1'b1;
drv_if.data[0] <= block[i];
drv_if.data[1] <= block[32 + i];
drv_if.data[2] <= block[64 + i];
drv_if.data[3] <= block[96 + i];
end
endtask
//-------------------------------------------------------------------------
// Send multiple identical 128-bit blocks consecutively
//-------------------------------------------------------------------------
task send_blocks(input [127:0] block, input int count);
repeat (count) send_block(block);
endtask
//-------------------------------------------------------------------------
// Send a sequence of 32-bit words (automatically pack into 128-bit blocks, zero-pad if needed)
// Parameter words is an array, num_words is the actual number of words (up to 256)
//-------------------------------------------------------------------------
task send_words(input int num_words, input [31:0] words[0:255]);
int word_idx;
int j;
reg [127:0] block;
word_idx = 0;
while (word_idx < num_words) begin
block = 128'h0;
for (j = 0; j < 4; j++) begin
if (word_idx < num_words) begin
block[32*j +: 32] = words[word_idx];
word_idx++;
end
end
send_block(block);
end
endtask
//-------------------------------------------------------------------------
// Initialize LFSR (all lanes use the same seed)
//-------------------------------------------------------------------------
function void init_scrambler(bit [31:0] seed);
for (int i = 0; i < 4; i++) lfsr_lane[i] = seed;
endfunction
//-------------------------------------------------------------------------
// Update a single LFSR (polynomial: x^32 + x^30 + x^29 + x^28 + 1)
//-------------------------------------------------------------------------
function bit [31:0] lfsr_update(bit [31:0] lfsr);
bit feedback;
feedback = lfsr[31] ^ lfsr[30] ^ lfsr[29] ^ lfsr[28];
lfsr_update = {lfsr[30:0], feedback};
endfunction
//-------------------------------------------------------------------------
// Scramble a single 128-bit block with lane mask support
// Input original: original block; mask: whether each lane is scrambled (1=scramble)
// Output scrambled: scrambled block
// Note: This method updates the internal LFSR state (only for lanes that are scrambled)
//-------------------------------------------------------------------------
function void scramble_block_masked(input [127:0] original, input [3:0] mask, output [127:0] scrambled);
bit [31:0] lane_word;
scrambled = 128'h0;
for (int i = 0; i < 4; i++) begin
lane_word = original[32*i +: 32];
if (mask[i]) begin
scrambled[32*i +: 32] = lane_word ^ lfsr_lane[i];
lfsr_lane[i] = lfsr_update(lfsr_lane[i]); // Update LFSR only for scrambled lanes
end else begin
scrambled[32*i +: 32] = lane_word;
end
end
endfunction
//-------------------------------------------------------------------------
// Send a scrambled 128-bit block (using current scrambler_en and scrambler_mask)
// If scrambler_en is 0, directly send the original block (same as send_block)
//-------------------------------------------------------------------------
task send_block_scrambled(input [127:0] block);
if (scrambler_en) begin
logic [127:0] scrambled;
scramble_block_masked(block, scrambler_mask, scrambled);
send_block(scrambled);
end else begin
send_block(block);
end
endtask
//-------------------------------------------------------------------------
// Send a sequence of scrambled 32-bit words (using fixed scrambler_mask)
//-------------------------------------------------------------------------
task send_words_scrambled(input int num_words, input [31:0] words[0:255]);
int word_idx;
int j;
reg [127:0] block;
word_idx = 0;
while (word_idx < num_words) begin
block = 128'h0;
for (j = 0; j < 4; j++) begin
if (word_idx < num_words) begin
block[32*j +: 32] = words[word_idx];
word_idx++;
end
end
send_block_scrambled(block);
end
endtask
//-------------------------------------------------------------------------
// Frame check function, verifies if words form a valid frame (header, length, CRC)
// Returns 1 for pass, 0 for fail, err_msg outputs error information
//-------------------------------------------------------------------------
static function bit check_frame(bit [31:0] words[$], ref string err_msg);
int num_words = words.size();
bit [31:0] header, addr_len, crc_expected, crc_computed;
int data_len;
bit [31:0] data_words[0:255]; // For CRC calculation
int i;
// Basic length check: at least 3 words (header, addr/len, CRC)
if (num_words < 3) begin
err_msg = "Frame word count less than 3";
return 0;
end
header = words[0];
if (header !== 32'hBCBCBCBC) begin
err_msg = $sformatf("Frame header error, expected 32'hBCBCBCBC, got 32'h%h", header);
return 0;
end
addr_len = words[1];
data_len = addr_len[15:0]; // Lower 16 bits are data length
// Verify total word count: 1(header) + 1(addr/len) + data_len + 1(CRC) = 3 + data_len
if (num_words != 3 + data_len) begin
err_msg = $sformatf("Frame length mismatch: expected %0d words from length field, got %0d words", 3+data_len, num_words);
return 0;
end
// Collect data part (addr_len word + data words) for CRC calculation
// Note: CRC range is from words[1] to words[1+data_len-1] (total 1+data_len words)
for (i = 0; i < 1+data_len; i++) begin
data_words[i] = words[1 + i];
end
crc_computed = crc32_compute(data_words, 1+data_len);
crc_expected = words[2+data_len]; // CRC word
if (crc_computed !== crc_expected) begin
err_msg = $sformatf("CRC error: computed 32'h%h, expected 32'h%h", crc_computed, crc_expected);
return 0;
end
err_msg = "Frame check passed";
return 1;
endfunction
//-------------------------------------------------------------------------
// Read complete frame data from file, auto-detect frame header and dynamically adjust scrambler mask
// The lane containing the frame header is not scrambled, other lanes are scrambled; all subsequent blocks are fully scrambled
// Perform frame integrity check before transmission (if enable_frame_check is 1)
//-------------------------------------------------------------------------
task send_frame_from_file(string filename);
DataReader reader = new();
bit bit_stream[$];
lvds_item item = new();
string err_msg;
reader.read_txt_file(filename);
reader.get_data_queue(bit_stream);
item.unpack(bit_stream);
// Frame check
if (enable_frame_check) begin
if (!check_frame(item.data, err_msg)) begin
if (ignore_crc_error) begin
$warning("Frame check warning: %s, file %s (ignored)", err_msg, filename);
end else begin
$error("Frame check failed: %s, file %s", err_msg, filename);
return; // Stop transmission
end
end else begin
$display("Frame check passed: %s", err_msg);
end
end
send_words_scrambled_auto(item.data);
endtask
//-------------------------------------------------------------------------
// Send word queue, auto-detect frame header and dynamically adjust scrambler mask
// If auto_detect_header is 0 or scrambler_en is 0, use fixed mask (scrambler_mask)
// Otherwise, detect per block; when header is encountered, set that lane mask to 0, subsequent blocks all 1
//-------------------------------------------------------------------------
task send_words_scrambled_auto(bit [31:0] words[$]);
int word_idx;
int block_idx;
bit header_found;
reg [127:0] block;
bit [3:0] mask;
int num;
bit [31:0] words_arr[0:255];
if (!scrambler_en || !auto_detect_header) begin
// Fallback to fixed mask transmission
num = words.size();
foreach (words[i]) words_arr[i] = words[i];
send_words_scrambled(num, words_arr);
return;
end
// Auto-detection mode
word_idx = 0;
block_idx = 0;
header_found = 0;
while (word_idx < words.size()) begin
// Pack current block (up to 4 words)
block = 128'h0;
for (int j = 0; j < 4; j++) begin
if (word_idx < words.size()) begin
block[32*j +: 32] = words[word_idx];
word_idx++;
end
end
// Determine scrambler mask for current block
if (!header_found) begin
mask = 4'b1111; // Default all scrambled
// Check if current block contains the frame header
for (int j = 0; j < 4; j++) begin
if (word_idx - 4 + j < words.size()) begin // Ensure index is valid
if (block[32*j +: 32] == frame_header) begin
mask[j] = 1'b0; // Lane containing header is not scrambled
header_found = 1;
// Note: Even if multiple headers appear in one block (theoretically impossible), we handle only the first
end
end
end
end else begin
mask = 4'b1111; // Header already found, subsequent blocks all scrambled
end
// Send current block (scrambled according to mask)
if (scrambler_en) begin
logic [127:0] scrambled;
scramble_block_masked(block, mask, scrambled);
send_block(scrambled);
end else begin
send_block(block);
end
block_idx++;
end
endtask
//-------------------------------------------------------------------------
// Main driving task: Read data from file and send (using full parallel 128-bit scrambled transmission)
// Automatically detect frame header and apply scrambler mask (if scrambler enabled)
//-------------------------------------------------------------------------
task do_drive();
send_frame_from_file(file_path);
endtask
//-------------------------------------------------------------------------
// Send training sequence: train_count blocks of TRAINING_PATN, then one block of TRAINING_EXIT
// Optionally disable scrambling during training
//-------------------------------------------------------------------------
task send_training();
bit [127:0] train_block = {4{TRAINING_PATN}};
bit [127:0] exit_block = {4{TRAINING_EXIT}};
bit prev_scrambler_en = scrambler_en;
scrambler_en = 0; // Training patterns are not scrambled
send_blocks(train_block, train_count);
send_block(exit_block);
scrambler_en = prev_scrambler_en;
endtask
//-------------------------------------------------------------------------
// Optional: Combined task to send training then data frame
//-------------------------------------------------------------------------
task send_training_and_data(string filename);
send_training();
send_frame_from_file(filename);
endtask
//-------------------------------------------------------------------------
// CRC32 calculation functions (polynomial 0x04C11DB7, initial value 0xFFFFFFFF)
//-------------------------------------------------------------------------
static function automatic bit [31:0] crc32_next(input bit [31:0] crc, input bit [31:0] data);
bit [31:0] new_crc;
bit b;
new_crc = crc;
for (int i = 0; i < 32; i++) 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
return new_crc;
endfunction
static function automatic bit [31:0] crc32_compute(input bit [31:0] words[0:255], input int num);
bit [31:0] crc = 32'hFFFFFFFF;
for (int i = 0; i < num; i++) begin
crc = crc32_next(crc, words[i]);
end
return crc;
endfunction
endclass
`endif // LVDS_DRIVER_SV