-- $Source: /evtfs/home/tres/vhdl/ref_design/RCS/test_uart.vhd,v $ -- $Revision: 1.37 $ $Date: 2006/08/10 17:32:40 $ ------------------------------------------------------------------------------- -- Synchronous Procedural Coding Style for Simulation Example: test_uart -- Tue Feb 8 13:49:01 2005 Mike Treseler ------------------------------------------------------------------------------- -- This source file will be maintained for a while at -- http://home.comcast.net/~mike_treseler/test_uart.vhd -- This vhdl code example is a simulation testbench for the design entity: -- http://home.comcast.net/~mike_treseler/uart.vhd ------------------------------------------------------------------------------- -- This vhdl code example demonstrates a synchronous testbench coding -- style using small, well-named procedure declarations to support the -- main process shown as a comment below: ------------------------------------------------------------------------------- -- constant reps : natural := 16; -- begin -- process main -- init; -- for i in 1 to reps loop -- timed_cycle; -- end loop; -- for i in 1 to reps loop -- handshake_cycle; -- end loop; -- coda; -- end process main; ------------------------------------------------------------------------------- -- This testbench contains an instance process named DUT, a fixed -- stimulus process named TB_CLK and a MAIN process to provide -- stimulus and verification procedures. Start reading from the end of -- this file for a "top-down" view of the testbench. All functions and -- procedures are intended to be short enough that their purpose is -- obvious but long enough to be useful and nameable. Procedures -- access testbench object directly, without parameters in most cases. ------------------------------------------------------------------------------- -- Modelsim commands: -- run assertions only: vsim -c test_uart -do "run -all; exit" -- force an error 116: vsim -Gwire_g=random -c test_uart -do "run -all; exit" -- force an error 0: vsim -Gwire_g=stuck_hi -c test_uart -do "run -all; exit" -- change template: vsim -Gtemplate_g=s_rst -c test_uart -do "run -all; exit" -- slow baud: vsim -Gtb_tics_g=42 -c test_uart -do "run -all; exit" -- run with waves: vsim test_uart -do uart.do -- verify strobe calibration: vsim -Gtb_tics_g=42 test_uart -- then "do uart.do" from modelsim prompt ------------------------------------------------------------------------------- --# http://home.comcast.net/~mike_treseler/uart.do -- set this uart -- set mydesign $this -- set mytb test_$this -- echo Assuming [pwd] is the right directory. -- vdel -all ;# clean last compilation -- vlib work -- vmap work work -- vcom $mydesign.vhd $mytb.vhd -- restart -f ;# so I can use this same script to rerun -- radix hex; ;# Make bus values easy to read -- add wave * ;# Signals -- add wave /$mytb/main/* ;# Test variables -- add wave /$mytb/dut/main/* ;# UUT variables -- run -all; -- # Make wave window readable -- WaveRestoreCursors {{Cursor 1} {194 ns} 0} -- configure wave -namecolwidth 277 -- WaveRestoreZoom {178 ns} {281 ns} ------------------------------------------------------------------------------- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use work.uart_pkg.all; -- see top of uart.vhd: flag_t, t_rdy_c, r_rdy_c,r_err_c ------------------------------------------------------------------------------- entity test_uart is generic (tb_tics_g : natural := 3; tb_char_g : natural := 8; -- generic type verbose_g : boolean := true; template_g: template_t := v_rst; wire_g : wire_t := loopback); -- wire_t (loopback, stuck_hi, stuck_low, random); end entity test_uart; ------------------------------------------------------------------------------- architecture sim of test_uart is -- signals and other declarations visible to all processes go here: constant msb : natural := tb_char_g -1; subtype char_t is unsigned(msb downto 0); -- generic type, usually 8 bits subtype byte_t is unsigned(7 downto 0); -- static type, always 8 bits constant zero_c : char_t := (others => '0'); constant seed_c : char_t := "100001" + zero_c; -- works for any width constant exit_loop_c : natural := 1000; -- used on init and wait_for_rx constant clk_cy : time := 10 ns; -- used on tb_clk and timed_cycle ------------------------------------------------------------------------------- -- instance wires signal address_s : std_ulogic; -- [in] signal writeData_s : std_logic_vector(msb downto 0); -- [in] signal write_stb_s : std_ulogic; -- [in] signal readData_s : std_logic_vector(msb downto 0); -- [out] by uut signal read_stb_s : std_ulogic; -- [in] signal serialIn_s : std_ulogic; -- [in] by main signal serialOut_s : std_ulogic; -- [out] by uut ------------------------------------------------------------------------------- -- testbench wires: signal done_s : boolean; -- stops all processes signal clk_s : std_ulogic; -- sim clock signal rst_s : std_ulogic; -- sim reset signal random_byte_s : char_t := seed_c; -- sorta random byte every tic signal ref_count_s : natural:= 0; -- new count every tic ------------------------------------------------------------------------------- begin -- architecture sim dut : entity work.uart generic map (char_len_g => tb_char_g, -- for vsim command line overload tic_per_bit_g => tb_tics_g) port map ( clock => clk_s, -- [in] -- by tb_clk reset => rst_s, -- [in] -- by tb_clk address => address_s, -- [in] -- by main writeData => writeData_s, -- [in] -- by main write_stb => write_stb_s, -- [in] -- by main readData => readData_s, -- [out]-- by uut read_stb => read_stb_s, -- [in] -- by main serialIn => serialIn_s, -- [in] -- by main,loopback destination serialOut => serialOut_s -- [out]-- by uut, loopback source ); ------------------------------------------------------------------------------- tb_clk : process is -- clock, reset, and full clock rate procedures ------------------------------------------------------------------------------- constant rst_time : time := 10*clk_cy; constant mask_c : char_t := "111" + zero_c; variable serialIn_v : std_ulogic; -- loopback variable ------------------------------------------------------------------------------- function randomize (arg_byte : char_t) return char_t is variable result_v : char_t; begin result_v := arg_byte; -- here it is result_v := shift_left(result_v, 1); -- shift it if (result_v(result_v'left)) = '1' then -- maybe invert mask bits result_v := result_v xor mask_c; end if; return result_v; end function randomize; ------------------------------------------------------------------------------- impure function loopback_mux (wire_arg : wire_t) return std_ulogic is variable ret_v : std_ulogic; -- Purpose: Select serialIn from the serial output (loopback) or -- use error values to verify that this testbench handles errors -- cleanly. The loopback mux was a separate process before rev -- 1.28, but is now combined with the tb_clk fixed_stim procedure. begin case wire_arg is -- First Reading when loopback => ret_v := serialOut_s; -- 36 when stuck_low => ret_v := '0'; -- 0 when stuck_hi => ret_v := '1'; -- 0 when random => ret_v := random_byte_s(0); -- 116 when others => ret_v := serialOut_s; -- none -- others won't elaborate, out of wire_t range 0 to 3. end case; return ret_v; end function loopback_mux; ------------------------------------------------------------------------------- procedure fixed_stim is begin random_byte_s <= randomize(random_byte_s); ref_count_s <= ref_count_s+1; serialIn_s <= loopback_mux(wire_g); end procedure fixed_stim; ------------------------------------------------------------------------------- begin -- process tb_clk clk_s <= '0'; if now < rst_time then rst_s <= '1'; -- rst once with clk running else rst_s <= '0'; -- then low forever end if; if done_s then wait; end if; -- Stop clock wait for clk_cy/2; -- clock low clk_s <= '1'; fixed_stim; -- auxiliary output from tb_clk process wait for clk_cy/2; -- clock high end process tb_clk; ------------------------------------------------------------------------------- main : process is -- synchronized using procedure "tic" below ------------------------------------------------------------------------------- variable step_v : natural; -- verification step variable expect_v : char_t; -- expected value variable match_v : boolean; -- this match variable pass_v : boolean; -- all match so far ------------------------------------------------------------------------------- procedure tic is begin wait until rising_edge(clk_s); end procedure tic; ------------------------------------------------------------------------------- procedure set_bit (signal arg_s : inout std_ulogic) is begin-- skip tic if already set if arg_s /= '1' then arg_s <= '1'; tic; end if; end procedure set_bit; ------------------------------------------------------------------------------- procedure clr_bit (signal arg_s : inout std_ulogic) is begin -- skip tic if already clear if arg_s /= '0' then arg_s <= '0'; tic; end if; end procedure clr_bit; ------------------------------------------------------------------------------- procedure toggle (signal arg_s : inout std_ulogic) is begin clr_bit(arg_s); -- costs no time if already low set_bit(arg_s); clr_bit(arg_s); end procedure toggle; ------------------------------------------------------------------------------- procedure results is begin if pass_v then report "___ALL PASS___"; else report "____FAILED_____"; end if; end procedure results; ------------------------------------------------------------------------------- procedure coda is begin results; done_s <= true; tic; wait; end procedure coda; ------------------------------------------------------------------------------- procedure die is begin pass_v := false; coda; end procedure die; ------------------------------------------------------------------------------- procedure nop is begin address_s <= '0'; read_stb_s <= '0'; writedata_s <= (others => '0'); write_stb_s <= '0'; tic; end procedure nop; ------------------------------------------------------------------------------- constant fixed_tics_c : natural := 3 * (tb_char_g + 4) * tb_tics_g; -- fixed delay adjusted for baud rate constant fixed_delay_c : time := fixed_tics_c * clk_cy; procedure init is variable width_v : natural; begin width_v := 0; step_v := 0; pass_v := true; expect_v := (others => '0'); nop; ck_rst : for i in 1 to exit_loop_c loop if i = exit_loop_c then report "Gave up waiting for reset" severity error; die; end if; if rst_s = '1' then -- wait for end of reset tic; width_v := width_v + 1; elsif width_v > 0 then -- reset done report "Saw reset rise and fall OK"; report "Using fixed_delay_c = " & time'image(fixed_delay_c) & " That's " & natural'image(fixed_tics_c) & " ticks."; exit ck_rst; end if; end loop ck_rst; end procedure init; ------------------------------------------------------------------------------- procedure verify is type string_p is access string; variable string_v : string_p; begin string_v := new string'(integer'image(to_integer(expect_v))); report "___Step " & integer'image(step_v); match_v := expect_v = unsigned(readData_s); -- expected value? pass_v := pass_v and match_v; -- all ok so far? step_v := step_v + 1; ck : if match_v then boring : if verbose_g then report "____________ saw " & string_v.all & " as expected"; end if boring; else report "_____________Expected byte is " & string_v.all; report "_____________Actual bus data is " & integer'image(to_integer(unsigned(readData_s))) severity error; report "________________________________________WIRE STATE IS " & wire_t'image(wire_g); die; end if ck; end procedure verify; ------------------------------------------------------------------------------- procedure read_data is begin clr_bit(address_s); toggle(read_stb_s); verify; -- data is valid now for one clock period end procedure read_data; ------------------------------------------------------------------------------- subtype flag_t is natural range char_t'range; -- +----------------------------------------+ -- | Table 1 UART Memory Map | -- +------+-----+-----+----------+----------+ -- |Name |Add |R/W |Data |Comment | -- +------+-----+-----+----------+----------+ -- |TXQ |0 |W |7_downto_0| Transmit | -- | | | | | Data | -- +------+-----+-----+----------+----------+ -- |RXQ |0 |R |7_downto 0| Receive | -- | | | | | Data | -- +------+-----+-----+----------+----------+ -- |StatQ |1 |R |2->TxReady| Status | -- | | | |1->RxError| Data | -- | | | |0->RxReady| | -- +------+-----+-----+----------+----------+ -- note: valid_handshake_c has been replaced with named readData_s -- bits in rev 1.36 ------------------------------------------------------------------------------- procedure read_status is -- see package uart_pkg top of uart.vhd begin set_bit(address_s); toggle(read_stb_s); good_stop_bit:assert readData_s(r_err_c) = '0' report "_____________Stop Bit Failure" severity error; -- don't die until next verify end procedure read_status; ------------------------------------------------------------------------------- procedure wait_for_rx is begin -- example of handshake delay; handshake : for i in 1 to exit_loop_c loop read_status; if readdata_s(r_rdy_c) = '1' and readdata_s(r_err_c) = '0' then -- report "rx arbitration tics = "&integer'image(i); -- 16 exit handshake; elsif i = exit_loop_c then report "wait_for_rx: Tired of waiting. Handshake error." severity error; die; end if; end loop handshake; end procedure wait_for_rx; ------------------------------------------------------------------------------- procedure wait_for_tx is begin -- example of handshake delay; handshake : for i in 1 to exit_loop_c loop read_status; if readdata_s(t_rdy_c) = '1' then -- report "tx arbitration tics = "&integer'image(i); -- 1 exit handshake; elsif i = exit_loop_c then report "wait_for_tx: Tired of waiting. Handshake error." severity error; die; end if; end loop handshake; end procedure wait_for_tx; ------------------------------------------------------------------------------- procedure write_data is begin expect_v := random_byte_s; address_s <= '0'; writedata_s <= std_logic_vector(expect_v); tic; toggle(write_stb_s); end procedure write_data; ------------------------------------------------------------------------------- procedure timed_cycle is begin -- example of waiting instead of handshaking write_data; wait for fixed_delay_c; -- example of wasting time between cycles ---------wait for 10*clk_cy; -- example of reading too soon, reads 4 for 36 tic; -- synchronize delay read_status; -- check stop bit read_data; end procedure timed_cycle; ------------------------------------------------------------------------------- procedure handshake_cycle is begin -- example of handshaking loopback data wait_for_tx; write_data; wait_for_rx; read_data; end procedure handshake_cycle; ------------------------------------------------------------------------------- constant reps : natural := 8; begin -- process main: Top level loop invokes top procedures. init; for i in 1 to reps loop timed_cycle; end loop; for i in 1 to reps loop handshake_cycle; end loop; coda; end process main; -- that's it end architecture sim;