国外大学生都用FPGA做什么项目(十八)

FPGA技术江湖 2026-07-10 08:20
本文介绍了两个基于 FPGA 的创新项目。第一个项目结合机器学习与光线追踪技术,利用 Spotify 音乐特征训练情绪分类模型,对歌曲进行情绪识别,并通过 HPS 将识别结果传递给 FPGA,驱动基于 Ray Marching(光线步进)算法的实时光线追踪 GPU,生成与音乐情绪相匹配的三维可视化效果。项目重点展示了 FPGA 在浮点运算、流水线设计、SDF 场景建模以及实时图形渲染方面的优势,实现了机器学习与硬件加速相结合的音乐可视化系统。

第二个项目则将蒙特卡洛算法应用于二十一点(Blackjack)游戏,通过 FPGA 并行执行大量随机对局模拟,实时计算玩家胜率、平局概率以及下一张牌出现的概率,为玩家提供决策辅助。系统采用 HPS+FPGA 协同架构,利用 FPGA 完成高吞吐量概率计算和 VGA 图形显示,HPS 负责游戏逻辑与数据处理,充分体现了 FPGA 在并行计算、概率仿真和实时交互系统中的高性能优势。

小引:

据我了解,目前国内很多大学是没有开设FPGA相关课程的,所以很多同学都是自学,但是自学需要一定的目标和项目,今天我们就去看看常春藤盟校Cornell University 康奈尔大学开设的FPGA项目课程,大部分课程是有源码的,而且和国内使用习惯类似都是Verilog开发,还是很有借鉴意义的。

项目链接

https://people.ece.cornell.edu/land/courses/ece5760/FinalProjects/

项目介绍

2024年春季 开发板:CycloneV DE1-SoC

使用机器学习和定制FPGA光线追踪GPU实现音频响应式可视化

国外大学生都用FPGA做什么项目(十八)图1

介绍

在传统艺术表达中,视觉与听觉往往相互交融,以此调动受众的情绪共鸣。视听元素若能实现精妙融合,便能营造出极具冲击力的独特艺术效果。但和诸多人文交叉领域的议题相似,影像与声音之间内在的关联很难用标准化、精准的定义加以概括。也正因这份复杂难解的特质,视听融合的研究议题反而对创作者、科研工作者,以及兼具两种身份的跨界从业者产生了强烈的吸引力。

本次设计选取了一套差异化的实现方案,依托课程所学的专业技术与工具,完成一首乐曲的视觉化演绎创作。整体流程借助机器学习算法挖掘音乐载体中蕴藏的情感特征,并基于 FPGA 硬件平台实现光线追踪视觉画面的实时生成

结果

国外大学生都用FPGA做什么项目(十八)图2
国外大学生都用FPGA做什么项目(十八)图3
国外大学生都用FPGA做什么项目(十八)图4

情感分析

构建预测歌曲情感的模型主要涉及两个不同的开发流程:应用程序开发和机器学习工程。本节将概述设计 Python 应用程序的各个组成部分,该应用程序最终将作为项目的用户界面。

Spotify Web 开发 API

流媒体 API 开发便捷,本项目选用 Spotify Web API 搭建应用框架,依托 Python 封装库 Spotipy 调取曲库与歌单数据。该接口可提取量化歌曲风格的多维度音乐特征,拼接后作为模型输入向量;同时支持批量读取歌单曲目,为此我们自建公开歌单收录 421 首标注歌曲作为训练集,方便人工标注情感标签。

频繁重复调用 API 存在访问限额瓶颈,开发中多次触发接口限流。为规避该问题,基于 OpenPyxl 编写存储工具,将歌单音乐特征持久化存入 Excel 表格,减少重复接口请求。

模型迭代训练

数据集保存为 xlsx 格式后,人工新增情绪标签列,初始划分焦虑、低落、悲喜交织、愤怒、愉悦五类情绪,采用 Spotify 提供的声学度、舞曲度、能量、效价等十项音乐特征,参考相关技术文章搭建分类模型。

基于 PyTorch 搭建可变宽度三层 MLP,激活函数选用 ReLU,损失函数采用交叉熵。初代模型分类效果极差,一是各特征数值区间差异过大未做归一化,易造成过拟合;统一缩放至 [0,1] 区间后效果仍不理想。为此简化任务:仅保留能量、效价两大核心特征,改为正负情绪二分类,最终线性模型测试准确率约 70%。

后续持续调整标签体系、学习率、迭代轮次、网络层数等超参,迭代优化模型。最终模型仅使用能量、效价两项特征,三层 MLP 结构(输入 2 维、隐藏层 10 维、输出 3 维),实现快乐、悲伤、愤怒三分类,测试准确率约 85%,满足演示需求。

GUI 界面与板端通信

采用 Tkinter 开发 Python 图形交互界面,界面设切换歌曲可视化按钮。通过 Spotipy 绑定用户账号,实时拉取当前播放曲目特征,送入训练好的模型推理情绪,将情绪与能量参数下发至 HPS 端,组合生成 6 套 FPGA 可视化渲染方案。

对比多套进程通信方案后选用 SFTP 完成 PC 与 SoC 数据传输,配置 SSH 密钥实现一键传输参数文本文件;HPS 通过 AXI 轻量总线 PIO 接口,把音乐情感参数下发至 FPGA,完成视觉效果动态配置。

基于FPGA的光线追踪GPU

光线步进算法

为简洁起见,本节将对光线步进算法进行简要描述。如果想了解更多关于该算法的信息,Michael在他的网站上撰写了一篇完整的文章(还附有精美的演示!)(https://michael-crum.com/raymarching/)。

光线追踪是一种基于物理的渲染技术:它模拟光线与物理对象的相互作用,从而模拟真实世界的视觉效果。由于模拟光源发出的数十亿个光子是不切实际的,基于物理的渲染技术转而反向追踪光子的路径,最终到达摄像机(或眼睛,取决于你的理解方式)。

从根本上讲,渲染的目标是为屏幕上的每个像素分配颜色。你可以把屏幕想象成一扇窗户。透过这扇窗户,你可以看到我们想要渲染的场景,它由简单的形状组成。屏幕/窗户可以通过像素进行量化,每个像素都指定了窗户上的一个唯一坐标。场景中的光线要进入你的眼睛,就必须穿过窗户上的某个像素。假设计算出从你的眼睛到窗户上每个像素的光子向量。然后,可以利用这个向量反向推导,确定穿过该像素的光子信息。根据这些信息,可以确定该像素的颜色。图 1 展示了单个像素的渲染过程。

国外大学生都用FPGA做什么项目(十八)图5
图 1:从像素到光线(图片来源:Michael Walczyk)

该过程在计算机图形学领域已有深入研究,并使用逆相机投影矩阵进行计算。对于虚拟相机,此操作可简化为以下几个步骤:

国外大学生都用FPGA做什么项目(十八)图6

计算出逐像素光线后,就可以开始反向追踪它们在场景中的传播。首要挑战是确定光线与场景的交点。一种方法是解析地计算光线与场景的交点。这是光线追踪的基础,但这种计算成本很高,并且随着场景复杂性的增加,计算效率会大幅下降。为了在资源受限的FPGA上使用,需要更巧妙的方法。

引入光线步进算法,这是一种用于计算光线与场景交点的迭代解决方案。光线步进算法的关键抽象概念是符号距离场(SDF)。SDF 是一个函数,它接收空间中的一个点作为输入,并返回该点到场景的距离。为了说明这个概念,将使用二维 SDF,它们很容易扩展到三维空间。最简单的二维 SDF 是位于原点的圆。计算非常简单:

return length(point) - radius;

对于各种图元,无论是二维还是三维,都存在这样的距离函数(见图 2)。

国外大学生都用FPGA做什么项目(十八)图7
图 2:光线追踪图元

光线步进利用场景函数(SDF)来确定在不与场景相交的情况下,光线可以安全行进多远。具体过程如下:

国外大学生都用FPGA做什么项目(十八)图8

需要注意的是,步骤 3保证不会设置任何内容。p在物体内部。因为p从自由空间开始,d是沿任意方向到场景的最小距离,沿任意射线移动d这将导致新的距离大于等于 0。如果光线与场景相交,d将收敛到零,算法会将该点注册为交点。图 3 展示了该算法在二维场景中的应用。每个蓝点都是p经过若干次迭代后,每个圆圈代表d评估p每次迭代。

国外大学生都用FPGA做什么项目(十八)图9

图 3:2D 光线步进演示(源代码:https://github.com/usedhondacivic/ThreeJS-Raymarcher/blob/master/2d_demo.html)

多个对象的SDF可以通过取各自SDF的最小值来简单地合并。类似的运算也适用于求交集和求差集。SDF还可以进行变形,例如缩放、旋转、扭曲以及在空间中重复。

在计算SDF比解析交点更高效的情况下,光线步进算法的性能优于光线追踪。作为一种迭代解决方案,它也非常适合在FPGA上被分解成多个时钟周期。此外,每个像素的计算都是完全独立的,这非常适合大规模并行化。这正是GPU的用途所在,也是它们在图形应用中如此重要的原因。

在FPGA上进行浮点和向量运算

为了能够运行光线步进算法,需要一个可以在FPGA上运行的分数表示。传统的解决方案是使用定点表示,但这会在幅度或精度方面做出权衡。由于光线步进算法需要在很宽的幅度范围内运行,因此定点表示并不适用。最终决定使用由往届学生编写并经Bruce Land改进的1.8.18浮点实现(https://people.ece.cornell.edu/land/courses/ece5760/DE1_SOC/HPS_peripherials/Floating_Point_index.html)。该实现兼顾了低幅度精度(这对归一化向量至关重要)和高幅度表示(这对远距离物体渲染至关重要),并提供了令人印象深刻的精度范围。

为了简化 Verilog 代码中许多重复的操作,编写了一个利用浮点运算的向量数学库。该库位于附录的 vector_ops.v 文件中,包含了点积、标量乘法、加法和 3x3 矩阵乘法等运算。

原型制作与验证

在正式开始使用 Verilog 实现之前,先用 GLSL(OpenGL 着色语言)创建了一个参考实现。GLSL 是一种类似 C 语言的语言,专门用于编写着色器(在 GPU 上逐像素运行的程序)。这个 GLSL 实现只有 100 行代码,可以在附录的“fractal frag”部分找到。使用该参考实现渲染了谢尔宾斯基金字塔分形(图 4)。

国外大学生都用FPGA做什么项目(十八)图10
图 4:使用 GLSL 渲染的塞尔宾斯基金字塔

在完成 GLSL 算法原型后,我们转入 Verilog 硬件开发。为避开 Quartus 长达数分钟的全编译耗时,我们采用 Verilator 工具:将 Verilog 代码编译为多线程 C++ 程序,再结合 SDL 库直接渲染 VGA 图像。这套仿真环境能跑出 2~3 帧每秒的画面,大幅加快调试迭代速度,让我们高效完成代码排错。图 5 就是仿真输出的 VGA 效果图。

国外大学生都用FPGA做什么项目(十八)图11
图 5:使用 SDL 渲染的验证模型的输出

架构

光线步进GPU采用流水线架构,可以同时计算数十个像素。对于任何像素,第一步都是使用之前详述的公式计算其对应的光线。之后,像素和光线分量被传递到第一个光线步进核心。每个光线步进核心负责光线步进算法的一次迭代。由于算法本身需要多个时钟周期,而SDF计算可能需要更多,因此整个核心作为一个长流水线运行。每个时钟周期传递一个像素/光线组合,并且每个时钟周期,核心都会输出相同的像素和光线信息,并附带一个新的p值。这些核心可以串联起来,从而在每个时钟周期内实现多阶段迭代。

国外大学生都用FPGA做什么项目(十八)图12
图 6:简化的管道架构

可串联的核心数量取决于FPGA上的可用硬件。由于计算更复杂的SDF需要额外的硬件,因此级数也与场景的复杂性成反比。对于简单的场景(例如单个立方体),最多可以串联六个核心。对于更复杂的场景,核心数量会降至三个甚至两个,并且很快就会对渲染质量造成不利影响。事实上,即使是六次迭代的光线追踪器也只能提供较差的渲染结果,如图7所示。

国外大学生都用FPGA做什么项目(十八)图13
图 7:经过六次迭代渲染的“立方体”

为了获得更好的效果,必须对架构进行改进,允许像素在需要进一步细化时重新进入流水线。采用这种策略,每个像素/光线都会在最后一个核心中进行评估,以确定它是否与场景相交。如果相交,则将一个新的像素推送到流水线中,并将全局像素索引递增以表示下一个像素。如果尚未与场景相交,则将其返回到第一个核心,并且像素索引不递增。这样,像素可以根据需要多次通过流水线。

国外大学生都用FPGA做什么项目(十八)图14
图 8:更新后的管道架构

这种架构为需要迭代的像素提供了无限次的迭代次数,从而实现了更清晰、更精细的渲染效果。它还支持可变刷新率,低渲染负荷的像素可以持续重新渲染,而高渲染负荷的像素则在后台缓慢渲染。这使得镜头移动时画面响应迅速,同时在近距离观察时也能呈现丰富的细节。此外,它还允许渲染原本需要大量资源才能在单一渲染管线中完成的高复杂度场景。

国外大学生都用FPGA做什么项目(十八)图15
图 9:使用可变像素迭代次数渲染的更佳立方体。

如何在不崩溃的情况下管理管道

使用的浮点库需要两个时钟周期才能完成一次浮点加法运算,五个时钟周期才能完成一次平方根倒数运算。这导致整个项目都需要流水线处理,以确保相关数据在正确的时钟周期可用。手动编写每个流水线阶段既繁琐又容易出错,因此采用了一种使用 generate 语句来处理流水线寄存器的系统。例如:

module FP_sqrt #(
    parameter PIPELINE_STAGES = 4
) (
    input i_clk,
    input [26:0] i_a,
    output [26:0] o_sqrt
);
    wire [26:0] inv_sqrt;
    reg  [26:0] i_a_pipe [PIPELINE_STAGES:0];
    FpInvSqrt inv_sq (
        .iCLK(i_clk),
        .iA(i_a),
        .oInvSqrt(inv_sqrt)
    );
    FpMul recip (
        .iA(inv_sqrt),
        .iB(i_a_pipe[PIPELINE_STAGES]),
        .oProd(o_sqrt)
    );
    genvar i;
    generate
        for (i = 0; i < PIPELINE_STAGES; i = i + 1) begin : g_ray_pipeline
            always @(posedge i_clk) begin
                i_a_pipe[0] = i_a;
                i_a_pipe[i+1] <= i_a_pipe[i];
            end
        end
    endgenerate
endmodule

这段代码创建了一系列长度为PIPELINE_STAGES 的流水线寄存器,并在每个时钟周期将数据推送到流水线中。这是必需的,因为输入i_a会在每个周期更新,并且在FpInvSqrt的输出可用于乘法运算时,输入值会发生变化。这段代码只是一个简单的示例,但该系统对于代码的其他部分至关重要,因为这些部分需要流水线处理多达十个不同的值。

像素到光线

如背景部分所述,与像素对应的光线可以通过以下方式计算:

国外大学生都用FPGA做什么项目(十八)图16

这假设摄像头始终保持直线运动,但这有点乏味。为了增加趣味性,加入了矩阵运算。由于摄像头运动将在HPS上计算,我们用C语言计算了矩阵,并通过PIO将其传输到FPGA。

vec2 xy = coordinate.xy - resolution.xy / 2.0;
float z = resolution.y / tan(radians(FIELD_OF_VIEW) / 2.0);
vec3 ray = lookAt(
    -camera_pos,
    vec3(0.0, 0.0, 0.0),
    vec3(0.0, 1.0, 0.0)
) * normalize(vec3(xy, -z));

乍一看,这些运算在 FPGA 上执行起来似乎很复杂,尤其是除法和正切运算。然而,除法resolution和field_of_view正切都是常量,因此任何涉及它们的运算都可以预先计算。实际上,整个z赋值操作最终都归结为一个常量。所有其他运算都是线性的,可以使用我们创建的向量数学库进行计算。计算一条射线需要 9 个周期,但它是流水线式的。以下是该运算在 Verilog 中的实现:

wire signed [`CORDW:0] x_signed, y_signed, x_adj, y_adj;
assign x_signed = {1'b0, i_x};
assign y_signed = {1'
b0, i_y};

assign x_adj = x_signed - (`SCREEN_WIDTH >> 1);
assign y_adj = y_signed - (`SCREEN_HEIGHT >> 1);
wire [26:0] x_fp, y_fp, z_fp, res_x_fp, res_y_fp;
Int2Fp px_fp (
    .iInteger({{5{x_adj[`CORDW]}}, x_adj[`CORDW:0]}),
    .oA(x_fp)
);
Int2Fp py_fp (
    .iInteger({{5{y_adj[`CORDW]}}, y_adj[`CORDW:0]}),
    .oA(y_fp)
);
Int2Fp calc_res_x_fp (
    .iInteger(`SCREEN_WIDTH),
    .oA(res_x_fp)
);
Int2Fp calc_res_y_fp (
    .iInteger(`SCREEN_HEIGHT),
    .oA(res_y_fp)
);
FpMul z_calc (
    .iA(res_y_fp),
    .iB(`FOV_MAGIC_NUMBER),
    .oProd(z_fp)
);
wire [26:0] x_norm_fp, y_norm_fp, z_norm_fp;
VEC_normalize hi (
    .i_clk(i_clk),
    .i_x(x_fp),
    .i_y(y_fp),
    .i_z(z_fp),
    .o_norm_x(x_norm_fp),
    .o_norm_y(y_norm_fp),
    .o_norm_z(z_norm_fp)
);
wire [26:0] z_neg_fp;
FpNegate negate_z (
    .iA(z_norm_fp),
    .oNegative(z_neg_fp)
);
VEC_3x3_mult oh_god (
    .i_clk(i_clk),
    .i_m_1_1(look_at_1_1),
    .i_m_1_2(look_at_1_2),
    .i_m_1_3(look_at_1_3),
    .i_m_2_1(look_at_2_1),
    .i_m_2_2(look_at_2_2),
    .i_m_2_3(look_at_2_3),
    .i_m_3_1(look_at_3_1),
    .i_m_3_2(look_at_3_2),
    .i_m_3_3(look_at_3_3),
    .i_x(x_norm_fp),
    .i_y(y_norm_fp),
    .i_z(z_neg_fp),
    .o_x(o_x),
    .o_y(o_y),
    .o_z(o_z)
);

射线行进核心

光线追踪核心是根据以下GLSL模型构建的:

rayInfo raymarch() {
    vec3 dir = getPixelRay();
    float depth = MIN_DIST;
    for (int i = 0; i < MAX_MARCHING_STEPS; i++) {
        float dist = sceneSDF(u_camera + depth * dir);
        if (dist < EPSILON) {
            return rayInfo(vec3(1.0, 1.0, 1.0)));
        }
        depth += dist;
        if (depth >= MAX_DIST) {
            return rayInfo(vec3(0.0, 0.0, 0.0));
        }
    }
    return rayInfo(vec3(0.0, 0.0, 0.0));
}

这里的主要难点在于需要流水线处理并保持同步的输入数量庞大。在光线追踪核心之间(每个核心代表 for 循环的一次迭代),我们必须跟踪:

  • 当前点 (x, y, z)
  • 当前深度
  • 像素位置(x,y)
  • 对应于该像素 (x, y, z) 的光线

此外,SDF 的流水线长度会根据场景而变化,因此整个系统必须基于此参数进行设置。下面的框图忽略了这些复杂情况,但可以在代码库中找到更详细的信息。

国外大学生都用FPGA做什么项目(十八)图17
图 10:射线追踪核心示意图。

SDFs

计算 SDF 的方法与本文中的其他数学运算类似。为了展示两种基本图形的硬件要求差异,请比较球体和立方体的框图:

国外大学生都用FPGA做什么项目(十八)图18
图 11:球体的 SDF 示意图。
国外大学生都用FPGA做什么项目(十八)图19
图 12:立方体的 SDF 示意图。

如背景部分所述,SDF 也可以组合使用。然而,这在流水线技术中又会变得棘手。操作中速度较快的 SDF(从时钟周期到有效输出所需时间较短)必须进行流水线处理,以匹配速度较慢的 SDF 的延迟。我们使用模块参数来增强操作的鲁棒性:

box BOX (
    .clk(clk),
    .point_x(a_x),
    .point_y(a_y),
    .point_z(a_z),
    .dim_x(`ONE),
    .dim_y(`ONE),
    .dim_z(`ONE),
    .distance(cube_dist)
);
sphere BALL (
    .clk(clk),
    .point_x(a_x),
    .point_y(a_y),
    .point_z(a_z),
    .radius(`ONE_POINT_THREE),
    .distance(sphere_dist)
);
sdf_difference #(
    .SDF_A_PIPELINE_CYCLES(9),
    .SDF_B_PIPELINE_CYCLES(11),
) DIFF (
    .clk(clk),
    .i_dist_a(sphere_dist),
    .i_dist_b(cube_dist),
    .o_dist(distance)
);
国外大学生都用FPGA做什么项目(十八)图20
图 13:一个立方体减去一个球体。

下面链接里可以找到使用的所有 SDF 的 Verilog 代码以及一些基本操作(并集、差集)。

https://github.com/usedhondacivic/fractal_gpu/tree/main/verilator/sdf

色彩计算与 VGA 驱动

单独设计了色彩转换模块,将光线步进输出的距离值转为像素颜色。方案十分轻量化:对浮点距离值移位后取低位作为 RGB 分量,移位参数由 HPS 通过 PIO 实时修改,自由切换画面色调,还可单独关闭任意色彩通道。

现存问题与优化展望

项目硬件已经跑满 FPGA 性能,复杂场景下出现明显画面撕裂。仿真结果正常,问题主要源于时序违规,超长流水线也难以规避差一错误。

情绪识别模型同样存在局限:仅 3 个分类无法包容模糊情感,仅依靠能量与情绪两项特征容易出现误判。后续可以扩充情感类别,并加入音调等更多音频特征来提升识别效果。

整体来看,项目成功实现了音乐情绪驱动 3D 实时渲染,圆满完成了跨媒介情感表达的设计目标。

项目链接

https://people.ece.cornell.edu/land/courses/ece5760/FinalProjects/s2024/mmc323_ahm234/mmc323_ahm234/index.html

代码链接

https://github.com/usedhondacivic/fractal_gpu/tree/main

视频链接

https://youtu.be/r3ks_tyS-WY?si=itMd-UVCDGFUDa-O

使用蒙特卡洛概率的二十一点

项目引言

本项目将 21 点纸牌游戏与蒙特卡洛仿真相结合,打造出体验丰富的交互式系统。玩家在游玩 21 点的同时,能够获取海量仿真计算得出的实时概率参考。游戏画面输出到 FPGA 驱动的 640×480 分辨率 VGA 显示器上,图形界面清晰、交互流畅。项目核心采用蒙特卡洛方法,模拟数百万种对局走向,为玩家提供数据决策建议。系统会根据当前手牌与玩家操作推演对局结果,算出获胜、平局的概率,同时预测玩家下一张抽到牌的点数,辅助玩家做出最优选择。

游戏规则

21 点是玩家与庄家对抗的纸牌游戏。每局开局,1~4 名玩家各拿到 2 张手牌;庄家同样拿到两张牌,但庄家的第一张牌会隐藏,要等到所有玩家完成操作后才会翻开。游戏目标:让手牌点数尽可能接近 21 点,但不能超过 21 点,且最终点数大于庄家手牌点数。牌面点数规则:

  • 10、J、Q、K 计为 10 点;
  • 数字牌 2~9,点数与牌面数字一致;
  • A 牌可由玩家自行选择计为 1 点或 11 点。

若玩家手牌点数不足 21,可以选择 “要牌(hit)”,随机抽取一张新牌;也可以选择 “停牌(stand)”,保留现有手牌。一旦手牌总点数超过 21,玩家爆牌,直接输掉本局。如果玩家拿到初始两张牌刚好凑成 21 点,即为 “黑杰克(Blackjack)”。若庄家没有同时拿到黑杰克,玩家直接获胜;如果庄家初始手牌也是黑杰克,则本局平局。其他情况下:玩家点数更接近 21 且大于庄家,或是玩家未爆牌而庄家爆牌,玩家获胜。

在本项目中,每位玩家初始本金为 1000 美元,单局下注下限为 3 美元,上限为 1000 美元。

项目目标

本项目在 DE1-SoC 开发板上完成了蒙特卡洛算法的软硬件设计与部署。系统可以根据玩家与庄家的初始手牌,实时计算本局胜、平、负的概率;同时实现算牌功能,预测下一张牌及其出现概率,并按概率升序给出 5 张候选牌,帮助玩家判断是继续要牌还是选择停牌。

国外大学生都用FPGA做什么项目(十八)图21

总体方案设计

随机数发生器

HPS 处理器直接调用软件随机函数;FPGA 硬件仿真则使用硬件随机模块。项目先用 LFSR 线性移位寄存器生成伪随机数,但单路 LFSR 序列可预测、随机性不足。为此我们采用多路并行 LFSR,将各路随机首位比特拼接,大幅提升随机质量。这套随机电路用来生成片上 SRAM 的随机地址,实现游戏里的随机发牌。若要规避连续重复数值,可额外增加数值校验逻辑,重复则重新生成。

总线互联配置

在 Platform Designer 中配置了片上 SRAM 与 PIO 接口:PIO 挂载轻量级 AXI 总线,SRAM 挂载高速 AXI 总线,所有外设都分配独立的内存映射地址,完成 ARM 与 FPGA 的数据交互。

国外大学生都用FPGA做什么项目(十八)图22
国外大学生都用FPGA做什么项目(十八)图23
国外大学生都用FPGA做什么项目(十八)图24
国外大学生都用FPGA做什么项目(十八)图25
国外大学生都用FPGA做什么项目(十八)图26
国外大学生都用FPGA做什么项目(十八)图27

胜负平局概率计算

每个并行硬件单元独立完成 100 次对局仿真并统计胜场,所有单元运算结束后统一汇总总获胜、平局次数,再通过 PIO 传给 HPS。HPS 用总胜场数除以仿真总次数,算出胜率和平局率,并实时换算为百分比。

抽牌概率预测

硬件状态机统计 6000 次仿真里 10 种牌面的出现次数,把计数结果上传给 ARM。HPS 调用快速排序对牌面概率排序,只筛选出概率最高的 5 张牌展示给玩家,为是否要牌提供参考。 软硬件数据流同步

系统采用握手同步方案,避免软件盲目延时等待,提升整体吞吐。卡牌与随机种子数据存放在片上 SRAM 中,并行单元会先把全局卡牌数据并行加载到各自独立的 M10K 块,全部加载完成后再启动批量蒙特卡洛仿真。

国外大学生都用FPGA做什么项目(十八)图28

游戏玩法

图形

本游戏的图形设计针对连接到FPGA的VGA显示器。FPGA的内存映射I/O用于控制VGA显示器(vga_char_ptr允许程序向屏幕写入文本,vga_pixel_ptr允许程序直接绘制形状和颜色)。我们使用了多种绘图函数在VGA屏幕上创建不同的元素:

国外大学生都用FPGA做什么项目(十八)图29

游戏流程

  • 主菜单:游戏开始时,VGA屏幕上会显示主菜单,其中包含游戏标题、简要规则说明以及进入游戏界面的提示。

  • 游戏初始化:游戏初始化时会设置默认界面,并提示用户输入玩家人数。

  • 投注阶段:玩家需要输入投注金额。

  • 发牌和游戏玩法:游戏开始后,玩家和庄家会分别发初始牌;玩家可以选择要牌或停牌,他们的选择会显示在VGA屏幕上并实时更新。

  • 结果显示:显示每位玩家的游戏结果(赢、输、爆牌或平局)及其更新后的筹码余额,以及荷官的成绩。

国外大学生都用FPGA做什么项目(十八)图30

上图展示了游戏主界面。底部是代表筹码、加注/减注选项以及要牌/停牌选项的用户界面元素。空白区域用于放置每位玩家的牌。左侧显示了最有可能出现的下一张牌的点数及其对应的概率(基于模拟结果)。右侧显示游戏正在进行 1200 万次模拟,最低投注额为 3 美元,最高投注额为 1000 美元。它还显示了获胜概率和平局概率(基于模拟结果)。顶部中间区域显示了庄家的牌的摆放位置。

国外大学生都用FPGA做什么项目(十八)图31

这里展示了一个玩家与庄家对战的例子。玩家初始资金为1000美元,下注100美元。开局牌面为3和Q(总点数为13),图中显示了最有可能出现的下一张牌的点数,获胜概率为34.47%,平局概率为4.05%。玩家选择要牌,下一张牌是8(恰好不在最有可能出现的下一张牌的点数范围内)。

国外大学生都用FPGA做什么项目(十八)图32

由于点数总和为21点,接下来所有可能的牌面点数都为0,因为此时胜券在握(庄家没有拿到黑杰克),建议停牌。胜率也因此飙升至91.37%,平局概率为6.96%。

国外大学生都用FPGA做什么项目(十八)图33

现在玩家已经停牌,场上没有其他玩家了,庄家的底牌(梅花3)以及剩余的牌都已亮出。这些牌加起来是17点,由于玩家的点数是21点,所以他赢得了100美元,这在牌上方的黑色方框中显示。

国外大学生都用FPGA做什么项目(十八)图34

这段视频展示了同一用户进行另一轮游戏的画面(余额以第一轮的盈利调整后为+100美元)。视频还演示了平局的情况:玩家停牌前,牌面点数总和为18(如图左侧所示),与庄家牌面点数总和相同(如图右侧所示)。

国外大学生都用FPGA做什么项目(十八)图35

这是用户在游戏过程中输入信息的界面示例。游戏从主菜单界面开始,用户点击“s”键进入游戏界面。然后,系统会提示用户输入玩家人数(1-4人)。屏幕上半部分显示的是单人模拟游戏。结果显示后,系统会询问用户是否要与相同玩家再次游戏、使用新玩家重新开始游戏,或者退出游戏。屏幕下半部分则对应下一个游戏环节,玩家可以在此进行下一轮游戏。

国外大学生都用FPGA做什么项目(十八)图36

这段视频展示了 3 名玩家的游戏过程,庄家爆牌,所以玩家 1(总点数 19)和玩家 3(总点数 17)获胜,但玩家 2(总点数 22)爆牌。

国外大学生都用FPGA做什么项目(十八)图37
国外大学生都用FPGA做什么项目(十八)图38

这段视频展示了四名玩家的游戏过程,其中玩家 4 看起来似乎要赢了,因为他拿到了黑杰克(红桃 10 和方块 A,A 代表 11 点)。然而,由于庄家也拿到了黑杰克,除了玩家 4 和庄家平局之外,其他所有玩家都将自动输掉游戏(不能再要牌)。

结果

国外大学生都用FPGA做什么项目(十八)图39

正如芯片规划器图像所示,FPGA 的主要限制因素是可用的 ALM(应用逻辑模块)。该项目大量依赖寄存器来跟踪所有内部仿真中的临时结果,并在生成的语句中使用更高位宽的寄存器来处理并行模块中运行的多个内部仿真。最终 ALM 的使用率达到了 97%,而额外的并行模块使其使用率接近 103%。另一方面,内存非常充足,尽管支持一个主要的中央片上 SRAM 和每个并行模块的多个重复 M10K 模块以便轻松操作卡值,但该项目仅使用了 34% 的可用内存。

国外大学生都用FPGA做什么项目(十八)图40

上图展示了多次运行的收敛曲线。目标仍然是找出在玩家和庄家牌型相同的情况下,每次模拟的概率值达到多少次后开始收敛到一个稳定值。观察结果表明,在 45 万次到 60 万次模拟之间,所有结果(输、赢和平局)的概率值开始趋于平稳并收敛到一个单一值。如图所示,在上述点之后,概率值基本保持不变,形成一条近乎直线,只有百分比的小数点略有差异。

项目链接

https://people.ece.cornell.edu/land/courses/ece5760/FinalProjects/s2024/hh759_jcs556_ns594/hh759_jcs556_ns594/index.html

代码链接

https://people.ece.cornell.edu/land/courses/ece5760/FinalProjects/s2024/hh759_jcs556_ns594/hh759_jcs556_ns594/BlackJack_Verilog_C.zip

视频链接

https://youtu.be/r3ks_tyS-WY?si=itMd-UVCDGFUDa-O

总结

本文整理了康奈尔大学 2024 年春季 ECE5760 课程基于 CycloneV DE1-SoC 开发板的 2 个 FPGA 实战项目,全部采用 Verilog 开发,具体项目如下:

第一个项目结合机器学习与光线追踪技术,利用 Spotify 音乐特征训练情绪分类模型,对歌曲进行情绪识别,并通过 HPS 将识别结果传递给 FPGA,驱动基于 Ray Marching(光线步进)算法的实时光线追踪 GPU,生成与音乐情绪相匹配的三维可视化效果。项目重点展示了 FPGA 在浮点运算、流水线设计、SDF 场景建模以及实时图形渲染方面的优势,实现了机器学习与硬件加速相结合的音乐可视化系统。

第二个项目则将蒙特卡洛算法应用于二十一点(Blackjack)游戏,通过 FPGA 并行执行大量随机对局模拟,实时计算玩家胜率、平局概率以及下一张牌出现的概率,为玩家提供决策辅助。系统采用 HPS+FPGA 协同架构,利用 FPGA 完成高吞吐量概率计算和 VGA 图形显示,HPS 负责游戏逻辑与数据处理,充分体现了 FPGA 在并行计算、概率仿真和实时交互系统中的高性能优势。

END


往期精选 

国外大学生都用FPGA做什么项目(十八)图41 
国外大学生都用FPGA做什么项目(十八)图42 


国外大学生都用FPGA做什么项目(十八)图43
国外大学生都用FPGA做什么项目(十八)图44

FPGA技术江湖广发江湖帖

无广告纯净模式,给技术交流一片净土,从初学小白到行业精英业界大佬等,从军工领域到民用企业等,从通信、图像处理到人工智能等各个方向应有尽有,QQ微信双选,FPGA技术江湖打造最纯净最专业的技术交流学习平台。


FPGA技术江湖微信交流群

国外大学生都用FPGA做什么项目(十八)图45

加群主微信,备注姓名+公司/学校+岗位/专业进群


FPGA技术江湖QQ交流群

国外大学生都用FPGA做什么项目(十八)图46

备注姓名+公司/学校+岗位/专业进群

国外大学生都用FPGA做什么项目(十八)图47

声明:内容取材于网络,仅代表作者观点,如有内容违规问题,请联系处理。 
FPGA
more
FPGA 还有另一条路?Efinix 正在重新思考“逻辑与布线”的取舍
43年前失败的 Apple Lisa,居然被 FPGA 复活了。
SystemVerilog如何优雅的提升FPGA开发效率
资料汇总|FPGA软件安装包、书籍、源码、技术文档…(2026.02.28更新)
FPGA Vivado调用IP核详细操作步骤
高能开源|“fpgadeveloper”—— FPGA 工程师的代码仓库,学习者的宝藏资源库
FPGA 原型验证中的内存模型应用:基于 DDR5 的 Linux 系统启动与测试
广东FPGA龙头,获数亿元Pre-IPO轮融资
国外大学生都用FPGA做什么项目(十八)
FPGA,焕发新生
Copyright © 2025 成都区角科技有限公司
蜀ICP备2025143415号-1
  
川公网安备51015602001305号