VHDL is not good with binary files. I am considering using SystemVerilog for this and thinking how it could read bitmap file (image format .bmp) and process it. This shall be used to create a model to compare the RTL code against.
Many years ago I did something like this in C++:
struct BMPFileHeader {
uint16_t file_type{0x4D42}; // File type always BM which is 0x4D42
uint32_t file_size{0}; // Size of the file (in bytes)
uint16_t reserved1{0}; // Reserved, always 0
uint16_t reserved2{0}; // Reserved, always 0
uint32_t offset_data{0}; // Start position of pixel data (bytes from the beginning of the file)
};
Then I had to apply pragma to ensure that the struct was packed:
#pragma pack(push, 1)
// ... your packed structures
#pragma pack(pop)
I could then read the file headers into the packed struct using something like this:
void read(const char *fname) {
std::ifstream inp{ fname, std::ios_base::binary };
if (inp) {
inp.read((char*)&file_header, sizeof(file_header));
if(file_header.file_type != 0x4D42) {
throw std::runtime_error("Error! Unrecognized file format.");
}
inp.read((char*)&bmp_info_header, sizeof(bmp_info_header));
...
Is the same possible with SystemVerilog as well for reading and writing binary files?
I was able to write a basic program that opens bitmap and stores the data into a dynamic array. Here it is:
module tb_bmp;
// 14 bytes of file header
typedef struct packed {
shortint unsigned file_type; // File type always BM (0x4D42)
int unsigned file_size; // Size of the file in bytes
shortint unsigned reserved1; // Reserved, always 0
shortint unsigned reserved2; // Reserved, always 0
int unsigned offset_data; // Offset where the pixel data starts
} BMPFileHeader;
// 40 bytes of info header
typedef struct packed {
int unsigned header_size; // Size of the header
int signed width; // Width of the image
int signed height; // Height of the image
shortint unsigned color_planes; // Number of color planes, must be 1
shortint unsigned bits_per_pixel; // Number of bits per pixel
int unsigned compression_method; // Compression type (0 = uncompressed)
int unsigned image_size; // Size of the raw bitmap data
int signed x_pixels_per_meter; // Horizontal resolution of the image. (pixel per metre, signed integer)
int signed y_pixels_per_meter; // Vertical resolution of the image. (pixel per metre, signed integer)
int unsigned colors_used; // Number of colors used
int unsigned important_colors; // Number of important colors
} BMPInfoHeader;
typedef struct packed {
byte b; // Red component
byte g; // Green component
byte r; // Blue component
} pixel_t;
// File variables
int file;
int result;
int img_h;
int img_w;
int img_h_8;
int img_w_8;
int offset;
int pixel;
int ar_index;
BMPFileHeader file_header;
BMPInfoHeader info_header;
pixel_t pixel_data;
pixel_t pixel_data_ar[];
initial begin
// Open the BMP file in binary read mode
file = $fopen("image.bmp", "rb");
if (file == 0) begin
$display("Error: Cannot open file.");
$finish;
end
else begin
$display("BMP opened successfully");
end
// Read BMPFileHeader (14 bytes)
$fread(file_header, file);
$display("%x", file_header);
if (file_header.file_type != 16'h424D) begin
$display("Error: Not a BMP file.");
$fclose(file);
$stop;
end
// Read BMPInfoHeader (40 bytes)
$fread(info_header, file);
$display("%x", info_header);
img_h = {<<byte {info_header.width}};
img_w = {<<byte {info_header.height}};
offset = {<<byte {file_header.offset_data}};
img_h_8 = img_h + (img_h % 8);
img_w_8 = img_w + (img_w % 8);
// Print the bitmap file while converting the big endian data to little endian
$display(" info header : %0d", {<<byte {info_header.header_size}});
$display(" BMP Width : %0d", img_w);
$display(" BMP Height : %0d", img_h);
$display("Bits per pixel : %0d", {<<byte {info_header.bits_per_pixel}});
$display(" Data offset : %0d", {<<byte {file_header.offset_data}});
// Move file pointer to start of pixel data to ensure all headers are skipped
result = $fseek(file, offset, 0);
$display($ftell(file));
if (result != 0) begin
$display("Error: fseek failed.");
$stop;
end
// Allocate the outer dynamic array for rows (height)
pixel_data_ar = new[img_h_8*img_w_8]; // Allocate for the number of rows
// read the image data and store it upside down in the pixel data array
$display("\nread file pixel data \n");
for (int h = 0; h < img_h; h++) begin
for (int w = 0; w < img_w; w++) begin
// for (int i = 0; i < 10; i++) begin
$fread(pixel_data, file);
ar_index = (img_h-1-h)*img_h + w;
pixel_data_ar[ar_index].r = pixel_data.r;
pixel_data_ar[ar_index].g = pixel_data.g;
pixel_data_ar[ar_index].b = pixel_data.b;
$display("RGB [%u,%u] %u, %u, %u", w, h, pixel_data.r, pixel_data.g, pixel_data.b);
end
end
// print the accumulated pixel data into the terminal
$display("\nread extracted pixel data \n");
for (int h = 0; h < img_h; h++) begin
for (int w = 0; w < img_w; w++) begin
ar_index = h*img_h + w;
pixel_data.r = pixel_data_ar[ar_index].r;
pixel_data.g = pixel_data_ar[ar_index].g;
pixel_data.b = pixel_data_ar[ar_index].b;
$display("RGB [%u,%u] %u, %u, %u", w, h, pixel_data.r, pixel_data.g, pixel_data.b);
end
end
// Close the file
$fclose(file);
end
endmodule
I will eventually store the pixel data into 8x8 blocks. This is why there are two variables that round the width and height to the nearest 8.