Sharing OSVVM Experience – Miroslav Marinkovic
We are very pleased to post a real example OSVVM experience which has been shared by Miroslav Marinkovic.
Miroslav has made use of Functional coverage, Random test generation and Intelligent Coverage Randomization and Message logging and reporting : Alerts and Affirmations in his testbench.
We want to test BCH code which provides DEC-TED capability (Double-Error-Correction, Triple-Error-Detection).
Such code is able to correct all single bit errors as well as all random double bit errors.
Additionaly, it is able to detect (but not to correct) all random triple bit errors.
The code (a short version with 16, 32, 64 source bits) can be used as EDAC protection in SRAM memories.
Testbench includes both BCH encoder and decoder.
First, functional coverage model shall be created.
For DEC-TED code, we want to test all combinations of single, double and triple bit error patterns.
If N is a codeword bit-width, the number of error patterns is :
– single bit errors (SBE) : N
– double bit errors (DBE) : N*(N-1)/2;
– triple bit errors (TBE) : N*(N-1)*(N-2)/6;
We declare firstly the constant for codeword length (bit-width):
constant c_edac_codeword_width : natural := 45;
Then, three coverage objects are declared:
shared variable v_cover_sbe : CovPType;
shared variable v_cover_dbe : CovPType;
shared variable v_cover_tbe : CovPType;
…
Create functional coverage model :
-- coverage point for single bit errors
v_cover_sbe.AddBins(AtLeast => c_at_least, CovBin => GenBin(0, c_edac_codeword_width - 1));
-- coverage point for double bit errors
for i in 0 to c_edac_codeword_width - 2 loop
v_cover_dbe.AddCross(AtLeast => c_at_least, Bin1 => GenBin(i), Bin2 => GenBin(i + 1, c_edac_codeword_width - 1));
end loop;
-- coverage point for triple bit errors
for i in 0 to c_edac_codeword_width - 3 loop
for j in i + 1 to c_edac_codeword_width - 2 loop
v_cover_tbe.AddCross(AtLeast => c_at_least, Bin1 => GenBin(i), Bin2 => GenBin(j),
Bin3 => GenBin(j + 1, c_edac_codeword_width - 1));
end loop;
end loop;
…
Encoder data input(source bits) should be randomized :
…
variable v_data_in : unsigned(c_edac_data_width - 1 downto 0);
-- maximum number of input data words to be encoded
constant c_max_num_data : positive := 50000;
constant c_max : unsigned(c_edac_data_width - 1 downto 0) := (others => '1');
-- Declare random item object
variable v_random_data_in : RandomPType;
…
-- Generate random stimuli until the functional coverage is achieved or
-- the maximum number of data words is achieved
while (not(v_coverage_done) and v_num_data_in < c_max_num_data) loop
wait until rising_edge(clk);
-- Use RandUnsigned method for large vector randomization
v_data_in := v_random_data_in.RandUnsigned(c_max);
-- Encoder data input
data_in <= std_logic_vector(v_data_in);
v_coverage_done := (v_cover_sbe.IsCovered and v_cover_dbe.IsCovered and v_cover_tbe.IsCovered);
v_num_data_in := v_num_data_in + 1;
end loop;
…
The key block of our testbench is an error injection block placed between the encoder and the decoder.
In the error injection block, we use Intelligent Coverage Randomization (for the error patterns),
because a smaller number of input data words can achieve the functional coverage !
…
p_error_inj : process
variable v_random_err : RandomPType;
variable v_num_err : natural range 0 to 3;
variable v_bit_sbe : integer_vector(0 to 0);
variable v_bit_dbe : integer_vector(0 to 1);
variable v_bit_tbe : integer_vector(0 to 2);
variable v_dout : std_logic_vector(c_edac_codeword_width - 1 downto 0);
begin
...
-- generate numbers 0, 1, 2, 3 with probabilites given by constants
-- c_likelihood_no_err, c_likelihood_single_err, c_likelihood_double_err, c_likelihood_triple_err, respectively
v_num_err := v_random_err.DistValInt(( (0, c_likelihood_no_err), (1, c_likelihood_single_err),
(2, c_likelihood_double_err),(3, c_likelihood_triple_err) )) ;
case v_num_err is
when 0 => -- no errors
rx_data <= encoded_data;
when 1 => -- single bit error
-- Intelligent Coverage Randomization !
v_bit_sbe := v_cover_sbe.RandCovPoint;
v_dout := encoded_data;
-- invert a bit
v_dout(v_bit_sbe(0)) := not(encoded_data(v_bit_sbe(0)));
-- Decoder input data
rx_data <= v_dout;
-- update coverage information
v_cover_sbe.ICover(v_bit_sbe);
when 2 => -- double bit error
-- Intelligent Coverage Randomization !
v_bit_dbe := v_cover_dbe.RandCovPoint;
v_dout := encoded_data;
-- invert two bits
v_dout(v_bit_dbe(0)) := not(encoded_data(v_bit_dbe(0)));
v_dout(v_bit_dbe(1)) := not(encoded_data(v_bit_dbe(1)));
-- Decoder input data
rx_data <= v_dout;
-- update coverage information
v_cover_dbe.ICover(v_bit_dbe);
when 3 => -- triple bit error
-- Intelligent Coverage Randomization !
v_bit_tbe := v_cover_tbe.RandCovPoint;
v_dout := encoded_data;
-- invert three bits
v_dout(v_bit_tbe(0)) := not(encoded_data(v_bit_tbe(0)));
v_dout(v_bit_tbe(1)) := not(encoded_data(v_bit_tbe(1)));
v_dout(v_bit_tbe(2)) := not(encoded_data(v_bit_tbe(2)));
-- Decoder input data
rx_data <= v_dout;
-- update coverage information
v_cover_tbe.ICover(v_bit_tbe);
when others => null;
end case;
...
We check the decoder output vs. reference values (source bits), by using AffirmIfEqual procedure.
Triple bit errors are uncorrectable, so shall be excluded.
constant c_alert_decoder : AlertLogIDType := GetAlertLogID(“DECODER DATA VECTORS”, ALERTLOG_BASE_ID);
…
if triple_bit_err_edac = '0' then
-- If Affirmation is false, then an alert is generated to increment Alert counter
AffirmIfEqual(AlertLogID => c_alert_decoder, Received => corrected_data_out, Expected => data_reference, Message => "BCH Decoder output =>");
end if;
During simulation, we are logging the number of injected errors as well as the number of corrected/detected errors by the decoder.
For that purpose, we use error counters.
When the simulation is finished, we print reports :
– Functional coverage reports for SBE, DBE and TBE;
– Error Counter values. The number of injected errors shall be equal to the number of corrected (SBE, DBE) or the number of detected (TBE) errors !
For example, by using AffirmIf procedure :
constant c_alert_fc_dbe : AlertLogIDType := GetAlertLogID("FC DBE", ALERTLOG_BASE_ID);
-- print coverpoint report in detail if true
constant c_coverpoint_report_detail : boolean := FALSE;
…
-- Print message about functional coverage for DBE
AffirmIf(AlertLogID => c_alert_fc_dbe, condition => v_cover_dbe.IsCovered,
ReceivedMessage => "Functional Coverage for DBE achieved !",
ExpectedMessage => "Functional Coverage for DBE NOT achieved !",
Enable => TRUE);
if c_coverpoint_report_detail then
Log("Generate coverpoint report for DBE", INFO, TRUE);
v_cover_dbe.WriteBin;
end if;
…
constant c_alert_dbe : AlertLogIDType := GetAlertLogID("TEST DBE", ALERTLOG_BASE_ID);
…
-- Print message about DBE
AffirmIf(AlertLogID => c_alert_dbe, condition => (v_counter_double_err_inj = v_counter_double_err_edac),
ReceivedMessage => "Number of injected DBE is equal to number of detected errors by EDAC = " & to_string(v_counter_double_err_edac),
ExpectedMessage => "Number of injected DBE is NOT equal to number of detected errors by EDAC = : number of injected = "
& to_string(v_counter_double_err_inj), Enable => TRUE);
We hope that you found this experience useful and we thank Miroslav for taking the time to share his experience.