目录
1. 简介
2. 构建工程
2.1 Vivado 工程
2.2 TestBench 代码
2.3 关键代码分析
3. VPG Background Pattern ID (0x0020) Register
4. 总结
1. 简介
使用 SystemVerilog 将 AXI4-Stream 图像数据从仿真输出到图像文件 (PPM)。
用到的函数包括 $fopen、$fwrite 和 $fclose。
PPM 格式
PPM (Portable Pixmap Format) 是一种简单的图像文件格式,用于存储彩色图像。PPM 格式很简单,主要用于科研或某些需要图像处理但不需要复杂图像格式的场合。PPM 文件通常较大,因为它们不使用压缩。一个 PPM 文件由一个文件头和图像数据体组成。文件头指定了图像的宽度、高度和最大颜色值,而数据体则包含按顺序排列的像素颜色值,每个像素颜色通常由红、绿、蓝三个颜色分量组成。
2. 构建工程
2.1 Vivado 工程
2.2 TestBench 代码
`timescale 1ns / 1ps import axi_vip_pkg::*; import design_1_axi_vip_0_0_pkg::*; module tb_tpg(); bit aclk = 0, aresetn = 0; xil_axi_resp_t resp; bit tpg_tready = 1, tpg_tuser, tpg_tvalid, tpg_tlast; bit [23:0] tpg_tdata; integer counter_width = 0, counter_height = 0; integer final_width = 0, final_height = 0; integer output_file; integer img_writing = 1, img_start = 0; parameter integer tpg_base_address = 12'h000; parameter integer TPG_CONTROL_REG = tpg_base_address; parameter integer TPG_ACTIVE_H_REG = tpg_base_address + 8'h10; parameter integer TPG_ACTIVE_W_REG = tpg_base_address + 8'h18; parameter integer TPG_BG_PATTERN_REG = tpg_base_address + 8'h20; integer height=400, width=640; integer pattern_id = 8'h09; always #10ns aclk = ~aclk; design_1_wrapper UUT ( .aclk_50MHz (aclk ), .aresetn_0 (aresetn ), .tpg_tdata (tpg_tdata ), .tpg_tlast (tpg_tlast ), .tpg_tready (tpg_tready), .tpg_tuser (tpg_tuser ), .tpg_tvalid (tpg_tvalid)); initial begin #350ns aresetn = 1; @(posedge tpg_tuser); // Start of the first frame @(posedge tpg_tuser); // Start of the second frame, stop the simulation wait (tpg_tuser == 1'b0); #20ns; if((final_height == height)&&(final_height == height)) $display("Resolution match, test succeeded"); else $display("Resolution do not match, test failed"); $finish; end design_1_axi_vip_0_0_mst_t master_agent; initial begin master_agent = new("master vip agent",UUT.design_1_i.axi_vip_0.inst.IF); master_agent.start_master(); wait (aresetn == 1'b1); #200ns master_agent.AXI4LITE_WRITE_BURST(TPG_ACTIVE_H_REG, 0, height, resp); master_agent.AXI4LITE_WRITE_BURST(TPG_ACTIVE_W_REG, 0, width, resp); master_agent.AXI4LITE_WRITE_BURST(TPG_BG_PATTERN_REG, 0, pattern_id, resp); #200ns master_agent.AXI4LITE_WRITE_BURST(TPG_CONTROL_REG, 0, 8'h81, resp); end always @(posedge aclk) begin if((tpg_tvalid == 1) && (tpg_tready == 1)) begin if(tpg_tlast == 1) begin final_width = counter_width + 1; counter_width = 0; end else counter_width = counter_width + 1; end end always @(posedge aclk) begin if((tpg_tvalid == 1) && (tpg_tready == 1)) begin if(tpg_tuser == 1) begin final_height = counter_height; counter_height = 0; end else if(tpg_tlast == 1) counter_height = counter_height + 1; end end initial begin output_file = $fopen("image_out_1.ppm", "w"); $fwrite(output_file, "P3\n"); $fwrite(output_file, "%0d %0d\n", width, height); $fwrite(output_file, "%0d\n", 2**8-1); while(img_writing == 1) begin @(posedge aclk) #1ns; if ((tpg_tvalid == 1) && (tpg_tready == 1)) begin if((tpg_tuser == 1) && (img_start == 1)) img_writing = 0; else begin if(tpg_tuser == 1) img_start = 1; $fwrite(output_file, "%0d\n%0d\n%0d\n", int'(tpg_tdata[23:16]), int'(tpg_tdata[7:0]), int'(tpg_tdata[15:8])); end end end $fclose(output_file); $display("Image written"); end endmodule2.3 关键代码分析
initial begin output_file = $fopen("image_out_1.ppm", "w"); $fwrite(output_file, "P3\n"); $fwrite(output_file, "%0d %0d\n", width, height); $fwrite(output_file, "%0d\n", 2**8-1); while(img_writing == 1) begin @(posedge aclk); #1ns; if ((tpg_tvalid == 1) && (tpg_tready == 1)) begin if((tpg_tuser == 1) && (img_start == 1)) img_writing = 0; else begin if(tpg_tuser == 1) img_start = 1; $fwrite(output_file, "%0d\n%0d\n%0d\n", int'(tpg_tdata[23:16]), int'(tpg_tdata[ 7: 0]), int'(tpg_tdata[15:8])); end end end $fclose(output_file); $display("Image written"); end文件创建与头信息写入:
- 使用 $fopen 函数打开(或创建)一个新的文件image_out_1.ppm,用于写入模式("w")。
- 利用 $fwrite 函数向文件写入PPM图像的头信息:
- "P3\n":PPM文件的格式标识,表示该文件是一个ASCII编码的彩色PPM图像。
- "%0d %0d\n":接下来写入图像的宽度(width)和高度(height),这两个数值应该在代码的其他部分定义。
- "%0d\n":写入颜色的最大值,这里是2**8-1,即255,表示每个颜色通道(红、绿、蓝)的最大值。
图像数据写入:
- 代码进入一个while循环,循环条件是 img_writing 等于1,这意味着将在满足某些条件时写入图像数据。
- 在每个时钟周期的上升沿(@(posedge aclk)),并在等待1纳秒(#1ns;)后,检查tpg_tvalid和tpg_tready信号。只有当这两个信号都为1时,才会执行数据写入的逻辑。
- 如果tpg_tuser信号为1且img_start也为1,这表示图像数据的结束,将img_writing设置为0,退出循环。
- 如果仅tpg_tuser为1,这意味着图像数据的开始,设置img_start为1。
- 在其他情况下,使用$fwrite函数将图像数据(tpg_tdata)写入文件。这里的数据被分解为红、蓝、绿三个颜色通道,并按顺序写入文件。
文件关闭与信息显示:
- 使用$fclose函数关闭文件。
- 通过$display函数在仿真控制台显示“Image written”信息,表示图像数据已成功写入文件。
if ((tpg_tvalid == 1) && (tpg_tready == 1)) begin if((tpg_tuser == 1) && (img_start == 1)) img_writing = 0; // 第二帧开始后,img_writing标记为0 else begin if(tpg_tuser == 1) img_start = 1; // 第一帧开始,img_start标记为1 $fwrite(output_file, "%0d\n%0d\n%0d\n", xxx); // 第一、二帧之间,逐个像素写入 end end3. VPG Background Pattern ID (0x0020) Register
背景模式 ID 寄存器控制 TPG Core 生成的模式操作。
该寄存器根据以下值控制核心输出的模式:
- 0x00 - 直接将视频输入传递到视频输出
- 0x1 - 水平坡道,每个分量(RGB或Y)水平增加1
- 0x2 - 垂直坡道,每个分量(RGB或Y)垂直增加1
- 0x3 - 时间坡道,根据运动速度寄存器设置的值,每帧逐像素增加
- 0x4 - 纯红色输出
- 0x5 - 纯绿色输出
- 0x6 - 纯蓝色输出
- 0x7 - 纯黑色输出
- 0x8 - 纯白色输出
- 0x9 - 色条
- 0xA - 区域板输出产生一个基于ROM的正弦模式。此选项依赖于运动速度、zplate水平起点、zplate水平增量、zplate垂直起点和zplate垂直增量寄存器。
- 0xB - 方格色条
- 0xC - 绘制十字交叉线模式
- 0xD - 色彩扫描模式
- 0xE - 组合的垂直和水平坡道
- 0xF - 黑白棋盘
- 0x10 - 伪随机模式
- 0x11 - DisplayPort颜色坡道
- 0x12 - DisplayPort黑白垂直线
- 0x13 - DisplayPort彩色方块
4. 总结
本文介绍了如何使用 SystemVerilog 将 AXI4-Stream 图像数据输出到 PPM 格式的图像文件中。通过使用 $fopen、$fwrite 和 $fclose 函数,成功地将仿真生成的图像数据写入到文件中,实现了图像数据的输出。在 TestBench 代码中,设置了图像的分辨率和颜色模式,并通过循环逐像素地写入数据。通过这种方式,我们可以将仿真生成的图像数据保存为 PPM 格式的文件,方便后续的图像处理和分析。这种方法可以帮助开发人员在仿真过程中方便地将图像数据输出到文件中,以便进行后续的验证和分析工作。