Author Topic: SystemVerilog Example Testbench which Saves a .BMP picture and executes a script  (Read 3078 times)

0 Members and 1 Guest are viewing this topic.

Online BrianHGTopic starter

  • Super Contributor
  • ***
  • Posts: 7638
  • Country: ca
Hi Everyone,

    Once again, a new project for those who are a step above beginner.  The attached .zip SystemVerilog test-bench was tested in Altera ModelSim 10 & 20, but contains no Altera specific code.  It should work in any ModelSim.

     This example .BMP generator and ASCII script file reader can be adapted to test code such as pixel drawing algorithms, picture filters, and make use of a source ascii file to drive the inputs of your .sv DUT module while offering logging of the results, and executing the list of commands in order.

    How to setup:
1.  Unzip files into their own folder.
2.  Open ModelSim.
3.  Select 'File - Change directory', select the directory with the source files.
4.  In the transcript, type 'do setup.do' to setup ModelSim's environment.
5.  In the transcript, type 'do run.do' to re-compile and run the test-bench.

The test-bench source 'ellipse_generator_tb.sv' contains the relevant code example while it drives and responds to the DUT 'ellipse_generator.sv'.

Inside 'ellipse_generator_tb.sv', there are 2 main 'task' calls.
Code: [Select]
execute_ascii_file("source_file_name.txt");                // Executes the command ascii file, searches for each '@' and executes the command string listed right after.
save_bmp_256( "bmp_file_name.bmp" , <bw_nocolor> );        // saves a 256 color BMP picture.


The 'save_bmp_256' requires 2 parameters setup at the beginning of the test-bench, plus one 8 bit 2 dimensional logic array called bitmap.

Code: [Select]
// ***********************************************************************************************************
// ***********************************************************************************************************
// Setup global bitmap size and logic memory array.
//
localparam BMP_WIDTH  = 1024;
localparam BMP_HEIGHT = 1024;
logic [7:0] bitmap [0:BMP_WIDTH-1][0:BMP_HEIGHT-1];
//
// ***********************************************************************************************************
// ***********************************************************************************************************

When writing to the bitmap, use this line #441:

Code: [Select]
if (X_coord>=0 && Y_coord>=0 && X_coord<1024 && Y_coord<1024) bitmap[X_coord][Y_coord] = draw_color;
The 'if' command prevents pixel writes outside the allocated logic array size.

Next, executing an ascii file with a list of commands:

Line #110 calls the 'execute_ascii_file("ellipse_commands_in.txt")' task which opens and reads the .txt file in the quotes.
The command names are executed by the 'case' statement on line #285.

Example command '@DRAW_ELLI' on line #287 calls the draw_ellipse() task:
Line #396 begins the 'task draw_ellipse(integer src, integer dest);' task.
Inside, line #414 reads the 7 decimal parameters & sets the 'ellipse_generator.sv' DUT inputs.

Lines #438-447 waits while the 'ellipse_generator.sv' busy output is high while inside, line #439 waits for the (pixel_data_rdy) before it logs and writes a pixel into the bitmap.  Crucially line #446,  '@(negedge clk);' prevents the writing of infinite entries into the log file the moment the first pixel data is ready.  (Basically, waits 1 clock per acknowledged write.)


Finals:
Line #115 is what keeps the 'clk' line always oscillating.
While lines 120&121 create a dumb watchdog timer which stops the simulation after 15 clocks of no activity from the DUT ellipse_generator module.


Enjoy.
Plus, if anyone gets this project working in other vendor's version of ModelSim other than the free Altera version, please let us know.

(Improved V4.0 located 3 posts below here: Now Supports Active-HDL (Comes with Lattice Diamond) as well as ModelSim (Comes with Quartus) )
« Last Edit: February 15, 2021, 01:26:22 am by BrianHG »
 
The following users thanked this post: nockieboy

Online BrianHGTopic starter

  • Super Contributor
  • ***
  • Posts: 7638
  • Country: ca
A far more advanced full geometry processor simulation can be found here: GPU_GEOMETRY_Testbench_v1.0.zip

https://www.eevblog.com/forum/fpga/fpga-vga-controller-for-8-bit-computer/msg3447916/#msg3447916

(This one is overkill, but, the .bmp save algorithm has been patched and improved as well as a nice full command script features for the source script file.)
 

Online BrianHGTopic starter

  • Super Contributor
  • ***
  • Posts: 7638
  • Country: ca
Here is the patched/updated testbench with an improved .bmp saver and script reader.

Code: [Select]
* V3.0 - Improved save_bmp_256 which opens a file using "wb" -> write binary.
 *      - Improved script processing which supports multiple commands.
 *      - Improved 'STOP' and error handling allows one to continue after a stop by typing 'run -all' in the transcript.
 *      - Improved Ellipse generator with a parameter '.USE_ALTERA_IP()' to select whether Altera's LMP_MULT will be used
 *                 or the default SystemVerilog multiply.

*Note, if you want to see how to insert text comments into the waveform display in your simulation testbenches at key points, go to my link for the full 'GPU_GEOMETRY_Testbench_v1.0.zip' located in the previous post.
 

Online BrianHGTopic starter

  • Super Contributor
  • ***
  • Posts: 7638
  • Country: ca
Here is Version 4, which now supports Active-HDL / (For Lattice Diamond users) as well as ModelSim.

Docs:
Code: [Select]
/*
 * Test Bench ELLIPSE GENERATOR MODULE (ELLIE) for ModelSim and Active-HDL.
 *
 * Features control from a source ascii text file script,
 * and a 256 color .BMP picture file generator.
 * Tested on free Altera ModelSim 10 & 20.  However, it does not contain
 * any Altera specific code.  It should work on other SystemVerilog simulators.
 *
 * Written by Brian Guralnick.
 *
 * v 4.0   Feb 14, 2021
 *
 * V4.0 - Added Active-HDL support.  (Active-HDL comes with Lattice Diamond FPGA Developement Suite.)
 * V3.0 - Improved save_bmp_256 which opens a file using "wb" -> write binary.
 *      - Improved script processing which supports multiple commands.
 *      - Improved 'STOP' and error handling allows one to continue after a stop by typing 'run -all' in the transcript.
 *      - Improved Ellipse generator with a parameter '.USE_ALTERA_IP()' to select whether Altera's LMP_MULT will be used
 *                 or the default SystemVerilog multiply.
 *
 * To setup simulation, Start Modelsim, The goto 'File - Change Directory' and select this files directory.
 * Then in the transcript, type:
 * do setup_ModelSim.do
 *
 * To re-run the simulation, (also does a quick recompile) type:
 * do run.do
 *
 * The text file 'ellipse_commands_in.txt' contains the commands with simulation drawing coordinates.
 * Read the file for instructions.
 *
 * The testbench will run as many commands you list in in the .txt file.
 * After simulation, all the generated ellipse's coordinates will be stored in the log file:
 * ellipse_generated_results.txt
 *
 * as well as full logic waveform in the wave display.
 *
 * An output 1024x1024 bitmap picture will also be generated called:
 * ellipse_generated_bitmap.bmp
 *
 *
 *****************************************************************************
 * For Active-HDL (Comes with Lattice Diamond FPGA developement enviroment.)
 *****************************************************************************
 *
 * Run Active-HDL all by itself.  ( For Lattice users, it's located in your start menu, Lattice Diamond / Accessories / Active-HDL )
 *
 * Go to 'File - New / Design'
 *        Create an empty design.
 *        Choose Verilog for HDL language, ignore 'Target Technology'.
 *        Type in 'ELLIPSE_tb' for design name.
 *        Next/Finish.
 *       
 * Unzip all the files directly into the 'src' directory inside the 'GPU_GEO_tb' folder.
 * In the console, type:
 *
 * do setup_active-hdl.do
 *
 * *** The result 'ellipse_generated_bitmap.bmp' and 'ellipse_generated_results.txt' files generated by
 *     the simulation will be located in the main 'ELLIPSE_tb' folder.
 *
 */

Enjoy.
 

Offline asmi

  • Super Contributor
  • ***
  • Posts: 2728
  • Country: ca
With few minor changes this version works in Vivado Simulator too.
The change is - Vivado Sim doesn't like %u specifier (it outputs text representation of a value instead of actual value), so I had to replace it with four %c in little-endian format. Here is updated code of the task:
Code: [Select]
task save_bmp_256(string bmp_file_name,bit bw_nocolor,integer width, integer height);
begin

    integer unsigned        fout_bmp_pointer, BMP_file_size,BMP_row_size,r;
    logic   unsigned [31:0] BMP_header[0:12];

                            BMP_row_size  = 32'(width) & 32'hFFFC; // When saving a bitmap, the row size/width must be
if ((width & 32'd3) !=0)    BMP_row_size  = BMP_row_size + 4;      // padded to chunks of 4 bytes.

    fout_bmp_pointer= $fopen(bmp_file_name,"wb");
    if (fout_bmp_pointer==0)
    begin
       $display("Could not open file '%s' for writing",bmp_file_name);
       $stop;     
    end
    $display(" *************************************************************** ");
    $display(" ****** Saving bitmap '%s'. ********** ",bmp_file_name);
    $display(" *************************************************************** ");

BMP_file_size    =  (54+1024+(BMP_row_size*height));
BMP_header[0:12] = '{BMP_file_size,0,1078,40,width,height,{16'd8,16'd1},0,(BMP_row_size*height),2835,2835,256,256};

//$fwrite(fout_bmp_pointer,"BM%u",BMP_header); //  Not compatible with Lattice Active_HDL.
$fwrite(fout_bmp_pointer,"BM");
for (int i =0 ; i <13 ; i++ ) $fwrite(fout_bmp_pointer,"%c%c%c%c",BMP_header[i][7 -:8],BMP_header[i][15 -:8],BMP_header[i][23 -:8],BMP_header[i][31 -:8]); // Better compatibility with Lattice Active_HDL.

    //  Save 256 color .bmp palette
    if (!bw_nocolor) for (int i=0 ; i<256 ; i++)  $fwrite(fout_bmp_pointer,"%c%c%c%c",8'({i[3:0],i[3:0]}),8'({i[5:2],i[5:2]}),8'({i[7:4],i[7:4]}),8'h00);// Generate a dummy colorized palette

    // This makes the palette go from color 0=black to 255=100% white.
    else             for (int i=0 ; i<256 ; i++)  $fwrite(fout_bmp_pointer,"%c%c%c%c",8'(i), 8'(i), 8'(i), 8'h00); // Generate a monochrome 256 shade grey palette

    //  Save BMP_WIDTHxBMP_HEIGHT .bmp image.
    for (int y=height-1;y>=0;y--) begin
                                  for (int x=0;x<width;x+=4)  $fwrite(fout_bmp_pointer,"%c%c%c%c",bitmap[x][y], bitmap[x+1][y], bitmap[x+2][y], bitmap[x+3][y]) ;
                                  end

    $fclose(fout_bmp_pointer);
end

endtask
Simulation run produces the image in attachment (I converted it from bmp into png for size reasons).
 
The following users thanked this post: BrianHG

Online BrianHGTopic starter

  • Super Contributor
  • ***
  • Posts: 7638
  • Country: ca
Thanks for the heads up. 

The %c does works ModelSim, but it takes a few seconds to generate the bitmap.  Something like 8 seconds for 1024x1024 & 32 seconds for 2048x2048.

Is there a 'Vivado' compiler string I can test for so I can make a simple 'IF (Vivado==True)' and have the code auto select between using %u VS %c?

It's really only needed for the big final loop where the raw graphic data is being saved...

Is this the same bug for the entire 'GEOMETRY' unit in the other thread?

I'm currently adding support for the HDMI out with auto PLL for Lattice diamond, then I was going to install Xilinx & adapt the GEO/BMP testbenches + HDMI generator.  Might as well have my projects work on all the 3 major platforms automatically.
« Last Edit: February 15, 2021, 08:08:47 am by BrianHG »
 

Offline asmi

  • Super Contributor
  • ***
  • Posts: 2728
  • Country: ca
Is there a 'Vivado' compiler string I can test for so I can make a simple 'IF (Vivado==True)' and have the code auto select between using %u VS %c?
From UG900:
Quote
XILINX_SIMULATOR is a Verilog predefined-macro. The value of this macro is 1. Predefined
macros perform tool-specific functions, or identify which tool to use in a design flow. The
following is an example usage:
`ifdef VCS
// VCS specific code
`endif
`ifdef INCA
// NCSIM specific code
`endif
`ifdef MODEL_TECH
// MODELSIM specific code
`endif
`ifdef XILINX_ISIM
// ISE Simulator (ISim) specific code
`endif
`ifdef XILINX_SIMULATOR
// Vivado Simulator (XSim) specific code
`endif

Is this the same bug for the entire 'GEOMETRY' unit in the other thread?
I don't know yet. It's rather large for me to tackle with attempts to fix.

I'm currently adding support for the HDMI out with auto PLL for Lattice diamond, then I was going to install Xilinx & adapt the GEO/BMP testbenches + HDMI generator.  Might as well have my projects work on all the 3 major platforms automatically.
I would recommend leaving PLL outside of component, as in most architectures they can have more outputs than used by your component and so when they are outside, they can by utilized to generate additional clocks as needed by design. I only include PLL inside component when IO architecture requires very specific way of connecting it to IO logic (for example source-synchronous reception via SERDES requires very specific clocking on 7 series platform using specific clock buffers).
 
The following users thanked this post: BrianHG

Online BrianHGTopic starter

  • Super Contributor
  • ***
  • Posts: 7638
  • Country: ca
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf