-------------------------------------------------------------------------------- -- Copyright 2000 Xilinx, Inc. All rights reserved. -------------------------------------------------------------------------------- -- -- Description: Testbench for Xilinx Reed-Solomon Tutorial 1. -- -- If you intend to use this file to simulate a parameter combination -- which is different to the combination in tutorial1, 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; -- utility functions package USE work.pgm_pkg.ALL; -- read and write pgm format files package USE work.RNG.ALL; -- random number generator package ENTITY tb_rs_encoder IS GENERIC ( ifile : STRING := "input.pgm"; efile : STRING := "encoded.pgm"; nfile : STRING := "noise.pgm"; rfile : STRING := "received.pgm"; num_errors : NATURAL := 0; use_behavioral_model : BOOLEAN := FALSE ); END tb_rs_encoder; ARCHITECTURE example OF tb_rs_encoder IS ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- -- CHECKPOINT 1 -- -- The component declared below must match the component declaration -- given in rsenc_wrap.vhd i.e. -- 1. The component name must be "rse" -- 2. There must be the same number of ports -- 3. The port names must match -- 4. The data_in and data_out bus widths must match -- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- COMPONENT rse PORT ( bypass : IN STD_LOGIC; clk : IN STD_LOGIC; data_in : IN STD_LOGIC_VECTOR(7 DOWNTO 0); data_out : OUT STD_LOGIC_VECTOR(7 DOWNTO 0); enable : IN STD_LOGIC; info : OUT STD_LOGIC; reset : IN STD_LOGIC; start : IN STD_LOGIC); END COMPONENT; ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- -- END OF CHECKPOINT 1 -- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- -- CONSTANT DECLARATIONS -- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- -- CHECKPOINT 2 -- -- The values for gen_start, h, k, n, optimization, polynomial & symbol_width -- must match the values given in the configuration statement in rsenc_wrap.vhd -- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- CONSTANT gen_start : NATURAL := 1; CONSTANT h : NATURAL := 1; CONSTANT k : NATURAL := 187; CONSTANT n : NATURAL := 207; --CONSTANT optimization : NATURAL := 1; -- area CONSTANT optimization : NATURAL := 2; -- speed CONSTANT polynomial : NATURAL := 391; CONSTANT symbol_width : NATURAL := 8; ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- -- END OF CHECKPOINT 2 -- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- read in input pgm image file CONSTANT input_pgm : pgm_record_type := read_pgm_file(ifile); -- calculate how many code words are required to encode input image CONSTANT num_code_words : NATURAL := input_pgm.width*input_pgm.height/k; CONSTANT no_debug : BOOLEAN := FALSE; -- to print simulation stage reports CONSTANT clk_period : TIME := 20 NS; -- 50 MHz clock CONSTANT t_hold : TIME := 1 NS; ------------------------------------------------------------------------------- -- -- SIGNAL DECLARATIONS -- ------------------------------------------------------------------------------- SIGNAL reset : STD_LOGIC := '0'; SIGNAL clk : STD_LOGIC := '1'; SIGNAL enable : STD_LOGIC; SIGNAL bypass : STD_LOGIC; SIGNAL start : STD_LOGIC; SIGNAL data_in : STD_LOGIC_VECTOR(symbol_width - 1 DOWNTO 0); SIGNAL data_out : STD_LOGIC_VECTOR(symbol_width - 1 DOWNTO 0); SIGNAL info : 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 enable_tmp : STD_LOGIC := '1'; SIGNAL bypass_tmp : STD_LOGIC := '0'; SIGNAL start_tmp : STD_LOGIC := '0'; SIGNAL data_in_tmp : STD_LOGIC_VECTOR(symbol_width - 1 DOWNTO 0) := (OTHERS => '0'); -- -- chug is a signal to flag when encoder is outputting -- information and check symbols. It should go low when -- 1. encoder has finished outputting information and check symbols, -- 2. enable is de-asserted, -- 3. bypass is asserted. -- -- A pipeline for chug is created to match the latency of the encoder core. -- SIGNAL chug_pipe_in : STD_LOGIC; SIGNAL chug_pipe : STD_LOGIC_VECTOR(optimization DOWNTO 0); SIGNAL chug : STD_LOGIC; -- -- 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; ------------------------------------------------------------------------------- -- -- Generate a random STD_LOGIC_VECTOR -- ------------------------------------------------------------------------------- PROCEDURE get_rand_num(rand_num:INOUT uniform; rand_val:OUT STD_LOGIC_VECTOR) IS VARIABLE rand_real : REAL; BEGIN -- Generate random number genrnd(rand_num); rand_real := rand_num.rnd; rand_val := natural_to_std_logic_vector(NATURAL(rand_real), symbol_width); END get_rand_num; ---------------------------------------------------------------------------- -- -- Generate a random INTEGER -- ---------------------------------------------------------------------------- PROCEDURE get_rand_num(rand_num:INOUT uniform; rand_val:OUT NATURAL) IS BEGIN -- Generate random number genrnd(rand_num); rand_val := NATURAL(rand_num.rnd); END get_rand_num; ---------------------------------------------------------------------------- -- -- Add noise to pgm record pixel data -- ---------------------------------------------------------------------------- PROCEDURE add_noise_to_pgm(pgm, noise_pgm: INOUT pgm_record_type) IS VARIABLE rand_po : uniform := InitUniform(7, 0.0, REAL(pgm.width - 1)); VARIABLE rand_val : uniform := InitUniform(13, 1.0, REAL(pgm.max_val)); VARIABLE po : NATURAL; -- pixel offset within code word VARIABLE pi : NATURAL; -- pixel index VARIABLE pix_val : STD_LOGIC_VECTOR(symbol_width - 1 DOWNTO 0); VARIABLE err_val : STD_LOGIC_VECTOR(symbol_width - 1 DOWNTO 0); VARIABLE po_already_used : STD_LOGIC_VECTOR(0 TO pgm.width - 1) := (OTHERS => '0'); VARIABLE found_unused_po : BOOLEAN := FALSE; BEGIN FOR bi IN 0 TO (num_code_words - 1) LOOP po_already_used := (OTHERS => '0'); IF num_errors > 0 THEN FOR ni IN 1 TO num_errors LOOP -- go round the following loop until suitable po is found -- this ensures that the same po isn't used twice in the same code word found_unused_po := FALSE; WHILE (found_unused_po = FALSE) LOOP get_rand_num(rand_po, po); -- random pixel offset within code word IF (po_already_used(po) = '0') THEN po_already_used(po) := '1'; -- so that it can't be used again found_unused_po := TRUE; -- get out of loop END IF; END LOOP; pi := bi*n + po; -- calculate true pixel index in 1-D array pix_val := natural_to_std_logic_vector(pgm.pixel(pi), symbol_width); get_rand_num(rand_val, err_val); noise_pgm.pixel(pi) := std_logic_vector_to_natural(err_val); -- XOR random noise with pixel pix_val := pix_val XOR err_val; pgm.pixel(pi) := std_logic_vector_to_natural(pix_val); END LOOP; -- ni END IF; -- num_errors END LOOP; -- bi END add_noise_to_pgm; ------------------------------------------------------------------------------- -- -- Beginning of architecture block -- ------------------------------------------------------------------------------- BEGIN -- -- Define clk -- PROCESS BEGIN clk <= '1'; WAIT FOR clk_period/2; clk <= '0'; WAIT FOR clk_period/2; END PROCESS; -- -- All synchronous inputs are updated t_hold after a rising clock -- edge to control the set up time. -- PROCESS BEGIN IF (clk'EVENT AND clk = '1') THEN WAIT FOR t_hold; enable <= enable_tmp; bypass <= bypass_tmp; start <= start_tmp; data_in <= data_in_tmp; END IF; WAIT ON clk; END PROCESS; -- -- Create chug_pipe pipeline -- PROCESS (clk) BEGIN IF reset = '1' THEN -- reset pipeline registers chug <= '0'; FOR pi IN optimization DOWNTO 1 LOOP chug_pipe(pi) <= '0'; END LOOP; -- pi chug_pipe(0) <= '0'; ELSIF (clk'EVENT AND clk = '1') THEN chug <= chug_pipe(optimization); FOR pi IN optimization DOWNTO 1 LOOP chug_pipe(pi) <= chug_pipe(pi - 1); END LOOP; -- pi chug_pipe(0) <= chug_pipe_in; END IF; END PROCESS; -- -- Start simulation -- PROCESS CONSTANT lf : CHARACTER := lf; BEGIN simulation_finished <= FALSE; reset <= '1'; enable_tmp <= '1'; bypass_tmp <= '0'; start_tmp <= '0'; chug_pipe_in <= '0'; cycle(5); reset <= '0'; cycle(1); ASSERT no_debug REPORT "Encoding "& 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 start_tmp -- FOR cbi IN 0 TO (num_code_words - 1) LOOP -- assert start_tmp for first symbol of information block chug_pipe_in <= '1'; start_tmp <= '1'; data_in_tmp <= natural_to_std_logic_vector(input_pgm.pixel(cbi*k), symbol_width); cycle(1); start_tmp <= '0'; FOR si IN 1 TO (k - 1) LOOP data_in_tmp <= natural_to_std_logic_vector(input_pgm.pixel(cbi*k + si), symbol_width); cycle(1); END LOOP; -- si FOR si IN k TO (n - 1) LOOP data_in_tmp <= natural_to_std_logic_vector(0, symbol_width); cycle(1); END LOOP; -- si -- make sure chug_pipe_in is set to '0' if any extra clock cycles are -- added for assertions of bypass, or deassertions of enable. -- Otherwise data will continue to be added to encoded_pgm record -- For example, -- enable_tmp <= '0'; -- chug_pipe_in <= '0'; -- cycle(5); -- enable_tmp <= '1'; -- chug_pipe_in <= '1'; -- -- Structural 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 encoded." SEVERITY NOTE; END IF; END LOOP; -- bi -- End simulation cycle(5); -- just a few more cycles to ensure everything's finished ASSERT no_debug REPORT "100% of code words encoded." SEVERITY NOTE; simulation_finished <= TRUE; cycle(1); -- make sure pgm files are written ASSERT no_debug REPORT lf & "Added " & int_2_string(num_errors) & " errors to each code word. " & lf & lf & "Input file: " & ifile & lf & "Encoded file: " & efile & lf & "Noise file: " & nfile & lf & "Received file: " & rfile & lf SEVERITY NOTE; ASSERT FALSE REPORT "Simulation is complete (this isn't a real failure)." SEVERITY FAILURE; WAIT; END PROCESS; -- -- collect data_out into encoded record, write record to file, -- add errors, and write both noise and received records to files. -- PROCESS (clk) VARIABLE epi : NATURAL := 0; VARIABLE encoded_pgm : pgm_record_type; VARIABLE noise_pgm : pgm_record_type; VARIABLE wrote_encoded_pgm_file : BOOLEAN := FALSE; VARIABLE wrote_noise_pgm_file : BOOLEAN := FALSE; BEGIN IF (clk'EVENT AND clk = '1') THEN -- -- only add useful information and check symbols to record -- IF chug = '1' THEN encoded_pgm.pixel(epi) := std_logic_vector_to_natural(data_out); epi := epi + 1; END IF; IF simulation_finished = TRUE THEN -- add header information to encoded_pgm record encoded_pgm.magic_number := input_pgm.magic_number; encoded_pgm.width := n; encoded_pgm.height := num_code_words; encoded_pgm.max_val := input_pgm.max_val; wrote_encoded_pgm_file := write_pgm_file(efile, encoded_pgm); add_noise_to_pgm(encoded_pgm, noise_pgm); wrote_encoded_pgm_file := write_pgm_file(rfile, encoded_pgm); -- add header information to noise_pgm record noise_pgm.magic_number := encoded_pgm.magic_number; noise_pgm.width := encoded_pgm.width; noise_pgm.height := encoded_pgm.height; noise_pgm.max_val := encoded_pgm.max_val; wrote_noise_pgm_file := write_pgm_file(nfile, noise_pgm); END IF; END IF; END PROCESS; -- -- Instantiate either the behavioral or structural model of the encoder core -- behavioral_gen : IF use_behavioral_model GENERATE encoder1 : rse PORT MAP (bypass=>bypass, clk=>clk, data_in=>data_in, data_out=>data_out, enable=>enable, info=>info, reset=>reset, start=>start); END GENERATE; structural_gen : IF (use_behavioral_model = FALSE) GENERATE encoder1 : rse PORT MAP (bypass=>bypass, clk=>clk, data_in=>data_in, data_out=>data_out, enable=>enable, info=>info, reset=>reset, start=>start); END GENERATE; END example; ------------------------------------------------------------------------------- -- -- this configuration statement is a little more complex than the -- one given in rsenc_wrap.vhd. It supports the testbench input generic -- "use_behavioral_model", to permit the user to select whether the -- behavioral or structural model for the core is used in the simulation. -- Note that the generics are abstracted to use the values defined -- in CHECKPOINT 2 -- ------------------------------------------------------------------------------- CONFIGURATION tb_rs_encoder_config OF tb_rs_encoder IS FOR example FOR behavioral_gen FOR encoder1 : rse USE ENTITY work.rs_encoder(behavioral) GENERIC MAP (gen_start => gen_start, h => h, k => k, n => n, optimization => optimization, polynomial => polynomial, symbol_width => symbol_width); END FOR; -- encoder1 END FOR; -- example FOR structural_gen FOR encoder1 : rse USE ENTITY work.rse(structure); END FOR; END FOR; END FOR; -- example END tb_rs_encoder_config;