Тестбенч: модель микрофона и сквозная проверка тракта
Модель -- сигма-дельта первого порядка с высокоимпедансным состоянием вне рабочей фазы, так что выборка не в той фазе видна сразу. Три режима дают точно вычислимый ответ: постоянные 1 и 0 -- плюс/минус полная шкала, а чередование 1010 -- ровно ноль, эта частота попадает в нуль АЧХ CIC. Четвёртый режим синусный, проверяется размах. Буфер ФНЧ получил начальное значение: иначе первые свёртки тянули неопределённость через весь тракт.
This commit is contained in:
+249
@@ -0,0 +1,249 @@
|
||||
----------------------------------------------------------------------------------
|
||||
-- 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;
|
||||
Reference in New Issue
Block a user