如何使用SystemVerilog读取并处理BMP位图文件以构建RTL代码比对模型
如何使用SystemVerilog读取并处理BMP位图文件以构建RTL代码比对模型
我太懂你为啥不想用VHDL搞二进制文件处理了——这玩意儿确实不太顺手,转用SystemVerilog来做BMP文件的读取和处理,用来搭建RTL比对模型,绝对是个靠谱的思路!毕竟SystemVerilog在验证侧的文件操作和结构化数据处理上灵活多了,刚好可以参考你之前用C++实现的那套逻辑来搬过来。
一、先对应C++的打包结构体,在SystemVerilog里定义BMP头
你之前C++里用#pragma pack来保证结构体紧凑对齐,SystemVerilog里直接用packed struct就能实现同样的效果,不用搞编译器指令这么麻烦。比如BMP的文件头和信息头可以这么定义:
typedef struct packed { bit [15:0] file_type; // 固定为0x4D42(BM) bit [31:0] file_size; // 文件总字节数 bit [15:0] reserved1; // 保留位,固定0 bit [15:0] reserved2; // 保留位,固定0 bit [31:0] offset_data; // 像素数据在文件中的起始偏移量 } BMPFileHeader; typedef struct packed { bit [31:0] size; // 本结构体的大小(一般是40字节) bit [31:0] width; // 图像宽度(像素数) bit [31:0] height; // 图像高度(像素数,负数表示顶行在前) bit [15:0] planes; // 色彩平面数,固定1 bit [15:0] bits_per_pixel; // 每个像素的位数(比如24位RGB) bit [31:0] compression; // 压缩方式,0表示无压缩 bit [31:0] image_size; // 像素数据的字节数(无压缩时可设0) bit [31:0] x_pixels_per_m; // 水平分辨率(像素/米) bit [31:0] y_pixels_per_m; // 垂直分辨率(像素/米) bit [31:0] colors_used; // 实际使用的颜色数,0表示用满所有可能 bit [31:0] colors_important;// 重要颜色数,0表示全部重要 } BMPInfoHeader;
这里的packed struct会自动按位紧凑排列,和你C++里用#pragma pack(push,1)的效果完全一致,不用担心字节对齐的问题。
二、SystemVerilog中实现BMP文件的读取逻辑
接下来就是读取文件了,SystemVerilog的$fopen、$fread这些系统任务可以搞定。和你C++里的std::ifstream用法类似,我们可以写个任务来读取整个BMP文件的头信息和像素数据:
class BMPProcessor; BMPFileHeader file_hdr; BMPInfoHeader info_hdr; bit [7:0] pixel_data[]; // 用来存读取到的像素原始数据 task read_bmp(string fname); int fd; int bytes_read; // 打开文件,二进制模式 fd = $fopen(fname, "rb"); if (fd == 0) begin $error("Failed to open BMP file: %s", fname); return; end // 读取文件头 bytes_read = $fread(file_hdr, fd); if (bytes_read != $bits(file_hdr)/8) begin $error("Failed to read BMP file header"); $fclose(fd); return; end // 校验文件类型是否为BM if (file_hdr.file_type != 16'h4D42) begin $error("Not a valid BMP file (file type is not BM)"); $fclose(fd); return; end // 读取信息头 bytes_read = $fread(info_hdr, fd); if (bytes_read != $bits(info_hdr)/8) begin $error("Failed to read BMP info header"); $fclose(fd); return; end // 定位到像素数据起始位置 $fseek(fd, file_hdr.offset_data, 0); // 0表示从文件开头偏移 // 计算像素数据的总字节数,若info_hdr.image_size为0则自行计算 int pixel_size = (info_hdr.image_size == 0) ? (((info_hdr.width * info_hdr.bits_per_pixel + 31) / 32) * 4) * info_hdr.height : info_hdr.image_size; // 动态分配像素数据数组 pixel_data = new[pixel_size]; // 读取像素数据 bytes_read = $fread(pixel_data, fd); if (bytes_read != pixel_size) begin $warning("Pixel data read incomplete: expected %0d bytes, got %0d", pixel_size, bytes_read); end // 关闭文件 $fclose(fd); endtask // 这里可以加处理像素数据的任务,比如转成RGB分量、做裁剪/缩放等 task process_pixels(); // 示例:打印图像的基本信息 $display("BMP Image Info:"); $display(" Width: %0d pixels", info_hdr.width); $display(" Height: %0d pixels", info_hdr.height); $display(" Bits per pixel: %0d", info_hdr.bits_per_pixel); $display(" Pixel data size: %0d bytes", pixel_data.size()); // 这里可以根据你的RTL功能需求,对pixel_data做进一步处理 // 比如如果RTL是做RGB转灰度,就可以在这里计算灰度值,之后和RTL输出比对 endtask endclass
三、在RTL比对模型中使用这个BMP处理器
最后,你可以在你的验证环境(比如testbench)里实例化这个类,读取BMP文件后处理数据,再把处理后的数据和RTL的输出做比对:
module tb_bmp_compare; // 这里例化你的RTL模块 // rtl_image_processor u_dut(...); BMPProcessor bmp_proc; initial begin bmp_proc = new(); // 读取BMP文件 bmp_proc.read_bmp("test_image.bmp"); // 处理像素数据 bmp_proc.process_pixels(); // 接下来就是把处理后的像素数据(或者原始数据)发给RTL模块 // 然后采集RTL的输出,和bmp_proc中预期的处理结果做比对 // 比如用$assert或者自定义的比对任务来验证 end endmodule
一些需要注意的细节
- 注意BMP文件的像素数据是行优先存储,而且很多BMP的行字节数会对齐到4字节,所以在解析像素的时候要考虑行填充字节的问题(上面的代码里计算pixel_size已经考虑了这一点)
- 如果是24位RGB的BMP,每个像素是3字节,顺序是BGR(蓝、绿、红),不是你直觉里的RGB,这个要注意,处理的时候要转过来
- SystemVerilog的动态数组
pixel_data可以灵活应对不同大小的BMP文件,不用提前固定大小
备注:内容来源于stack exchange,提问作者gyuunyuu
相关产品推荐
相关产品推荐

