Behavioral RTL Simulation with MicroBlaze

Neelam Sharma
8 min readJul 4, 2020

Don’t have an FPGA board? Still want to verify your complete system on Vivado using SDK. MicroBlaze comes to your rescue. In this post, I will be covering how to create an HLS IP and use .elf file of your SDK code to run Behavioral RTL Simulation on Vivado. MicroBlaze, being a soft-core processor, it is possible to do the entire system simulation using a .elf file.

What is a soft-core processor?

Soft-core processors are processors which are implemented using FPGA resources like LUT, DSP blocks etc when synthesis and implementation are being done.

Link to all the codes used for this post and a video on similar topics made by my friend can be found at the end of this post.

The topics that I will be covering in this post are:

  1. Design of an IP in HLS
  2. Making block diagram of hardware in Vivado using MicroBlaze
  3. Exporting hardware and launching SDK to create .elf file
  4. Associating .elf file for Behavioral simulation.

Software Used: Vivado WebPACK 2019.1

FPGA family: Artix-7

Xilinx Part no: XC7A200T-1SBG484C

Design of an IP in HLS

I have created a simple IP that takes the address of a memory location as an interface and sums up the values of nine elements (assuming value on memory locations are of float type). This IP then returns the summed value using an AXI slave interface.

HLS code for core.cpp

HLS project can be build using commands:

Type the following commands on Vivado HLS Command prompt.

vivado_hls -f script.tcl
vivado_hls -p Array_sum

Array_sum in the second command is the name of the project which is mentioned in script.tcl

Making block diagram of hardware in Vivado using MicroBlaze

  1. Block design can also be created either sourcing build.tcl or manually. If you use the first method then steps 1–7 can be skipped and you can start from step 8 and follow along. People who want to create the block design manually can skip step 8 and follow rest. There can be some errors if you source build.tcl. Debugging of these errors will be mentioned in GitHub project link.
  2. Create a new block design.
  3. Add the path to the exported IP repository of your HLS project. It should be like:
<path_to_hls_project>/solution1/impl/ip
Setting IP repository

3. Add MicroBlaze to the block design and run block automation, set the values as:

MicroBlaze block automation

4. Add BRAM controller IP to the block design.

5. Set clocking wizard’s reset source to active low and run connection automation. The input and output clock of clocking wizard should match. It was 100 MHz for my case. At the end of connection automation block design should look like:

Running connection automation for the first time

6. Add Sum_arr IP (IP exported from HLS) and AXI Uartlite IP to your block design. Make sure to set the baud rate of AXI Uartlite IP to 115200 because testbench.vhd is written for this baud rate.

AXI Uartlite block settings

7. Run connection automation for the second time and your block design should look now as:

Running connection automation for the second time

And address editor which looks like(it can have different addresses) :

Layout of Address Editor

Address editor has important addresses which will be used in SDK.

One more important thing that I want to point out is that you can always increase the size of instruction memory of microblaze_0 from address editor by using drop-down list beside SLMB under Instruction section of microblaze_0. This can be used when your SDK code size is large. My code size was small so I managed to do it with 128K instruction size.

Choosing the instruction memory size of MicroBlaze

8. Source the build.tcl file for creating block design:

source build.tcl
Setting top module

Make the choice as shown above. There can be some errors in this step for which I will mention their debugging on GitHub project link.

9. Validate your block design and Generate Block Design.

10. Create an HDL wrapper of your block design and choose the option “Let Vivado manage the wrapper and auto-update” in the prompt.

Creating HDL wrapper

Exporting hardware and launching SDK to create .elf file

Export your hardware without including bitstream and launch SDK.

  1. Create an application project using HelloWorld template in SDK.
Creating a new application project
Creating a new application project

Click “Next”

Creating a new application project

Click “Finish”.

2. Then add following code lines to your helloworld.c in your application project created in step 1.

helloworld.c

I need to point out a few things for helloworld.c.

You might be wondering why I have used union in above code. Since values in memory are stored considering them as an unsigned integer. It is just the way how the bit values are interpreted by a hardware. So, by using a union structure I have easily converted floating-point representation of bits to its corresponding unsigned int. When HLS IP uses the bits stored at a location it interprets it them as they are of float data type.

xil_printf: It is a light-weight implementation of printf and much smaller in size (only 1 kB).

Functions like Xil_In32() and Xil_Out32() are used for accessing hardware IP registers. These function declarations are present in xil_io.h

Xil_In32(): It has only one argument which is the read address of the register.

Xil_Out32(): The first argument is the address of the register that we want to write and the second argument is the value to be written to the register.

The top most include in helloworld.c is a xparameters.h file that contains macro definitions for all the IP addresses in our design. It is present in the folder path:

<path-to-sdk-project>/<project-name_bsp>/microblaze_0/include

Following are some macro definitions taken from xparameters.h:

XPAR_BRAM_0_BASEADDR: It is the base address of BRAM. The value of this macro is same as the base address of BRAM as can be seen from address editor.

XPAR_SUM_ARR_0_S_AXI_AXILITES_BASEADDR: It is the base address of AXI slave register that contains the mapping for master and slave interfaces that we created in HLS IP.

For offset values like +0x18 etc. you can refer section “IP blocks present in the design” section of system.hdf that appears on your SDK project. It will have a link for Registers for our custom IP.

system.hdf

By clicking on Registers link you can get below mapping addresses for AXI Lite slave interface.

AXI slave register mapping

3. By saving and building the SDK project, it creates a .elf file which we will be using for our simulation.

Associating .elf file for Behavioral simulation.

Now, come back to Vivado GUI. It’s time to use your .elf file for simulation.

  1. Now add testbench.vhd as a simulation source to your Vivado project. The testbench has VHDL code for sending and receiving any UART communication and displaying received data on TCL console.
  2. There are a few things to be taken care of in testbench.vhd. Values like half period etc. have to be calculated according to the clock period that is chosen inside your block diagram for pin diff_clock_rtl_0. The calculations for my clock frequency of 100 MHz can be found out in the testbench.vhd in comments of the file. Follow along the calculations shown inside the file and place your hex values at the same positions inside the testbench file as I have done if you want to use different clock frequency.

3. Add your generated .elf file from your SDK project inside the folder:

<path-to-.sdk-folder inside Vivado project>/<name-of-your-sdk-application-project>/Debug

4. Now, associate your .elf file by right-clicking on your .elf file under Simulation sources hierarchy. Make the GUI choices as shown below:

Associate .elf file

Click on “…” option beside microblaze_0 under Simulation Sources.

Choosing a .elf file

Choose the .elf file of your SDK project and click OK 2 times.

Choosing a .elf file

5. Make sure Properties of your .elf file looks like:

Properties of array_sum_sdk.elf

6. Now, run Behavioral RTL Simulation.

I have included returned value of sum (I chose it by searching inside objects window of sum_arr_0 entity for returned signal) as obtained from HLS IP to waveform along with some HLS IP signals like ap_clk, ap_done etc.

To get the below output you should change the radix of sum_1_reg_216 signal to single-precision from real settings because remember return type of our HLS IP was float.

Then the output obtained would be:

Simulated waveform

The final output will also be printed on TCL console. Since xil_printf() is a slow function so it will take some time to print it on the console. So be patient.

TCL console output

Thanks for bearing with me till here. Now, I want to congratulate you for completing MicroBlaze RTL Simulation.

Kudos to you.

Link to all useful codes for this post: https://github.com/Neelam96/MicroBlaze-RTL-Simulation

Link to the video: https://www.youtube.com/watch?v=EjnH-CgOp2g&feature=youtu.be

I would like to thanks my friends Anurag for making the video and Shreekanya for reviewing this post.

--

--