Тракт молча отдавал нули: CIC с 26-битными аккумуляторами в железе не считает, хотя синтез, трассировка, тайминг и симуляция чистые. Порог найден перебором на плате с приёмом по UART: 26, 24 и 20 бит мертвы, 18, 16 и 10 работают. Порядок 3 требует ровно 18 бит вместо 24 и попадает в рабочую область. Характеристики от этого стали лучше: подавление в задерживании 76 дБ вместо 72, сворачиваемых полос 92 дБ вместо 77, полоса та же 0.032 дБ. Коэффициенты ФНЧ пересчитаны под sinc^3, сдвиг выхода уменьшен до 13 -- усиление CIC теперь 50^3, и коэффициенты по-прежнему укладываются в 18 бит. Сброс по включению убран: он тоже убивает тракт, проверено прошивкой в обе стороны. Маяк светодиода вынесен в условную генерацию, чтобы его широкий счётчик не собирался в рабочем режиме. Проверено на плате: 1481 кадр за 6 с, 74050 отсчётов, CRC без ошибок, STATUS чистый, на входе живой звук.
180 lines
11 KiB
Markdown
180 lines
11 KiB
Markdown
# Цифровой микрофон SPH9855 на Tang Primer 25K
|
||
|
||
Прошивка принимает поток PDM с микрофона SPH9855LM4H-C, приводит его к
|
||
**24 бит / 12 500 Гц** и отдаёт отсчёты кадрами по UART.
|
||
|
||
MIC_CLK 3.125 МГц -> CIC sinc^3 /50 -> 62.5 кГц -> ФНЧ /5 -> **12 500 Гц** ->
|
||
кадры по 50 отсчётов -> UART 921600.
|
||
|
||
## Тактирование и частоты
|
||
|
||
| Параметр | Значение | Обоснование |
|
||
|:--|:--|:--|
|
||
| Системный клок | 50 МГц (E2) | генератор платы |
|
||
| MIC_CLK | 3.125 МГц = 50/16 | Normal mode микрофона 1.1...4.8 МГц |
|
||
| CIC | sinc^3, децимация 50 | до 62.5 кГц, без умножителей, аккумуляторы 18 бит |
|
||
| ФНЧ | 221 отвод, децимация 5 | до 12 500 Гц |
|
||
| Выходная ЧД | 12 500 Гц | |
|
||
| UART | 921600, 8N1 | 157 байт на кадр, 250 кадров/с, 39.25 кБ/с (43% канала) |
|
||
|
||
Делитель 16 выбран не произвольно: чтобы обойтись без PLL, частота PDM должна
|
||
быть 50 МГц / N, а чтобы децимация до 12 500 Гц была целой, N обязан делить
|
||
4000. Из подходящих делителей 16 даёт самую высокую частоту в пределах
|
||
Normal mode, то есть наибольшее передискретизирование (250).
|
||
|
||
## Дециматор
|
||
|
||
Двухступенчатый, потому что одного CIC мало: его нули стоят на кратных 12.5 кГц,
|
||
и в рабочей полосе он подавил бы сворачиваемые участки только на ~24 дБ. Поэтому
|
||
CIC снимает поток до 62.5 кГц, а всю избирательность даёт ФНЧ.
|
||
|
||
| Показатель | Значение |
|
||
|:--|:--|
|
||
| неравномерность полосы 0...5000 Гц | 0.032 дБ |
|
||
| подавление от 6250 Гц | 76 дБ |
|
||
| подавление сворачиваемых полос | 92 дБ |
|
||
|
||
Спад CIC в полосе не компенсируется отдельным звеном: он задан прямо в желаемой
|
||
АЧХ при расчёте ФНЧ, поэтому смена порядка CIC требует только перезапуска
|
||
генератора коэффициентов.
|
||
|
||
Порядок 3, а не 4, выбран не из соображений АЧХ — характеристики с ним даже
|
||
лучше — а по ограничению кристалла, см. ниже.
|
||
|
||
Коэффициенты и разрядности лежат в `src/fir_coefs_pkg.vhd`, файл генерируется:
|
||
|
||
```bash
|
||
py -3 tools/gen_fir_coefs.py
|
||
```
|
||
|
||
## Шкала
|
||
|
||
Постоянная `'1'` на выводе данных микрофона даёт полную шкалу 24 бит
|
||
(код 8 388 596). Отсюда шкала выхода — dBFS, ровно та, в которой микрофон
|
||
специфицирован. Вольт в тракте нет, `V_REF` в приёмных скриптах не нужен.
|
||
|
||
## Формат кадра UART
|
||
|
||
| Поле | Байт | Описание |
|
||
|:--|--:|:--|
|
||
| SYNC | 2 | `A5 5A` |
|
||
| FRAME_CNT | 2 | uint16 BE, инкремент на кадр |
|
||
| STATUS | 1 | бит0 — переполнение FIFO, бит1 — насыщение дециматора |
|
||
| DATA | 150 | 50 отсчётов x 24 бита, MSB first |
|
||
| CRC16 | 2 | CCITT, poly 0x1021, init 0xFFFF, по FRAME_CNT..DATA |
|
||
|
||
SYNC в CRC не входит: он нужен приёмнику для ресинхронизации до того, как тот
|
||
начнёт что-либо считать.
|
||
|
||
## Распиновка
|
||
|
||
| Цепь | Назначение | Пин ПЛИС | Контакт J1 |
|
||
|:--|:--|:--:|:--:|
|
||
| clk_50m | тактовый генератор платы, 50 МГц | E2 | — |
|
||
| UART_TX | на внешний мост, ПЛИС -> ПК | G5 | 22 |
|
||
| LED | индикация | E8 | 17 |
|
||
| MIC_CLK | тактирование микрофона, 3.125 МГц | J11 | 33 |
|
||
| MIC_DAT | однобитный поток PDM | J10 | 35 |
|
||
| MIC_SEL | вывод SELECT микрофона | K7 | 34 |
|
||
|
||
UART идёт на внешний мост, посаженный на гребёнку: встроенный BL616 ограничен
|
||
по скорости. Его цепи, если понадобятся, — C3 (`FPGA_UART_TX`, контакт 21) и
|
||
B3 (`FPGA_UART_RX`, контакт 19).
|
||
|
||
Микрофон сидит в банке 7, а VCCIO банков 6/7 на этой плате подаётся снаружи
|
||
через контакты 29 и 31 гребёнки — без 3.3 В на них выводы микрофона не работают.
|
||
Питание самого микрофона 1.65...3.6 В.
|
||
|
||
`MIC_SEL` прошивка держит в `'0'`: микрофон выставляет бит по спаду MIC_CLK,
|
||
выборка идёт в середине низкой фазы. Если вывод SELECT посажен на питание
|
||
платой, поменять `SEL_HIGH` в `Top.vhd` на `'1'` — точка выборки переедет сама.
|
||
|
||
## Два ограничения кристалла, добытые на железе
|
||
|
||
Оба проявляются одинаково: синтез и трассировка проходят без замечаний, тайминг
|
||
чистый, симуляция верная, а в кристалле тракт молча отдаёт нули.
|
||
|
||
**Разрядность аккумуляторов CIC не выше 18 бит.** Проверено перебором на плате
|
||
с приёмом по UART: 26, 24 и 20 бит — тракт мёртв, 18, 16 и 10 бит — работает.
|
||
Отсюда порядок 3: требуется `ORDER*log2(DECIM)+1`, для 3 и 50 это ровно 18, для
|
||
4 было бы 24. На аккумулятор ФНЧ (40 бит) ограничение не распространяется — он
|
||
проверен на железе и считает верно.
|
||
|
||
**Сброс по включению ставить нельзя.** Добавление счётчика, держащего `rst_n`
|
||
в нуле первые 256 тактов, убивает тракт целиком. С константой `'1'` всё
|
||
работает. Проверено прошивкой в обе стороны.
|
||
|
||
## Сборка
|
||
|
||
Выводы `clk` (E2) и `LED` (E8) на этом корпусе сидят на двойных функциях
|
||
(SSPI/CPU и READY), поэтому в свойствах проекта их надо разрешить как обычные
|
||
выводы, иначе размещение падает с PR2017. В IDE это флажки CPU, SSPI и READY,
|
||
в `gw_sh` -- `set_option -use_cpu_as_gpio 1 -use_sspi_as_gpio 1
|
||
-use_ready_as_gpio 1`.
|
||
|
||
Занято на GW5A-25: 649 LUT/ALU (3%), 461 регистр (2%), 4 BSRAM (8%), 1 DSP (4%).
|
||
Тайминг сходится с запасом, но полагаться на него как на признак исправности
|
||
нельзя — см. раздел про ограничения кристалла выше.
|
||
|
||
## Состав
|
||
|
||
| Файл | Назначение |
|
||
|:--|:--|
|
||
| `src/Top.vhd` | верхний уровень: только экземпляры и связи |
|
||
| `src/pdm_rx.vhd` | тактирование микрофона и выборка бита в нужной фазе |
|
||
| `src/cic_decim.vhd` | CIC sinc^3, децимация 50 |
|
||
| `src/fir_decim.vhd` | ФНЧ с децимацией 5, последовательная свёртка |
|
||
| `src/fir_coefs_pkg.vhd` | коэффициенты ФНЧ, генерируется |
|
||
| `src/fifo_top.vhd` | буфер отсчётов, заглушка под IP-ядро Gowin |
|
||
| `src/Packets_creator.vhd` | сборка кадра с CRC-16 |
|
||
| `src/UART.vhd` | передатчик |
|
||
| `src/led_status.vhd` | индикация частотой мигания |
|
||
| `src/DigMic.cst` | привязка выводов |
|
||
| `tb/pdm_mic_model.vhd` | поведенческая модель PDM-микрофона |
|
||
| `tb/tb_top.vhd` | сквозной тест: модель -> ПЛИС -> UART -> разбор кадра |
|
||
| `tools/gen_fir_coefs.py` | расчёт коэффициентов ФНЧ |
|
||
| `tools/uart_capture.py` | приём кадров, запись `.raw` и `.bin` |
|
||
| `tools/uart_live.py` | живой просмотр: осциллограмма, спектр, счётчики |
|
||
|
||
## Симуляция (GHDL)
|
||
|
||
```bash
|
||
mkdir -p build && ghdl -a --std=08 --workdir=build src/fifo_top.vhd src/UART.vhd src/led_status.vhd src/pdm_rx.vhd src/cic_decim.vhd src/fir_coefs_pkg.vhd src/fir_decim.vhd src/Packets_creator.vhd src/Top.vhd tb/pdm_mic_model.vhd tb/tb_top.vhd && ghdl -e --std=08 --workdir=build -o build/tb_top tb_top && build/tb_top --stop-time=400ms
|
||
```
|
||
|
||
Модель прогоняет четыре режима. Три из них дают точно вычислимый ответ:
|
||
постоянные `'1'` и `'0'` — плюс и минус полная шкала, чередование `1010` —
|
||
ровно ноль (эта частота попадает в нуль АЧХ CIC). Четвёртый — синус, у него
|
||
проверяется размах. Тракт настраивается около 10 мс, поэтому в каждом режиме
|
||
кадры читаются до первого сошедшегося.
|
||
|
||
## Запись с железа
|
||
|
||
```bash
|
||
py -3 tools/uart_capture.py --list
|
||
```
|
||
|
||
```bash
|
||
py -3 tools/uart_capture.py --port COM5 --seconds 10 --out test1
|
||
```
|
||
|
||
Получаются `test1.raw` (весь поток как пришёл, для отладки канала),
|
||
`test1.bin` (только отсчёты, читается `read_24bit_bin()`) и `test1.log`.
|
||
Кадр с несошедшейся CRC отбрасывается, пропуск по FRAME_CNT добивается нулями,
|
||
чтобы не сдвинулась ось времени.
|
||
|
||
## Живой просмотр
|
||
|
||
```bash
|
||
py -3 tools/uart_live.py --port COM10
|
||
```
|
||
|
||
Осциллограмма в долях шкалы, спектр в dBFS и счётчики кадров в реальном
|
||
времени. Управление кнопками внизу окна, те же действия — клавишами `r`, `a`,
|
||
`d`, `s`, `f`, выход `q`.
|
||
|
||
Запись встроена в просмотр по необходимости: COM-порт держит один процесс, и
|
||
запустить `uart_capture.py` на том же порту параллельно нельзя. Разбор кадров у
|
||
обоих инструментов общий (`FrameParser` в `uart_capture.py`), так что просмотр
|
||
и запись не могут разойтись.
|