/////////////////////////////////////////////////////////////////////////////// // 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