---------------------------------------------------------------------------------- -- Module Name: tb_top -- Project Name: DigMic_prj -- Description: -- Сквозной тест: модель микрофона -> Top -> UART -> разбор кадра в тестбенче. -- Проверяются формат кадра, CRC, счётчик и коэффициент передачи тракта. -- -- Три режима дают точно вычислимый ответ: -- постоянная '1' и '0' на выходе микрофона -- плюс и минус полная шкала, -- ровно FIR_DC_CODE (сумма коэффициентов на усиление CIC); -- чередование 1010 -- ровно ноль: частота PDM/2 попадает в нуль АЧХ CIC. -- Четвёртый режим -- синус, у него проверяется размах. -- -- Тракт настраивается около 10 мс (запаздывание ФНЧ, заполнение FIFO), -- поэтому в каждом режиме кадры читаются до первого сошедшегося, но не -- больше MAX_TRY. ---------------------------------------------------------------------------------- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use std.env.all; use work.fir_coefs_pkg.all; entity tb_top is generic ( MAX_TRY : integer := 12 -- сколько кадров ждать настройки тракта в режиме ); end entity tb_top; architecture sim of tb_top is constant CLK_T : time := 20 ns; constant BAUD : integer := 921600; constant BIT_T : time := 1 sec / BAUD; constant SPF : integer := 50; -- отсчётов в кадре constant SIN_AMP : real := 0.5; constant PAYLOAD_LEN : integer := 2 + 1 + SPF * 3; -- CNT + STATUS + DATA signal clk : std_logic := '0'; signal run : boolean := true; signal mic_clk : std_logic; signal mic_dat : std_logic; signal mic_sel : std_logic; signal uart_tx : std_logic; signal led : std_logic; signal mode : integer := 2; type byte_arr_t is array (natural range <>) of std_logic_vector(7 downto 0); function crc16_upd(crc : std_logic_vector(15 downto 0); d : std_logic_vector(7 downto 0)) return std_logic_vector is variable c : std_logic_vector(15 downto 0) := crc; variable fb : std_logic; begin for i in 7 downto 0 loop fb := c(15) xor d(i); c := c(14 downto 0) & '0'; if fb = '1' then c := c xor x"1021"; end if; end loop; return c; end function; procedure get_byte(signal rx : in std_logic; b : out std_logic_vector(7 downto 0)) is variable v : std_logic_vector(7 downto 0); begin wait until rx = '0'; -- фронт старт-бита wait for BIT_T / 2; assert rx = '0' report "start bit not confirmed" severity error; for i in 0 to 7 loop wait for BIT_T; v(i) := rx; end loop; wait for BIT_T; assert rx = '1' report "stop bit not at expected position" severity error; b := v; end procedure; function mode_name(m : integer) return string is begin case m is when 0 => return "+full scale"; when 1 => return "-full scale"; when 2 => return "zero"; when others => return "sine"; end case; end function; begin clk <= not clk after CLK_T / 2 when run else '0'; dut : entity work.Top(Behavioral) generic map( SYS_CLK_HZ => 50_000_000, BAUD_RATE => BAUD ) port map ( clk => clk, MIC_DAT => mic_dat, MIC_CLK => mic_clk, MIC_SEL => mic_sel, UART_TX => uart_tx, LED => led ); mic : entity work.pdm_mic_model(model) generic map( F_PDM => 3_125_000.0, SEL_HIGH => '0', AMP => SIN_AMP, FREQ => 1000.0 ) port map ( mic_clk => mic_clk, mode => mode, mic_dat => mic_dat ); check_proc : process variable b : std_logic_vector(7 downto 0); variable prev : std_logic_vector(7 downto 0); variable pl : byte_arr_t(0 to PAYLOAD_LEN - 1); variable crc : std_logic_vector(15 downto 0); variable crc_rx : std_logic_vector(15 downto 0); variable cnt_rx : integer; variable cnt_exp : integer := -1; variable s : integer; variable smin : integer; variable smax : integer; variable ok : boolean; variable want : integer; variable tol : integer; variable tries : integer; variable errors : integer := 0; begin wait for CLK_T; assert mic_sel = '0' report "MIC_SEL does not match the sampling phase" severity error; report "start: FIR_DC_CODE = " & integer'image(FIR_DC_CODE); for m in 0 to 3 loop mode <= m; case m is when 0 => want := FIR_DC_CODE; tol := 0; when 1 => want := - FIR_DC_CODE; tol := 0; when 2 => want := 0; tol := 0; when others => want := integer(SIN_AMP * real(FIR_DC_CODE)); tol := want / 20; -- 5 % end case; ok := false; tries := 0; while not ok and tries < MAX_TRY loop tries := tries + 1; -- Ищем SYNC A5 5A, как это будет делать приёмник на ПК prev := (others => '0'); loop get_byte(uart_tx, b); exit when prev = x"A5" and b = x"5A"; prev := b; end loop; for i in 0 to PAYLOAD_LEN - 1 loop get_byte(uart_tx, b); pl(i) := b; end loop; get_byte(uart_tx, b); crc_rx(15 downto 8) := b; get_byte(uart_tx, b); crc_rx(7 downto 0) := b; crc := x"FFFF"; for i in 0 to PAYLOAD_LEN - 1 loop crc := crc16_upd(crc, pl(i)); end loop; if crc /= crc_rx then report "CRC mismatch" severity error; errors := errors + 1; end if; cnt_rx := to_integer(unsigned(std_logic_vector'(pl(0) & pl(1)))); if cnt_exp >= 0 and cnt_rx /= cnt_exp then report "FRAME_CNT = " & integer'image(cnt_rx) & ", expected " & integer'image(cnt_exp) severity error; errors := errors + 1; end if; cnt_exp := (cnt_rx + 1) mod 65536; -- STATUS не проверяем отдельно: пока тракт не настроился, скачок на -- полную шкалу законно даёт выброс ФНЧ и взводит бит насыщения. -- Кадр с ненулевым STATUS просто не может стать сошедшимся. smin := integer'high; smax := integer'low; for i in 0 to SPF - 1 loop s := to_integer(signed(std_logic_vector'( pl(3 + i * 3) & pl(4 + i * 3) & pl(5 + i * 3)))); if s < smin then smin := s; end if; if s > smax then smax := s; end if; end loop; if m < 3 then ok := smin = want and smax = want; else ok := abs(smax - want) <= tol and abs(smin + want) <= tol; end if; ok := ok and pl(2) = x"00"; end loop; if ok then report mode_name(m) & ": OK, min " & integer'image(smin) & ", max " & integer'image(smax) & ", frames used " & integer'image(tries); else report mode_name(m) & ": FAILED, want " & integer'image(want) & " +-" & integer'image(tol) & ", got min " & integer'image(smin) & ", max " & integer'image(smax) & ", STATUS " & integer'image(to_integer(unsigned(pl(2)))) severity error; errors := errors + 1; end if; end loop; if errors = 0 then report "TEST PASSED"; else report "TEST FAILED: errors = " & integer'image(errors) severity error; end if; run <= false; stop; end process check_proc; end architecture sim;