Skip to content
LNC0831Public

About

Stereo FM music from a single Zynq-7010 FPGA pin: Simulink, RTL, lwIP and AXI DMA

Topics

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

1bitFM — 基于 FPGA 的 1 bit 立体声 FM 调制器

电脑上的 MP3,经以太网送进 Zynq-7010,最后变成 FPGA 引脚上的 1 bit 波形,让普通 FM 收音机播放音乐。 数字音乐输入不需要 ADC,RF 输出没有使用多位 DAC。首次桌面实验中,一根杜邦线就让收音机听到了歌声。

Zynq-7010 与调到 87.00 MHz 的收音机,杜邦线作为实验天线

已实现 87 MHz、真正立体声、480 Mbit/s 串行输出,包含官方 FIFO/FIR IP、定点 DSP、NCO、OSERDESE2 和 PS 顶层连接。 Vitis Unified 2024.2 的 lwIP TCP → DDR 缓冲 → AXI DMA 音频链路已上板播放 MP3; 修复接收窗口流控后,使用者反馈连续播放没有卡顿。轻微底噪、音量和射频质量仍在改进。 软件用法见 网络音频固件说明,RTL 测量结果见 RTL 验证记录。

版本提示:当前 RTL 音频增益为 1/4,比原 1/8 版本提高约 6 dB。 增益回归已通过,新版 bitstream 已上板;当前硬件交接文件为 xsa/091302.xsa,xsa/091301.xsa 保留原 1/8 版本。 现有射频分离度/SNR 仿真报告也属于原版本,不是新版实测指标。

flowchart TD
    A[电脑 MP3 / WAV] --> B[解码为 48 kHz S16LE 双声道]
    B --> C[lwIP TCP / PS DDR]
    C --> D[AXI DMA / AXIS FIFO]
    D --> E[预加重 / 19 kHz 导频 / 立体声编码]
    E --> F[FM NCO / OSERDESE2]
    F --> G[1 bit RF 输出]
    G --> H[FM 收音机]
Loading

快速播放

先按 软件说明 加载匹配的 bitstream、初始化 PS、运行 fm_app.elf 并 Continue。 开发板默认 192.168.2.10/24、TCP 端口 5001,电脑使用同网段地址。

uv run --no-project scripts/send-audio.py --tone 10
uv run --no-project scripts/send-audio.py --file "D:\Music\song.mp3"

测试音为左声道 1 kHz、右声道 2 kHz。--file 在电脑上转换音频,支持 MP3; 首次运行 uv 会下载解码依赖。--wav 可直接发送已经准备好的 48 kHz PCM16 双声道 WAV。

从源码构建

  • FPGA:Vivado 2024.2,器件 xc7z010clg400-2,打开 prj/prj.xpr,顶层 fm_top;生成 IP、综合、实现并生成 bitstream。
  • PS 软件:Vitis Unified 2024.2(不是旧版 SDK/XSCT 工程流程),从 XSA 创建 standalone Cortex-A9 #0 平台, 加入 lwip220,并使用 vitis/fm_app/src 中的源文件。详细域名、参数及 Python API 构建入口见 软件说明。
  • 仿真:MATLAB/Simulink R2023b 及文中列出的工具箱。基本 RTL 回归使用 Icarus Verilog;官方 IP 联合仿真使用 Vivado/XSim。

工程来自 Windows 开发环境,Vitis 组件配置和部分历史文档保留原工作区绝对路径。 换目录/换电脑时应重建 Vitis 平台、更新 XSA 路径,不能直接复用缺失的 BSP/export 缓存。 constraints/top.xdc 针对本次接线:50 MHz 输入 K17、复位 M20、LED T12、RF P20/LVCMOS33;其他板卡需重新核对。 目前模拟输出滤波、杂散、时钟抖动和真实射频指标尚未完成测量;桌面能接收不代表成品发射机指标。

开源

原创内容使用 MIT 许可证,第三方组件范围见 NOTICE.md。 参与方式和回归入口见 CONTRIBUTING.md。

87 MHz 真正立体声 FM 发射 RTL:用户通过 Zynq HP + AXI DMA 提供 48 kHz 双声道 S16LE PCM,RTL 接收 32bit AXIS(低 16bit 左、高 16bit 右,带 TKEEP/TLAST)。 详细接线、包语义、时钟、缓冲、立体声编码与验证计划见 RTL 设计文档。 下文仿真操作与参数表仍描述现有单声道参考模型。

FPGA 基础模块优先使用 Vivado IP/XPM(FIFO、时钟、FIR、CDC),不自行重写;Vivado 操作统一使用 synthpilot MCP。 用户最新 DMA 为 32bit AXIS,直接对接音频格式,音频跨域使用 AXIS Data FIFO。 现有时钟和 BD 的核对结果见 BD 对接记录。

仓库结构

1bitFM/
├── simulink/       # MATLAB 源码与可直接运行的 .slx 模型
├── rtl/            # 可综合 RTL 源码
├── tb/             # RTL 仿真测试平台
├── constraints/    # 时钟、引脚与 IO 约束
├── prj/            # 用户创建的 Vivado 工程,入口 prj.xpr
├── xsa/            # 含 bitstream 的硬件交接文件
├── vitis/          # Unified 2024.2 平台配置与 fm_app C 源码
├── scripts/        # 构建、验证与网络音频发送工具
├── tests/software/ # C 固件状态机与 Python 发送工具测试
└── docs/           # FPGA 架构和开发步骤

FPGA 工程使用 Vivado 2024.2,器件为 xc7z010clg400-2,综合顶层为 fm_top。 见 FPGA 开发说明。模型、源码和工程配置纳入 Git; 仿真结果、模型备份、编译缓存及 Vivado 运行产物保留在本机并由 .gitignore 排除。

Simulink 参考模型

在 MATLAB 中将当前目录设为本项目根目录,然后运行:

addpath(fullfile(pwd, 'simulink'));
metrics = runFmDemo();

首次运行会生成并打开 simulink/fm1bit_tx.slx。已有模型会保留,不会自动重建覆盖。 也可直接打开模型,点击 Run,默认输入为 10 ms 的 1 kHz 测试音。 双击 Audio_scope 查看输入音频和恢复音频,双击两个子系统查看内部信号链。 接收解调随模型运行,频谱绘图与定量验证使用 runFmDemo。 修改源代码后运行 buildFmModel(true) 重建;重建前会备份磁盘模型,且拒绝覆盖尚未保存的模型编辑。

测试自己的音乐片段:

metrics = runFmDemo("F:/audio/music.wav", 0.05);

脚本读取文件开头,转为单声道 48 kHz,限制音频带宽至 15 kHz,并限制幅度。完整 RF 仿真每秒产生 4.8 亿个比特,当前入口限定片段为 6–100 ms;这用于波形和频谱验证,不适合整首音乐播放。 输入文件没有自动导出或播放。默认单音测试要求音频相关系数 > 0.98、重建 SNR > 20 dB。

信号链

48 kHz PCM → 限幅 → 频率控制字 → 32bit 相位累加 → 最高位 → 1bit RF

顶层:PCM_48kHz → FM_Modulator → FM_Demodulator → Recovered_audio。

FM_Modulator 内部包含限幅和 32bit 相位累加器,输出 RF 比特、频率控制字和限幅后的监测音频。

FM_Demodulator 内部为:87 MHz 复数下变频 → 两级低通抽取至 480 kHz → 相位差鉴频 → 15 kHz 低通抽取至 48 kHz。 本振相位、滤波器状态和鉴频器的前一采样值均跨帧保持,每个 RF 帧恢复一个音频样本。 接收端只接收 RF 比特,不读取发射频率控制字或原始音频。

接收滤波改为因果 FIR 实现,保留启动瞬态;滤波与鉴频的名义延迟为 190.625 微秒, 此外发射音频零阶保持对应约半个音频采样周期的延迟。Scope 显示真实延迟,脚本测量则进行延迟对齐, 并排除首尾瞬态。该指标与旧版离线 filtfilt 接收器的数值不能直接等同。 验证脚本通过独立批量 upfirdn 接收链检查 Simulink 的流式输出,不对模型输出再做解调。

参数 当前值
输入 48 kHz 单声道 PCM
默认测试音 1 kHz,幅度 0.8
载频 87 MHz
满幅频偏 ±75 kHz(测试音峰值频偏 ±60 kHz)
RF 比特率 480 Mbit/s
相位 32bit、模 2^32 回绕
帧大小 每个音频样本 10000 个比特

频率控制字:K = round((fc + deviation * audio) / bitRate * 2^32)。 每个比特先更新相位 phase = mod(phase + K, 2^32),再取最高位。 模型使用整数值 double 精确表示 32bit 状态,避免 MATLAB 原生 uint32 加法饱和;它是算法参考模型, 并非可直接部署的 HDL 架构。帧之间连续保留相位,验证脚本用独立闭式公式逐比特检查。

这里的 1bit 是 FM 方波的两个电平,不是音频 PCM 压缩成 1bit,也不是 Sigma-Delta 调制。 数字音乐文件不需要 ADC;输出端不需要多位 DAC,但实际硬件仍需要合适的输出接口和射频滤波, 用于选择目标频带并抑制谐波。仿真的数字频谱只覆盖第一奈奎斯特区,不包含物理引脚的模拟频谱副本。

文件

  • simulink/buildFmModel.m:程序化构建模型。
  • simulink/fmNcoFrame.m:写入 MATLAB Function 模块的 NCO 代码;修改后需另行重建模型才能生效。
  • simulink/fmDownconvertFrame.m:连续本振与复数混频。
  • simulink/fmChannelFilterFrame.m:两级多相 FIR 滤波抽取。
  • simulink/fmDiscriminateFrame.m:相位差鉴频。
  • simulink/fmAudioFilterFrame.m:音频低通与抽取。
  • simulink/runFmDemo.m:运行、逐比特及接收参考核对、测量和绘图。
  • simulink/results/fm_demo.png:波形、频谱和解调音频。
  • simulink/results/metrics.json:本次仿真测量。
  • simulink/results/fm_metrics.mat:测量与音频数据,不保存庞大 RF 数据。

依赖:MATLAB、Simulink、MATLAB Function 模块支持、Signal Processing Toolbox、DSP System Toolbox;已检测环境为 R2023b。

后续阶段

按 RTL 设计文档的 M1~M5 推进: AXIS/FIFO/并行 NCO → 双声道定点与立体声编码 → 集成回归 → 串行器和时序验证 → 用户 BD 对接。

上述旧 Simulink 模型仍为单声道;新增 RTL 回归覆盖预加重、立体声复合信号和串行器延迟。 输出模拟滤波器、时钟抖动和 IO 电气特性尚未模拟。 音频 SNR 是理想软件接收链下与对齐输入相比的重建误差指标,不是板级实测指标。

官方参考


📢 广告位 · FPGA 开发 MCP 插件:synthpilot.dev

About

Stereo FM music from a single Zynq-7010 FPGA pin: Simulink, RTL, lwIP and AXI DMA

Topics

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages