476 lines
18 KiB
Systemverilog
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
|