时间:2025.10 - 2025.12 | 身份:数字系统与处理器实验课助教 | 黄继业教授 指导

教学助理项目:RISC-V SoC 与 HDMI 外设在 FPGA 上的构建

本项目为杭州电子科技大学黄继业教授指导的教学实践与硬件升级项目。项目原计划由老师安排研究生负责完成,本人在大三期间担任《数字系统与处理器实验》课程助教(该课程理论课本人排名2/55,绩点4.6/5;实验课满绩,因此被老师遴选),在指导大二学生掌握 Quartus 工具使用、Verilog HDL 设计基础的同时,承接并独立完成了该任务。
关键词:HDMI硬件驱动,RV32I SoC,CDC问题,流水线对齐 最终结果:HDMI 硬件实验例程(彩条、乐谱播放),SoC HDMI 数字时钟 Demo

摘要

项目针对数电实验课所用 Cyclone10LP FPGA 开发板进行升级,将原有 SoC 系统中的 VGA 显示接口,替换为通用性更强、应用更广的 HDMI 外设接口,顺利完成硬件适配与逻辑开发,实现了实验平台外设功能的迭代优化。为完成此项综合任务,首先需验证FPGA上720P@60Hz的视频链路有效性,制作了彩条显示与HDMI乐谱播放器两个工程Demo;在此RTL有效基础上,再在RV32I SoC 系统中集成了HDMI 显示外设,并针对CPU 系统时钟域与像素时钟域的跨时钟数据交互(CDC 问题)、以及显示流水线延迟下的时序/像素对齐问题给出可靠实现;最终以基于C 语言编写对于SoC编程的数字时钟Demo 进行验证,结果表明SoC 上的HDMI外设可稳定输出720p 信号,字符显示清晰无乱码且可连续刷新,项目得以完成。

助教任务目标

完成任务 1:驱动与教学工程迁移

实现 FPGA HDMI 驱动并给出彩条 Demo;在此基础上将原 VGA 图像工程迁移到 HDMI,验证复杂应用可用性。

完成任务 2:SoC HDMI 外设集成

在 RV32I 五级流水线 SoC 中集成 HDMI 字符显示,形成 CPU 写显存即显示的 MMIO 访问模型,并完成数字时钟 Demo 验证。

FPGA HDMI 驱动实现

首先,对于FPGA HDMI的硬件构建,本项目中只使用与DVI兼容的“视频传输”部分,其特点是不以数据包形式发送图像,而是以连续像素流的方式按行、按帧扫描输出。发送端负责生成视频时序,接收端依据时序在正确的时间点采样并还原每个像素的RGB 数据,而在传输中间HDMI采用了TDMS编码的形式减少跳变与实现直流平衡,包括了每个通过的8位像素信息、行/场同步信号、有效显示使能信号全部编入TMDS,并调用IP核实现编码),显示器接收端在CLK差分信号下完成串并转换与TMDS 解码,即可连续恢复出逐像素的RGB 数据流。如下图就是本人总结的HDMI视频流编码输出过程:

HDMI视频流编码输出过程
图1 HDMI视频流编码输出过程

HDMI 所需信号传输线(硬件引出)

信号名称 FPGA 引脚 信号名称 FPGA 引脚
HDMIB_D2_PB8HDMIB_D0_PF15
HDMIB_D2_NA8HDMIB_D0_NF16
HDMIB_D1_PB9HDMIB_CLK_NG16
HDMIB_D1_NA9HDMIB_CLK_PG15

上表对应 3 组 TMDS 数据差分对(D2/D1/D0)与 1 组时钟差分对(CLK),共 4 组差分线。

参数 水平(H) 垂直(V) 说明
同步脉冲405H_SYNC / V_SYNC
后沿22020H_BACK / V_BACK
有效显示1280720H_DISP / V_DISP
前沿1105H_FRONT / V_FRONT
总计1650750H_TOTAL / V_TOTAL

HDMI 乐谱播放器

通过以上基本原理,结合网络开源的FPGA-HDMI RTL,即可搭建出一个HDMI-Display的顶层应用模块,通过队应用模块的行列像素指定,即可得到一个完整的彩条显示;同理的,对于HDMI乐谱播放器,再设计乐谱图像显示、边框绘制与动态方框叠加模块,实现乐谱图像的HDMI 显示与当前音符位置指示。HDMI显示效果如下:

HDMI输出彩条
图2(a) HDMI输出彩条(Ref2 报告原图)
HDMI输出数字乐谱
图2(b) HDMI输出数字乐谱,红色框随音乐同步移动(Ref2 报告原图)

SoC HDMI 改造与 CDC 问题

在HDMI硬件充分实现后,开始进行SoC的视频外设改造。首先要对此SoC进行基本认识;此为USTC的开源教学用SoC,一个 SystemVerilog 编写的,以一个 RV32I CPU 为核心的(除CSR 指令),普林斯顿结构的 SoC ,可作为 MCU 使用,包括了UART、指令ROM/RAM、数据RAM、显存RAM与VGA输出、LED/数码管上述外设,并已经开源配置了相应的汇编/C语言编译工具(参考项目开源网址https://github.com/WangXuan95/USTC-RVSoC)(本人升级项目)。CPU 采用经典的五级流水线(IF 取指、ID 译码、EX 执行、MEM 访存、WB回写);总线不属于任何现有标准,就是一种简易的同步握手总线,总线仲裁器上挂载了3个主接口(CPU数据与取值、UART调试)和6个从接口(都可以拓展更改),每个从接口都占有一段地址空间。当主接口访问总线时,仲裁器通过目标地址与从接口起始地址的硬件运算判断该地址属于哪个地址空间,然后将它路由到相应的从接口。

从设备地址范围功能
Slave00x0000_0000 – 0x0000_0FFF指令 ROM
Slave10x0000_8000 – 0x0000_8FFF指令 RAM
Slave20x0001_0000 – 0x0001_0FFF数据 RAM
Slave30x0002_0000 – 0x0002_0FFFVideoRAM(HDMI)
Slave40x0003_0000 – 0x0003_0003用户 UART
Slave50x0003_1000 – 0x0003_100FLED / SEG

注:Slave3 在总线层预留了 4KB 地址窗口(0x0002_0000~0x0002_0FFF), 但当前字符显示链路按 64×8 字符网格工作,实际访问的是前 512B 显存区间。

在本项目已有SoC原理基本了解后,重点对原有VGA外设进行分析,包括了VideoRAM与VGA字符显示模块。字符显示主要是时序生成、当前需要显示的字符定位、请求需要显示的ASCII 码、查询8*16点阵字模、输出像素这些流程完成;VideoRAM是有FPGA上4个M9K块,CPU取指后向总线写入ASCII码,VGA 显示模块不断读取显存用于显示;同时字模的信息,放入VideoRAM中。

理解原有的VGA显示原理后,进行HDMI外设的移植,实质上是在解决驱动层的硬件接口实现的CDC问题,即如何将运行在50MHz 系统时钟域的CPU 与运行在74.25MHz 像素时钟域的HDMI 720P@60Hz 显示逻辑进行安全可靠的跨时钟域数据传输,并且保证在6 级流水线延迟的情况下时序信号与像素数据能够精确对齐,这是整个项目的难点;相比之下,VGA的显示驱动则容易许多,它不存在CDC问题,可以与CPU-总线都用50MHz时钟域。传统的CDC 解决方案包括双触发器同步链(用于单bit 信号)、握手协议(用于多bit 数据但需要复杂的请求-应答逻辑)、异步FIFO(需要额外的读写指针管理和空满检测),但这些方案要么不适用于512 字节大容量存储,要么会显著增加设计复杂度和延迟。

传统FIFO方案对于该CDC问题的不适用性

传统 FIFO 的语义与本工程显存模型并不匹配。在本系统中,CPU 侧以 addr/byte_en 对字符单元进行随机寻址更新,而显示侧以像素时序对当前帧状态进行顺序扫描读取。 HDMI的显示读取,是周期性重读 hdmi_ram 设定显示内容的形式,并非写入顺序等于读取顺序、数据消费关系平衡的单向数据流。

显示链路对连续吞吐的要求决定了 FIFO 不是合适的一线承载体。720p 模式下 HDMI 像素路径需按 74.25MHz 持续供数,FIFO 一旦出现空或者满状态将直接表现为花屏、撕裂或输出不连续;与此同时,FIFO 数据在读出后即被消耗,不适用于保存“当前屏幕内容”这一持久状态。

如果要使用 FIFO,系统复杂度并不会下降。工程要么每帧重发整屏数据,要么仍需在 HDMI 域设计此时钟域的 RAM以维持帧内可重复读取;此外,CPU 写后可见性还需要额外确认/屏障机制。综合语义匹配性、时序稳定性与实现成本,因此本项目不采用“纯 FIFO”作为核心 CDC 方案。

两组 RAM 镜像并行写对于CDC问题的解决

为了彻底规避CDC 问题并保持设计简洁性,并将教学项目尽快落地,本人采用了一种“空间换时间”的双IP核双端口RAM 方案,其核心思想是核心思想是先解除不同时钟域的读写同一设备的冲突,因此为CPU 和HDMI 各自准备一套完全独立的512 字节显存RAM 副本,CPU 侧RAM 工作在50MHz 时钟域,HDMI 侧RAM 工作在74.25MHz 时钟域,两套RAM 的写端口都连接到总线上;因此当CPU 执行Store 指令向显存写入字符时,写使能信号wren 会同时作用于两套RAM(单个 IP_RAM2P 本质是 Simple Dual Port(单写单读)),使得相同的数据在同一时刻(以50MHz 时钟为基准)被写入两套RAM 的相同地址,实现复制HDMI RAM 的效果;而在读操作方面,CPU 通过自己的读端口从CPU 侧RAM 读取数据(这是纯粹的同时钟域读操作,无延迟无风险),HDMI 显示模块则通过自己的读端口从HDMI 侧RAM 读取数据(这也是同时钟域读操作,因为HDMI 侧RAM 的读端口时钟被连接到74.25MHz 像素时钟),两个读操作在各自的时钟域内独立进行,完全没有交集,从而彻底消除了CDC 问题。Altera 提供的IP 核的双端口RAM内部已经处理了“写端口和读端口使用不同时钟” 的情况(通过专用的多时钟域SRAM宏单元和内建的同步电路),因此设计者无需手动编写任何跨时钟域逻辑,只需正确配置IP 核参数即可。双RAM 方案的代价是硬件资源翻倍,但是稳定性完全保障,设计复杂度大幅降低。由此SoC上的HDMI外设升级得以完成。

对于本SoC HDMI外设,可以将VideoRAM设计为“只写不读”,那么的确可以去除CPU时钟域的HDMI镜像 ; 或者在SoC用户的C/汇编语言编译上使用软件影子显存(shadow VRAM)方案,自动将写入VideoRAM的内容,放入DataRAM,也能做到可读可写,但是软件双写还会增加指令数和总线压力,实时性更差;同时还要在硬件或者编译层面,做一个对于DataRAM的一部分区域视同VideoRAM不能随意写的约束,这样的设计过于复杂且难以稳定了;对于数字硬件系统的设计,在ISA不可见层,直接使用硬件进行稳定的解决,这是更好的设计结果

学生使用SoC HDMI的C语言方法

在学生使用层面,不需要知道这些较为复杂的架构原理,只需要利用C语言,对于指定的地址空间,进行ASCII字符的定义即可,这是因良好的硬件设计使得ISA的执行具有不错的稳定性,烧录程序到SoC后HDMI即刻显示对应位置的字符,并可以通过软件延时控制字符跳变。经过教学实践,多名大二同学成功基于该HDMI-SoC 平台开发了PWM 字符闪动显示等其他应用,验证了系统的易用性和稳定性。HDMI-SoC时钟Demo字符显示效果如下图所示:
视频 2:RV32I SoC 数字时钟(HDMI 输出)

学生端C语言代码片段:对于VideoRAM写入字符

#define VRAM_BASE 0x00020000

                void putchar_at(int row, int col, char c) {
                    volatile unsigned char *vram = (unsigned char *)VRAM_BASE;
                    int addr = row * 64 + col;
                    vram[addr] = c;
                }

                int main() {
                    int hour = 12, min = 0, sec = 0;
                    while (1) {
                        putchar_at(3, 0, '0' + hour/10);
                        putchar_at(3, 1, '0' + hour%10);
                        putchar_at(3, 2, ':');
                        putchar_at(3, 3, '0' + min/10);
                        putchar_at(3, 4, '0' + min%10);
                        putchar_at(3, 5, ':');
                        putchar_at(3, 6, '0' + sec/10);
                        putchar_at(3, 7, '0' + sec%10);
                        delay_1s();
                    }
                }

总结

该助教项目完成了从“驱动可用”到“系统可用”的完整闭环: FPGA 端打通 HDMI 输出链路,应用层完成复杂工程迁移,SoC 端完成 MMIO 外设化与 CDC/时序对齐处理。 最终数字时钟 Demo 连续稳定运行,验证了本方案在教学平台中的可靠性与可扩展性。

源码开源地址

PDF报告

若浏览器无法直接预览 PDF,可使用“新窗口打开”按钮。

返回主页对应模块