Тестбенч: модель микрофона и сквозная проверка тракта
Модель -- сигма-дельта первого порядка с высокоимпедансным состоянием вне рабочей фазы, так что выборка не в той фазе видна сразу. Три режима дают точно вычислимый ответ: постоянные 1 и 0 -- плюс/минус полная шкала, а чередование 1010 -- ровно ноль, эта частота попадает в нуль АЧХ CIC. Четвёртый режим синусный, проверяется размах. Буфер ФНЧ получил начальное значение: иначе первые свёртки тянули неопределённость через весь тракт.
This commit is contained in:
+3
-1
@@ -47,7 +47,9 @@ end entity fir_decim;
|
|||||||
architecture rtl of fir_decim is
|
architecture rtl of fir_decim is
|
||||||
|
|
||||||
type buf_t is array (0 to 2 ** BUF_AW - 1) of signed(DIN_W - 1 downto 0);
|
type buf_t is array (0 to 2 ** BUF_AW - 1) of signed(DIN_W - 1 downto 0);
|
||||||
signal buf : buf_t;
|
-- Начальное значение, а не сброс: блочная память его поддерживает, и первые
|
||||||
|
-- свёртки не тянут неопределённость через весь тракт
|
||||||
|
signal buf : buf_t := (others => (others => '0'));
|
||||||
|
|
||||||
signal wr_ptr : unsigned(BUF_AW - 1 downto 0) := (others => '0');
|
signal wr_ptr : unsigned(BUF_AW - 1 downto 0) := (others => '0');
|
||||||
signal base : unsigned(BUF_AW - 1 downto 0) := (others => '0');
|
signal base : unsigned(BUF_AW - 1 downto 0) := (others => '0');
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
----------------------------------------------------------------------------------
|
||||||
|
-- Module Name: pdm_mic_model
|
||||||
|
-- Project Name: DigMic_prj
|
||||||
|
-- Description:
|
||||||
|
-- Поведенческая модель PDM-микрофона: сигма-дельта модулятор первого порядка
|
||||||
|
-- с передачей сигнала 1 и шумом (1 - z^-1). Что подаётся на модулятор,
|
||||||
|
-- выбирает вход mode.
|
||||||
|
--
|
||||||
|
-- Вывод данных как в даташите: бит выставляется через TDD после рабочего
|
||||||
|
-- фронта (при SELECT = '0' -- по спаду) и через TDZ после противоположного
|
||||||
|
-- уходит в высокоимпедансное. Так тестбенч ловит выборку не в той фазе.
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
library ieee;
|
||||||
|
use ieee.std_logic_1164.all;
|
||||||
|
use ieee.math_real.all;
|
||||||
|
|
||||||
|
entity pdm_mic_model is
|
||||||
|
generic (
|
||||||
|
F_PDM : real := 3_125_000.0;
|
||||||
|
SEL_HIGH : std_logic := '0';
|
||||||
|
AMP : real := 0.5; -- амплитуда синуса в долях шкалы
|
||||||
|
FREQ : real := 1000.0; -- частота синуса, Гц
|
||||||
|
TDD : time := 18 ns; -- задержка выдачи после рабочего фронта
|
||||||
|
TDZ : time := 15 ns -- задержка ухода в Z
|
||||||
|
);
|
||||||
|
port (
|
||||||
|
mic_clk : in std_logic;
|
||||||
|
mode : in integer; -- 0 +шкала, 1 -шкала, 2 ноль, 3 синус
|
||||||
|
mic_dat : out std_logic
|
||||||
|
);
|
||||||
|
end entity pdm_mic_model;
|
||||||
|
|
||||||
|
architecture model of pdm_mic_model is
|
||||||
|
constant T_HALF : time := (1 sec / integer(F_PDM)) / 2;
|
||||||
|
begin
|
||||||
|
|
||||||
|
mod_proc : process
|
||||||
|
variable e : real := 0.0; -- накопитель ошибки
|
||||||
|
variable y : real := 0.0; -- предыдущий выход
|
||||||
|
variable x : real;
|
||||||
|
variable n : integer := 0;
|
||||||
|
variable b : std_logic;
|
||||||
|
begin
|
||||||
|
if SEL_HIGH = '1' then
|
||||||
|
wait until rising_edge(mic_clk);
|
||||||
|
else
|
||||||
|
wait until falling_edge(mic_clk);
|
||||||
|
end if;
|
||||||
|
|
||||||
|
case mode is
|
||||||
|
when 0 => x := 1.0;
|
||||||
|
when 1 => x := - 1.0;
|
||||||
|
when 2 => x := 0.0;
|
||||||
|
when others => x := AMP * sin(2.0 * MATH_PI * FREQ * real(n) / F_PDM);
|
||||||
|
end case;
|
||||||
|
|
||||||
|
e := e + x - y;
|
||||||
|
if e >= 0.0 then
|
||||||
|
y := 1.0;
|
||||||
|
b := '1';
|
||||||
|
else
|
||||||
|
y := - 1.0;
|
||||||
|
b := '0';
|
||||||
|
end if;
|
||||||
|
n := n + 1;
|
||||||
|
|
||||||
|
-- Обе точки одной формой сигнала: раздельными присваиваниями инерционная
|
||||||
|
-- задержка второго вычистила бы транзакцию первого
|
||||||
|
mic_dat <= b after TDD, 'Z' after T_HALF + TDZ;
|
||||||
|
end process mod_proc;
|
||||||
|
|
||||||
|
end architecture model;
|
||||||
+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