-------------------------------------------------------------------------------- -- Copyright 2000 Xilinx, Inc. All rights reserved. -------------------------------------------------------------------------------- -- -- Description: Testbench for Xilinx Reed-Solomon Tutorial 2. -- -- If you intend to use this file to simulate a parameter combination -- which is different to the combination in tutorial2, you -- must make some changes to this file before you can compile and -- simulate properly with it. -- Search for the word CHECKPOINT in this file. There are two -- checkpoints where you must follow the instructions exactly. -- -------------------------------------------------------------------------------- LIBRARY ieee; USE ieee.std_logic_1164.ALL; LIBRARY work; USE work.utils_pkg.ALL; USE work.pgm_pkg.ALL; ENTITY tb_rs_decoder IS GENERIC ( rfile : STRING := "received.pgm"; dfile : STRING := "decoded.pgm" ); END tb_rs_decoder; ARCHITECTURE example OF tb_rs_decoder IS ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- -- CHECKPOINT 1 -- -- The component declared below must match the component declaration -- given in rsdec_wrap.vhd i.e. -- 1. The component name must be "rsd" -- 2. There must be the same number of ports -- 3. The port names must match -- 4. The bus widths for data_in, data_out err_cnt and (if applicable) erase_cnt -- must match -- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- COMPONENT rsd PORT ( data_in : IN STD_LOGIC_VECTOR(7 DOWNTO 0); sync : IN STD_LOGIC; reset : IN STD_LOGIC; clk : IN STD_LOGIC; ce : IN STD_LOGIC := '1'; sr : IN STD_LOGIC := '0'; data_out : OUT STD_LOGIC_VECTOR(7 DOWNTO 0); blk_strt : OUT STD_LOGIC; blk_end : OUT STD_LOGIC; err_found : OUT STD_LOGIC; err_cnt : OUT STD_LOGIC_VECTOR(4 DOWNTO 0); fail : OUT STD_LOGIC; ready : OUT STD_LOGIC ); END COMPONENT; ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- -- END OF CHECKPOINT 1 -- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- -- CONSTANT DECLARATIONS -- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- -- CHECKPOINT 2 -- -- The values for gen_start, k, n, polynomial, symbol_width, -- c_has_ce, c_has_erase, c_has_sr, sync_mode, and clks_per_sym -- must match the values given in the configuration statement in rsdec_wrap.vhd -- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- CONSTANT gen_start : NATURAL := 1; CONSTANT k : NATURAL := 187; CONSTANT n : NATURAL := 207; CONSTANT polynomial : NATURAL := 391; CONSTANT symbol_width : NATURAL := 8; CONSTANT c_has_ce : NATURAL := 1; CONSTANT c_has_erase : NATURAL := 0; CONSTANT c_has_sr : NATURAL := 1; CONSTANT sync_mode : NATURAL := 0; CONSTANT clks_per_sym : NATURAL := 1; ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- -- END OF CHECKPOINT 2 -- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- CONSTANT t : NATURAL := (n-k)/2; CONSTANT r : NATURAL := select_val(i0=>2*t, i1=>n-k, sel=>c_has_erase); CONSTANT clks_per_sym_minus_1 : NATURAL := clks_per_sym-1; CONSTANT c_has_ce_bool : BOOLEAN := c_has_ce /= 0; CONSTANT c_has_sr_bool : BOOLEAN := c_has_sr /= 0; ------------------------------------------------------------------------------- -- -- Calculate Processing Delay -- ------------------------------------------------------------------------------- FUNCTION calc_proc_delay RETURN NATURAL IS VARIABLE proc_delay : NATURAL; BEGIN proc_delay := 0; IF c_has_erase = 0 THEN FOR i IN 1 TO (t+1) LOOP proc_delay := proc_delay + i; END LOOP; proc_delay := t*t + 2*(proc_delay + 3*t) + 2; ELSE FOR i IN 1 TO (r+1) LOOP proc_delay := proc_delay + i; END LOOP; proc_delay := 2*proc_delay + 3*r + 3; END IF; -- c_has_erase -- Value of proc_delay is in symbol periods proc_delay := proc_delay / clks_per_sym; RETURN proc_delay; END calc_proc_delay; -- Processing delay CONSTANT proc_delay : NATURAL := calc_proc_delay; ------------------------------------------------------------------------------- -- -- Calculate Decoder Latency -- ------------------------------------------------------------------------------- FUNCTION calc_latency RETURN NATURAL IS VARIABLE apparent_proc_delay : NATURAL; VARIABLE elatency, latency : NATURAL; BEGIN IF symbol_width = 8 THEN elatency := 2; ELSE elatency := 1; END IF; -- Total latency in symbol periods latency := proc_delay+n+5+elatency; IF (clks_per_sym > 1) THEN apparent_proc_delay := proc_delay + 1; ELSE apparent_proc_delay := proc_delay; END IF; -- clks_per_sym -- Report latency as defined in datasheet. 1 must be subtracted from -- above result, as datasheet defines latency as being from when the -- first symbol is sampled ASSERT (FALSE) REPORT lf&"Processing delay = "&int_2_string(apparent_proc_delay)&lf& "Latency = "&int_2_string(latency-1)&lf SEVERITY NOTE; RETURN latency; END calc_latency; CONSTANT latency : NATURAL := calc_latency; ------------------------------------------------------------------------------- -- -- Calculate total number of cycles before it is safe to input a subsequent -- code word block. -- ------------------------------------------------------------------------------- FUNCTION calc_total_proc_delay RETURN NATURAL IS BEGIN IF proc_delay <= n THEN RETURN n; ELSE RETURN proc_delay; END IF; -- proc_delay END calc_total_proc_delay; CONSTANT total_proc_delay : NATURAL := calc_total_proc_delay; -- -- read in input pgm image file -- CONSTANT received_pgm : pgm_record_type := read_pgm_file(rfile); -- -- calculate how many code words are required to decode image -- CONSTANT num_code_words : NATURAL := received_pgm.width*received_pgm.height/n; -- -- set no_debug=FALSE to print simulation stage reports -- CONSTANT no_debug : BOOLEAN := FALSE; -- -- define simulation clk period and input signals hold time -- CONSTANT clk_period : TIME := 20 NS; -- 50 MHz CONSTANT t_hold : TIME := 1 NS; ------------------------------------------------------------------------------- -- -- SIGNAL DECLARATIONS -- ------------------------------------------------------------------------------- -- core input signals SIGNAL reset : STD_LOGIC := '0'; SIGNAL clk : STD_LOGIC := '1'; SIGNAL sr : STD_LOGIC; SIGNAL ce : STD_LOGIC; SIGNAL erase : STD_LOGIC; SIGNAL sync : STD_LOGIC; SIGNAL data_in : STD_LOGIC_VECTOR(symbol_width - 1 DOWNTO 0); -- core output signals SIGNAL data_out : STD_LOGIC_VECTOR(symbol_width - 1 DOWNTO 0); SIGNAL erase_cnt : STD_LOGIC_VECTOR((bitsneededtorepresent(n) - 1) DOWNTO 0); SIGNAL err_cnt : STD_LOGIC_VECTOR((bitsneededtorepresent(n - k) - 1) DOWNTO 0); SIGNAL err_found : STD_LOGIC; SIGNAL fail : STD_LOGIC; SIGNAL blk_strt : STD_LOGIC; SIGNAL blk_end : STD_LOGIC; SIGNAL ready : STD_LOGIC; -- -- the following temporary signals are used to ensure there are no setup -- and hold time violations on the core's input signals. The temporary -- signals change in step with clk, while the core input signals -- change "t_hold" ns later i.e. enable is a delayed version (t_hold ns) of -- enable_tmp -- SIGNAL sr_tmp : STD_LOGIC := '0'; SIGNAL ce_tmp : STD_LOGIC := '1'; SIGNAL erase_tmp : STD_LOGIC := '0'; SIGNAL sync_tmp : STD_LOGIC := '0'; SIGNAL data_in_tmp : STD_LOGIC_VECTOR(symbol_width - 1 DOWNTO 0) := (OTHERS => '0'); -- -- this flag triggers pgm file writing when set to true -- SIGNAL simulation_finished : BOOLEAN := FALSE; ------------------------------------------------------------------------------- -- -- PROCEDURES -- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- -- Wait for c clock cycles -- ------------------------------------------------------------------------------- PROCEDURE cycle (c :IN NATURAL) IS BEGIN IF (c > 0) THEN FOR cycle_num IN 1 TO c LOOP WAIT UNTIL clk'EVENT AND clk = '1'; END LOOP; ELSE -- catch c = 0 WAIT UNTIL clk'EVENT AND clk = '1'; END IF; END cycle; ------------------------------------------------------------------------------- -- -- Beginning of architecture block -- ------------------------------------------------------------------------------- BEGIN -- -- Define clk -- toggle_clk : PROCESS BEGIN clk <= '1'; WAIT FOR clk_period/2; clk <= '0'; WAIT FOR clk_period/2; END PROCESS; -- toggle_clk -- -- All synchronous inputs are updated t_hold after a rising clock -- edge to control the set up time. -- inputs : PROCESS BEGIN IF (clk'EVENT AND clk = '1') THEN WAIT FOR t_hold; sr <= sr_tmp; ce <= ce_tmp; erase <= erase_tmp; sync <= sync_tmp; data_in <= data_in_tmp; END IF; WAIT ON clk; END PROCESS; -- inputs -- -- Main simulation process -- sim : PROCESS BEGIN simulation_finished <= FALSE; reset <= '1'; sr_tmp <= '0'; ce_tmp <= '1'; erase_tmp <= '0'; sync_tmp <= '0'; cycle(5*clks_per_sym); reset <= '0'; cycle(clks_per_sym); -- -- The following cycle(clks_per_sym - 1) illustrates re-syncing on -- the second sync pulse when clks_per_sym > 1. -- Note that the first symbol will be sampled twice. The uncorrected -- version of the first symbol will appear on data_out one -- clks_per_sym cycle BEFORE blk_start is asserted, then the -- corrected version will appear along with blk_strt being asserted. -- (Make sure that the first symbol has an error in it) cycle(clks_per_sym - 1); -- ASSERT no_debug REPORT "Decoding "& int_2_string(num_code_words) & " code words " & "using RS("& int_2_string(n)&", "& int_2_string(k)&")." SEVERITY NOTE; -- -- Define data_in_tmp and sync_tmp -- note that if you are using multiple clocks per symbol and you -- change any code within the following for loop, you should -- ensure that the total number of clk cycles is a multiple -- of clks_per_sym for each iteration of the loop. -- This is to ensure that the sim and data_collection processes -- and the core are always in sync. -- FOR cbi IN 0 TO (num_code_words - 1) LOOP -- -- assert sync_tmp for first symbol of code word block -- sync_tmp <= '1'; data_in_tmp <= natural_to_std_logic_vector(received_pgm.pixel(cbi*n), symbol_width); cycle(clks_per_sym); sync_tmp <= '0'; FOR si IN 1 TO (n - 1) LOOP data_in_tmp <= natural_to_std_logic_vector(received_pgm.pixel(cbi*n + si), symbol_width); cycle(clks_per_sym); END LOOP; -- si IF (total_proc_delay > n) THEN -- -- wait until it is safe to input next code block -- i.e. decoder must be non-streaming because Processing Delay -- is greater than n -- FOR esi IN 1 TO (total_proc_delay - n) LOOP cycle(clks_per_sym); END LOOP; END IF; -- -- Simulation can take several minutes so give a % progress -- report so that user can gauge how long simulation is likely to take. -- IF (cbi > 0 AND (cbi REM (num_code_words/10) = 0)) THEN ASSERT no_debug REPORT int_2_string((100*cbi)/num_code_words) & "% of code words decoded." SEVERITY NOTE; END IF; END LOOP; -- bi -- End simulation cycle(latency*clks_per_sym); -- ensure last code word is decoded ASSERT no_debug REPORT "100% of code words decoded." SEVERITY NOTE; simulation_finished <= TRUE; cycle(1); -- make sure pgm files are written ASSERT no_debug REPORT lf & "Received file: " & rfile & lf & "Decoded file: " & dfile & lf SEVERITY NOTE; ASSERT FALSE REPORT "Simulation is complete (this isn't a real failure)." SEVERITY FAILURE; WAIT; END PROCESS; -- sim -- -- collect data_out into decoded_pgm record and write record to file. -- data_collection : PROCESS (clk) VARIABLE dpi : NATURAL := 0; VARIABLE ci : NATURAL := 0; -- clk index within symbol period VARIABLE code_word_started : BOOLEAN := FALSE; VARIABLE decoded_pgm : pgm_record_type; VARIABLE wrote_decoded_pgm_file : BOOLEAN := FALSE; BEGIN IF (clk'EVENT AND clk = '1') THEN -- -- only add values on data_out to record if code word is -- being outputted - use code_word_started to flag this. -- code_word_started is triggered in the first clk cycle of blk_strt and -- reset on the last clk cycle within the blk_end symbol period -- When code_word_started is asserted, the value on data_out -- is added to the pixel array in decoded_pgm on the last clk -- cycle within each symbol period. Note that for clks_per_sym = 1, -- data_out is sampled every clock cycle within code word block -- IF blk_strt = '1' THEN IF code_word_started = FALSE THEN code_word_started := TRUE; ci := 0; END IF; END IF; IF (code_word_started = TRUE) THEN -- -- only add value on data_out to pixel array within record -- when ci refers to the last clk cycle within a symbol period -- IF (ci MOD clks_per_sym = clks_per_sym_minus_1) THEN decoded_pgm.pixel(dpi) := std_logic_vector_to_natural(data_out); dpi := dpi + 1; ci := 0; -- reset clk index otherwise it might get too big in long simulations -- -- de-assert code_word_started when blk_end is asserted -- IF blk_end = '1' THEN code_word_started := FALSE; END IF; ELSE ci := ci + 1; END IF; END IF; -- -- once simulation_finished is asserted by the sim process, -- write the contents of decoded_pgm to a file referred to by dfile -- IF simulation_finished = TRUE THEN -- -- add header information to decoded_pgm record -- decoded_pgm.magic_number := received_pgm.magic_number; decoded_pgm.width := received_pgm.width; decoded_pgm.height := num_code_words; decoded_pgm.max_val := received_pgm.max_val; wrote_decoded_pgm_file := write_pgm_file(dfile, decoded_pgm); END IF; END IF; END PROCESS; -- data_collection -- -- Instantiate the decoder core -- decoder1 : rsd PORT MAP (data_in=>data_in, sync=>sync, reset=>reset, clk=>clk, ce=>ce, sr=>sr, data_out=>data_out, blk_strt=>blk_strt, blk_end=>blk_end, err_found=>err_found, err_cnt=>err_cnt, fail=>fail, ready=>ready); END example; CONFIGURATION tb_rs_decoder_config OF tb_rs_decoder IS FOR example FOR decoder1 : rsd USE ENTITY work.rs_decoder(behavioral) GENERIC MAP (gen_start => gen_start, k => k, n => n, polynomial => polynomial, symbol_width => symbol_width, c_has_ce => c_has_ce, c_has_erase => c_has_erase, c_has_sr => c_has_sr, sync_mode => sync_mode, clks_per_sym => clks_per_sym); END FOR; -- decoder1 -- -- If you want to instantiate the structural model: -- 1. Ensure the structural entity is called "rsd" and architecture is "structure" -- 2. Compile it into work before this file is compiled. -- 3. Comment out the above FOR decoder1 configuration block and -- 4. uncomment the following two lines to instantiate the structural model -- FOR decoder1 : rsd USE ENTITY work.rsd(structure); -- END FOR; -- 5. Expect much longer simulation times ! -- END FOR; -- example END tb_rs_decoder_config;