-------------------------------------- -- -- SAR_ADC_model_tb.vhdl -- -- created 10/6/26 -- tj -- -- rev 0 ---------------------------------------- -- -- Testbench for SAR_ADC_model -- -- Test 1: ramp vin from below 0 V to above VREF, check each -- code against the ideal floor(vin / LSB), clamped -- Test 2: convert a sine wave (view vin and data_out as -- analog waveforms in ModelSim) -- ---------------------------------------- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use ieee.math_real.all; entity SAR_ADC_model_tb is generic( DAC_GAIN_ERR_tb: real := 0.001 ); end entity; architecture testbench of SAR_ADC_model_tb is constant N_BITS: positive := 8; constant VREF: real := 3.3; constant LSB: real := VREF / real(2**N_BITS); signal CLK: std_logic; signal RSTB: std_logic; signal START: std_logic; signal VIN: real; signal DATA_OUT: std_logic_vector((N_BITS - 1) downto 0); signal BUSY: std_logic; signal DONE: std_logic; constant PER: time := 20 ns; -- ideal integer code for a given input function ideal_code(v: real) return integer is variable c: integer; begin if(v <= 0.0) then return 0; end if; c := integer(floor(v / LSB)); if(c > (2**N_BITS - 1)) then c := 2**N_BITS - 1; end if; return c; end function; component SAR_ADC_model is generic( N_BITS: positive := 8; SAMPLE_CLKS: positive := 4; SETTLE_CLKS: positive := 1; VREF: real := 3.3; DAC_OFFSET: real := 0.0; DAC_GAIN_ERR: real := 0.0; DAC_T_SETTLE: time := 5 ns; COMP_OFFSET: real := 0.0; COMP_T_PD: time := 3 ns ); port( i_clk: in std_logic; i_rstb: in std_logic; i_start: in std_logic; i_vin: in real; o_data_out: out std_logic_vector((N_BITS - 1) downto 0); o_busy: out std_logic; o_done: out std_logic ); end component; begin dut: SAR_ADC_model generic map( N_BITS => N_BITS, VREF => VREF, DAC_GAIN_ERR => DAC_GAIN_ERR_tb ) port map( i_clk => CLK, i_rstb => RSTB, i_start => START, i_vin => VIN, o_data_out => DATA_OUT, o_busy => BUSY, o_done => DONE ); -- Clock process clock: process -- note - no sensitivity list allowed begin CLK <= '0'; wait for PER/2; infinite: loop CLK <= not CLK; wait for PER/2; end loop; end process clock; -- RSTB process reset: process -- note - no sensitivity list allowed begin RSTB <= '0'; wait for 2*PER; RSTB <= '1'; wait; end process reset; -- stimulus run: process variable v: real; variable errors: natural := 0; -- just to simplify the code later procedure convert is begin wait until rising_edge(CLK); START <= '1'; wait until rising_edge(CLK); START <= '0'; wait until DONE = '1'; end procedure; begin start <= '0'; -- wait for reset wait for PER * 4; -- test 1 - ramp, step is not a multiple of LSB to avoid exact thresholds v := -0.1; while(v < (VREF + 0.1)) loop VIN <= v; convert; -- using if instead of assert to allow error to be incremented if(to_integer(unsigned(DATA_OUT)) /= ideal_code(v)) then report "Test 1 FAIL: " & LF & "vin = " & real'image(v) & " expected = " & integer'image(ideal_code(v)) & " data_out = " & integer'image(to_integer(unsigned(DATA_OUT))) severity error; errors := errors + 1; end if; v := v + (LSB * 0.37); end loop; report LF & "Ramp test complete - " & integer'image(errors) & " errors" & LF severity note; errors := 0; -- test 2 - sine wave, 1.5 cycles, about 40 samples per cycle for i in 0 to 59 loop VIN <= (VREF / 2.0) + (VREF / 2.2) * sin(MATH_2_PI * real(i) / 40.0); convert; -- using if instead of assert to allow error to be incremented if(to_integer(unsigned(DATA_OUT)) /= ideal_code(VIN)) then report "Test 2 FAIL: " & LF & "vin = " & real'image(VIN) & " expected = " & integer'image(ideal_code(VIN)) & " data_out = " & integer'image(to_integer(unsigned(DATA_OUT))) severity error; errors := errors + 1; end if; end loop; report LF & "Sin test complete - " & integer'image(errors) & " errors" & LF severity note; wait for PER * 4; report "Simulation complete" severity failure; end process; end architecture;