URL
https://opencores.org/ocsvn/dvb_s2_ldpc_decoder/dvb_s2_ldpc_decoder/trunk
Subversion Repositories dvb_s2_ldpc_decoder
[/] [dvb_s2_ldpc_decoder/] [trunk/] [tb/] [tb_ldpc_vcs.v] - Rev 2
Compare with Previous | Blame | View Log
`timescale 1ns/10ps module tb_ldpc(); localparam CLK_PERIOD = 5ns; localparam HOLD = 1ns; localparam SYMS_PER_EBN0 = 1; localparam EBN0_MIN = 0.6; localparam EBN0_MAX = 1.8; localparam EBN0_STEP = 0.2; localparam CODE_TYPE = "1_4"; localparam LLRWIDTH = 6; ////////// // Clocks ////////// logic clk; logic rst; initial begin clk <= 1'b0; forever #(CLK_PERIOD /2) clk <= ~clk; end initial begin rst <= 1'b1; repeat(3) @(posedge clk); rst <= #HOLD 1'b0; end // VCS was crashing with my old testbench, so I simplified dramatically and got rid of // SystemVerilog classes int debug_level = 0; // -1=no output, 0=report error, 1=display some info, 2=display lots string label = CODE_TYPE; // Data int n; int k; int q; int word_width; // Parity function int h_defs_file; int h_defs_height; int h_defs_width; int h_defs[]; // binary data int orig_data[64800]; int coded_data[64800]; int decoded_data[64800]; // data after AWGN real ebn0, N0; real r[64800]; int r_quantized[64800]; /////////////// // Functions /////////////// function void encode(); int parity_bits[]; parity_bits = new[n-k]; for( int rownum=0; rownum<h_defs_height; rownum++ ) for( int colnum=0; colnum<h_defs_width; colnum++ ) begin int base_position; base_position = h_defs[rownum*h_defs_width + colnum]; if( base_position!=-1 ) for( int local_offset=0; local_offset<360; local_offset++ ) begin int parity_address; parity_address = (base_position + local_offset*q) % (n-k); parity_bits[parity_address] ^= orig_data[rownum*360 + local_offset]; end end for( int parityloc=1; parityloc<n-k; parityloc++ ) parity_bits[parityloc] ^= parity_bits[parityloc-1]; // Copy input to output for( int j=0; j<n; j++ ) begin if( j<k ) coded_data[j] = -2*orig_data[j] +1; else coded_data[j] = -2*parity_bits[j-k] +1; end parity_bits.delete(); endfunction // Add noise to coded data and store result in r function void AddNoise( real ebn0db ); real noisevec[64800]; real rate; int result_10000; int stddev_10000; rate = (1.0*k)/ (1.0*n); N0 = 10.0**(-ebn0db/10.0) / rate / 2.0; $display( "RATE, N0 = %0f, %0f", rate, N0 ); N0 *= 1.1; // match to C simulation noise genNoiseVec( noisevec ); // print noise vector for( int j=0; j<n; j++ ) if( debug_level>1 ) $display( "noise %d = %f", j, noisevec[j] ); // add noise vector for( int j=0; j<n; j++ ) r[j] = 1.0*coded_data[j] + noisevec[j]; // Convert to LLR // Based on IT++: LLR=received * 4 / N0 (I don't know why) for( int i=0; i<n; i++ ) r[i] *= 4.0 / N0; endfunction // Quantize LLR to a few bits function void QuantizeLlr( int llr_bits ); real MAXVAL; int table_range; real quant_table[2048]; // only 2*table_range is used! MAXVAL = 12.0 / N0; table_range = 1<<(llr_bits-1); // build table for( int i=0; i<2*table_range; i++ ) begin quant_table[i] = 0.5*i * MAXVAL / (1.0*table_range-1.0); //if( this.debug_level>0 ) $display( "quant table[%0d] = %0f", i, quant_table[i] ); end // find correct place in table for all values for( int i=0; i<n; i++ ) begin int orig_sign; int j; orig_sign = r[i] < 0.0 ? -1 : 1; j=2*table_range-1; while( (r[i]*orig_sign) < quant_table[j] ) j--; j = j>>1; r_quantized[i] = orig_sign * j; end if( debug_level>1 ) for( int i=0; i<10; i++ ) $display( "%0d --> %0d --> %0f --> %0d", orig_data[i], coded_data[i], r[i], r_quantized[i] ); endfunction // Compare signs of r_quantized and coded_data to return number of errors function int CountOrigErrs(); CountOrigErrs = 0; for( int i=0; i<n; i++ ) if( ((r_quantized[i]<0) && (coded_data[i]>=0)) || ((r_quantized[i]>=0) && (coded_data[i]<0)) ) CountOrigErrs++; endfunction // Compare signs of r_quantized and coded_data to return number of errors after decode function int CountDecodedErrs(); CountDecodedErrs = 0; for( int i=0; i<n; i++ ) if( ((decoded_data[i]<0) && (coded_data[i]>=0)) || ((decoded_data[i]>=0) && (coded_data[i]<0)) ) CountDecodedErrs++; endfunction function void genNoiseVec( output real result[64800] ); string filename = { "noise_", CODE_TYPE, ".txt" }; real rand_noise_vec[10000]; int noise_file = $fopen( filename, "r" ); int check_eof; for( int i=0; i<10000; i++ ) check_eof = $fscanf( noise_file, "%f", rand_noise_vec[i] ); for( int i=0; i<68000; i++ ) result[i] = rand_noise_vec[ { $random() } % 64800 ]; $fclose( noise_file ); endfunction ////////////////////////// // Read matrix from file ////////////////////////// // Variables for file input int found_target; string oneline; string locallabel; int temp_i; initial begin if( debug_level>0 ) $display( "Creating object" ); // H is a sparse matrix. The location of each one is stored as an integer. // -1 is used to represent unused memory locations. h_defs_file = $fopen( "dvbs2_hdef.txt", "r" ); if( !h_defs_file ) $stop( "File dvbs2_hdef.txt not found\n" ); h_defs_width = 30; found_target = 0; while( !found_target ) begin temp_i = $fgets( oneline, h_defs_file ); temp_i = $sscanf( oneline, "label %s lines %d n %d q %d", locallabel, h_defs_height, n, q ); if( label==locallabel ) found_target = 1; else // discard this group for( int linenum=0; linenum<h_defs_height; linenum++ ) temp_i = $fgets( oneline, h_defs_file ); if( $feof(h_defs_file) ) begin $fclose(h_defs_file); $stop( "Didn't find requested code type!" ); end end // at this point, the label has been found and the file pointer is at the correct position h_defs = new[h_defs_height*h_defs_width]; // fill array with -1 for( int hdef_pos=0; hdef_pos<h_defs_height*h_defs_width; hdef_pos++ ) h_defs[hdef_pos] = -1; // put correct values in array for( int linenum=0; linenum<h_defs_height; linenum++ ) begin int eol; string c; string onechar; string oneword; int word_offset; eol = 0; oneword = ""; word_offset = 0; while( !$feof(h_defs_file) && !eol ) begin c = $fgetc( h_defs_file ); // onechar = (string)c; eol = (c=="\n"); oneword = { oneword, c }; if( eol || (c==" ") ) begin temp_i = $sscanf( oneword, "%d", h_defs[linenum*h_defs_width + word_offset] ); word_offset = word_offset + 1; oneword = ""; end end end $fclose(h_defs_file); k = n - 360*q; end ////////////// // Transactors ////////////// // Load data into LLR // LLR I/O logic llr_access; logic[7:0] llr_addr; logic llr_din_we; logic[LLRWIDTH-1:0] llr_din; bit signed[LLRWIDTH-1:0] llr_dout; // start command; completion indicator logic start; logic[4:0] mode; logic[5:0] iter_limit; bit done; initial begin debug_level = 0; llr_access <= 0; llr_addr <= 0; llr_din_we <= 0; llr_din <= 0; start <= 0; mode <= 0; iter_limit <= 30; @( posedge rst ); repeat( 5 ) @( posedge clk ); for( real ebn0dB=EBN0_MIN; ebn0dB<EBN0_MAX; ebn0dB+=EBN0_STEP ) begin int err_bits = 0; int tested_bits = 0; real ber_result; err_bits = 0; tested_bits = 0; //for( int symnum=0; symnum<SYMS_PER_EBN0; symnum++ ) while( (err_bits<1000) && (tested_bits<1000000) ) begin // create random data for( int i=0; i<k; i++ ) orig_data[i] = {$random()} % 2; encode(); AddNoise( ebn0dB ); QuantizeLlr( LLRWIDTH ); // begin testing DUT llr_access <= 1; @(posedge clk); // write normal data bits for( int i=0; i<k; i++ ) begin int wr_val; int abs_wr_val; wr_val = r_quantized[i]; abs_wr_val = wr_val < 0 ? -1*wr_val : wr_val; llr_addr <= #HOLD i/360; llr_din_we <= #HOLD (i%360)==359; llr_din <= #HOLD (wr_val<0) ? { 1'b1, abs_wr_val[LLRWIDTH-2:0] } : { 1'b0, abs_wr_val[LLRWIDTH-2:0] }; @( posedge clk ); end // write parity bits for( int i=0; i<(n-k); i++ ) begin int rotate_pos; int wr_val; int abs_wr_val; rotate_pos = k + (i/360) + ((i%360)*q); llr_addr <= #HOLD (k + i)/360; llr_din_we <= #HOLD (i%360)==359; wr_val = r_quantized[rotate_pos]; abs_wr_val = wr_val < 0 ? -1*wr_val : wr_val; llr_din <= #HOLD (wr_val<0) ? { 1'b1, abs_wr_val[LLRWIDTH-2:0] } : { 1'b0, abs_wr_val[LLRWIDTH-2:0] }; @( posedge clk ); end llr_din_we <= #HOLD 0; llr_access <= #HOLD 0; @( posedge clk ); //controls start <= #HOLD 1; case( CODE_TYPE ) "1_4": mode <= #HOLD 0; "1_3": mode <= #HOLD 1; "2_5": mode <= #HOLD 2; "1_2": mode <= #HOLD 3; "3_5": mode <= #HOLD 4; "2_3": mode <= #HOLD 5; "3_4": mode <= #HOLD 6; "4_5": mode <= #HOLD 7; "5_6": mode <= #HOLD 8; "8_9": mode <= #HOLD 9; "9_10": mode <= #HOLD 10; "1_5s": mode <= #HOLD 11; "1_3s": mode <= #HOLD 12; "2_5s": mode <= #HOLD 13; "4_9s": mode <= #HOLD 14; "3_5s": mode <= #HOLD 15; "2_3s": mode <= #HOLD 16; "11_15s": mode <= #HOLD 17; "7_9s": mode <= #HOLD 18; "37_45s": mode <= #HOLD 19; "8_9s": mode <= #HOLD 20; default: $stop( "Illegal code type!" ); endcase @( posedge clk ); start <= #HOLD 0; @( posedge clk ); // read data out @(posedge done); @(posedge clk); llr_addr <= #HOLD 0; llr_access <= #HOLD 1; @(posedge clk); // read normal data bits for( int i=0; i<k; i++ ) begin int result; if( (i%360)==0 ) begin llr_addr <= #HOLD i/360; repeat(2) @(posedge clk); llr_din_we <= #HOLD 1; //load up from RAM @( posedge clk ); llr_din_we <= #HOLD 0; @( posedge clk ); end llr_din <= #HOLD { LLRWIDTH{1'bX} }; result = llr_dout[LLRWIDTH-1] ? -1 * llr_dout[LLRWIDTH-2:0] : llr_dout[LLRWIDTH-2:0]; decoded_data[i] = result; //if( i<10 ) $display( "After decode: pos=%0d, result=%0d", i, result ); @( posedge clk ); end $display( "Begin read parity bits" ); // read parity bits for( int i=0; i<(n-k); i++ ) begin int result; int rotate_pos; if( ((k + i)%360)==0 ) begin llr_addr <= #HOLD (k + i)/360; repeat(2) @(posedge clk); llr_din_we <= #HOLD 1; //load up from RAM @( posedge clk ); llr_din_we <= #HOLD 0; @( posedge clk ); end rotate_pos = k + (i/360) + ((i%360)*q); llr_din <= #HOLD { LLRWIDTH{1'bX} }; result = llr_dout[LLRWIDTH-1] ? -1 * llr_dout[LLRWIDTH-2:0] : llr_dout[LLRWIDTH-2:0]; decoded_data[rotate_pos] = result; @( posedge clk ); end llr_din_we <= #HOLD 0; llr_access <= #HOLD 0; @( posedge clk ); tested_bits += 64800; err_bits += CountDecodedErrs(); end $write( "EbN0 = %0fdB: ", ebn0dB ); ber_result = 1.0*err_bits / (1.0*tested_bits); $write( "%0d errs / %0d bits ==> BER = %0f\n", err_bits, tested_bits, ber_result ); end h_defs.delete(); $finish(); end //////////// // Instance //////////// ldp_top ldp_top_i( .clk(clk), .rst(rst), .llr_access (llr_access), .llr_addr (llr_addr), .llr_din_we (llr_din_we), .llr_din (llr_din), .llr_dout (llr_dout), .start (start), .mode (mode), .iter_limit(iter_limit), .done (done) ); endmodule