使用 HDL Coder 将 MATLAB 转换为 FPGA

如果您正在使用 MATLAB 建模数字信号处理(DSP)或者视频和图像处理算法,并且最终将其用于 FPGA 或 ASIC,本文可能将为你带来帮助。

从 MATLAB 生成 HDL 代码
FPGA 在通用处理器(GPP)和专用集成电路(ASIC)之间提供了很好的折中方案。GPP 是完全可编程的,但在功率和性能方面效率较低;ASIC 可实现专用的功能,并展现出最佳的功率和性能特性,但需要极其昂贵的设计验证和实现周期。FPGA 也用于 ASIC 工作流中的原型设计,以进行硬件验证和早期软件开发。

由于在运行高吞吐量、高性能的应用程序时,性能有了大幅度的提高,算法设计者越来越多地使用 FPGA 而不是传统的处理器来原型化和验证创新。然而,由于 MATLAB 简单易用的编程模型和丰富的分析和可视化能力,许多算法都是在其中实现的。当针对 FPGA 或 ASIC 时,这些 MATLAB 算法必须手动转换为 HDL。

对于许多精通软件编程的算法开发人员来说,掌握 FPGA 设计工作流是一项挑战。与软件算法开发不同,硬件开发要求并行思想。存在的其他障碍包括:学习 VHDL 或 Verilog 语言,掌握 FPGA 供应商的 IDE,理解深奥的术语,如“多周期路径”和“延迟平衡”等。

在这篇文章中,我将介绍一个从 MATLAB 转换为 FPGA 的简单路径。我将展示如何从 MATLAB 算法自动生成 HDL 代码,在 FPGA 上实现 HDL 代码,并使用 MATLAB 来验证您的 HDL 代码。

MATLAB 到硬件工作流
将 MATLAB 设计转换为硬件的过程包括以下步骤:
1. 在 MATLAB 中建模您的算法——使用 MATLAB 来模拟、调试、迭代测试并优化设计。
2. 生成 HDL 代码——自动创建用于 FPGA 原型的 HDL 代码。
3. 验证 HDL 代码——重用您的 MATLAB test bench 来验证生成的 HDL 代码。
4. 创建和验证 FPGA 原型——在 FPGA 上实现和验证您的设计。

将 MATLAB 转换为硬件存在一些独特的挑战。MATLAB 代码是过程性的,可以高度抽象;它可以使用浮点数据,并且没有时间概念。复杂的循环可以从矩阵运算和工具箱函数推断出来。

在硬件中实现 MATLAB 代码包含以下操作:

  • 将浮点 MATLAB 代码转换为具有优化位宽的定点 MATLAB 代码,以实现高效的硬件生成。
  • 识别程序结构并将其映射到并发的经过面积和速度优化的硬件操作上。
  • 通过添加时钟和时钟率来调度硬件中的操作,引入时间的概念。
  • 创建资源共享架构来实现昂贵的操作符,如乘数和 for 循环体。
  • 将大型持久化数组映射到硬件中的块RAM
  • HDL Coder™ 可通过工作流自动化简化上述任务。

    MATLAB 算法示例
    让我们用MATLAB函数来实现直方图均衡化并完成此工作流。该算法在 MATLAB 中实现,通过变换灰度像中的值来增强图像对比度,使输出图像的直方图近似平坦。
    type mlhdlc_heq.m

    % Histogram Equalization Algorithm
    function [pixel_out] = mlhdlc_heq(x_in, y_in, pixel_in, width, height)

    persistent histogram
    persistent transferFunc
    persistent histInd
    persistent cumSum

    if isempty(histogram)
    histogram = zeros(1, 2^8);
    transferFunc = zeros(1, 2^8);
    histInd = 0;
    cumSum = 0;
    end

    % Figure out indices based on where we are in the frame
    if y_in < height && x_in < width % valid pixel data
    histInd = pixel_in + 1;
    elseif y_in == height && x_in == 0 % first column of height+1
    histInd = 1;
    elseif y_in >= height % vertical blanking period
    histInd = min(histInd + 1, 2^8);
    elseif y_in < height % horizontal blanking - do nothing
    histInd = 1;
    end

    %Read histogram
    histValRead = histogram(histInd);

    %Read transfer function
    transValRead = transferFunc(histInd);

    %If valid part of frame add one to pixel bin and keep transfer func val
    if y_in < height && x_in < width
    histValWrite = histValRead + 1; %Add pixel to bin
    transValWrite = transValRead; %Write back same value
    cumSum = 0;
    elseif y_in >= height %In blanking time index through all bins and reset to zero
    histValWrite = 0;
    transValWrite = cumSum + histValRead;
    cumSum = transValWrite;
    else
    histValWrite = histValRead;
    transValWrite = transValRead;
    end

    %Write histogram
    histogram(histInd) = histValWrite;

    %Write transfer function
    transferFunc(histInd) = transValWrite;

    pixel_out = transValRead;

    MATLAB Test Bench 示例
    下面是一个 test bench,用于验证算法是否对示例图像起作用。(注意,此 testbench 使用 Image Processing Toolbox 的内置函数来读取原始图像,并在均衡后绘制转换的图像。)
    type mlhdlc_heq_tb.m

    %% Test bench for Histogram Equalization Algorithm
    clear mlhdlc_heq;
    testFile = 'office.png';
    RGB = imread(testFile);

    % Get intensity part of color image
    YCBCR = rgb2ycbcr(RGB);
    imgOrig = YCBCR(:,:,1);

    [height, width] = size(imgOrig);
    imgOut = zeros(height,width);
    hBlank = 20;
    % make sure we have enough vertical blanking to filter the histogram
    vBlank = ceil(2^14/(width+hBlank));

    for frame = 1:2
    disp(['working on frame: ', num2str(frame)]);
    for y_in = 0:height+vBlank-1
    %disp(['frame: ', num2str(frame), ' of 2, row: ', num2str(y_in)]);
    for x_in = 0:width+hBlank-1
    if x_in < width && y_in < height
    pixel_in = double(imgOrig(y_in+1, x_in+1));
    else
    pixel_in = 0;
    end

    [pixel_out] = mlhdlc_heq(x_in, y_in, pixel_in, width, height);

    if x_in < width && y_in < height
    imgOut(y_in+1,x_in+1) = pixel_out;
    end
    end
    end
    end

    % Make color image from equalized intensity image
    % Rescale image
    imgOut = double(imgOut);
    imgOut(:) = imgOut/max(imgOut(:));
    imgOut = uint8(imgOut*255);

    YCBCR(:,:,1) = imgOut;
    RGBOut = ycbcr2rgb(YCBCR);

    figure(1)
    subplot(2,2,1); imshow(RGB, []);
    title('Original Image');
    subplot(2,2,2); imshow(RGBOut, []);
    title('Equalized Image');
    subplot(2,2,3); hist(double(imgOrig(:)),2^14-1);
    title('Histogram of original Image');
    subplot(2,2,4); hist(double(imgOut(:)),2^14-1);
    title('Histogram of equalized Image');

    我们来仿真一下此算法,看看结果。

    mlhdlc_heq_tb

    HDL Workflow Advisor
    HDL Workflow Advisor(请参见下面的快照)有助于自动执行步骤,并提供从MATLAB到硬件的引导。您可以在 Workflow Advisor 的左窗格中看到工作流的以下关键步骤:
    1. 定点转换
    2. HDL 代码生成
    3. HDL 验证
    4. HDL 综合与分析

    我们来详细看看工作流中的每个步骤。

    定点转换
    信号处理应用程序通常使用 MATLAB 中的浮点运算来实现。但是,出于功耗、成本和性能的原因,在面向硬件时,需要将这些算法转换为使用定点运算。定点转换非常具有挑战性并且非常耗时,通常需要占用整个设计和实施时间的 25% 到 50%。HDL Coder™ 中用于浮点到定点自动转换的工作流可以极大地简化和加速转换过程。

    浮点到定点转换工作流包括以下步骤:
    1. 验证浮点设计与代码生成兼容。
    2. 根据计算范围,通过模拟 test bench 或通过静态分析(将传播设计范围以计算所有变量的派生范围)提出定点类型。
    3. 通过应用建议的定点类型生成定点 MATLAB 代码。
    4. 验证生成的定点代码,并将生成的定点代码的数值精度与原始浮点代码进行比较。

    请注意,此步骤是可选的。如果您的 MATLAB 设计已在定点实现,则可以跳过此步骤。

    HDL 代码生成
    HDL 代码生成步骤通过定点 MATLAB 代码生成 HDL 代码。您可以生成实现 MATLAB 设计的 VHDL 或 Verilog 代码。除了生成可综合的HDL代码外,HDL Coder™ 还可生成各种报告,包括可帮助您在 MATLAB 代码和生成的 HDL 代码之间导航的可跟踪报告,以及在算法级别显示实现设计所需硬件资源(加法器、乘法器和 RAM)的资源利用率报告。

    在代码生成期间,您可以指定各种优化选项来探索设计空间,而无需修改算法。在下面的“设计空间探索和优化选项”部分中,您可以看到如何修改代码生成选项以及如何针对速度或面积来优化设计。

    HDL 验证
    独立 HDL test bench 的生成:
    HDL Coder™ 可通过您的 MATLAB 脚本生成 VHDL 和 Verilog test bench,以快速验证生成的 HDL 代码。您可以使用将激励应用于 HDL 代码的多个选项来自定义 HDL test bench。您还可以生成脚本文件,以自动执行在 HDL 模拟器中编译和模拟代码的过程。这些步骤有助于确保 MATLAB 仿真的结果与 HDL 仿真的结果相匹配。

    HDL Coder™ 还与 HDL Verifier 一起使用,以自动生成两种类型的协同仿真 test bench:

  • 基于 HDL 协同仿真的验证可与 MentorGraphics® ModelSim® 和 QuestaSim® 配合使用,其中 MATLAB 和 HDL 仿真可前后相接。
  • FPGA 在环仿真允许您与 FPGA 电路板严格同步地运行 MATLAB 仿真。您可以使用 MATLAB 将实际的数据输入到 FPGA 的设计中,并确保该算法在硬件中实现时能够达到预期的性能。
  • HDL 综合
    除了与语言相关的挑战外,FPGA 编程还需要使用复杂的 EDA 工具。从 HDL 设计中生成比特流并对 FPGA 进行编程可能是一项艰巨的任务。HDL Coder™ 通过为 Xilinx® 和 Altera® 创建用生成的HDL代码配置的项目文件,可在此处提供自动化。您可以使用工作流步骤在MATLAB 环境中综合 HDL 代码,查看综合结果,并迭代 MATLAB 设计以改善综合结果。

    设计空间探索和优化选项
    HDL Coder™ 提供以下优化,以帮助您探索如何在设计空间的面积和速度之间进行权衡。您可以使用这些选项来探索各种架构并进行各种权衡,而不必手动重写算法。

    速度优化

  • 流水线:为了提高设计的时钟频率,HDL Coder 使您可以在设计中的各个位置插入流水线寄存器。例如,您可以在设计的输入和输出处以及在算法中给定 MATLAB 变量的输出处插入寄存器。
  • 分布式流水线:HDL Coder 还提供了基于重定时的优化,可通过设计中的组合路径将延迟降到最小,以此自动移动插入的流水线寄存器,从而最大化时钟频率。
  • 面积优化

  • RAM 映射:HDL Coder™ 将矩阵映射到硬件中的导线或寄存器。如果将持久性矩阵变量映射到寄存器,则它们会占用大量的FPGA面积。HDL Coder™会自动将持久性矩阵映射到块RAM,以提高面积效率。将MATLAB矩阵映射到块RAM的挑战在于,硬件中的块RAM通常具有一组有限的读写端口。HDL Coder™ 通过自动划分和调度矩阵读写来满足块 RAM 的端口限制,同时仍然遵循设计中的其他控制依赖性和数据依赖性来解决此问题。
  • 资源共享:此优化可以识别并共享 MATLAB 代码中具有等效功能的乘法器操作。您可以控制设计中的乘数器共享量。
  • 循环流:MATLAB 的 for 循环可在 VHDL 中创建一个 FOR_GENERATE 循环。循环主体在硬件中的复制次数与循环迭代次数相同。这会导致面积使用的低效。循环流优化会创建循环体的单个硬件实例,该实例在循环迭代之间进行时间多路复用。
  • 常数乘法器优化:此设计级别的优化使用正则有符号数(CSD)技术将常数乘法器转换为移位和加法运算。
  • 最佳实践方法
    现在,我们来看几个在面向 FPGA 来编写 MATLAB 代码时的最佳实践方法。

    编写 MATLAB 设计时:

  • 使用支持 HDL 代码生成的 MATLAB 代码生成子集。
  • 使顶层接口尽可能简单。顶层功能的大小、类型和复杂性决定了在硬件中实现的芯片接口。
  • 不要将大量并行数据传递到设计中。并行数据需要芯片上的大量 IO 引脚,并且可能无法综合。在典型的图像处理设计中,您应该将像素输入串行化,并在算法内部缓冲它们。
  • 编写 MATLAB test bench 时:

  • 从 test bench 函数调用设计。
  • 彻底执行设计。这对于浮点到定点转换尤为重要,其中 HDL Coder™ 会根据 test bench 分配给变量的值来确定算法中变量的范围。您可以重复使用此测试工作台来生成 HDL test bench,以测试所生成的硬件。
  • 在代码生成之前用 test bench 对设计进行仿真,以确保没有仿真错误,并确保所有必需的文件都在路径中。
  • 结论
    HDL Coder™ 提供了在 FPGA 中实现算法的无缝工作流。在本文中,我向您展示了如何采用 MATLAB 图像处理算法、将其转换为定点、生成 HDL 代码、使用 test bench 验证生成的 HDL 代码,以及最终综合设计并在硬件中实现它。

    通过本文对 HDL Coder™ 以及 MATLAB 转 HDL 代码生成和验证框架的简要介绍,我们希望能助您了解如何开始快速实施 MATLAB 设计和目标 FPGA。

    文章转载自:MATLAB微信公众号

    最新文章